diff --git a/verkle/Cargo.toml b/verkle/Cargo.toml index 9c93c60..e400abd 100644 --- a/verkle/Cargo.toml +++ b/verkle/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -alloy-primitives = { version = "0.7.0", features = ["serde", "ssz"] } +alloy-primitives = { version = "0.7.0", features = ["serde", "ssz", "rand"] } anyhow = "1.0.82" ark-ec = "0.4.2" ark-ed-on-bls12-381-bandersnatch = "0.4.0" @@ -17,9 +17,12 @@ db = { path = "../db" } derive_more = "0.99.17" ethereum_ssz = "0.5.3" ethereum_ssz_derive = "0.5.3" +hex = "0.4.3" once_cell = "1.19.0" sha2 = "0.10.8" ssz_types = "0.6.0" [dev-dependencies] -hex = "0.4.3" +claims = "0.7.1" +rand = "0.8.5" +rstest = "0.19.0" diff --git a/verkle/src/lib.rs b/verkle/src/lib.rs index e9797c4..5aa4dc5 100644 --- a/verkle/src/lib.rs +++ b/verkle/src/lib.rs @@ -1,14 +1,14 @@ use alloy_primitives::{B256, U256}; -use derive_more::{Deref, Index}; +use derive_more::{Constructor, Deref, From, Index}; use banderwagon::Fr; -pub use stem::Stem as TrieStem; +pub use trie::Trie; mod committer; mod constants; pub mod crs; pub mod nodes; -mod stem; +pub mod stem; pub mod trie; mod utils; @@ -16,7 +16,7 @@ pub type TrieValue = U256; type Db = dyn db::Db>; -#[derive(PartialEq, Eq, Clone, Index, Deref)] +#[derive(PartialEq, Eq, Clone, Copy, Constructor, Index, Deref, From)] pub struct TrieKey(B256); impl TrieKey { diff --git a/verkle/src/nodes/branch.rs b/verkle/src/nodes/branch.rs index bbe1f55..6cb2f6a 100644 --- a/verkle/src/nodes/branch.rs +++ b/verkle/src/nodes/branch.rs @@ -64,7 +64,7 @@ impl Default for BranchNode { } impl NodeTrait for BranchNode { - fn commit(&self) -> Fr { + fn commitment(&self) -> Fr { self.cp.map_to_scalar_field() } } @@ -83,7 +83,7 @@ impl Encode for BranchNode { if node.is_empty() { None } else { - Some((index as u8, fr_to_b256(&node.commit()))) + Some((index as u8, fr_to_b256(&node.commitment()))) } }) .collect(); @@ -115,7 +115,7 @@ impl Decode for BranchNode { commitments .get(&i) .map(|c| Node::Commitment(*c)) - .unwrap_or_default() + .unwrap_or_else(|| Node::Empty) }); let cp = DEFAULT_COMMITER.commit_sparse(commitments.into_iter().collect()); diff --git a/verkle/src/nodes/leaf.rs b/verkle/src/nodes/leaf.rs index 460caaa..699a868 100644 --- a/verkle/src/nodes/leaf.rs +++ b/verkle/src/nodes/leaf.rs @@ -8,7 +8,7 @@ use ssz::{Decode, SszDecoderBuilder}; use ssz_derive::Encode; use crate::{ - committer::DEFAULT_COMMITER, constants::VERKLE_NODE_WIDTH, crs::CRS, TrieKey, TrieStem, + committer::DEFAULT_COMMITER, constants::VERKLE_NODE_WIDTH, crs::CRS, stem::Stem, TrieKey, TrieValue, }; @@ -22,7 +22,7 @@ static TWO_POWER_128: Lazy = Lazy::new(|| { #[derive(Index, Encode)] pub struct LeafNode { - stem: TrieStem, + stem: Stem, #[index] values: BTreeMap, @@ -38,7 +38,7 @@ pub struct LeafNode { } impl LeafNode { - pub fn new(stem: TrieStem) -> Self { + pub fn new(stem: Stem) -> Self { let const_c = DEFAULT_COMMITER.commit_sparse(vec![ (0, Fr::one()), (1, Fr::from_le_bytes_mod_order(stem.as_slice())), @@ -59,7 +59,7 @@ impl LeafNode { result } - pub fn stem(&self) -> &TrieStem { + pub fn stem(&self) -> &Stem { &self.stem } @@ -89,7 +89,7 @@ impl LeafNode { Self::value_low_high_16(&old_value) }); - let low_index = (index % VERKLE_NODE_WIDTH) / 2; + let low_index = index % (VERKLE_NODE_WIDTH / 2) * 2; let high_index = low_index + 1; let diff = CRS[low_index] * (value_low_16 - old_value_low_16) @@ -110,42 +110,25 @@ impl LeafNode { } fn value_low_high_16(value: &TrieValue) -> (Fr, Fr) { + let value_as_le_slice = value.as_le_slice(); ( - Fr::from_le_bytes_mod_order(&value.as_le_slice()[0..16]) + TWO_POWER_128.deref(), - Fr::from_le_bytes_mod_order(&value.as_le_slice()[16..32]), + Fr::from_le_bytes_mod_order(&value_as_le_slice[0..16]) + TWO_POWER_128.deref(), + Fr::from_le_bytes_mod_order(&value_as_le_slice[16..32]), ) } } impl NodeTrait for LeafNode { - fn commit(&self) -> Fr { + fn commitment(&self) -> Fr { self.c.unwrap_or_else(|| self.calculate_commitment()) } - fn commit_mut(&mut self) -> Fr { + fn commit(&mut self) -> Fr { self.c = self.c.or_else(|| Some(self.calculate_commitment())); self.c.expect("Value must be present") } } -// impl NodeTrait for LeafNode { -// fn commit(&self) -> Fr { -// self.c.unwrap_or_else(|| { -// let c = self.const_c -// + CRS[2] * self.c1.map_to_scalar_field() -// + CRS[3] * self.c2.map_to_scalar_field(); -// c.map_to_scalar_field() -// }) -// } - -// fn commit_and_save(&mut self, db: &mut Db) -> Fr { -// let c = self.commit(); -// self.c = Some(c); -// db.write(c, self.as_ssz_bytes()).unwrap(); -// c -// } -// } - impl Decode for LeafNode { fn is_ssz_fixed_len() -> bool { false @@ -153,11 +136,11 @@ impl Decode for LeafNode { fn from_ssz_bytes(bytes: &[u8]) -> Result { let mut decoder_builder = SszDecoderBuilder::new(bytes); - decoder_builder.register_type::()?; + decoder_builder.register_type::()?; decoder_builder.register_type::>()?; let mut decoder = decoder_builder.build()?; - let stem = decoder.decode_next::()?; + let stem = decoder.decode_next::()?; let values = decoder.decode_next::>()?; let mut result = Self::new(stem); @@ -165,3 +148,91 @@ impl Decode for LeafNode { Ok(result) } } + +#[cfg(test)] +mod tests { + use alloy_primitives::{B256, U256}; + + use crate::utils::fr_to_b256; + + use super::*; + + #[test] + fn insert_key0_value0() { + let key = TrieKey::new(B256::ZERO); + let mut leaf = LeafNode::new_for_key_value(&key, TrieValue::ZERO); + + assert_eq!( + fr_to_b256(&leaf.commit()).to_string(), + "0x1c0727f0c6c9887189f75a9d08b804aba20892a238e147750767eac22a830d08" + ); + } + + #[test] + fn insert_key1_value1() { + let key = TrieKey::new(U256::from(1).into()); + let mut leaf = LeafNode::new_for_key_value(&key, TrieValue::from(1)); + + assert_eq!( + fr_to_b256(&leaf.commit()).to_string(), + "0x6ef020caaeda01ff573afe6df6460d4aae14b4987e02ea39074f270ce62dfc14" + ); + } + + #[test] + fn insert_increasing() { + let bytes = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, + ]; + let key = TrieKey::new(B256::from(bytes)); + let mut leaf = LeafNode::new_for_key_value(&key, TrieValue::from_le_bytes(bytes)); + + assert_eq!( + fr_to_b256(&leaf.commit()).to_string(), + "0xb897ba52c5317acd75f5f3c3922f461357d4fb8b685fe63f20a3b2adb014370a" + ); + } + + #[test] + fn insert_eoa_with_1eth_balance() { + let stem = Stem::from(&TrieKey::from(B256::from([ + 245, 110, 100, 66, 36, 244, 87, 100, 144, 207, 224, 222, 20, 36, 164, 83, 34, 18, 82, + 155, 254, 55, 71, 19, 216, 78, 125, 126, 142, 146, 114, 0, + ]))); + let values = vec![ + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + ], + [ + 0, 0, 100, 167, 179, 182, 224, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + ], + [ + 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, + 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + ], + ]; + let mut leaf = LeafNode::new(stem); + leaf.set_all( + values + .into_iter() + .enumerate() + .map(|(index, value)| (index as u8, TrieValue::from_le_bytes(value))), + ); + + assert_eq!( + fr_to_b256(&leaf.commit()).to_string(), + "0xcc30be1f0d50eacfacaa3361b8df4d2014a849854a6cf35e6c55e07d6963f519" + ); + } +} diff --git a/verkle/src/nodes/node.rs b/verkle/src/nodes/node.rs index f0f0a56..095b0b5 100644 --- a/verkle/src/nodes/node.rs +++ b/verkle/src/nodes/node.rs @@ -10,16 +10,14 @@ use crate::{Db, TrieKey, TrieValue}; use super::{BranchNode, LeafNode}; pub trait NodeTrait { - fn commit(&self) -> Fr; + fn commitment(&self) -> Fr; - fn commit_mut(&mut self) -> Fr { - self.commit() + fn commit(&mut self) -> Fr { + self.commitment() } } -#[derive(Default)] pub enum Node { - #[default] Empty, Branch(BranchNode), Leaf(LeafNode), @@ -27,20 +25,20 @@ pub enum Node { } impl NodeTrait for Node { - fn commit(&self) -> Fr { + fn commitment(&self) -> Fr { match self { Node::Empty => Fr::zero(), - Node::Branch(branch_node) => branch_node.commit(), - Node::Leaf(leaf_node) => leaf_node.commit(), + Node::Branch(branch_node) => branch_node.commitment(), + Node::Leaf(leaf_node) => leaf_node.commitment(), Node::Commitment(c) => *c, } } - fn commit_mut(&mut self) -> Fr { + fn commit(&mut self) -> Fr { match self { Node::Empty => Fr::zero(), - Node::Branch(branch_node) => branch_node.commit_mut(), - Node::Leaf(leaf_node) => leaf_node.commit_mut(), + Node::Branch(branch_node) => branch_node.commit(), + Node::Leaf(leaf_node) => leaf_node.commit(), Node::Commitment(c) => *c, } } @@ -52,12 +50,12 @@ impl Node { } pub fn check(&self, commitment: &Fr) -> Result<()> { - if &self.commit() == commitment { + if &self.commitment() == commitment { Ok(()) } else { Err(anyhow!( "Node's commitment {:?} doesn't match expected {commitment:?}", - self.commit() + self.commitment() )) } } @@ -102,7 +100,7 @@ impl Node { } else { let mut branch_node = BranchNode::new(); branch_node.set( - key[depth] as usize, + leaf_node.stem()[depth] as usize, Node::Leaf(mem::replace( leaf_node, LeafNode::new(TrieKey(B256::ZERO).stem()), diff --git a/verkle/src/trie.rs b/verkle/src/trie.rs index b965fb5..50a66f6 100644 --- a/verkle/src/trie.rs +++ b/verkle/src/trie.rs @@ -1,8 +1,9 @@ use alloy_primitives::B256; use anyhow::Result; +use banderwagon::Fr; use crate::{ - nodes::Node, + nodes::{BranchNode, Node}, utils::{b256_to_fr, fr_to_b256}, Db, TrieKey, TrieValue, }; @@ -15,7 +16,7 @@ pub struct Trie { impl Trie { pub fn new(db: Box) -> Self { Self { - root: Node::Empty, + root: Node::Branch(BranchNode::new()), db, } } @@ -37,7 +38,147 @@ impl Trie { self.root.insert(0, key, value, self.db.as_ref()) } - pub fn commit(&mut self) -> Result { - Ok(fr_to_b256(&self.root.write_and_commit(self.db.as_mut())?)) + pub fn root(&mut self) -> Result { + Ok(fr_to_b256(&self.root_hash_commitment()?)) + } + + pub fn root_hash_commitment(&mut self) -> Result { + self.root.write_and_commit(self.db.as_mut()) + } +} + +#[cfg(test)] +mod tests { + use alloy_primitives::U256; + use anyhow::Result; + use ark_ff::UniformRand; + use claims::{assert_none, assert_some_eq}; + use db::memory_db::MemoryDb; + use rand::{rngs::StdRng, SeedableRng}; + use rstest::rstest; + + use super::*; + + fn init() -> Trie { + Trie::new(Box::new(MemoryDb::new())) + } + + #[test] + fn empty() -> Result<()> { + let mut trie = init(); + assert_eq!(trie.root()?, B256::ZERO); + Ok(()) + } + + #[test] + fn insert_key0_value0() -> Result<()> { + let mut trie = init(); + let key = TrieKey::new(B256::ZERO); + let value = TrieValue::ZERO; + + assert_none!(trie.get(key)?); + + trie.insert(key, value)?; + assert_some_eq!(trie.get(key)?, value); + + assert_eq!( + trie.root()?.to_string(), + "0xff00a9f3f2d4f58fc23bceebf6b2310419ceac2c30445e2f374e571487715015" + ); + assert_some_eq!(trie.get(key)?, value); + + Ok(()) + } + + #[test] + fn insert_key1_value1() -> Result<()> { + let mut trie = init(); + let key = TrieKey::new(U256::from(1).into()); + let value = TrieValue::from(1); + + assert_none!(trie.get(key)?); + + trie.insert(key, value)?; + assert_some_eq!(trie.get(key)?, value); + + assert_eq!( + trie.root()?.to_string(), + "0x11b55d77cefcb0b1903d6156f3011511a81ec0c838a03a074eba374545b00a06" + ); + assert_some_eq!(trie.get(key)?, value); + + Ok(()) + } + + #[test] + fn insert_keys_0_1() -> Result<()> { + let mut trie = init(); + + let key0 = TrieKey::new(B256::ZERO); + let value0 = TrieValue::ZERO; + let key1 = TrieKey::new(U256::from(1).into()); + let value1 = TrieValue::from(1); + + trie.insert(key0, value0)?; + trie.insert(key1, value1)?; + assert_some_eq!(trie.get(key0)?, value0); + assert_some_eq!(trie.get(key1)?, value1); + + trie.root()?; + assert_some_eq!(trie.get(key0)?, value0); + assert_some_eq!(trie.get(key1)?, value1); + + Ok(()) + } + + #[test] + fn insert_keys_0_max() -> Result<()> { + let mut trie = init(); + + let key0 = TrieKey::new(B256::ZERO); + let value0 = TrieValue::ZERO; + let key_max = TrieKey::new(U256::MAX.into()); + let value_max = TrieValue::MAX; + + trie.insert(key0, value0)?; + trie.insert(key_max, value_max)?; + assert_some_eq!(trie.get(key0)?, value0); + assert_some_eq!(trie.get(key_max)?, value_max); + + trie.root()?; + assert_some_eq!(trie.get(key0)?, value0); + assert_some_eq!(trie.get(key_max)?, value_max); + + Ok(()) + } + + #[rstest] + #[case(12345, 10)] + #[case(12345, 100)] + #[case(12345, 1000)] + fn insert_random(#[case] seed: u64, #[case] count: usize) -> Result<()> { + let mut trie = init(); + + let mut key_values = vec![]; + + let mut rng = StdRng::seed_from_u64(seed); + + while key_values.len() < count { + let key = TrieKey::new(B256::random_with(&mut rng)); + let value = U256::rand(&mut rng); + key_values.push((key, value)); + trie.insert(key, value)?; + } + + for (key, value) in &key_values { + assert_some_eq!(trie.get(*key)?, *value); + } + + trie.root()?; + for (key, value) in key_values { + assert_some_eq!(trie.get(key)?, value); + } + + Ok(()) } } diff --git a/verkle/tests/golang_interop.rs b/verkle/tests/golang_interop.rs new file mode 100644 index 0000000..dc513a3 --- /dev/null +++ b/verkle/tests/golang_interop.rs @@ -0,0 +1,72 @@ +use alloy_primitives::B256; +use banderwagon::{CanonicalDeserialize, Element}; +use db::memory_db::MemoryDb; +use verkle::{Trie, TrieKey, TrieValue}; + +// This is a fixed test, that checks whether the verkle trie logic has changed +// This test is also in the golang code, see: https://github.com/ethereum/go-verkle/blob/f8289fc59149a40673e56f790f6edaec64992294/tree_test.go#L1081 +#[test] +fn golang_rust_interop() { + let mut trie = Trie::new(Box::new(MemoryDb::new())); + let keys = vec![ + [ + 245, 110, 100, 66, 36, 244, 87, 100, 144, 207, 224, 222, 20, 36, 164, 83, 34, 18, 82, + 155, 254, 55, 71, 19, 216, 78, 125, 126, 142, 146, 114, 0, + ], + [ + 245, 110, 100, 66, 36, 244, 87, 100, 144, 207, 224, 222, 20, 36, 164, 83, 34, 18, 82, + 155, 254, 55, 71, 19, 216, 78, 125, 126, 142, 146, 114, 1, + ], + [ + 245, 110, 100, 66, 36, 244, 87, 100, 144, 207, 224, 222, 20, 36, 164, 83, 34, 18, 82, + 155, 254, 55, 71, 19, 216, 78, 125, 126, 142, 146, 114, 2, + ], + [ + 245, 110, 100, 66, 36, 244, 87, 100, 144, 207, 224, 222, 20, 36, 164, 83, 34, 18, 82, + 155, 254, 55, 71, 19, 216, 78, 125, 126, 142, 146, 114, 3, + ], + [ + 245, 110, 100, 66, 36, 244, 87, 100, 144, 207, 224, 222, 20, 36, 164, 83, 34, 18, 82, + 155, 254, 55, 71, 19, 216, 78, 125, 126, 142, 146, 114, 4, + ], + ]; + + let values = vec![ + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + [ + 0, 0, 100, 167, 179, 182, 224, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + [ + 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, + 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + ]; + for (key, value) in keys.into_iter().zip(values) { + trie.insert( + TrieKey::new(B256::from(key)), + TrieValue::from_le_bytes(value), + ) + .unwrap(); + } + + let root = trie.root_hash_commitment().unwrap(); + + let expected_commitment = "10ed89d89047bb168baa4e69b8607e260049e928ddbcb2fdd23ea0f4182b1f8a"; + let expected_hash_commitment = + Element::deserialize_compressed(hex::decode(expected_commitment).unwrap().as_slice()) + .unwrap(); + + assert_eq!(root, expected_hash_commitment.map_to_scalar_field()); +}