Skip to content

Commit

Permalink
feat: BMT node buffer and table definition update (#136)
Browse files Browse the repository at this point in the history
* BMT Node buffer

* Refactor buffer, add buffer views

* Simplify Node interface

* Clippy

* Remove unnecessary node clones

* Add safety comments

* Cosmetic

* Return hash as ref

* Replace Schema with constants

* Update safety notes in BMT buffer

* Replace Buffer with Primitive

* Remove Primitive trait

* Refactor primitive

* Simplify

* Make node members private

* Use as ref
  • Loading branch information
Brandon Vrooman authored Jan 13, 2023
1 parent e001e55 commit 73cedea
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 34 deletions.
7 changes: 4 additions & 3 deletions fuel-merkle/src/binary.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
mod hash;
mod merkle_tree;
mod node;
mod primitive;

pub(crate) use hash::empty_sum;
pub(crate) use hash::{leaf_sum, node_sum};
pub(crate) use node::Node;

pub use hash::empty_sum;
pub use merkle_tree::MerkleTree;
pub use merkle_tree::MerkleTreeError;
pub use merkle_tree::{MerkleTree, MerkleTreeError};
pub use primitive::Primitive;
pub mod in_memory;
54 changes: 29 additions & 25 deletions fuel-merkle/src/binary/merkle_tree.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use crate::{
binary::{empty_sum, Node},
binary::{empty_sum, Node, Primitive},
common::{Bytes32, Position, ProofSet, Subtree},
};

use fuel_storage::{Mappable, StorageMutate};

use alloc::{boxed::Box, vec::Vec};
use core::fmt;

#[derive(Debug, Clone)]
#[cfg_attr(feature = "std", derive(thiserror::Error))]
Expand Down Expand Up @@ -44,16 +43,15 @@ pub struct MerkleTree<StorageType> {
pub struct NodesTable;

impl Mappable for NodesTable {
/// The index of the node in the array.
type Key = u64;
type SetValue = Node;
type SetValue = Primitive;
type GetValue = Self::SetValue;
}

impl<StorageType, StorageError> MerkleTree<StorageType>
where
StorageType: StorageMutate<NodesTable, Error = StorageError>,
StorageError: fmt::Debug + Clone + 'static,
StorageError: Clone + 'static,
{
pub fn new(storage: StorageType) -> Self {
Self {
Expand Down Expand Up @@ -101,10 +99,12 @@ where
let root_node = self.root_node()?.unwrap();
let root_position = root_node.position();
let leaf_position = Position::from_leaf_index(proof_index);
let leaf_node = self
let primitive = self
.storage
.get(&leaf_position.in_order_index())?
.ok_or(MerkleTreeError::LoadError(proof_index))?;
.ok_or(MerkleTreeError::LoadError(proof_index))?
.into_owned();
let leaf_node = Node::from(primitive);
proof_set.push(*leaf_node.hash());

let (_, mut side_positions): (Vec<_>, Vec<_>) = root_position
Expand All @@ -116,10 +116,12 @@ where

for side_position in side_positions {
let key = side_position.in_order_index();
let node = self
let primitive = self
.storage
.get(&key)?
.ok_or(MerkleTreeError::LoadError(key))?;
.ok_or(MerkleTreeError::LoadError(key))?
.into_owned();
let node = Node::from(primitive);
proof_set.push(*node.hash());
}

Expand All @@ -129,7 +131,7 @@ where

pub fn push(&mut self, data: &[u8]) -> Result<(), MerkleTreeError<StorageError>> {
let node = Node::create_leaf(self.leaves_count, data);
self.storage.insert(&node.key(), &node)?;
self.storage.insert(&node.key(), &node.as_ref().into())?;
let next = self.head.take();
let head = Box::new(Subtree::<Node>::new(node, next));
self.head = Some(head);
Expand Down Expand Up @@ -234,7 +236,7 @@ where
let leaf_position = Position::from_leaf_index(leaves_count - 1);

// The root position of a tree will always have an in-order index equal
// to N' - 1, where N is the leaves count and N` is N rounded (or equal)
// to N' - 1, where N is the leaves count and N' is N rounded (or equal)
// to the next power of 2.
let root_index = leaves_count.next_power_of_two() - 1;
let root_position = Position::from_in_order_index(root_index);
Expand All @@ -252,7 +254,8 @@ where
.storage
.get(&key)?
.ok_or(MerkleTreeError::LoadError(key))?
.into_owned();
.into_owned()
.into();
let next = Box::new(Subtree::<Node>::new(node, current_head));
current_head = Some(next);
}
Expand Down Expand Up @@ -307,7 +310,8 @@ where
rhs: &mut Subtree<Node>,
) -> Result<Box<Subtree<Node>>, StorageError> {
let joined_node = Node::create_node(lhs.node(), rhs.node());
self.storage.insert(&joined_node.key(), &joined_node)?;
self.storage
.insert(&joined_node.key(), &joined_node.as_ref().into())?;
let joined_head = Subtree::new(joined_node, lhs.take_next());
Ok(Box::new(joined_head))
}
Expand All @@ -317,7 +321,7 @@ where
mod test {
use super::{MerkleTree, MerkleTreeError, NodesTable};
use crate::{
binary::{empty_sum, leaf_sum, node_sum},
binary::{empty_sum, leaf_sum, node_sum, Node},
common::StorageMap,
};
use fuel_merkle_test_helpers::TEST_DATA;
Expand Down Expand Up @@ -374,17 +378,17 @@ mod test {
let s_node_9 = storage_map.get(&9).unwrap().unwrap();
let s_node_3 = storage_map.get(&3).unwrap().unwrap();

assert_eq!(s_leaf_0.hash(), &leaf_0);
assert_eq!(s_leaf_1.hash(), &leaf_1);
assert_eq!(s_leaf_2.hash(), &leaf_2);
assert_eq!(s_leaf_3.hash(), &leaf_3);
assert_eq!(s_leaf_4.hash(), &leaf_4);
assert_eq!(s_leaf_5.hash(), &leaf_5);
assert_eq!(s_leaf_6.hash(), &leaf_6);
assert_eq!(s_node_1.hash(), &node_1);
assert_eq!(s_node_5.hash(), &node_5);
assert_eq!(s_node_9.hash(), &node_9);
assert_eq!(s_node_3.hash(), &node_3);
assert_eq!(*Node::from(s_leaf_0.into_owned()).hash(), leaf_0);
assert_eq!(*Node::from(s_leaf_1.into_owned()).hash(), leaf_1);
assert_eq!(*Node::from(s_leaf_2.into_owned()).hash(), leaf_2);
assert_eq!(*Node::from(s_leaf_3.into_owned()).hash(), leaf_3);
assert_eq!(*Node::from(s_leaf_4.into_owned()).hash(), leaf_4);
assert_eq!(*Node::from(s_leaf_5.into_owned()).hash(), leaf_5);
assert_eq!(*Node::from(s_leaf_6.into_owned()).hash(), leaf_6);
assert_eq!(*Node::from(s_node_1.into_owned()).hash(), node_1);
assert_eq!(*Node::from(s_node_5.into_owned()).hash(), node_5);
assert_eq!(*Node::from(s_node_9.into_owned()).hash(), node_9);
assert_eq!(*Node::from(s_node_3.into_owned()).hash(), node_3);
}

#[test]
Expand Down
16 changes: 14 additions & 2 deletions fuel-merkle/src/binary/node.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::binary::{leaf_sum, node_sum};
use crate::common::{Bytes32, Position};
use crate::{
binary::{leaf_sum, node_sum},
common::{Bytes32, Position},
};

use core::fmt::Debug;

Expand All @@ -10,6 +12,10 @@ pub struct Node {
}

impl Node {
pub fn new(position: Position, hash: Bytes32) -> Self {
Self { position, hash }
}

pub fn create_leaf(index: u64, data: &[u8]) -> Self {
let position = Position::from_leaf_index(index);
let hash = leaf_sum(data);
Expand All @@ -34,3 +40,9 @@ impl Node {
&self.hash
}
}

impl AsRef<Node> for Node {
fn as_ref(&self) -> &Node {
self
}
}
35 changes: 35 additions & 0 deletions fuel-merkle/src/binary/primitive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use crate::{
binary::Node,
common::{Bytes32, Position},
};

pub type Primitive = (u64, Bytes32);

pub trait PrimitiveView {
fn position(&self) -> Position;
fn hash(&self) -> &Bytes32;
}

impl PrimitiveView for Primitive {
fn position(&self) -> Position {
Position::from_in_order_index(self.0)
}

fn hash(&self) -> &Bytes32 {
&self.1
}
}

impl From<&Node> for Primitive {
fn from(node: &Node) -> Self {
(node.position().in_order_index(), *node.hash())
}
}

impl From<Primitive> for Node {
fn from(primitive: Primitive) -> Self {
let position = primitive.position();
let hash = *primitive.hash();
Node::new(position, hash)
}
}
10 changes: 6 additions & 4 deletions fuel-merkle/src/common/path_iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,13 @@ where
Ok(path_node) if path_node.is_node() => {
let path = self.leaf.leaf_key();
let instruction = path.get_instruction(self.current_offset);
self.current = instruction.map(|instruction| match instruction {
Instruction::Left => (path_node.left_child(), path_node.right_child()),
Instruction::Right => (path_node.right_child(), path_node.left_child()),
self.current = instruction.map(|instruction| {
self.current_offset += 1;
match instruction {
Instruction::Left => (path_node.left_child(), path_node.right_child()),
Instruction::Right => (path_node.right_child(), path_node.left_child()),
}
});
self.current_offset += 1;
}
// Terminate the iterator if any of the following are true:
// - The path node is a leaf (traversal is complete)
Expand Down

0 comments on commit 73cedea

Please sign in to comment.