Repository: Calisto-Mathias/braidpool-v1
Pull request #8: Add in bead validity checks
Author: login: Calisto-Mathias
Created: 2025-03-28T22:02:06Z | Merged: 2025-03-29T06:36:53Z

1. Refactored the project to use braidpool-primitives as a library as intended.
2. Added in the required commit structure for future integration into the branch on braidpool as discussed on Discord
3. Added the checks for the validity of the bead data included in a DagBead

Future work:
1. Rename the types xD (Forgot to do this)

Closes #4 

Linked issue #4: Add in checks for validity of beads.

Diff:
diff --git a/Cargo.lock b/Cargo.lock
index 7ac4493..03668af 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -118,7 +118,7 @@ dependencies = [
 ]
 
 [[package]]
-name = "braidpool-simulator"
+name = "braidpool-primitives"
 version = "0.1.0"
 dependencies = [
  "bitcoin",
diff --git a/Cargo.toml b/Cargo.toml
index c960d6a..3c839f2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,11 +1,3 @@
-[package]
-name = "braidpool-primitives"
-version = "0.1.0"
-edition = "2024"
-
-[dependencies]
-hex = "0.4.3"
-num-bigint = "0.4.6"
-ordered-float = "5.0.0"
-transitive = "1.1.0"
-bitcoin = { git = "https://github.com/braidpool/rust-bitcoin.git" }
+[workspace]
+members = ["braidpool-primitives"]
+resolver = "3"
\ No newline at end of file
diff --git a/braidpool-primitives/Cargo.toml b/braidpool-primitives/Cargo.toml
new file mode 100644
index 0000000..8dea77d
--- /dev/null
+++ b/braidpool-primitives/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "braidpool-primitives"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+hex = "0.4.3"
+num-bigint = "0.4.6"
+ordered-float = "5.0.0"
+transitive = "1.1.0"
+bitcoin = { git = "https://github.com/braidpool/rust-bitcoin.git" }
+
+[lib]
+name = "braidpool_primitives"
+path = "src/lib.rs"
\ No newline at end of file
diff --git a/braidpool-primitives/main.rs b/braidpool-primitives/main.rs
deleted file mode 100644
index 7de3083..0000000
--- a/braidpool-primitives/main.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-mod nodes;
-mod braid;
-mod beads;
-mod utils;
-
-fn main() {
-
-}
\ No newline at end of file
diff --git a/braidpool-primitives/beads.rs b/braidpool-primitives/src/beads.rs
similarity index 56%
rename from braidpool-primitives/beads.rs
rename to braidpool-primitives/src/beads.rs
index 1aa8095..18d26f9 100644
--- a/braidpool-primitives/beads.rs
+++ b/braidpool-primitives/src/beads.rs
@@ -3,6 +3,8 @@ use std::collections::HashSet;
 
 // Bitcoin primitives
 use bitcoin::absolute::Time;
+use bitcoin::hashes::Sha256d;
+use bitcoin::transaction::TransactionExt;
 use bitcoin::{BlockHeader, CompactTarget, Transaction};
 
 // Custom Imports
@@ -38,8 +40,26 @@ impl DagBead {
 }
 
 impl Bead {
+
+    #[inline]
+    fn is_transaction_included_in_block(&self, transaction_with_proof: &TransactionWithMerklePath)
+        -> bool 
+    {
+        transaction_with_proof.1.calculate_corresponding_merkle_root() == self.block_header.merkle_root
+    }
+
     pub fn is_valid_bead(&self) -> bool {
-        // TODO: Implement Checks
-        true
+        // Check whether the transactions are included in the block!
+        if self.is_transaction_included_in_block(&self.coinbase_transaction) == false {
+            false
+        } else if self.is_transaction_included_in_block(&self.payout_update_transaction) == false {
+            false
+        } else if self.coinbase_transaction.0.is_coinbase() == false {
+            false
+        } else if self.bead_hash != self.block_header.block_hash() {
+            false
+        } else {
+            true
+        }
     }
 }
\ No newline at end of file
diff --git a/braidpool-primitives/braid.rs b/braidpool-primitives/src/braid.rs
similarity index 100%
rename from braidpool-primitives/braid.rs
rename to braidpool-primitives/src/braid.rs
diff --git a/braidpool-primitives/src/lib.rs b/braidpool-primitives/src/lib.rs
new file mode 100644
index 0000000..5dc3b2c
--- /dev/null
+++ b/braidpool-primitives/src/lib.rs
@@ -0,0 +1,3 @@
+pub mod braid;
+pub mod beads;
+pub mod utils;
\ No newline at end of file
diff --git a/braidpool-primitives/utils/bitcoin.rs b/braidpool-primitives/src/utils/bitcoin.rs
similarity index 60%
rename from braidpool-primitives/utils/bitcoin.rs
rename to braidpool-primitives/src/utils/bitcoin.rs
index d215f84..a398feb 100644
--- a/braidpool-primitives/utils/bitcoin.rs
+++ b/braidpool-primitives/src/utils/bitcoin.rs
@@ -3,7 +3,7 @@ use std::cell::Cell;
 
 // Primitives Imports
 use bitcoin::{BlockHash, BlockHeader, BlockTime, BlockVersion, CompactTarget, TxMerkleNode, Txid};
-
+use bitcoin::hashes::Sha256d;
 
 // Internal Type Definitions for Clarity
 type MerkleRoot = TxMerkleNode;
@@ -13,6 +13,23 @@ pub struct MerklePathProof {
     merkle_path: Vec<TxMerkleNode>
 }
 
+impl MerklePathProof {
+    pub fn calculate_corresponding_merkle_root(&self) -> MerkleRoot {
+        let mut current_hash = self.transaction_hash.as_byte_array().clone();
+        for merkle_node in &self.merkle_path {
+            let merkle_node_as_bytes = merkle_node.as_byte_array();
+            
+            let mut concatenated_hashes = Vec::new();
+            concatenated_hashes.extend_from_slice(&current_hash);
+            concatenated_hashes.extend_from_slice(merkle_node_as_bytes);
+
+            current_hash = Sha256d::hash(&concatenated_hashes).as_byte_array().clone();
+        };
+
+        TxMerkleNode::from_byte_array(current_hash)
+    }
+}
+
 #[derive(Debug)]
 pub struct MiningBlockHeader {
     pub version: BlockVersion,
diff --git a/braidpool-primitives/utils/mod.rs b/braidpool-primitives/src/utils/mod.rs
similarity index 100%
rename from braidpool-primitives/utils/mod.rs
rename to braidpool-primitives/src/utils/mod.rs


Reviews:
user: login: Aritra8438
body: Everything looks fine.
(We need to add tests `:3`)
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-29T06:04:41Z
user: login: Calisto-Mathias
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-29T06:13:48Z
user: login: Calisto-Mathias
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-29T06:15:09Z
user: login: Sansh2356
body: As discussed for implementation requirements.
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-29T06:28:01Z
user: login: Sansh2356
body: Done !
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-29T06:31:35Z
user: login: Sansh2356
body: Resubmitting 
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-29T06:34:33Z

Review comments:
diff_hunk: @@ -38,8 +40,26 @@ impl DagBead {
 }
 
 impl Bead {
+
+    #[inline]
+    fn is_transaction_included_in_block(&self, transaction_with_proof: &TransactionWithMerklePath)
+        -> bool 
+    {
+        transaction_with_proof.1.calculate_corresponding_merkle_root() == self.block_header.merkle_root
+    }
+
     pub fn is_valid_bead(&self) -> bool {
-        // TODO: Implement Checks
-        true
+        // Check whether the transactions are included in the block!
+        if self.is_transaction_included_in_block(&self.coinbase_transaction) == false {
+            return false
+        } else if self.is_transaction_included_in_block(&self.payout_update_transaction) == false {
+            return false
+        } else if self.coinbase_transaction.0.is_coinbase() == false {
+            return false
+        } else if self.bead_hash != self.block_header.block_hash() {
+            return false
+        } else {
+            true
path: braidpool-primitives/src/beads.rs
user: login: Aritra8438
body: (Not a big problem at all, but) Can we omit `return` in the 4 `return false` instances?
created_at: 2025-03-29T06:00:29Z
author_association: COLLABORATOR
diff_hunk: @@ -13,6 +13,23 @@ pub struct MerklePathProof {
     merkle_path: Vec<TxMerkleNode>
 }
 
+impl MerklePathProof {
+    pub fn calculate_corresponding_merkle_root(&self) -> MerkleRoot {
+        let mut current_hash = self.transaction_hash.as_byte_array().clone();
+        for merkle_node in &self.merkle_path {
+            let merkle_node_as_bytes = merkle_node.as_byte_array();
+            
+            let mut concatenated_hashes = Vec::new();
+            concatenated_hashes.extend_from_slice(&current_hash);
+            concatenated_hashes.extend_from_slice(merkle_node_as_bytes);
+
+            current_hash = Sha256d::hash(&concatenated_hashes).as_byte_array().clone();
+        };
+
+        TxMerkleNode::from_byte_array(current_hash)
+    }
+}
+
path: braidpool-primitives/src/utils/bitcoin.rs
user: login: Aritra8438
body: ```
       Root (HashABCD)
      /               \
  HashAB             HashCD
 /     \             /     \
HashA  HashB     HashC    HashD
 |      |         |        |
TxA    TxB       TxC      TxD
```
So, to verify `TxA`, the merkle path will be, [HashB, HashCD], is that correct?
created_at: 2025-03-29T06:03:34Z
author_association: COLLABORATOR
diff_hunk: @@ -13,6 +13,23 @@ pub struct MerklePathProof {
     merkle_path: Vec<TxMerkleNode>
 }
 
+impl MerklePathProof {
+    pub fn calculate_corresponding_merkle_root(&self) -> MerkleRoot {
+        let mut current_hash = self.transaction_hash.as_byte_array().clone();
+        for merkle_node in &self.merkle_path {
+            let merkle_node_as_bytes = merkle_node.as_byte_array();
+            
+            let mut concatenated_hashes = Vec::new();
+            concatenated_hashes.extend_from_slice(&current_hash);
+            concatenated_hashes.extend_from_slice(merkle_node_as_bytes);
+
+            current_hash = Sha256d::hash(&concatenated_hashes).as_byte_array().clone();
+        };
+
+        TxMerkleNode::from_byte_array(current_hash)
+    }
+}
+
path: braidpool-primitives/src/utils/bitcoin.rs
user: login: Calisto-Mathias
body: Yepp. That would be the merkle path.
created_at: 2025-03-29T06:13:48Z
author_association: COLLABORATOR
diff_hunk: @@ -38,8 +40,26 @@ impl DagBead {
 }
 
 impl Bead {
+
+    #[inline]
+    fn is_transaction_included_in_block(&self, transaction_with_proof: &TransactionWithMerklePath)
+        -> bool 
+    {
+        transaction_with_proof.1.calculate_corresponding_merkle_root() == self.block_header.merkle_root
+    }
+
     pub fn is_valid_bead(&self) -> bool {
-        // TODO: Implement Checks
-        true
+        // Check whether the transactions are included in the block!
+        if self.is_transaction_included_in_block(&self.coinbase_transaction) == false {
+            return false
+        } else if self.is_transaction_included_in_block(&self.payout_update_transaction) == false {
+            return false
+        } else if self.coinbase_transaction.0.is_coinbase() == false {
+            return false
+        } else if self.bead_hash != self.block_header.block_hash() {
+            return false
+        } else {
+            true
path: braidpool-primitives/src/beads.rs
user: login: Calisto-Mathias
body: yepp! I'll just remove that. I don't know why that didn't come to mind xD
created_at: 2025-03-29T06:15:09Z
author_association: COLLABORATOR

PR comments:
user: login: Calisto-Mathias
created_at: 2025-03-29T06:14:05Z
body: > Everything looks fine. (We need to add tests `:3`)

I know right. I actually did add in a tests directory but its empty :P
author_association: COLLABORATOR
user: login: Sansh2356
created_at: 2025-03-29T06:16:44Z
body: > > Everything looks fine. (We need to add tests `:3`)
> 
> I know right. I actually did add in a tests directory but its empty :P

I'll add tests , if that's fine @Aritra8438  and @Calisto-Mathias  ?
author_association: COLLABORATOR
user: login: Calisto-Mathias
created_at: 2025-03-29T06:19:47Z
body: > > > Everything looks fine. (We need to add tests `:3`)
> > 
> > 
> > I know right. I actually did add in a tests directory but its empty :P
> 
> I'll add tests , if that's fine @Aritra8438 and @Calisto-Mathias ?

That sounds perfect. Create the relevant issues and add in the tests under a tests/ directory in the braidpool-primitives crate.
author_association: COLLABORATOR