Skip to content

Commit 0305ea1

Browse files
committed
refactor: traverse module
1 parent 40849ef commit 0305ea1

File tree

15 files changed

+1470
-1325
lines changed

15 files changed

+1470
-1325
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ trevm = { version = "0.33.0", features = ["full_env_cfg"] }
5252
# alloy
5353
alloy = { version = "1.0.9", default-features = false, features = ["consensus", "rlp"] }
5454

55+
ahash = "0.8"
5556
auto_impl = "1.3.0"
5657
bytes = "1.11.0"
5758
byteorder = "1.5.0"

crates/hot-mdbx/src/tx.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,8 @@ impl<K: TransactionKind> Tx<K> {
4444
key[..to_copy].copy_from_slice(&name.as_bytes()[..to_copy]);
4545

4646
let db = self.inner.open_db(None)?;
47-
// Note: We request Vec<u8> (owned) since we only need it briefly for decoding
48-
// and don't need zero-copy for this small metadata read.
49-
let data: Vec<u8> = self
47+
48+
let data: [u8; 8] = self
5049
.inner
5150
.get(db.dbi(), key.as_slice())
5251
.map_err(MdbxError::from)?

crates/hot/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ trevm.workspace = true
1515

1616
alloy.workspace = true
1717

18+
ahash.workspace = true
1819
auto_impl.workspace = true
1920
bytes.workspace = true
2021
itertools.workspace = true

crates/hot/src/db/consistent.rs

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ use crate::{
22
db::{HistoryError, UnsafeDbWrite, UnsafeHistoryWrite},
33
tables,
44
};
5+
use ahash::AHashSet;
56
use alloy::primitives::{Address, BlockNumber, U256, address};
67
use signet_storage_types::{BlockNumberList, SealedHeader};
7-
use std::collections::HashSet;
88
use trevm::revm::database::BundleState;
99

1010
/// Maximum address value (all bits set to 1).
@@ -24,13 +24,10 @@ pub trait HistoryWrite: UnsafeDbWrite + UnsafeHistoryWrite {
2424
where
2525
I: IntoIterator<Item = &'a SealedHeader>,
2626
{
27-
let headers: Vec<_> = headers.into_iter().collect();
28-
if headers.is_empty() {
29-
return Err(HistoryError::EmptyRange);
30-
}
27+
let mut iter = headers.into_iter();
28+
let first = iter.next().ok_or(HistoryError::EmptyRange)?;
3129

3230
// Validate first header against current DB tip
33-
let first = headers[0];
3431
match self.get_chain_tip().map_err(HistoryError::Db)? {
3532
None => {
3633
// Empty DB - first block is valid as genesis
@@ -52,11 +49,8 @@ pub trait HistoryWrite: UnsafeDbWrite + UnsafeHistoryWrite {
5249
}
5350
}
5451

55-
// Validate each subsequent header extends the previous
56-
for window in headers.windows(2) {
57-
let prev = window[0];
58-
let curr = window[1];
59-
52+
// Validate each subsequent header extends the previous using fold
53+
iter.try_fold(first, |prev, curr| {
6054
let expected_number = prev.number + 1;
6155
if curr.number != expected_number {
6256
return Err(HistoryError::NonContiguousBlock {
@@ -72,7 +66,9 @@ pub trait HistoryWrite: UnsafeDbWrite + UnsafeHistoryWrite {
7266
got: curr.parent_hash,
7367
});
7468
}
75-
}
69+
70+
Ok(curr)
71+
})?;
7672

7773
Ok(())
7874
}
@@ -113,7 +109,8 @@ pub trait HistoryWrite: UnsafeDbWrite + UnsafeHistoryWrite {
113109
// ═══════════════════════════════════════════════════════════════════
114110
// 1. STREAM AccountChangeSets → restore + filter history in one pass
115111
// ═══════════════════════════════════════════════════════════════════
116-
let mut seen_accounts: HashSet<Address> = HashSet::new();
112+
// TODO: estimate capacity from block range size for better allocation
113+
let mut seen_accounts: AHashSet<Address> = AHashSet::new();
117114
let mut account_cursor = self.traverse_dual::<tables::AccountChangeSets>()?;
118115

119116
// Position at first entry
@@ -136,8 +133,8 @@ pub trait HistoryWrite: UnsafeDbWrite + UnsafeHistoryWrite {
136133
// Filter history index
137134
if let Some((shard_key, list)) = self.last_account_history(address)? {
138135
self.queue_delete_dual::<tables::AccountsHistory>(&address, &shard_key)?;
139-
let filtered: Vec<u64> = list.iter().filter(|&bn| bn <= block).collect();
140-
if !filtered.is_empty() {
136+
let mut filtered = list.iter().take_while(|&bn| bn <= block).peekable();
137+
if filtered.peek().is_some() {
141138
self.write_account_history(
142139
&address,
143140
u64::MAX,
@@ -153,7 +150,8 @@ pub trait HistoryWrite: UnsafeDbWrite + UnsafeHistoryWrite {
153150
// ═══════════════════════════════════════════════════════════════════
154151
// 2. STREAM StorageChangeSets → restore + filter history in one pass
155152
// ═══════════════════════════════════════════════════════════════════
156-
let mut seen_storage: HashSet<(Address, U256)> = HashSet::new();
153+
// TODO: estimate capacity from block range size for better allocation
154+
let mut seen_storage: AHashSet<(Address, U256)> = AHashSet::new();
157155
let mut storage_cursor = self.traverse_dual::<tables::StorageChangeSets>()?;
158156

159157
// Position at first entry
@@ -176,8 +174,8 @@ pub trait HistoryWrite: UnsafeDbWrite + UnsafeHistoryWrite {
176174
// Filter history index
177175
if let Some((shard_key, list)) = self.last_storage_history(&address, &slot)? {
178176
self.queue_delete_dual::<tables::StorageHistory>(&address, &shard_key)?;
179-
let filtered: Vec<u64> = list.iter().filter(|&bn| bn <= block).collect();
180-
if !filtered.is_empty() {
177+
let mut filtered = list.iter().take_while(|&bn| bn <= block).peekable();
178+
if filtered.peek().is_some() {
181179
self.write_storage_history(
182180
&address,
183181
slot,

crates/hot/src/db/inconsistent.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ use crate::{
33
model::HotKvWrite,
44
tables,
55
};
6+
use ahash::AHashMap;
67
use alloy::{
78
consensus::Header,
89
primitives::{Address, B256, BlockNumber, U256},
910
};
1011
use itertools::Itertools;
1112
use signet_storage_types::{Account, BlockNumberList, SealedHeader, ShardedKey};
12-
use std::{collections::HashMap, ops::RangeInclusive};
13+
use std::ops::RangeInclusive;
1314
use trevm::revm::{
1415
bytecode::Bytecode,
1516
database::{
@@ -23,7 +24,7 @@ use trevm::revm::{
2324
/// Maps address -> (old_account, new_account, storage_changes)
2425
/// where storage_changes maps slot (B256) -> (old_value, new_value)
2526
pub type BundleInit =
26-
HashMap<Address, (Option<Account>, Option<Account>, HashMap<B256, (U256, U256)>)>;
27+
AHashMap<Address, (Option<Account>, Option<Account>, AHashMap<B256, (U256, U256)>)>;
2728

2829
/// Trait for database write operations on standard hot tables.
2930
///
@@ -264,16 +265,20 @@ pub trait UnsafeHistoryWrite: UnsafeDbWrite + HistoryRead {
264265
///
265266
/// Iterates over entries starting from the first block in the range,
266267
/// collecting changes while the block number remains in range.
268+
// TODO: estimate capacity from block range size for better allocation
267269
fn changed_accounts_with_range(
268270
&self,
269271
range: RangeInclusive<BlockNumber>,
270-
) -> Result<HashMap<Address, Vec<u64>>, Self::Error> {
272+
) -> Result<AHashMap<Address, Vec<u64>>, Self::Error> {
271273
self.traverse_dual::<tables::AccountChangeSets>()?
272274
.iter_from(range.start(), &Address::ZERO)?
273275
.process_results(|iter| {
274276
iter.take_while(|(num, _, _)| range.contains(num))
275277
.map(|(num, addr, _)| (addr, num))
276-
.into_group_map()
278+
.into_group_map_by(|(addr, _)| *addr)
279+
.into_iter()
280+
.map(|(addr, pairs)| (addr, pairs.into_iter().map(|(_, num)| num).collect()))
281+
.collect()
277282
})
278283
}
279284

@@ -299,17 +304,21 @@ pub trait UnsafeHistoryWrite: UnsafeDbWrite + HistoryRead {
299304
///
300305
/// Iterates over entries starting from the first block in the range,
301306
/// collecting changes while the block number remains in range.
307+
// TODO: estimate capacity from block range size for better allocation
302308
#[allow(clippy::type_complexity)]
303309
fn changed_storages_with_range(
304310
&self,
305311
range: RangeInclusive<BlockNumber>,
306-
) -> Result<HashMap<(Address, U256), Vec<u64>>, Self::Error> {
312+
) -> Result<AHashMap<(Address, U256), Vec<u64>>, Self::Error> {
307313
self.traverse_dual::<tables::StorageChangeSets>()?
308314
.iter_from(&(*range.start(), Address::ZERO), &U256::ZERO)?
309315
.process_results(|iter| {
310316
iter.take_while(|(num_addr, _, _)| range.contains(&num_addr.0))
311317
.map(|(num_addr, slot, _)| ((num_addr.1, slot), num_addr.0))
312-
.into_group_map()
318+
.into_group_map_by(|(key, _)| *key)
319+
.into_iter()
320+
.map(|(key, pairs)| (key, pairs.into_iter().map(|(_, num)| num).collect()))
321+
.collect()
313322
})
314323
}
315324

crates/hot/src/db/read.rs

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -220,23 +220,11 @@ pub trait HistoryRead: HotDbRead {
220220

221221
/// Get headers in a range (inclusive).
222222
fn get_headers_range(&self, start: u64, end: u64) -> Result<Vec<Header>, Self::Error> {
223-
let mut cursor = self.traverse::<tables::Headers>()?;
224-
let mut headers = Vec::new();
225-
226-
if cursor.lower_bound(&start)?.is_none() {
227-
return Ok(headers);
228-
}
229-
230-
loop {
231-
match cursor.read_next()? {
232-
Some((num, header)) if num <= end => {
233-
headers.push(header);
234-
}
235-
_ => break,
236-
}
237-
}
238-
239-
Ok(headers)
223+
self.traverse::<tables::Headers>()?
224+
.iter_from(&start)?
225+
.take_while(|r| r.as_ref().is_ok_and(|(num, _)| *num <= end))
226+
.map(|r| r.map(|(_, header)| header))
227+
.collect()
240228
}
241229
}
242230

0 commit comments

Comments
 (0)