diff --git a/CHANGELOG.md b/CHANGELOG.md index 38ec56853e..81a42ec3bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Breaking + +- [#726](https://github.com/FuelLabs/fuel-vm/pull/726): Removed code related to Binary Merkle Sum Trees (BMSTs). The BMST is deprecated and not used in production environments. + ## [Version 0.49.0] ### Added diff --git a/fuel-merkle/src/lib.rs b/fuel-merkle/src/lib.rs index 3bb72df028..0380065b0f 100644 --- a/fuel-merkle/src/lib.rs +++ b/fuel-merkle/src/lib.rs @@ -10,7 +10,6 @@ pub mod binary; pub mod common; pub mod sparse; pub mod storage; -pub mod sum; #[cfg(test)] mod tests; diff --git a/fuel-merkle/src/sum.rs b/fuel-merkle/src/sum.rs deleted file mode 100644 index 090fc638fe..0000000000 --- a/fuel-merkle/src/sum.rs +++ /dev/null @@ -1,14 +0,0 @@ -mod hash; -mod merkle_tree; -mod node; - -pub(crate) use hash::{ - empty_sum, - leaf_sum, - node_sum, -}; -pub use merkle_tree::{ - MerkleTree, - MerkleTreeError, -}; -pub(crate) use node::Node; diff --git a/fuel-merkle/src/sum/hash.rs b/fuel-merkle/src/sum/hash.rs deleted file mode 100644 index 5e4a31e8fe..0000000000 --- a/fuel-merkle/src/sum/hash.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::common::{ - self, - Bytes32, - Prefix, -}; - -use digest::Digest; -use sha2::Sha256; - -// Merkle Tree hash of an empty list -// MTH({}) = Hash() -pub const fn empty_sum() -> &'static Bytes32 { - common::empty_sum_sha256() -} - -// Merkle tree hash of an n-element list D[n] -// MTH(D[n]) = Hash(0x01 || LHS fee || MTH(D[0:k]) || RHS fee || MTH(D[k:n]) -pub fn node_sum(lhs_fee: u64, lhs_data: &[u8], rhs_fee: u64, rhs_data: &[u8]) -> Bytes32 { - let mut hash = Sha256::new(); - hash.update(Prefix::Node); - hash.update(lhs_fee.to_be_bytes()); - hash.update(lhs_data); - hash.update(rhs_fee.to_be_bytes()); - hash.update(rhs_data); - hash.finalize().into() -} - -// Merkle tree hash of a list with one entry -// MTH({d(0)}) = Hash(0x00 || fee || d(0)) -pub fn leaf_sum(fee: u64, data: &[u8]) -> Bytes32 { - let mut hash = Sha256::new(); - hash.update(Prefix::Leaf); - hash.update(fee.to_be_bytes()); - hash.update(data); - hash.finalize().into() -} diff --git a/fuel-merkle/src/sum/merkle_tree.rs b/fuel-merkle/src/sum/merkle_tree.rs deleted file mode 100644 index 38d1f62257..0000000000 --- a/fuel-merkle/src/sum/merkle_tree.rs +++ /dev/null @@ -1,317 +0,0 @@ -use crate::{ - common::{ - Bytes32, - Subtree, - }, - sum::{ - empty_sum, - Node, - }, -}; - -use fuel_storage::{ - Mappable, - StorageMutate, -}; - -use core::marker::PhantomData; - -#[derive(Debug, Clone, derive_more::Display)] -pub enum MerkleTreeError { - #[display(fmt = "proof index {_0} is not valid")] - InvalidProofIndex(u64), -} - -/// The Binary Merkle Sum Tree is an extension to the existing Binary -/// [`MerkleTree`](crate::binary::MerkleTree). A node (leaf or internal node) in the tree -/// is defined as having: -/// - a fee (u64, 8 bytes) -/// - a digest (array of bytes) -/// -/// Therefore, a node's data is now a data pair formed by `(fee, digest)`. The data pair -/// of a node with two or more leaves is defined as: -/// -/// (left.fee + right.fee, hash(0x01 ++ left.fee ++ left.digest ++ right.fee ++ -/// right.digest)) -/// -/// This is in contrast to the Binary Merkle Tree node, where a node has only a digest. -/// -/// See the [specification](https://github.com/FuelLabs/fuel-specs/blob/master/src/protocol/cryptographic-primitives.md#merkle-trees) -/// for more details. -/// -/// **Details** -/// -/// When joining subtrees `a` and `b`, the joined subtree is now defined as: -/// -/// fee: a.fee + b.fee -/// data: node_sum(a.fee, a.data, b.fee, b.data) -/// -/// where `node_sum` is defined as the hash function described in the data pair -/// description above. -pub struct MerkleTree { - storage: StorageType, - head: Option>, - phantom_table: PhantomData, -} - -impl MerkleTree { - pub const fn empty_root() -> (u64, Bytes32) { - (0, *empty_sum()) - } -} - -impl MerkleTree -where - TableType: Mappable, - StorageType: StorageMutate, -{ - pub fn new(storage: StorageType) -> Self { - Self { - storage, - head: None, - phantom_table: Default::default(), - } - } - - pub fn root(&mut self) -> Result<(u64, Bytes32), StorageError> { - let root_node = self.root_node()?; - let root_pair = match root_node { - None => Self::empty_root(), - Some(ref node) => (node.fee(), *node.hash()), - }; - - Ok(root_pair) - } - - pub fn push(&mut self, fee: u64, data: &[u8]) -> Result<(), StorageError> { - let node = Node::create_leaf(fee, data); - self.storage.insert(node.hash(), &node)?; - - let next = self.head.take(); - let head = Subtree::::new(node, next); - self.head = Some(head); - self.join_all_subtrees()?; - - Ok(()) - } - - // PRIVATE - // - - fn root_node(&mut self) -> Result, StorageError> { - let root_node = match self.head { - None => None, - Some(ref initial) => { - let mut current = initial.clone(); - while current.next().is_some() { - let mut head = current; - let mut head_next = head.take_next().unwrap(); - current = self.join_subtrees(&mut head_next, &mut head)? - } - Some(current.node().clone()) - } - }; - - Ok(root_node) - } - - fn join_all_subtrees(&mut self) -> Result<(), StorageError> { - loop { - let current = self.head.as_ref().unwrap(); - if !(current.next().is_some() - && current.node().height() == current.next_node().unwrap().height()) - { - break - } - - // Merge the two front nodes of the list into a single node - let joined_node = { - let mut head = self.head.take().unwrap(); - let mut head_next = head.take_next().unwrap(); - self.join_subtrees(&mut head_next, &mut head)? - }; - self.head = Some(joined_node); - } - - Ok(()) - } - - fn join_subtrees( - &mut self, - lhs: &mut Subtree, - rhs: &mut Subtree, - ) -> Result, StorageError> { - let height = lhs.node().height() + 1; - let joined_node = Node::create_node( - height, - lhs.node().fee(), - lhs.node().hash(), - rhs.node().fee(), - rhs.node().hash(), - ); - self.storage.insert(joined_node.hash(), &joined_node)?; - - let joined_head = Subtree::new(joined_node, lhs.take_next()); - - Ok(joined_head) - } -} - -#[cfg(test)] -mod test { - use crate::{ - common::{ - Bytes32, - StorageMap, - }, - sum::{ - leaf_sum, - node_sum, - MerkleTree, - Node, - }, - }; - use fuel_merkle_test_helpers::TEST_DATA; - use fuel_storage::Mappable; - - pub struct TestTable; - - impl Mappable for TestTable { - type Key = Self::OwnedKey; - type OwnedKey = Bytes32; - type OwnedValue = Node; - type Value = Self::OwnedValue; - } - - const FEE: u64 = 100; - - #[test] - fn root_returns_the_hash_of_the_empty_string_when_no_leaves_are_pushed() { - let mut storage_map = StorageMap::::new(); - let mut tree = MerkleTree::new(&mut storage_map); - - let root = tree.root().unwrap(); - assert_eq!(root, MerkleTree::<(), ()>::empty_root()); - } - - #[test] - fn root_returns_the_hash_of_the_leaf_when_one_leaf_is_pushed() { - let mut storage_map = StorageMap::::new(); - let mut tree = MerkleTree::new(&mut storage_map); - - let data = &TEST_DATA[0]; - let _ = tree.push(FEE, data); - let root = tree.root().unwrap(); - - let expected = (FEE, leaf_sum(FEE, data)); - assert_eq!(root, expected); - } - - #[test] - fn root_returns_the_hash_of_the_head_when_4_leaves_are_pushed() { - let mut storage_map = StorageMap::::new(); - let mut tree = MerkleTree::new(&mut storage_map); - - let data = &TEST_DATA[0..4]; // 4 leaves - for datum in data.iter() { - let _ = tree.push(FEE, datum); - } - let root = tree.root().unwrap(); - - // N2 - // / \ - // / \ - // N0 N1 - // / \ / \ - // L0 L1 L2 L3 - - let leaf_0 = leaf_sum(FEE, data[0]); - let leaf_1 = leaf_sum(FEE, data[1]); - let leaf_2 = leaf_sum(FEE, data[2]); - let leaf_3 = leaf_sum(FEE, data[3]); - - let node_0 = node_sum(FEE * 1, &leaf_0, FEE * 1, &leaf_1); - let node_1 = node_sum(FEE * 1, &leaf_2, FEE * 1, &leaf_3); - let node_2 = node_sum(FEE * 2, &node_0, FEE * 2, &node_1); - - let expected = (FEE * 4, node_2); - assert_eq!(root, expected); - } - - #[test] - fn root_returns_the_hash_of_the_head_when_5_leaves_are_pushed() { - let mut storage_map = StorageMap::::new(); - let mut tree = MerkleTree::new(&mut storage_map); - - let data = &TEST_DATA[0..5]; // 5 leaves - for datum in data.iter() { - let _ = tree.push(FEE, datum); - } - let root = tree.root().unwrap(); - - // N3 - // / \ - // N2 \ - // / \ \ - // / \ \ - // N0 N1 \ - // / \ / \ \ - // L0 L1 L2 L3 L4 - - let leaf_0 = leaf_sum(FEE, data[0]); - let leaf_1 = leaf_sum(FEE, data[1]); - let leaf_2 = leaf_sum(FEE, data[2]); - let leaf_3 = leaf_sum(FEE, data[3]); - let leaf_4 = leaf_sum(FEE, data[4]); - - let node_0 = node_sum(FEE * 1, &leaf_0, FEE * 1, &leaf_1); - let node_1 = node_sum(FEE * 1, &leaf_2, FEE * 1, &leaf_3); - let node_2 = node_sum(FEE * 2, &node_0, FEE * 2, &node_1); - let node_3 = node_sum(FEE * 4, &node_2, FEE * 1, &leaf_4); - - let expected = (FEE * 5, node_3); - assert_eq!(root, expected); - } - - #[test] - fn root_returns_the_hash_of_the_head_when_7_leaves_are_pushed() { - let mut storage_map = StorageMap::::new(); - let mut tree = MerkleTree::new(&mut storage_map); - - let data = &TEST_DATA[0..7]; // 7 leaves - for datum in data.iter() { - let _ = tree.push(FEE, datum); - } - let root = tree.root().unwrap(); - - // N5 - // / \ - // / \ - // / \ - // / \ - // N3 N4 - // / \ /\ - // / \ / \ - // N0 N1 N2 \ - // / \ / \ / \ \ - // L0 L1 L2 L3 L4 L5 L6 - - let leaf_0 = leaf_sum(FEE, data[0]); - let leaf_1 = leaf_sum(FEE, data[1]); - let leaf_2 = leaf_sum(FEE, data[2]); - let leaf_3 = leaf_sum(FEE, data[3]); - let leaf_4 = leaf_sum(FEE, data[4]); - let leaf_5 = leaf_sum(FEE, data[5]); - let leaf_6 = leaf_sum(FEE, data[6]); - - let node_0 = node_sum(FEE * 1, &leaf_0, FEE * 1, &leaf_1); - let node_1 = node_sum(FEE * 1, &leaf_2, FEE * 1, &leaf_3); - let node_2 = node_sum(FEE * 1, &leaf_4, FEE * 1, &leaf_5); - let node_3 = node_sum(FEE * 2, &node_0, FEE * 2, &node_1); - let node_4 = node_sum(FEE * 2, &node_2, FEE * 1, &leaf_6); - let node_5 = node_sum(FEE * 4, &node_3, FEE * 3, &node_4); - - let expected = (FEE * 7, node_5); - assert_eq!(root, expected); - } -} diff --git a/fuel-merkle/src/sum/node.rs b/fuel-merkle/src/sum/node.rs deleted file mode 100644 index 1059ec1f4e..0000000000 --- a/fuel-merkle/src/sum/node.rs +++ /dev/null @@ -1,98 +0,0 @@ -use crate::{ - common::Bytes32, - sum::{ - leaf_sum, - node_sum, - }, -}; -use core::fmt; - -#[derive(Clone)] -pub struct Node { - height: u32, - hash: Bytes32, - fee: u64, - left_child_key: Option, - right_child_key: Option, -} - -impl Node { - pub fn create_leaf(fee: u64, data: &[u8]) -> Self { - Self { - height: 0, - hash: leaf_sum(fee, data), - fee, - left_child_key: None, - right_child_key: None, - } - } - - pub fn create_node( - height: u32, - lhs_fee: u64, - lhs_key: &Bytes32, - rhs_fee: u64, - rhs_key: &Bytes32, - ) -> Self { - Self { - height, - hash: node_sum(lhs_fee, lhs_key, rhs_fee, rhs_key), - fee: lhs_fee + rhs_fee, - left_child_key: Some(*lhs_key), - right_child_key: Some(*rhs_key), - } - } - - pub fn height(&self) -> u32 { - self.height - } - - pub fn hash(&self) -> &Bytes32 { - &self.hash - } - - pub fn fee(&self) -> u64 { - self.fee - } - - pub fn left_child_key(&self) -> Option { - self.left_child_key - } - - pub fn right_child_key(&self) -> Option { - self.right_child_key - } - - pub fn is_leaf(&self) -> bool { - self.height == 0 - } - - pub fn is_node(&self) -> bool { - !self.is_leaf() - } -} - -impl fmt::Debug for Node { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.is_node() { - f.debug_struct("Node (Internal)") - .field("Hash", &hex::encode(self.hash())) - .field("Fee", &self.fee) - .field( - "Left child key", - &hex::encode(self.left_child_key().unwrap()), - ) - .field( - "Right child key", - &hex::encode(self.right_child_key().unwrap()), - ) - .finish() - } else { - f.debug_struct("Node (Leaf)") - .field("Hash", &hex::encode(self.hash())) - .field("Fee", &self.fee) - .field("Key", &hex::encode(self.hash())) - .finish() - } - } -}