From 9abd37f98eed4a16b88720caff328cc83dc64c1a Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Thu, 13 Nov 2025 17:34:15 -0300 Subject: [PATCH 01/18] [QND] perf: query all nodes in path together --- crates/common/trie/db.rs | 8 ++++++++ crates/common/trie/trie.rs | 27 +++++++++++++++++++++++++-- crates/storage/trie_db/rocksdb.rs | 16 ++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/crates/common/trie/db.rs b/crates/common/trie/db.rs index 46f603f7f74..963369ea215 100644 --- a/crates/common/trie/db.rs +++ b/crates/common/trie/db.rs @@ -12,6 +12,14 @@ pub type NodeMap = Arc, Vec>>>; pub trait TrieDB: Send + Sync { fn get(&self, key: Nibbles) -> Result>, TrieError>; + fn get_nodes_in_path(&self, key: Nibbles) -> Result>>, TrieError> { + let keys = (0..key.len()).map(|i| key.slice(0, i)); + let mut values = Vec::with_capacity(key.len()); + for key in keys { + values.push(self.get(key)?); + } + Ok(values) + } fn put_batch(&self, key_values: Vec<(Nibbles, Vec)>) -> Result<(), TrieError>; // TODO: replace putbatch with this function. fn put_batch_no_alloc(&self, key_values: &[(Nibbles, Node)]) -> Result<(), TrieError> { diff --git a/crates/common/trie/trie.rs b/crates/common/trie/trie.rs index 2596dab9558..a2f706224d2 100644 --- a/crates/common/trie/trie.rs +++ b/crates/common/trie/trie.rs @@ -130,15 +130,38 @@ impl Trie { let path = Nibbles::from_bytes(&path); self.pending_removal.remove(&path); self.dirty.insert(path.clone()); + let encoded_nodes = self.db.get_nodes_in_path(path.clone())?; + let keys = (0..path.len()).map(|i| path.slice(0, i)); + let mut nodes = Vec::with_capacity(path.len()); + for (path, encoded) in std::iter::zip(keys, encoded_nodes) { + let Some(encoded) = encoded else { + continue; + }; + nodes.push((path, encoded)); + } + + struct VecTrieDB(Vec<(Nibbles, Vec)>); + impl TrieDB for VecTrieDB { + fn get(&self, key: Nibbles) -> Result>, TrieError> { + let Ok(pos) = self.0.binary_search_by(|(k, _)| k.cmp(&key)) else { + return Ok(None); + }; + Ok(self.0.get(pos).map(|(_, v)| v.clone())) + } + fn put_batch(&self, key_values: Vec<(Nibbles, Vec)>) -> Result<(), TrieError> { + unimplemented!() + } + } + let fake_db = VecTrieDB(nodes); if self.root.is_valid() { // If the trie is not empty, call the root node's insertion logic. self.root - .get_node_mut(self.db.as_ref(), Nibbles::default())? + .get_node_mut(&fake_db, Nibbles::default())? .ok_or_else(|| { TrieError::InconsistentTree(Box::new(InconsistentTreeError::RootNotFoundNoHash)) })? - .insert(self.db.as_ref(), path, value)? + .insert(&fake_db, path, value)? } else { // If the trie is empty, just add a leaf. self.root = Node::from(LeafNode::new(path, value)).into() diff --git a/crates/storage/trie_db/rocksdb.rs b/crates/storage/trie_db/rocksdb.rs index 3547393dd7a..f3dab93283b 100644 --- a/crates/storage/trie_db/rocksdb.rs +++ b/crates/storage/trie_db/rocksdb.rs @@ -107,6 +107,22 @@ impl TrieDB for RocksDBTrieDB { Ok(res) } + fn get_nodes_in_path(&self, key: Nibbles) -> Result>>, TrieError> { + let cf = self.cf_handle_for_key(&key)?; + let keys: Vec<_> = (0..key.len()) + .map(|i| self.make_key(key.slice(0, i))) + .collect(); + let values = self.db.batched_multi_get_cf(&cf, &keys, true); + let mut res = Vec::with_capacity(key.len()); + for value in values { + let value = value + .map_err(|e| TrieError::DbError(anyhow::anyhow!("RocksDB get error: {}", e)))? + .map(|v| v.to_vec()); + res.push(value); + } + Ok(res) + } + fn put_batch(&self, key_values: Vec<(Nibbles, Vec)>) -> Result<(), TrieError> { let mut batch = rocksdb::WriteBatch::default(); From 151eaa737805476bcc946e876f54ff1fadbac7af Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Thu, 13 Nov 2025 18:00:07 -0300 Subject: [PATCH 02/18] TrieWrapper be mad --- crates/storage/trie_db/layering.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/crates/storage/trie_db/layering.rs b/crates/storage/trie_db/layering.rs index a1e9e2b227e..2359cb8a6e9 100644 --- a/crates/storage/trie_db/layering.rs +++ b/crates/storage/trie_db/layering.rs @@ -209,6 +209,19 @@ impl TrieDB for TrieWrapper { self.db.get(key) } + fn get_nodes_in_path(&self, key: Nibbles) -> Result>>, TrieError> { + let mut values = self.db.get_nodes_in_path(key.clone())?; + let start = self.prefix.map(|_| 66).unwrap_or(0); + let end = start + key.len(); + let key = apply_prefix(self.prefix, key); + for (i, j) in (start..end).enumerate() { + if let Some(value) = self.inner.get(self.state_root, &key.as_ref()[..j]) { + values[i] = Some(value); + } + } + Ok(values) + } + fn put_batch(&self, _key_values: Vec<(Nibbles, Vec)>) -> Result<(), TrieError> { // TODO: Get rid of this. unimplemented!("This function should not be called"); From 121ac0dd1434c14ed858232d4a63afc54af12bb7 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Thu, 13 Nov 2025 18:21:44 -0300 Subject: [PATCH 03/18] fix handle --- crates/storage/trie_db/rocksdb.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/crates/storage/trie_db/rocksdb.rs b/crates/storage/trie_db/rocksdb.rs index f3dab93283b..df257894d66 100644 --- a/crates/storage/trie_db/rocksdb.rs +++ b/crates/storage/trie_db/rocksdb.rs @@ -108,12 +108,13 @@ impl TrieDB for RocksDBTrieDB { } fn get_nodes_in_path(&self, key: Nibbles) -> Result>>, TrieError> { - let cf = self.cf_handle_for_key(&key)?; - let keys: Vec<_> = (0..key.len()) - .map(|i| self.make_key(key.slice(0, i))) - .collect(); - let values = self.db.batched_multi_get_cf(&cf, &keys, true); + let cf = self.cf_handle()?; let mut res = Vec::with_capacity(key.len()); + let start = self.address_prefix.map(|_| 66).unwrap_or(0); + let end = start + key.len(); + let key = self.make_key(key); + let keys = (start..end).map(|i| &key[..i]); + let values = self.db.batched_multi_get_cf(&cf, keys, true); for value in values { let value = value .map_err(|e| TrieError::DbError(anyhow::anyhow!("RocksDB get error: {}", e)))? From f6bc953947e6ec6422d3286e97d32e97c7348f78 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Thu, 13 Nov 2025 18:48:52 -0300 Subject: [PATCH 04/18] trade efficiency for clarity --- crates/storage/trie_db/layering.rs | 4 ++-- crates/storage/trie_db/rocksdb.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/storage/trie_db/layering.rs b/crates/storage/trie_db/layering.rs index 2359cb8a6e9..11e5748cf1d 100644 --- a/crates/storage/trie_db/layering.rs +++ b/crates/storage/trie_db/layering.rs @@ -211,9 +211,9 @@ impl TrieDB for TrieWrapper { fn get_nodes_in_path(&self, key: Nibbles) -> Result>>, TrieError> { let mut values = self.db.get_nodes_in_path(key.clone())?; - let start = self.prefix.map(|_| 66).unwrap_or(0); - let end = start + key.len(); let key = apply_prefix(self.prefix, key); + let start = apply_prefix(self.prefix, Nibbles::default()).len(); + let end = key.len(); for (i, j) in (start..end).enumerate() { if let Some(value) = self.inner.get(self.state_root, &key.as_ref()[..j]) { values[i] = Some(value); diff --git a/crates/storage/trie_db/rocksdb.rs b/crates/storage/trie_db/rocksdb.rs index df257894d66..7f2250dbe5e 100644 --- a/crates/storage/trie_db/rocksdb.rs +++ b/crates/storage/trie_db/rocksdb.rs @@ -110,9 +110,9 @@ impl TrieDB for RocksDBTrieDB { fn get_nodes_in_path(&self, key: Nibbles) -> Result>>, TrieError> { let cf = self.cf_handle()?; let mut res = Vec::with_capacity(key.len()); - let start = self.address_prefix.map(|_| 66).unwrap_or(0); - let end = start + key.len(); let key = self.make_key(key); + let start = self.make_key(Nibbles::default()).len(); + let end = key.len(); let keys = (start..end).map(|i| &key[..i]); let values = self.db.batched_multi_get_cf(&cf, keys, true); for value in values { From 8365596f966f915728fc3a8d269a09e26c4afa0a Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Thu, 13 Nov 2025 18:55:48 -0300 Subject: [PATCH 05/18] need full key, discard unwanted nodes after --- crates/storage/trie_db/layering.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/storage/trie_db/layering.rs b/crates/storage/trie_db/layering.rs index 11e5748cf1d..d2d95a085ae 100644 --- a/crates/storage/trie_db/layering.rs +++ b/crates/storage/trie_db/layering.rs @@ -210,10 +210,11 @@ impl TrieDB for TrieWrapper { } fn get_nodes_in_path(&self, key: Nibbles) -> Result>>, TrieError> { - let mut values = self.db.get_nodes_in_path(key.clone())?; let key = apply_prefix(self.prefix, key); let start = apply_prefix(self.prefix, Nibbles::default()).len(); let end = key.len(); + let mut values = self.db.get_nodes_in_path(key.clone())?; + values.drain(0..start); for (i, j) in (start..end).enumerate() { if let Some(value) = self.inner.get(self.state_root, &key.as_ref()[..j]) { values[i] = Some(value); From 733f930aa25fe4a87b2c61b50e3eebe6edd33526 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Thu, 13 Nov 2025 19:14:32 -0300 Subject: [PATCH 06/18] skip DB if the nodes are in difflayers --- crates/storage/trie_db/layering.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/storage/trie_db/layering.rs b/crates/storage/trie_db/layering.rs index d2d95a085ae..8a7d88422ba 100644 --- a/crates/storage/trie_db/layering.rs +++ b/crates/storage/trie_db/layering.rs @@ -213,8 +213,10 @@ impl TrieDB for TrieWrapper { let key = apply_prefix(self.prefix, key); let start = apply_prefix(self.prefix, Nibbles::default()).len(); let end = key.len(); - let mut values = self.db.get_nodes_in_path(key.clone())?; - values.drain(0..start); + let mut values = match self.inner.get(self.state_root, key.as_ref()) { + Some(_) => vec![None; end - start], // If we have the FKV key then we have the nodes + None => self.db.get_nodes_in_path(key.clone())?.split_off(start), + }; for (i, j) in (start..end).enumerate() { if let Some(value) = self.inner.get(self.state_root, &key.as_ref()[..j]) { values[i] = Some(value); From 48df0e9ca097cb85f7ed7a8d70f6ee52629b4240 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Thu, 13 Nov 2025 19:41:53 -0300 Subject: [PATCH 07/18] move to inside of noderef --- crates/common/trie/node.rs | 31 +++++++++++++++++++++++++++++-- crates/common/trie/trie.rs | 27 ++------------------------- crates/storage/trie_db/rocksdb.rs | 2 +- 3 files changed, 32 insertions(+), 28 deletions(-) diff --git a/crates/common/trie/node.rs b/crates/common/trie/node.rs index ee670652f26..d0744c1530c 100644 --- a/crates/common/trie/node.rs +++ b/crates/common/trie/node.rs @@ -82,7 +82,34 @@ impl NodeRef { self.get_node_mut(db, path) } NodeRef::Hash(hash @ NodeHash::Hashed(_)) => { - let Some(node) = db + let encoded_nodes = db.get_nodes_in_path(path.clone())?; + let keys = (0..path.len()).map(|i| path.slice(0, i)); + let mut nodes = Vec::with_capacity(path.len()); + for (path, encoded) in std::iter::zip(keys, encoded_nodes) { + let Some(encoded) = encoded else { + continue; + }; + nodes.push((path, encoded)); + } + + struct VecTrieDB(Vec<(Nibbles, Vec)>); + impl TrieDB for VecTrieDB { + fn get(&self, key: Nibbles) -> Result>, TrieError> { + let Ok(pos) = self.0.binary_search_by(|(k, _)| k.cmp(&key)) else { + return Ok(None); + }; + Ok(self.0.get(pos).map(|(_, v)| v.clone())) + } + fn put_batch( + &self, + key_values: Vec<(Nibbles, Vec)>, + ) -> Result<(), TrieError> { + unimplemented!() + } + } + let fake_db = VecTrieDB(nodes); + + let Some(node) = fake_db .get(path.clone())? .filter(|rlp| !rlp.is_empty()) .map(|rlp| Node::decode(&rlp).map_err(TrieError::RLPDecode)) @@ -91,7 +118,7 @@ impl NodeRef { return Ok(None); }; *self = NodeRef::Node(Arc::new(node), OnceLock::from(*hash)); - self.get_node_mut(db, path) + self.get_node_mut(&fake_db, path) } } } diff --git a/crates/common/trie/trie.rs b/crates/common/trie/trie.rs index a2f706224d2..2596dab9558 100644 --- a/crates/common/trie/trie.rs +++ b/crates/common/trie/trie.rs @@ -130,38 +130,15 @@ impl Trie { let path = Nibbles::from_bytes(&path); self.pending_removal.remove(&path); self.dirty.insert(path.clone()); - let encoded_nodes = self.db.get_nodes_in_path(path.clone())?; - let keys = (0..path.len()).map(|i| path.slice(0, i)); - let mut nodes = Vec::with_capacity(path.len()); - for (path, encoded) in std::iter::zip(keys, encoded_nodes) { - let Some(encoded) = encoded else { - continue; - }; - nodes.push((path, encoded)); - } - - struct VecTrieDB(Vec<(Nibbles, Vec)>); - impl TrieDB for VecTrieDB { - fn get(&self, key: Nibbles) -> Result>, TrieError> { - let Ok(pos) = self.0.binary_search_by(|(k, _)| k.cmp(&key)) else { - return Ok(None); - }; - Ok(self.0.get(pos).map(|(_, v)| v.clone())) - } - fn put_batch(&self, key_values: Vec<(Nibbles, Vec)>) -> Result<(), TrieError> { - unimplemented!() - } - } - let fake_db = VecTrieDB(nodes); if self.root.is_valid() { // If the trie is not empty, call the root node's insertion logic. self.root - .get_node_mut(&fake_db, Nibbles::default())? + .get_node_mut(self.db.as_ref(), Nibbles::default())? .ok_or_else(|| { TrieError::InconsistentTree(Box::new(InconsistentTreeError::RootNotFoundNoHash)) })? - .insert(&fake_db, path, value)? + .insert(self.db.as_ref(), path, value)? } else { // If the trie is empty, just add a leaf. self.root = Node::from(LeafNode::new(path, value)).into() diff --git a/crates/storage/trie_db/rocksdb.rs b/crates/storage/trie_db/rocksdb.rs index 7f2250dbe5e..a7bfd9ecb01 100644 --- a/crates/storage/trie_db/rocksdb.rs +++ b/crates/storage/trie_db/rocksdb.rs @@ -1,7 +1,7 @@ use ethrex_common::H256; use ethrex_rlp::encode::RLPEncode; use ethrex_trie::{Nibbles, Node, TrieDB, error::TrieError}; -use rocksdb::{DBWithThreadMode, MultiThreaded}; +use rocksdb::{DBWithThreadMode, MultiThreaded, ReadOptions}; use std::sync::Arc; use crate::trie_db::layering::apply_prefix; From 482cb8531709c38b65cbc56d0e0a7eafecfc6eb9 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Thu, 13 Nov 2025 19:46:59 -0300 Subject: [PATCH 08/18] Revert "move to inside of noderef" This reverts commit 48df0e9ca097cb85f7ed7a8d70f6ee52629b4240. --- crates/common/trie/node.rs | 31 ++----------------------------- crates/common/trie/trie.rs | 27 +++++++++++++++++++++++++-- crates/storage/trie_db/rocksdb.rs | 2 +- 3 files changed, 28 insertions(+), 32 deletions(-) diff --git a/crates/common/trie/node.rs b/crates/common/trie/node.rs index d0744c1530c..ee670652f26 100644 --- a/crates/common/trie/node.rs +++ b/crates/common/trie/node.rs @@ -82,34 +82,7 @@ impl NodeRef { self.get_node_mut(db, path) } NodeRef::Hash(hash @ NodeHash::Hashed(_)) => { - let encoded_nodes = db.get_nodes_in_path(path.clone())?; - let keys = (0..path.len()).map(|i| path.slice(0, i)); - let mut nodes = Vec::with_capacity(path.len()); - for (path, encoded) in std::iter::zip(keys, encoded_nodes) { - let Some(encoded) = encoded else { - continue; - }; - nodes.push((path, encoded)); - } - - struct VecTrieDB(Vec<(Nibbles, Vec)>); - impl TrieDB for VecTrieDB { - fn get(&self, key: Nibbles) -> Result>, TrieError> { - let Ok(pos) = self.0.binary_search_by(|(k, _)| k.cmp(&key)) else { - return Ok(None); - }; - Ok(self.0.get(pos).map(|(_, v)| v.clone())) - } - fn put_batch( - &self, - key_values: Vec<(Nibbles, Vec)>, - ) -> Result<(), TrieError> { - unimplemented!() - } - } - let fake_db = VecTrieDB(nodes); - - let Some(node) = fake_db + let Some(node) = db .get(path.clone())? .filter(|rlp| !rlp.is_empty()) .map(|rlp| Node::decode(&rlp).map_err(TrieError::RLPDecode)) @@ -118,7 +91,7 @@ impl NodeRef { return Ok(None); }; *self = NodeRef::Node(Arc::new(node), OnceLock::from(*hash)); - self.get_node_mut(&fake_db, path) + self.get_node_mut(db, path) } } } diff --git a/crates/common/trie/trie.rs b/crates/common/trie/trie.rs index 2596dab9558..a2f706224d2 100644 --- a/crates/common/trie/trie.rs +++ b/crates/common/trie/trie.rs @@ -130,15 +130,38 @@ impl Trie { let path = Nibbles::from_bytes(&path); self.pending_removal.remove(&path); self.dirty.insert(path.clone()); + let encoded_nodes = self.db.get_nodes_in_path(path.clone())?; + let keys = (0..path.len()).map(|i| path.slice(0, i)); + let mut nodes = Vec::with_capacity(path.len()); + for (path, encoded) in std::iter::zip(keys, encoded_nodes) { + let Some(encoded) = encoded else { + continue; + }; + nodes.push((path, encoded)); + } + + struct VecTrieDB(Vec<(Nibbles, Vec)>); + impl TrieDB for VecTrieDB { + fn get(&self, key: Nibbles) -> Result>, TrieError> { + let Ok(pos) = self.0.binary_search_by(|(k, _)| k.cmp(&key)) else { + return Ok(None); + }; + Ok(self.0.get(pos).map(|(_, v)| v.clone())) + } + fn put_batch(&self, key_values: Vec<(Nibbles, Vec)>) -> Result<(), TrieError> { + unimplemented!() + } + } + let fake_db = VecTrieDB(nodes); if self.root.is_valid() { // If the trie is not empty, call the root node's insertion logic. self.root - .get_node_mut(self.db.as_ref(), Nibbles::default())? + .get_node_mut(&fake_db, Nibbles::default())? .ok_or_else(|| { TrieError::InconsistentTree(Box::new(InconsistentTreeError::RootNotFoundNoHash)) })? - .insert(self.db.as_ref(), path, value)? + .insert(&fake_db, path, value)? } else { // If the trie is empty, just add a leaf. self.root = Node::from(LeafNode::new(path, value)).into() diff --git a/crates/storage/trie_db/rocksdb.rs b/crates/storage/trie_db/rocksdb.rs index a7bfd9ecb01..7f2250dbe5e 100644 --- a/crates/storage/trie_db/rocksdb.rs +++ b/crates/storage/trie_db/rocksdb.rs @@ -1,7 +1,7 @@ use ethrex_common::H256; use ethrex_rlp::encode::RLPEncode; use ethrex_trie::{Nibbles, Node, TrieDB, error::TrieError}; -use rocksdb::{DBWithThreadMode, MultiThreaded, ReadOptions}; +use rocksdb::{DBWithThreadMode, MultiThreaded}; use std::sync::Arc; use crate::trie_db::layering::apply_prefix; From f81e7bb03c7f890a5fcdab04a6352aff5ab57682 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Thu, 13 Nov 2025 19:54:07 -0300 Subject: [PATCH 09/18] extremely qnd: separate account and storage insert, do bulk read for storage only --- crates/blockchain/blockchain.rs | 2 +- crates/common/trie/trie.rs | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/crates/blockchain/blockchain.rs b/crates/blockchain/blockchain.rs index 614344d5137..13242854a93 100644 --- a/crates/blockchain/blockchain.rs +++ b/crates/blockchain/blockchain.rs @@ -433,7 +433,7 @@ impl Blockchain { storage_trie.remove(&hashed_key)?; } else { trace!(slot = hex::encode(&hashed_key), "Inserting"); - storage_trie.insert(hashed_key, storage_value.encode_to_vec())?; + storage_trie.insert_storage(hashed_key, storage_value.encode_to_vec())?; } } trace!( diff --git a/crates/common/trie/trie.rs b/crates/common/trie/trie.rs index a2f706224d2..ca206e8d0dd 100644 --- a/crates/common/trie/trie.rs +++ b/crates/common/trie/trie.rs @@ -130,6 +130,29 @@ impl Trie { let path = Nibbles::from_bytes(&path); self.pending_removal.remove(&path); self.dirty.insert(path.clone()); + + if self.root.is_valid() { + // If the trie is not empty, call the root node's insertion logic. + self.root + .get_node_mut(self.db.as_ref(), Nibbles::default())? + .ok_or_else(|| { + TrieError::InconsistentTree(Box::new(InconsistentTreeError::RootNotFoundNoHash)) + })? + .insert(self.db.as_ref(), path, value)? + } else { + // If the trie is empty, just add a leaf. + self.root = Node::from(LeafNode::new(path, value)).into() + }; + self.root.clear_hash(); + + Ok(()) + } + + /// Insert an RLP-encoded value into the trie. + pub fn insert_storage(&mut self, path: PathRLP, value: ValueRLP) -> Result<(), TrieError> { + let path = Nibbles::from_bytes(&path); + self.pending_removal.remove(&path); + self.dirty.insert(path.clone()); let encoded_nodes = self.db.get_nodes_in_path(path.clone())?; let keys = (0..path.len()).map(|i| path.slice(0, i)); let mut nodes = Vec::with_capacity(path.len()); From 3c7e02706038ad747f232feb37312a4236c239ed Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Thu, 13 Nov 2025 20:10:09 -0300 Subject: [PATCH 10/18] skip unnecessary nodes --- crates/common/trie/db.rs | 8 ++++++-- crates/common/trie/trie.rs | 2 +- crates/storage/trie_db/layering.rs | 10 +++++++--- crates/storage/trie_db/rocksdb.rs | 7 +++++-- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/crates/common/trie/db.rs b/crates/common/trie/db.rs index 963369ea215..3331828eb69 100644 --- a/crates/common/trie/db.rs +++ b/crates/common/trie/db.rs @@ -12,8 +12,12 @@ pub type NodeMap = Arc, Vec>>>; pub trait TrieDB: Send + Sync { fn get(&self, key: Nibbles) -> Result>, TrieError>; - fn get_nodes_in_path(&self, key: Nibbles) -> Result>>, TrieError> { - let keys = (0..key.len()).map(|i| key.slice(0, i)); + fn get_nodes_in_path( + &self, + key: Nibbles, + start: usize, + ) -> Result>>, TrieError> { + let keys = (start..key.len()).map(|i| key.slice(0, i)); let mut values = Vec::with_capacity(key.len()); for key in keys { values.push(self.get(key)?); diff --git a/crates/common/trie/trie.rs b/crates/common/trie/trie.rs index ca206e8d0dd..4f4d653d0b1 100644 --- a/crates/common/trie/trie.rs +++ b/crates/common/trie/trie.rs @@ -153,7 +153,7 @@ impl Trie { let path = Nibbles::from_bytes(&path); self.pending_removal.remove(&path); self.dirty.insert(path.clone()); - let encoded_nodes = self.db.get_nodes_in_path(path.clone())?; + let encoded_nodes = self.db.get_nodes_in_path(path.clone(), 0)?; let keys = (0..path.len()).map(|i| path.slice(0, i)); let mut nodes = Vec::with_capacity(path.len()); for (path, encoded) in std::iter::zip(keys, encoded_nodes) { diff --git a/crates/storage/trie_db/layering.rs b/crates/storage/trie_db/layering.rs index 8a7d88422ba..749e25e4e99 100644 --- a/crates/storage/trie_db/layering.rs +++ b/crates/storage/trie_db/layering.rs @@ -209,13 +209,17 @@ impl TrieDB for TrieWrapper { self.db.get(key) } - fn get_nodes_in_path(&self, key: Nibbles) -> Result>>, TrieError> { + fn get_nodes_in_path( + &self, + key: Nibbles, + start: usize, + ) -> Result>>, TrieError> { let key = apply_prefix(self.prefix, key); - let start = apply_prefix(self.prefix, Nibbles::default()).len(); + let sk_start = apply_prefix(self.prefix, Nibbles::default()).len(); let end = key.len(); let mut values = match self.inner.get(self.state_root, key.as_ref()) { Some(_) => vec![None; end - start], // If we have the FKV key then we have the nodes - None => self.db.get_nodes_in_path(key.clone())?.split_off(start), + None => self.db.get_nodes_in_path(key.clone(), start + sk_start)?, }; for (i, j) in (start..end).enumerate() { if let Some(value) = self.inner.get(self.state_root, &key.as_ref()[..j]) { diff --git a/crates/storage/trie_db/rocksdb.rs b/crates/storage/trie_db/rocksdb.rs index 7f2250dbe5e..d60c4b60f88 100644 --- a/crates/storage/trie_db/rocksdb.rs +++ b/crates/storage/trie_db/rocksdb.rs @@ -107,11 +107,14 @@ impl TrieDB for RocksDBTrieDB { Ok(res) } - fn get_nodes_in_path(&self, key: Nibbles) -> Result>>, TrieError> { + fn get_nodes_in_path( + &self, + key: Nibbles, + start: usize, + ) -> Result>>, TrieError> { let cf = self.cf_handle()?; let mut res = Vec::with_capacity(key.len()); let key = self.make_key(key); - let start = self.make_key(Nibbles::default()).len(); let end = key.len(); let keys = (start..end).map(|i| &key[..i]); let values = self.db.batched_multi_get_cf(&cf, keys, true); From 380bca19bf85c83291d447f3abfb2e98c427cd15 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Thu, 13 Nov 2025 20:14:48 -0300 Subject: [PATCH 11/18] ignore start at layer level, use only to skip unneeded nodes --- crates/storage/trie_db/layering.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/storage/trie_db/layering.rs b/crates/storage/trie_db/layering.rs index 749e25e4e99..c1f348de069 100644 --- a/crates/storage/trie_db/layering.rs +++ b/crates/storage/trie_db/layering.rs @@ -212,14 +212,14 @@ impl TrieDB for TrieWrapper { fn get_nodes_in_path( &self, key: Nibbles, - start: usize, + _start: usize, ) -> Result>>, TrieError> { let key = apply_prefix(self.prefix, key); - let sk_start = apply_prefix(self.prefix, Nibbles::default()).len(); + let start = apply_prefix(self.prefix, Nibbles::default()).len(); let end = key.len(); let mut values = match self.inner.get(self.state_root, key.as_ref()) { Some(_) => vec![None; end - start], // If we have the FKV key then we have the nodes - None => self.db.get_nodes_in_path(key.clone(), start + sk_start)?, + None => self.db.get_nodes_in_path(key.clone(), start)?, }; for (i, j) in (start..end).enumerate() { if let Some(value) = self.inner.get(self.state_root, &key.as_ref()[..j]) { From ef4bd4e6106f339d11de7d0c25f062fae184abf7 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Thu, 13 Nov 2025 20:19:54 -0300 Subject: [PATCH 12/18] reunify --- crates/blockchain/blockchain.rs | 2 +- crates/common/trie/trie.rs | 23 ----------------------- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/crates/blockchain/blockchain.rs b/crates/blockchain/blockchain.rs index 13242854a93..614344d5137 100644 --- a/crates/blockchain/blockchain.rs +++ b/crates/blockchain/blockchain.rs @@ -433,7 +433,7 @@ impl Blockchain { storage_trie.remove(&hashed_key)?; } else { trace!(slot = hex::encode(&hashed_key), "Inserting"); - storage_trie.insert_storage(hashed_key, storage_value.encode_to_vec())?; + storage_trie.insert(hashed_key, storage_value.encode_to_vec())?; } } trace!( diff --git a/crates/common/trie/trie.rs b/crates/common/trie/trie.rs index 4f4d653d0b1..ac074d1606f 100644 --- a/crates/common/trie/trie.rs +++ b/crates/common/trie/trie.rs @@ -130,29 +130,6 @@ impl Trie { let path = Nibbles::from_bytes(&path); self.pending_removal.remove(&path); self.dirty.insert(path.clone()); - - if self.root.is_valid() { - // If the trie is not empty, call the root node's insertion logic. - self.root - .get_node_mut(self.db.as_ref(), Nibbles::default())? - .ok_or_else(|| { - TrieError::InconsistentTree(Box::new(InconsistentTreeError::RootNotFoundNoHash)) - })? - .insert(self.db.as_ref(), path, value)? - } else { - // If the trie is empty, just add a leaf. - self.root = Node::from(LeafNode::new(path, value)).into() - }; - self.root.clear_hash(); - - Ok(()) - } - - /// Insert an RLP-encoded value into the trie. - pub fn insert_storage(&mut self, path: PathRLP, value: ValueRLP) -> Result<(), TrieError> { - let path = Nibbles::from_bytes(&path); - self.pending_removal.remove(&path); - self.dirty.insert(path.clone()); let encoded_nodes = self.db.get_nodes_in_path(path.clone(), 0)?; let keys = (0..path.len()).map(|i| path.slice(0, i)); let mut nodes = Vec::with_capacity(path.len()); From dd99ca40662b3152eff73d2a8607e8ff4760138a Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Fri, 14 Nov 2025 12:25:57 -0300 Subject: [PATCH 13/18] lazy fetch --- crates/common/trie/db.rs | 77 +++++++++++++++++++++++++++++- crates/common/trie/trie.rs | 28 ++--------- crates/storage/trie_db/layering.rs | 4 +- 3 files changed, 81 insertions(+), 28 deletions(-) diff --git a/crates/common/trie/db.rs b/crates/common/trie/db.rs index 3331828eb69..275363578e2 100644 --- a/crates/common/trie/db.rs +++ b/crates/common/trie/db.rs @@ -4,7 +4,10 @@ use ethrex_rlp::encode::RLPEncode; use crate::{Nibbles, Node, NodeRLP, Trie, error::TrieError}; use std::{ collections::BTreeMap, - sync::{Arc, Mutex}, + sync::{ + Arc, Mutex, + atomic::{AtomicPtr, AtomicUsize}, + }, }; // Nibbles -> encoded node @@ -42,6 +45,78 @@ pub trait TrieDB: Send + Sync { } } +pub(crate) struct BulkTrieDB<'a> { + db: &'a dyn TrieDB, + path: Nibbles, + nodes: AtomicPtr>>, + nodes_count: AtomicUsize, + nodes_cap: AtomicUsize, +} + +impl<'a> BulkTrieDB<'a> { + pub fn new(db: &'a dyn TrieDB, path: Nibbles) -> Self { + Self { + db, + path, + // NOTE: in normal usage, none of these atomics will be contended, + // they were chosen just to avoid playing with `UnsafeCell` while + // meeting the trait requirements of `Send + Sync`. + nodes: AtomicPtr::default(), + nodes_count: AtomicUsize::default(), + // NOTE: needed to meet the invariants for freeing + nodes_cap: AtomicUsize::default(), + } + } + + fn get_nodes(&self, first: usize) -> Result<&'a [Option>], TrieError> { + // NOTE: in theory, `leak` could produce a `NULL` pointer if the vector + // is empty. Using `with_capacity` guarantees it's not `NULL` because it + // forces preallocation. So, in this initial version that call to + // `with_capacity` has semantic relevance and is not just an optimization. + use std::sync::atomic::Ordering::Relaxed; + let nodes_ptr = self.nodes.load(Relaxed); + if !nodes_ptr.is_null() { + let count = self.nodes_count.load(Relaxed); + let nodes = unsafe { std::slice::from_raw_parts(nodes_ptr, count) }; + return Ok(nodes); + } + let encoded_nodes = self.db.get_nodes_in_path(self.path.clone(), first)?; + let cap = encoded_nodes.capacity(); + let encoded_nodes = encoded_nodes.leak(); + self.nodes_count.store(encoded_nodes.len(), Relaxed); + self.nodes_cap.store(cap, Relaxed); + self.nodes.store(encoded_nodes.as_ptr().cast_mut(), Relaxed); + Ok(encoded_nodes) + } +} +impl<'a> Drop for BulkTrieDB<'a> { + fn drop(&mut self) { + use std::sync::atomic::Ordering::Relaxed; + let ptr = self.nodes.load(Relaxed); + if ptr.is_null() { + return; + } + let len = self.nodes_count.load(Relaxed); + let cap = self.nodes_cap.load(Relaxed); + unsafe { Vec::from_raw_parts(ptr, len, cap) }; + } +} +impl<'a> TrieDB for BulkTrieDB<'a> { + fn get(&self, key: Nibbles) -> Result>, TrieError> { + if !self.path.as_ref().starts_with(key.as_ref()) { + // key not in path + return Ok(None); + } + let nodes = self.get_nodes(key.len())?; + // Because we skip some nodes, we need to offset the relative position + // by the difference between the full path and what we actually have. + let index = key.len() - (self.path.len() - nodes.len()); + Ok(nodes.get(index).cloned().flatten()) + } + fn put_batch(&self, key_values: Vec<(Nibbles, Vec)>) -> Result<(), TrieError> { + unimplemented!() + } +} /// InMemory implementation for the TrieDB trait, with get and put operations. #[derive(Default)] pub struct InMemoryTrieDB { diff --git a/crates/common/trie/trie.rs b/crates/common/trie/trie.rs index ac074d1606f..a54d3817626 100644 --- a/crates/common/trie/trie.rs +++ b/crates/common/trie/trie.rs @@ -130,38 +130,16 @@ impl Trie { let path = Nibbles::from_bytes(&path); self.pending_removal.remove(&path); self.dirty.insert(path.clone()); - let encoded_nodes = self.db.get_nodes_in_path(path.clone(), 0)?; - let keys = (0..path.len()).map(|i| path.slice(0, i)); - let mut nodes = Vec::with_capacity(path.len()); - for (path, encoded) in std::iter::zip(keys, encoded_nodes) { - let Some(encoded) = encoded else { - continue; - }; - nodes.push((path, encoded)); - } - - struct VecTrieDB(Vec<(Nibbles, Vec)>); - impl TrieDB for VecTrieDB { - fn get(&self, key: Nibbles) -> Result>, TrieError> { - let Ok(pos) = self.0.binary_search_by(|(k, _)| k.cmp(&key)) else { - return Ok(None); - }; - Ok(self.0.get(pos).map(|(_, v)| v.clone())) - } - fn put_batch(&self, key_values: Vec<(Nibbles, Vec)>) -> Result<(), TrieError> { - unimplemented!() - } - } - let fake_db = VecTrieDB(nodes); + let bulk_db = db::BulkTrieDB::new(self.db.as_ref(), path.clone()); if self.root.is_valid() { // If the trie is not empty, call the root node's insertion logic. self.root - .get_node_mut(&fake_db, Nibbles::default())? + .get_node_mut(&bulk_db, Nibbles::default())? .ok_or_else(|| { TrieError::InconsistentTree(Box::new(InconsistentTreeError::RootNotFoundNoHash)) })? - .insert(&fake_db, path, value)? + .insert(&bulk_db, path, value)? } else { // If the trie is empty, just add a leaf. self.root = Node::from(LeafNode::new(path, value)).into() diff --git a/crates/storage/trie_db/layering.rs b/crates/storage/trie_db/layering.rs index c1f348de069..517cabf68a1 100644 --- a/crates/storage/trie_db/layering.rs +++ b/crates/storage/trie_db/layering.rs @@ -212,10 +212,10 @@ impl TrieDB for TrieWrapper { fn get_nodes_in_path( &self, key: Nibbles, - _start: usize, + start: usize, ) -> Result>>, TrieError> { let key = apply_prefix(self.prefix, key); - let start = apply_prefix(self.prefix, Nibbles::default()).len(); + let start = start + apply_prefix(self.prefix, Nibbles::default()).len(); let end = key.len(); let mut values = match self.inner.get(self.state_root, key.as_ref()) { Some(_) => vec![None; end - start], // If we have the FKV key then we have the nodes From add922646b6f1e8b57306a4942023707a869db07 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Fri, 14 Nov 2025 17:13:52 -0300 Subject: [PATCH 14/18] prefix extractors --- Cargo.lock | 80 ++++++------------------------ crates/storage/store_db/rocksdb.rs | 5 +- 2 files changed, 20 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c3bfe9bb938..65fa7f81ab5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -129,7 +129,7 @@ dependencies = [ "hashbrown 0.16.0", "indexmap 2.12.0", "itoa", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", + "k256", "paste", "rand 0.9.2", "ruint", @@ -1634,7 +1634,7 @@ dependencies = [ "coins-core", "digest 0.10.7", "hmac", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", + "k256", "serde", "sha2", "thiserror 1.0.69", @@ -2703,25 +2703,12 @@ dependencies = [ "der", "digest 0.10.7", "elliptic-curve", - "rfc6979 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rfc6979", "serdect", "signature", "spki", ] -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "git+https://github.com/sp1-patches/signatures.git?tag=patch-16.9-sp1-4.1.0#1880299a48fe7ef249edaa616fd411239fb5daf1" -dependencies = [ - "der", - "digest 0.10.7", - "elliptic-curve", - "rfc6979 0.4.0 (git+https://github.com/sp1-patches/signatures.git?tag=patch-16.9-sp1-4.1.0)", - "signature", - "spki", -] - [[package]] name = "educe" version = "0.6.0" @@ -2758,7 +2745,6 @@ dependencies = [ "ff 0.13.1", "generic-array 0.14.9", "group 0.13.0", - "hkdf", "pem-rfc7468", "pkcs8", "rand_core 0.6.4", @@ -2813,7 +2799,7 @@ dependencies = [ "base64 0.21.7", "bytes", "hex", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", + "k256", "log", "rand 0.8.5", "rlp 0.5.2", @@ -3126,7 +3112,7 @@ dependencies = [ "elliptic-curve", "ethabi", "generic-array 0.14.9", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", + "k256", "num_enum 0.7.5", "once_cell", "open-fastrlp", @@ -3558,7 +3544,7 @@ dependencies = [ "ethrex-crypto", "ethrex-rlp", "hex", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", + "k256", "lambdaworks-math 0.13.0", "lazy_static", "malachite 0.6.1", @@ -4681,15 +4667,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" -[[package]] -name = "hkdf" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac", -] - [[package]] name = "hmac" version = "0.12.1" @@ -5468,7 +5445,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if 1.0.4", - "ecdsa 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", + "ecdsa", "elliptic-curve", "once_cell", "serdect", @@ -5476,21 +5453,6 @@ dependencies = [ "signature", ] -[[package]] -name = "k256" -version = "0.13.4" -source = "git+https://github.com/sp1-patches/elliptic-curves?tag=patch-k256-13.4-sp1-5.0.0#f7d8998e05d8cbcbd8e543eba1030a7385011fa8" -dependencies = [ - "cfg-if 1.0.4", - "ecdsa 0.16.9 (git+https://github.com/sp1-patches/signatures.git?tag=patch-16.9-sp1-4.1.0)", - "elliptic-curve", - "hex", - "once_cell", - "sha2", - "signature", - "sp1-lib", -] - [[package]] name = "keccak" version = "0.1.5" @@ -6733,7 +6695,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ - "ecdsa 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", + "ecdsa", "elliptic-curve", "primeorder", "sha2", @@ -8221,15 +8183,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "git+https://github.com/sp1-patches/signatures.git?tag=patch-16.9-sp1-4.1.0#1880299a48fe7ef249edaa616fd411239fb5daf1" -dependencies = [ - "hmac", - "subtle", -] - [[package]] name = "rgb" version = "0.8.52" @@ -9257,19 +9210,19 @@ dependencies = [ [[package]] name = "secp256k1" version = "0.30.0" -source = "git+https://github.com/sp1-patches/rust-secp256k1?tag=patch-0.30.0-sp1-5.0.0#04d87db04bcc2dc5dd8e1ab3f046cc655440d07a" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" dependencies = [ "bitcoin_hashes", - "cfg-if 1.0.4", - "k256 0.13.4 (git+https://github.com/sp1-patches/elliptic-curves?tag=patch-k256-13.4-sp1-5.0.0)", "rand 0.8.5", "secp256k1-sys", ] [[package]] name = "secp256k1-sys" -version = "0.10.0" -source = "git+https://github.com/sp1-patches/rust-secp256k1?tag=patch-0.30.0-sp1-5.0.0#04d87db04bcc2dc5dd8e1ab3f046cc655440d07a" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" dependencies = [ "cc", ] @@ -9785,7 +9738,7 @@ dependencies = [ "hashbrown 0.14.5", "hex", "itertools 0.13.0", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", + "k256", "num", "num_cpus", "p256", @@ -9853,7 +9806,7 @@ dependencies = [ "elliptic-curve", "generic-array 1.1.0", "itertools 0.13.0", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", + "k256", "num", "p256", "p3-field", @@ -9881,7 +9834,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fce8ad0f153443d09d398eccb650a0b2dcbf829470e394e4bf60ec4379c7af93" dependencies = [ "bincode", - "elliptic-curve", "serde", "sp1-primitives", ] @@ -10106,7 +10058,7 @@ dependencies = [ "hex", "indicatif", "itertools 0.13.0", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", + "k256", "p3-baby-bear", "p3-field", "p3-fri", diff --git a/crates/storage/store_db/rocksdb.rs b/crates/storage/store_db/rocksdb.rs index d2273cd3895..ca18f0d9326 100644 --- a/crates/storage/store_db/rocksdb.rs +++ b/crates/storage/store_db/rocksdb.rs @@ -16,7 +16,7 @@ use ethrex_common::{ use ethrex_trie::{Nibbles, Node, Trie}; use rocksdb::{ BlockBasedOptions, BoundColumnFamily, ColumnFamilyDescriptor, DBWithThreadMode, MultiThreaded, - Options, WriteBatch, checkpoint::Checkpoint, + Options, SliceTransform, WriteBatch, checkpoint::Checkpoint, }; use std::{ collections::HashSet, @@ -296,6 +296,9 @@ impl Store { cf_opts.set_min_write_buffer_number_to_merge(2); cf_opts.set_target_file_size_base(256 * 1024 * 1024); // 256MB cf_opts.set_memtable_prefix_bloom_ratio(0.2); // Bloom filter + cf_opts.set_prefix_extractor(SliceTransform::create_fixed_prefix( + (CF_STORAGE_FLATKEYVALUE == cf_name) as usize * 66 + 10, + )); let mut block_opts = BlockBasedOptions::default(); block_opts.set_block_size(16 * 1024); // 16KB From 8cc1a034a9451552f0c0defdfa9a23f85accf76d Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Sat, 15 Nov 2025 01:11:00 -0300 Subject: [PATCH 15/18] bring fewer nodes --- crates/common/trie/trie.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/common/trie/trie.rs b/crates/common/trie/trie.rs index a54d3817626..5075e63b9d4 100644 --- a/crates/common/trie/trie.rs +++ b/crates/common/trie/trie.rs @@ -130,7 +130,12 @@ impl Trie { let path = Nibbles::from_bytes(&path); self.pending_removal.remove(&path); self.dirty.insert(path.clone()); - let bulk_db = db::BulkTrieDB::new(self.db.as_ref(), path.clone()); + // Cap the path to 14 nibbles. Rationale: + // - Keccak256 collision in 7 or more bytes is considered impossibly low; + // - Any branch at position `p` requires a collision of at least `p` nibbles; + // - Any extension at position `p` requires a branch at position `q > p`. + let bulk_path = path.slice(0, 14.min(path.len())); + let bulk_db = db::BulkTrieDB::new(self.db.as_ref(), bulk_path); if self.root.is_valid() { // If the trie is not empty, call the root node's insertion logic. From bfd4616b220c8b6a8788b730352471f4621683cc Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Sat, 15 Nov 2025 01:11:07 -0300 Subject: [PATCH 16/18] Revert "prefix extractors" This reverts commit add922646b6f1e8b57306a4942023707a869db07. --- Cargo.lock | 80 ++++++++++++++++++++++++------ crates/storage/store_db/rocksdb.rs | 5 +- 2 files changed, 65 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 65fa7f81ab5..c3bfe9bb938 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -129,7 +129,7 @@ dependencies = [ "hashbrown 0.16.0", "indexmap 2.12.0", "itoa", - "k256", + "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", "paste", "rand 0.9.2", "ruint", @@ -1634,7 +1634,7 @@ dependencies = [ "coins-core", "digest 0.10.7", "hmac", - "k256", + "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde", "sha2", "thiserror 1.0.69", @@ -2703,12 +2703,25 @@ dependencies = [ "der", "digest 0.10.7", "elliptic-curve", - "rfc6979", + "rfc6979 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "serdect", "signature", "spki", ] +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "git+https://github.com/sp1-patches/signatures.git?tag=patch-16.9-sp1-4.1.0#1880299a48fe7ef249edaa616fd411239fb5daf1" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979 0.4.0 (git+https://github.com/sp1-patches/signatures.git?tag=patch-16.9-sp1-4.1.0)", + "signature", + "spki", +] + [[package]] name = "educe" version = "0.6.0" @@ -2745,6 +2758,7 @@ dependencies = [ "ff 0.13.1", "generic-array 0.14.9", "group 0.13.0", + "hkdf", "pem-rfc7468", "pkcs8", "rand_core 0.6.4", @@ -2799,7 +2813,7 @@ dependencies = [ "base64 0.21.7", "bytes", "hex", - "k256", + "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", "log", "rand 0.8.5", "rlp 0.5.2", @@ -3112,7 +3126,7 @@ dependencies = [ "elliptic-curve", "ethabi", "generic-array 0.14.9", - "k256", + "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", "num_enum 0.7.5", "once_cell", "open-fastrlp", @@ -3544,7 +3558,7 @@ dependencies = [ "ethrex-crypto", "ethrex-rlp", "hex", - "k256", + "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", "lambdaworks-math 0.13.0", "lazy_static", "malachite 0.6.1", @@ -4667,6 +4681,15 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + [[package]] name = "hmac" version = "0.12.1" @@ -5445,7 +5468,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if 1.0.4", - "ecdsa", + "ecdsa 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", "elliptic-curve", "once_cell", "serdect", @@ -5453,6 +5476,21 @@ dependencies = [ "signature", ] +[[package]] +name = "k256" +version = "0.13.4" +source = "git+https://github.com/sp1-patches/elliptic-curves?tag=patch-k256-13.4-sp1-5.0.0#f7d8998e05d8cbcbd8e543eba1030a7385011fa8" +dependencies = [ + "cfg-if 1.0.4", + "ecdsa 0.16.9 (git+https://github.com/sp1-patches/signatures.git?tag=patch-16.9-sp1-4.1.0)", + "elliptic-curve", + "hex", + "once_cell", + "sha2", + "signature", + "sp1-lib", +] + [[package]] name = "keccak" version = "0.1.5" @@ -6695,7 +6733,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ - "ecdsa", + "ecdsa 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", "elliptic-curve", "primeorder", "sha2", @@ -8183,6 +8221,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "git+https://github.com/sp1-patches/signatures.git?tag=patch-16.9-sp1-4.1.0#1880299a48fe7ef249edaa616fd411239fb5daf1" +dependencies = [ + "hmac", + "subtle", +] + [[package]] name = "rgb" version = "0.8.52" @@ -9210,19 +9257,19 @@ dependencies = [ [[package]] name = "secp256k1" version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" +source = "git+https://github.com/sp1-patches/rust-secp256k1?tag=patch-0.30.0-sp1-5.0.0#04d87db04bcc2dc5dd8e1ab3f046cc655440d07a" dependencies = [ "bitcoin_hashes", + "cfg-if 1.0.4", + "k256 0.13.4 (git+https://github.com/sp1-patches/elliptic-curves?tag=patch-k256-13.4-sp1-5.0.0)", "rand 0.8.5", "secp256k1-sys", ] [[package]] name = "secp256k1-sys" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +version = "0.10.0" +source = "git+https://github.com/sp1-patches/rust-secp256k1?tag=patch-0.30.0-sp1-5.0.0#04d87db04bcc2dc5dd8e1ab3f046cc655440d07a" dependencies = [ "cc", ] @@ -9738,7 +9785,7 @@ dependencies = [ "hashbrown 0.14.5", "hex", "itertools 0.13.0", - "k256", + "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", "num", "num_cpus", "p256", @@ -9806,7 +9853,7 @@ dependencies = [ "elliptic-curve", "generic-array 1.1.0", "itertools 0.13.0", - "k256", + "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", "num", "p256", "p3-field", @@ -9834,6 +9881,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fce8ad0f153443d09d398eccb650a0b2dcbf829470e394e4bf60ec4379c7af93" dependencies = [ "bincode", + "elliptic-curve", "serde", "sp1-primitives", ] @@ -10058,7 +10106,7 @@ dependencies = [ "hex", "indicatif", "itertools 0.13.0", - "k256", + "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", "p3-baby-bear", "p3-field", "p3-fri", diff --git a/crates/storage/store_db/rocksdb.rs b/crates/storage/store_db/rocksdb.rs index ca18f0d9326..d2273cd3895 100644 --- a/crates/storage/store_db/rocksdb.rs +++ b/crates/storage/store_db/rocksdb.rs @@ -16,7 +16,7 @@ use ethrex_common::{ use ethrex_trie::{Nibbles, Node, Trie}; use rocksdb::{ BlockBasedOptions, BoundColumnFamily, ColumnFamilyDescriptor, DBWithThreadMode, MultiThreaded, - Options, SliceTransform, WriteBatch, checkpoint::Checkpoint, + Options, WriteBatch, checkpoint::Checkpoint, }; use std::{ collections::HashSet, @@ -296,9 +296,6 @@ impl Store { cf_opts.set_min_write_buffer_number_to_merge(2); cf_opts.set_target_file_size_base(256 * 1024 * 1024); // 256MB cf_opts.set_memtable_prefix_bloom_ratio(0.2); // Bloom filter - cf_opts.set_prefix_extractor(SliceTransform::create_fixed_prefix( - (CF_STORAGE_FLATKEYVALUE == cf_name) as usize * 66 + 10, - )); let mut block_opts = BlockBasedOptions::default(); block_opts.set_block_size(16 * 1024); // 16KB From e0088194b0dff8476b959a6912894ef298430088 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Sat, 15 Nov 2025 21:18:08 -0300 Subject: [PATCH 17/18] way too much stuff going on --- crates/common/trie/db.rs | 17 +++++--- crates/common/trie/trie.rs | 13 +++--- crates/storage/trie_db/layering.rs | 65 +++++++++++++++++++++++++----- crates/storage/trie_db/rocksdb.rs | 4 +- 4 files changed, 74 insertions(+), 25 deletions(-) diff --git a/crates/common/trie/db.rs b/crates/common/trie/db.rs index 275363578e2..8d6cf4af70e 100644 --- a/crates/common/trie/db.rs +++ b/crates/common/trie/db.rs @@ -19,9 +19,10 @@ pub trait TrieDB: Send + Sync { &self, key: Nibbles, start: usize, + count: usize, ) -> Result>>, TrieError> { - let keys = (start..key.len()).map(|i| key.slice(0, i)); - let mut values = Vec::with_capacity(key.len()); + let keys = (start..start + count).map(|i| key.slice(0, i)); + let mut values = Vec::with_capacity(count); for key in keys { values.push(self.get(key)?); } @@ -51,6 +52,7 @@ pub(crate) struct BulkTrieDB<'a> { nodes: AtomicPtr>>, nodes_count: AtomicUsize, nodes_cap: AtomicUsize, + first_idx: AtomicUsize, } impl<'a> BulkTrieDB<'a> { @@ -65,10 +67,11 @@ impl<'a> BulkTrieDB<'a> { nodes_count: AtomicUsize::default(), // NOTE: needed to meet the invariants for freeing nodes_cap: AtomicUsize::default(), + first_idx: AtomicUsize::default(), } } - fn get_nodes(&self, first: usize) -> Result<&'a [Option>], TrieError> { + fn get_nodes(&self, first: usize, count: usize) -> Result<&'a [Option>], TrieError> { // NOTE: in theory, `leak` could produce a `NULL` pointer if the vector // is empty. Using `with_capacity` guarantees it's not `NULL` because it // forces preallocation. So, in this initial version that call to @@ -80,12 +83,13 @@ impl<'a> BulkTrieDB<'a> { let nodes = unsafe { std::slice::from_raw_parts(nodes_ptr, count) }; return Ok(nodes); } - let encoded_nodes = self.db.get_nodes_in_path(self.path.clone(), first)?; + let encoded_nodes = self.db.get_nodes_in_path(self.path.clone(), first, count)?; let cap = encoded_nodes.capacity(); let encoded_nodes = encoded_nodes.leak(); self.nodes_count.store(encoded_nodes.len(), Relaxed); self.nodes_cap.store(cap, Relaxed); self.nodes.store(encoded_nodes.as_ptr().cast_mut(), Relaxed); + self.first_idx.store(first, Relaxed); Ok(encoded_nodes) } } @@ -107,10 +111,11 @@ impl<'a> TrieDB for BulkTrieDB<'a> { // key not in path return Ok(None); } - let nodes = self.get_nodes(key.len())?; + let count = 14; //self.path.len().saturating_sub(key.len()).min(14); + let nodes = self.get_nodes(key.len(), count)?; // Because we skip some nodes, we need to offset the relative position // by the difference between the full path and what we actually have. - let index = key.len() - (self.path.len() - nodes.len()); + let index = key.len() - self.first_idx.load(std::sync::atomic::Ordering::Relaxed); Ok(nodes.get(index).cloned().flatten()) } fn put_batch(&self, key_values: Vec<(Nibbles, Vec)>) -> Result<(), TrieError> { diff --git a/crates/common/trie/trie.rs b/crates/common/trie/trie.rs index 5075e63b9d4..128beccc5bd 100644 --- a/crates/common/trie/trie.rs +++ b/crates/common/trie/trie.rs @@ -130,12 +130,7 @@ impl Trie { let path = Nibbles::from_bytes(&path); self.pending_removal.remove(&path); self.dirty.insert(path.clone()); - // Cap the path to 14 nibbles. Rationale: - // - Keccak256 collision in 7 or more bytes is considered impossibly low; - // - Any branch at position `p` requires a collision of at least `p` nibbles; - // - Any extension at position `p` requires a branch at position `q > p`. - let bulk_path = path.slice(0, 14.min(path.len())); - let bulk_db = db::BulkTrieDB::new(self.db.as_ref(), bulk_path); + let bulk_db = db::BulkTrieDB::new(self.db.as_ref(), path.clone()); if self.root.is_valid() { // If the trie is not empty, call the root node's insertion logic. @@ -162,15 +157,17 @@ impl Trie { return Ok(None); } self.pending_removal.insert(Nibbles::from_bytes(path)); + let path = Nibbles::from_bytes(path); + let bulk_db = db::BulkTrieDB::new(self.db.as_ref(), path.clone()); // If the trie is not empty, call the root node's removal logic. let (is_trie_empty, value) = self .root - .get_node_mut(self.db.as_ref(), Nibbles::default())? + .get_node_mut(&bulk_db, Nibbles::default())? .ok_or_else(|| { TrieError::InconsistentTree(Box::new(InconsistentTreeError::RootNotFoundNoHash)) })? - .remove(self.db.as_ref(), Nibbles::from_bytes(path))?; + .remove(&bulk_db, path)?; if is_trie_empty { self.root = NodeRef::default(); } else { diff --git a/crates/storage/trie_db/layering.rs b/crates/storage/trie_db/layering.rs index 517cabf68a1..ac0fdddcd9c 100644 --- a/crates/storage/trie_db/layering.rs +++ b/crates/storage/trie_db/layering.rs @@ -77,6 +77,52 @@ impl TrieLayerCache { None } + /// Returns tuple containing a vector of `count` optional nodes (None if absent) in matching + /// starting at `path[..start]` and a boolean indicating whether the DB needs to be queried. + /// The vector is empty if none of the nodes can be found. + pub fn get_nodes_in_path( + &self, + state_root: H256, + path: &[u8], + start: usize, + count: usize, + ) -> (Vec>>, bool) { + let mut nodes = vec![None; count]; + if let Some(filter) = &self.bloom + && !filter.contains(&path[..start]) + { + // The queried node is not in the diff layers, so none of the children can be. + return (nodes, true); + } + let mut rem: Vec<_> = (start..start + count) + .rev() + .filter(|i| self.bloom.as_ref().is_none_or(|f| f.contains(&path[..*i]))) + .collect(); + let mut has_value = false; + let maybe_has_value = self.bloom.as_ref().is_none_or(|f| f.contains(path)); + let mut current_state_root = state_root; + while let Some(layer) = self.layers.get(¤t_state_root) { + let mut new_size = rem.len(); + for (i, j) in rem.iter_mut().enumerate().rev() { + let key = &path[..*j]; + if let Some(value) = layer.nodes.get(key) { + new_size = i; + nodes[*j - start] = Some(value.clone()); + } + } + rem.truncate(new_size); + if maybe_has_value && layer.nodes.contains_key(path) { + has_value = true; + break; + } + if rem.is_empty() { + break; + } + current_state_root = layer.parent; + } + (nodes, !has_value) + } + // TODO: use finalized hash to know when to commit pub fn get_commitable(&self, mut state_root: H256, commit_threshold: usize) -> Option { let mut counter = 0; @@ -213,18 +259,19 @@ impl TrieDB for TrieWrapper { &self, key: Nibbles, start: usize, + count: usize, ) -> Result>>, TrieError> { let key = apply_prefix(self.prefix, key); let start = start + apply_prefix(self.prefix, Nibbles::default()).len(); - let end = key.len(); - let mut values = match self.inner.get(self.state_root, key.as_ref()) { - Some(_) => vec![None; end - start], // If we have the FKV key then we have the nodes - None => self.db.get_nodes_in_path(key.clone(), start)?, - }; - for (i, j) in (start..end).enumerate() { - if let Some(value) = self.inner.get(self.state_root, &key.as_ref()[..j]) { - values[i] = Some(value); - } + let (mut values, query_db) = + self.inner + .get_nodes_in_path(self.state_root, key.as_ref(), start, count); + if query_db { + let db_count = values.iter().rev().take_while(|v| v.is_none()).count(); + let db_start = start + count - db_count; + let db_nodes = self.db.get_nodes_in_path(key, db_start, db_count)?; + values.truncate(values.len() - db_count); + values.extend(db_nodes); } Ok(values) } diff --git a/crates/storage/trie_db/rocksdb.rs b/crates/storage/trie_db/rocksdb.rs index d60c4b60f88..9c3a0562c20 100644 --- a/crates/storage/trie_db/rocksdb.rs +++ b/crates/storage/trie_db/rocksdb.rs @@ -111,12 +111,12 @@ impl TrieDB for RocksDBTrieDB { &self, key: Nibbles, start: usize, + count: usize, ) -> Result>>, TrieError> { let cf = self.cf_handle()?; let mut res = Vec::with_capacity(key.len()); let key = self.make_key(key); - let end = key.len(); - let keys = (start..end).map(|i| &key[..i]); + let keys = (start..start + count).map(|i| &key[..i]); let values = self.db.batched_multi_get_cf(&cf, keys, true); for value in values { let value = value From f4022e569660e16672dd449a488015d140d2a319 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Tue, 18 Nov 2025 17:22:18 -0300 Subject: [PATCH 18/18] bulk accounts only --- Cargo.lock | 80 +++++++-------------------------- crates/blockchain/blockchain.rs | 4 +- crates/common/trie/trie.rs | 54 +++++++++++++++++++++- 3 files changed, 70 insertions(+), 68 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c3bfe9bb938..65fa7f81ab5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -129,7 +129,7 @@ dependencies = [ "hashbrown 0.16.0", "indexmap 2.12.0", "itoa", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", + "k256", "paste", "rand 0.9.2", "ruint", @@ -1634,7 +1634,7 @@ dependencies = [ "coins-core", "digest 0.10.7", "hmac", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", + "k256", "serde", "sha2", "thiserror 1.0.69", @@ -2703,25 +2703,12 @@ dependencies = [ "der", "digest 0.10.7", "elliptic-curve", - "rfc6979 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rfc6979", "serdect", "signature", "spki", ] -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "git+https://github.com/sp1-patches/signatures.git?tag=patch-16.9-sp1-4.1.0#1880299a48fe7ef249edaa616fd411239fb5daf1" -dependencies = [ - "der", - "digest 0.10.7", - "elliptic-curve", - "rfc6979 0.4.0 (git+https://github.com/sp1-patches/signatures.git?tag=patch-16.9-sp1-4.1.0)", - "signature", - "spki", -] - [[package]] name = "educe" version = "0.6.0" @@ -2758,7 +2745,6 @@ dependencies = [ "ff 0.13.1", "generic-array 0.14.9", "group 0.13.0", - "hkdf", "pem-rfc7468", "pkcs8", "rand_core 0.6.4", @@ -2813,7 +2799,7 @@ dependencies = [ "base64 0.21.7", "bytes", "hex", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", + "k256", "log", "rand 0.8.5", "rlp 0.5.2", @@ -3126,7 +3112,7 @@ dependencies = [ "elliptic-curve", "ethabi", "generic-array 0.14.9", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", + "k256", "num_enum 0.7.5", "once_cell", "open-fastrlp", @@ -3558,7 +3544,7 @@ dependencies = [ "ethrex-crypto", "ethrex-rlp", "hex", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", + "k256", "lambdaworks-math 0.13.0", "lazy_static", "malachite 0.6.1", @@ -4681,15 +4667,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" -[[package]] -name = "hkdf" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac", -] - [[package]] name = "hmac" version = "0.12.1" @@ -5468,7 +5445,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if 1.0.4", - "ecdsa 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", + "ecdsa", "elliptic-curve", "once_cell", "serdect", @@ -5476,21 +5453,6 @@ dependencies = [ "signature", ] -[[package]] -name = "k256" -version = "0.13.4" -source = "git+https://github.com/sp1-patches/elliptic-curves?tag=patch-k256-13.4-sp1-5.0.0#f7d8998e05d8cbcbd8e543eba1030a7385011fa8" -dependencies = [ - "cfg-if 1.0.4", - "ecdsa 0.16.9 (git+https://github.com/sp1-patches/signatures.git?tag=patch-16.9-sp1-4.1.0)", - "elliptic-curve", - "hex", - "once_cell", - "sha2", - "signature", - "sp1-lib", -] - [[package]] name = "keccak" version = "0.1.5" @@ -6733,7 +6695,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ - "ecdsa 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", + "ecdsa", "elliptic-curve", "primeorder", "sha2", @@ -8221,15 +8183,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "git+https://github.com/sp1-patches/signatures.git?tag=patch-16.9-sp1-4.1.0#1880299a48fe7ef249edaa616fd411239fb5daf1" -dependencies = [ - "hmac", - "subtle", -] - [[package]] name = "rgb" version = "0.8.52" @@ -9257,19 +9210,19 @@ dependencies = [ [[package]] name = "secp256k1" version = "0.30.0" -source = "git+https://github.com/sp1-patches/rust-secp256k1?tag=patch-0.30.0-sp1-5.0.0#04d87db04bcc2dc5dd8e1ab3f046cc655440d07a" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" dependencies = [ "bitcoin_hashes", - "cfg-if 1.0.4", - "k256 0.13.4 (git+https://github.com/sp1-patches/elliptic-curves?tag=patch-k256-13.4-sp1-5.0.0)", "rand 0.8.5", "secp256k1-sys", ] [[package]] name = "secp256k1-sys" -version = "0.10.0" -source = "git+https://github.com/sp1-patches/rust-secp256k1?tag=patch-0.30.0-sp1-5.0.0#04d87db04bcc2dc5dd8e1ab3f046cc655440d07a" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" dependencies = [ "cc", ] @@ -9785,7 +9738,7 @@ dependencies = [ "hashbrown 0.14.5", "hex", "itertools 0.13.0", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", + "k256", "num", "num_cpus", "p256", @@ -9853,7 +9806,7 @@ dependencies = [ "elliptic-curve", "generic-array 1.1.0", "itertools 0.13.0", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", + "k256", "num", "p256", "p3-field", @@ -9881,7 +9834,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fce8ad0f153443d09d398eccb650a0b2dcbf829470e394e4bf60ec4379c7af93" dependencies = [ "bincode", - "elliptic-curve", "serde", "sp1-primitives", ] @@ -10106,7 +10058,7 @@ dependencies = [ "hex", "indicatif", "itertools 0.13.0", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", + "k256", "p3-baby-bear", "p3-field", "p3-fri", diff --git a/crates/blockchain/blockchain.rs b/crates/blockchain/blockchain.rs index 614344d5137..e80477685d6 100644 --- a/crates/blockchain/blockchain.rs +++ b/crates/blockchain/blockchain.rs @@ -350,7 +350,7 @@ impl Blockchain { ); if update.removed { // Remove account from trie - state_trie.remove(&hashed_address)?; + state_trie.remove_account(&hashed_address)?; account_states.remove(&hashed_address_h256); continue; } @@ -449,7 +449,7 @@ impl Blockchain { storage_updates_map.extend(storage_updates); account_state.storage_root = storage_hash; } - state_trie.insert(hashed_address, account_state.encode_to_vec())?; + state_trie.insert_account(hashed_address, account_state.encode_to_vec())?; } let (state_trie_hash, state_updates) = state_trie.collect_changes_since_last_hash(); state_updates_map.extend(state_updates); diff --git a/crates/common/trie/trie.rs b/crates/common/trie/trie.rs index 128beccc5bd..d0e20f47217 100644 --- a/crates/common/trie/trie.rs +++ b/crates/common/trie/trie.rs @@ -126,7 +126,7 @@ impl Trie { } /// Insert an RLP-encoded value into the trie. - pub fn insert(&mut self, path: PathRLP, value: ValueRLP) -> Result<(), TrieError> { + pub fn insert_account(&mut self, path: PathRLP, value: ValueRLP) -> Result<(), TrieError> { let path = Nibbles::from_bytes(&path); self.pending_removal.remove(&path); self.dirty.insert(path.clone()); @@ -149,9 +149,32 @@ impl Trie { Ok(()) } + /// Insert an RLP-encoded value into the trie. + pub fn insert(&mut self, path: PathRLP, value: ValueRLP) -> Result<(), TrieError> { + let path = Nibbles::from_bytes(&path); + self.pending_removal.remove(&path); + self.dirty.insert(path.clone()); + + if self.root.is_valid() { + // If the trie is not empty, call the root node's insertion logic. + self.root + .get_node_mut(self.db.as_ref(), Nibbles::default())? + .ok_or_else(|| { + TrieError::InconsistentTree(Box::new(InconsistentTreeError::RootNotFoundNoHash)) + })? + .insert(self.db.as_ref(), path, value)? + } else { + // If the trie is empty, just add a leaf. + self.root = Node::from(LeafNode::new(path, value)).into() + }; + self.root.clear_hash(); + + Ok(()) + } + /// Remove a value from the trie given its RLP-encoded path. /// Returns the value if it was succesfully removed or None if it wasn't part of the trie - pub fn remove(&mut self, path: &PathRLP) -> Result, TrieError> { + pub fn remove_account(&mut self, path: &PathRLP) -> Result, TrieError> { self.dirty.insert(Nibbles::from_bytes(path)); if !self.root.is_valid() { return Ok(None); @@ -177,6 +200,33 @@ impl Trie { Ok(value) } + /// Remove a value from the trie given its RLP-encoded path. + /// Returns the value if it was succesfully removed or None if it wasn't part of the trie + pub fn remove(&mut self, path: &PathRLP) -> Result, TrieError> { + let path = Nibbles::from_bytes(path); + self.dirty.insert(path.clone()); + if !self.root.is_valid() { + return Ok(None); + } + self.pending_removal.insert(path.clone()); + + // If the trie is not empty, call the root node's removal logic. + let (is_trie_empty, value) = self + .root + .get_node_mut(self.db.as_ref(), Nibbles::default())? + .ok_or_else(|| { + TrieError::InconsistentTree(Box::new(InconsistentTreeError::RootNotFoundNoHash)) + })? + .remove(self.db.as_ref(), path)?; + if is_trie_empty { + self.root = NodeRef::default(); + } else { + self.root.clear_hash(); + } + + Ok(value) + } + /// Return the hash of the trie's root node. /// Returns keccak(RLP_NULL) if the trie is empty /// Also commits changes to the DB