Repository: santiago-MV/merkle-tree
Pull request #3: Merkel Tree initializer
Author: login: santiago-MV
Created: 2025-04-28T21:00:18Z | Merged: 2025-04-29T15:21:21Z

Merkel tree creation + test


Diff:
diff --git a/src/main.rs b/src/main.rs
index e7a11a9..01b21ae 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,11 @@
+mod merkle_tree;
 fn main() {
-    println!("Hello, world!");
+    let mut test_tree = merkle_tree::new(&[0; 4]);
+    let test_tree_2 = merkle_tree::new(&[0; 5]);
+    test_tree.push(&0);
+    print!("========================================");
+    print!("{:?} ", test_tree.get_tree());
+    print!("========================================");
+    print!("{:?} ", test_tree_2.get_tree());
+    print!("========================================");
 }
diff --git a/src/merkle_tree.rs b/src/merkle_tree.rs
new file mode 100644
index 0000000..3c37d20
--- /dev/null
+++ b/src/merkle_tree.rs
@@ -0,0 +1,125 @@
+use std::hash::{DefaultHasher, Hash, Hasher};
+pub struct MerkleTree {
+    tree: Vec<u64>,
+    leaf_amount: usize,
+}
+impl MerkleTree {
+    fn generate_tree<H: Hash>(&mut self, data: &[H]) {
+        // Vector that will hold the hashed values
+        let mut hashed_tree: Vec<u64> = Vec::new();
+        // Hash the user data and push it into the vector
+        for d in data {
+            add_hash(d, &mut hashed_tree);
+        }
+        // Check if the amount of nodes is a power of two, if not complete it with 0
+        add_padding(&mut hashed_tree);
+        // Reverse hashed_tree so that the combined hashes can be pushed
+        hashed_tree.reverse();
+        self.tree = hashed_tree;
+        self.generate_from_hashes();
+    }
+
+    fn generate_from_hashes(&mut self) {
+        // Calculate the amount of nodes the binary tree will have
+        // number_of_nodes = 2 * amount_of_leafs - 1
+        let number_of_nodes = 2 * self.tree.len() - 1;
+        // Calculate the combined hashes and push it
+        let mut index = 0;
+        loop {
+            let left_data = self.tree[index];
+            let right_data = self.tree[index + 1];
+            add_two_hashes(&left_data, &right_data, &mut self.tree);
+            index += 2;
+            if index >= number_of_nodes - 1 {
+                break;
+            }
+        }
+        // Reverse the vector so that the root is the head
+        self.tree.reverse();
+    }
+
+    pub fn push<H: Hash>(&mut self, data: &H) {
+        let length = self.tree.len();
+        let mut leafs = self.tree[(length - self.leaf_amount)..].to_vec();
+        // Add the new leaf
+        add_hash(data, &mut leafs);
+        // Add padding
+        add_padding(&mut leafs);
+        // Reverse leafs so that the last one is in the head
+        leafs.reverse();
+        self.tree = leafs;
+        self.leaf_amount += 1;
+        // Generate the rest of the tree
+        self.generate_from_hashes();
+    }
+
+    pub fn get_tree(&self) -> Vec<u64> {
+        self.tree.clone()
+    }
+}
+// Creates a new instance of MerkleTree and generates its tree value from the array
+pub fn new<H: Hash>(data: &[H]) -> MerkleTree {
+    let mut initialized_tree = MerkleTree {
+        tree: Vec::new(),
+        leaf_amount: data.len(),
+    };
+    initialized_tree.generate_tree(data);
+    initialized_tree
+}
+
+fn closest_power_of_2(number: u128) -> u128 {
+    let mut power = 1;
+    loop {
+        if number < power {
+            return power;
+        }
+        power *= 2;
+    }
+}
+
+fn add_hash<H: Hash>(data: &H, hashed_tree: &mut Vec<u64>) {
+    let mut hasher = DefaultHasher::new();
+    (*data).hash(&mut hasher);
+    hashed_tree.push(hasher.finish());
+}
+
+fn add_two_hashes<H: Hash>(left_data: &H, right_data: &H, hashed_tree: &mut Vec<u64>) {
+    let mut hasher = DefaultHasher::new();
+    (*left_data).hash(&mut hasher);
+    (*right_data).hash(&mut hasher);
+    hashed_tree.push(hasher.finish());
+}
+// If the leaf amount isn't a power of 2 then add the minimum amount of 0 possible to make it one
+fn add_padding(tree: &mut Vec<u64>) {
+    if !tree.len().is_power_of_two() {
+        let closest_power_of_2 = closest_power_of_2(tree.len() as u128);
+        loop {
+            if closest_power_of_2 == tree.len() as u128 {
+                break;
+            }
+            add_hash(&0, tree);
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::new;
+
+    #[test]
+    fn creation_test() {
+        let not_power_of_two_tree = new(&[0; 5]);
+        let closes_power_of_two_tree = new(&[0; 8]);
+        assert_eq!(
+            not_power_of_two_tree.get_tree(),
+            closes_power_of_two_tree.get_tree()
+        );
+    }
+    #[test]
+    fn push_test() {
+        let mut first_tree = new(&[0, 0, 0, 0]);
+        let second_tree = new(&[0, 0, 0, 0, 1]);
+        first_tree.push(&1);
+        assert_eq!(first_tree.get_tree(), second_tree.get_tree());
+    }
+}


Reviews:
user: login: CimimUxMaio
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-28T21:17:01Z
user: login: santiago-MV
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-29T12:19:19Z
user: login: santiago-MV
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-29T12:21:33Z
user: login: CimimUxMaio
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-29T13:13:50Z
user: login: santiago-MV
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-29T13:24:11Z
user: login: CimimUxMaio
body: Looks good, it's approved.
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-29T15:13:17Z

Review comments:
diff_hunk: @@ -0,0 +1,99 @@
+use std::hash::{DefaultHasher, Hash, Hasher};
+pub struct MerkleTree {
+    tree: Vec<u64>,
+}
+impl MerkleTree {
+    fn generate_tree<H: Hash>(&mut self, data: &[H]) {
+        // Vector that will hold the hashed values
+        let mut hashed_tree: Vec<u64> = Vec::new();
+        // Hash the user data and push it into the vector
+        for d in data {
+            let mut hasher = DefaultHasher::new();
+            (*d).hash(&mut hasher);
+            hashed_tree.push(hasher.finish());
path: src/merkle_tree.rs
user: login: CimimUxMaio
body: I suggest we could move this logic to a new function. This way we could re-use it later.
created_at: 2025-04-28T21:09:35Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,99 @@
+use std::hash::{DefaultHasher, Hash, Hasher};
+pub struct MerkleTree {
+    tree: Vec<u64>,
+}
+impl MerkleTree {
+    fn generate_tree<H: Hash>(&mut self, data: &[H]) {
+        // Vector that will hold the hashed values
+        let mut hashed_tree: Vec<u64> = Vec::new();
+        // Hash the user data and push it into the vector
+        for d in data {
+            let mut hasher = DefaultHasher::new();
+            (*d).hash(&mut hasher);
+            hashed_tree.push(hasher.finish());
+        }
+        // Check if the amount of nodes is a power of two, if not complete it with 0
+        if !is_power_of_2(hashed_tree.len() as u128) {
+            let power_of_2 = closest_power_of_2(hashed_tree.len() as u128);
+            loop {
+                if power_of_2 == hashed_tree.len() as u128 {
+                    break;
+                }
+                let mut hasher = DefaultHasher::new();
+                0.hash(&mut hasher);
+                hashed_tree.push(hasher.finish());
+            }
+        }
+        // Reverse hashed_tree so that the combined hashes can be pushed
+        hashed_tree.reverse();
+        // Calculate the amount of nodes the binary tree will have
+        // number_of_nodes = 2 * amount_of_leafs - 1
+        let number_of_nodes = 2 * hashed_tree.len() - 1;
+        // Calculate the combined hashes and push it
+        let mut index = 0;
+        loop {
+            let mut hasher = DefaultHasher::new();
+            (hashed_tree[index] as u128 + hashed_tree[index + 1] as u128).hash(&mut hasher);
path: src/merkle_tree.rs
user: login: CimimUxMaio
body: Adding 2 hashes may cause overflow. I understand that converting hash values from u64 to u128 may avoid this problem, but maybe we could use the DefaultHasher's functionality to create hashes given 2 different inputs.

For example:
```
    let mut hasher = DefaultHasher::new();
    value1.hash(&mut hasher);
    value2.hash(&mut hasher);
    let combined_hash = hasher.finish();
```
created_at: 2025-04-28T21:16:06Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,99 @@
+use std::hash::{DefaultHasher, Hash, Hasher};
+pub struct MerkleTree {
+    tree: Vec<u64>,
+}
+impl MerkleTree {
+    fn generate_tree<H: Hash>(&mut self, data: &[H]) {
+        // Vector that will hold the hashed values
+        let mut hashed_tree: Vec<u64> = Vec::new();
+        // Hash the user data and push it into the vector
+        for d in data {
+            let mut hasher = DefaultHasher::new();
+            (*d).hash(&mut hasher);
+            hashed_tree.push(hasher.finish());
path: src/merkle_tree.rs
user: login: santiago-MV
body: Done
created_at: 2025-04-29T12:19:19Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,99 @@
+use std::hash::{DefaultHasher, Hash, Hasher};
+pub struct MerkleTree {
+    tree: Vec<u64>,
+}
+impl MerkleTree {
+    fn generate_tree<H: Hash>(&mut self, data: &[H]) {
+        // Vector that will hold the hashed values
+        let mut hashed_tree: Vec<u64> = Vec::new();
+        // Hash the user data and push it into the vector
+        for d in data {
+            let mut hasher = DefaultHasher::new();
+            (*d).hash(&mut hasher);
+            hashed_tree.push(hasher.finish());
+        }
+        // Check if the amount of nodes is a power of two, if not complete it with 0
+        if !is_power_of_2(hashed_tree.len() as u128) {
+            let power_of_2 = closest_power_of_2(hashed_tree.len() as u128);
+            loop {
+                if power_of_2 == hashed_tree.len() as u128 {
+                    break;
+                }
+                let mut hasher = DefaultHasher::new();
+                0.hash(&mut hasher);
+                hashed_tree.push(hasher.finish());
+            }
+        }
+        // Reverse hashed_tree so that the combined hashes can be pushed
+        hashed_tree.reverse();
+        // Calculate the amount of nodes the binary tree will have
+        // number_of_nodes = 2 * amount_of_leafs - 1
+        let number_of_nodes = 2 * hashed_tree.len() - 1;
+        // Calculate the combined hashes and push it
+        let mut index = 0;
+        loop {
+            let mut hasher = DefaultHasher::new();
+            (hashed_tree[index] as u128 + hashed_tree[index + 1] as u128).hash(&mut hasher);
path: src/merkle_tree.rs
user: login: santiago-MV
body: Good suggestion. I have implemented it.
created_at: 2025-04-29T12:21:33Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,108 @@
+use std::hash::{DefaultHasher, Hash, Hasher};
+pub struct MerkleTree {
+    tree: Vec<u64>,
+}
+impl MerkleTree {
+    fn generate_tree<H: Hash>(&mut self, data: &[H]) {
+        // Vector that will hold the hashed values
+        let mut hashed_tree: Vec<u64> = Vec::new();
+        // Hash the user data and push it into the vector
+        for d in data {
+            add_hash(d, &mut hashed_tree);
+        }
+        // Check if the amount of nodes is a power of two, if not complete it with 0
+        if !is_power_of_2(hashed_tree.len() as u128) {
+            let power_of_2 = closest_power_of_2(hashed_tree.len() as u128);
+            loop {
+                if power_of_2 == hashed_tree.len() as u128 {
+                    break;
+                }
+                add_hash(&0, &mut hashed_tree);
+            }
+        }
+        // Reverse hashed_tree so that the combined hashes can be pushed
+        hashed_tree.reverse();
+        // Calculate the amount of nodes the binary tree will have
+        // number_of_nodes = 2 * amount_of_leafs - 1
+        let number_of_nodes = 2 * hashed_tree.len() - 1;
+        // Calculate the combined hashes and push it
+        let mut index = 0;
+        loop {
+            let left_data = hashed_tree[index];
+            let right_data = hashed_tree[index + 1];
+            add_two_hashes(&left_data, &right_data, &mut hashed_tree);
+            index += 2;
+            if index >= number_of_nodes - 1 {
+                break;
+            }
+        }
+        // Reverse the vector so that the root is the head
+        hashed_tree.reverse();
+        // Set the value into the tree
+        self.tree = hashed_tree.clone();
+    }
+
+    pub fn get_tree(&self) -> Vec<u64> {
+        self.tree.clone()
+    }
+}
+// Creates a new instance of MerkleTree and generates its tree value from the array
+pub fn new<H: Hash>(data: &[H]) -> MerkleTree {
+    let mut initialized_tree = MerkleTree { tree: Vec::new() };
+    initialized_tree.generate_tree(data);
+    initialized_tree
+}
+
+fn is_power_of_2(number: u128) -> bool {
+    let mut value = number;
+    loop {
+        // If the value is 2 then return true
+        if value == 2 {
+            return true;
+        }
+        // if the value is odd return false
+        if value % 2 != 0 {
+            return false;
+        }
+        // Neither of the above then divide the value by 2 and continue
+        value /= 2;
+    }
+}
path: src/merkle_tree.rs
user: login: CimimUxMaio
body: One last comment that could be useful. I've noticed that rust already provides a function with this functionality for `usize`. You can find the docs [here](https://doc.rust-lang.org/std/primitive.usize.html#method.is_power_of_two).

Example:
```
let number: usize = 16;
number.is_power_of_two()
> true
```
created_at: 2025-04-29T13:13:43Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,108 @@
+use std::hash::{DefaultHasher, Hash, Hasher};
+pub struct MerkleTree {
+    tree: Vec<u64>,
+}
+impl MerkleTree {
+    fn generate_tree<H: Hash>(&mut self, data: &[H]) {
+        // Vector that will hold the hashed values
+        let mut hashed_tree: Vec<u64> = Vec::new();
+        // Hash the user data and push it into the vector
+        for d in data {
+            add_hash(d, &mut hashed_tree);
+        }
+        // Check if the amount of nodes is a power of two, if not complete it with 0
+        if !is_power_of_2(hashed_tree.len() as u128) {
+            let power_of_2 = closest_power_of_2(hashed_tree.len() as u128);
+            loop {
+                if power_of_2 == hashed_tree.len() as u128 {
+                    break;
+                }
+                add_hash(&0, &mut hashed_tree);
+            }
+        }
+        // Reverse hashed_tree so that the combined hashes can be pushed
+        hashed_tree.reverse();
+        // Calculate the amount of nodes the binary tree will have
+        // number_of_nodes = 2 * amount_of_leafs - 1
+        let number_of_nodes = 2 * hashed_tree.len() - 1;
+        // Calculate the combined hashes and push it
+        let mut index = 0;
+        loop {
+            let left_data = hashed_tree[index];
+            let right_data = hashed_tree[index + 1];
+            add_two_hashes(&left_data, &right_data, &mut hashed_tree);
+            index += 2;
+            if index >= number_of_nodes - 1 {
+                break;
+            }
+        }
+        // Reverse the vector so that the root is the head
+        hashed_tree.reverse();
+        // Set the value into the tree
+        self.tree = hashed_tree.clone();
+    }
+
+    pub fn get_tree(&self) -> Vec<u64> {
+        self.tree.clone()
+    }
+}
+// Creates a new instance of MerkleTree and generates its tree value from the array
+pub fn new<H: Hash>(data: &[H]) -> MerkleTree {
+    let mut initialized_tree = MerkleTree { tree: Vec::new() };
+    initialized_tree.generate_tree(data);
+    initialized_tree
+}
+
+fn is_power_of_2(number: u128) -> bool {
+    let mut value = number;
+    loop {
+        // If the value is 2 then return true
+        if value == 2 {
+            return true;
+        }
+        // if the value is odd return false
+        if value % 2 != 0 {
+            return false;
+        }
+        // Neither of the above then divide the value by 2 and continue
+        value /= 2;
+    }
+}
path: src/merkle_tree.rs
user: login: santiago-MV
body: I've missed that. Thanks!
created_at: 2025-04-29T13:24:11Z
author_association: OWNER

PR comments:
user: login: santiago-MV
created_at: 2025-04-29T12:38:38Z
body: Ready for revision.
author_association: OWNER
user: login: santiago-MV
created_at: 2025-04-29T13:31:49Z
body: Changed the way to check if a number is a power of 2.
Added push and tests
author_association: OWNER