Skip to content

Commit 851f514

Browse files
authored
feat: add legacy support (#58)
* add legacy support * clippy * update changelog * defaults to legacy
1 parent ac6476b commit 851f514

File tree

5 files changed

+183
-38
lines changed

5 files changed

+183
-38
lines changed

CHANGELOG.md

+16
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Add a new struct `LegacyStorageTrace` to support legacy storage trace support
13+
([#58](https://github.com/scroll-tech/stateless-block-verifier/pull/58))
14+
15+
### Changed
16+
17+
- `flatten_proofs` in `StorageTrace` is changed from `Vec<(B256, Bytes)` to `Vec<Bytes>`
18+
since the node hash will be recalculated when adding to zktrie
19+
([#58](https://github.com/scroll-tech/stateless-block-verifier/pull/58))
20+
- `BlockTrace` now has a generic parameter `S` for the storage trace type, default to `StorageTrace`
21+
([#58](https://github.com/scroll-tech/stateless-block-verifier/pull/58))
22+
- rpc mode defaults to use legacy storage trace, using the flag `--flatten-proofs` to enable support of flatten proofs
23+
([#58](https://github.com/scroll-tech/stateless-block-verifier/pull/58))
24+
25+
1026
## [2.0.0] - 2024-09-04
1127

1228
### Added

crates/bin/src/commands/run_rpc.rs

+35-16
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::utils;
22
use alloy::providers::{Provider, ProviderBuilder};
33
use clap::Args;
44
use futures::future::OptionFuture;
5+
use sbv::primitives::types::LegacyStorageTrace;
56
use sbv::{
67
core::HardforkConfig,
78
primitives::{types::BlockTrace, Block},
@@ -19,6 +20,9 @@ pub struct RunRpcCommand {
1920
/// RPC URL
2021
#[arg(short, long, default_value = "http://localhost:8545")]
2122
url: Url,
23+
/// Enable flatten proofs
24+
#[arg(short, long)]
25+
flatten_proofs: bool,
2226
/// Start Block number
2327
#[arg(short, long, default_value = "latest")]
2428
start_block: StartBlockSpec,
@@ -49,7 +53,11 @@ pub enum StartBlockSpec {
4953

5054
impl RunRpcCommand {
5155
pub async fn run(self, fork_config: impl Fn(u64) -> HardforkConfig) -> anyhow::Result<()> {
52-
dev_info!("Running RPC command with url: {}", self.url);
56+
dev_info!(
57+
"Running RPC command with url: {}, flatten proofs support: {}",
58+
self.url,
59+
self.flatten_proofs
60+
);
5361
let provider = ProviderBuilder::new().on_http(self.url);
5462

5563
let chain_id = provider.get_chain_id().await?;
@@ -75,21 +83,32 @@ impl RunRpcCommand {
7583
let rx = rx.clone();
7684
handles.spawn(async move {
7785
while let Ok(block_number) = rx.recv().await {
78-
let l2_trace = _provider
79-
.raw_request::<_, BlockTrace>(
80-
"scroll_getBlockTraceByNumberOrHash".into(),
81-
(
82-
format!("0x{:x}", block_number),
83-
serde_json::json!({
84-
"ExcludeExecutionResults": true,
85-
"ExcludeTxStorageTraces": true,
86-
"StorageProofFormat": "flatten",
87-
"FlattenProofsOnly": true
88-
}),
89-
),
90-
)
91-
.await
92-
.map_err(|e| (block_number, e.into()))?;
86+
let l2_trace: BlockTrace = if !self.flatten_proofs {
87+
let trace = _provider
88+
.raw_request::<_, BlockTrace<LegacyStorageTrace>>(
89+
"scroll_getBlockTraceByNumberOrHash".into(),
90+
(format!("0x{:x}", block_number),),
91+
)
92+
.await
93+
.map_err(|e| (block_number, e.into()))?;
94+
trace.into()
95+
} else {
96+
_provider
97+
.raw_request::<_, BlockTrace>(
98+
"scroll_getBlockTraceByNumberOrHash".into(),
99+
(
100+
format!("0x{:x}", block_number),
101+
serde_json::json!({
102+
"ExcludeExecutionResults": true,
103+
"ExcludeTxStorageTraces": true,
104+
"StorageProofFormat": "flatten",
105+
"FlattenProofsOnly": true
106+
}),
107+
),
108+
)
109+
.await
110+
.map_err(|e| (block_number, e.into()))?
111+
};
93112

94113
dev_info!(
95114
"worker#{_idx}: load trace for block #{block_number}({})",

crates/core/src/error.rs

+2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ use std::error::Error;
44
/// Error variants encountered during manipulation of a zkTrie.
55
#[derive(Debug, thiserror::Error)]
66
pub enum DatabaseError {
7+
/// Error encountered from code db.
78
#[error("error encountered from code db: {0}")]
89
CodeDb(Box<dyn Error + Send + Sync>),
10+
/// Error encountered from zkTrie.
911
#[error("error encountered from zkTrie: {0}")]
1012
ZkTrie(Box<dyn Error + Send + Sync>),
1113
}

crates/primitives/src/lib.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -76,18 +76,17 @@ pub trait Block: Debug {
7676
fn start_l1_queue_index(&self) -> u64;
7777

7878
/// flatten proofs
79-
fn flatten_proofs(&self) -> impl Iterator<Item = (&B256, &[u8])>;
79+
fn flatten_proofs(&self) -> impl Iterator<Item = &[u8]>;
8080

8181
/// Update zktrie state from trace
8282
#[inline]
8383
fn build_zktrie_db<Db: KVDatabase>(&self, db: &mut Db) -> Result<(), Db::Error> {
84-
for (k, bytes) in self.flatten_proofs() {
84+
for bytes in self.flatten_proofs() {
8585
if bytes == MAGIC_NODE_BYTES {
8686
continue;
8787
}
8888
let node = Node::<Poseidon>::try_from(bytes).expect("invalid node");
8989
let node_hash = node.get_or_calculate_node_hash().expect("infallible");
90-
debug_assert_eq!(k.as_slice(), node_hash.as_slice());
9190
dev_trace!("put zktrie node: {:?}", node);
9291
db.put_owned(node_hash.as_slice(), node.canonical_value(false))?;
9392
}
@@ -334,7 +333,7 @@ impl<T: Block> Block for &T {
334333
(*self).start_l1_queue_index()
335334
}
336335

337-
fn flatten_proofs(&self) -> impl Iterator<Item = (&B256, &[u8])> {
336+
fn flatten_proofs(&self) -> impl Iterator<Item = &[u8]> {
338337
(*self).flatten_proofs()
339338
}
340339
}

crates/primitives/src/types/mod.rs

+127-18
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
use crate::Block;
22
use alloy::primitives::{Address, Bytes, B256, U256};
3-
use serde::{Deserialize, Serialize};
3+
use rkyv::Archive;
4+
use serde::{Deserialize, Deserializer, Serialize};
45
use serde_with::{serde_as, Map};
6+
use std::collections::{BTreeSet, HashMap};
7+
use std::fmt::Debug;
8+
use std::hash::Hash;
59

610
mod tx;
711
pub use tx::{ArchivedTransactionTrace, TransactionTrace, TxL1Msg, TypedTransaction};
@@ -58,16 +62,15 @@ pub struct BytecodeTrace {
5862
}
5963

6064
/// storage trace
61-
#[serde_as]
6265
#[derive(
6366
rkyv::Archive,
6467
rkyv::Serialize,
6568
rkyv::Deserialize,
6669
Serialize,
67-
Deserialize,
6870
Default,
6971
Debug,
7072
Clone,
73+
Hash,
7174
Eq,
7275
PartialEq,
7376
)]
@@ -82,8 +85,45 @@ pub struct StorageTrace {
8285
pub root_after: B256,
8386
/// proofs
8487
#[serde(rename = "flattenProofs")]
88+
pub flatten_proofs: Vec<Bytes>,
89+
}
90+
91+
/// legacy storage trace
92+
#[serde_as]
93+
#[derive(
94+
rkyv::Archive,
95+
rkyv::Serialize,
96+
rkyv::Deserialize,
97+
Serialize,
98+
Deserialize,
99+
Default,
100+
Debug,
101+
Clone,
102+
Hash,
103+
Eq,
104+
PartialEq,
105+
)]
106+
#[archive(check_bytes)]
107+
#[archive_attr(derive(Debug, Hash, PartialEq, Eq))]
108+
#[allow(clippy::type_complexity)]
109+
pub struct LegacyStorageTrace {
110+
/// root before
111+
#[serde(rename = "rootBefore")]
112+
pub root_before: B256,
113+
/// root after
114+
#[serde(rename = "rootAfter")]
115+
pub root_after: B256,
116+
/// account proofs
117+
#[serde(default)]
85118
#[serde_as(as = "Map<_, _>")]
86-
pub flatten_proofs: Vec<(B256, Bytes)>,
119+
pub proofs: Vec<(Address, Vec<Bytes>)>,
120+
#[serde(rename = "storageProofs", default)]
121+
#[serde_as(as = "Map<_, Map<_, _>>")]
122+
/// storage proofs for each account
123+
pub storage_proofs: Vec<(Address, Vec<(B256, Vec<Bytes>)>)>,
124+
#[serde(rename = "deletionProofs", default)]
125+
/// additional deletion proofs
126+
pub deletion_proofs: Vec<Bytes>,
87127
}
88128

89129
/// Block trace format
@@ -94,7 +134,11 @@ pub struct StorageTrace {
94134
)]
95135
#[archive(check_bytes)]
96136
#[archive_attr(derive(Debug, Hash, PartialEq, Eq))]
97-
pub struct BlockTrace {
137+
pub struct BlockTrace<S = StorageTrace>
138+
where
139+
S: Archive,
140+
<S as Archive>::Archived: Debug + Hash + PartialEq + Eq,
141+
{
98142
/// chain id
99143
#[serde(rename = "chainID", default)]
100144
pub chain_id: u64,
@@ -108,14 +152,86 @@ pub struct BlockTrace {
108152
pub codes: Vec<BytecodeTrace>,
109153
/// storage trace BEFORE execution
110154
#[serde(rename = "storageTrace")]
111-
pub storage_trace: StorageTrace,
155+
pub storage_trace: S,
112156
/// l1 tx queue
113157
#[serde(rename = "startL1QueueIndex", default)]
114158
pub start_l1_queue_index: u64,
115159
/// Withdraw root
116160
pub withdraw_trie_root: B256,
117161
}
118162

163+
impl<'de> Deserialize<'de> for StorageTrace {
164+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
165+
where
166+
D: Deserializer<'de>,
167+
{
168+
#[derive(Deserialize)]
169+
#[serde(untagged)]
170+
enum FlattenProofs {
171+
Map(HashMap<B256, Bytes>),
172+
Vec(Vec<Bytes>),
173+
}
174+
#[derive(Deserialize)]
175+
struct StorageTraceDe {
176+
#[serde(rename = "rootBefore")]
177+
pub root_before: B256,
178+
#[serde(rename = "rootAfter")]
179+
pub root_after: B256,
180+
#[serde(rename = "flattenProofs")]
181+
pub flatten_proofs: FlattenProofs,
182+
}
183+
184+
let de = StorageTraceDe::deserialize(deserializer)?;
185+
let mut flatten_proofs = match de.flatten_proofs {
186+
FlattenProofs::Map(map) => map.into_values().collect(),
187+
FlattenProofs::Vec(vec) => vec,
188+
};
189+
flatten_proofs.sort();
190+
191+
Ok(StorageTrace {
192+
root_before: de.root_before,
193+
root_after: de.root_after,
194+
flatten_proofs,
195+
})
196+
}
197+
}
198+
199+
impl From<LegacyStorageTrace> for StorageTrace {
200+
fn from(trace: LegacyStorageTrace) -> Self {
201+
let mut flatten_proofs = BTreeSet::new();
202+
for (_, proofs) in trace.proofs {
203+
flatten_proofs.extend(proofs);
204+
}
205+
for (_, proofs) in trace.storage_proofs {
206+
for (_, proofs) in proofs {
207+
flatten_proofs.extend(proofs);
208+
}
209+
}
210+
flatten_proofs.extend(trace.deletion_proofs);
211+
212+
StorageTrace {
213+
root_before: trace.root_before,
214+
root_after: trace.root_after,
215+
flatten_proofs: flatten_proofs.into_iter().collect(),
216+
}
217+
}
218+
}
219+
220+
impl From<BlockTrace<LegacyStorageTrace>> for BlockTrace {
221+
fn from(trace: BlockTrace<LegacyStorageTrace>) -> Self {
222+
BlockTrace {
223+
chain_id: trace.chain_id,
224+
coinbase: trace.coinbase,
225+
header: trace.header,
226+
transactions: trace.transactions,
227+
codes: trace.codes,
228+
storage_trace: trace.storage_trace.into(),
229+
start_l1_queue_index: trace.start_l1_queue_index,
230+
withdraw_trie_root: trace.withdraw_trie_root,
231+
}
232+
}
233+
}
234+
119235
impl Block for BlockTrace {
120236
type Tx = TransactionTrace;
121237

@@ -179,11 +295,8 @@ impl Block for BlockTrace {
179295
self.start_l1_queue_index
180296
}
181297

182-
fn flatten_proofs(&self) -> impl Iterator<Item = (&B256, &[u8])> {
183-
self.storage_trace
184-
.flatten_proofs
185-
.iter()
186-
.map(|(k, v)| (k, v.as_ref()))
298+
fn flatten_proofs(&self) -> impl Iterator<Item = &[u8]> {
299+
self.storage_trace.flatten_proofs.iter().map(|v| v.as_ref())
187300
}
188301
}
189302

@@ -254,11 +367,8 @@ impl Block for ArchivedBlockTrace {
254367
self.start_l1_queue_index
255368
}
256369

257-
fn flatten_proofs(&self) -> impl Iterator<Item = (&B256, &[u8])> {
258-
self.storage_trace
259-
.flatten_proofs
260-
.iter()
261-
.map(|(k, v)| (k, v.as_ref()))
370+
fn flatten_proofs(&self) -> impl Iterator<Item = &[u8]> {
371+
self.storage_trace.flatten_proofs.iter().map(|v| v.as_ref())
262372
}
263373
}
264374

@@ -404,8 +514,7 @@ mod tests {
404514
.iter()
405515
.zip(archived_block.storage_trace.flatten_proofs.iter())
406516
{
407-
assert_eq!(proof.0, archived_proof.0);
408-
assert_eq!(proof.1.as_ref(), archived_proof.1.as_ref());
517+
assert_eq!(proof.as_ref(), archived_proof.as_ref());
409518
}
410519

411520
assert_eq!(

0 commit comments

Comments
 (0)