Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
2c5797e
refactor(l1): unify snapsync healing modules into sync/healing/ direc…
pablodeymo Jan 21, 2026
713ea92
refactor(l1): modularize snap protocol code into dedicated directories
pablodeymo Jan 21, 2026
9f9214f
docs: add snap sync refactoring plan
pablodeymo Jan 21, 2026
2013570
refactor(l1): split sync.rs into full.rs and snap_sync.rs modules
pablodeymo Jan 21, 2026
68867ae
refactor(l1): extract snap client methods from peer_handler.rs to sna…
pablodeymo Jan 21, 2026
d91bc8b
refactor(l1): consolidate snap protocol error handling into unified S…
pablodeymo Jan 22, 2026
57ebf37
Merge branch 'main' into refactor/snapsync-healing-unification
pablodeymo Jan 22, 2026
f680777
fix(l1): use consistent usize type for missing_children_count
pablodeymo Jan 22, 2026
17744dc
fix(l1): fix typos in healing module comments
pablodeymo Jan 22, 2026
2836e3f
Merge main into refactor/snapsync-healing-unification
pablodeymo Jan 27, 2026
c40de29
fix(l1): fix typo in snap client error message
pablodeymo Jan 27, 2026
ffba3fe
fix(l1): prevent panic on empty accounts vector in snap client
pablodeymo Jan 27, 2026
a682d76
fix(l1): handle empty bytecode hashes in request_bytecodes
pablodeymo Jan 27, 2026
6c99453
fix(l1): prevent panics from empty vector indexing in snap client
pablodeymo Jan 27, 2026
91b926e
fix(l1): prevent zero chunk_size in request_account_range
pablodeymo Jan 27, 2026
f3983c3
Merge branch 'main' into refactor/snapsync-healing-unification
pablodeymo Jan 28, 2026
3c962dd
Merge branch 'main' into refactor/snapsync-healing-unification
pablodeymo Jan 29, 2026
1e87e25
Merge branch 'main' into refactor/snapsync-healing-unification
pablodeymo Jan 30, 2026
a36ce69
Use Bytes for trie values to enable O(1) clones in cache lookups
pablodeymo Jan 28, 2026
4c1ca45
Use ValueRLP::from() instead of Into::<ValueRLP>::into() for cleaner …
pablodeymo Jan 30, 2026
3fc5a75
Add CHANGELOG entry for Bytes trie values optimization
pablodeymo Jan 30, 2026
bb614af
Fix Bytes to [u8; 8] conversion by using as_ref()
pablodeymo Jan 30, 2026
afdfe91
Remove needless reference in slice comparison
pablodeymo Jan 30, 2026
9a98f65
Merge main into refactor/snapsync-healing-unification
pablodeymo Feb 2, 2026
e424237
Replace .get(0) with .first() in snap client to fix clippy lint errors
pablodeymo Feb 2, 2026
02b1dc2
fmt
pablodeymo Feb 2, 2026
0656d2e
Remove plan_snap_sync.md (content moved to PR description)
pablodeymo Feb 2, 2026
94a35ff
Derive thiserror::Error for DumpError
pablodeymo Feb 2, 2026
8eabda7
Merge branch 'main' into refactor/snapsync-healing-unification
pablodeymo Feb 3, 2026
161cf78
Merge branch 'main' into refactor/snapsync-healing-unification
pablodeymo Feb 4, 2026
270443c
Merge branch 'refactor/snapsync-healing-unification' into perf/avoid-…
pablodeymo Feb 4, 2026
a242f55
Add per-phase timing breakdown to Slack notifications and run logs
pablodeymo Feb 5, 2026
7042b83
Update tooling/sync/docker_monitor.py
pablodeymo Feb 5, 2026
6f12101
Merge branch 'main' into feature/slack-phase-breakdown
pablodeymo Feb 6, 2026
216ee7f
Merge branch 'main' into feature/slack-phase-breakdown
pablodeymo Feb 6, 2026
b44c4ea
Merge branch 'main' into feature/slack-phase-breakdown
pablodeymo Feb 6, 2026
d7f6f26
Merge branch 'main' into perf/avoid-copying-trie-leaves
pablodeymo Feb 6, 2026
44761da
Merge branch 'feature/slack-phase-breakdown' into perf/avoid-copying-…
pablodeymo Feb 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Perf

### 2026-01-30

- Use `Bytes` for trie values to enable O(1) clones [#6057](https://github.com/lambdaclass/ethrex/pull/6057)

### 2026-01-27

- Optimize prewarmer by grouping transactions by sender [#6047](https://github.com/lambdaclass/ethrex/pull/6047)
Expand Down
11 changes: 6 additions & 5 deletions crates/common/trie/db.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use bytes::Bytes;
use ethereum_types::H256;
use ethrex_rlp::encode::RLPEncode;

Expand All @@ -12,17 +13,17 @@ pub type NodeMap = Arc<Mutex<BTreeMap<Vec<u8>, Vec<u8>>>>;

pub trait TrieDB: Send + Sync {
fn get(&self, key: Nibbles) -> Result<Option<Vec<u8>>, TrieError>;
fn put_batch(&self, key_values: Vec<(Nibbles, Vec<u8>)>) -> Result<(), TrieError>;
fn put_batch(&self, key_values: Vec<(Nibbles, Bytes)>) -> Result<(), TrieError>;
// TODO: replace putbatch with this function.
fn put_batch_no_alloc(&self, key_values: &[(Nibbles, Node)]) -> Result<(), TrieError> {
self.put_batch(
key_values
.iter()
.map(|node| (node.0.clone(), node.1.encode_to_vec()))
.map(|node| (node.0.clone(), node.1.encode_to_vec().into()))
.collect(),
)
}
fn put(&self, key: Nibbles, value: Vec<u8>) -> Result<(), TrieError> {
fn put(&self, key: Nibbles, value: Bytes) -> Result<(), TrieError> {
self.put_batch(vec![(key, value)])
}
/// Commits any pending changes to the underlying storage
Expand Down Expand Up @@ -107,12 +108,12 @@ impl TrieDB for InMemoryTrieDB {
.cloned())
}

fn put_batch(&self, key_values: Vec<(Nibbles, Vec<u8>)>) -> Result<(), TrieError> {
fn put_batch(&self, key_values: Vec<(Nibbles, Bytes)>) -> Result<(), TrieError> {
let mut db = self.inner.lock().map_err(|_| TrieError::LockError)?;

for (key, value) in key_values {
let prefixed_key = self.apply_prefix(key);
db.insert(prefixed_key.into_vec(), value);
db.insert(prefixed_key.into_vec(), value.to_vec());
}

Ok(())
Expand Down
5 changes: 3 additions & 2 deletions crates/common/trie/logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::{

use ethrex_rlp::decode::RLPDecode;

use bytes::Bytes;
use crate::{Nibbles, Node, NodeHash, Trie, TrieDB, TrieError};

pub type TrieWitness = Arc<Mutex<HashMap<NodeHash, Node>>>;
Expand Down Expand Up @@ -44,11 +45,11 @@ impl TrieDB for TrieLogger {
Ok(result)
}

fn put(&self, key: Nibbles, value: Vec<u8>) -> Result<(), TrieError> {
fn put(&self, key: Nibbles, value: Bytes) -> Result<(), TrieError> {
self.inner_db.put(key, value)
}

fn put_batch(&self, key_values: Vec<(Nibbles, Vec<u8>)>) -> Result<(), TrieError> {
fn put_batch(&self, key_values: Vec<(Nibbles, Bytes)>) -> Result<(), TrieError> {
self.inner_db.put_batch(key_values)
}
}
10 changes: 8 additions & 2 deletions crates/common/trie/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ impl NodeRef {
node.encode(&mut buf);
let hash = *hash.get_or_init(|| NodeHash::from_encoded(&buf));
if let Node::Leaf(leaf) = node.as_ref() {
acc.push((path.concat(&leaf.partial), leaf.value.clone()));
acc.push((path.concat(&leaf.partial), leaf.value.to_vec()));
}
acc.push((path, buf));
Comment on lines 150 to 155
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ValueRLP is now Bytes (cheap to clone), but NodeRef::commit converts leaf values with leaf.value.to_vec(), reintroducing an O(n) copy per leaf. If these leaf values are intended to be written as flat-key-value entries, consider changing the accumulator to store Bytes (or otherwise avoid copying) so commits preserve the intended O(1) clone behavior.

Copilot uses AI. Check for mistakes.

Expand Down Expand Up @@ -240,6 +240,12 @@ impl From<ValueRLP> for ValueOrHash {
}
}

impl From<Vec<u8>> for ValueOrHash {
fn from(value: Vec<u8>) -> Self {
Self::Value(value.into())
}
}

impl From<NodeHash> for ValueOrHash {
fn from(value: NodeHash) -> Self {
Self::Hash(value)
Expand Down Expand Up @@ -268,7 +274,7 @@ impl Default for Node {
// empty leaf node as a placeholder
Self::Leaf(LeafNode {
partial: Nibbles::from_bytes(&[]),
value: Vec::new(),
value: Default::default(),
})
}
}
Expand Down
32 changes: 18 additions & 14 deletions crates/common/trie/node/branch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use super::{ExtensionNode, LeafNode, Node, NodeRef, ValueOrHash};
)]
pub struct BranchNode {
pub choices: [NodeRef; 16],
#[rkyv(with = crate::rkyv_utils::BytesWrapper)]
pub value: ValueRLP,
}

Expand All @@ -42,13 +43,16 @@ impl BranchNode {
}

/// Creates a new branch node given its children and value
pub const fn new_with_value(choices: [NodeRef; 16], value: ValueRLP) -> Self {
Self { choices, value }
pub fn new_with_value(choices: [NodeRef; 16], value: impl Into<ValueRLP>) -> Self {
Self {
choices,
value: value.into(),
}
}

/// Updates the node's path and value
pub fn update(&mut self, new_value: ValueRLP) {
self.value = new_value;
pub fn update(&mut self, new_value: impl Into<ValueRLP>) {
self.value = new_value.into();
}

/// Retrieves a value from the subtrie originating from this node given its path
Expand Down Expand Up @@ -365,12 +369,12 @@ mod test {
assert_eq!(
node.get(trie.db.as_ref(), Nibbles::from_bytes(&[0x00]))
.unwrap(),
Some(vec![0x12, 0x34, 0x56, 0x78]),
Some(vec![0x12, 0x34, 0x56, 0x78].into()),
);
assert_eq!(
node.get(trie.db.as_ref(), Nibbles::from_bytes(&[0x10]))
.unwrap(),
Some(vec![0x34, 0x56, 0x78, 0x9A]),
Some(vec![0x34, 0x56, 0x78, 0x9A].into()),
);
}

Expand Down Expand Up @@ -406,7 +410,7 @@ mod test {
node.insert(trie.db.as_ref(), path.clone(), value.clone().into())
.unwrap();

assert_eq!(node.get(trie.db.as_ref(), path).unwrap(), Some(value));
assert_eq!(node.get(trie.db.as_ref(), path).unwrap(), Some(value.into()));
}

#[test]
Expand All @@ -425,7 +429,7 @@ mod test {
node.insert(trie.db.as_ref(), path.clone(), value.clone().into())
.unwrap();

assert_eq!(node.get(trie.db.as_ref(), path).unwrap(), Some(value));
assert_eq!(node.get(trie.db.as_ref(), path).unwrap(), Some(value.into()));
}

#[test]
Expand All @@ -448,7 +452,7 @@ mod test {
.unwrap();

assert_eq!(new_node.choices, node.choices);
assert_eq!(new_node.value, value);
assert_eq!(new_node.value, ValueRLP::from(value));
}

#[test]
Expand All @@ -466,7 +470,7 @@ mod test {
.unwrap();

assert!(matches!(node, Some(NodeRemoveResult::New(Node::Leaf(_)))));
assert_eq!(value, Some(vec![0x00]));
assert_eq!(value, Some(vec![0x00].into()));
}

#[test]
Expand All @@ -485,7 +489,7 @@ mod test {
.unwrap();

assert!(matches!(node, Some(NodeRemoveResult::Mutated)));
assert_eq!(value, Some(vec![0x00]));
assert_eq!(value, Some(vec![0x00].into()));
}

#[test]
Expand All @@ -502,7 +506,7 @@ mod test {
.unwrap();

assert!(matches!(node, Some(NodeRemoveResult::New(Node::Leaf(_)))));
assert_eq!(value, Some(vec![0x00]));
assert_eq!(value, Some(vec![0x00].into()));
}

#[test]
Expand All @@ -519,7 +523,7 @@ mod test {
.unwrap();

assert!(matches!(node, Some(NodeRemoveResult::New(Node::Leaf(_)))));
assert_eq!(value, Some(vec![0xFF]));
assert_eq!(value, Some(vec![0xFF].into()));
}

#[test]
Expand All @@ -537,7 +541,7 @@ mod test {
.unwrap();

assert!(matches!(node, Some(NodeRemoveResult::Mutated)));
assert_eq!(value, Some(vec![0xFF]));
assert_eq!(value, Some(vec![0xFF].into()));
}

#[test]
Expand Down
16 changes: 8 additions & 8 deletions crates/common/trie/node/extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,12 +297,12 @@ mod test {
assert_eq!(
node.get(trie.db.as_ref(), Nibbles::from_bytes(&[0x00]))
.unwrap(),
Some(vec![0x12, 0x34, 0x56, 0x78]),
Some(vec![0x12, 0x34, 0x56, 0x78].into()),
);
assert_eq!(
node.get(trie.db.as_ref(), Nibbles::from_bytes(&[0x01]))
.unwrap(),
Some(vec![0x34, 0x56, 0x78, 0x9A]),
Some(vec![0x34, 0x56, 0x78, 0x9A].into()),
);
}

Expand Down Expand Up @@ -369,7 +369,7 @@ mod test {
assert_eq!(
node.get(trie.db.as_ref(), Nibbles::from_bytes(&[0x10]))
.unwrap(),
Some(vec![0x20])
Some(vec![0x20].into())
);
}

Expand Down Expand Up @@ -397,7 +397,7 @@ mod test {
assert_eq!(
node.get(trie.db.as_ref(), Nibbles::from_bytes(&[0x10]))
.unwrap(),
Some(vec![0x20])
Some(vec![0x20].into())
);
}

Expand All @@ -419,7 +419,7 @@ mod test {
.unwrap();

assert!(none.is_none());
assert_eq!(node.get(trie.db.as_ref(), path).unwrap(), Some(value));
assert_eq!(node.get(trie.db.as_ref(), path).unwrap(), Some(value.into()));
}

#[test]
Expand All @@ -440,7 +440,7 @@ mod test {
.unwrap();

assert!(none.is_none());
assert_eq!(node.get(trie.db.as_ref(), path).unwrap(), Some(value));
assert_eq!(node.get(trie.db.as_ref(), path).unwrap(), Some(value.into()));
}

#[test]
Expand Down Expand Up @@ -476,7 +476,7 @@ mod test {
.unwrap();

assert!(matches!(node, Some(NodeRemoveResult::New(Node::Leaf(_)))));
assert_eq!(value, Some(vec![0x01]));
assert_eq!(value, Some(vec![0x01].into()));
}

#[test]
Expand All @@ -500,7 +500,7 @@ mod test {
node,
Some(NodeRemoveResult::New(Node::Extension(_)))
));
assert_eq!(value, Some(vec![0x00]));
assert_eq!(value, Some(vec![0x00].into()));
}

#[test]
Expand Down
22 changes: 13 additions & 9 deletions crates/common/trie/node/leaf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,17 @@ use super::{ExtensionNode, Node, ValueOrHash};
)]
pub struct LeafNode {
pub partial: Nibbles,
#[rkyv(with = crate::rkyv_utils::BytesWrapper)]
pub value: ValueRLP,
}

impl LeafNode {
/// Creates a new leaf node and stores the given (path, value) pair
pub const fn new(partial: Nibbles, value: ValueRLP) -> Self {
Self { partial, value }
pub fn new(partial: Nibbles, value: impl Into<ValueRLP>) -> Self {
Self {
partial,
value: value.into(),
}
}

/// Returns the stored value if the given path matches the stored path
Expand Down Expand Up @@ -180,7 +184,7 @@ mod test {

#[test]
fn new() {
let node = LeafNode::new(Default::default(), Default::default());
let node = LeafNode::new(Default::default(), ValueRLP::default());
assert_eq!(node.value, ValueRLP::default());
}

Expand All @@ -192,7 +196,7 @@ mod test {

assert_eq!(
node.get(Nibbles::from_bytes(&[0x12])).unwrap(),
Some(vec![0x12, 0x34, 0x56, 0x78]),
Some(vec![0x12, 0x34, 0x56, 0x78].into()),
);
}

Expand Down Expand Up @@ -233,7 +237,7 @@ mod test {
Some(Node::Branch(x)) => x,
_ => panic!("expected a branch node"),
};
assert_eq!(node.get(trie.db.as_ref(), path).unwrap(), Some(value));
assert_eq!(node.get(trie.db.as_ref(), path).unwrap(), Some(value.into()));
}

#[test]
Expand All @@ -251,7 +255,7 @@ mod test {
assert!(matches!(node, Some(Node::Extension(_))));
assert_eq!(
node.unwrap().get(trie.db.as_ref(), path).unwrap(),
Some(value)
Some(value.into())
);
}

Expand All @@ -270,7 +274,7 @@ mod test {
assert!(matches!(node, Some(Node::Extension(_))));
assert_eq!(
node.unwrap().get(trie.db.as_ref(), path).unwrap(),
Some(value)
Some(value.into())
);
}

Expand All @@ -289,7 +293,7 @@ mod test {
assert!(matches!(node, Some(Node::Extension(_))));
assert_eq!(
node.unwrap().get(trie.db.as_ref(), path).unwrap(),
Some(value)
Some(value.into())
);
}

Expand All @@ -310,7 +314,7 @@ mod test {
let (node, value) = node.remove(Nibbles::from_bytes(&[0x12, 0x34])).unwrap();

assert!(node.is_none());
assert_eq!(value, Some(vec![0x12, 0x34, 0x56, 0x78]));
assert_eq!(value, Some(vec![0x12, 0x34, 0x56, 0x78].into()));
}

#[test]
Expand Down
18 changes: 18 additions & 0 deletions crates/common/trie/rkyv_utils.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use bytes::Bytes;
use ethereum_types::H256;
use rkyv::{Archive, Deserialize, Serialize};
use std::hash::{Hash, Hasher};
Expand Down Expand Up @@ -39,3 +40,20 @@ impl Hash for ArchivedH256Wrapper {
self.0.hash(state);
}
}

#[derive(Archive, Serialize, Deserialize)]
#[rkyv(remote = Bytes)]
pub struct BytesWrapper {
#[rkyv(getter = bytes_to_vec)]
bytes: Vec<u8>,
}

fn bytes_to_vec(bytes: &Bytes) -> Vec<u8> {
bytes.to_vec()
}

impl From<BytesWrapper> for Bytes {
fn from(value: BytesWrapper) -> Self {
Self::from(value.bytes)
}
}
Loading
Loading