From 6a2653dfbda60c9e254c8979a387d8c45afeb2f2 Mon Sep 17 00:00:00 2001 From: Georgy Shepelev Date: Thu, 15 Aug 2024 10:32:29 +0400 Subject: [PATCH 1/4] add merkle patricia trie to ethereum-common --- Cargo.lock | 100 +++++++-- Cargo.toml | 1 + ethereum-common/Cargo.toml | 19 ++ ethereum-common/src/beacon/tests.rs | 160 ++++++++++++++ ethereum-common/src/keccak_hasher.rs | 24 +++ ethereum-common/src/lib.rs | 13 +- ethereum-common/src/patricia_trie.rs | 136 ++++++++++++ ethereum-common/src/rlp_node_codec.rs | 291 ++++++++++++++++++++++++++ ethereum-common/src/utils.rs | 35 ++++ 9 files changed, 765 insertions(+), 14 deletions(-) create mode 100644 ethereum-common/src/keccak_hasher.rs create mode 100644 ethereum-common/src/patricia_trie.rs create mode 100644 ethereum-common/src/rlp_node_codec.rs diff --git a/Cargo.lock b/Cargo.lock index 66bd9a7a..b98e8a3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1576,7 +1576,7 @@ name = "binary-merkle-tree" version = "4.0.0-dev" source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-v1.4.0#09bdd2a6953d057ae360ec3ef6ec735f9306cc04" dependencies = [ - "hash-db", + "hash-db 0.16.0", "log", ] @@ -3455,18 +3455,28 @@ dependencies = [ name = "ethereum-common" version = "0.1.0" dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", "bitvec", "derive_more", "ethereum-types", + "hash-db 0.15.2", + "hash256-std-hasher", "hex", "hex-literal", + "memory-db 0.27.0", "parity-scale-codec", "ring 0.17.8 (git+https://github.com/gear-tech/ring.git?branch=gear-v0.17.8)", + "rlp", "scale-info", "serde", "serde_json", + "tiny-keccak", "tree_hash", "tree_hash_derive", + "trie-db 0.22.6", ] [[package]] @@ -4391,7 +4401,7 @@ dependencies = [ "sp-runtime", "sp-trie", "subxt", - "trie-db", + "trie-db 0.28.0", ] [[package]] @@ -4816,6 +4826,12 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" +[[package]] +name = "hash-db" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" + [[package]] name = "hash-db" version = "0.16.0" @@ -4831,6 +4847,15 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash 0.7.8", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -6494,13 +6519,24 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memory-db" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de006e09d04fc301a5f7e817b75aa49801c4479a8af753764416b085337ddcc5" +dependencies = [ + "hash-db 0.15.2", + "hashbrown 0.11.2", + "parity-util-mem", +] + [[package]] name = "memory-db" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe" dependencies = [ - "hash-db", + "hash-db 0.16.0", ] [[package]] @@ -7459,6 +7495,31 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9777aa91b8ad9dd5aaa04a9b6bcb02c7f1deb952fca5a66034d5e63afc5c6f" +[[package]] +name = "parity-util-mem" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f4cb4e169446179cbc6b8b6320cc9fca49bd2e94e8db25f25f200a8ea774770" +dependencies = [ + "cfg-if", + "hashbrown 0.11.2", + "impl-trait-for-tuples", + "parity-util-mem-derive", + "parking_lot 0.11.2", + "winapi", +] + +[[package]] +name = "parity-util-mem-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" +dependencies = [ + "proc-macro2", + "syn 1.0.109", + "synstructure", +] + [[package]] name = "parity-wasm" version = "0.45.0" @@ -8201,7 +8262,7 @@ dependencies = [ "sp-core", "sp-trie", "static_assertions", - "trie-db", + "trie-db 0.28.0", "unroll", ] @@ -10183,7 +10244,7 @@ name = "sp-api" version = "4.0.0-dev" source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-v1.4.0#09bdd2a6953d057ae360ec3ef6ec735f9306cc04" dependencies = [ - "hash-db", + "hash-db 0.16.0", "log", "parity-scale-codec", "scale-info", @@ -10366,7 +10427,7 @@ dependencies = [ "dyn-clonable", "ed25519-zebra", "futures", - "hash-db", + "hash-db 0.16.0", "hash256-std-hasher", "impl-serde", "itertools 0.10.5", @@ -10752,7 +10813,7 @@ name = "sp-state-machine" version = "0.28.0" source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-v1.4.0#09bdd2a6953d057ae360ec3ef6ec735f9306cc04" dependencies = [ - "hash-db", + "hash-db 0.16.0", "log", "parity-scale-codec", "parking_lot 0.12.3", @@ -10765,7 +10826,7 @@ dependencies = [ "sp-trie", "thiserror", "tracing", - "trie-db", + "trie-db 0.28.0", ] [[package]] @@ -10875,10 +10936,10 @@ version = "22.0.0" source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-v1.4.0#09bdd2a6953d057ae360ec3ef6ec735f9306cc04" dependencies = [ "ahash 0.8.11", - "hash-db", + "hash-db 0.16.0", "hashbrown 0.13.2", "lazy_static", - "memory-db", + "memory-db 0.32.0", "nohash-hasher", "parity-scale-codec", "parking_lot 0.12.3", @@ -10889,7 +10950,7 @@ dependencies = [ "sp-std 8.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-v1.4.0)", "thiserror", "tracing", - "trie-db", + "trie-db 0.28.0", "trie-root", ] @@ -11729,13 +11790,26 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "trie-db" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eac131e334e81b6b3be07399482042838adcd7957aa0010231d0813e39e02fa" +dependencies = [ + "hash-db 0.15.2", + "hashbrown 0.11.2", + "log", + "rustc-hex", + "smallvec", +] + [[package]] name = "trie-db" version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff28e0f815c2fea41ebddf148e008b077d2faddb026c9555b29696114d602642" dependencies = [ - "hash-db", + "hash-db 0.16.0", "hashbrown 0.13.2", "log", "rustc-hex", @@ -11748,7 +11822,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4ed310ef5ab98f5fa467900ed906cb9232dd5376597e00fd4cba2a449d06c0b" dependencies = [ - "hash-db", + "hash-db 0.16.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ebe800a5..63f94240 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,6 +65,7 @@ env_logger = "0.9.0" ethereum-types = { version = "0.14.1", default-features = false, features = [ "serialize", "codec", + "rlp", ] } ff = { version = "0.13.0", features = ["derive"] } futures = { version = "0.3.30", features = ["executor"] } diff --git a/ethereum-common/Cargo.toml b/ethereum-common/Cargo.toml index 6bed2cd3..1fbc7109 100644 --- a/ethereum-common/Cargo.toml +++ b/ethereum-common/Cargo.toml @@ -16,6 +16,16 @@ bitvec.workspace = true scale-info = { workspace = true, features = ["bit-vec"] } hex-literal.workspace = true ring.workspace = true +hash-db = { version = "0.15.2", default-features = false } +hash256-std-hasher = { version = "0.15.2", default-features = false } +tiny-keccak = { version = "2.0.2", features = ["keccak"] } +rlp = { version = "0.5.2", default-features = false } +trie-db = { version = "0.22.6", default-features = false } +memory-db = { version = "0.27.0", default-features = false } +alloy-consensus = { version = "0.2.1", default-features = false } +alloy-eips = { version = "0.2.1", default-features = false } +alloy-rlp = { version = "0.3.8", default-features = false } +alloy-primitives = { version = "0.7.7", default-features = false } [dev-dependencies] serde_json.workspace = true @@ -31,4 +41,13 @@ std = [ "bitvec/std", "scale-info/std", "ring/std", + "hash-db/std", + "hash256-std-hasher/std", + "rlp/std", + "trie-db/std", + "memory-db/std", + "alloy-consensus/std", + "alloy-eips/std", + "alloy-rlp/std", + "alloy-primitives/std", ] diff --git a/ethereum-common/src/beacon/tests.rs b/ethereum-common/src/beacon/tests.rs index 44204524..9cbdfe5e 100644 --- a/ethereum-common/src/beacon/tests.rs +++ b/ethereum-common/src/beacon/tests.rs @@ -1,5 +1,9 @@ use super::*; +use alloy_consensus::{Receipt, ReceiptEnvelope, ReceiptWithBloom}; +use alloy_primitives::Log; use hex_literal::hex; +use patricia_trie::TrieDBMut; +use trie_db::TrieMut; const ETHEREUM_9_230_177: &[u8; 133_287] = include_bytes!("./chain-data/ethereum-9_230_177.json"); const SEPOLIA_5_151_035: &[u8; 10_722] = include_bytes!("./chain-data/sepolia-5_151_035.json"); @@ -106,3 +110,159 @@ fn holesky_slot_1_820_966() { assert_eq!(block_root, block.tree_hash_root()); } + +#[test] +fn patricia_trie_root_simple() { + let root_expected = hex!("056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2"); + + let receipt = ReceiptWithBloom::new( + Receipt:: { + status: true.into(), + // 0x5208 + cumulative_gas_used: 21_000, + logs: vec![], + }, + Default::default(), + ); + + let receipt = ReceiptEnvelope::Legacy(receipt); + + let (key, value) = utils::rlp_encode_index_and_receipt(&0, &receipt); + + let root = { + let mut memdb = new_memory_db(); + let mut root = H256::default(); + let mut triedbmut = TrieDBMut::new(&mut memdb, &mut root); + triedbmut.insert(&key, &value).unwrap(); + + *triedbmut.root() + }; + + assert_eq!(root.as_ref(), root_expected); + + // wrong index + let (key, value) = utils::rlp_encode_index_and_receipt(&1, &receipt); + + let root = { + let mut memdb = new_memory_db(); + let mut root = H256::default(); + let mut triedbmut = TrieDBMut::new(&mut memdb, &mut root); + triedbmut.insert(&key, &value).unwrap(); + + *triedbmut.root() + }; + + assert_ne!(root.as_ref(), root_expected); + + // wrong status + let receipt = ReceiptWithBloom::new( + Receipt:: { + status: false.into(), + // 0x5208 + cumulative_gas_used: 21_000, + logs: vec![], + }, + Default::default(), + ); + + let receipt = ReceiptEnvelope::Legacy(receipt); + + let (key, value) = utils::rlp_encode_index_and_receipt(&0, &receipt); + + let root = { + let mut memdb = new_memory_db(); + let mut root = H256::default(); + let mut triedbmut = TrieDBMut::new(&mut memdb, &mut root); + triedbmut.insert(&key, &value).unwrap(); + + *triedbmut.root() + }; + + assert_ne!(root.as_ref(), root_expected); +} + +// Test data source: https://github.com/ethereum/go-ethereum/blob/2b9d19870646140c4dc90645cf7c828cc76860aa/cmd/evm/testdata/30/exp.json +#[test] +fn dynamic_fee_transaction_type() { + let root_expected = hex!("75308898d571eafb5cd8cde8278bf5b3d13c5f6ec074926de3bb895b519264e1"); + + let receipt1 = ReceiptEnvelope::Eip1559(ReceiptWithBloom::new( + Receipt:: { + status: true.into(), + // 0x5208 + cumulative_gas_used: 21_000, + logs: vec![], + }, + Default::default(), + )); + + let receipt2 = ReceiptEnvelope::Eip1559(ReceiptWithBloom::new( + Receipt:: { + status: true.into(), + // 0xA410 + cumulative_gas_used: 42_000, + logs: vec![], + }, + Default::default(), + )); + + let encoded = utils::rlp_encode_receipts_and_nibble_tuples(&[ + (0, receipt1.clone()), + (1, receipt2.clone()), + ]); + + let root = { + let mut memdb = new_memory_db(); + let mut root = H256::default(); + let mut triedbmut = TrieDBMut::new(&mut memdb, &mut root); + for (key, value) in encoded { + triedbmut.insert(&key, &value).unwrap(); + } + + *triedbmut.root() + }; + + assert_eq!(root.as_ref(), root_expected); + + // wrong index + let encoded = + utils::rlp_encode_receipts_and_nibble_tuples(&[(11, receipt1.clone()), (1, receipt2)]); + + let root = { + let mut memdb = new_memory_db(); + let mut root = H256::default(); + let mut triedbmut = TrieDBMut::new(&mut memdb, &mut root); + for (key, value) in encoded { + triedbmut.insert(&key, &value).unwrap(); + } + + *triedbmut.root() + }; + + assert_ne!(root.as_ref(), root_expected); + + // wrong cumulative gas used of receipt2 + let receipt2 = ReceiptEnvelope::Eip1559(ReceiptWithBloom::new( + Receipt:: { + status: true.into(), + cumulative_gas_used: 42_001, + logs: vec![], + }, + Default::default(), + )); + + let encoded = utils::rlp_encode_receipts_and_nibble_tuples(&[(0, receipt1), (1, receipt2)]); + + let root = { + let mut memdb = new_memory_db(); + let mut root = H256::default(); + let mut triedbmut = TrieDBMut::new(&mut memdb, &mut root); + for (key, value) in encoded { + triedbmut.insert(&key, &value).unwrap(); + } + + *triedbmut.root() + }; + + assert_ne!(root.as_ref(), root_expected); +} diff --git a/ethereum-common/src/keccak_hasher.rs b/ethereum-common/src/keccak_hasher.rs new file mode 100644 index 00000000..af6ef38f --- /dev/null +++ b/ethereum-common/src/keccak_hasher.rs @@ -0,0 +1,24 @@ +use ethereum_types::H256; +use hash256_std_hasher::Hash256StdHasher; +use hash_db::Hasher; +use tiny_keccak::{Hasher as _, Keccak}; + +/// Concrete `Hasher` implementation for the Keccak-256 hash +#[derive(Default, Debug, Clone, PartialEq)] +pub struct KeccakHasher; + +impl Hasher for KeccakHasher { + type Out = H256; + type StdHasher = Hash256StdHasher; + const LENGTH: usize = 32; + + fn hash(x: &[u8]) -> Self::Out { + let mut out = [0; 32]; + + let mut hasher = Keccak::v256(); + hasher.update(x); + hasher.finalize(&mut out); + + out.into() + } +} diff --git a/ethereum-common/src/lib.rs b/ethereum-common/src/lib.rs index c9095850..277d5466 100644 --- a/ethereum-common/src/lib.rs +++ b/ethereum-common/src/lib.rs @@ -2,8 +2,11 @@ pub mod base_types; pub mod beacon; +pub mod keccak_hasher; pub mod merkle; pub mod network; +pub mod patricia_trie; +pub mod rlp_node_codec; pub mod utils; #[cfg(not(feature = "std"))] @@ -19,13 +22,21 @@ use core::{ slice::{self, SliceIndex}, }; -pub use ethereum_types::U256; +pub use ethereum_types::{H256, U256}; +use keccak_hasher::KeccakHasher; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use serde::{de, Deserialize}; pub use tree_hash::{self, Hash256}; use tree_hash::{TreeHash, TreeHashType}; +pub use trie_db; pub const SLOTS_PER_EPOCH: u64 = 32; pub const EPOCHS_PER_SYNC_COMMITTEE: u64 = 256; pub const SYNC_COMMITTEE_SIZE: usize = 512; + +pub type MemoryDB = memory_db::MemoryDB, Vec>; + +pub fn new_memory_db() -> MemoryDB { + memory_db::MemoryDB::from_null_node(&rlp::NULL_RLP, rlp::NULL_RLP.as_ref().into()) +} diff --git a/ethereum-common/src/patricia_trie.rs b/ethereum-common/src/patricia_trie.rs new file mode 100644 index 00000000..1a14c59d --- /dev/null +++ b/ethereum-common/src/patricia_trie.rs @@ -0,0 +1,136 @@ +// Inspired by the Parity Ethereum project. + +pub use crate::{keccak_hasher::KeccakHasher, rlp_node_codec::RlpNodeCodec}; +use ethereum_types::H256; +use rlp::DecoderError; + +/// Convenience type alias to instantiate a Keccak-flavoured `RlpNodeCodec` +pub type RlpCodec = RlpNodeCodec; + +#[derive(Clone, Default)] +/// Defines the working of a particular flavour of trie: +/// how keys are hashed, how values are encoded, does it use extension nodes or not. +pub struct Layout; + +impl trie_db::TrieLayout for Layout { + const USE_EXTENSION: bool = true; + type Hash = KeccakHasher; + type Codec = RlpNodeCodec; +} + +/// Convenience type alias to instantiate a Keccak/Rlp-flavoured `TrieDB` +/// +/// Use it as a `Trie` trait object. You can use `db()` to get the backing database object. +/// Use `get` and `contains` to query values associated with keys in the trie. +/// +/// # Example +/// ``` +/// use hash_db::*; +/// use memory_db::*; +/// use trie_db::*; +/// use ethereum_types::H256; +/// use ethereum_common::patricia_trie::{TrieDB, TrieDBMut}; +/// +/// let mut memdb = ethereum_common::new_memory_db(); +/// let mut root = H256::zero(); +/// TrieDBMut::new(&mut memdb, &mut root).insert(b"foo", b"bar").unwrap(); +/// let t = TrieDB::new(&memdb, &root).unwrap(); +/// assert!(t.contains(b"foo").unwrap()); +/// assert_eq!(t.get(b"foo").unwrap().unwrap(), b"bar".to_vec()); +/// ``` +pub type TrieDB<'db> = trie_db::TrieDB<'db, Layout>; + +/// Convenience type alias to instantiate a Keccak/Rlp-flavoured `SecTrieDB` +pub type SecTrieDB<'db> = trie_db::SecTrieDB<'db, Layout>; + +/// Convenience type alias to instantiate a Keccak/Rlp-flavoured `FatDB` +pub type FatDB<'db> = trie_db::FatDB<'db, Layout>; + +/// Convenience type alias to instantiate a Keccak/Rlp-flavoured `TrieDBMut` +/// +/// Use it as a `TrieMut` trait object. You can use `db()` to get the backing database object. +/// Note that changes are not committed to the database until `commit` is called. +/// Querying the root or dropping the trie will commit automatically. + +/// # Example +/// ``` +/// use hash_db::*; +/// use memory_db::*; +/// use trie_db::*; +/// use ethereum_types::H256; +/// use ethereum_common::{patricia_trie::{TrieDB, TrieDBMut}, rlp_node_codec}; +/// +/// let mut memdb = ethereum_common::new_memory_db(); +/// let mut root = H256::zero(); +/// let mut t = TrieDBMut::new(&mut memdb, &mut root); +/// assert!(t.is_empty()); +/// assert_eq!(*t.root(), rlp_node_codec::HASHED_NULL_NODE); +/// t.insert(b"foo", b"bar").unwrap(); +/// assert!(t.contains(b"foo").unwrap()); +/// assert_eq!(t.get(b"foo").unwrap().unwrap(), b"bar".to_vec()); +/// t.remove(b"foo").unwrap(); +/// assert!(!t.contains(b"foo").unwrap()); +/// ``` +pub type TrieDBMut<'db> = trie_db::TrieDBMut<'db, Layout>; + +/// Convenience type alias to instantiate a Keccak/Rlp-flavoured `SecTrieDBMut` +pub type SecTrieDBMut<'db> = trie_db::SecTrieDBMut<'db, Layout>; + +/// Convenience type alias to instantiate a Keccak/Rlp-flavoured `FatDBMut` +pub type FatDBMut<'db> = trie_db::FatDBMut<'db, Layout>; + +/// Convenience type alias to instantiate a Keccak/Rlp-flavoured `TrieFactory` +pub type TrieFactory = trie_db::TrieFactory; + +/// Convenience type alias for Keccak/Rlp flavoured trie errors +pub type TrieError = trie_db::TrieError; +/// Convenience type alias for Keccak/Rlp flavoured trie results +pub type Result = trie_db::Result; + +#[cfg(test)] +mod tests { + use super::*; + use ethereum_types::H256; + use memory_db::{HashKey, MemoryDB}; + use trie_db::{Trie, TrieMut}; + + #[test] + fn test_inline_encoding_branch() { + let mut memdb = MemoryDB::, Vec>::from_null_node( + &rlp::NULL_RLP, + rlp::NULL_RLP.as_ref().into(), + ); + let mut root = H256::zero(); + { + let mut triedbmut = TrieDBMut::new(&mut memdb, &mut root); + triedbmut.insert(b"foo", b"bar").unwrap(); + triedbmut.insert(b"fog", b"b").unwrap(); + triedbmut.insert(b"fot", &vec![0u8; 33][..]).unwrap(); + } + let t = TrieDB::new(&memdb, &root).unwrap(); + assert!(t.contains(b"foo").unwrap()); + assert!(t.contains(b"fog").unwrap()); + assert_eq!(t.get(b"foo").unwrap().unwrap(), b"bar".to_vec()); + assert_eq!(t.get(b"fog").unwrap().unwrap(), b"b".to_vec()); + assert_eq!(t.get(b"fot").unwrap().unwrap(), vec![0u8; 33]); + } + + #[test] + fn test_inline_encoding_extension() { + let mut memdb = MemoryDB::, Vec>::from_null_node( + &rlp::NULL_RLP, + rlp::NULL_RLP.as_ref().into(), + ); + let mut root = H256::zero(); + { + let mut triedbmut = TrieDBMut::new(&mut memdb, &mut root); + triedbmut.insert(b"foo", b"b").unwrap(); + triedbmut.insert(b"fog", b"a").unwrap(); + } + let t = TrieDB::new(&memdb, &root).unwrap(); + assert!(t.contains(b"foo").unwrap()); + assert!(t.contains(b"fog").unwrap()); + assert_eq!(t.get(b"foo").unwrap().unwrap(), b"b".to_vec()); + assert_eq!(t.get(b"fog").unwrap().unwrap(), b"a".to_vec()); + } +} diff --git a/ethereum-common/src/rlp_node_codec.rs b/ethereum-common/src/rlp_node_codec.rs new file mode 100644 index 00000000..dbda6c0c --- /dev/null +++ b/ethereum-common/src/rlp_node_codec.rs @@ -0,0 +1,291 @@ +//! `NodeCodec` implementation for Rlp + +// Inspired by the Parity Ethereum project. + +#[cfg(not(feature = "std"))] +extern crate alloc; + +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; + +use crate::keccak_hasher::KeccakHasher; +use core::{borrow::Borrow, marker::PhantomData, ops::Range}; +use ethereum_types::H256; +use hash_db::Hasher; +use rlp::{DecoderError, Prototype, Rlp, RlpStream}; +use trie_db::{ + node::{NibbleSlicePlan, NodeHandlePlan, NodePlan}, + ChildReference, NodeCodec, Partial, +}; + +/// Concrete implementation of a `NodeCodec` with Rlp encoding, generic over the `Hasher` +#[derive(Default, Clone)] +pub struct RlpNodeCodec { + mark: PhantomData, +} + +pub const HASHED_NULL_NODE_BYTES: [u8; 32] = [ + 0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6, 0xff, 0x83, 0x45, 0xe6, 0x92, 0xc0, 0xf8, 0x6e, + 0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0, 0x01, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21, +]; +pub const HASHED_NULL_NODE: H256 = H256(HASHED_NULL_NODE_BYTES); + +/// Encode a partial value with a partial tuple as input. +fn encode_partial_iter(partial: Partial<'_>, is_leaf: bool) -> impl Iterator + '_ { + encode_partial_inner_iter( + (partial.0).1, + partial.1.iter().copied(), + (partial.0).0 > 0, + is_leaf, + ) +} + +/// Encode a partial value with an iterator as input. +fn encode_partial_from_iterator_iter<'a>( + mut partial: impl Iterator + 'a, + odd: bool, + is_leaf: bool, +) -> impl Iterator + 'a { + let first = if odd { partial.next().unwrap_or(0) } else { 0 }; + encode_partial_inner_iter(first, partial, odd, is_leaf) +} + +/// Encode a partial value with an iterator as input. +fn encode_partial_inner_iter<'a>( + first_byte: u8, + partial_remaining: impl Iterator + 'a, + odd: bool, + is_leaf: bool, +) -> impl Iterator + 'a { + let encoded_type = if is_leaf { 0x20 } else { 0 }; + let first = if odd { + 0x10 + encoded_type + first_byte + } else { + encoded_type + }; + + core::iter::once(first).chain(partial_remaining) +} + +fn decode_value_range(rlp: Rlp, mut offset: usize) -> Result, DecoderError> { + let payload = rlp.payload_info()?; + offset += payload.header_len; + Ok(offset..(offset + payload.value_len)) +} + +fn decode_child_handle_plan( + child_rlp: Rlp, + mut offset: usize, +) -> Result { + Ok(if child_rlp.is_data() && child_rlp.size() == H::LENGTH { + let payload = child_rlp.payload_info()?; + offset += payload.header_len; + NodeHandlePlan::Hash(offset..(offset + payload.value_len)) + } else { + NodeHandlePlan::Inline(offset..(offset + child_rlp.as_raw().len())) + }) +} + +impl NodeCodec for RlpNodeCodec { + type Error = DecoderError; + type HashOut = ::Out; + + fn hashed_null_node() -> ::Out { + HASHED_NULL_NODE + } + + fn decode_plan(data: &[u8]) -> Result { + let r = Rlp::new(data); + match r.prototype()? { + // either leaf or extension - decode first item with NibbleSlice::??? + // and use is_leaf return to figure out which. + // if leaf, second item is a value (is_data()) + // if extension, second item is a node (either SHA3 to be looked up and + // fed back into this function or inline RLP which can be fed back into this function). + Prototype::List(2) => { + let (partial_rlp, mut partial_offset) = r.at_with_offset(0)?; + let partial_payload = partial_rlp.payload_info()?; + partial_offset += partial_payload.header_len; + + let (partial, is_leaf) = if partial_rlp.is_empty() { + ( + NibbleSlicePlan::new(partial_offset..partial_offset, 0), + false, + ) + } else { + let partial_header = partial_rlp.data()?[0]; + // check leaf bit from header. + let is_leaf = partial_header & 32 == 32; + // Check the header bit to see if we're dealing with an odd partial (only a nibble of header info) + // or an even partial (skip a full byte). + let (start, byte_offset) = if partial_header & 16 == 16 { + (0, 1) + } else { + (1, 0) + }; + let range = + (partial_offset + start)..(partial_offset + partial_payload.value_len); + (NibbleSlicePlan::new(range, byte_offset), is_leaf) + }; + + let (value_rlp, value_offset) = r.at_with_offset(1)?; + Ok(if is_leaf { + let value = decode_value_range(value_rlp, value_offset)?; + NodePlan::Leaf { partial, value } + } else { + let child = decode_child_handle_plan::(value_rlp, value_offset)?; + NodePlan::Extension { partial, child } + }) + } + // branch - first 16 are nodes, 17th is a value (or empty). + Prototype::List(17) => { + let mut children = [ + None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, + ]; + for (i, child) in children.iter_mut().enumerate() { + let (child_rlp, child_offset) = r.at_with_offset(i)?; + if !child_rlp.is_empty() { + *child = Some(decode_child_handle_plan::( + child_rlp, + child_offset, + )?); + } + } + let (value_rlp, value_offset) = r.at_with_offset(16)?; + let value = if value_rlp.is_empty() { + None + } else { + Some(decode_value_range(value_rlp, value_offset)?) + }; + Ok(NodePlan::Branch { value, children }) + } + // an empty branch index. + Prototype::Data(0) => Ok(NodePlan::Empty), + // something went wrong. + _ => Err(DecoderError::Custom("Rlp is not valid.")), + } + } + + fn is_empty_node(data: &[u8]) -> bool { + Rlp::new(data).is_empty() + } + + fn empty_node() -> &'static [u8] { + &[0x80] + } + + fn leaf_node(partial: Partial, value: &[u8]) -> Vec { + let mut stream = RlpStream::new_list(2); + stream.append_iter(encode_partial_iter(partial, true)); + stream.append(&value); + stream.out().to_vec() + } + + fn extension_node( + partial: impl Iterator, + number_nibble: usize, + child_ref: ChildReference<::Out>, + ) -> Vec { + let mut stream = RlpStream::new_list(2); + stream.append_iter(encode_partial_from_iterator_iter( + partial, + number_nibble % 2 > 0, + false, + )); + match child_ref { + ChildReference::Hash(hash) => stream.append(&hash), + ChildReference::Inline(inline_data, length) => { + let bytes = &AsRef::<[u8]>::as_ref(&inline_data)[..length]; + stream.append_raw(bytes, 1) + } + }; + stream.out().to_vec() + } + + fn branch_node( + children: impl Iterator< + Item = impl Borrow::Out>>>, + >, + maybe_value: Option<&[u8]>, + ) -> Vec { + let mut stream = RlpStream::new_list(17); + for child_ref in children { + match child_ref.borrow() { + Some(c) => match c { + ChildReference::Hash(h) => stream.append(h), + ChildReference::Inline(inline_data, length) => { + let bytes = &AsRef::<[u8]>::as_ref(inline_data)[..*length]; + stream.append_raw(bytes, 1) + } + }, + None => stream.append_empty_data(), + }; + } + if let Some(value) = maybe_value { + stream.append(&value); + } else { + stream.append_empty_data(); + } + stream.out().to_vec() + } + + fn branch_node_nibbled( + _partial: impl Iterator, + _number_nibble: usize, + _children: impl Iterator< + Item = impl Borrow::Out>>>, + >, + _maybe_value: Option<&[u8]>, + ) -> Vec { + unreachable!("This codec is only used with a trie Layout that uses extension node.") + } +} + +#[cfg(test)] +mod tests { + use super::RlpNodeCodec; + use rlp::RlpStream; + use trie_db::{ + node::{Node, NodeHandle}, + NibbleSlice, NodeCodec, + }; + + #[test] + fn decode_leaf() { + let mut stream = RlpStream::new_list(2); + stream.append(&"cat").append(&"dog"); + let data = stream.out(); + let r = RlpNodeCodec::decode(&data); + assert!(r.is_ok()); + // "c" & 16 != 16 => `start` == 1 + let cat_nib = NibbleSlice::new(&b"at"[..]); + assert_eq!(r.unwrap(), Node::Leaf(cat_nib, &b"dog"[..])); + } + + #[test] + fn decode_ext() { + let mut stream = RlpStream::new_list(2); + let payload = vec![0x1, 0x2, 0x3u8]; + stream.append(&"").append(&payload); + let data = stream.out(); + let decoded = RlpNodeCodec::decode(&data); + + assert!(decoded.is_ok()); + assert_eq!( + decoded.unwrap(), + Node::Extension( + NibbleSlice::new(&[]), + NodeHandle::Inline(&[0x80 + 0x3, 0x1, 0x2, 0x3]) + ) + ); + } + + #[test] + fn decode_empty_data() { + let mut stream = RlpStream::new(); + stream.append_empty_data(); + let data = stream.out(); + assert_eq!(RlpNodeCodec::decode(&data), Ok(Node::Empty),); + } +} diff --git a/ethereum-common/src/utils.rs b/ethereum-common/src/utils.rs index e70cb231..85117da6 100644 --- a/ethereum-common/src/utils.rs +++ b/ethereum-common/src/utils.rs @@ -1,6 +1,15 @@ use super::*; +use alloy_consensus::ReceiptEnvelope; +use alloy_eips::eip2718::Encodable2718; +use alloy_primitives::Log; +use alloy_rlp::Encodable; use core::str::FromStr; +const CAPACITY_RLP_RECEIPT: usize = 10_000; + +/// Tuple with a transaction index and the related receipt. +pub type Receipt = (u64, ReceiptEnvelope); + pub fn calculate_epoch(slot: u64) -> u64 { slot / SLOTS_PER_EPOCH } @@ -103,3 +112,29 @@ pub fn bitfield_bytes_tree_hash_root(bytes: &[u8]) -> Hash256 { .finish() .expect("bitfield tree hash buffer should not exceed leaf limit") } + +pub fn rlp_encode_transaction_index(index: &u64) -> Vec { + let mut buf = Vec::with_capacity(100); + Encodable::encode(&index, &mut buf); + + buf +} + +pub fn rlp_encode_index_and_receipt( + index: &u64, + receipt: &ReceiptEnvelope, +) -> (Vec, Vec) { + let mut buf = Vec::with_capacity(CAPACITY_RLP_RECEIPT); + receipt.encode_2718(&mut buf); + + (rlp_encode_transaction_index(index), buf) +} + +pub fn rlp_encode_receipts_and_nibble_tuples(receipts: &[Receipt]) -> Vec<(Vec, Vec)> { + receipts + .iter() + .map(|(transaction_index, receipt)| { + rlp_encode_index_and_receipt(transaction_index, receipt) + }) + .collect::>() +} From d5ea005b2b8bf915c5c4577d58991d870724e448 Mon Sep 17 00:00:00 2001 From: Georgy Shepelev Date: Fri, 16 Aug 2024 11:49:41 +0400 Subject: [PATCH 2/4] fix several review remarks --- Cargo.toml | 9 +++++++++ ethereum-common/Cargo.toml | 22 +++++++++++----------- ethereum-common/src/patricia_trie.rs | 2 +- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 63f94240..a04133ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,6 +70,8 @@ ethereum-types = { version = "0.14.1", default-features = false, features = [ ff = { version = "0.13.0", features = ["derive"] } futures = { version = "0.3.30", features = ["executor"] } futures-util = "0.3.28" +hash-db = { version = "0.15.2", default-features = false } +hash256-std-hasher = { version = "0.15.2", default-features = false } hex = { version = "0.4.3", default-features = false } hex-literal = "0.4.1" itertools = "0.10.5" @@ -78,6 +80,7 @@ keccak-hash = "0.10.0" lazy_static = "1.4.0" libc = "0.2.153" log = "0.4.14" +memory-db = { version = "0.27.0", default-features = false } num = { version = "0.4", features = ["rand"] } paste = "1.0.14" pretty_env_logger = "0.5.0" @@ -89,6 +92,7 @@ reqwest = "0.11.24" ring = { git = "https://github.com/gear-tech/ring.git", branch = "gear-v0.17.8", default-features = false, features = [ "alloc", ] } +rlp = { version = "0.5.2", default-features = false } scale-info = { version = "2.10", default-features = false, features = [ "derive", ] } @@ -100,6 +104,7 @@ serde_json = "1.0" sha2 = "0.10" static_assertions = "1.1.0" thiserror = "1.0.61" +tiny-keccak = { version = "2.0.2", features = ["keccak"] } tokio = { version = "1.23.0", features = ["full"] } tree_hash = { git = "https://github.com/gear-tech/tree_hash.git", branch = "gear-v0.6.0", default-features = false } tree_hash_derive = { git = "https://github.com/gear-tech/tree_hash.git", branch = "gear-v0.6.0" } @@ -132,6 +137,10 @@ primitive-types = { version = "0.12.2", default-features = false } binary-merkle-tree = { version = "4.0.0-dev", git = "https://github.com/gear-tech/polkadot-sdk.git", branch = "gear-v1.4.0", default-features = false } # Alloy deps +alloy-consensus = { version = "0.2.1", default-features = false } +alloy-eips = { version = "0.2.1", default-features = false } +alloy-rlp = { version = "0.3.8", default-features = false } +alloy-primitives = { version = "0.7.7", default-features = false } alloy = { version = "0.2.0", package = "alloy", features = [ "sol-types", "contract", diff --git a/ethereum-common/Cargo.toml b/ethereum-common/Cargo.toml index 1fbc7109..3f86f8eb 100644 --- a/ethereum-common/Cargo.toml +++ b/ethereum-common/Cargo.toml @@ -16,16 +16,16 @@ bitvec.workspace = true scale-info = { workspace = true, features = ["bit-vec"] } hex-literal.workspace = true ring.workspace = true -hash-db = { version = "0.15.2", default-features = false } -hash256-std-hasher = { version = "0.15.2", default-features = false } -tiny-keccak = { version = "2.0.2", features = ["keccak"] } -rlp = { version = "0.5.2", default-features = false } +hash-db.workspace = true +hash256-std-hasher.workspace = true +memory-db.workspace = true +rlp.workspace = true +tiny-keccak.workspace = true +alloy-consensus.workspace = true +alloy-eips.workspace = true +alloy-rlp.workspace = true +alloy-primitives.workspace = true trie-db = { version = "0.22.6", default-features = false } -memory-db = { version = "0.27.0", default-features = false } -alloy-consensus = { version = "0.2.1", default-features = false } -alloy-eips = { version = "0.2.1", default-features = false } -alloy-rlp = { version = "0.3.8", default-features = false } -alloy-primitives = { version = "0.7.7", default-features = false } [dev-dependencies] serde_json.workspace = true @@ -43,11 +43,11 @@ std = [ "ring/std", "hash-db/std", "hash256-std-hasher/std", - "rlp/std", - "trie-db/std", "memory-db/std", + "rlp/std", "alloy-consensus/std", "alloy-eips/std", "alloy-rlp/std", "alloy-primitives/std", + "trie-db/std", ] diff --git a/ethereum-common/src/patricia_trie.rs b/ethereum-common/src/patricia_trie.rs index 1a14c59d..54853e34 100644 --- a/ethereum-common/src/patricia_trie.rs +++ b/ethereum-common/src/patricia_trie.rs @@ -7,9 +7,9 @@ use rlp::DecoderError; /// Convenience type alias to instantiate a Keccak-flavoured `RlpNodeCodec` pub type RlpCodec = RlpNodeCodec; -#[derive(Clone, Default)] /// Defines the working of a particular flavour of trie: /// how keys are hashed, how values are encoded, does it use extension nodes or not. +#[derive(Clone, Default)] pub struct Layout; impl trie_db::TrieLayout for Layout { From 36400372d605534c87457074ceb1f67f2e31c0de Mon Sep 17 00:00:00 2001 From: Georgy Shepelev Date: Fri, 16 Aug 2024 19:32:01 +0400 Subject: [PATCH 3/4] fix review remarks --- ethereum-common/src/beacon/tests.rs | 24 +++++++++++----------- ethereum-common/src/lib.rs | 8 +------- ethereum-common/src/memory_db.rs | 8 ++++++++ ethereum-common/src/patricia_trie.rs | 30 ++++------------------------ 4 files changed, 25 insertions(+), 45 deletions(-) create mode 100644 ethereum-common/src/memory_db.rs diff --git a/ethereum-common/src/beacon/tests.rs b/ethereum-common/src/beacon/tests.rs index 9cbdfe5e..60459da7 100644 --- a/ethereum-common/src/beacon/tests.rs +++ b/ethereum-common/src/beacon/tests.rs @@ -130,9 +130,9 @@ fn patricia_trie_root_simple() { let (key, value) = utils::rlp_encode_index_and_receipt(&0, &receipt); let root = { - let mut memdb = new_memory_db(); + let mut mem_db = memory_db::new(); let mut root = H256::default(); - let mut triedbmut = TrieDBMut::new(&mut memdb, &mut root); + let mut triedbmut = TrieDBMut::new(&mut mem_db, &mut root); triedbmut.insert(&key, &value).unwrap(); *triedbmut.root() @@ -144,9 +144,9 @@ fn patricia_trie_root_simple() { let (key, value) = utils::rlp_encode_index_and_receipt(&1, &receipt); let root = { - let mut memdb = new_memory_db(); + let mut mem_db = memory_db::new(); let mut root = H256::default(); - let mut triedbmut = TrieDBMut::new(&mut memdb, &mut root); + let mut triedbmut = TrieDBMut::new(&mut mem_db, &mut root); triedbmut.insert(&key, &value).unwrap(); *triedbmut.root() @@ -170,9 +170,9 @@ fn patricia_trie_root_simple() { let (key, value) = utils::rlp_encode_index_and_receipt(&0, &receipt); let root = { - let mut memdb = new_memory_db(); + let mut mem_db = memory_db::new(); let mut root = H256::default(); - let mut triedbmut = TrieDBMut::new(&mut memdb, &mut root); + let mut triedbmut = TrieDBMut::new(&mut mem_db, &mut root); triedbmut.insert(&key, &value).unwrap(); *triedbmut.root() @@ -212,9 +212,9 @@ fn dynamic_fee_transaction_type() { ]); let root = { - let mut memdb = new_memory_db(); + let mut mem_db = memory_db::new(); let mut root = H256::default(); - let mut triedbmut = TrieDBMut::new(&mut memdb, &mut root); + let mut triedbmut = TrieDBMut::new(&mut mem_db, &mut root); for (key, value) in encoded { triedbmut.insert(&key, &value).unwrap(); } @@ -229,9 +229,9 @@ fn dynamic_fee_transaction_type() { utils::rlp_encode_receipts_and_nibble_tuples(&[(11, receipt1.clone()), (1, receipt2)]); let root = { - let mut memdb = new_memory_db(); + let mut mem_db = memory_db::new(); let mut root = H256::default(); - let mut triedbmut = TrieDBMut::new(&mut memdb, &mut root); + let mut triedbmut = TrieDBMut::new(&mut mem_db, &mut root); for (key, value) in encoded { triedbmut.insert(&key, &value).unwrap(); } @@ -254,9 +254,9 @@ fn dynamic_fee_transaction_type() { let encoded = utils::rlp_encode_receipts_and_nibble_tuples(&[(0, receipt1), (1, receipt2)]); let root = { - let mut memdb = new_memory_db(); + let mut mem_db = memory_db::new(); let mut root = H256::default(); - let mut triedbmut = TrieDBMut::new(&mut memdb, &mut root); + let mut triedbmut = TrieDBMut::new(&mut mem_db, &mut root); for (key, value) in encoded { triedbmut.insert(&key, &value).unwrap(); } diff --git a/ethereum-common/src/lib.rs b/ethereum-common/src/lib.rs index 277d5466..fe09204d 100644 --- a/ethereum-common/src/lib.rs +++ b/ethereum-common/src/lib.rs @@ -3,6 +3,7 @@ pub mod base_types; pub mod beacon; pub mod keccak_hasher; +pub mod memory_db; pub mod merkle; pub mod network; pub mod patricia_trie; @@ -23,7 +24,6 @@ use core::{ }; pub use ethereum_types::{H256, U256}; -use keccak_hasher::KeccakHasher; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use serde::{de, Deserialize}; @@ -34,9 +34,3 @@ pub use trie_db; pub const SLOTS_PER_EPOCH: u64 = 32; pub const EPOCHS_PER_SYNC_COMMITTEE: u64 = 256; pub const SYNC_COMMITTEE_SIZE: usize = 512; - -pub type MemoryDB = memory_db::MemoryDB, Vec>; - -pub fn new_memory_db() -> MemoryDB { - memory_db::MemoryDB::from_null_node(&rlp::NULL_RLP, rlp::NULL_RLP.as_ref().into()) -} diff --git a/ethereum-common/src/memory_db.rs b/ethereum-common/src/memory_db.rs new file mode 100644 index 00000000..ef7fa5ef --- /dev/null +++ b/ethereum-common/src/memory_db.rs @@ -0,0 +1,8 @@ +use super::{keccak_hasher::KeccakHasher, *}; +use ::memory_db::HashKey; + +pub type MemoryDB = ::memory_db::MemoryDB, Vec>; + +pub fn new() -> MemoryDB { + memory_db::MemoryDB::from_null_node(&rlp::NULL_RLP, rlp::NULL_RLP.as_ref().into()) +} diff --git a/ethereum-common/src/patricia_trie.rs b/ethereum-common/src/patricia_trie.rs index 54853e34..64724c94 100644 --- a/ethereum-common/src/patricia_trie.rs +++ b/ethereum-common/src/patricia_trie.rs @@ -1,8 +1,6 @@ // Inspired by the Parity Ethereum project. pub use crate::{keccak_hasher::KeccakHasher, rlp_node_codec::RlpNodeCodec}; -use ethereum_types::H256; -use rlp::DecoderError; /// Convenience type alias to instantiate a Keccak-flavoured `RlpNodeCodec` pub type RlpCodec = RlpNodeCodec; @@ -29,9 +27,9 @@ impl trie_db::TrieLayout for Layout { /// use memory_db::*; /// use trie_db::*; /// use ethereum_types::H256; -/// use ethereum_common::patricia_trie::{TrieDB, TrieDBMut}; +/// use ethereum_common::{patricia_trie::{TrieDB, TrieDBMut}, memory_db}; /// -/// let mut memdb = ethereum_common::new_memory_db(); +/// let mut memdb = memory_db::new(); /// let mut root = H256::zero(); /// TrieDBMut::new(&mut memdb, &mut root).insert(b"foo", b"bar").unwrap(); /// let t = TrieDB::new(&memdb, &root).unwrap(); @@ -40,12 +38,6 @@ impl trie_db::TrieLayout for Layout { /// ``` pub type TrieDB<'db> = trie_db::TrieDB<'db, Layout>; -/// Convenience type alias to instantiate a Keccak/Rlp-flavoured `SecTrieDB` -pub type SecTrieDB<'db> = trie_db::SecTrieDB<'db, Layout>; - -/// Convenience type alias to instantiate a Keccak/Rlp-flavoured `FatDB` -pub type FatDB<'db> = trie_db::FatDB<'db, Layout>; - /// Convenience type alias to instantiate a Keccak/Rlp-flavoured `TrieDBMut` /// /// Use it as a `TrieMut` trait object. You can use `db()` to get the backing database object. @@ -58,9 +50,9 @@ pub type FatDB<'db> = trie_db::FatDB<'db, Layout>; /// use memory_db::*; /// use trie_db::*; /// use ethereum_types::H256; -/// use ethereum_common::{patricia_trie::{TrieDB, TrieDBMut}, rlp_node_codec}; +/// use ethereum_common::{patricia_trie::{TrieDB, TrieDBMut}, rlp_node_codec, memory_db}; /// -/// let mut memdb = ethereum_common::new_memory_db(); +/// let mut memdb = memory_db::new(); /// let mut root = H256::zero(); /// let mut t = TrieDBMut::new(&mut memdb, &mut root); /// assert!(t.is_empty()); @@ -73,20 +65,6 @@ pub type FatDB<'db> = trie_db::FatDB<'db, Layout>; /// ``` pub type TrieDBMut<'db> = trie_db::TrieDBMut<'db, Layout>; -/// Convenience type alias to instantiate a Keccak/Rlp-flavoured `SecTrieDBMut` -pub type SecTrieDBMut<'db> = trie_db::SecTrieDBMut<'db, Layout>; - -/// Convenience type alias to instantiate a Keccak/Rlp-flavoured `FatDBMut` -pub type FatDBMut<'db> = trie_db::FatDBMut<'db, Layout>; - -/// Convenience type alias to instantiate a Keccak/Rlp-flavoured `TrieFactory` -pub type TrieFactory = trie_db::TrieFactory; - -/// Convenience type alias for Keccak/Rlp flavoured trie errors -pub type TrieError = trie_db::TrieError; -/// Convenience type alias for Keccak/Rlp flavoured trie results -pub type Result = trie_db::Result; - #[cfg(test)] mod tests { use super::*; From 1bfe708cb767e1b790c0cd416d06eb4548871046 Mon Sep 17 00:00:00 2001 From: Georgy Shepelev Date: Mon, 19 Aug 2024 12:29:37 +0300 Subject: [PATCH 4/4] fix review remarks --- ethereum-common/Cargo.toml | 3 +++ ethereum-common/src/patricia_trie.rs | 36 ---------------------------- 2 files changed, 3 insertions(+), 36 deletions(-) diff --git a/ethereum-common/Cargo.toml b/ethereum-common/Cargo.toml index 3f86f8eb..f35f0f73 100644 --- a/ethereum-common/Cargo.toml +++ b/ethereum-common/Cargo.toml @@ -25,6 +25,9 @@ alloy-consensus.workspace = true alloy-eips.workspace = true alloy-rlp.workspace = true alloy-primitives.workspace = true +# we need to use this specific version of the crate since +# its API changed a lot and the rlp implementation of NodeCodec +# requires lots of non-trivial changes. trie-db = { version = "0.22.6", default-features = false } [dev-dependencies] diff --git a/ethereum-common/src/patricia_trie.rs b/ethereum-common/src/patricia_trie.rs index 64724c94..69bee4f1 100644 --- a/ethereum-common/src/patricia_trie.rs +++ b/ethereum-common/src/patricia_trie.rs @@ -20,22 +20,6 @@ impl trie_db::TrieLayout for Layout { /// /// Use it as a `Trie` trait object. You can use `db()` to get the backing database object. /// Use `get` and `contains` to query values associated with keys in the trie. -/// -/// # Example -/// ``` -/// use hash_db::*; -/// use memory_db::*; -/// use trie_db::*; -/// use ethereum_types::H256; -/// use ethereum_common::{patricia_trie::{TrieDB, TrieDBMut}, memory_db}; -/// -/// let mut memdb = memory_db::new(); -/// let mut root = H256::zero(); -/// TrieDBMut::new(&mut memdb, &mut root).insert(b"foo", b"bar").unwrap(); -/// let t = TrieDB::new(&memdb, &root).unwrap(); -/// assert!(t.contains(b"foo").unwrap()); -/// assert_eq!(t.get(b"foo").unwrap().unwrap(), b"bar".to_vec()); -/// ``` pub type TrieDB<'db> = trie_db::TrieDB<'db, Layout>; /// Convenience type alias to instantiate a Keccak/Rlp-flavoured `TrieDBMut` @@ -43,26 +27,6 @@ pub type TrieDB<'db> = trie_db::TrieDB<'db, Layout>; /// Use it as a `TrieMut` trait object. You can use `db()` to get the backing database object. /// Note that changes are not committed to the database until `commit` is called. /// Querying the root or dropping the trie will commit automatically. - -/// # Example -/// ``` -/// use hash_db::*; -/// use memory_db::*; -/// use trie_db::*; -/// use ethereum_types::H256; -/// use ethereum_common::{patricia_trie::{TrieDB, TrieDBMut}, rlp_node_codec, memory_db}; -/// -/// let mut memdb = memory_db::new(); -/// let mut root = H256::zero(); -/// let mut t = TrieDBMut::new(&mut memdb, &mut root); -/// assert!(t.is_empty()); -/// assert_eq!(*t.root(), rlp_node_codec::HASHED_NULL_NODE); -/// t.insert(b"foo", b"bar").unwrap(); -/// assert!(t.contains(b"foo").unwrap()); -/// assert_eq!(t.get(b"foo").unwrap().unwrap(), b"bar".to_vec()); -/// t.remove(b"foo").unwrap(); -/// assert!(!t.contains(b"foo").unwrap()); -/// ``` pub type TrieDBMut<'db> = trie_db::TrieDBMut<'db, Layout>; #[cfg(test)]