Skip to content

Commit f11065e

Browse files
authored
feat: use r0 and reth-stateless (#141)
1 parent 09e90fa commit f11065e

File tree

23 files changed

+715
-668
lines changed

23 files changed

+715
-668
lines changed

Cargo.lock

Lines changed: 165 additions & 60 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ members = [
33
"crates/bin",
44
"crates/core",
55
"crates/helpers",
6-
"crates/kv",
76
"crates/primitives",
87
"crates/sbv",
98
"crates/trie",
@@ -22,24 +21,24 @@ repository = "https://github.com/scroll-tech/stateless-block-verifier"
2221

2322
[workspace.dependencies]
2423
# https://github.com/alloy-rs/alloy
25-
alloy = { version = "1.0.30", default-features = false }
26-
alloy-consensus = { version = "1.0.30", default-features = false }
27-
alloy-eips = { version = "1.0.30", default-features = false }
28-
alloy-network = { version = "1.0.30", default-features = false }
29-
alloy-provider = { version = "1.0.30", default-features = false }
30-
alloy-rpc-client = { version = "1.0.30", default-features = false }
31-
alloy-rpc-types-eth = { version = "1.0.30", default-features = false }
32-
alloy-rpc-types-debug = { version = "1.0.30", default-features = false }
33-
alloy-serde = { version = "1.0.30", default-features = false }
34-
alloy-transport = { version = "1.0.30", default-features = false }
24+
alloy = { version = "1.0.37", default-features = false }
25+
alloy-consensus = { version = "1.0.37", default-features = false }
26+
alloy-eips = { version = "1.0.37", default-features = false }
27+
alloy-network = { version = "1.0.37", default-features = false }
28+
alloy-provider = { version = "1.0.37", default-features = false }
29+
alloy-rpc-client = { version = "1.0.37", default-features = false }
30+
alloy-rpc-types-eth = { version = "1.0.37", default-features = false }
31+
alloy-rpc-types-debug = { version = "1.0.37", default-features = false }
32+
alloy-serde = { version = "1.0.37", default-features = false }
33+
alloy-transport = { version = "1.0.37", default-features = false }
3534
# https://github.com/alloy-rs/rlp
3635
alloy-rlp = { version = "0.3.10", default-features = false }
3736
# https://github.com/alloy-rs/trie
3837
alloy-trie = { version = "0.9.1", default-features = false }
3938
# https://github.com/alloy-rs/core
4039
alloy-primitives = { version = "1.3.1", default-features = false, features = ["map-hashbrown", "map-fxhash"] }
4140
# https://github.com/alloy-rs/evm
42-
alloy-evm = { version = "0.20.1", default-features = false }
41+
alloy-evm = { version = "0.21.2", default-features = false }
4342

4443
revm-scroll = { git = "https://github.com/scroll-tech/scroll-revm", default-features = false }
4544

@@ -50,6 +49,7 @@ reth-ethereum-forks = { git = "https://github.com/scroll-tech/reth", branch = "s
5049
reth-execution-types = { git = "https://github.com/scroll-tech/reth", branch = "scroll", default-features = false }
5150
reth-primitives = { git = "https://github.com/scroll-tech/reth", branch = "scroll", default-features = false }
5251
reth-primitives-traits = { git = "https://github.com/scroll-tech/reth", branch = "scroll", default-features = false }
52+
reth-stateless = { git = "https://github.com/scroll-tech/reth", branch = "scroll", default-features = false }
5353
reth-trie = { git = "https://github.com/scroll-tech/reth", branch = "scroll", default-features = false }
5454

5555
reth-scroll-chainspec = { git = "https://github.com/scroll-tech/reth", branch = "scroll", default-features = false }
@@ -62,6 +62,8 @@ scroll-alloy-consensus = { git = "https://github.com/scroll-tech/reth", branch =
6262
scroll-alloy-rpc-types = { git = "https://github.com/scroll-tech/reth", branch = "scroll", default-features = false }
6363
scroll-alloy-network = { git = "https://github.com/scroll-tech/reth", branch = "scroll", default-features = false }
6464

65+
risc0-ethereum-trie = { git = "https://github.com/risc0/risc0-ethereum" }
66+
6567
#reth-chainspec = { path = "../reth/crates/chainspec", default-features = false }
6668
#reth-evm = { path = "../reth/crates/evm/evm", default-features = false }
6769
#reth-evm-ethereum = { path = "../reth/crates/ethereum/evm", default-features = false }
@@ -88,7 +90,6 @@ futures = "0.3"
8890
indicatif = "0.18"
8991
itertools = "0.14"
9092
rkyv = "0.8"
91-
rustc-hash = "2.1"
9293
thiserror = "2.0"
9394
url = ">=2.5.3"
9495
rstest = "0.26"
@@ -108,7 +109,6 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
108109
# workspace
109110
sbv = { path = "crates/sbv" }
110111
sbv-core = { path = "crates/core" }
111-
sbv-kv = { path = "crates/kv" }
112112
sbv-primitives = { path = "crates/primitives" }
113113
sbv-trie = { path = "crates/trie" }
114114
sbv-helpers = { path = "crates/helpers" }

crates/bin/src/helpers/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use sbv::primitives::types::Network;
88
use std::future::Future;
99
use url::Url;
1010

11-
mod dump;
1211
pub mod verifier;
1312

1413
#[derive(Debug, Args)]

crates/bin/src/helpers/verifier.rs

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
1-
use crate::helpers::dump::dump_bundle_state;
21
use eyre::eyre;
32
use sbv::{
43
core::{
5-
VerificationError,
64
verifier::{self, VerifyResult},
75
witness::BlockWitness,
86
},
97
primitives::chainspec::ChainSpec,
108
};
119
use std::{
12-
env,
1310
panic::{AssertUnwindSafe, catch_unwind},
1411
sync::Arc,
1512
};
@@ -18,28 +15,8 @@ pub fn verify_catch_panics(
1815
witness: BlockWitness,
1916
chain_spec: Arc<ChainSpec>,
2017
) -> eyre::Result<VerifyResult> {
21-
let chain_id = witness.chain_id;
22-
let block_number = witness.header.number;
23-
2418
catch_unwind(AssertUnwindSafe(|| {
25-
verifier::run_host(&[witness], chain_spec).inspect_err(|e| {
26-
if let VerificationError::RootMismatch { bundle_state, .. } = e {
27-
let dump_dir = env::temp_dir()
28-
.join("dumps")
29-
.join(format!("{chain_id}-{block_number}"));
30-
dump_bundle_state(bundle_state, &dump_dir)
31-
.inspect(|_| {
32-
dev_info!("Dumped bundle state to: {}", dump_dir.display());
33-
})
34-
.inspect_err(|_e| {
35-
dev_error!(
36-
"Failed to dump bundle state to {}: {_e}",
37-
dump_dir.display(),
38-
);
39-
})
40-
.ok();
41-
}
42-
})
19+
verifier::run_host(&[witness], chain_spec)
4320
}))
4421
.map_err(|e| {
4522
e.downcast_ref::<&str>()

crates/core/Cargo.toml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,13 @@ workspace = true
1414

1515
[dependencies]
1616
auto_impl.workspace = true
17-
thiserror.workspace = true
1817
tracing = { workspace = true, optional = true }
1918
serde.workspace = true
2019
rkyv = { workspace = true, optional = true }
21-
cfg-if.workspace = true
2220
itertools.workspace = true
2321
serde_with.workspace = true
2422
reth-primitives-traits = { workspace = true, features = ["serde", "serde-bincode-compat"] }
23+
reth-stateless.workspace = true
2524

2625
sbv-primitives = { workspace = true, features = [
2726
"chainspec",
@@ -30,7 +29,6 @@ sbv-primitives = { workspace = true, features = [
3029
"reth-execution-types",
3130
] }
3231
sbv-helpers.workspace = true
33-
sbv-kv.workspace = true
3432
sbv-trie.workspace = true
3533

3634
[dev-dependencies]

crates/core/src/database.rs

Lines changed: 81 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,147 +1,102 @@
1-
use sbv_kv::{HashMap, KeyValueStoreGet};
2-
pub use sbv_primitives::types::revm::database::DatabaseRef;
1+
//! Most copied from <https://github.com/paradigmxyz/reth/blob/5c18df9889941837e61929be4b51abb75f07f152/crates/stateless/src/witness_db.rs>
2+
//! Under MIT license
3+
4+
use reth_stateless::StatelessTrie;
5+
pub use sbv_primitives::types::revm::database::Database;
36
use sbv_primitives::{
4-
Address, B256, Bytes, U256,
5-
types::revm::{
6-
AccountInfo, Bytecode,
7-
database::{BundleAccount, DBErrorMarker},
7+
Address, B256, U256,
8+
alloy_primitives::map::B256Map,
9+
types::{
10+
reth::evm::execute::ProviderError,
11+
revm::{AccountInfo, Bytecode},
812
},
913
};
10-
use sbv_trie::PartialStateTrie;
11-
use std::{cell::RefCell, collections::BTreeMap, fmt};
14+
use sbv_trie::r0::SparseState;
15+
use std::collections::BTreeMap;
1216

1317
/// A database that consists of account and storage information.
14-
pub struct EvmDatabase<CodeDb, BlockHashProvider> {
15-
/// Map of code hash to bytecode.
16-
pub(crate) code_db: CodeDb,
17-
/// Cache of analyzed code
18-
analyzed_code_cache: RefCell<HashMap<B256, Option<Bytecode>>>,
19-
/// partial merkle patricia trie
20-
pub(crate) state: PartialStateTrie,
21-
/// Provider of block hashes
22-
block_hashes: BlockHashProvider,
23-
}
24-
25-
/// Database error.
26-
#[derive(Debug, thiserror::Error)]
27-
pub enum DatabaseError {
28-
/// Missing L2 message queue witness
29-
#[cfg(feature = "scroll")]
30-
#[error("missing L2 message queue witness")]
31-
MissingL2MessageQueueWitness,
32-
/// Partial state trie error
33-
#[error(transparent)]
34-
PartialStateTrie(#[from] sbv_trie::PartialStateTrieError),
35-
/// Requested code not loaded
36-
#[error("requested code({0}) not loaded")]
37-
CodeNotLoaded(B256),
38-
}
39-
40-
type Result<T, E = DatabaseError> = std::result::Result<T, E>;
41-
42-
impl<CodeDb, BlockHashProvider> fmt::Debug for EvmDatabase<CodeDb, BlockHashProvider> {
43-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
44-
f.debug_struct("EvmDatabase").finish()
45-
}
18+
#[derive(Debug)]
19+
pub struct WitnessDatabase<'a> {
20+
/// Map of block numbers to block hashes.
21+
/// This is used to service the `BLOCKHASH` opcode.
22+
block_hashes_by_block_number: &'a BTreeMap<u64, B256>,
23+
/// Map of code hashes to bytecode.
24+
/// Used to fetch contract code needed during execution.
25+
bytecode: &'a B256Map<Bytecode>,
26+
/// The sparse Merkle Patricia Trie containing account and storage state.
27+
/// This is used to provide account/storage values during EVM execution.
28+
pub(crate) trie: &'a SparseState,
4629
}
4730

48-
impl<CodeDb: KeyValueStoreGet<B256, Bytes>, BlockHashProvider: KeyValueStoreGet<u64, B256>>
49-
EvmDatabase<CodeDb, BlockHashProvider>
50-
{
51-
/// Initialize an EVM database from a zkTrie root.
52-
pub fn new(code_db: CodeDb, state: PartialStateTrie, block_hashes: BlockHashProvider) -> Self {
53-
EvmDatabase {
54-
code_db,
55-
analyzed_code_cache: Default::default(),
56-
block_hashes,
57-
state,
58-
}
59-
}
60-
61-
/// Update changes to the database.
62-
pub fn commit(&mut self, post_state: BTreeMap<Address, BundleAccount>) -> Result<B256> {
63-
Ok(self.state.update(post_state)?)
64-
}
65-
66-
/// Get the withdrawal trie root of scroll.
31+
impl<'a> WitnessDatabase<'a> {
32+
/// Creates a new [`WitnessDatabase`] instance.
6733
///
68-
/// Note: this should not be confused with the withdrawal of the beacon chain.
69-
#[cfg(feature = "scroll")]
70-
pub fn withdraw_root(&self) -> Result<B256, DatabaseError> {
71-
/// L2MessageQueue pre-deployed address
72-
pub const ADDRESS: Address =
73-
sbv_primitives::address!("5300000000000000000000000000000000000000");
74-
/// the slot of withdraw root in L2MessageQueue
75-
pub const WITHDRAW_TRIE_ROOT_SLOT: U256 = U256::ZERO;
76-
77-
self.basic_ref(ADDRESS)?
78-
.ok_or(DatabaseError::MissingL2MessageQueueWitness)?;
79-
let withdraw_root = self.storage_ref(ADDRESS, WITHDRAW_TRIE_ROOT_SLOT)?;
80-
Ok(withdraw_root.into())
81-
}
82-
83-
fn load_code(&self, hash: B256) -> Option<Bytecode> {
84-
let mut code_cache = self.analyzed_code_cache.borrow_mut();
85-
if let Some(code) = code_cache.get(&hash) {
86-
code.clone()
87-
} else {
88-
let code = self.code_db.get(&hash).cloned().map(Bytecode::new_raw);
89-
code_cache.insert(hash, code.clone());
90-
code
34+
/// # Assumptions
35+
///
36+
/// This function assumes:
37+
/// 1. The provided `trie` has been populated with state data consistent with a known state root
38+
/// (e.g., using witness data and verifying against a parent block's state root).
39+
/// 2. The `bytecode` map contains all bytecode corresponding to code hashes present in the
40+
/// account data within the `trie`.
41+
/// 3. The `ancestor_hashes` map contains the block hashes for the relevant ancestor blocks (up
42+
/// to 256 including the current block number). It assumes these hashes correspond to a
43+
/// contiguous chain of blocks. The caller is responsible for verifying the contiguity and
44+
/// the block limit.
45+
pub(crate) const fn new(
46+
trie: &'a SparseState,
47+
bytecode: &'a B256Map<Bytecode>,
48+
ancestor_hashes: &'a BTreeMap<u64, B256>,
49+
) -> Self {
50+
Self {
51+
trie,
52+
block_hashes_by_block_number: ancestor_hashes,
53+
bytecode,
9154
}
9255
}
9356
}
9457

95-
impl<CodeDb: KeyValueStoreGet<B256, Bytes>, BlockHashProvider: KeyValueStoreGet<u64, B256>>
96-
DatabaseRef for EvmDatabase<CodeDb, BlockHashProvider>
97-
{
98-
type Error = DatabaseError;
99-
100-
/// Get basic account information.
101-
fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
102-
let Some(account) = self.state.get_account(address)? else {
103-
return Ok(None);
104-
};
105-
dev_trace!("load trie account of {address:?}: {account:?}");
106-
let code = self.load_code(account.code_hash);
107-
let info = AccountInfo {
108-
balance: account.balance,
109-
nonce: account.nonce,
110-
code_hash: account.code_hash,
111-
code,
112-
};
113-
114-
#[cfg(debug_assertions)]
115-
if let Some(ref code) = info.code {
116-
assert_eq!(
117-
info.code_hash,
118-
code.hash_slow(),
119-
"code hash mismatch for account {address:?}",
120-
);
121-
}
58+
impl Database for WitnessDatabase<'_> {
59+
/// The database error type.
60+
type Error = ProviderError;
12261

123-
Ok(Some(info))
62+
/// Get basic account information by hashing the address and looking up the account RLP
63+
/// in the underlying [`StatelessTrie`] implementation.
64+
///
65+
/// Returns `Ok(None)` if the account is not found in the trie.
66+
fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
67+
self.trie.account(address).map(|opt| {
68+
opt.map(|account| AccountInfo {
69+
balance: account.balance,
70+
nonce: account.nonce,
71+
code_hash: account.code_hash,
72+
code: None,
73+
})
74+
})
12475
}
12576

126-
/// Get account code by its code hash.
127-
fn code_by_hash_ref(&self, hash: B256) -> Result<Bytecode, Self::Error> {
128-
self.load_code(hash)
129-
.ok_or(DatabaseError::CodeNotLoaded(hash))
77+
/// Get account code by its hash from the provided bytecode map.
78+
///
79+
/// Returns an error if the bytecode for the given hash is not found in the map.
80+
fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
81+
self.bytecode.get(&code_hash).cloned().ok_or_else(|| {
82+
ProviderError::TrieWitnessError(format!("bytecode for {code_hash} not found"))
83+
})
13084
}
13185

132-
/// Get storage value of address at index.
133-
fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
134-
dev_trace!("get storage of {:?} at index {:?}", address, index);
135-
Ok(self.state.get_storage(address, index)?)
86+
/// Get storage value of an account at a specific slot.
87+
///
88+
/// Returns `U256::ZERO` if the slot is not found in the trie.
89+
fn storage(&mut self, address: Address, slot: U256) -> Result<U256, Self::Error> {
90+
self.trie.storage(address, slot)
13691
}
13792

138-
/// Get block hash by block number.
139-
fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
140-
Ok(*self
141-
.block_hashes
142-
.get(&number)
143-
.unwrap_or_else(|| panic!("block hash of number {number} not found")))
93+
/// Get block hash by block number from the provided ancestor hashes map.
94+
///
95+
/// Returns an error if the hash for the given block number is not found in the map.
96+
fn block_hash(&mut self, block_number: u64) -> Result<B256, Self::Error> {
97+
self.block_hashes_by_block_number
98+
.get(&block_number)
99+
.copied()
100+
.ok_or(ProviderError::StateForNumberNotFound(block_number))
144101
}
145102
}
146-
147-
impl DBErrorMarker for DatabaseError {}

0 commit comments

Comments
 (0)