From 980c5f04eaf74186d2e467b9867f26b0abb6884b Mon Sep 17 00:00:00 2001 From: Boog900 <54e72d8a-345f-4599-bd90-c6b9bc7d0ec5@aleeas.com> Date: Fri, 15 Nov 2024 16:44:11 +0000 Subject: [PATCH 1/3] Monero: expose merkel root function --- networks/monero/src/block.rs | 11 ++++++++--- networks/monero/src/lib.rs | 3 ++- networks/monero/src/merkle.rs | 11 +++++------ 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/networks/monero/src/block.rs b/networks/monero/src/block.rs index 15a8d1fc5..9b787c613 100644 --- a/networks/monero/src/block.rs +++ b/networks/monero/src/block.rs @@ -121,8 +121,13 @@ impl Block { /// use the [`Block::hash`] function. pub fn serialize_pow_hash(&self) -> Vec { let mut blob = self.header.serialize(); - blob.extend_from_slice(&merkle_root(self.miner_transaction.hash(), &self.transactions)); - write_varint(&(1 + u64::try_from(self.transactions.len()).unwrap()), &mut blob).unwrap(); + + let mut transactions = Vec::with_capacity(self.transactions.len() + 1); + transactions.push(self.miner_transaction.hash()); + transactions.extend_from_slice(&self.transactions); + + blob.extend_from_slice(&merkle_root(&transactions)); + write_varint(&(1 + self.transactions.len()), &mut blob).unwrap(); blob } @@ -132,7 +137,7 @@ impl Block { // Monero pre-appends a VarInt of the block-to-hash'ss length before getting the block hash, // but doesn't do this when getting the proof of work hash :) let mut hashing_blob = Vec::with_capacity(9 + hashable.len()); - write_varint(&u64::try_from(hashable.len()).unwrap(), &mut hashing_blob).unwrap(); + write_varint(&hashable.len(), &mut hashing_blob).unwrap(); hashing_blob.append(&mut hashable); let hash = keccak256(hashing_blob); diff --git a/networks/monero/src/lib.rs b/networks/monero/src/lib.rs index 2eaa63e18..1af98e68c 100644 --- a/networks/monero/src/lib.rs +++ b/networks/monero/src/lib.rs @@ -7,7 +7,8 @@ pub use monero_io as io; pub use monero_generators as generators; pub use monero_primitives as primitives; -mod merkle; +/// Merkel tree functionality. +pub mod merkle; /// Ring Signature structs and functionality. pub mod ring_signatures; diff --git a/networks/monero/src/merkle.rs b/networks/monero/src/merkle.rs index 6c689618d..e052f6c98 100644 --- a/networks/monero/src/merkle.rs +++ b/networks/monero/src/merkle.rs @@ -2,14 +2,13 @@ use std_shims::vec::Vec; use crate::primitives::keccak256; -pub(crate) fn merkle_root(root: [u8; 32], leafs: &[[u8; 32]]) -> [u8; 32] { +/// Calculates the merkel root of the given tree. Equivalent to `tree_hash` in monero-core. +pub fn merkle_root(leafs: &[[u8; 32]]) -> [u8; 32] { match leafs.len() { - 0 => root, - 1 => keccak256([root, leafs[0]].concat()), + 1 => leafs[0], + 2 => keccak256([leafs[0], leafs[1]].concat()), _ => { - let mut hashes = Vec::with_capacity(1 + leafs.len()); - hashes.push(root); - hashes.extend(leafs); + let mut hashes = leafs.to_vec(); // Monero preprocess this so the length is a power of 2 let mut high_pow_2 = 4; // 4 is the lowest value this can be From 05cda9762fed504cce9bc6b30d0a4c7286590df3 Mon Sep 17 00:00:00 2001 From: Boog900 <54e72d8a-345f-4599-bd90-c6b9bc7d0ec5@aleeas.com> Date: Fri, 15 Nov 2024 19:36:51 +0000 Subject: [PATCH 2/3] review fixes --- networks/monero/src/block.rs | 2 +- networks/monero/src/lib.rs | 2 +- networks/monero/src/merkle.rs | 36 +++++++++++++++++++---------------- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/networks/monero/src/block.rs b/networks/monero/src/block.rs index 9b787c613..a0bbe506b 100644 --- a/networks/monero/src/block.rs +++ b/networks/monero/src/block.rs @@ -126,7 +126,7 @@ impl Block { transactions.push(self.miner_transaction.hash()); transactions.extend_from_slice(&self.transactions); - blob.extend_from_slice(&merkle_root(&transactions)); + blob.extend_from_slice(&merkle_root(transactions)); write_varint(&(1 + self.transactions.len()), &mut blob).unwrap(); blob } diff --git a/networks/monero/src/lib.rs b/networks/monero/src/lib.rs index 1af98e68c..e058f6206 100644 --- a/networks/monero/src/lib.rs +++ b/networks/monero/src/lib.rs @@ -7,7 +7,7 @@ pub use monero_io as io; pub use monero_generators as generators; pub use monero_primitives as primitives; -/// Merkel tree functionality. +/// Merkle tree functionality. pub mod merkle; /// Ring Signature structs and functionality. diff --git a/networks/monero/src/merkle.rs b/networks/monero/src/merkle.rs index e052f6c98..8a0f8134b 100644 --- a/networks/monero/src/merkle.rs +++ b/networks/monero/src/merkle.rs @@ -2,25 +2,29 @@ use std_shims::vec::Vec; use crate::primitives::keccak256; -/// Calculates the merkel root of the given tree. Equivalent to `tree_hash` in monero-core. -pub fn merkle_root(leafs: &[[u8; 32]]) -> [u8; 32] { +/// Calculates the Merkle root of the given tree. Equivalent to `tree_hash` in monero-core: +/// https://github.com/monero-project/monero/blob/893916ad091a92e765ce3241b94e706ad012b62a +/// /src/crypto/tree-hash.c#L62 +/// +/// # Panics +/// This function will panic if the tree is empty. +pub fn merkle_root(mut leafs: Vec<[u8; 32]>) -> [u8; 32] { match leafs.len() { + 0 => panic!("Can't compute Merkle root for empty tree"), 1 => leafs[0], 2 => keccak256([leafs[0], leafs[1]].concat()), _ => { - let mut hashes = leafs.to_vec(); - // Monero preprocess this so the length is a power of 2 let mut high_pow_2 = 4; // 4 is the lowest value this can be - while high_pow_2 < hashes.len() { + while high_pow_2 < leafs.len() { high_pow_2 *= 2; } let low_pow_2 = high_pow_2 / 2; // Merge right-most hashes until we're at the low_pow_2 { - let overage = hashes.len() - low_pow_2; - let mut rightmost = hashes.drain((low_pow_2 - overage) ..); + let overage = leafs.len() - low_pow_2; + let mut rightmost = leafs.drain((low_pow_2 - overage) ..); // This is true since we took overage from beneath and above low_pow_2, taking twice as // many elements as overage debug_assert_eq!(rightmost.len() % 2, 0); @@ -32,23 +36,23 @@ pub fn merkle_root(leafs: &[[u8; 32]]) -> [u8; 32] { } drop(rightmost); - hashes.extend(paired_hashes); - assert_eq!(hashes.len(), low_pow_2); + leafs.extend(paired_hashes); + assert_eq!(leafs.len(), low_pow_2); } // Do a traditional pairing off - let mut new_hashes = Vec::with_capacity(hashes.len() / 2); - while hashes.len() > 1 { + let mut new_hashes = Vec::with_capacity(leafs.len() / 2); + while leafs.len() > 1 { let mut i = 0; - while i < hashes.len() { - new_hashes.push(keccak256([hashes[i], hashes[i + 1]].concat())); + while i < leafs.len() { + new_hashes.push(keccak256([leafs[i], leafs[i + 1]].concat())); i += 2; } - hashes = new_hashes; - new_hashes = Vec::with_capacity(hashes.len() / 2); + leafs = new_hashes; + new_hashes = Vec::with_capacity(leafs.len() / 2); } - hashes[0] + leafs[0] } } } From e6fdef6d0b4481932ac9647796eb3fa56197ed66 Mon Sep 17 00:00:00 2001 From: Boog900 <54e72d8a-345f-4599-bd90-c6b9bc7d0ec5@aleeas.com> Date: Fri, 15 Nov 2024 20:34:11 +0000 Subject: [PATCH 3/3] change `merkle_root` return to `Option` --- networks/monero/src/block.rs | 5 ++++- networks/monero/src/merkle.rs | 13 ++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/networks/monero/src/block.rs b/networks/monero/src/block.rs index a0bbe506b..a15bd7eb6 100644 --- a/networks/monero/src/block.rs +++ b/networks/monero/src/block.rs @@ -126,7 +126,10 @@ impl Block { transactions.push(self.miner_transaction.hash()); transactions.extend_from_slice(&self.transactions); - blob.extend_from_slice(&merkle_root(transactions)); + blob.extend_from_slice( + &merkle_root(transactions) + .expect("the tree will not be empty, the miner tx is always present"), + ); write_varint(&(1 + self.transactions.len()), &mut blob).unwrap(); blob } diff --git a/networks/monero/src/merkle.rs b/networks/monero/src/merkle.rs index 8a0f8134b..c30e83b4f 100644 --- a/networks/monero/src/merkle.rs +++ b/networks/monero/src/merkle.rs @@ -6,13 +6,12 @@ use crate::primitives::keccak256; /// https://github.com/monero-project/monero/blob/893916ad091a92e765ce3241b94e706ad012b62a /// /src/crypto/tree-hash.c#L62 /// -/// # Panics -/// This function will panic if the tree is empty. -pub fn merkle_root(mut leafs: Vec<[u8; 32]>) -> [u8; 32] { +/// This function returns [`None`] if the tree is empty. +pub fn merkle_root(mut leafs: Vec<[u8; 32]>) -> Option<[u8; 32]> { match leafs.len() { - 0 => panic!("Can't compute Merkle root for empty tree"), - 1 => leafs[0], - 2 => keccak256([leafs[0], leafs[1]].concat()), + 0 => None, + 1 => Some(leafs[0]), + 2 => Some(keccak256([leafs[0], leafs[1]].concat())), _ => { // Monero preprocess this so the length is a power of 2 let mut high_pow_2 = 4; // 4 is the lowest value this can be @@ -52,7 +51,7 @@ pub fn merkle_root(mut leafs: Vec<[u8; 32]>) -> [u8; 32] { leafs = new_hashes; new_hashes = Vec::with_capacity(leafs.len() / 2); } - leafs[0] + Some(leafs[0]) } } }