├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── README.md ├── crates ├── bin │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── commands │ │ ├── mod.rs │ │ ├── run │ │ │ ├── file.rs │ │ │ ├── mod.rs │ │ │ └── rpc.rs │ │ └── witness │ │ │ ├── dump.rs │ │ │ ├── mod.rs │ │ │ └── rkyv_convert.rs │ │ ├── helpers │ │ ├── mod.rs │ │ ├── retry.rs │ │ ├── tower.rs │ │ └── verifier.rs │ │ └── main.rs ├── core │ ├── Cargo.toml │ └── src │ │ ├── database.rs │ │ ├── error.rs │ │ ├── executor.rs │ │ └── lib.rs ├── helpers │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── macros.rs │ │ ├── metrics │ │ ├── mod.rs │ │ └── registry.rs │ │ └── utils │ │ ├── debug.rs │ │ └── mod.rs ├── kv │ ├── Cargo.toml │ └── src │ │ ├── imps │ │ ├── mod.rs │ │ ├── nohash.rs │ │ ├── null.rs │ │ └── std_collections.rs │ │ └── lib.rs ├── primitives │ ├── Cargo.toml │ └── src │ │ ├── chainspec.rs │ │ ├── data │ │ ├── v1_l1_oracle_bytecode.bin │ │ ├── v1_l1_oracle_bytecode.txt │ │ ├── v2_l1_oracle_bytecode.bin │ │ └── v2_l1_oracle_bytecode.txt │ │ ├── ext.rs │ │ ├── hardforks.rs │ │ ├── lib.rs │ │ ├── predeployed.rs │ │ └── types │ │ ├── access_list.rs │ │ ├── auth_list.rs │ │ ├── block_header.rs │ │ ├── consensus.rs │ │ ├── eips.rs │ │ ├── mod.rs │ │ ├── reth.rs │ │ ├── rpc.rs │ │ ├── scroll │ │ ├── chunk.rs │ │ ├── chunk_builder.rs │ │ └── mod.rs │ │ ├── signature.rs │ │ ├── transaction.rs │ │ ├── withdrawal.rs │ │ └── witness.rs ├── sbv │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── stateful │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── error.rs │ │ ├── lib.rs │ │ ├── main.rs │ │ ├── pipeline.rs │ │ ├── sanity_check.rs │ │ └── utils.rs ├── trie │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── utils │ ├── Cargo.toml │ └── src │ ├── lib.rs │ ├── rpc.rs │ └── witness.rs ├── grafana.json ├── rust-toolchain.toml ├── rustfmt.toml ├── scripts └── download.sh └── testdata ├── .gitignore ├── holesky_witness ├── 2971844.json ├── 2971845.json ├── 2971846.json └── 2971847.json └── scroll_witness ├── 12508460.json ├── 12508461.json ├── 12508462.json ├── 12508463.json ├── enforce-revert.json └── euclid_v2 ├── 1.json ├── 2.json ├── 3.json ├── 4.json ├── 5.json ├── 6.json ├── 7.json └── 8.json /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | types: [synchronize, opened, reopened, ready_for_review] 6 | push: 7 | branches: 8 | - master 9 | 10 | env: 11 | CARGO_NET_GIT_FETCH_WITH_CLI: true 12 | CARGO_REGISTRIES_CRATES_IO_PROTOCOL: git 13 | 14 | jobs: 15 | skip_check: 16 | runs-on: ubuntu-latest 17 | outputs: 18 | should_skip: ${{ steps.skip_check.outputs.should_skip }} 19 | steps: 20 | - id: skip_check 21 | uses: fkirc/skip-duplicate-actions@v5 22 | with: 23 | cancel_others: 'true' 24 | concurrent_skipping: 'same_content_newer' 25 | paths_ignore: '["**/README.md"]' 26 | 27 | fmt: 28 | needs: [ skip_check ] 29 | if: | 30 | github.event.pull_request.draft == false && 31 | (github.event.action == 'ready_for_review' || needs.skip_check.outputs.should_skip != 'true') 32 | name: Rustfmt 33 | timeout-minutes: 30 34 | runs-on: ubuntu-latest 35 | steps: 36 | - uses: actions/checkout@v4 37 | - uses: dtolnay/rust-toolchain@master 38 | with: 39 | toolchain: "nightly-2024-12-06" 40 | components: rustfmt 41 | - name: Cargo fmt 42 | run: cargo fmt --all -- --check 43 | 44 | clippy: 45 | needs: [ fmt ] 46 | if: | 47 | github.event.pull_request.draft == false && 48 | (github.event.action == 'ready_for_review' || needs.skip_check.outputs.should_skip != 'true') 49 | name: Clippy 50 | timeout-minutes: 30 51 | runs-on: ubuntu-latest 52 | steps: 53 | - uses: actions/checkout@v4 54 | - uses: dtolnay/rust-toolchain@master 55 | with: 56 | toolchain: "nightly-2024-12-06" 57 | components: clippy 58 | - name: cargo cache 59 | uses: Swatinem/rust-cache@v2 60 | - name: clippy default 61 | run: cargo clippy --workspace -- -D warnings 62 | - name: clippy dev 63 | run: cargo clippy --workspace --features dev -- -D warnings 64 | - name: clippy metrics 65 | run: cargo clippy --workspace --features metrics -- -D warnings 66 | - name: clippy all 67 | run: cargo clippy --workspace --all-features -- -D warnings 68 | - name: clippy test 69 | run: cargo clippy --workspace --tests -- -D warnings 70 | - name: clippy test all 71 | run: cargo clippy --workspace --tests --all-features -- -D warnings 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 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` 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 | 26 | ## [2.0.0] - 2024-09-04 27 | 28 | ### Added 29 | 30 | - Add rkyv support, support zero-copy deserialization 31 | - Add an `ordered-db` feature gate to ensure the order when iterating over the database 32 | - Add a new sp1 cycle tracker macro `cycle_track!` which returns wrapped expression 33 | - Add chunk mode to work in chunk mode ([#29](https://github.com/scroll-tech/stateless-block-verifier/pull/29)) 34 | - Add openmetrics support and a `metrics` feature gate ([#33](https://github.com/scroll-tech/stateless-block-verifier/pull/33)) 35 | - Add zktrie lazy commitment ([#39](https://github.com/scroll-tech/stateless-block-verifier/pull/39)) 36 | 37 | ### Fixed 38 | 39 | - revm v40 upgrade cause `EXTCODEHASH` loads code to check if it's EOF, fixed by [revm#17](https://github.com/scroll-tech/revm/pull/17/files) 40 | - The tx hash is ignored and the tx hash is calculated from the tx body instead 41 | - The `from` field of the transaction trace is ignored if it's not l1 msg, the `tx.from` will be recovered from the signature instead 42 | - `BLOBHASH` & `BLOBBASEFEE` opcodes were accidentally enabled in CURIE ([#40](https://github.com/scroll-tech/stateless-block-verifier/pull/40)) 43 | 44 | ### Changed 45 | 46 | - Code database now use the keccak code hash as key, instead of the poseidon hash of the code ([#20](https://github.com/scroll-tech/stateless-block-verifier/pull/20)) 47 | - Remove StateDB, direct query the zktrie db ([#38](https://github.com/scroll-tech/stateless-block-verifier/pull/38)) 48 | - Dependency of `eth-types` is removed ([#43](https://github.com/scroll-tech/stateless-block-verifier/pull/43)) 49 | - Dependency of `mpt-zktrie` is removed ([#45](https://github.com/scroll-tech/stateless-block-verifier/pull/45)) 50 | - Dependency of `ethers-rs` is removed ([#46](https://github.com/scroll-tech/stateless-block-verifier/pull/46)) 51 | 52 | ### Removed 53 | 54 | - `post_check` is removed as long as the command line argument `--disable-check` 55 | - Support of legacy trace format is removed, only support the trace with codes and flatten proofs now. 56 | 57 | ## [1.0.0] - 2024-07-26 58 | 59 | ### Added 60 | 61 | - Initial release 62 | 63 | [unreleased]: https://github.com/scroll-tech/stateless-block-verifier/compare/2.0.0...HEAD 64 | [2.0.0]: https://github.com/scroll-tech/stateless-block-verifier/compare/v1.0.0...v2.0.0 65 | [1.0.0]: https://github.com/scroll-tech/stateless-block-verifier/releases/tag/v1.0.0 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Stateless Block Verifier 2 | 3 | This project provides tools for stateless verification of blocks with mpt state roots. 4 | 5 | ## Cli Interface 6 | 7 | See [./crates/bin/README.md](./crates/bin/README.md) for more details. 8 | -------------------------------------------------------------------------------- /crates/bin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sbv-cli" 3 | homepage = "https://github.com/scroll-tech/stateless-block-verifier/tree/master/crates/bin" 4 | 5 | version.workspace = true 6 | edition.workspace = true 7 | rust-version.workspace = true 8 | authors.workspace = true 9 | license.workspace = true 10 | repository.workspace = true 11 | 12 | [lints] 13 | workspace = true 14 | 15 | [dependencies] 16 | alloy = { workspace = true, features = ["provider-http", "transport-http", "reqwest", "reqwest-rustls-tls", "json-rpc"] } 17 | 18 | anyhow.workspace = true 19 | clap = { workspace = true, features = ["derive"] } 20 | console.workspace = true 21 | indicatif.workspace = true 22 | num_cpus = "1.16" 23 | pumps = "0.0.4" 24 | rkyv.workspace = true 25 | serde_json.workspace = true 26 | serde_path_to_error.workspace = true 27 | tower = { workspace = true, features = ["limit"] } 28 | tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } 29 | url.workspace = true 30 | 31 | pprof = { workspace = true, optional = true } 32 | tracing-subscriber = { workspace = true, optional = true } 33 | 34 | sbv.workspace = true 35 | 36 | [dev-dependencies] 37 | tracing.workspace = true 38 | tracing-subscriber.workspace = true 39 | 40 | [features] 41 | default = ["dev"] 42 | dev = ["sbv/dev", "dep:tracing-subscriber"] 43 | debug-account = ["sbv/debug-account"] 44 | debug-storage = ["sbv/debug-storage"] 45 | profiling = ["dep:pprof"] 46 | scroll = ["sbv/scroll"] 47 | metrics = ["sbv/metrics"] 48 | -------------------------------------------------------------------------------- /crates/bin/src/commands/mod.rs: -------------------------------------------------------------------------------- 1 | use clap::Subcommand; 2 | 3 | mod run; 4 | mod witness; 5 | 6 | #[derive(Debug, Subcommand)] 7 | pub enum Commands { 8 | #[command(subcommand, about = "Run and verify witness")] 9 | Run(run::RunCommands), 10 | #[command(subcommand, about = "Witness helpers")] 11 | Witness(witness::WitnessCommands), 12 | } 13 | 14 | impl Commands { 15 | pub fn run(self) -> anyhow::Result<()> { 16 | match self { 17 | Commands::Run(cmd) => cmd.run(), 18 | Commands::Witness(cmd) => cmd.run(), 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /crates/bin/src/commands/run/file.rs: -------------------------------------------------------------------------------- 1 | use crate::helpers::verifier::*; 2 | use clap::Args; 3 | #[cfg(feature = "dev")] 4 | use sbv::helpers::tracing; 5 | use sbv::primitives::types::BlockWitness; 6 | use std::path::PathBuf; 7 | 8 | #[derive(Args, Debug)] 9 | pub struct RunFileCommand { 10 | /// Path to the witness file 11 | #[arg(default_value = "witness.json")] 12 | path: Vec, 13 | /// Chunk mode 14 | #[cfg(feature = "scroll")] 15 | #[arg(short, long)] 16 | chunk_mode: bool, 17 | #[cfg(feature = "scroll")] 18 | #[arg(long)] 19 | prev_msg_queue_hash: Option, 20 | } 21 | 22 | impl RunFileCommand { 23 | #[cfg(not(feature = "scroll"))] 24 | pub fn run(self) -> anyhow::Result<()> { 25 | self.run_witnesses() 26 | } 27 | 28 | #[cfg(feature = "scroll")] 29 | pub fn run(self) -> anyhow::Result<()> { 30 | if self.chunk_mode { 31 | self.run_chunk() 32 | } else { 33 | self.run_witnesses() 34 | } 35 | } 36 | 37 | fn run_witnesses(self) -> anyhow::Result<()> { 38 | let mut gas_used = 0; 39 | for path in self.path.into_iter() { 40 | gas_used += run_witness(path)? 41 | } 42 | dev_info!("Gas used: {}", gas_used); 43 | 44 | Ok(()) 45 | } 46 | 47 | #[cfg(feature = "scroll")] 48 | fn run_chunk(self) -> anyhow::Result<()> { 49 | use anyhow::bail; 50 | use sbv::{ 51 | core::{EvmDatabase, EvmExecutor}, 52 | kv::{nohash::NoHashMap, null::NullProvider}, 53 | primitives::{ 54 | chainspec::{Chain, get_chain_spec_or_build}, 55 | ext::{BlockWitnessChunkExt, BlockWitnessExt}, 56 | types::{BlockWitness, reth::BlockWitnessRethExt, scroll::ChunkInfoBuilder}, 57 | }, 58 | trie::BlockWitnessTrieExt, 59 | }; 60 | 61 | let witnesses = self 62 | .path 63 | .iter() 64 | .map(read_witness) 65 | .collect::, _>>()?; 66 | 67 | if !witnesses.has_same_chain_id() { 68 | bail!("All traces must have the same chain id in chunk mode"); 69 | } 70 | 71 | if !witnesses.has_seq_block_number() { 72 | bail!("All traces must have sequential block numbers in chunk mode"); 73 | } 74 | 75 | let blocks = witnesses 76 | .iter() 77 | .map(|w| w.build_reth_block()) 78 | .collect::, _>>()?; 79 | 80 | let chain_spec = get_chain_spec_or_build(Chain::from_id(witnesses.chain_id()), |_spec| { 81 | #[cfg(feature = "scroll")] 82 | { 83 | use sbv::primitives::hardforks::{ForkCondition, ScrollHardfork}; 84 | _spec 85 | .inner 86 | .hardforks 87 | .insert(ScrollHardfork::EuclidV2, ForkCondition::Timestamp(0)); 88 | } 89 | }); 90 | 91 | let mut chunk_info_builder = 92 | ChunkInfoBuilder::new(&chain_spec, witnesses.prev_state_root(), &blocks); 93 | if let Some(prev_msg_queue_hash) = self.prev_msg_queue_hash { 94 | chunk_info_builder.set_prev_msg_queue_hash(prev_msg_queue_hash); 95 | } 96 | 97 | let mut code_db = NoHashMap::default(); 98 | witnesses.import_codes(&mut code_db); 99 | let mut nodes_provider = NoHashMap::default(); 100 | witnesses.import_nodes(&mut nodes_provider)?; 101 | 102 | let mut db = EvmDatabase::new_from_root( 103 | &code_db, 104 | chunk_info_builder.prev_state_root(), 105 | &nodes_provider, 106 | &NullProvider, 107 | )?; 108 | for block in blocks.iter() { 109 | let output = EvmExecutor::new(chain_spec.clone(), &db, block).execute()?; 110 | db.update(&nodes_provider, output.state.state.iter())?; 111 | } 112 | let post_state_root = db.commit_changes(); 113 | if post_state_root != chunk_info_builder.post_state_root() { 114 | bail!("post state root mismatch"); 115 | } 116 | 117 | let chunk_info = chunk_info_builder.build(db.withdraw_root()?); 118 | let _public_input_hash = chunk_info.pi_hash(); 119 | dev_info!("[chunk mode] public input hash: {_public_input_hash:?}"); 120 | 121 | Ok(()) 122 | } 123 | } 124 | 125 | fn read_witness(path: &PathBuf) -> anyhow::Result { 126 | let witness = std::fs::File::open(path)?; 127 | let jd = &mut serde_json::Deserializer::from_reader(&witness); 128 | let witness = serde_path_to_error::deserialize::<_, BlockWitness>(jd)?; 129 | Ok(witness) 130 | } 131 | 132 | #[cfg_attr(feature = "dev", tracing::instrument(skip_all, fields(path = %path.display()), err))] 133 | fn run_witness(path: PathBuf) -> anyhow::Result { 134 | let witness = read_witness(&path)?; 135 | verify_catch_panics(&witness).inspect(|_| dev_info!("verified")) 136 | } 137 | -------------------------------------------------------------------------------- /crates/bin/src/commands/run/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::helpers::run_async; 2 | use clap::Subcommand; 3 | 4 | mod file; 5 | mod rpc; 6 | 7 | #[derive(Subcommand, Debug)] 8 | pub enum RunCommands { 9 | /// Run and verify a trace file 10 | #[command(name = "file")] 11 | RunFile(file::RunFileCommand), 12 | /// Run and verify from RPC 13 | #[command(name = "rpc")] 14 | RunRpc(rpc::RunRpcCommand), 15 | } 16 | 17 | impl RunCommands { 18 | pub fn run(self) -> anyhow::Result<()> { 19 | match self { 20 | RunCommands::RunFile(cmd) => cmd.run(), 21 | RunCommands::RunRpc(cmd) => Ok(run_async(cmd.run())?), 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /crates/bin/src/commands/run/rpc.rs: -------------------------------------------------------------------------------- 1 | use crate::helpers::{RpcArgs, verifier::verify_catch_panics}; 2 | use clap::Args; 3 | use pumps::{Concurrency, Pipeline}; 4 | use sbv::{primitives::BlockWitness, utils::rpc::ProviderExt}; 5 | use std::{ 6 | iter, 7 | sync::{ 8 | Arc, Mutex, 9 | atomic::{AtomicBool, AtomicUsize}, 10 | }, 11 | time::Instant, 12 | }; 13 | 14 | #[derive(Args, Debug)] 15 | pub struct RunRpcCommand { 16 | #[arg(long, help = "start block number")] 17 | pub start_block: u64, 18 | #[command(flatten)] 19 | pub rpc_args: RpcArgs, 20 | } 21 | 22 | impl RunRpcCommand { 23 | pub async fn run(self) -> anyhow::Result<()> { 24 | let max_concurrency = self.rpc_args.max_concurrency; 25 | let provider = self.rpc_args.into_provider(); 26 | let running = Arc::new(AtomicBool::new(true)); 27 | 28 | let last_time = Mutex::new(Instant::now()); 29 | let processed_blocks = Arc::new(AtomicUsize::new(0)); 30 | 31 | let blocks = { 32 | let running = running.clone(); 33 | iter::successors(Some(self.start_block), move |n| { 34 | if running.load(std::sync::atomic::Ordering::SeqCst) { 35 | Some(n + 1) 36 | } else { 37 | dev_warn!( 38 | "received stop signal, stop emitting new blocks, current block: #{n}" 39 | ); 40 | None 41 | } 42 | }) 43 | }; 44 | 45 | let (_out, h) = Pipeline::from_iter(blocks) 46 | .filter_map( 47 | move |block_number| { 48 | let provider = provider.clone(); 49 | async move { 50 | loop { 51 | #[cfg(feature = "scroll")] 52 | let fut = provider.dump_block_witness(block_number.into()); 53 | #[cfg(not(feature = "scroll"))] 54 | let fut = provider.dump_block_witness(block_number.into(), None); 55 | match fut.await { 56 | Ok(Some(w)) => { 57 | dev_info!("dumped block witness for #{block_number}"); 58 | return Some(w); 59 | } 60 | Ok(None) => { 61 | tokio::time::sleep(tokio::time::Duration::from_millis(500)) 62 | .await; 63 | } 64 | Err(_e) => { 65 | dev_error!( 66 | "failed to dump block witness for #{block_number}: {_e:?}" 67 | ); 68 | return None; 69 | } 70 | } 71 | } 72 | } 73 | }, 74 | Concurrency::concurrent_unordered(max_concurrency), 75 | ) 76 | .backpressure(max_concurrency) 77 | .map( 78 | |witness| async move { 79 | let _number = witness.number(); 80 | 81 | match tokio::task::spawn_blocking(move || verify_catch_panics(witness)) 82 | .await 83 | .map_err(anyhow::Error::from) 84 | .and_then(|e| e) 85 | { 86 | Ok(_) => dev_info!("block#{_number} verified"), 87 | Err(_e) => dev_info!("failed to verify block#{_number}: {_e:?}"), 88 | } 89 | }, 90 | Concurrency::concurrent_unordered(num_cpus::get()), 91 | ) 92 | .filter_map( 93 | move |_| { 94 | processed_blocks.fetch_add(1, std::sync::atomic::Ordering::Relaxed); 95 | if processed_blocks 96 | .compare_exchange( 97 | 100, 98 | 0, 99 | std::sync::atomic::Ordering::SeqCst, 100 | std::sync::atomic::Ordering::SeqCst, 101 | ) 102 | .is_ok() 103 | { 104 | let now = Instant::now(); 105 | let _elapsed = { 106 | let mut last = last_time.lock().unwrap(); 107 | let elapsed = now.duration_since(*last); 108 | *last = now; 109 | elapsed.as_secs_f64() 110 | }; 111 | dev_info!("bps: {:.2}", 100.0 / _elapsed); 112 | } 113 | async { None::<()> } 114 | }, 115 | Concurrency::concurrent_unordered(usize::MAX), 116 | ) 117 | .build(); 118 | 119 | tokio::signal::ctrl_c().await?; 120 | running.store(false, std::sync::atomic::Ordering::SeqCst); 121 | tokio::select! { 122 | _ = h => { 123 | dev_info!("pipeline finished"); 124 | } 125 | _ = tokio::signal::ctrl_c() => { 126 | dev_warn!("received ctrl-c again, force stop"); 127 | } 128 | } 129 | Ok(()) 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /crates/bin/src/commands/witness/dump.rs: -------------------------------------------------------------------------------- 1 | use crate::helpers::RpcArgs; 2 | use clap::Args; 3 | use console::{Emoji, style}; 4 | use indicatif::{HumanBytes, HumanDuration, ProgressBar, ProgressStyle}; 5 | use rkyv::rancor; 6 | use sbv::utils::rpc::ProviderExt; 7 | use std::{ 8 | path::PathBuf, 9 | time::{Duration, Instant}, 10 | }; 11 | 12 | #[derive(Debug, Args)] 13 | pub struct DumpWitnessCommand { 14 | #[arg(long, help = "Block number")] 15 | pub block: u64, 16 | #[arg(long, help = "Ancestor blocks", default_value_t = 256)] 17 | #[cfg(not(feature = "scroll"))] 18 | pub ancestors: usize, 19 | #[arg(long, help = "Output directory", default_value_os_t = std::env::current_dir().unwrap())] 20 | pub out_dir: PathBuf, 21 | #[arg(long, help = "Output json")] 22 | pub json: bool, 23 | #[arg(long, help = "Output rkyv")] 24 | pub rkyv: bool, 25 | 26 | #[command(flatten)] 27 | pub rpc_args: RpcArgs, 28 | } 29 | 30 | impl DumpWitnessCommand { 31 | pub async fn run(self) -> anyhow::Result<()> { 32 | let started = Instant::now(); 33 | 34 | if self.out_dir.is_file() { 35 | anyhow::bail!("Output path is a file"); 36 | } 37 | std::fs::create_dir_all(&self.out_dir)?; 38 | if !self.json && !self.rkyv { 39 | eprintln!("{}No output format specified", Emoji("⚠️ ", "")); 40 | } 41 | 42 | #[cfg(not(feature = "scroll"))] 43 | if self.ancestors < 1 || self.ancestors > 256 { 44 | anyhow::bail!("Invalid ancestor blocks count"); 45 | } 46 | 47 | let mut steps = 1; 48 | let total_steps = 1 + self.json as usize + self.rkyv as usize; 49 | 50 | let provider = self.rpc_args.into_provider(); 51 | 52 | let pb = ProgressBar::new_spinner(); 53 | pb.set_style(ProgressStyle::with_template("{prefix}{msg} {spinner}").unwrap()); 54 | pb.set_prefix(format!( 55 | "{} {}", 56 | style(format!("[{}/{}]", steps, total_steps)).bold().dim(), 57 | Emoji("🔗 ", "") 58 | )); 59 | pb.enable_steady_tick(Duration::from_millis(100)); 60 | pb.set_message(format!("Dumping witness for block {}", self.block)); 61 | steps += 1; 62 | 63 | #[cfg(not(feature = "scroll"))] 64 | let witness = provider 65 | .dump_block_witness(self.block.into(), Some(self.ancestors)) 66 | .await?; 67 | #[cfg(feature = "scroll")] 68 | let witness = provider.dump_block_witness(self.block.into()).await?; 69 | 70 | pb.finish_with_message(format!("Dumped witness for block {}", self.block)); 71 | println!(); 72 | 73 | if self.json { 74 | let json = serde_json::to_string_pretty(&witness)?; 75 | let path = self.out_dir.join(format!("{}.json", self.block)); 76 | std::fs::write(&path, json)?; 77 | let size = HumanBytes(std::fs::metadata(&path)?.len()); 78 | println!( 79 | "{} {}JSON witness({}) saved to {}", 80 | style(format!("[{}/{}]", steps, total_steps)).bold().dim(), 81 | Emoji("📃 ", ""), 82 | size, 83 | path.display() 84 | ); 85 | steps += 1; 86 | } 87 | 88 | if self.rkyv { 89 | let serialized = rkyv::to_bytes::(&witness)?; 90 | let path = self.out_dir.join(format!("{}.rkyv", self.block)); 91 | std::fs::write(&path, serialized)?; 92 | let size = HumanBytes(std::fs::metadata(&path)?.len()); 93 | println!( 94 | "{} {}rkyv witness({}) saved to {}", 95 | style(format!("[{}/{}]", steps, total_steps)).bold().dim(), 96 | Emoji("🏛 ", ""), 97 | size, 98 | path.display() 99 | ); 100 | } 101 | 102 | println!( 103 | "{} Done in {}", 104 | Emoji("✨ ", ":-)"), 105 | HumanDuration(started.elapsed()) 106 | ); 107 | Ok(()) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /crates/bin/src/commands/witness/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::helpers::run_async; 2 | use clap::Subcommand; 3 | 4 | pub mod dump; 5 | pub mod rkyv_convert; 6 | 7 | #[derive(Debug, Subcommand)] 8 | pub enum WitnessCommands { 9 | #[command(about = "Dump a witness from reth RPC")] 10 | Dump(dump::DumpWitnessCommand), 11 | #[command(about = "Convert a witness json to rkyv")] 12 | Rkyv(rkyv_convert::RkyvConvertCommand), 13 | } 14 | 15 | impl WitnessCommands { 16 | pub fn run(self) -> anyhow::Result<()> { 17 | match self { 18 | WitnessCommands::Dump(cmd) => Ok(run_async(cmd.run())?), 19 | WitnessCommands::Rkyv(cmd) => cmd.run(), 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /crates/bin/src/commands/witness/rkyv_convert.rs: -------------------------------------------------------------------------------- 1 | use clap::Args; 2 | use rkyv::{rancor, vec::ArchivedVec}; 3 | use sbv::primitives::types::{ArchivedBlockWitness, BlockWitness}; 4 | use std::path::PathBuf; 5 | 6 | #[derive(Debug, Args)] 7 | pub struct RkyvConvertCommand { 8 | /// Path to the witness json file 9 | witnesses: Vec, 10 | /// Make chunk 11 | #[arg(long, help = "Make single chunk rkyv instead of multiple blocks")] 12 | chunk: bool, 13 | /// Output directory 14 | #[arg(long)] 15 | out_dir: Option, 16 | } 17 | 18 | impl RkyvConvertCommand { 19 | pub fn run(self) -> anyhow::Result<()> { 20 | if self.witnesses.is_empty() { 21 | anyhow::bail!("No witness files provided"); 22 | } 23 | let mut witnesses = Vec::new(); 24 | for path in self.witnesses.iter() { 25 | let witness = std::fs::File::open(path)?; 26 | let witness: BlockWitness = serde_json::from_reader(witness)?; 27 | witnesses.push(witness); 28 | } 29 | 30 | if self.chunk { 31 | if !witnesses.windows(2).all(|w| w[0].chain_id == w[1].chain_id) { 32 | anyhow::bail!("All witnesses must have the same chain id in chunk mode"); 33 | } 34 | if !witnesses 35 | .windows(2) 36 | .all(|w| w[0].header.number + 1 == w[1].header.number) 37 | { 38 | anyhow::bail!("All witnesses must have sequential block numbers in chunk mode"); 39 | } 40 | 41 | let serialized = rkyv::to_bytes::(&witnesses)?; 42 | let _ = 43 | rkyv::access::, rancor::Error>(&serialized[..])?; 44 | 45 | let start_block_number = witnesses[0].header.number; 46 | let chunk_size = witnesses.len(); 47 | let filename = format!("chunk-{}-{}.rkyv", start_block_number, chunk_size); 48 | let path = if let Some(ref out_dir) = self.out_dir { 49 | out_dir 50 | } else { 51 | self.witnesses[0].parent().unwrap() 52 | }; 53 | let rkyv_path = path.join(filename); 54 | std::fs::write(&rkyv_path, serialized)?; 55 | eprintln!( 56 | "Converted {} witnesses to chunk {}", 57 | chunk_size, 58 | rkyv_path.display() 59 | ); 60 | } else { 61 | for (witness, path) in witnesses.into_iter().zip(self.witnesses.into_iter()) { 62 | let serialized = rkyv::to_bytes::(&witness)?; 63 | let path = if let Some(ref out_dir) = self.out_dir { 64 | out_dir.join(path.file_name().unwrap()) 65 | } else { 66 | path 67 | }; 68 | let rkyv_path = path.with_extension("rkyv"); 69 | std::fs::write(&rkyv_path, serialized)?; 70 | eprintln!("Converted {} to {}", path.display(), rkyv_path.display()); 71 | } 72 | } 73 | Ok(()) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /crates/bin/src/helpers/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::helpers::{retry::RateLimitRetryPolicy, tower::ConcurrencyLimitLayer}; 2 | use alloy::{ 3 | providers::{ProviderBuilder, RootProvider}, 4 | rpc::client::ClientBuilder, 5 | transports::layers::RetryBackoffLayer, 6 | }; 7 | use clap::Args; 8 | use sbv::primitives::types::Network; 9 | use std::future::Future; 10 | use url::Url; 11 | 12 | pub mod retry; 13 | /// Helper functions for the tower 14 | pub mod tower; 15 | pub mod verifier; 16 | 17 | #[cfg(feature = "scroll")] 18 | const MAINNET_RPC: &str = "https://euclid-l2-mpt.scroll.systems"; 19 | #[cfg(feature = "scroll")] 20 | const SEPOLIA_RPC: &str = "https://sepolia-rpc.scroll.io"; 21 | const LOCAL_RPC: &str = "http://localhost:8545"; 22 | 23 | #[derive(Debug, Args)] 24 | pub struct RpcArgs { 25 | #[arg(long, help = "URL to the RPC server, defaults to localhost:8545")] 26 | pub rpc: Option, 27 | 28 | #[cfg_attr( 29 | feature = "scroll", 30 | arg( 31 | long, 32 | help = "using mainnet default rpc url: https://euclid-l2-mpt.scroll.systems" 33 | ) 34 | )] 35 | pub mainnet: bool, 36 | #[cfg_attr( 37 | feature = "scroll", 38 | arg( 39 | long, 40 | help = "using sepolia default rpc url: https://sepolia-rpc.scroll.io" 41 | ) 42 | )] 43 | pub sepolia: bool, 44 | 45 | // Concurrency Limit 46 | #[arg( 47 | long, 48 | help = "Concurrency Limit: maximum number of concurrent requests", 49 | default_value = "10" 50 | )] 51 | pub max_concurrency: usize, 52 | 53 | // Retry parameters 54 | #[arg( 55 | long, 56 | help = "Retry Backoff: maximum number of retries", 57 | default_value = "10" 58 | )] 59 | pub max_retry: u32, 60 | #[arg( 61 | long, 62 | help = "Retry Backoff: backoff duration in milliseconds", 63 | default_value = "100" 64 | )] 65 | pub backoff: u64, 66 | #[arg( 67 | long, 68 | help = "Retry Backoff: compute units per second", 69 | default_value = "100" 70 | )] 71 | pub cups: u64, 72 | } 73 | 74 | impl RpcArgs { 75 | /// Construct a provider from the rpc arguments 76 | pub fn into_provider(self) -> RootProvider { 77 | #[cfg(feature = "scroll")] 78 | let rpc = self.rpc.unwrap_or_else(|| { 79 | if self.mainnet { 80 | MAINNET_RPC.parse().unwrap() 81 | } else if self.sepolia { 82 | SEPOLIA_RPC.parse().unwrap() 83 | } else { 84 | LOCAL_RPC.parse().unwrap() 85 | } 86 | }); 87 | #[cfg(not(feature = "scroll"))] 88 | let rpc = self.rpc.unwrap_or_else(|| LOCAL_RPC.parse().unwrap()); 89 | dev_info!("Using RPC: {}", rpc); 90 | let retry_layer = RetryBackoffLayer::new_with_policy( 91 | self.max_retry, 92 | self.backoff, 93 | self.cups, 94 | RateLimitRetryPolicy, 95 | ); 96 | let limit_layer = ConcurrencyLimitLayer::new(self.max_concurrency); 97 | let client = ClientBuilder::default() 98 | .layer(limit_layer) 99 | .layer(retry_layer) 100 | .http(rpc); 101 | 102 | ProviderBuilder::<_, _, Network>::default().on_client(client) 103 | } 104 | } 105 | 106 | /// defer run in async 107 | pub fn run_async(future: F) -> F::Output { 108 | let rt = tokio::runtime::Builder::new_multi_thread() 109 | .enable_all() 110 | .build() 111 | .expect("tokio runtime"); 112 | rt.block_on(future) 113 | } 114 | -------------------------------------------------------------------------------- /crates/bin/src/helpers/retry.rs: -------------------------------------------------------------------------------- 1 | use alloy::transports::{TransportError, layers}; 2 | use std::time::Duration; 3 | 4 | #[derive(Debug, Copy, Clone)] 5 | pub struct RateLimitRetryPolicy; 6 | 7 | impl layers::RetryPolicy for RateLimitRetryPolicy { 8 | fn should_retry(&self, _error: &TransportError) -> bool { 9 | true 10 | } 11 | 12 | fn backoff_hint(&self, _error: &TransportError) -> Option { 13 | None 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /crates/bin/src/helpers/tower.rs: -------------------------------------------------------------------------------- 1 | use alloy::{ 2 | rpc::json_rpc::{RequestPacket, ResponsePacket}, 3 | transports::{TransportError, TransportFut}, 4 | }; 5 | use tower::{Layer, Service}; 6 | 7 | /// Enforces a limit on the concurrent number of requests the underlying 8 | /// service can handle. 9 | #[derive(Debug, Clone)] 10 | pub struct ConcurrencyLimitLayer { 11 | max: usize, 12 | } 13 | 14 | impl ConcurrencyLimitLayer { 15 | /// Create a new concurrency limit layer. 16 | pub const fn new(max: usize) -> Self { 17 | Self { max } 18 | } 19 | } 20 | 21 | impl Layer for ConcurrencyLimitLayer { 22 | type Service = ConcurrencyLimit; 23 | 24 | fn layer(&self, inner: S) -> Self::Service { 25 | ConcurrencyLimit::new(inner, self.max) 26 | } 27 | } 28 | 29 | /// Enforces a limit on the concurrent number of requests the underlying 30 | /// service can handle. 31 | #[derive(Debug, Clone)] 32 | pub struct ConcurrencyLimit { 33 | inner: tower::limit::ConcurrencyLimit, 34 | } 35 | 36 | impl ConcurrencyLimit { 37 | pub fn new(inner: S, max: usize) -> Self { 38 | ConcurrencyLimit { 39 | inner: tower::limit::ConcurrencyLimit::new(inner, max), 40 | } 41 | } 42 | } 43 | 44 | impl Service for ConcurrencyLimit 45 | where 46 | S: Service, Error = TransportError> 47 | + Send 48 | + 'static 49 | + Clone, 50 | { 51 | type Response = ResponsePacket; 52 | type Error = TransportError; 53 | type Future = TransportFut<'static>; 54 | 55 | fn poll_ready( 56 | &mut self, 57 | cx: &mut std::task::Context<'_>, 58 | ) -> std::task::Poll> { 59 | self.inner.poll_ready(cx) 60 | } 61 | 62 | fn call(&mut self, request: RequestPacket) -> Self::Future { 63 | Box::pin(self.inner.call(request)) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /crates/bin/src/helpers/verifier.rs: -------------------------------------------------------------------------------- 1 | use anyhow::anyhow; 2 | #[cfg(feature = "dev")] 3 | use sbv::helpers::tracing; 4 | use sbv::{ 5 | core::{EvmDatabase, EvmExecutor, VerificationError}, 6 | kv::nohash::NoHashMap, 7 | primitives::{ 8 | chainspec::{Chain, get_chain_spec_or_build}, 9 | ext::BlockWitnessExt, 10 | types::reth::BlockWitnessRethExt, 11 | }, 12 | trie::BlockWitnessTrieExt, 13 | }; 14 | use std::panic::{UnwindSafe, catch_unwind}; 15 | 16 | #[cfg_attr(feature = "dev", tracing::instrument(skip_all, fields(block_number = %witness.number()), err))] 17 | pub fn verify_catch_panics< 18 | T: BlockWitnessRethExt + BlockWitnessTrieExt + BlockWitnessExt + UnwindSafe, 19 | >( 20 | witness: T, 21 | ) -> anyhow::Result { 22 | catch_unwind(|| verify(witness)) 23 | .map_err(|e| { 24 | e.downcast_ref::<&str>() 25 | .map(|s| anyhow!("task panics with: {s}")) 26 | .or_else(|| { 27 | e.downcast_ref::() 28 | .map(|s| anyhow!("task panics with: {s}")) 29 | }) 30 | .unwrap_or_else(|| anyhow!("task panics")) 31 | }) 32 | .and_then(|r| r.map_err(anyhow::Error::from)) 33 | } 34 | 35 | #[cfg_attr(feature = "dev", tracing::instrument(skip_all, fields(block_number = %witness.number()), err))] 36 | pub fn verify( 37 | witness: T, 38 | ) -> Result { 39 | measure_duration_millis!( 40 | total_block_verification_duration_milliseconds, 41 | verify_inner(witness) 42 | ) 43 | } 44 | 45 | fn verify_inner( 46 | witness: T, 47 | ) -> Result { 48 | dev_trace!("{witness:#?}"); 49 | 50 | #[cfg(feature = "profiling")] 51 | let guard = pprof::ProfilerGuardBuilder::default() 52 | .frequency(1000) 53 | .blocklist(&["libc", "libgcc", "pthread", "vdso"]) 54 | .build() 55 | .unwrap(); 56 | 57 | let chain_spec = get_chain_spec_or_build(Chain::from_id(witness.chain_id()), |_spec| { 58 | #[cfg(feature = "scroll")] 59 | { 60 | use sbv::primitives::hardforks::{ForkCondition, ScrollHardfork}; 61 | _spec 62 | .inner 63 | .hardforks 64 | .insert(ScrollHardfork::EuclidV2, ForkCondition::Timestamp(0)); 65 | } 66 | }); 67 | 68 | let mut code_db = NoHashMap::default(); 69 | witness.import_codes(&mut code_db); 70 | let mut nodes_provider = NoHashMap::default(); 71 | witness.import_nodes(&mut nodes_provider).unwrap(); 72 | #[cfg(not(feature = "scroll"))] 73 | let block_hashes = { 74 | let mut block_hashes = NoHashMap::default(); 75 | witness.import_block_hashes(&mut block_hashes); 76 | block_hashes 77 | }; 78 | #[cfg(feature = "scroll")] 79 | let block_hashes = &sbv::kv::null::NullProvider; 80 | let mut db = EvmDatabase::new_from_root( 81 | code_db, 82 | witness.pre_state_root(), 83 | &nodes_provider, 84 | &block_hashes, 85 | )?; 86 | 87 | let block = witness.build_reth_block()?; 88 | 89 | let output = EvmExecutor::new(chain_spec, &db, &block) 90 | .execute() 91 | .inspect_err(|_e| { 92 | dev_error!( 93 | "Error occurs when executing block #{}: {_e:?}", 94 | block.number 95 | ); 96 | 97 | update_metrics_counter!(verification_error); 98 | })?; 99 | 100 | db.update(&nodes_provider, output.state.state.iter())?; 101 | let post_state_root = db.commit_changes(); 102 | 103 | #[cfg(feature = "profiling")] 104 | if let Ok(report) = guard.report().build() { 105 | let dir = std::env::temp_dir() 106 | .join(env!("CARGO_PKG_NAME")) 107 | .join("profiling"); 108 | std::fs::create_dir_all(&dir).unwrap(); 109 | let path = dir.join(format!("block-{}.svg", block.number)); 110 | let file = std::fs::File::create(&path).unwrap(); 111 | report.flamegraph(file).unwrap(); 112 | dev_info!("Profiling report saved to: {:?}", path); 113 | } 114 | 115 | if block.state_root != post_state_root { 116 | dev_error!( 117 | "Block #{} root mismatch: root after in trace = {:x}, root after in reth = {:x}", 118 | block.number, 119 | block.state_root, 120 | post_state_root 121 | ); 122 | 123 | update_metrics_counter!(verification_error); 124 | 125 | return Err(VerificationError::root_mismatch( 126 | block.state_root, 127 | post_state_root, 128 | )); 129 | } 130 | dev_info!("Block #{} verified successfully", block.number); 131 | 132 | Ok(output.gas_used) 133 | } 134 | -------------------------------------------------------------------------------- /crates/bin/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Stateless Block Verifier 2 | 3 | #[macro_use] 4 | extern crate sbv; 5 | 6 | use clap::Parser; 7 | 8 | #[cfg(feature = "dev")] 9 | use tracing_subscriber::EnvFilter; 10 | 11 | mod commands; 12 | mod helpers; 13 | 14 | #[derive(Parser)] 15 | #[command(version, about = "Stateless Block Verifier")] 16 | struct Cli { 17 | #[command(subcommand)] 18 | commands: commands::Commands, 19 | /// Start metrics server 20 | #[cfg(feature = "metrics")] 21 | #[arg(long)] 22 | metrics: bool, 23 | /// Metrics server address 24 | #[cfg(feature = "metrics")] 25 | #[arg(long, default_value = "127.0.0.1:9090")] 26 | metrics_addr: std::net::SocketAddr, 27 | } 28 | 29 | fn main() -> anyhow::Result<()> { 30 | // Install the tracing subscriber that will listen for events and filters. We try to use the 31 | // `RUST_LOG` environment variable and default to RUST_LOG=info if unset. 32 | #[cfg(feature = "dev")] 33 | tracing_subscriber::fmt() 34 | .with_env_filter( 35 | EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")), 36 | ) 37 | .init(); 38 | 39 | let cmd = Cli::parse(); 40 | 41 | #[cfg(feature = "metrics")] 42 | if cmd.metrics { 43 | sbv::helpers::metrics::start_metrics_server(cmd.metrics_addr); 44 | } 45 | 46 | cmd.commands.run()?; 47 | 48 | Ok(()) 49 | } 50 | -------------------------------------------------------------------------------- /crates/core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sbv-core" 3 | homepage = "https://github.com/scroll-tech/stateless-block-verifier/tree/master/crates/core" 4 | 5 | version.workspace = true 6 | edition.workspace = true 7 | rust-version.workspace = true 8 | authors.workspace = true 9 | license.workspace = true 10 | repository.workspace = true 11 | 12 | [lints] 13 | workspace = true 14 | 15 | [dependencies] 16 | reth-evm.workspace = true 17 | reth-evm-ethereum.workspace = true 18 | reth-execution-types.workspace = true 19 | reth-scroll-evm = { workspace = true, optional = true } 20 | reth-storage-errors.workspace = true 21 | 22 | thiserror.workspace = true 23 | 24 | sbv-primitives = { workspace = true, features = ["ethereum-all", "rkyv", "serde"] } 25 | sbv-helpers.workspace = true 26 | sbv-kv.workspace = true 27 | sbv-trie.workspace = true 28 | 29 | [dev-dependencies] 30 | ctor.workspace = true 31 | tracing.workspace = true 32 | tracing-subscriber.workspace = true 33 | 34 | [features] 35 | scroll = [ 36 | "dep:reth-scroll-evm", 37 | "reth-evm/scroll", 38 | "reth-evm-ethereum/scroll", 39 | "reth-execution-types/scroll", 40 | "reth-scroll-evm/scroll", 41 | "sbv-primitives/scroll-all", 42 | ] 43 | debug-account = ["sbv-helpers/debug-account"] 44 | debug-storage = ["sbv-helpers/debug-storage"] 45 | dev = ["sbv-primitives/dev", "sbv-helpers/dev", "sbv-trie/dev"] 46 | metrics = ["sbv-helpers/metrics"] 47 | 48 | # sp1 related 49 | sp1 = ["sbv-trie/sp1"] 50 | cycle-tracker = ["sbv-trie/cycle-tracker"] 51 | 52 | openvm = ["sbv-primitives/openvm"] 53 | -------------------------------------------------------------------------------- /crates/core/src/database.rs: -------------------------------------------------------------------------------- 1 | use sbv_kv::{HashMap, KeyValueStoreGet}; 2 | use sbv_primitives::{ 3 | Address, B256, Bytes, U256, 4 | types::{ 5 | AccountInfo, Bytecode, 6 | revm::{db::BundleAccount, interpreter::analysis::to_analysed}, 7 | }, 8 | }; 9 | use sbv_trie::{PartialStateTrie, TrieNode}; 10 | use std::{cell::RefCell, fmt}; 11 | 12 | pub use sbv_primitives::types::revm::db::DatabaseRef; 13 | 14 | /// A database that consists of account and storage information. 15 | pub struct EvmDatabase { 16 | /// Map of code hash to bytecode. 17 | pub(crate) code_db: CodeDb, 18 | /// Cache of analyzed code 19 | analyzed_code_cache: RefCell>>, 20 | /// Provider of trie nodes 21 | pub(crate) nodes_provider: NodesProvider, 22 | /// Provider of block hashes 23 | block_hashes: BlockHashProvider, 24 | /// partial merkle patricia trie 25 | pub(crate) state: PartialStateTrie, 26 | } 27 | 28 | /// Database error. 29 | #[derive(Debug, thiserror::Error)] 30 | pub enum DatabaseError { 31 | /// Missing L2 message queue witness 32 | #[cfg(feature = "scroll")] 33 | #[error("missing L2 message queue witness")] 34 | MissingL2MessageQueueWitness, 35 | /// Partial state trie error 36 | #[error(transparent)] 37 | PartialStateTrie(#[from] sbv_trie::PartialStateTrieError), 38 | /// Requested code not loaded 39 | #[error("requested code({0}) not loaded")] 40 | CodeNotLoaded(B256), 41 | } 42 | 43 | type Result = std::result::Result; 44 | 45 | impl fmt::Debug 46 | for EvmDatabase 47 | { 48 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 49 | f.debug_struct("EvmDatabase").finish() 50 | } 51 | } 52 | 53 | impl< 54 | CodeDb: KeyValueStoreGet, 55 | NodesProvider: KeyValueStoreGet, 56 | BlockHashProvider: KeyValueStoreGet, 57 | > EvmDatabase 58 | { 59 | /// Initialize an EVM database from a zkTrie root. 60 | pub fn new_from_root( 61 | code_db: CodeDb, 62 | state_root_before: B256, 63 | nodes_provider: NodesProvider, 64 | block_hashes: BlockHashProvider, 65 | ) -> Result { 66 | dev_trace!("open trie from root {:?}", state_root_before); 67 | 68 | let state = cycle_track!( 69 | PartialStateTrie::open(&nodes_provider, state_root_before), 70 | "PartialStateTrie::open" 71 | )?; 72 | 73 | Ok(EvmDatabase { 74 | code_db, 75 | analyzed_code_cache: Default::default(), 76 | nodes_provider, 77 | block_hashes, 78 | state, 79 | }) 80 | } 81 | 82 | /// Update changes to the database. 83 | pub fn update<'a, P: KeyValueStoreGet + Copy>( 84 | &mut self, 85 | nodes_provider: P, 86 | post_state: impl IntoIterator, 87 | ) -> Result<()> { 88 | self.state.update(nodes_provider, post_state)?; 89 | Ok(()) 90 | } 91 | 92 | /// Commit changes and return the new state root. 93 | pub fn commit_changes(&mut self) -> B256 { 94 | self.state.commit_state() 95 | } 96 | 97 | /// Get the withdrawal trie root of scroll. 98 | /// 99 | /// Note: this should not be confused with the withdrawal of the beacon chain. 100 | #[cfg(feature = "scroll")] 101 | pub fn withdraw_root(&self) -> Result { 102 | use sbv_primitives::predeployed::message_queue; 103 | self.basic_ref(message_queue::ADDRESS)? 104 | .ok_or(DatabaseError::MissingL2MessageQueueWitness)?; 105 | let withdraw_root = self.storage_ref( 106 | message_queue::ADDRESS, 107 | message_queue::WITHDRAW_TRIE_ROOT_SLOT, 108 | )?; 109 | Ok(withdraw_root.into()) 110 | } 111 | 112 | fn load_code(&self, hash: B256) -> Option { 113 | let mut code_cache = self.analyzed_code_cache.borrow_mut(); 114 | if let Some(code) = code_cache.get(&hash) { 115 | code.clone() 116 | } else { 117 | let code = self 118 | .code_db 119 | .get(&hash) 120 | .cloned() 121 | .map(Bytecode::new_raw) 122 | .map(to_analysed); 123 | code_cache.insert(hash, code.clone()); 124 | code 125 | } 126 | } 127 | } 128 | 129 | impl< 130 | CodeDb: KeyValueStoreGet, 131 | NodesProvider: KeyValueStoreGet, 132 | BlockHashProvider: KeyValueStoreGet, 133 | > DatabaseRef for EvmDatabase 134 | { 135 | type Error = DatabaseError; 136 | 137 | /// Get basic account information. 138 | fn basic_ref(&self, address: Address) -> Result, Self::Error> { 139 | let Some(account) = self.state.get_account(address) else { 140 | return Ok(None); 141 | }; 142 | dev_trace!("load trie account of {address:?}: {account:?}"); 143 | let code = self.load_code(account.code_hash); 144 | let info = AccountInfo { 145 | balance: account.balance, 146 | nonce: account.nonce, 147 | #[cfg(feature = "scroll")] 148 | code_size: code.as_ref().map(|c| c.len()).unwrap_or(0), // FIXME: this should be remove 149 | code_hash: account.code_hash, 150 | code, 151 | }; 152 | 153 | #[cfg(debug_assertions)] 154 | if let Some(ref code) = info.code { 155 | assert_eq!( 156 | info.code_hash, 157 | code.hash_slow(), 158 | "code hash mismatch for account {address:?}", 159 | ); 160 | } 161 | 162 | Ok(Some(info)) 163 | } 164 | 165 | /// Get account code by its code hash. 166 | fn code_by_hash_ref(&self, hash: B256) -> Result { 167 | self.load_code(hash) 168 | .ok_or(DatabaseError::CodeNotLoaded(hash)) 169 | } 170 | 171 | /// Get storage value of address at index. 172 | fn storage_ref(&self, address: Address, index: U256) -> Result { 173 | dev_trace!("get storage of {:?} at index {:?}", address, index); 174 | Ok(self 175 | .state 176 | .get_storage(&self.nodes_provider, address, index)? 177 | .unwrap_or(U256::ZERO)) 178 | } 179 | 180 | /// Get block hash by block number. 181 | fn block_hash_ref(&self, number: u64) -> Result { 182 | Ok(*self 183 | .block_hashes 184 | .get(&number) 185 | .unwrap_or_else(|| panic!("block hash of number {number} not found"))) 186 | } 187 | } 188 | 189 | impl From for reth_storage_errors::provider::ProviderError { 190 | fn from(e: DatabaseError) -> Self { 191 | reth_storage_errors::provider::ProviderError::TrieWitnessError(e.to_string()) 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /crates/core/src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::database::DatabaseError; 2 | use reth_evm::execute::BlockExecutionError; 3 | use sbv_primitives::{B256, alloy_primitives::SignatureError}; 4 | 5 | /// Error variants encountered during verification of transactions in a L2 block. 6 | #[derive(Debug, thiserror::Error)] 7 | pub enum VerificationError { 8 | /// Error while recovering signer from an ECDSA signature. 9 | #[error("invalid signature: {0}")] 10 | InvalidSignature(#[from] SignatureError), 11 | /// Error encountered from database. 12 | #[error(transparent)] 13 | Database(#[from] DatabaseError), 14 | /// Error encountered from [`revm`]. 15 | #[error(transparent)] 16 | Execution(#[from] BlockExecutionError), 17 | /// Root mismatch error 18 | #[error( 19 | "state root in trace doesn't match with state root executed: expected {expected}, actual {actual}" 20 | )] 21 | RootMismatch { 22 | /// Root after in trace 23 | expected: B256, 24 | /// Root after in revm 25 | actual: B256, 26 | }, 27 | } 28 | 29 | impl VerificationError { 30 | /// Create a new [`VerificationError::RootMismatch`] variant. 31 | #[inline] 32 | pub fn root_mismatch(expected: B256, actual: B256) -> Self { 33 | VerificationError::RootMismatch { expected, actual } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /crates/core/src/executor.rs: -------------------------------------------------------------------------------- 1 | use crate::{database::EvmDatabase, error::VerificationError}; 2 | use reth_evm::execute::{BlockExecutorProvider, Executor}; 3 | use reth_execution_types::BlockExecutionOutput; 4 | use sbv_kv::KeyValueStoreGet; 5 | use sbv_primitives::{ 6 | B256, Bytes, 7 | chainspec::ChainSpec, 8 | types::{ 9 | reth::{Block, Receipt, RecoveredBlock}, 10 | revm::db::CacheDB, 11 | }, 12 | }; 13 | use sbv_trie::TrieNode; 14 | use std::{fmt::Debug, sync::Arc}; 15 | 16 | #[cfg(not(feature = "scroll"))] 17 | use reth_evm_ethereum::execute::EthExecutorProvider as ExecutorProvider; 18 | #[cfg(feature = "scroll")] 19 | use reth_scroll_evm::ScrollExecutorProvider as ExecutorProvider; 20 | 21 | /// EVM executor that handles the block. 22 | #[derive(Debug)] 23 | pub struct EvmExecutor<'a, CodeDb, NodesProvider, BlockHashProvider> { 24 | chain_spec: Arc, 25 | db: &'a EvmDatabase, 26 | block: &'a RecoveredBlock, 27 | } 28 | 29 | impl<'a, CodeDb, NodesProvider, BlockHashProvider> 30 | EvmExecutor<'a, CodeDb, NodesProvider, BlockHashProvider> 31 | { 32 | /// Create a new EVM executor 33 | pub fn new( 34 | chain_spec: Arc, 35 | db: &'a EvmDatabase, 36 | block: &'a RecoveredBlock, 37 | ) -> Self { 38 | Self { 39 | chain_spec, 40 | db, 41 | block, 42 | } 43 | } 44 | } 45 | 46 | impl< 47 | CodeDb: KeyValueStoreGet, 48 | NodesProvider: KeyValueStoreGet, 49 | BlockHashProvider: KeyValueStoreGet, 50 | > EvmExecutor<'_, CodeDb, NodesProvider, BlockHashProvider> 51 | { 52 | /// Handle the block with the given witness 53 | pub fn execute(self) -> Result, VerificationError> { 54 | #[cfg(not(feature = "scroll"))] 55 | let provider = ExecutorProvider::ethereum(self.chain_spec.clone()); 56 | #[cfg(feature = "scroll")] 57 | let provider = ExecutorProvider::scroll(self.chain_spec.clone()); 58 | 59 | #[allow(clippy::let_and_return)] 60 | let output = measure_duration_millis!( 61 | handle_block_duration_milliseconds, 62 | cycle_track!( 63 | provider.executor(CacheDB::new(self.db)).execute(self.block), 64 | "handle_block" 65 | ) 66 | )?; 67 | 68 | #[cfg(feature = "metrics")] 69 | sbv_helpers::metrics::REGISTRY.block_counter.inc(); 70 | 71 | Ok(output) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /crates/core/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Stateless Block Verifier core library. 2 | 3 | #[macro_use] 4 | extern crate sbv_helpers; 5 | extern crate core; 6 | 7 | mod database; 8 | pub use database::{DatabaseError, DatabaseRef, EvmDatabase}; 9 | 10 | mod error; 11 | pub use error::VerificationError; 12 | 13 | mod executor; 14 | pub use executor::EvmExecutor; 15 | 16 | #[cfg(test)] 17 | #[ctor::ctor] 18 | fn init() { 19 | use tracing_subscriber::EnvFilter; 20 | tracing_subscriber::fmt() 21 | .with_env_filter( 22 | EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")), 23 | ) 24 | .init(); 25 | } 26 | -------------------------------------------------------------------------------- /crates/helpers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sbv-helpers" 3 | homepage = "https://github.com/scroll-tech/stateless-block-verifier/tree/master/crates/utils" 4 | 5 | version.workspace = true 6 | edition.workspace = true 7 | rust-version.workspace = true 8 | authors.workspace = true 9 | license.workspace = true 10 | repository.workspace = true 11 | 12 | [lints] 13 | workspace = true 14 | 15 | [dependencies] 16 | revm.workspace = true 17 | 18 | csv = { workspace = true, optional = true } 19 | hyper = { workspace = true, optional = true } 20 | hyper-util = { workspace = true, optional = true } 21 | http-body-util = { workspace = true, optional = true } 22 | once_cell = { workspace = true, optional = true } 23 | prometheus-client = { workspace = true, optional = true } 24 | serde = { workspace = true, optional = true } 25 | tokio = { workspace = true, optional = true } 26 | tracing = { workspace = true, optional = true } 27 | 28 | [dev-dependencies] 29 | rand.workspace = true 30 | reqwest.workspace = true 31 | 32 | [features] 33 | debug-account = ["dep:csv", "dep:serde", "revm/serde"] 34 | debug-storage = ["dep:csv", "dep:serde", "revm/serde"] 35 | dev = ["dep:tracing"] 36 | metrics = ["dep:hyper", "dep:hyper-util", "dep:http-body-util", "dep:once_cell", "dep:prometheus-client", "dep:tokio", "tokio/macros", "tokio/signal"] 37 | 38 | openvm = ["revm/openvm"] 39 | -------------------------------------------------------------------------------- /crates/helpers/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Stateless Block Verifier utils library. 2 | 3 | #[cfg(any(feature = "dev", test))] 4 | pub use tracing; 5 | 6 | #[macro_use] 7 | mod macros; 8 | 9 | mod utils; 10 | #[cfg(any(feature = "debug-account", feature = "debug-storage"))] 11 | pub use utils::debug::DebugRecorder; 12 | 13 | /// Metrics module 14 | #[cfg(feature = "metrics")] 15 | #[doc(hidden)] 16 | pub mod metrics; 17 | -------------------------------------------------------------------------------- /crates/helpers/src/macros.rs: -------------------------------------------------------------------------------- 1 | /// This macro is used to notify sp1 cycle tracker that a new routine has started. 2 | #[macro_export] 3 | macro_rules! cycle_track { 4 | ($e:expr, $($arg:tt)*) => { 5 | { 6 | #[cfg(all(feature = "sp1", feature = "cycle-tracker"))] 7 | println!("cycle-tracker-start: {}", format!($($arg)*)); 8 | 9 | #[allow(clippy::let_and_return)] 10 | let __cycle_track_result = $e; 11 | 12 | #[cfg(all(feature = "sp1", feature = "cycle-tracker"))] 13 | println!("cycle-tracker-end: {}", format!($($arg)*)); 14 | 15 | __cycle_track_result 16 | } 17 | }; 18 | } 19 | 20 | /// This macro is used to notify sp1 cycle tracker that a new routine has started. 21 | #[macro_export] 22 | macro_rules! cycle_tracker_start { 23 | ($($arg:tt)*) => { 24 | #[cfg(all(feature = "sp1", feature = "cycle-tracker"))] 25 | println!("cycle-tracker-start: {}", format!($($arg)*)); 26 | }; 27 | } 28 | 29 | /// This macro is used to notify sp1 cycle tracker that a routine has ended. 30 | #[macro_export] 31 | macro_rules! cycle_tracker_end { 32 | ($($arg:tt)*) => { 33 | #[cfg(all(feature = "sp1", feature = "cycle-tracker"))] 34 | println!("cycle-tracker-end: {}", format!($($arg)*)); 35 | }; 36 | } 37 | 38 | /// This macro is for logging level trace 39 | #[macro_export] 40 | macro_rules! dev_trace { 41 | ($($arg:tt)*) => { 42 | { 43 | #[cfg(any(feature = "dev", test))] 44 | $crate::tracing::trace!($($arg)*); 45 | } 46 | }; 47 | } 48 | 49 | /// This macro is for logging level info 50 | #[macro_export] 51 | macro_rules! dev_info { 52 | ($($arg:tt)*) => { 53 | { 54 | #[cfg(any(feature = "dev", test))] 55 | $crate::tracing::info!($($arg)*); 56 | } 57 | }; 58 | } 59 | 60 | /// This macro is for logging level error 61 | #[macro_export] 62 | macro_rules! dev_error { 63 | ($($arg:tt)*) => { 64 | { 65 | #[cfg(any(feature = "dev", test))] 66 | $crate::tracing::error!($($arg)*); 67 | } 68 | }; 69 | } 70 | 71 | /// This macro is for logging level debug 72 | #[macro_export] 73 | macro_rules! dev_debug { 74 | ($($arg:tt)*) => { 75 | { 76 | #[cfg(any(feature = "dev", test))] 77 | $crate::tracing::debug!($($arg)*); 78 | } 79 | }; 80 | } 81 | 82 | /// This macro is for logging level warn 83 | #[macro_export] 84 | macro_rules! dev_warn { 85 | ($($arg:tt)*) => { 86 | { 87 | #[cfg(any(feature = "dev", test))] 88 | $crate::tracing::warn!($($arg)*); 89 | } 90 | }; 91 | } 92 | 93 | /// This macro is for measuring duration to metrics 94 | #[macro_export] 95 | macro_rules! measure_duration_millis { 96 | ($label:ident, $e:expr) => {{ 97 | #[cfg(feature = "metrics")] 98 | let __measure_duration_histogram_start = std::time::Instant::now(); 99 | 100 | #[allow(clippy::let_and_return)] 101 | let __measure_duration_histogram_result = $e; 102 | 103 | #[cfg(feature = "metrics")] 104 | let __measure_duration_histogram_elasped = __measure_duration_histogram_start.elapsed(); 105 | #[cfg(feature = "metrics")] 106 | let __duration_millis = __measure_duration_histogram_elasped.as_secs() as f64 * 1_000.0 107 | + __measure_duration_histogram_elasped.subsec_nanos() as f64 / 1_000_000.0; 108 | 109 | #[cfg(feature = "metrics")] 110 | $crate::metrics::REGISTRY.$label.observe(__duration_millis); 111 | 112 | #[cfg(feature = "metrics")] 113 | dev_debug!( 114 | "measured duration {} = {:?}ms", 115 | stringify!($label), 116 | __duration_millis, 117 | ); 118 | 119 | __measure_duration_histogram_result 120 | }}; 121 | } 122 | 123 | /// This macro is for measuring duration to metrics 124 | #[macro_export] 125 | macro_rules! measure_duration_micros { 126 | ($label:ident, $e:expr) => {{ 127 | #[cfg(feature = "metrics")] 128 | let __measure_duration_histogram_start = std::time::Instant::now(); 129 | 130 | #[allow(clippy::let_and_return)] 131 | let __measure_duration_histogram_result = $e; 132 | 133 | #[cfg(feature = "metrics")] 134 | $crate::metrics::REGISTRY 135 | .$label 136 | .observe(__measure_duration_histogram_start.elapsed().as_micros() as f64); 137 | 138 | #[cfg(feature = "metrics")] 139 | dev_debug!( 140 | "measured duration {} = {:?}us", 141 | stringify!($label), 142 | __measure_duration_histogram_start.elapsed().as_micros() as f64, 143 | ); 144 | 145 | __measure_duration_histogram_result 146 | }}; 147 | } 148 | 149 | /// This macro is for update gauge to metrics 150 | #[macro_export] 151 | macro_rules! update_metrics_gauge { 152 | ($label:ident, $e:expr) => { 153 | #[cfg(feature = "metrics")] 154 | { 155 | $crate::metrics::REGISTRY.$label.set($e); 156 | } 157 | }; 158 | } 159 | 160 | /// This macro is for update counter to metrics 161 | #[macro_export] 162 | macro_rules! update_metrics_counter { 163 | ($label:ident) => { 164 | #[cfg(feature = "metrics")] 165 | { 166 | $crate::metrics::REGISTRY.$label.inc(); 167 | } 168 | }; 169 | } 170 | -------------------------------------------------------------------------------- /crates/helpers/src/metrics/mod.rs: -------------------------------------------------------------------------------- 1 | use http_body_util::{BodyExt, Full}; 2 | use hyper::{ 3 | Request, Response, 4 | body::{Bytes, Incoming}, 5 | server::conn::http1, 6 | service::service_fn, 7 | }; 8 | use hyper_util::rt::TokioIo; 9 | use once_cell::sync::Lazy; 10 | use prometheus_client::encoding::text::encode; 11 | use std::{io, net::SocketAddr}; 12 | use tokio::{ 13 | net::TcpListener, 14 | pin, 15 | signal::unix::{SignalKind, signal}, 16 | }; 17 | 18 | mod registry; 19 | 20 | /// Global registry for metrics. 21 | pub static REGISTRY: Lazy = Lazy::new(registry::init); 22 | 23 | /// Start a HTTP server to report metrics. 24 | pub fn start_metrics_server(metrics_addr: SocketAddr) { 25 | tokio::spawn(start_metrics_server_inner(metrics_addr)); 26 | } 27 | 28 | async fn start_metrics_server_inner(metrics_addr: SocketAddr) { 29 | dev_info!("Starting metrics server on {metrics_addr}"); 30 | 31 | let tcp_listener = TcpListener::bind(metrics_addr).await.unwrap(); 32 | let server = http1::Builder::new(); 33 | while let Ok((stream, _)) = tcp_listener.accept().await { 34 | let mut shutdown_stream = signal(SignalKind::terminate()).unwrap(); 35 | let io = TokioIo::new(stream); 36 | let _server = server.clone(); 37 | tokio::task::spawn(async move { 38 | let conn = _server.serve_connection( 39 | io, service_fn( 40 | move |_req: Request| { 41 | Box::pin(async move { 42 | let mut buf = String::new(); 43 | encode(&mut buf, ®ISTRY.registry) 44 | .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) 45 | .map(|_| { 46 | let body = Full::new(Bytes::from(buf)).boxed(); 47 | Response::builder() 48 | .header( 49 | hyper::header::CONTENT_TYPE, 50 | "application/openmetrics-text; version=1.0.0; charset=utf-8", 51 | ) 52 | .body(body) 53 | .unwrap() 54 | }) 55 | }) 56 | }) 57 | ); 58 | pin!(conn); 59 | tokio::select! { 60 | _ = conn.as_mut() => {} 61 | _ = shutdown_stream.recv() => { 62 | conn.as_mut().graceful_shutdown(); 63 | } 64 | } 65 | }); 66 | } 67 | } 68 | 69 | #[cfg(test)] 70 | mod tests { 71 | use super::*; 72 | use std::net::{IpAddr, Ipv4Addr}; 73 | 74 | #[tokio::test] 75 | async fn test_start_metrics_server() { 76 | let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), rand::random()); 77 | start_metrics_server(addr); 78 | let client = reqwest::Client::new(); 79 | let res = client.get(format!("http://{addr}")).send().await.unwrap(); 80 | assert_eq!(res.status(), 200); 81 | assert!( 82 | res.headers() 83 | .get(reqwest::header::CONTENT_TYPE) 84 | .unwrap() 85 | .to_str() 86 | .unwrap() 87 | .contains("openmetrics") 88 | ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /crates/helpers/src/metrics/registry.rs: -------------------------------------------------------------------------------- 1 | use prometheus_client::{ 2 | metrics::{ 3 | counter::Counter, 4 | gauge::Gauge, 5 | histogram::{Histogram, linear_buckets}, 6 | }, 7 | registry, 8 | }; 9 | 10 | #[derive(Debug)] 11 | pub struct Registry { 12 | pub registry: registry::Registry, 13 | 14 | pub block_counter: Counter, 15 | pub fetched_rpc_block_height: Gauge, 16 | pub latest_rpc_block_height: Gauge, 17 | 18 | pub verification_error: Counter, 19 | 20 | // database metrics 21 | pub build_zktrie_db_duration_milliseconds: Histogram, 22 | pub update_db_duration_milliseconds: Histogram, 23 | pub zktrie_get_duration_microseconds: Histogram, 24 | pub zktrie_update_duration_microseconds: Histogram, 25 | pub zktrie_commit_duration_microseconds: Histogram, 26 | 27 | // executor metrics 28 | pub transact_commit_duration_milliseconds: Histogram, 29 | pub handle_block_duration_milliseconds: Histogram, 30 | pub commit_changes_duration_milliseconds: Histogram, 31 | pub total_block_verification_duration_milliseconds: Histogram, 32 | } 33 | 34 | pub(super) fn init() -> Registry { 35 | let mut registry = registry::Registry::default(); 36 | 37 | let block_counter = Counter::default(); 38 | registry.register( 39 | "block_counter", 40 | "Number of blocks processed", 41 | block_counter.clone(), 42 | ); 43 | 44 | let fetched_rpc_block_height = Gauge::default(); 45 | registry.register( 46 | "fetched_rpc_block_height", 47 | "Fetched RPC block height", 48 | fetched_rpc_block_height.clone(), 49 | ); 50 | 51 | let latest_rpc_block_height = Gauge::default(); 52 | registry.register( 53 | "latest_rpc_block_height", 54 | "Latest RPC block height", 55 | latest_rpc_block_height.clone(), 56 | ); 57 | 58 | let verification_error = Counter::default(); 59 | registry.register( 60 | "verification_error", 61 | "Number of verification errors", 62 | verification_error.clone(), 63 | ); 64 | 65 | let build_zktrie_db_duration_milliseconds = Histogram::new(linear_buckets(50.0, 50.0, 10)); 66 | registry.register( 67 | "build_zktrie_db_duration", 68 | "Duration of build_zktrie_db_duration in milliseconds", 69 | build_zktrie_db_duration_milliseconds.clone(), 70 | ); 71 | 72 | let update_db_duration_milliseconds = Histogram::new(linear_buckets(2.0, 4.0, 10)); 73 | registry.register( 74 | "update_db_duration", 75 | "Duration of update_db in milliseconds", 76 | update_db_duration_milliseconds.clone(), 77 | ); 78 | 79 | let zktrie_get_duration_microseconds = Histogram::new(linear_buckets(50.0, 500.0, 10)); 80 | registry.register( 81 | "zktrie_get_duration", 82 | "Duration of zktrie_get in microseconds", 83 | zktrie_get_duration_microseconds.clone(), 84 | ); 85 | 86 | let zktrie_update_duration_microseconds = Histogram::new(linear_buckets(50.0, 500.0, 10)); 87 | registry.register( 88 | "zktrie_update_duration", 89 | "Duration of zktrie_update in microseconds", 90 | zktrie_update_duration_microseconds.clone(), 91 | ); 92 | 93 | let zktrie_commit_duration_microseconds = Histogram::new(linear_buckets(100.0, 2000.0, 10)); 94 | registry.register( 95 | "zktrie_commit_duration", 96 | "Duration of zktrie_commit in microseconds", 97 | zktrie_commit_duration_microseconds.clone(), 98 | ); 99 | 100 | let transact_commit_duration_milliseconds = Histogram::new(linear_buckets(0.1, 15.0, 10)); 101 | registry.register( 102 | "transact_commit_duration", 103 | "Duration of transact_commit in milliseconds", 104 | transact_commit_duration_milliseconds.clone(), 105 | ); 106 | 107 | let handle_block_duration_milliseconds = Histogram::new(linear_buckets(1.0, 5.0, 10)); 108 | registry.register( 109 | "handle_block_duration", 110 | "Duration of handle_block in milliseconds", 111 | handle_block_duration_milliseconds.clone(), 112 | ); 113 | 114 | let commit_changes_duration_milliseconds = Histogram::new(linear_buckets(25.0, 50.0, 10)); 115 | registry.register( 116 | "commit_changes_duration", 117 | "Duration of commit_changes in milliseconds", 118 | commit_changes_duration_milliseconds.clone(), 119 | ); 120 | 121 | let total_block_verification_duration_milliseconds = 122 | Histogram::new(linear_buckets(50.0, 50.0, 15)); 123 | registry.register( 124 | "total_block_verification_duration", 125 | "Total block verification duration in milliseconds", 126 | total_block_verification_duration_milliseconds.clone(), 127 | ); 128 | 129 | Registry { 130 | registry, 131 | 132 | block_counter, 133 | fetched_rpc_block_height, 134 | latest_rpc_block_height, 135 | 136 | verification_error, 137 | 138 | build_zktrie_db_duration_milliseconds, 139 | update_db_duration_milliseconds, 140 | zktrie_get_duration_microseconds, 141 | zktrie_update_duration_microseconds, 142 | zktrie_commit_duration_microseconds, 143 | 144 | handle_block_duration_milliseconds, 145 | transact_commit_duration_milliseconds, 146 | commit_changes_duration_milliseconds, 147 | total_block_verification_duration_milliseconds, 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /crates/helpers/src/utils/debug.rs: -------------------------------------------------------------------------------- 1 | use revm::primitives::{AccountInfo, Address, B256, U256, hex}; 2 | use std::{collections::BTreeMap, io::Write, path::PathBuf}; 3 | 4 | #[derive(Debug, serde::Serialize)] 5 | struct StorageOps { 6 | kind: &'static str, 7 | key: U256, 8 | value: Option, 9 | } 10 | 11 | #[derive(Debug, serde::Serialize)] 12 | struct AccountData { 13 | addr: Address, 14 | nonce: u64, 15 | balance: U256, 16 | code_hash: B256, 17 | code_size: u64, 18 | storage_root: B256, 19 | } 20 | 21 | /// Debug recorder for recording account and storage data. 22 | #[derive(Debug)] 23 | pub struct DebugRecorder { 24 | base_dir: PathBuf, 25 | accounts: BTreeMap, 26 | storages_roots: BTreeMap, 27 | storages: BTreeMap>, 28 | codes: BTreeMap>, 29 | } 30 | 31 | impl DebugRecorder { 32 | /// Create a new debug recorder. 33 | pub fn new(prefix: &str, prev_root: B256) -> Self { 34 | let base_dir = PathBuf::from(format!("/tmp/sbv-debug/{prefix}/{prev_root:?}")); 35 | 36 | #[cfg(any(feature = "debug-account", feature = "debug-storage"))] 37 | std::fs::create_dir_all(&base_dir).expect("failed to create debug dir"); 38 | 39 | Self { 40 | base_dir, 41 | accounts: BTreeMap::new(), 42 | storages_roots: BTreeMap::new(), 43 | storages: BTreeMap::new(), 44 | codes: BTreeMap::new(), 45 | } 46 | } 47 | 48 | /// Record the account data. 49 | #[cfg(feature = "debug-account")] 50 | #[allow(clippy::too_many_arguments)] 51 | pub fn record_account(&mut self, addr: Address, info: AccountInfo, storage_root: B256) { 52 | self.accounts.insert(addr, AccountData { 53 | addr, 54 | nonce: info.nonce, 55 | balance: info.balance, 56 | code_hash: info.code_hash, 57 | code_size: info.code_size as u64, 58 | storage_root, 59 | }); 60 | } 61 | 62 | /// Record the storage root of an account. 63 | #[cfg(feature = "debug-storage")] 64 | pub fn record_storage_root(&mut self, addr: Address, storage_root: B256) { 65 | self.storages_roots.insert(addr, storage_root); 66 | } 67 | 68 | /// Record the storage operation. 69 | #[cfg(feature = "debug-storage")] 70 | pub fn record_storage(&mut self, addr: Address, key: U256, value: U256) { 71 | let entry = self.storages.entry(addr).or_default(); 72 | if !value.is_zero() { 73 | entry.insert(key, StorageOps { 74 | kind: "update", 75 | key, 76 | value: Some(value), 77 | }); 78 | } else { 79 | entry.insert(key, StorageOps { 80 | kind: "delete", 81 | key, 82 | value: None, 83 | }); 84 | } 85 | } 86 | 87 | /// Record the code 88 | #[cfg(feature = "debug-account")] 89 | pub fn record_code(&mut self, code_hash: B256, code: &[u8]) { 90 | self.codes.insert(code_hash, code.to_owned()); 91 | } 92 | } 93 | 94 | impl Drop for DebugRecorder { 95 | fn drop(&mut self) { 96 | #[cfg(feature = "debug-account")] 97 | { 98 | let output = std::fs::File::create(self.base_dir.join("accounts.csv")) 99 | .expect("failed to create debug file"); 100 | let mut wtr = csv::Writer::from_writer(output); 101 | 102 | for (_, acc) in self.accounts.iter() { 103 | wtr.serialize(acc).expect("failed to write record"); 104 | } 105 | 106 | for (code_hash, code) in self.codes.iter() { 107 | let mut output = 108 | std::fs::File::create(self.base_dir.join(format!("code_{code_hash:?}.txt"))) 109 | .expect("failed to create debug file"); 110 | let code = hex::encode(code); 111 | output 112 | .write_all(code.as_bytes()) 113 | .expect("failed to write code"); 114 | } 115 | } 116 | 117 | #[cfg(feature = "debug-storage")] 118 | { 119 | for (addr, storages) in self.storages.iter() { 120 | let storage_root = self.storages_roots.get(addr).copied().unwrap_or_default(); 121 | let output = std::fs::File::create( 122 | self.base_dir 123 | .join(format!("storage_{addr:?}_{storage_root:?}.csv")), 124 | ) 125 | .expect("failed to create debug file"); 126 | let mut wtr = csv::Writer::from_writer(output); 127 | for ops in storages.values() { 128 | wtr.serialize(ops).expect("failed to write record"); 129 | } 130 | } 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /crates/helpers/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | /// Debugging utilities. 2 | #[cfg(any(feature = "debug-account", feature = "debug-storage"))] 3 | pub mod debug; 4 | -------------------------------------------------------------------------------- /crates/kv/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sbv-kv" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | authors.workspace = true 7 | license.workspace = true 8 | homepage.workspace = true 9 | repository.workspace = true 10 | 11 | [lints] 12 | workspace = true 13 | 14 | [dependencies] 15 | auto_impl.workspace = true 16 | hashbrown = { workspace = true, features = ["rayon"] } # reth use rayon::IntoParallelIterator 17 | rustc-hash.workspace = true -------------------------------------------------------------------------------- /crates/kv/src/imps/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::{KeyValueStore, KeyValueStoreGet, KeyValueStoreInsert}; 2 | use std::{ 3 | borrow::Borrow, 4 | hash::Hash, 5 | mem::ManuallyDrop, 6 | ops::{Deref, DerefMut}, 7 | }; 8 | 9 | pub mod nohash; 10 | pub mod null; 11 | mod std_collections; 12 | 13 | impl> KeyValueStoreGet for ManuallyDrop { 14 | fn get(&self, k: &Q) -> Option<&V> 15 | where 16 | K: Borrow, 17 | Q: Ord + Hash + Eq + ?Sized, 18 | { 19 | self.deref().get(k) 20 | } 21 | } 22 | 23 | impl> KeyValueStoreInsert 24 | for ManuallyDrop 25 | { 26 | fn insert(&mut self, k: K, v: V) { 27 | self.deref_mut().insert(k, v) 28 | } 29 | 30 | fn or_insert_with V>(&mut self, k: K, default: F) { 31 | self.deref_mut().or_insert_with(k, default) 32 | } 33 | } 34 | 35 | impl> KeyValueStore for ManuallyDrop {} 36 | -------------------------------------------------------------------------------- /crates/kv/src/imps/nohash.rs: -------------------------------------------------------------------------------- 1 | //! NoHash is a [`HashMap`] optimized for key already being a hash. 2 | use crate::HashMap; 3 | use std::hash::{BuildHasher, Hasher}; 4 | 5 | /// [`HashMap`] optimized for key already being a hash. 6 | pub type NoHashMap = HashMap; 7 | 8 | /// A build hasher that does not hash anything. 9 | #[derive(Default, Debug, Copy, Clone)] 10 | pub struct NoHashBuildHasher; 11 | 12 | /// A hasher that does not hash anything, truncates input to u64. 13 | /// 14 | /// Expect input to be a fairly random distributed slice greater than 8 bytes, like a code hash. 15 | #[derive(Default, Debug, Copy, Clone)] 16 | pub struct NoHashHasher(u64); 17 | 18 | impl BuildHasher for NoHashBuildHasher { 19 | type Hasher = NoHashHasher; 20 | fn build_hasher(&self) -> Self::Hasher { 21 | NoHashHasher::default() 22 | } 23 | } 24 | 25 | impl Hasher for NoHashHasher { 26 | fn finish(&self) -> u64 { 27 | self.0 28 | } 29 | 30 | fn write(&mut self, bytes: &[u8]) { 31 | // expect the input to be a slice greater than 8 bytes 32 | self.0 = u64::from_le_bytes(bytes[..8].try_into().unwrap()); 33 | } 34 | 35 | fn write_usize(&mut self, _i: usize) { 36 | // ignore length prefix 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /crates/kv/src/imps/null.rs: -------------------------------------------------------------------------------- 1 | //! A null provider that does nothing. 2 | use crate::{KeyValueStoreGet, KeyValueStoreInsert}; 3 | use std::{borrow::Borrow, hash::Hash}; 4 | 5 | /// A null provider that does nothing. 6 | #[derive(Debug, Copy, Clone)] 7 | pub struct NullProvider; 8 | 9 | impl KeyValueStoreGet for NullProvider { 10 | fn get(&self, _k: &Q) -> Option<&V> 11 | where 12 | K: Borrow, 13 | Q: Ord + Hash + Eq + ?Sized, 14 | { 15 | None 16 | } 17 | } 18 | 19 | impl KeyValueStoreInsert for NullProvider { 20 | fn insert(&mut self, _k: K, _v: V) { 21 | // do nothing 22 | } 23 | 24 | fn or_insert_with V>(&mut self, _k: K, _default: F) { 25 | // do nothing 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /crates/kv/src/imps/std_collections.rs: -------------------------------------------------------------------------------- 1 | use crate::{HashMap, KeyValueStore, KeyValueStoreGet, KeyValueStoreInsert}; 2 | use core::hash::{BuildHasher, Hash}; 3 | use std::{borrow::Borrow, collections::BTreeMap}; 4 | 5 | impl KeyValueStoreInsert for HashMap { 6 | fn insert(&mut self, k: K, v: V) { 7 | HashMap::insert(self, k, v); 8 | } 9 | fn or_insert_with V>(&mut self, k: K, default: F) { 10 | HashMap::entry(self, k).or_insert_with(default); 11 | } 12 | } 13 | 14 | impl KeyValueStoreGet for HashMap { 15 | fn get(&self, k: &Q) -> Option<&V> 16 | where 17 | K: Borrow, 18 | Q: Ord + Hash + Eq + ?Sized, 19 | { 20 | HashMap::get(self, k) 21 | } 22 | } 23 | 24 | impl KeyValueStore for HashMap {} 25 | 26 | impl KeyValueStoreInsert for BTreeMap { 27 | fn insert(&mut self, k: K, v: V) { 28 | BTreeMap::insert(self, k, v); 29 | } 30 | fn or_insert_with V>(&mut self, k: K, default: F) { 31 | BTreeMap::entry(self, k).or_insert_with(default); 32 | } 33 | } 34 | 35 | impl KeyValueStoreGet for BTreeMap { 36 | fn get(&self, k: &Q) -> Option<&V> 37 | where 38 | K: Borrow, 39 | Q: Ord + Hash + Eq + ?Sized, 40 | { 41 | BTreeMap::get(self, k) 42 | } 43 | } 44 | 45 | impl KeyValueStore for BTreeMap {} 46 | -------------------------------------------------------------------------------- /crates/kv/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Abstract KV-Store interface. 2 | 3 | use auto_impl::auto_impl; 4 | use std::{borrow::Borrow, hash::Hash}; 5 | 6 | mod imps; 7 | pub use hashbrown; 8 | pub use imps::{nohash, null}; 9 | 10 | /// HashMap 11 | pub type HashMap = hashbrown::HashMap; 12 | /// HashSet 13 | pub type HashSet = hashbrown::HashSet; 14 | 15 | /// Key-Value store insert trait 16 | #[auto_impl(&mut, Box)] 17 | pub trait KeyValueStoreInsert { 18 | /// Insert key-value pair 19 | fn insert(&mut self, k: K, v: V); 20 | /// Insert key-value pair if key does not exist 21 | fn or_insert_with V>(&mut self, k: K, default: F); 22 | } 23 | 24 | /// Key-Value store trait 25 | #[auto_impl(&, &mut, Box, Rc, Arc)] 26 | pub trait KeyValueStoreGet { 27 | /// Get value by key 28 | fn get(&self, k: &Q) -> Option<&V> 29 | where 30 | K: Borrow, 31 | Q: Ord + Hash + Eq + ?Sized; 32 | } 33 | 34 | /// Key-Value store trait 35 | #[auto_impl(&, &mut, Box, Rc, Arc)] 36 | pub trait KeyValueStore: 37 | KeyValueStoreInsert + KeyValueStoreGet 38 | { 39 | } 40 | -------------------------------------------------------------------------------- /crates/primitives/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sbv-primitives" 3 | homepage = "https://github.com/scroll-tech/stateless-block-verifier/tree/master/crates/primitives" 4 | 5 | version.workspace = true 6 | edition.workspace = true 7 | rust-version.workspace = true 8 | authors.workspace = true 9 | license.workspace = true 10 | repository.workspace = true 11 | 12 | [lints] 13 | workspace = true 14 | 15 | [dependencies] 16 | auto_impl.workspace = true 17 | itertools.workspace = true 18 | tiny-keccak.workspace = true 19 | 20 | rkyv = { workspace = true, optional = true } 21 | serde = { workspace = true, optional = true } 22 | 23 | alloy-consensus = { workspace = true, optional = true } 24 | alloy-eips = { workspace = true, optional = true } 25 | alloy-network = { workspace = true, optional = true } 26 | alloy-rpc-types-eth = { workspace = true, optional = true } 27 | alloy-serde = { workspace = true, optional = true } 28 | 29 | alloy-primitives.workspace = true 30 | 31 | revm = { workspace = true, optional = true } 32 | reth-chainspec = { workspace = true, optional = true } 33 | reth-ethereum-forks = { workspace = true, optional = true } 34 | reth-primitives = { workspace = true, optional = true } 35 | reth-primitives-traits = { workspace = true, optional = true } 36 | reth-scroll-chainspec = { workspace = true, optional = true } 37 | reth-scroll-forks = { workspace = true, optional = true } 38 | reth-scroll-primitives = { workspace = true, optional = true } 39 | 40 | scroll-alloy-consensus = { workspace = true, optional = true } 41 | scroll-alloy-rpc-types = { workspace = true, optional = true } 42 | scroll-alloy-network = { workspace = true, optional = true } 43 | 44 | sbv-helpers.workspace = true 45 | sbv-kv.workspace = true 46 | 47 | [dev-dependencies] 48 | 49 | [features] 50 | # serialization 51 | rkyv = [ 52 | "dep:rkyv", 53 | "alloy-primitives/rkyv", 54 | ] 55 | serde = [ 56 | "dep:alloy-serde", 57 | "dep:serde", 58 | "reth-scroll-primitives?/serde", 59 | ] 60 | 61 | # core features switch 62 | chainspec = ["dep:reth-chainspec"] 63 | consensus-types = ["dep:alloy-consensus"] 64 | eips = ["dep:alloy-eips"] 65 | network-types = ["dep:alloy-network"] 66 | hardforks = ["dep:reth-ethereum-forks"] 67 | reth-types = [ 68 | "serde", 69 | "consensus-types", 70 | "eips", 71 | "dep:reth-primitives", 72 | "dep:reth-primitives-traits", 73 | ] 74 | revm-types = ["dep:revm"] 75 | rpc-types = ["dep:alloy-rpc-types-eth", "eips"] 76 | ethereum-all = [ 77 | "chainspec", 78 | "consensus-types", 79 | "eips", 80 | "network-types", 81 | "hardforks", 82 | "reth-types", 83 | "revm-types", 84 | "rpc-types", 85 | ] 86 | 87 | scroll = [] 88 | scroll-chainspec = ["scroll", "chainspec", "dep:reth-scroll-chainspec"] 89 | scroll-consensus-types = [ 90 | "scroll", 91 | "consensus-types", 92 | "dep:scroll-alloy-consensus", 93 | "scroll-alloy-consensus/serde", # This is a bug 94 | "scroll-alloy-consensus/reth-codec", # This is a bug 95 | ] 96 | scroll-hardforks = [ 97 | "scroll", 98 | "hardforks", 99 | "dep:reth-scroll-forks", 100 | "reth-scroll-forks/serde", 101 | ] 102 | scroll-network-types = ["scroll", "network-types", "dep:scroll-alloy-network"] 103 | scroll-pre-deployed = ["scroll"] 104 | scroll-reth-types = [ 105 | "scroll", 106 | "reth-types", 107 | "scroll-revm", # This is a bug 108 | "scroll-consensus-types", 109 | "dep:reth-scroll-primitives", 110 | "reth-scroll-primitives/scroll", 111 | ] 112 | scroll-revm = [ 113 | "scroll", 114 | "revm-types", 115 | "revm/scroll-default-handler", 116 | "revm/optional_no_base_fee", 117 | ] 118 | scroll-rpc-types = ["scroll", "rpc-types", "dep:scroll-alloy-rpc-types"] 119 | scroll-all = [ 120 | "scroll", 121 | "scroll-chainspec", 122 | "scroll-consensus-types", 123 | "scroll-network-types", 124 | "scroll-hardforks", 125 | "scroll-pre-deployed", 126 | "scroll-reth-types", 127 | "scroll-revm", 128 | "scroll-rpc-types", 129 | ] 130 | 131 | dev = ["sbv-helpers/dev"] 132 | 133 | sp1 = [] 134 | cycle-tracker = [] 135 | 136 | openvm = [ 137 | "alloy-primitives/openvm", 138 | "revm/openvm" 139 | ] 140 | -------------------------------------------------------------------------------- /crates/primitives/src/chainspec.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | pub use reth_chainspec::{self, *}; 4 | 5 | #[cfg(feature = "scroll")] 6 | pub use reth_scroll_chainspec as scroll; 7 | #[cfg(feature = "scroll")] 8 | pub use reth_scroll_chainspec::{SCROLL_DEV, SCROLL_MAINNET, SCROLL_SEPOLIA}; 9 | 10 | /// An Ethereum chain specification. 11 | /// 12 | /// A chain specification describes: 13 | /// 14 | /// - Meta-information about the chain (the chain ID) 15 | /// - The genesis block of the chain ([`Genesis`]) 16 | /// - What hardforks are activated, and under which conditions 17 | #[cfg(not(feature = "scroll"))] 18 | pub type ChainSpec = reth_chainspec::ChainSpec; 19 | /// Scroll chain spec type. 20 | #[cfg(feature = "scroll")] 21 | pub type ChainSpec = scroll::ScrollChainSpec; 22 | 23 | /// Get chain spec 24 | #[cfg(not(feature = "scroll"))] 25 | pub fn get_chain_spec(chain: Chain) -> Option> { 26 | if chain == Chain::from_named(NamedChain::Mainnet) { 27 | return Some(MAINNET.clone()); 28 | } 29 | if chain == Chain::from_named(NamedChain::Sepolia) { 30 | return Some(SEPOLIA.clone()); 31 | } 32 | if chain == Chain::from_named(NamedChain::Holesky) { 33 | return Some(HOLESKY.clone()); 34 | } 35 | if chain == Chain::dev() { 36 | return Some(DEV.clone()); 37 | } 38 | None 39 | } 40 | 41 | /// Get chain spec 42 | #[cfg(feature = "scroll")] 43 | pub fn get_chain_spec(chain: Chain) -> Option> { 44 | if chain == Chain::from_named(NamedChain::Scroll) { 45 | return Some(SCROLL_MAINNET.clone()); 46 | } 47 | if chain == Chain::from_named(NamedChain::ScrollSepolia) { 48 | return Some(SCROLL_SEPOLIA.clone()); 49 | } 50 | if chain == Chain::dev() { 51 | return Some(SCROLL_DEV.clone()); 52 | } 53 | None 54 | } 55 | 56 | /// Get chain spec or build one from dev config as blueprint 57 | pub fn get_chain_spec_or_build(chain: Chain, f: F) -> Arc 58 | where 59 | F: Fn(&mut ChainSpec), 60 | { 61 | get_chain_spec(chain).unwrap_or_else(|| { 62 | #[cfg(not(feature = "scroll"))] 63 | let mut spec = { 64 | let mut spec = (**DEV).clone(); 65 | spec.chain = chain; 66 | spec 67 | }; 68 | #[cfg(feature = "scroll")] 69 | let mut spec = { 70 | let mut spec = (**SCROLL_DEV).clone(); 71 | spec.inner.chain = chain; 72 | spec 73 | }; 74 | 75 | f(&mut spec); 76 | Arc::new(spec) 77 | }) 78 | } 79 | 80 | #[cfg(test)] 81 | mod tests { 82 | 83 | #[cfg(feature = "scroll")] 84 | #[test] 85 | fn test_build_chain_spec() { 86 | use super::*; 87 | use crate::hardforks::ScrollHardfork; 88 | 89 | let chain_spec = get_chain_spec_or_build(Chain::from_id(42424242), |spec| { 90 | spec.inner 91 | .hardforks 92 | .insert(ScrollHardfork::DarwinV2, ForkCondition::Block(10)); 93 | }); 94 | assert_eq!(chain_spec.chain, Chain::from_id(42424242)); 95 | assert!(!chain_spec.is_fork_active_at_block(ScrollHardfork::DarwinV2, 0)); 96 | assert!(chain_spec.is_fork_active_at_block(ScrollHardfork::DarwinV2, 10)); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /crates/primitives/src/data/v1_l1_oracle_bytecode.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scroll-tech/stateless-block-verifier/50efdcc3f02afe68151fbe2ffd7b804d6e255d4e/crates/primitives/src/data/v1_l1_oracle_bytecode.bin -------------------------------------------------------------------------------- /crates/primitives/src/data/v1_l1_oracle_bytecode.txt: -------------------------------------------------------------------------------- 1 | 608060405234801561001057600080fd5b50600436106100cf5760003560e01c8063715018a61161008c578063bede39b511610066578063bede39b51461018d578063de26c4a1146101a0578063f2fde38b146101b3578063f45e65d8146101c657600080fd5b8063715018a6146101475780638da5cb5b1461014f57806393e59dc11461017a57600080fd5b80630c18c162146100d45780633577afc5146100f05780633d0f963e1461010557806349948e0e14610118578063519b4bd31461012b5780637046559714610134575b600080fd5b6100dd60025481565b6040519081526020015b60405180910390f35b6101036100fe366004610671565b6101cf565b005b61010361011336600461068a565b610291565b6100dd6101263660046106d0565b61031c565b6100dd60015481565b610103610142366004610671565b610361565b610103610416565b600054610162906001600160a01b031681565b6040516001600160a01b0390911681526020016100e7565b600454610162906001600160a01b031681565b61010361019b366004610671565b61044c565b6100dd6101ae3660046106d0565b610533565b6101036101c136600461068a565b610595565b6100dd60035481565b6000546001600160a01b031633146102025760405162461bcd60e51b81526004016101f990610781565b60405180910390fd5b621c9c388111156102555760405162461bcd60e51b815260206004820152601760248201527f657863656564206d6178696d756d206f7665726865616400000000000000000060448201526064016101f9565b60028190556040518181527f32740b35c0ea213650f60d44366b4fb211c9033b50714e4a1d34e65d5beb9bb4906020015b60405180910390a150565b6000546001600160a01b031633146102bb5760405162461bcd60e51b81526004016101f990610781565b600480546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f22d1c35fe072d2e42c3c8f9bd4a0d34aa84a0101d020a62517b33fdb3174e5f7910160405180910390a15050565b60008061032883610533565b905060006001548261033a91906107b8565b9050633b9aca006003548261034f91906107b8565b61035991906107e5565b949350505050565b6000546001600160a01b0316331461038b5760405162461bcd60e51b81526004016101f990610781565b61039b633b9aca006103e86107b8565b8111156103e15760405162461bcd60e51b8152602060048201526014602482015273657863656564206d6178696d756d207363616c6560601b60448201526064016101f9565b60038190556040518181527f3336cd9708eaf2769a0f0dc0679f30e80f15dcd88d1921b5a16858e8b85c591a90602001610286565b6000546001600160a01b031633146104405760405162461bcd60e51b81526004016101f990610781565b61044a6000610621565b565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa158015610495573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104b99190610807565b6104fe5760405162461bcd60e51b81526020600482015260166024820152752737ba103bb434ba32b634b9ba32b21039b2b73232b960511b60448201526064016101f9565b60018190556040518181527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c4490602001610286565b80516000908190815b818110156105865784818151811061055657610556610829565b01602001516001600160f81b0319166000036105775760048301925061057e565b6010830192505b60010161053c565b50506002540160400192915050565b6000546001600160a01b031633146105bf5760405162461bcd60e51b81526004016101f990610781565b6001600160a01b0381166106155760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f206164647265737300000060448201526064016101f9565b61061e81610621565b50565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006020828403121561068357600080fd5b5035919050565b60006020828403121561069c57600080fd5b81356001600160a01b03811681146106b357600080fd5b9392505050565b634e487b7160e01b600052604160045260246000fd5b6000602082840312156106e257600080fd5b813567ffffffffffffffff808211156106fa57600080fd5b818401915084601f83011261070e57600080fd5b813581811115610720576107206106ba565b604051601f8201601f19908116603f01168101908382118183101715610748576107486106ba565b8160405282815287602084870101111561076157600080fd5b826020860160208301376000928101602001929092525095945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b60008160001904831182151516156107e057634e487b7160e01b600052601160045260246000fd5b500290565b60008261080257634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561081957600080fd5b815180151581146106b357600080fd5b634e487b7160e01b600052603260045260246000fdfea26469706673582212205ea335809638809cf032c794fd966e2439020737b1dcc2218435cb438286efcf64736f6c63430008100033 -------------------------------------------------------------------------------- /crates/primitives/src/data/v2_l1_oracle_bytecode.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scroll-tech/stateless-block-verifier/50efdcc3f02afe68151fbe2ffd7b804d6e255d4e/crates/primitives/src/data/v2_l1_oracle_bytecode.bin -------------------------------------------------------------------------------- /crates/primitives/src/data/v2_l1_oracle_bytecode.txt: -------------------------------------------------------------------------------- 1 | 608060405234801561000f575f80fd5b5060043610610132575f3560e01c8063715018a6116100b4578063a911d77f11610079578063a911d77f1461024c578063bede39b514610254578063de26c4a114610267578063e88a60ad1461027a578063f2fde38b1461028d578063f45e65d8146102a0575f80fd5b8063715018a6146101eb57806384189161146101f35780638da5cb5b146101fc57806393e59dc114610226578063944b247f14610239575f80fd5b80633d0f963e116100fa5780633d0f963e146101a057806349948e0e146101b3578063519b4bd3146101c65780636a5e67e5146101cf57806370465597146101d8575f80fd5b80630c18c1621461013657806313dad5be1461015257806323e524ac1461016f5780633577afc51461017857806339455d3a1461018d575b5f80fd5b61013f60025481565b6040519081526020015b60405180910390f35b60085461015f9060ff1681565b6040519015158152602001610149565b61013f60065481565b61018b6101863660046109b3565b6102a9565b005b61018b61019b3660046109ca565b61033b565b61018b6101ae3660046109ea565b610438565b61013f6101c1366004610a2b565b6104bb565b61013f60015481565b61013f60075481565b61018b6101e63660046109b3565b6104e0565b61018b61056e565b61013f60055481565b5f5461020e906001600160a01b031681565b6040516001600160a01b039091168152602001610149565b60045461020e906001600160a01b031681565b61018b6102473660046109b3565b6105a2565b61018b61062e565b61018b6102623660046109b3565b61068a565b61013f610275366004610a2b565b610747565b61018b6102883660046109b3565b610764565b61018b61029b3660046109ea565b6107f0565b61013f60035481565b5f546001600160a01b031633146102db5760405162461bcd60e51b81526004016102d290610ad6565b60405180910390fd5b621c9c388111156102ff57604051635742c80560e11b815260040160405180910390fd5b60028190556040518181527f32740b35c0ea213650f60d44366b4fb211c9033b50714e4a1d34e65d5beb9bb4906020015b60405180910390a150565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa158015610382573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103a69190610b0d565b6103c3576040516326b3506d60e11b815260040160405180910390fd5b600182905560058190556040518281527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c449060200160405180910390a16040518181527f9a14bfb5d18c4c3cf14cae19c23d7cf1bcede357ea40ca1f75cd49542c71c214906020015b60405180910390a15050565b5f546001600160a01b031633146104615760405162461bcd60e51b81526004016102d290610ad6565b600480546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f22d1c35fe072d2e42c3c8f9bd4a0d34aa84a0101d020a62517b33fdb3174e5f7910161042c565b6008545f9060ff16156104d7576104d18261087b565b92915050565b6104d1826108c1565b5f546001600160a01b031633146105095760405162461bcd60e51b81526004016102d290610ad6565b610519633b9aca006103e8610b40565b81111561053957604051631e44fdeb60e11b815260040160405180910390fd5b60038190556040518181527f3336cd9708eaf2769a0f0dc0679f30e80f15dcd88d1921b5a16858e8b85c591a90602001610330565b5f546001600160a01b031633146105975760405162461bcd60e51b81526004016102d290610ad6565b6105a05f610904565b565b5f546001600160a01b031633146105cb5760405162461bcd60e51b81526004016102d290610ad6565b6105d9633b9aca0080610b40565b8111156105f95760405163874f603160e01b815260040160405180910390fd5b60068190556040518181527f2ab3f5a4ebbcbf3c24f62f5454f52f10e1a8c9dcc5acac8f19199ce881a6a10890602001610330565b5f546001600160a01b031633146106575760405162461bcd60e51b81526004016102d290610ad6565b60085460ff161561067b576040516379f9c57560e01b815260040160405180910390fd5b6008805460ff19166001179055565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa1580156106d1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106f59190610b0d565b610712576040516326b3506d60e11b815260040160405180910390fd5b60018190556040518181527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c4490602001610330565b6008545f9060ff161561075b57505f919050565b6104d182610953565b5f546001600160a01b0316331461078d5760405162461bcd60e51b81526004016102d290610ad6565b61079b633b9aca0080610b40565b8111156107bb5760405163f37ec21560e01b815260040160405180910390fd5b60078190556040518181527f6b332a036d8c3ead57dcb06c87243bd7a2aed015ddf2d0528c2501dae56331aa90602001610330565b5f546001600160a01b031633146108195760405162461bcd60e51b81526004016102d290610ad6565b6001600160a01b03811661086f5760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f206164647265737300000060448201526064016102d2565b61087881610904565b50565b5f633b9aca0060055483516007546108939190610b40565b61089d9190610b40565b6001546006546108ad9190610b40565b6108b79190610b57565b6104d19190610b6a565b5f806108cc83610953565b90505f600154826108dd9190610b40565b9050633b9aca00600354826108f29190610b40565b6108fc9190610b6a565b949350505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80515f908190815b818110156109a45784818151811061097557610975610b89565b01602001516001600160f81b0319165f036109955760048301925061099c565b6010830192505b60010161095b565b50506002540160400192915050565b5f602082840312156109c3575f80fd5b5035919050565b5f80604083850312156109db575f80fd5b50508035926020909101359150565b5f602082840312156109fa575f80fd5b81356001600160a01b0381168114610a10575f80fd5b9392505050565b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215610a3b575f80fd5b813567ffffffffffffffff80821115610a52575f80fd5b818401915084601f830112610a65575f80fd5b813581811115610a7757610a77610a17565b604051601f8201601f19908116603f01168101908382118183101715610a9f57610a9f610a17565b81604052828152876020848701011115610ab7575f80fd5b826020860160208301375f928101602001929092525095945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b5f60208284031215610b1d575f80fd5b81518015158114610a10575f80fd5b634e487b7160e01b5f52601160045260245ffd5b80820281158282048414176104d1576104d1610b2c565b808201808211156104d1576104d1610b2c565b5f82610b8457634e487b7160e01b5f52601260045260245ffd5b500490565b634e487b7160e01b5f52603260045260245ffdfea26469706673582212200c2ac583f18be4f94ab169ae6f2ea3a708a7c0d4424746b120b177adb39e626064736f6c63430008180033 -------------------------------------------------------------------------------- /crates/primitives/src/hardforks.rs: -------------------------------------------------------------------------------- 1 | pub use reth_ethereum_forks::*; 2 | 3 | #[cfg(feature = "scroll-hardforks")] 4 | pub use reth_scroll_forks::{ 5 | DEV_HARDFORKS as SCROLL_DEV_HARDFORKS, ScrollHardfork, ScrollHardforks, 6 | }; 7 | -------------------------------------------------------------------------------- /crates/primitives/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Stateless Block Verifier primitives library. 2 | 3 | use auto_impl::auto_impl; 4 | use std::fmt; 5 | 6 | /// The spec of an Ethereum network 7 | #[cfg(feature = "chainspec")] 8 | pub mod chainspec; 9 | /// Extension Traits 10 | pub mod ext; 11 | /// Ethereum fork types 12 | #[cfg(feature = "hardforks")] 13 | pub mod hardforks; 14 | /// Predeployed contracts 15 | #[cfg(feature = "scroll-pre-deployed")] 16 | pub mod predeployed; 17 | /// Types definition 18 | pub mod types; 19 | 20 | pub use alloy_primitives::{ 21 | self, Address, B64, B256, BlockHash, BlockNumber, Bloom, Bytes, ChainId, PrimitiveSignature, 22 | TxHash, U8, U256, address, b256, keccak256, 23 | }; 24 | 25 | /// BlockWitness trait 26 | #[auto_impl(&, &mut, Box, Rc, Arc)] 27 | pub trait BlockWitness: fmt::Debug { 28 | /// Chain id 29 | fn chain_id(&self) -> ChainId; 30 | /// Block number 31 | fn number(&self) -> BlockNumber; 32 | /// Pre-state root 33 | fn pre_state_root(&self) -> B256; 34 | /// Pre-state root 35 | fn post_state_root(&self) -> B256; 36 | /// Withdrawal root 37 | fn withdrawals_root(&self) -> Option; 38 | /// Number of transactions 39 | fn num_transactions(&self) -> usize; 40 | /// Block hashes 41 | #[must_use] 42 | #[cfg(not(feature = "scroll"))] 43 | fn block_hashes_iter(&self) -> impl ExactSizeIterator; 44 | /// Withdrawals 45 | #[must_use] 46 | fn withdrawals_iter(&self) -> Option>; 47 | /// States 48 | #[must_use] 49 | fn states_iter(&self) -> impl ExactSizeIterator>; 50 | /// Codes 51 | #[must_use] 52 | fn codes_iter(&self) -> impl ExactSizeIterator>; 53 | 54 | // provided methods 55 | 56 | /// Number of states 57 | fn num_states(&self) -> usize { 58 | self.states_iter().len() 59 | } 60 | /// Number of codes 61 | fn num_codes(&self) -> usize { 62 | self.codes_iter().len() 63 | } 64 | } 65 | 66 | /// Withdrawal trait 67 | #[auto_impl(&, &mut, Box, Rc, Arc)] 68 | pub trait Withdrawal: fmt::Debug { 69 | /// Monotonically increasing identifier issued by consensus layer. 70 | fn index(&self) -> u64; 71 | /// Index of validator associated with withdrawal. 72 | fn validator_index(&self) -> u64; 73 | /// Target address for withdrawn ether. 74 | fn address(&self) -> Address; 75 | /// Value of the withdrawal in gwei. 76 | fn amount(&self) -> u64; 77 | } 78 | -------------------------------------------------------------------------------- /crates/primitives/src/predeployed.rs: -------------------------------------------------------------------------------- 1 | /// Pre-deployed L2MessageQueue 2 | pub mod message_queue { 3 | use crate::{Address, U256, address}; 4 | 5 | /// L2MessageQueue pre-deployed address 6 | pub const ADDRESS: Address = address!("5300000000000000000000000000000000000000"); 7 | /// the slot of withdraw root in L2MessageQueue 8 | pub const WITHDRAW_TRIE_ROOT_SLOT: U256 = U256::ZERO; 9 | } 10 | 11 | /// Pre-deployed Gas Price Oracle 12 | pub mod l1_gas_price_oracle { 13 | use crate::{Address, U256, address}; 14 | 15 | /// L1GasPriceOracle pre-deployed address 16 | pub const ADDRESS: Address = address!("5300000000000000000000000000000000000002"); 17 | /// L1 base fee slot in L1GasPriceOracle 18 | pub const BASE_FEE_SLOT: U256 = U256::from_limbs([1, 0, 0, 0]); 19 | 20 | /// The following 2 slots will be depreciated after curie fork 21 | /// L1 overhead slot in L1GasPriceOracle 22 | pub const OVERHEAD_SLOT: U256 = U256::from_limbs([2, 0, 0, 0]); 23 | /// L1 scalar slot in L1GasPriceOracle 24 | pub const SCALAR_SLOT: U256 = U256::from_limbs([3, 0, 0, 0]); 25 | 26 | /// THe following 3 slots plus `BASE_FEE_SLOT` will be used for l1 fee after curie fork 27 | /// L1 BlobBaseFee slot in L1GasPriceOracle after Curie fork 28 | pub const L1_BLOB_BASEFEE_SLOT: U256 = U256::from_limbs([5, 0, 0, 0]); 29 | /// L1 commitScalar slot in L1GasPriceOracle after Curie fork 30 | pub const COMMIT_SCALAR_SLOT: U256 = U256::from_limbs([6, 0, 0, 0]); 31 | /// L1 blob_scalar slot in L1GasPriceOracle after Curie fork 32 | pub const BLOB_SCALAR_SLOT: U256 = U256::from_limbs([7, 0, 0, 0]); 33 | /// L1 isCurie slot in L1GasPriceOracle after Curie fork 34 | pub const IS_CURIE_SLOT: U256 = U256::from_limbs([8, 0, 0, 0]); 35 | /// Initial commit scalar after curie fork 36 | pub const INITIAL_COMMIT_SCALAR: U256 = U256::from_limbs([230759955285, 0, 0, 0]); 37 | /// Initial blob scalar after curie fork 38 | pub const INITIAL_BLOB_SCALAR: U256 = U256::from_limbs([417565260, 0, 0, 0]); 39 | 40 | /// Bytecode before curie hardfork 41 | /// curl 127.0.0.1:8545 -X POST -H "Content-Type: application/json" --data 42 | /// '{"method":"eth_getCode","params":["0x5300000000000000000000000000000000000002","latest"]," 43 | /// id":1,"jsonrpc":"2.0"}' 44 | pub static V1_BYTECODE: &[u8] = include_bytes!("./data/v1_l1_oracle_bytecode.bin"); 45 | /// Bytecode after curie hardfork 46 | /// 47 | pub static V2_BYTECODE: &[u8] = include_bytes!("./data/v2_l1_oracle_bytecode.bin"); 48 | } 49 | -------------------------------------------------------------------------------- /crates/primitives/src/types/access_list.rs: -------------------------------------------------------------------------------- 1 | use crate::{Address, B256}; 2 | 3 | /// A list of addresses and storage keys that the transaction plans to access. 4 | /// Accesses outside the list are possible, but become more expensive. 5 | #[derive(Debug, Clone, Hash, Eq, PartialEq)] 6 | #[cfg_attr( 7 | feature = "rkyv", 8 | derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), 9 | rkyv(derive(Debug, Hash, PartialEq, Eq)) 10 | )] 11 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 12 | pub struct AccessListItem { 13 | /// Account addresses that would be loaded at the start of execution 14 | #[cfg_attr( 15 | feature = "rkyv", 16 | rkyv(attr(doc = "Account addresses that would be loaded at the start of execution")) 17 | )] 18 | pub address: Address, 19 | /// Keys of storage that would be loaded at the start of execution 20 | #[cfg_attr( 21 | feature = "rkyv", 22 | rkyv(attr(doc = "Keys of storage that would be loaded at the start of execution")) 23 | )] 24 | pub storage_keys: Vec, 25 | } 26 | 27 | /// AccessList as defined in EIP-2930 28 | #[derive(Debug, Clone, Hash, Eq, PartialEq)] 29 | #[cfg_attr( 30 | feature = "rkyv", 31 | derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), 32 | rkyv(derive(Debug, Hash, PartialEq, Eq)) 33 | )] 34 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 35 | pub struct AccessList(pub Vec); 36 | -------------------------------------------------------------------------------- /crates/primitives/src/types/auth_list.rs: -------------------------------------------------------------------------------- 1 | use crate::{Address, U8, U256}; 2 | 3 | #[derive(Debug, Clone, Hash, Eq, PartialEq)] 4 | #[cfg_attr( 5 | feature = "rkyv", 6 | derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), 7 | rkyv(derive(Debug, Hash, PartialEq, Eq)) 8 | )] 9 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 10 | pub struct Authorization { 11 | /// The chain ID of the authorization. 12 | pub chain_id: U256, 13 | /// The address of the authorization. 14 | pub address: Address, 15 | /// The nonce for the authorization. 16 | #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))] 17 | pub nonce: u64, 18 | } 19 | 20 | /// A signed EIP-7702 authorization. 21 | #[derive(Debug, Clone, Hash, Eq, PartialEq)] 22 | #[cfg_attr( 23 | feature = "rkyv", 24 | derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), 25 | rkyv(derive(Debug, Hash, PartialEq, Eq)) 26 | )] 27 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 28 | pub struct SignedAuthorization { 29 | /// Inner authorization. 30 | pub inner: Authorization, 31 | /// Signature parity value. We allow any [`U8`] here, however, the only valid values are `0` 32 | /// and `1` and anything else will result in error during recovery. 33 | #[cfg_attr(feature = "serde", serde(rename = "yParity", alias = "v"))] 34 | pub y_parity: U8, 35 | /// Signature `r` value. 36 | pub r: U256, 37 | /// Signature `s` value. 38 | pub s: U256, 39 | } 40 | -------------------------------------------------------------------------------- /crates/primitives/src/types/eips.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "rkyv")] 2 | use crate::types::{ 3 | access_list::{ArchivedAccessList, ArchivedAccessListItem}, 4 | auth_list::ArchivedSignedAuthorization, 5 | withdrawal::ArchivedWithdrawal, 6 | }; 7 | use crate::{ 8 | U8, 9 | types::{ 10 | access_list::{AccessList, AccessListItem}, 11 | auth_list::{Authorization, SignedAuthorization}, 12 | withdrawal::Withdrawal, 13 | }, 14 | }; 15 | 16 | pub use alloy_eips::*; 17 | 18 | impl From<&eip2930::AccessListItem> for AccessListItem { 19 | fn from(item: &eip2930::AccessListItem) -> Self { 20 | Self { 21 | address: item.address, 22 | storage_keys: item.storage_keys.clone(), 23 | } 24 | } 25 | } 26 | 27 | impl From for eip2930::AccessListItem { 28 | fn from(item: AccessListItem) -> Self { 29 | Self { 30 | address: item.address, 31 | storage_keys: item.storage_keys, 32 | } 33 | } 34 | } 35 | 36 | impl From<&eip2930::AccessList> for AccessList { 37 | fn from(list: &eip2930::AccessList) -> Self { 38 | Self(list.0.iter().map(Into::into).collect()) 39 | } 40 | } 41 | 42 | impl From for eip2930::AccessList { 43 | fn from(list: AccessList) -> Self { 44 | Self(list.0.into_iter().map(Into::into).collect()) 45 | } 46 | } 47 | 48 | #[cfg(feature = "rkyv")] 49 | impl From<&ArchivedAccessListItem> for eip2930::AccessListItem { 50 | fn from(item: &ArchivedAccessListItem) -> Self { 51 | Self { 52 | address: crate::Address::from(item.address), 53 | storage_keys: item 54 | .storage_keys 55 | .iter() 56 | .map(|key| crate::B256::from(*key)) 57 | .collect(), 58 | } 59 | } 60 | } 61 | 62 | #[cfg(feature = "rkyv")] 63 | impl From<&ArchivedAccessList> for eip2930::AccessList { 64 | fn from(list: &ArchivedAccessList) -> Self { 65 | Self( 66 | list.0 67 | .iter() 68 | .map(alloy_eips::eip2930::AccessListItem::from) 69 | .collect(), 70 | ) 71 | } 72 | } 73 | 74 | impl From<&eip7702::Authorization> for Authorization { 75 | fn from(auth: &eip7702::Authorization) -> Self { 76 | Self { 77 | chain_id: auth.chain_id, 78 | address: auth.address, 79 | nonce: auth.nonce, 80 | } 81 | } 82 | } 83 | 84 | impl From for eip7702::Authorization { 85 | fn from(auth: Authorization) -> Self { 86 | Self { 87 | chain_id: auth.chain_id, 88 | address: auth.address, 89 | nonce: auth.nonce, 90 | } 91 | } 92 | } 93 | 94 | impl From<&eip7702::SignedAuthorization> for SignedAuthorization { 95 | fn from(auth: &eip7702::SignedAuthorization) -> Self { 96 | Self { 97 | inner: Authorization { 98 | chain_id: auth.chain_id, 99 | address: auth.address, 100 | nonce: auth.nonce, 101 | }, 102 | y_parity: U8::from(auth.y_parity()), 103 | r: auth.r(), 104 | s: auth.s(), 105 | } 106 | } 107 | } 108 | 109 | impl From for eip7702::SignedAuthorization { 110 | fn from(auth: SignedAuthorization) -> Self { 111 | eip7702::SignedAuthorization::new_unchecked( 112 | auth.inner.into(), 113 | auth.y_parity.to(), 114 | auth.r, 115 | auth.s, 116 | ) 117 | } 118 | } 119 | 120 | #[cfg(feature = "rkyv")] 121 | impl From<&ArchivedSignedAuthorization> for eip7702::SignedAuthorization { 122 | fn from(auth: &ArchivedSignedAuthorization) -> Self { 123 | let y_parity: U8 = From::from(&auth.y_parity); 124 | eip7702::SignedAuthorization::new_unchecked( 125 | eip7702::Authorization { 126 | chain_id: auth.inner.chain_id.into(), 127 | address: crate::Address::from(auth.inner.address), 128 | nonce: auth.inner.nonce.to_native(), 129 | }, 130 | y_parity.to(), 131 | auth.r.into(), 132 | auth.s.into(), 133 | ) 134 | } 135 | } 136 | 137 | impl From<&eip4895::Withdrawal> for Withdrawal { 138 | fn from(withdrawal: &eip4895::Withdrawal) -> Self { 139 | Self { 140 | index: withdrawal.index, 141 | validator_index: withdrawal.validator_index, 142 | address: withdrawal.address, 143 | amount: withdrawal.amount, 144 | } 145 | } 146 | } 147 | 148 | impl From<&Withdrawal> for eip4895::Withdrawal { 149 | fn from(withdrawal: &Withdrawal) -> Self { 150 | Self { 151 | index: withdrawal.index, 152 | validator_index: withdrawal.validator_index, 153 | address: withdrawal.address, 154 | amount: withdrawal.amount, 155 | } 156 | } 157 | } 158 | 159 | #[cfg(feature = "rkyv")] 160 | impl From<&ArchivedWithdrawal> for eip4895::Withdrawal { 161 | fn from(withdrawal: &ArchivedWithdrawal) -> Self { 162 | Self { 163 | index: withdrawal.index.to_native(), 164 | validator_index: withdrawal.validator_index.to_native(), 165 | address: withdrawal.address.into(), 166 | amount: withdrawal.amount.to_native(), 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /crates/primitives/src/types/mod.rs: -------------------------------------------------------------------------------- 1 | mod access_list; 2 | mod auth_list; 3 | mod block_header; 4 | mod signature; 5 | mod transaction; 6 | mod withdrawal; 7 | mod witness; 8 | 9 | pub use access_list::AccessList; 10 | pub use block_header::BlockHeader; 11 | pub use signature::Signature; 12 | pub use transaction::Transaction; 13 | pub use withdrawal::Withdrawal; 14 | pub use witness::{BlockWitness, ExecutionWitness}; 15 | 16 | #[cfg(feature = "rkyv")] 17 | mod rkyv_types { 18 | pub use super::{ 19 | access_list::{ArchivedAccessList, ArchivedAccessListItem}, 20 | block_header::ArchivedBlockHeader, 21 | signature::ArchivedSignature, 22 | transaction::ArchivedTransaction, 23 | withdrawal::ArchivedWithdrawal, 24 | witness::ArchivedBlockWitness, 25 | }; 26 | } 27 | #[cfg(feature = "rkyv")] 28 | pub use rkyv_types::*; 29 | 30 | /// re-export types from alloy_consensus 31 | #[cfg(feature = "consensus-types")] 32 | pub mod consensus; 33 | #[cfg(feature = "consensus-types")] 34 | pub use consensus::{Header as AlloyHeader, TypedTransaction as AlloyTypedTransaction}; 35 | 36 | /// re-export types from alloy_eips 37 | #[cfg(feature = "eips")] 38 | pub mod eips; 39 | 40 | #[cfg(feature = "eips")] 41 | pub use eips::eip4895::{Withdrawal as AlloyWithdrawal, Withdrawals as AlloyWithdrawals}; 42 | 43 | /// re-export types from alloy_network 44 | #[cfg(feature = "network-types")] 45 | pub mod network { 46 | /// Network definition 47 | #[cfg(not(feature = "scroll"))] 48 | pub type Network = alloy_network::Ethereum; 49 | /// Network definition 50 | #[cfg(feature = "scroll")] 51 | pub type Network = scroll_alloy_network::Scroll; 52 | } 53 | #[cfg(feature = "network-types")] 54 | pub use network::*; 55 | 56 | /// re-export types from revm 57 | #[cfg(feature = "revm-types")] 58 | pub use revm; 59 | #[cfg(feature = "revm-types")] 60 | pub use revm::primitives::{AccountInfo, Bytecode}; 61 | 62 | /// re-export types from reth_primitives 63 | #[cfg(feature = "reth-types")] 64 | pub mod reth; 65 | 66 | /// re-export types from alloy_rpc_types_eth 67 | #[cfg(feature = "rpc-types")] 68 | pub mod rpc; 69 | 70 | /// Scroll types 71 | #[cfg(feature = "scroll")] 72 | pub mod scroll; 73 | -------------------------------------------------------------------------------- /crates/primitives/src/types/rpc.rs: -------------------------------------------------------------------------------- 1 | pub use alloy_rpc_types_eth::{Header, Transaction as AlloyRpcTransaction}; 2 | 3 | #[cfg(not(feature = "scroll"))] 4 | pub use alloy_rpc_types_eth::{Transaction, TransactionReceipt, TransactionRequest}; 5 | #[cfg(feature = "scroll")] 6 | pub use scroll_alloy_rpc_types::{ 7 | ScrollTransactionReceipt as TransactionReceipt, ScrollTransactionRequest as TransactionRequest, 8 | Transaction, 9 | }; 10 | 11 | /// Block representation for RPC. 12 | pub type Block = alloy_rpc_types_eth::Block; 13 | 14 | #[cfg(feature = "consensus-types")] 15 | use crate::types::{ 16 | auth_list::SignedAuthorization, 17 | consensus::{Transaction as _, TxEnvelope, TxEnvelopeExt}, 18 | eips::{Encodable2718, Typed2718}, 19 | }; 20 | 21 | #[cfg(feature = "consensus-types")] 22 | impl crate::types::Transaction { 23 | /// Create a transaction from a rpc transaction 24 | #[cfg(feature = "scroll")] 25 | pub fn from_rpc(tx: Transaction) -> Self { 26 | crate::types::Transaction::from_rpc_inner(tx.inner) 27 | } 28 | 29 | /// Create a transaction from a rpc transaction 30 | #[cfg(not(feature = "scroll"))] 31 | pub fn from_rpc(tx: Transaction) -> Self { 32 | crate::types::Transaction::from_rpc_inner(tx) 33 | } 34 | 35 | fn from_rpc_inner(tx: AlloyRpcTransaction) -> Self { 36 | Self { 37 | hash: tx.inner.trie_hash(), 38 | nonce: tx.nonce(), 39 | from: tx.from, 40 | to: tx.to(), 41 | value: tx.value(), 42 | gas_price: tx.gas_price(), 43 | gas: tx.gas_limit(), 44 | max_fee_per_gas: tx.max_fee_per_gas(), 45 | max_priority_fee_per_gas: tx.max_priority_fee_per_gas(), 46 | max_fee_per_blob_gas: tx.max_fee_per_blob_gas(), 47 | input: tx.input().clone(), 48 | signature: TxEnvelopeExt::signature(&tx.inner).map(Into::into), 49 | chain_id: tx.chain_id(), 50 | blob_versioned_hashes: tx.blob_versioned_hashes().map(Vec::from), 51 | access_list: tx.access_list().map(Into::into), 52 | transaction_type: tx.ty(), 53 | authorization_list: tx 54 | .authorization_list() 55 | .map(|list| list.iter().map(Into::::into).collect()), 56 | #[cfg(feature = "scroll")] 57 | queue_index: tx.inner.queue_index(), 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /crates/primitives/src/types/scroll/chunk_builder.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | B256, 3 | chainspec::ChainSpec, 4 | ext::{BlockChunkExt, TxBytesHashExt}, 5 | hardforks::ScrollHardforks, 6 | types::{ 7 | reth::{Block, RecoveredBlock}, 8 | scroll::{BlockContextV2, ChunkInfo, EuclidV2ChunkInfo, LegacyChunkInfo}, 9 | }, 10 | }; 11 | use alloy_primitives::U256; 12 | use itertools::Itertools; 13 | use sbv_helpers::cycle_track; 14 | use tiny_keccak::{Hasher, Keccak}; 15 | 16 | /// Builder for ChunkInfo 17 | #[derive(Clone, Debug)] 18 | pub struct ChunkInfoBuilder<'a> { 19 | chain_spec: &'a ChainSpec, 20 | blocks: &'a [RecoveredBlock], 21 | prev_state_root: B256, 22 | prev_msg_queue_hash: Option, 23 | } 24 | 25 | impl<'a> ChunkInfoBuilder<'a> { 26 | /// Create a new ChunkInfoBuilder 27 | pub fn new( 28 | chain_spec: &'a ChainSpec, 29 | prev_state_root: B256, 30 | blocks: &'a [RecoveredBlock], 31 | ) -> Self { 32 | assert!(!blocks.is_empty(), "blocks must not be empty"); 33 | 34 | assert!( 35 | blocks 36 | .iter() 37 | .map(|b| chain_spec.is_euclid_v2_active_at_timestamp(b.timestamp)) 38 | .tuple_windows() 39 | .all(|(a, b)| a == b), 40 | "all blocks must have the same hardfork enabled" 41 | ); 42 | 43 | ChunkInfoBuilder { 44 | chain_spec, 45 | blocks, 46 | prev_state_root, 47 | prev_msg_queue_hash: None, 48 | } 49 | } 50 | 51 | /// Check if EuclidV2 is enabled on this chunk 52 | #[inline] 53 | pub fn is_euclid_v2(&self) -> bool { 54 | self.chain_spec 55 | .is_euclid_v2_active_at_timestamp(self.blocks[0].timestamp) 56 | } 57 | 58 | /// Set the prev msg queue hash 59 | #[inline] 60 | pub fn set_prev_msg_queue_hash(&mut self, prev_msg_queue_hash: B256) -> &mut Self { 61 | assert!( 62 | self.is_euclid_v2(), 63 | "prev_msg_queue_hash is only for EuclidV2" 64 | ); 65 | 66 | self.prev_msg_queue_hash = Some(prev_msg_queue_hash); 67 | self 68 | } 69 | 70 | /// Get the previous state root 71 | #[inline] 72 | pub fn prev_state_root(&self) -> B256 { 73 | self.prev_state_root 74 | } 75 | 76 | /// Get the post state root 77 | #[inline] 78 | pub fn post_state_root(&self) -> B256 { 79 | self.blocks.last().expect("at least one block").state_root 80 | } 81 | 82 | /// Build the chunk info 83 | pub fn build(self, withdraw_root: B256) -> ChunkInfo { 84 | let chain_id = self.chain_spec.chain.id(); 85 | let prev_state_root = self.prev_state_root(); 86 | let post_state_root = self.post_state_root(); 87 | 88 | let (tx_data_length, tx_data_digest) = self 89 | .blocks 90 | .iter() 91 | .flat_map(|b| b.body().transactions.iter()) 92 | .tx_bytes_hash(); 93 | 94 | if self.is_euclid_v2() { 95 | let initial_block_number = self.blocks.first().expect("at least one block").number; 96 | let prev_msg_queue_hash = self 97 | .prev_msg_queue_hash 98 | .expect("msg queue hash is required"); 99 | let post_msg_queue_hash = cycle_track!( 100 | { 101 | let mut rolling_hash = prev_msg_queue_hash; 102 | for block in self.blocks.iter() { 103 | rolling_hash = block.hash_msg_queue(&rolling_hash); 104 | } 105 | rolling_hash 106 | }, 107 | "Keccak::v256" 108 | ); 109 | ChunkInfo::EuclidV2(EuclidV2ChunkInfo { 110 | chain_id, 111 | prev_state_root, 112 | post_state_root, 113 | withdraw_root, 114 | tx_data_length, 115 | tx_data_digest, 116 | prev_msg_queue_hash, 117 | post_msg_queue_hash, 118 | initial_block_number, 119 | block_ctxs: self.blocks.iter().map(BlockContextV2::from_block).collect(), 120 | }) 121 | } else { 122 | let data_hash = cycle_track!( 123 | { 124 | let mut data_hasher = Keccak::v256(); 125 | for block in self.blocks.iter() { 126 | block.legacy_hash_da_header(&mut data_hasher); 127 | } 128 | for block in self.blocks.iter() { 129 | block.legacy_hash_l1_msg(&mut data_hasher); 130 | } 131 | let mut data_hash = B256::ZERO; 132 | data_hasher.finalize(&mut data_hash.0); 133 | data_hash 134 | }, 135 | "Keccak::v256" 136 | ); 137 | ChunkInfo::Legacy(LegacyChunkInfo { 138 | chain_id, 139 | prev_state_root, 140 | post_state_root, 141 | withdraw_root, 142 | data_hash, 143 | tx_data_digest, 144 | }) 145 | } 146 | } 147 | } 148 | 149 | impl BlockContextV2 { 150 | fn from_block(block: &RecoveredBlock) -> Self { 151 | BlockContextV2 { 152 | timestamp: block.timestamp, 153 | base_fee: U256::from_limbs([ 154 | block.base_fee_per_gas.expect("base fee must enabled"), 155 | 0, 156 | 0, 157 | 0, 158 | ]), 159 | gas_limit: block.gas_limit, 160 | num_txs: block.body().transactions.len() as u16, 161 | num_l1_msgs: block 162 | .body() 163 | .transactions 164 | .iter() 165 | .filter(|tx| tx.is_l1_message()) 166 | .count() as u16, 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /crates/primitives/src/types/scroll/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::B256; 2 | 3 | mod chunk; 4 | pub use chunk::*; 5 | #[cfg(all(feature = "scroll-reth-types", feature = "scroll-hardforks"))] 6 | mod chunk_builder; 7 | #[cfg(all(feature = "scroll-reth-types", feature = "scroll-hardforks"))] 8 | pub use chunk_builder::*; 9 | 10 | /// RPC response of the `scroll_diskRoot` method. 11 | #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] 12 | #[cfg_attr( 13 | feature = "rkyv", 14 | derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), 15 | rkyv(derive(Debug, Hash, PartialEq, Eq)) 16 | )] 17 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 18 | pub struct DiskRoot { 19 | /// MPT state root 20 | #[cfg_attr(feature = "serde", serde(rename = "diskRoot"))] 21 | #[cfg_attr(feature = "rkyv", rkyv(attr(doc = "MPT state root")))] 22 | pub disk_root: B256, 23 | /// B-MPT state root 24 | #[cfg_attr(feature = "serde", serde(rename = "headerRoot"))] 25 | #[cfg_attr(feature = "rkyv", rkyv(attr(doc = "B-MPT state root")))] 26 | pub header_root: B256, 27 | } 28 | -------------------------------------------------------------------------------- /crates/primitives/src/types/signature.rs: -------------------------------------------------------------------------------- 1 | use crate::{PrimitiveSignature, U256}; 2 | 3 | /// An Ethereum ECDSA signature. 4 | #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] 5 | #[cfg_attr( 6 | feature = "rkyv", 7 | derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), 8 | rkyv(derive(Debug, Hash, PartialEq, Eq)) 9 | )] 10 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 11 | pub struct Signature { 12 | /// The R field of the signature; the point on the curve. 13 | #[cfg_attr( 14 | feature = "rkyv", 15 | rkyv(attr(doc = "The R field of the signature; the point on the curve.")) 16 | )] 17 | pub r: U256, 18 | /// The S field of the signature; the point on the curve. 19 | #[cfg_attr( 20 | feature = "rkyv", 21 | rkyv(attr(doc = "The S field of the signature; the point on the curve.")) 22 | )] 23 | pub s: U256, 24 | /// The parity of the Y coordinate of the public key. 25 | #[cfg_attr( 26 | feature = "rkyv", 27 | rkyv(attr(doc = "The parity of the Y coordinate of the public key.")) 28 | )] 29 | pub y_parity: bool, 30 | } 31 | 32 | impl From<&PrimitiveSignature> for Signature { 33 | fn from(sig: &PrimitiveSignature) -> Self { 34 | Self { 35 | r: sig.r(), 36 | s: sig.s(), 37 | y_parity: sig.v(), 38 | } 39 | } 40 | } 41 | 42 | impl From for PrimitiveSignature { 43 | fn from(sig: Signature) -> Self { 44 | Self::new(sig.r, sig.s, sig.y_parity) 45 | } 46 | } 47 | 48 | #[cfg(feature = "rkyv")] 49 | impl From<&ArchivedSignature> for PrimitiveSignature { 50 | fn from(sig: &ArchivedSignature) -> Self { 51 | Self::new(sig.r.into(), sig.s.into(), sig.y_parity) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /crates/primitives/src/types/transaction.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | Address, B256, Bytes, ChainId, TxHash, U256, 3 | types::{access_list::AccessList, auth_list::SignedAuthorization, signature::Signature}, 4 | }; 5 | 6 | /// Transaction object used in RPC 7 | #[derive(Debug, Clone, Hash, Eq, PartialEq)] 8 | #[cfg_attr( 9 | feature = "rkyv", 10 | derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), 11 | rkyv(derive(Debug, Hash, PartialEq, Eq)) 12 | )] 13 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 14 | pub struct Transaction { 15 | /// Hash 16 | #[cfg_attr(feature = "rkyv", rkyv(attr(doc = "Hash")))] 17 | pub hash: TxHash, 18 | /// Nonce 19 | #[cfg_attr(feature = "rkyv", rkyv(attr(doc = "Nonce")))] 20 | #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))] 21 | pub nonce: u64, 22 | /// Sender 23 | #[cfg_attr(feature = "rkyv", rkyv(attr(doc = "Sender")))] 24 | pub from: Address, 25 | /// Recipient 26 | #[cfg_attr(feature = "rkyv", rkyv(attr(doc = "Recipient")))] 27 | pub to: Option
, 28 | /// Transferred value 29 | #[cfg_attr(feature = "rkyv", rkyv(attr(doc = "Transferred value")))] 30 | pub value: U256, 31 | /// Gas Price 32 | #[cfg_attr(feature = "rkyv", rkyv(attr(doc = "Gas Price")))] 33 | #[cfg_attr( 34 | feature = "serde", 35 | serde(default, with = "alloy_serde::quantity::opt",) 36 | )] 37 | pub gas_price: Option, 38 | /// Gas amount 39 | #[cfg_attr(feature = "rkyv", rkyv(attr(doc = "Gas amount")))] 40 | #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))] 41 | pub gas: u64, 42 | /// Max BaseFeePerGas the user is willing to pay. 43 | #[cfg_attr( 44 | feature = "rkyv", 45 | rkyv(attr(doc = "Max BaseFeePerGas the user is willing to pay.")) 46 | )] 47 | #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))] 48 | pub max_fee_per_gas: u128, 49 | /// The miner's tip. 50 | #[cfg_attr(feature = "rkyv", rkyv(attr(doc = "The miner's tip.")))] 51 | #[cfg_attr( 52 | feature = "serde", 53 | serde(default, with = "alloy_serde::quantity::opt",) 54 | )] 55 | pub max_priority_fee_per_gas: Option, 56 | /// Configured max fee per blob gas for eip-4844 transactions 57 | #[cfg_attr( 58 | feature = "rkyv", 59 | rkyv(attr(doc = "Configured max fee per blob gas for eip-4844 transactions")) 60 | )] 61 | #[cfg_attr( 62 | feature = "serde", 63 | serde(default, with = "alloy_serde::quantity::opt",) 64 | )] 65 | pub max_fee_per_blob_gas: Option, 66 | /// Data 67 | #[cfg_attr(feature = "rkyv", rkyv(attr(doc = "Data")))] 68 | pub input: Bytes, 69 | /// All _flattened_ fields of the transaction signature. 70 | /// 71 | /// Note: this is an option so special transaction types without a signature (e.g. ) can be supported. 72 | #[cfg_attr( 73 | feature = "rkyv", 74 | rkyv(attr( 75 | doc = "All _flattened_ fields of the transaction signature. Note: this is an option so special transaction types without a signature (e.g. ) can be supported." 76 | )) 77 | )] 78 | pub signature: Option, 79 | /// The chain id of the transaction, if any. 80 | #[cfg_attr( 81 | feature = "rkyv", 82 | rkyv(attr(doc = "The chain id of the transaction, if any.")) 83 | )] 84 | #[cfg_attr(feature = "serde", serde(default, with = "alloy_serde::quantity::opt"))] 85 | pub chain_id: Option, 86 | /// Contains the blob hashes for eip-4844 transactions. 87 | #[cfg_attr( 88 | feature = "rkyv", 89 | rkyv(attr(doc = "Contains the blob hashes for eip-4844 transactions.")) 90 | )] 91 | pub blob_versioned_hashes: Option>, 92 | /// EIP2930 93 | /// 94 | /// Pre-pay to warm storage access. 95 | #[cfg_attr( 96 | feature = "rkyv", 97 | rkyv(attr(doc = "EIP2930 Pre-pay to warm storage access.")) 98 | )] 99 | pub access_list: Option, 100 | /// EIP7702 101 | /// 102 | /// Authorizations are used to temporarily set the code of its signer to 103 | /// the code referenced by `address`. These also include a `chain_id` (which 104 | /// can be set to zero and not evaluated) as well as an optional `nonce`. 105 | #[cfg_attr(feature = "rkyv", rkyv(attr(doc = "EIP7702 Authorizations")))] 106 | pub authorization_list: Option>, 107 | /// EIP2718 108 | /// 109 | /// Transaction type, 110 | /// Some(4) for EIP-7702 transaction, Some(3) for EIP-4844 transaction, Some(2) for EIP-1559 111 | /// transaction, Some(1) for AccessList transaction, None or Some(0) for Legacy 112 | #[cfg_attr( 113 | feature = "rkyv", 114 | rkyv(attr( 115 | doc = "EIP2718 Transaction type, Some(4) for EIP-7702 transaction, Some(3) for EIP-4844 transaction, Some(2) for EIP-1559 transaction, Some(1) for AccessList transaction, None or Some(0) for Legacy" 116 | )) 117 | )] 118 | #[doc(alias = "tx_type")] 119 | pub transaction_type: u8, 120 | /// L1Msg queueIndex 121 | #[cfg(feature = "scroll")] 122 | #[cfg_attr(feature = "rkyv", rkyv(attr(doc = "L1Msg queueIndex")))] 123 | #[cfg_attr( 124 | feature = "serde", 125 | serde(default, with = "alloy_serde::quantity::opt",) 126 | )] 127 | pub queue_index: Option, 128 | } 129 | -------------------------------------------------------------------------------- /crates/primitives/src/types/withdrawal.rs: -------------------------------------------------------------------------------- 1 | use crate::Address; 2 | 3 | /// Withdrawal represents a validator withdrawal from the consensus layer. 4 | #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] 5 | #[cfg_attr( 6 | feature = "rkyv", 7 | derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), 8 | rkyv(derive(Debug, Hash, PartialEq, Eq)) 9 | )] 10 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 11 | pub struct Withdrawal { 12 | /// Monotonically increasing identifier issued by consensus layer. 13 | #[cfg_attr( 14 | feature = "rkyv", 15 | rkyv(attr(doc = "Monotonically increasing identifier issued by consensus layer.")) 16 | )] 17 | #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))] 18 | pub index: u64, 19 | /// Index of validator associated with withdrawal. 20 | #[cfg_attr( 21 | feature = "rkyv", 22 | rkyv(attr(doc = "Index of validator associated with withdrawal.")) 23 | )] 24 | #[cfg_attr( 25 | feature = "serde", 26 | serde(with = "alloy_serde::quantity", rename = "validatorIndex") 27 | )] 28 | pub validator_index: u64, 29 | /// Target address for withdrawn ether. 30 | #[cfg_attr( 31 | feature = "rkyv", 32 | rkyv(attr(doc = "Target address for withdrawn ether.")) 33 | )] 34 | pub address: Address, 35 | /// Value of the withdrawal in gwei. 36 | #[cfg_attr(feature = "rkyv", rkyv(attr(doc = "Value of the withdrawal in gwei.")))] 37 | #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))] 38 | pub amount: u64, 39 | } 40 | 41 | impl crate::Withdrawal for Withdrawal { 42 | fn index(&self) -> u64 { 43 | self.index 44 | } 45 | fn validator_index(&self) -> u64 { 46 | self.validator_index 47 | } 48 | fn address(&self) -> Address { 49 | self.address 50 | } 51 | 52 | fn amount(&self) -> u64 { 53 | self.amount 54 | } 55 | } 56 | 57 | #[cfg(feature = "rkyv")] 58 | impl crate::Withdrawal for ArchivedWithdrawal { 59 | fn index(&self) -> u64 { 60 | self.index.to_native() 61 | } 62 | fn validator_index(&self) -> u64 { 63 | self.validator_index.to_native() 64 | } 65 | fn address(&self) -> Address { 66 | self.address.into() 67 | } 68 | 69 | fn amount(&self) -> u64 { 70 | self.amount.to_native() 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /crates/primitives/src/types/witness.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | B256, Bytes, ChainId, 3 | alloy_primitives::map::B256HashMap, 4 | types::{BlockHeader, Transaction, Withdrawal}, 5 | }; 6 | use alloy_primitives::BlockNumber; 7 | 8 | /// Represents the execution witness of a block. Contains an optional map of state preimages. 9 | #[derive(Debug, Clone, Eq, PartialEq)] 10 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 11 | pub struct ExecutionWitness { 12 | /// Map of all hashed trie nodes to their preimages that were required during the execution of 13 | /// the block, including during state root recomputation. 14 | /// 15 | /// `keccak(rlp(node)) => rlp(node)` 16 | pub state: B256HashMap, 17 | /// Map of all contract codes (created / accessed) to their preimages that were required during 18 | /// the execution of the block, including during state root recomputation. 19 | /// 20 | /// `keccak(bytecodes) => bytecodes` 21 | pub codes: B256HashMap, 22 | } 23 | 24 | /// Witness for a block. 25 | #[derive(Debug, Clone, Hash, Eq, PartialEq)] 26 | #[cfg_attr( 27 | feature = "rkyv", 28 | derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), 29 | rkyv(derive(Debug, Hash, PartialEq, Eq)) 30 | )] 31 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 32 | pub struct BlockWitness { 33 | /// Chain id 34 | #[cfg_attr(feature = "rkyv", rkyv(attr(doc = "Chain id")))] 35 | pub chain_id: ChainId, 36 | /// Block header representation. 37 | #[cfg_attr(feature = "rkyv", rkyv(attr(doc = "Block header representation")))] 38 | pub header: BlockHeader, 39 | /// State trie root before the block. 40 | #[cfg_attr(feature = "rkyv", rkyv(attr(doc = "State trie root before the block")))] 41 | pub pre_state_root: B256, 42 | /// Transactions in the block. 43 | #[cfg_attr(feature = "rkyv", rkyv(attr(doc = "Transactions in the block")))] 44 | pub transaction: Vec, 45 | /// Withdrawals in the block. 46 | #[cfg_attr(feature = "rkyv", rkyv(attr(doc = "Withdrawals in the block")))] 47 | pub withdrawals: Option>, 48 | /// Last 256 Ancestor block hashes. 49 | #[cfg_attr(feature = "rkyv", rkyv(attr(doc = "Ancestor block hashes")))] 50 | #[cfg(not(feature = "scroll"))] 51 | pub block_hashes: Vec, 52 | /// Rlp encoded state trie nodes. 53 | #[cfg_attr(feature = "rkyv", rkyv(attr(doc = "Rlp encoded state trie nodes")))] 54 | pub states: Vec, 55 | /// Code bytecodes 56 | #[cfg_attr(feature = "rkyv", rkyv(attr(doc = "Code bytecodes")))] 57 | pub codes: Vec, 58 | } 59 | 60 | impl crate::BlockWitness for BlockWitness { 61 | fn chain_id(&self) -> ChainId { 62 | self.chain_id 63 | } 64 | fn number(&self) -> BlockNumber { 65 | self.header.number 66 | } 67 | fn pre_state_root(&self) -> B256 { 68 | self.pre_state_root 69 | } 70 | fn post_state_root(&self) -> B256 { 71 | self.header.state_root 72 | } 73 | fn withdrawals_root(&self) -> Option { 74 | self.header.withdrawals_root 75 | } 76 | fn num_transactions(&self) -> usize { 77 | self.transaction.len() 78 | } 79 | #[cfg(not(feature = "scroll"))] 80 | fn block_hashes_iter(&self) -> impl ExactSizeIterator { 81 | self.block_hashes.iter().copied() 82 | } 83 | fn withdrawals_iter(&self) -> Option> { 84 | self.withdrawals.as_ref().map(|w| w.iter()) 85 | } 86 | fn states_iter(&self) -> impl ExactSizeIterator> { 87 | self.states.iter().map(|s| s.as_ref()) 88 | } 89 | fn codes_iter(&self) -> impl ExactSizeIterator> { 90 | self.codes.iter().map(|c| c.as_ref()) 91 | } 92 | } 93 | 94 | #[cfg(feature = "rkyv")] 95 | impl crate::BlockWitness for ArchivedBlockWitness { 96 | fn chain_id(&self) -> ChainId { 97 | self.chain_id.to_native() 98 | } 99 | fn number(&self) -> BlockNumber { 100 | self.header.number.to_native() 101 | } 102 | fn pre_state_root(&self) -> B256 { 103 | self.pre_state_root.into() 104 | } 105 | fn post_state_root(&self) -> B256 { 106 | self.header.state_root.into() 107 | } 108 | fn withdrawals_root(&self) -> Option { 109 | self.header.withdrawals_root.as_ref().map(|x| x.0.into()) 110 | } 111 | fn num_transactions(&self) -> usize { 112 | self.transaction.len() 113 | } 114 | #[cfg(not(feature = "scroll"))] 115 | fn block_hashes_iter(&self) -> impl ExactSizeIterator { 116 | self.block_hashes.iter().map(|h| B256::from(h.0)) 117 | } 118 | fn withdrawals_iter(&self) -> Option> { 119 | self.withdrawals.as_ref().map(|w| w.iter()) 120 | } 121 | fn states_iter(&self) -> impl ExactSizeIterator> { 122 | self.states.iter().map(|s| s.as_ref()) 123 | } 124 | fn codes_iter(&self) -> impl ExactSizeIterator> { 125 | self.codes.iter().map(|c| c.as_ref()) 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /crates/sbv/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sbv" 3 | homepage = "https://github.com/scroll-tech/stateless-block-verifier/tree/master/crates/sbv" 4 | 5 | version.workspace = true 6 | edition.workspace = true 7 | rust-version.workspace = true 8 | authors.workspace = true 9 | license.workspace = true 10 | repository.workspace = true 11 | 12 | [dependencies] 13 | sbv-core.workspace = true 14 | sbv-kv.workspace = true 15 | sbv-primitives.workspace = true 16 | sbv-trie.workspace = true 17 | sbv-helpers.workspace = true 18 | sbv-utils.workspace = true 19 | 20 | [features] 21 | rkyv = ["sbv-primitives/rkyv"] 22 | serde = ["sbv-primitives/serde"] 23 | 24 | scroll = ["sbv-core/scroll", "sbv-primitives/scroll", "sbv-utils/scroll"] 25 | debug-account = ["sbv-core/debug-account", "sbv-helpers/debug-account"] 26 | debug-storage = ["sbv-core/debug-storage", "sbv-helpers/debug-storage"] 27 | dev = ["sbv-core/dev", "sbv-helpers/dev", "sbv-primitives/dev", "sbv-trie/dev"] 28 | metrics = ["sbv-core/metrics", "sbv-helpers/metrics"] 29 | 30 | # sp1 related 31 | sp1 = ["sbv-core/sp1", "sbv-primitives/sp1"] 32 | cycle-tracker = ["sbv-core/cycle-tracker", "sbv-primitives/cycle-tracker"] 33 | 34 | openvm = [ 35 | "sbv-core/openvm", 36 | "sbv-helpers/openvm", 37 | "sbv-primitives/openvm", 38 | "sbv-trie/openvm" 39 | ] 40 | -------------------------------------------------------------------------------- /crates/sbv/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Umbrella crate for the SBV library. 2 | 3 | pub use sbv_core as core; 4 | pub use sbv_helpers as helpers; 5 | pub use sbv_kv as kv; 6 | pub use sbv_primitives as primitives; 7 | pub use sbv_trie as trie; 8 | pub use sbv_utils as utils; 9 | 10 | pub use sbv_helpers::{ 11 | cycle_track, cycle_tracker_end, cycle_tracker_start, dev_debug, dev_error, dev_info, dev_trace, 12 | dev_warn, measure_duration_millis, update_metrics_counter, update_metrics_gauge, 13 | }; 14 | -------------------------------------------------------------------------------- /crates/stateful/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stateful-block-verifier" 3 | homepage = "https://github.com/scroll-tech/stateless-block-verifier/tree/master/crates/bin" 4 | 5 | version.workspace = true 6 | edition.workspace = true 7 | rust-version.workspace = true 8 | authors.workspace = true 9 | license.workspace = true 10 | repository.workspace = true 11 | 12 | [dependencies] 13 | alloy = { workspace = true, features = ["provider-http", "serde", "transport-http"] } 14 | anyhow.workspace = true 15 | clap = { workspace = true, features = ["derive"] } 16 | revm.workspace = true 17 | sled.workspace = true 18 | serde = { workspace = true, features = ["derive"] } 19 | serde_json.workspace = true 20 | thiserror.workspace = true 21 | tokio = { workspace = true, features = ["fs", "io-util", "macros", "rt-multi-thread", "signal", "sync", "time"] } 22 | tokio-retry.workspace = true 23 | url.workspace = true 24 | 25 | sbv = { workspace = true, features = ["sled"]} 26 | 27 | tracing-subscriber = { workspace = true, optional = true } 28 | 29 | [features] 30 | default = ["dev"] 31 | dev = ["sbv/dev", "dep:tracing-subscriber"] 32 | debug-account = ["sbv/debug-account"] 33 | debug-storage = ["sbv/debug-storage"] 34 | 35 | [lints] 36 | workspace = true 37 | 38 | -------------------------------------------------------------------------------- /crates/stateful/README.md: -------------------------------------------------------------------------------- 1 | # Stateful Block Verifier 2 | 3 | With the `sled` feature enabled, the zkTrie can persist storage on disk, 4 | so we can run the verifier in stateful mode, which means we do not need the 5 | zktrie proofs to execute the transaction since we have all storage available. 6 | 7 | It runs much faster than stateless mode since `eth_getBlockByNumber` is faster 8 | than `scroll_getBlockTraceByNumberOrHash`. 9 | 10 | ## How to run 11 | 12 | *Note: In debug mode, it runs both stateless and stateful to performe sanity 13 | check, which requires the rpc endpoint supports `scroll_getBlockTraceByNumberOrHash` 14 | 15 | ```bash 16 | cargo run [--release] --bin stateful-block-verifier -- --db --url 17 | ``` 18 | -------------------------------------------------------------------------------- /crates/stateful/src/error.rs: -------------------------------------------------------------------------------- 1 | use alloy::transports::{RpcError, TransportErrorKind}; 2 | use sbv::primitives::zk_trie::{hash::poseidon::PoseidonError, trie::ZkTrieError}; 3 | 4 | /// Stateful block verifier error 5 | #[derive(thiserror::Error, Debug)] 6 | pub enum Error { 7 | /// Provider error 8 | #[error(transparent)] 9 | Provider(#[from] RpcError), 10 | /// Sled error 11 | #[error(transparent)] 12 | Sled(#[from] sled::Error), 13 | /// Zktrie error 14 | #[error(transparent)] 15 | Zktrie(#[from] ZkTrieError), 16 | /// Evm database error 17 | #[error(transparent)] 18 | EvmDatabase(#[from] sbv::core::DatabaseError), 19 | /// Evm verification error 20 | #[error(transparent)] 21 | EvmVerification(#[from] sbv::core::VerificationError), 22 | /// Invalid block number 23 | #[error("expected sequential block")] 24 | ExpectedSequentialBlock, 25 | /// Post state root mismatch 26 | #[error("post state root mismatch")] 27 | PostStateRootMismatch, 28 | /// Pipeline shutdown 29 | #[error("pipeline shutdown")] 30 | PipelineShutdown, 31 | } 32 | -------------------------------------------------------------------------------- /crates/stateful/src/main.rs: -------------------------------------------------------------------------------- 1 | //! This is a simple example of how to use the stateful executor to verify the state transition of the L2 chain. 2 | 3 | use alloy::providers::ProviderBuilder; 4 | use clap::Parser; 5 | use stateful_block_verifier::StatefulBlockExecutor; 6 | use std::path::PathBuf; 7 | use url::Url; 8 | 9 | #[cfg(feature = "dev")] 10 | use tracing_subscriber::EnvFilter; 11 | 12 | #[derive(Parser)] 13 | struct Cli { 14 | /// RPC URL 15 | #[arg(short, long, default_value = "http://localhost:8545")] 16 | url: Url, 17 | /// Path to the sled database 18 | #[arg(short, long)] 19 | db: PathBuf, 20 | } 21 | 22 | #[tokio::main] 23 | async fn main() -> anyhow::Result<()> { 24 | #[cfg(feature = "dev")] 25 | tracing_subscriber::fmt() 26 | .with_env_filter( 27 | EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")), 28 | ) 29 | .init(); 30 | 31 | let cmd = Cli::parse(); 32 | 33 | let provider = ProviderBuilder::new().on_http(cmd.url); 34 | let mut executor = StatefulBlockExecutor::new(sled::open(cmd.db)?, provider.clone()).await?; 35 | 36 | tokio::select! { 37 | _ = executor.run() => {} 38 | _ = tokio::signal::ctrl_c() => { 39 | executor.shutdown(); 40 | } 41 | } 42 | 43 | Ok(()) 44 | } 45 | -------------------------------------------------------------------------------- /crates/stateful/src/pipeline.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::patch_fix_block; 2 | use crate::{retry_if_transport_error, Error, Result}; 3 | use alloy::providers::{Provider, ReqwestProvider}; 4 | use alloy::rpc::types::Block; 5 | use sbv::primitives::{alloy_primitives::ChainId, types::AlloyTransaction, Address}; 6 | use std::collections::BinaryHeap; 7 | use std::fmt::Debug; 8 | use std::sync::{ 9 | atomic::{AtomicBool, AtomicU64}, 10 | Arc, 11 | }; 12 | use tokio::sync::Mutex; 13 | 14 | struct OrderedQueue { 15 | queue: BinaryHeap, 16 | sender_index: u64, 17 | } 18 | 19 | /// Fetcher to fetch blocks from the provider. 20 | #[derive(Clone, Debug)] 21 | pub struct Fetcher { 22 | count: usize, 23 | provider: ReqwestProvider, 24 | coinbase: Address, 25 | chain_id: ChainId, 26 | 27 | queue: Arc>, 28 | fetcher_index: Arc, 29 | tx: tokio::sync::mpsc::Sender>, 30 | shutdown: Arc, 31 | } 32 | 33 | #[derive(Eq, PartialEq)] 34 | struct FetchedBlock(Block); 35 | 36 | impl Fetcher { 37 | /// Spawn `count` fetchers to fetch blocks from the provider. 38 | pub fn spawn( 39 | count: usize, 40 | provider: ReqwestProvider, 41 | coinbase: Address, 42 | chain_id: ChainId, 43 | start_block: u64, 44 | shutdown: Arc, 45 | ) -> tokio::sync::mpsc::Receiver> { 46 | let (tx, rx) = tokio::sync::mpsc::channel(count); 47 | 48 | let queue = OrderedQueue { 49 | queue: BinaryHeap::new(), 50 | sender_index: start_block, 51 | }; 52 | 53 | let fetcher = Fetcher { 54 | count, 55 | provider, 56 | coinbase, 57 | chain_id, 58 | queue: Arc::new(Mutex::new(queue)), 59 | fetcher_index: Arc::new(AtomicU64::new(start_block)), 60 | tx, 61 | shutdown: shutdown.clone(), 62 | }; 63 | 64 | for _ in 0..count { 65 | let fetcher = fetcher.clone(); 66 | let shutdown = shutdown.clone(); 67 | tokio::spawn(async move { 68 | fetcher.run().await.ok(); 69 | shutdown.store(true, std::sync::atomic::Ordering::SeqCst); 70 | }); 71 | } 72 | 73 | rx 74 | } 75 | 76 | async fn run(self) -> Result<()> { 77 | while !self.shutdown.load(std::sync::atomic::Ordering::SeqCst) { 78 | loop { 79 | let queue = self.queue.lock().await; 80 | // back pressure 81 | if queue.queue.len() >= self.count { 82 | drop(queue); 83 | tokio::time::sleep(std::time::Duration::from_secs(1)).await; 84 | continue; 85 | } 86 | break; 87 | } 88 | 89 | let block_number = self 90 | .fetcher_index 91 | .fetch_add(1, std::sync::atomic::Ordering::SeqCst); 92 | 93 | // wait for new block 94 | while let Ok(latest_chain_block_number) = 95 | retry_if_transport_error!(self.provider.get_block_number()) 96 | { 97 | if block_number <= latest_chain_block_number { 98 | break; 99 | } 100 | tokio::time::sleep(std::time::Duration::from_secs(1)).await; 101 | } 102 | 103 | let mut block = retry_if_transport_error!(self 104 | .provider 105 | .raw_request::<_, Block>( 106 | "eth_getBlockByNumber".into(), 107 | (format!("0x{:x}", block_number), true), 108 | ))?; 109 | patch_fix_block(&mut block, self.coinbase, self.chain_id); 110 | 111 | dev_trace!( 112 | "block#{} block fetched, state root: {}", 113 | block.header.number, 114 | block.header.state_root 115 | ); 116 | 117 | let mut queue = self.queue.lock().await; 118 | queue.queue.push(FetchedBlock(block)); 119 | while queue 120 | .queue 121 | .peek() 122 | .map(|b| b.0.header.number == queue.sender_index) 123 | .unwrap_or_default() 124 | { 125 | let block = queue.queue.pop().unwrap().0; 126 | if self.tx.send(block).await.is_err() { 127 | return Err(Error::PipelineShutdown); 128 | } 129 | queue.sender_index += 1; 130 | } 131 | } 132 | Ok(()) 133 | } 134 | } 135 | 136 | impl Debug for OrderedQueue { 137 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 138 | f.debug_struct("OrderedQueue") 139 | .field("sender_index", &self.sender_index) 140 | .finish() 141 | } 142 | } 143 | 144 | impl Ord for FetchedBlock { 145 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { 146 | self.0.header.number.cmp(&other.0.header.number).reverse() 147 | } 148 | } 149 | 150 | impl PartialOrd for FetchedBlock { 151 | fn partial_cmp(&self, other: &Self) -> Option { 152 | Some(self.cmp(other)) 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /crates/stateful/src/sanity_check.rs: -------------------------------------------------------------------------------- 1 | use crate::{retry_if_transport_error, Result}; 2 | use alloy::primitives::ChainId; 3 | use alloy::providers::{Provider, ReqwestProvider}; 4 | use revm::primitives::BlockEnv; 5 | use sbv::{ 6 | core::{EvmExecutorBuilder, HardforkConfig}, 7 | primitives::{ 8 | types::{AlloyTransaction, BlockTrace, LegacyStorageTrace}, 9 | zk_trie::db::{kv::HashMapDb, NodeDb}, 10 | Block, Transaction, TxTrace, B256, U256, 11 | }, 12 | }; 13 | 14 | /// Assert that the given L2 trace and block are equal. 15 | pub fn assert_equal(l2_trace: impl Block, block: impl Block) { 16 | let trace_block_env = BlockEnv { 17 | number: U256::from_limbs([l2_trace.number(), 0, 0, 0]), 18 | coinbase: l2_trace.coinbase(), 19 | timestamp: l2_trace.timestamp(), 20 | gas_limit: l2_trace.gas_limit(), 21 | basefee: l2_trace.base_fee_per_gas().unwrap_or_default(), 22 | difficulty: l2_trace.difficulty(), 23 | prevrandao: l2_trace.prevrandao(), 24 | blob_excess_gas_and_price: None, 25 | }; 26 | let block_block_env = BlockEnv { 27 | number: U256::from_limbs([block.number(), 0, 0, 0]), 28 | coinbase: block.coinbase(), 29 | timestamp: block.timestamp(), 30 | gas_limit: block.gas_limit(), 31 | basefee: block.base_fee_per_gas().unwrap_or_default(), 32 | difficulty: block.difficulty(), 33 | prevrandao: block.prevrandao(), 34 | blob_excess_gas_and_price: None, 35 | }; 36 | assert_eq!(trace_block_env, block_block_env, "block_env mismatch"); 37 | for (i, (trace_tx, block_tx)) in l2_trace 38 | .transactions() 39 | .zip(block.transactions()) 40 | .enumerate() 41 | { 42 | let trace_tx = trace_tx.try_build_typed_tx().unwrap(); 43 | let block_tx = block_tx.try_build_typed_tx().unwrap(); 44 | assert_eq!(trace_tx, block_tx, "tx#{i} mismatch {block_tx:?}"); 45 | let trace_tx_signer = trace_tx.get_or_recover_signer().unwrap(); 46 | let block_tx_signer = block_tx.get_or_recover_signer().unwrap(); 47 | assert_eq!(trace_tx_signer, block_tx_signer, "tx#{i} signer mismatch"); 48 | let trace_gas_limit = trace_tx.gas_limit(); 49 | let block_gas_limit = block_tx.gas_limit(); 50 | assert_eq!( 51 | trace_gas_limit, block_gas_limit, 52 | "tx#{i} gas limit mismatch" 53 | ); 54 | let trace_gas_price = trace_tx 55 | .effective_gas_price(l2_trace.base_fee_per_gas().unwrap_or_default().to()) 56 | .map(U256::from); 57 | let block_gas_price = block_tx 58 | .effective_gas_price(l2_trace.base_fee_per_gas().unwrap_or_default().to()) 59 | .map(U256::from); 60 | assert_eq!( 61 | trace_gas_price, block_gas_price, 62 | "tx#{i} gas price mismatch" 63 | ); 64 | assert_eq!(trace_tx.to(), block_tx.to(), "tx#{i} transact_to mismatch"); 65 | assert_eq!(trace_tx.value(), block_tx.value(), "tx#{i} value mismatch"); 66 | assert_eq!(trace_tx.data(), block_tx.data(), "tx#{i} data mismatch"); 67 | assert_eq!( 68 | trace_tx.is_l1_msg(), 69 | block_tx.is_l1_msg(), 70 | "tx#{i} is_l1_msg mismatch" 71 | ); 72 | assert_eq!(trace_tx.nonce(), block_tx.nonce(), "tx#{i} nonce mismatch"); 73 | assert_eq!( 74 | trace_tx.chain_id(), 75 | block_tx.chain_id(), 76 | "tx#{i} chain_id mismatch" 77 | ); 78 | assert_eq!( 79 | trace_tx.access_list(), 80 | block_tx.access_list(), 81 | "tx#{i} access_list mismatch" 82 | ); 83 | assert_eq!( 84 | trace_tx.max_priority_fee_per_gas(), 85 | block_tx.max_priority_fee_per_gas(), 86 | "tx#{i} max_priority_fee_per_gas mismatch" 87 | ); 88 | assert_eq!(trace_tx.rlp(), block_tx.rlp(), "tx#{i} rlp mismatch"); 89 | } 90 | } 91 | 92 | /// Check the stateful execution of the given block. 93 | pub async fn check_stateless( 94 | provider: &ReqwestProvider, 95 | chain_id: ChainId, 96 | hardfork_config: HardforkConfig, 97 | storage_root_before: B256, 98 | block: &alloy::rpc::types::Block, 99 | ) -> Result { 100 | let block_number = block.header.number; 101 | let l2_trace = retry_if_transport_error!(provider 102 | .raw_request::<_, BlockTrace>( 103 | "scroll_getBlockTraceByNumberOrHash".into(), 104 | (format!("0x{:x}", block_number),), 105 | ))?; 106 | let l2_trace: BlockTrace = l2_trace.into(); 107 | let root_before = l2_trace.root_before(); 108 | let root_after = l2_trace.root_after(); 109 | 110 | assert_eq!(root_before, storage_root_before); 111 | dev_info!( 112 | "block#{block_number} trace fetched, root_before: {root_before}, root_after: {root_after}" 113 | ); 114 | 115 | { 116 | let mut code_db = HashMapDb::default(); 117 | let mut zktrie_db = NodeDb::new(HashMapDb::default()); 118 | l2_trace.build_zktrie_db(&mut zktrie_db).unwrap(); 119 | let mut executor = EvmExecutorBuilder::new(&mut code_db, &mut zktrie_db) 120 | .hardfork_config(hardfork_config) 121 | .chain_id(chain_id) 122 | .build(root_before)?; 123 | executor.insert_codes(&l2_trace)?; 124 | executor.handle_block(&l2_trace)?; 125 | let revm_root_after = executor.commit_changes()?; 126 | assert_eq!(root_after, revm_root_after); 127 | dev_info!("block#{block_number} stateless check ok"); 128 | } 129 | assert_equal(&l2_trace, block); 130 | 131 | Ok(root_after) 132 | } 133 | -------------------------------------------------------------------------------- /crates/stateful/src/utils.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::ChainId; 2 | use alloy::rpc::types::BlockTransactions; 3 | use alloy::transports::{RpcError, TransportErrorKind}; 4 | use sbv::dev_warn; 5 | use sbv::primitives::types::AlloyTransaction; 6 | use sbv::primitives::Address; 7 | use std::future::Future; 8 | use tokio_retry::strategy::{jitter, ExponentialBackoff}; 9 | use tokio_retry::RetryIf; 10 | 11 | /// Retry the given future if it returns a transport error. 12 | #[inline(always)] 13 | pub fn retry_if_transport_error( 14 | f: F, 15 | ) -> impl Future>> 16 | where 17 | F: FnMut() -> Fut, 18 | Fut: Future>>, 19 | { 20 | RetryIf::spawn( 21 | ExponentialBackoff::from_millis(10).map(jitter).take(10), 22 | f, 23 | |e: &RpcError| { 24 | if e.is_transport_error() { 25 | dev_warn!("retrying request due to transport error: {:?}", e); 26 | true 27 | } else { 28 | false 29 | } 30 | }, 31 | ) 32 | } 33 | 34 | /// Retry the given future if it returns a transport error. 35 | #[macro_export] 36 | macro_rules! retry_if_transport_error { 37 | ($fut:expr) => { 38 | $crate::utils::retry_if_transport_error(|| async { $fut.await }).await 39 | }; 40 | } 41 | 42 | /// Patch the given block to fix the miner and chain_id. 43 | #[inline(always)] 44 | pub fn patch_fix_block( 45 | block: &mut alloy::rpc::types::Block, 46 | coinbase: Address, 47 | chain_id: ChainId, 48 | ) { 49 | // Clique, which uses header.miner for a different purpose. in particular, header.miner != coinbase. 50 | block.header.miner = coinbase; 51 | 52 | if let BlockTransactions::Full(ref mut txs) = block.transactions { 53 | for (idx, tx) in txs.iter_mut().enumerate() { 54 | let tx_type = tx.transaction_type.unwrap_or(0); 55 | if tx_type == 0 && tx.signature.unwrap().v.to::() >= 35 { 56 | tx.chain_id = Some(chain_id); 57 | dev_trace!( 58 | "block#{block_number} tx#{idx} is Eip155 tx but chain_id is not set", 59 | block_number = block.header.number 60 | ); 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /crates/trie/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sbv-trie" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | authors.workspace = true 7 | license.workspace = true 8 | homepage.workspace = true 9 | repository.workspace = true 10 | 11 | [lints] 12 | workspace = true 13 | 14 | [dependencies] 15 | alloy-rlp.workspace = true 16 | alloy-trie.workspace = true 17 | reth-trie.workspace = true 18 | reth-trie-sparse.workspace = true 19 | 20 | sbv-primitives.workspace = true 21 | sbv-kv.workspace = true 22 | sbv-helpers.workspace = true 23 | 24 | thiserror.workspace = true 25 | 26 | [dev-dependencies] 27 | serde_json.workspace = true 28 | 29 | [features] 30 | scroll = [ 31 | "reth-trie/scroll", 32 | "sbv-primitives/scroll-all", 33 | ] 34 | dev = [ 35 | "sbv-primitives/dev", 36 | "sbv-helpers/dev" 37 | ] 38 | 39 | sp1 = [] 40 | cycle-tracker = [] 41 | 42 | openvm = [ 43 | "sbv-primitives/openvm", 44 | "sbv-helpers/openvm", 45 | ] 46 | -------------------------------------------------------------------------------- /crates/utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sbv-utils" 3 | description = "Utilities for Stateless Block Verifier" 4 | version.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | authors.workspace = true 8 | license.workspace = true 9 | homepage.workspace = true 10 | repository.workspace = true 11 | 12 | [lints] 13 | workspace = true 14 | 15 | [dependencies] 16 | alloy-provider.workspace = true 17 | alloy-transport.workspace = true 18 | 19 | async-trait.workspace = true 20 | futures.workspace = true 21 | thiserror.workspace = true 22 | 23 | sbv-primitives.workspace = true 24 | 25 | [features] 26 | scroll = ["sbv-primitives/scroll-all"] 27 | -------------------------------------------------------------------------------- /crates/utils/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Utilities for the Stateless Block Verifier 2 | 3 | pub mod rpc; 4 | pub mod witness; 5 | -------------------------------------------------------------------------------- /crates/utils/src/rpc.rs: -------------------------------------------------------------------------------- 1 | //! Rpc Extension 2 | use alloy_provider::{Provider, network::primitives::BlockTransactionsKind}; 3 | use alloy_transport::TransportResult; 4 | use sbv_primitives::types::{BlockWitness, ExecutionWitness, Network, eips::BlockNumberOrTag}; 5 | 6 | /// Extension trait for [`Provider`](Provider). 7 | #[async_trait::async_trait] 8 | pub trait ProviderExt: Provider { 9 | /// Get the execution witness for a block. 10 | async fn debug_execution_witness( 11 | &self, 12 | number: BlockNumberOrTag, 13 | ) -> TransportResult { 14 | self.client() 15 | .request::<_, ExecutionWitness>("debug_executionWitness", (number,)) 16 | .await 17 | } 18 | 19 | /// Get the disk root for a block. 20 | #[cfg(feature = "scroll")] 21 | async fn scroll_disk_root( 22 | &self, 23 | number: BlockNumberOrTag, 24 | ) -> TransportResult { 25 | self.client() 26 | .request::<_, sbv_primitives::types::scroll::DiskRoot>("scroll_diskRoot", (number,)) 27 | .await 28 | } 29 | 30 | /// Dump the block witness for a block. 31 | async fn dump_block_witness( 32 | &self, 33 | number: BlockNumberOrTag, 34 | #[cfg(not(feature = "scroll"))] ancestors: Option, 35 | ) -> TransportResult> { 36 | let builder = crate::witness::WitnessBuilder::new(); 37 | let Some(block) = self 38 | .get_block_by_number(number, BlockTransactionsKind::Full) 39 | .await? 40 | else { 41 | return Ok(None); 42 | }; 43 | let number = block.header.number; 44 | 45 | let builder = builder 46 | .block(block) 47 | .chain_id(self.get_chain_id().await?) 48 | .execution_witness(self.debug_execution_witness(number.into()).await?); 49 | 50 | #[cfg(not(feature = "scroll"))] 51 | let builder = builder 52 | .ancestor_blocks(self.dump_block_ancestors(number, ancestors).await?.unwrap()) 53 | .unwrap(); 54 | 55 | #[cfg(feature = "scroll")] 56 | let builder = builder 57 | .state_root(self.scroll_disk_root(number.into()).await?.disk_root) 58 | .unwrap() 59 | .prev_state_root(self.scroll_disk_root((number - 1).into()).await?.disk_root); 60 | 61 | Ok(Some(builder.build().unwrap())) 62 | } 63 | 64 | /// Dump the ancestor blocks for a block. 65 | #[doc(hidden)] 66 | #[cfg(not(feature = "scroll"))] 67 | async fn dump_block_ancestors( 68 | &self, 69 | number: sbv_primitives::BlockNumber, 70 | ancestors: Option, 71 | ) -> TransportResult>> { 72 | let ancestors = ancestors 73 | .unwrap_or_default() 74 | .clamp(1, (number as usize).min(256)); 75 | 76 | let ancestors = futures::future::try_join_all((1..=ancestors).map(|offset| { 77 | let block_number = number - offset as sbv_primitives::BlockNumber; 78 | self.get_block_by_number(block_number.into(), BlockTransactionsKind::Hashes) 79 | })) 80 | .await?; 81 | 82 | if ancestors.iter().any(Option::is_none) { 83 | return Ok(None); 84 | } 85 | 86 | Ok(Some(ancestors.into_iter().map(Option::unwrap).collect())) 87 | } 88 | } 89 | 90 | impl> ProviderExt for P {} 91 | -------------------------------------------------------------------------------- /crates/utils/src/witness.rs: -------------------------------------------------------------------------------- 1 | //! Witness builder. 2 | 3 | use sbv_primitives::{ 4 | B256, ChainId, 5 | types::{BlockWitness, ExecutionWitness, Transaction, rpc::Block as RpcBlock}, 6 | }; 7 | 8 | /// Block witness builder. 9 | #[derive(Debug, Default)] 10 | pub struct WitnessBuilder { 11 | chain_id: Option, 12 | block: Option, 13 | execution_witness: Option, 14 | prev_state_root: Option, 15 | 16 | #[cfg(not(feature = "scroll"))] 17 | blocks_hash: Option>, 18 | } 19 | 20 | /// Witness build error. 21 | #[derive(Debug, thiserror::Error)] 22 | pub enum WitnessBuildError { 23 | /// Missing field. 24 | #[error("missing field: {0}")] 25 | MissingField(&'static str), 26 | /// At least one ancestor block is required. 27 | #[cfg(not(feature = "scroll"))] 28 | #[error("at least one ancestor block is required")] 29 | AtLeastOneAncestorBlock, 30 | } 31 | 32 | impl WitnessBuilder { 33 | /// Create a new witness builder. 34 | pub fn new() -> Self { 35 | Self::default() 36 | } 37 | 38 | /// Set the chain ID. 39 | pub fn chain_id(mut self, chain_id: ChainId) -> Self { 40 | self.chain_id = Some(chain_id); 41 | self 42 | } 43 | 44 | /// Set the block. 45 | pub fn block(mut self, block: RpcBlock) -> Self { 46 | self.block = Some(block); 47 | self 48 | } 49 | 50 | /// Set the execution witness 51 | pub fn execution_witness(mut self, execution_witness: ExecutionWitness) -> Self { 52 | self.execution_witness = Some(execution_witness); 53 | self 54 | } 55 | 56 | /// Set the `blocks_hash` and `prev_state_root` from an iterator of ancestor blocks. 57 | #[cfg(not(feature = "scroll"))] 58 | pub fn ancestor_blocks(mut self, iter: I) -> Result 59 | where 60 | I: IntoIterator, 61 | { 62 | let mut iter = iter.into_iter(); 63 | let parent = iter 64 | .next() 65 | .ok_or(WitnessBuildError::AtLeastOneAncestorBlock)?; 66 | 67 | self.prev_state_root = Some(parent.header.state_root); 68 | self.blocks_hash = Some( 69 | std::iter::once(parent.header.hash) 70 | .chain(iter.map(|b| b.header.hash)) 71 | .collect(), 72 | ); 73 | 74 | Ok(self) 75 | } 76 | 77 | /// Set the state root. 78 | /// 79 | /// This is only available when the `scroll` feature is enabled. 80 | /// 81 | /// If the state root in block is not the one you want to use, you can override it with this method. 82 | #[cfg(feature = "scroll")] 83 | pub fn state_root(mut self, state_root: B256) -> Result { 84 | self.block 85 | .as_mut() 86 | .ok_or(WitnessBuildError::MissingField("block"))? 87 | .header 88 | .state_root = state_root; 89 | 90 | Ok(self) 91 | } 92 | 93 | /// Set the previous state root. 94 | pub fn prev_state_root(mut self, prev_state_root: B256) -> Self { 95 | self.prev_state_root = Some(prev_state_root); 96 | self 97 | } 98 | 99 | /// Build the block witness. 100 | pub fn build(self) -> Result { 101 | let block = self.block.ok_or(WitnessBuildError::MissingField("block"))?; 102 | let execution_witness = self 103 | .execution_witness 104 | .ok_or(WitnessBuildError::MissingField("execution_witness"))?; 105 | Ok(BlockWitness { 106 | chain_id: self 107 | .chain_id 108 | .ok_or(WitnessBuildError::MissingField("chain_id"))?, 109 | header: block.header.into(), 110 | pre_state_root: self 111 | .prev_state_root 112 | .ok_or(WitnessBuildError::MissingField("prev_state_root"))?, 113 | transaction: block 114 | .transactions 115 | .into_transactions() 116 | .map(Transaction::from_rpc) 117 | .collect(), 118 | #[cfg(not(feature = "scroll"))] 119 | block_hashes: self 120 | .blocks_hash 121 | .ok_or(WitnessBuildError::MissingField("ancestor_blocks"))?, 122 | withdrawals: block 123 | .withdrawals 124 | .map(|w| w.iter().map(From::from).collect()), 125 | states: execution_witness.state.into_values().collect(), 126 | codes: execution_witness.codes.into_values().collect(), 127 | }) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2024-12-06" -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | comment_width = 300 2 | edition = "2024" 3 | imports_granularity = "Crate" 4 | max_width = 100 5 | newline_style = "Unix" 6 | normalize_comments = true 7 | style_edition = "2024" 8 | wrap_comments = false 9 | -------------------------------------------------------------------------------- /scripts/download.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | if [ $# -eq 0 ]; then 6 | echo "Usage: $0 [url] [output_dir]" 7 | exit 0 8 | fi 9 | 10 | BLOCK=$1 11 | URL=${2:-http://localhost:8545} 12 | OUT_DIR=${3:-testdata/holesky_witness} 13 | 14 | HEX_BLOCK=$(printf '0x%x' $BLOCK) 15 | 16 | mkdir -p ${OUT_DIR}/${HEX_BLOCK} 17 | 18 | cast rpc -r $URL eth_getBlockByNumber "$HEX_BLOCK" true | jq > ${OUT_DIR}/${HEX_BLOCK}/block.json 19 | cast rpc -r $URL debug_executionWitness "$HEX_BLOCK" > ${OUT_DIR}/${HEX_BLOCK}/witness.json 20 | -------------------------------------------------------------------------------- /testdata/.gitignore: -------------------------------------------------------------------------------- 1 | *.rkyv -------------------------------------------------------------------------------- /testdata/scroll_witness/enforce-revert.json: -------------------------------------------------------------------------------- 1 | { 2 | "chain_id": 222222, 3 | "header": { 4 | "parent_hash": "0xb319afe72c883590c5034e10d00f3c5755b9b26ffb861536ac7dea78ab5d275f", 5 | "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", 6 | "miner": "0x0000000000000000000000000000000000000000", 7 | "state_root": "0x6e21182fe7fdfc3c145fe978f674260eae3c1a6dbd91e74f39d0dcbe3a08cf3c", 8 | "transactions_root": "0x5dde3059940c3d012aefa6139410af3da71c34b1de89c69e7035234abdba8a9b", 9 | "receipts_root": "0xfa57e9ba55a854d7df920db8c468ed58744d650de0a37865321cbb0f8acbabf3", 10 | "logs_bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 11 | "difficulty": "0x2", 12 | "number": "0x1b", 13 | "gas_limit": "0x989680", 14 | "gas_used": "0x5208", 15 | "timestamp": "0x67ac4b01", 16 | "extra_data": "0xd883050804846765746888676f312e32312e31856c696e75780000000000000080e1eed4748100c2ac01ff0384d448bb98d28f4f810ad0fb2608e917f65d599169230df65275f989bb217a896d5800017c23de4395f2c1307083641341a2219100", 17 | "mix_hash": "0x0000000000000000000000000000000000000000000000000000000000000000", 18 | "nonce": "0x0000000000000000", 19 | "base_fee_per_gas": "0x2562500" 20 | }, 21 | "pre_state_root": "0x009ad52c3d3e9aa748473f1f3c109e08878c1e472f229508ed4b0699456649f7", 22 | "transaction": [ 23 | { 24 | "hash": "0x82527e457f848d7efbb9ceff78d1c9c39b591c8db05a662c19afac0bca233e2e", 25 | "nonce": "0x0", 26 | "from": "0x2b5ad5c4795c026514f8317c7a215e218dccd6cf", 27 | "to": "0x0000000000000000000000000000000000000001", 28 | "value": "0x1", 29 | "gas": "0x30d40", 30 | "max_fee_per_gas": "0x0", 31 | "input": "0x", 32 | "transaction_type": 126, 33 | "queue_index": "0x1" 34 | } 35 | ], 36 | "withdrawals": null, 37 | "states": [ 38 | "0xf8d18080a0a097caf3d13d833fb31b884a4e9f5aeea03d704712c18eda66f3d671f3e4db9d8080a0edae92983bd2f254e78cf3e5618256882023601ea6d0da69f894682f3184bc7a8080808080a0896cd3af9e915f0221587854dc661da008bfff53c9731033d742109a97e754c280a0d7a31d848df8c1e9e8b824133cef5a71d2ca612bc41c9b0d65a80616b28935c0a03a5ff5155ffae95e2997a8c8a47dc3fd197142809393f3fd168a134b8bf44932a0899cf9be2c0a3901e95c320537e43a12c57dcd526ac3477b80f56742f3708b9a80", 39 | "0xf901f1a031034633a4d6f600a87ef45b206d43d3748ee5de3afe225ebc5f94a6f340e491a0038be5e564efc7d25269702bc593176afb7495710962a2a4c636b3d417b578a4a0a4ba30238eab284bd5192124455551eb85fb4ae0746fc498bfaf8318a7acdbe1a0031007470cb4c012240aa2c615b852871104bc5a33fe60e6f2c94f113f2f1829a020284c56c9f50eb8ad59b9f10153c02b2ae9ce527ac3edec91b4b81fc254b8d1a0ef5a71b29efdfc134052832763f8aa49a51898f41bbffb2da3be3b878e0b5916a0eeae449dc3a95d806b8f3878b43b52b6af2471155b1414ba676518fe47c13faea0f6678b86d499189203984c25108e1f9eacfad05dd27dcb3ee82b52befb19b619a0b1dfd09dd0c6c4f22a4fe09d13195b3a1cd176b278845331e8f425cd643515a8a05fb7601d74070f46bac445bdcacb2d3744cbaf6ab2f2c11044f7eaa61283562ea05a6410b9a5681ae393edcbc6a270f378c61146e1edc6773b793a4068a85f4e0aa0839a52deeec01b7a00f08fb7383b409efbe04e412124de4ab366e5a2fa9e6873a0128e30a4baba7dc3f3cee66c500780b541ce714e2db32685831b92a0c57f11c7a0112d87a0d46f72294591a8cbd15c56a2d2a2d6b90a4434218bba9ca2b092f9fc80a0d2f049cdff2e77ba9d768391128f3812bd5ff6d9f80b03a58a716a5903842f1480", 40 | "0xf901f1a0f5509071821d2953b53448397715aab3c8436b35cc877e14ddb459a25ab3a690a08a955e1b9ed7d6e47c399e1af7b803cacaddf66f4a44c0f8c7a4985d08485d1580a025b6c58fd3fd081af7ceef1d82a5974478fe20a04a2ff69e25f372b66f95baf6a0858d65b96b3c3b8ec28d4f47a40c4140641994ef6140e1223289607252a49148a03e860edf66403d134ea6f254e5b983f34575d1ccfbcdb6c6d332b45da5ab7207a048749d45c78345c131ebdccdd46749fce452bc6ae7f76db888cccebfde6759f3a003d84be52c9b2bb800428458e4ee566bbb442661e2510fb75264134a4787c7aea08a47b97108346b6c8851c21c9cbea202d3439bca89502afc45cb0a0fa4a21703a095707bb25b4f2c902d2ee2749673b6a15f4e4021fea65b96cb73f6df9f50f003a01546ee3d3994d0a1e70adb51772b875e1a0596fad57c3a7b020ffb1a068760cba0475ee2034ee15615f28a7097df855c3705a63c1b713263aecbd216b78ec48d55a04d609409356bc38ade27e717f4b836ddff70fbc7d3c857b6429cb463a493e047a0938755b85dad2e9f473d24e3555244d6ec01e91dc5ffd7fdd2e5d899d46ddc6ba07b85a5161815b739a0e6fe6c6e12bf600af6b6aa406e4a4c1dbf3ee88db2a3e9a0306dbf2a7ae616d4aa59e0b705e1fb7e9bb32d07a74fd851c5b01719235d089080" 41 | ], 42 | "codes": [] 43 | } 44 | -------------------------------------------------------------------------------- /testdata/scroll_witness/euclid_v2/1.json: -------------------------------------------------------------------------------- 1 | { 2 | "chain_id": 333333, 3 | "header": { 4 | "parent_hash": "0x818c7d06855afe7516b06b46edec2ed0029537e8548ffc161686d1ad0afd763a", 5 | "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", 6 | "miner": "0x0000000000000000000000000000000000000000", 7 | "state_root": "0x10ab3e5d45de7df81428a0c36426ee62abff54c2e16705148b94c93c0cefc4cd", 8 | "transactions_root": "0xfd3a328ddc0e4067263d8f5451793b245bd1cbcff4c901e4ca0b283011ef621b", 9 | "receipts_root": "0x8c521bf03b05e8859adb63ccd364c747b1c1830168dd612d45f6c64197de6c3a", 10 | "logs_bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 11 | "difficulty": "0x2", 12 | "number": "0x1", 13 | "gas_limit": "0x989680", 14 | "gas_used": "0x4b9a5", 15 | "timestamp": "0x67b4399f", 16 | "extra_data": "0xd883050808846765746888676f312e32312e31856c696e757800000000000000a472959ca8719182ddf67a63708f35598c145b2738b0b1445bcd02def3087e192a8bf45eccbf476ac2c7b0ff1b00046fe69dfff921de373c5cf7fe074134872901", 17 | "mix_hash": "0x0000000000000000000000000000000000000000000000000000000000000000", 18 | "nonce": "0x0000000000000000", 19 | "base_fee_per_gas": "0x2562500" 20 | }, 21 | "pre_state_root": "0x5302a56cbbec7d14d48d592b805d4ec3c7011439dfaa90d44deee02a9326d203", 22 | "transaction": [ 23 | { 24 | "hash": "0xd85c7e1a4f29929093fc1c376c7047972a7bbf74d39b1478753eeb68bb767e3f", 25 | "nonce": "0x0", 26 | "from": "0x6f4c950442e1af093bcff730381e63ae9171b87a", 27 | "to": null, 28 | "value": "0x0", 29 | "gas": "0xf4240", 30 | "max_fee_per_gas": "0x3b9aca00", 31 | "max_priority_fee_per_gas": "0x1", 32 | "input": "0x6080604052348015600e575f80fd5b506104a58061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c8063c566b2091461002d575b5f80fd5b61004760048036038101906100429190610298565b61005e565b604051610055929190610359565b60405180910390f35b5f606061010073ffffffffffffffffffffffffffffffffffffffff168360405161008891906103c1565b5f604051808303815f865af19150503d805f81146100c1576040519150601f19603f3d011682016040523d82523d5f602084013e6100c6565b606091505b5080925081935050508161010f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161010690610431565b60405180910390fd5b7fa82cc8725dfeb485badf0c830fc554a37ed268855e35805056c77c86946342098160405161013e919061044f565b60405180910390a1915091565b5f604051905090565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6101aa82610164565b810181811067ffffffffffffffff821117156101c9576101c8610174565b5b80604052505050565b5f6101db61014b565b90506101e782826101a1565b919050565b5f67ffffffffffffffff82111561020657610205610174565b5b61020f82610164565b9050602081019050919050565b828183375f83830152505050565b5f61023c610237846101ec565b6101d2565b90508281526020810184848401111561025857610257610160565b5b61026384828561021c565b509392505050565b5f82601f83011261027f5761027e61015c565b5b813561028f84826020860161022a565b91505092915050565b5f602082840312156102ad576102ac610154565b5b5f82013567ffffffffffffffff8111156102ca576102c9610158565b5b6102d68482850161026b565b91505092915050565b5f8115159050919050565b6102f3816102df565b82525050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f61032b826102f9565b6103358185610303565b9350610345818560208601610313565b61034e81610164565b840191505092915050565b5f60408201905061036c5f8301856102ea565b818103602083015261037e8184610321565b90509392505050565b5f81905092915050565b5f61039b826102f9565b6103a58185610387565b93506103b5818560208601610313565b80840191505092915050565b5f6103cc8284610391565b915081905092915050565b5f82825260208201905092915050565b7f507265636f6d70696c652063616c6c206661696c6564000000000000000000005f82015250565b5f61041b6016836103d7565b9150610426826103e7565b602082019050919050565b5f6020820190508181035f8301526104488161040f565b9050919050565b5f6020820190508181035f8301526104678184610321565b90509291505056fea26469706673582212200f74a92387ae88fee4fc7c9238aa9bb6091756accf22046271bf9d976083ed3664736f6c63430008190033", 33 | "chain_id": "0x51615", 34 | "access_list": [], 35 | "transaction_type": 2, 36 | "signature": { 37 | "r": "0xe6de9d0c2690cb78e410ee6a968110d5a713de6b9f613986ff5a987d32afea31", 38 | "s": "0x4e07c4fe9b184edef8c2c7a58e7b252fc19542c11474cd428d24c9da11bb9fcb", 39 | "y_parity": true 40 | } 41 | } 42 | ], 43 | "withdrawals": null, 44 | "states": [ 45 | "0xf851808080a0a87d9bb950836582673aa0eecc0ff64aac607870637a2dd2012b8b1b31981f698080a08da6d5c36a404670c553a2c9052df7cd604f04e3863c4c7b9e0027bfd54206d680808080808080808080", 46 | "0xf8689f3067596300e542c7cd2a1c5fca601da42a2739f790ad422b4f59fba0844e61b846f8448080a0762f18853f9b095d3cbb34af725cf2ec2be8a969a7b8d4cf70b8ede38d457a5da07f6f0daf66a63b4d504fabde8e9fa491ff678bf22082d8fee03ac3064fcf7de9", 47 | "0xe2a0336b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db001", 48 | "0xf871a0335d1857aacd2be4f5447c6ce16b3d0c2c5aa79ebf97f920200333b536e86861b84ef84c80888ac7230489e80000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", 49 | "0xe2a02052222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01", 50 | "0xe2a0366cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68801", 51 | "0xf851808080808080a032d1c4a6391dd38d71841fa7d5b3e4305e5ffb91227d7b303321d575e413b8f18080808080808080a03123f9aaae9cba26fb0f2407f39e8bce12878d354fc7370ffd4e9b4979411e1780", 52 | "0xf891a08857639e3944eefb53fc927fd82a371bd6fd6ada8f9fe9cbc2b6381c1aeb5fec80a01cd100c375b88cd4909b69a1c187a22169124e1dcbf910d68d391b79814a45c080808080808080a0c5d54b915b56a888eee4e6eeb3141e778f9b674d1d322962eed900f02c29990a80808080a03340bbaeafcda3a8672eb83099231dbbfab8dae02a1e8ec2f7180538fac207e080", 53 | "0xf838a120a9144a5e7efd259b8b0d55467f4696ed47ec83317d61501b76366dbcca65ce7395946f4c950442e1af093bcff730381e63ae9171b87a", 54 | "0xf8b180a03672d4a4951dbf05a8d18c33bd880a640aeb4dc1082bc96c489e3d658659c34080a082cc85168147c333f706c2b5691598335b1cd5cdb2e9ceb2549ada975b361aba80a039229c28de6ccaf16914959cbcd0a623a25303cde5643b98741689d36bc6a3a3808080a0394b537dac70183c9ae73cdc9170d23f63e2eab2c9c0558d8168c4a9b60882a78080808080a019f31f2ec09a6c71f568934c6b0a9d12198b8f2ef22d82a3283b4bca1b8ee60a80", 55 | "0xf85180808080a0166a095be91b1f2ffc9d1a8abc0522264f67121086a4ea0b22a0a6bef07b000a808080a0aae809664601487c8ce4c127ff74497983e8183aae5a17760bd024f62f2731668080808080808080", 56 | "0xf869a03b18a0b8246d45a5a396db3b461bf4a56bd646d9274adeadb5471dd31e30574fb846f8448080a0c12326139a2ceb734a1d1042b13dd94b7a2ea2b13cbd7fa0e2944790f4f70d7da019e0db18cf25c98a25c9e8eac4c99e0653cd8c395303140b41425e9fa3f9bb65", 57 | "0xe21ba05e56e3347565c4f1aed8369789b49a5fec2bb4cfda21c1de954ecee3b91ce3c2", 58 | "0xf869a020f884cdcd164a6b73972dc6625ecb078be7e607b54459f3edfaada99be20644b846f8448080a0f83a2f0b95b00ea04100cadcd981b0cc2edbf64dd961adf01fc51c9169394880a03733510decd4cdde078e264666d17cac208f9f9e93fc0a43b02921be5cf5726f" 59 | ], 60 | "codes": [] 61 | } -------------------------------------------------------------------------------- /testdata/scroll_witness/euclid_v2/3.json: -------------------------------------------------------------------------------- 1 | { 2 | "chain_id": 333333, 3 | "header": { 4 | "parent_hash": "0x988f6cd3f93ad3c41db9940a7a50220d6c81681cfdb81de5efce45328696bfc6", 5 | "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", 6 | "miner": "0x0000000000000000000000000000000000000000", 7 | "state_root": "0x11e60df02b7b252d9c6906ff654f50d874bb6a7a93a745f97b3adf722bd5c55f", 8 | "transactions_root": "0x6b09a0de7ddf8c9d86d0d805fde0915db2a84566b6349b45d53793a2ab5139ff", 9 | "receipts_root": "0xf93aded6293c6bf0958021bb8718873038a9b36de82b7487dbad66c3cd44b828", 10 | "logs_bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 11 | "difficulty": "0x2", 12 | "number": "0x3", 13 | "gas_limit": "0x989680", 14 | "gas_used": "0x2d07d", 15 | "timestamp": "0x67b439b1", 16 | "extra_data": "0xd883050808846765746888676f312e32312e31856c696e75780000000000000078ce00d6ffc49ab9ce8c25d5b0dc4f82138020cc0f129dda6bcf869ee4ed4a275b0875bfb0fe59f48aea2898083bfd00bee75ed2b798c1df2ba343e7dee62cd801", 17 | "mix_hash": "0x0000000000000000000000000000000000000000000000000000000000000000", 18 | "nonce": "0x0000000000000000", 19 | "base_fee_per_gas": "0x2562500" 20 | }, 21 | "pre_state_root": "0xd4821c5b832756d9c64d9a259f3f37dd77d6f4dbf2c674b8ff65481644073007", 22 | "transaction": [ 23 | { 24 | "hash": "0xc73ae10fe54de6b581d394ee592c7e92a3c612269cbd95667921fa4fb9ee66c0", 25 | "nonce": "0x9", 26 | "from": "0x6f4c950442e1af093bcff730381e63ae9171b87a", 27 | "to": null, 28 | "value": "0x0", 29 | "gas": "0xf4240", 30 | "max_fee_per_gas": "0x3b9aca00", 31 | "max_priority_fee_per_gas": "0x1", 32 | "input": "0x6080604052348015600e575f80fd5b506102618061001c5f395ff3fe60806040526004361061001d575f3560e01c8063d50f6bf014610021575b5f80fd5b61003b60048036038101906100369190610147565b61003d565b005b5f8173ffffffffffffffffffffffffffffffffffffffff16346040516100629061019f565b5f6040518083038185875af1925050503d805f811461009c576040519150601f19603f3d011682016040523d82523d5f602084013e6100a1565b606091505b50509050806100e5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100dc9061020d565b60405180910390fd5b5050565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610116826100ed565b9050919050565b6101268161010c565b8114610130575f80fd5b50565b5f813590506101418161011d565b92915050565b5f6020828403121561015c5761015b6100e9565b5b5f61016984828501610133565b91505092915050565b5f81905092915050565b50565b5f61018a5f83610172565b91506101958261017c565b5f82019050919050565b5f6101a98261017f565b9150819050919050565b5f82825260208201905092915050565b7f5472616e73666572206661696c656400000000000000000000000000000000005f82015250565b5f6101f7600f836101b3565b9150610202826101c3565b602082019050919050565b5f6020820190508181035f830152610224816101eb565b905091905056fea26469706673582212202a5c055e4367f030f500411364630895f8cd9dc26305098bfa0300261421697564736f6c63430008190033", 33 | "chain_id": "0x51615", 34 | "access_list": [], 35 | "transaction_type": 2, 36 | "signature": { 37 | "r": "0x91bc4e62d35a8b4be15ec8b1549acf39536d636f2f005c346d6b6fe8bd105520", 38 | "s": "0x96471830f9884799b12f755ffaea42c872e6a4de62cf257ff0255d5e2f17494", 39 | "y_parity": false 40 | } 41 | } 42 | ], 43 | "withdrawals": null, 44 | "states": [ 45 | "0xf891a08857639e3944eefb53fc927fd82a371bd6fd6ada8f9fe9cbc2b6381c1aeb5fec80a01cd100c375b88cd4909b69a1c187a22169124e1dcbf910d68d391b79814a45c080808080808080a0c5d54b915b56a888eee4e6eeb3141e778f9b674d1d322962eed900f02c29990a80808080a03340bbaeafcda3a8672eb83099231dbbfab8dae02a1e8ec2f7180538fac207e080", 46 | "0xf869a03b18a0b8246d45a5a396db3b461bf4a56bd646d9274adeadb5471dd31e30574fb846f8448080a0c12326139a2ceb734a1d1042b13dd94b7a2ea2b13cbd7fa0e2944790f4f70d7da019e0db18cf25c98a25c9e8eac4c99e0653cd8c395303140b41425e9fa3f9bb65", 47 | "0xf8d180a05d28b6ce2914d406fcfff1b7fc694d4deb32f5ce9717d402e0b69d940bef10b080a082cc85168147c333f706c2b5691598335b1cd5cdb2e9ceb2549ada975b361aba80a0ffef228a094db71b7357fb6b79cf247e5d227069d9cc19b25f1d2d9932464210808080a0394b537dac70183c9ae73cdc9170d23f63e2eab2c9c0558d8168c4a9b60882a78080a009df5c8a1852e34c7e6ac68cbde5c0e51d453cd81e73413944d64fe8e3e464bd8080a019f31f2ec09a6c71f568934c6b0a9d12198b8f2ef22d82a3283b4bca1b8ee60a80", 48 | "0xf86fa020f884cdcd164a6b73972dc6625ecb078be7e607b54459f3edfaada99be20644b84cf84a80861352cdd2102fa0f83a2f0b95b00ea04100cadcd981b0cc2edbf64dd961adf01fc51c9169394880a03733510decd4cdde078e264666d17cac208f9f9e93fc0a43b02921be5cf5726f", 49 | "0xe2a0366cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68801", 50 | "0xf851808080808080a032d1c4a6391dd38d71841fa7d5b3e4305e5ffb91227d7b303321d575e413b8f18080808080808080a03123f9aaae9cba26fb0f2407f39e8bce12878d354fc7370ffd4e9b4979411e1780", 51 | "0xe2a02052222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01", 52 | "0xf871a0335d1857aacd2be4f5447c6ce16b3d0c2c5aa79ebf97f920200333b536e86861b84ef84c09888ac70fb1bc15efd1a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", 53 | "0xf8689f3067596300e542c7cd2a1c5fca601da42a2739f790ad422b4f59fba0844e61b846f8448080a0762f18853f9b095d3cbb34af725cf2ec2be8a969a7b8d4cf70b8ede38d457a5da07f6f0daf66a63b4d504fabde8e9fa491ff678bf22082d8fee03ac3064fcf7de9", 54 | "0xf85180808080a019dc61b261d1bd98786f812272405a4fbd36cab0012d724fa5cfe1b4eb147d57808080a0aae809664601487c8ce4c127ff74497983e8183aae5a17760bd024f62f2731668080808080808080", 55 | "0xf838a120a9144a5e7efd259b8b0d55467f4696ed47ec83317d61501b76366dbcca65ce7395946f4c950442e1af093bcff730381e63ae9171b87a", 56 | "0xe21ba05e56e3347565c4f1aed8369789b49a5fec2bb4cfda21c1de954ecee3b91ce3c2", 57 | "0xe2a0336b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db001", 58 | "0xf851808080a0a87d9bb950836582673aa0eecc0ff64aac607870637a2dd2012b8b1b31981f698080a08da6d5c36a404670c553a2c9052df7cd604f04e3863c4c7b9e0027bfd54206d680808080808080808080" 59 | ], 60 | "codes": [] 61 | } -------------------------------------------------------------------------------- /testdata/scroll_witness/euclid_v2/4.json: -------------------------------------------------------------------------------- 1 | { 2 | "chain_id": 333333, 3 | "header": { 4 | "parent_hash": "0x40d7ad10fced69bb6db57042de3107af9b69f20ad286760aa223c83174f169af", 5 | "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", 6 | "miner": "0x0000000000000000000000000000000000000000", 7 | "state_root": "0xaf6696afb2e11052490051f0f9f6444be6e9f5bb82beb3c3dae846cfa59ed6e0", 8 | "transactions_root": "0xd469da5762f40decf92bc0639a1bcc96bba8d4c1cc7df6530cb6dfd88927893b", 9 | "receipts_root": "0x036c7d20420edbce24b5062148bce80563b48fd4532fb1c068b2c96a53117019", 10 | "logs_bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 11 | "difficulty": "0x2", 12 | "number": "0x4", 13 | "gas_limit": "0x989680", 14 | "gas_used": "0x8fc0", 15 | "timestamp": "0x67b439b4", 16 | "extra_data": "0xd883050808846765746888676f312e32312e31856c696e75780000000000000080783f6d8048f3dee0d6729d4ab7c471eab6c20704e779fafea5156b453826e53877abfc6c0c4f417feda18bf95d4d52f5bab67c2139495a09a528112b62ce4600", 17 | "mix_hash": "0x0000000000000000000000000000000000000000000000000000000000000000", 18 | "nonce": "0x0000000000000000", 19 | "base_fee_per_gas": "0x2562500" 20 | }, 21 | "pre_state_root": "0x11e60df02b7b252d9c6906ff654f50d874bb6a7a93a745f97b3adf722bd5c55f", 22 | "transaction": [ 23 | { 24 | "hash": "0x2b8f1c23da0ea6860baeabe4edbe1da9dd13ad92955f72ebe6c5c0c332d77c22", 25 | "nonce": "0xa", 26 | "from": "0x6f4c950442e1af093bcff730381e63ae9171b87a", 27 | "to": "0x0000000000000000000000000000000000000000", 28 | "value": "0x0", 29 | "gas": "0x186a0", 30 | "max_fee_per_gas": "0x3b9aca00", 31 | "max_priority_fee_per_gas": "0x1", 32 | "input": "0x", 33 | "chain_id": "0x51615", 34 | "access_list": [], 35 | "authorization_list": [ 36 | { 37 | "yParity": "0x0", 38 | "r": "0x3947f12c751a62a1aaa44e44637935d15968b9044ce55ed91ee67afbc5e82326", 39 | "s": "0x50ea6a71096a4e98041505c02f90b19fce59ab5c4ff6f58810e42cb54da0f4b9", 40 | "inner": { 41 | "chain_id": "0x51615", 42 | "address": "0x6f4c950442e1af093bcff730381e63ae9171b87a", 43 | "nonce": "0xb" 44 | } 45 | } 46 | ], 47 | "transaction_type": 4, 48 | "signature": { 49 | "r": "0x97430388f4eeb5a0ddc66fb50950acab5b3b769491631eebb7adfd2e44316526", 50 | "s": "0x1f8a7a95cc723657bcc06ce2a19f5cab3744875b9586f133f9375585b14ba8cd", 51 | "y_parity": false 52 | } 53 | } 54 | ], 55 | "withdrawals": null, 56 | "states": [ 57 | "0xf891a08857639e3944eefb53fc927fd82a371bd6fd6ada8f9fe9cbc2b6381c1aeb5fec80a01cd100c375b88cd4909b69a1c187a22169124e1dcbf910d68d391b79814a45c080808080808080a0c5d54b915b56a888eee4e6eeb3141e778f9b674d1d322962eed900f02c29990a80808080a03340bbaeafcda3a8672eb83099231dbbfab8dae02a1e8ec2f7180538fac207e080", 58 | "0xf869a03b18a0b8246d45a5a396db3b461bf4a56bd646d9274adeadb5471dd31e30574fb846f8448080a0c12326139a2ceb734a1d1042b13dd94b7a2ea2b13cbd7fa0e2944790f4f70d7da019e0db18cf25c98a25c9e8eac4c99e0653cd8c395303140b41425e9fa3f9bb65", 59 | "0xf851808080808080a032d1c4a6391dd38d71841fa7d5b3e4305e5ffb91227d7b303321d575e413b8f18080808080808080a03123f9aaae9cba26fb0f2407f39e8bce12878d354fc7370ffd4e9b4979411e1780", 60 | "0xe213a0c851999819d92bba3ba3364f495a62d72dace74d9a881b148a93ace017ebbebf", 61 | "0xf851808080a0a87d9bb950836582673aa0eecc0ff64aac607870637a2dd2012b8b1b31981f698080a08da6d5c36a404670c553a2c9052df7cd604f04e3863c4c7b9e0027bfd54206d680808080808080808080", 62 | "0xf86fa020f884cdcd164a6b73972dc6625ecb078be7e607b54459f3edfaada99be20644b84cf84a808619e639f4f1aca0f83a2f0b95b00ea04100cadcd981b0cc2edbf64dd961adf01fc51c9169394880a03733510decd4cdde078e264666d17cac208f9f9e93fc0a43b02921be5cf5726f", 63 | "0xf8518080808080a0b423fa34584689181df9dd41a74e97402410bcf6d17fe23456bfa1862f3ee9d580808080808080a0287664cf97c16c4b122747303c68961cc96ae5c178066dd9e57d7ee84042913b808080", 64 | "0xf85180808080a0380d4e7e4659ec4b903d0473dccc9996bf740740c3f035d0cbc4599a2e7be9c7808080a0aae809664601487c8ce4c127ff74497983e8183aae5a17760bd024f62f2731668080808080808080", 65 | "0xf8d180a0428504e8c333fe5658812263457c13897928143aac68da44b2a5f0b46cb9c5f480a082cc85168147c333f706c2b5691598335b1cd5cdb2e9ceb2549ada975b361aba80a000fe4466785b10d246d3cf8a8607fa0e7c2c1df44a2cf9dba1c28b6224313ec5808080a0394b537dac70183c9ae73cdc9170d23f63e2eab2c9c0558d8168c4a9b60882a78080a009df5c8a1852e34c7e6ac68cbde5c0e51d453cd81e73413944d64fe8e3e464bd8080a019f31f2ec09a6c71f568934c6b0a9d12198b8f2ef22d82a3283b4bca1b8ee60a80", 66 | "0xe2a02052222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01", 67 | "0xf838a120a9144a5e7efd259b8b0d55467f4696ed47ec83317d61501b76366dbcca65ce7395946f4c950442e1af093bcff730381e63ae9171b87a", 68 | "0xf8709f3d1857aacd2be4f5447c6ce16b3d0c2c5aa79ebf97f920200333b536e86861b84ef84c0a888ac7091e4ff30e54a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", 69 | "0xe21ba05e56e3347565c4f1aed8369789b49a5fec2bb4cfda21c1de954ecee3b91ce3c2", 70 | "0xe2a0336b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db001", 71 | "0xf8689f3067596300e542c7cd2a1c5fca601da42a2739f790ad422b4f59fba0844e61b846f8448080a0762f18853f9b095d3cbb34af725cf2ec2be8a969a7b8d4cf70b8ede38d457a5da07f6f0daf66a63b4d504fabde8e9fa491ff678bf22082d8fee03ac3064fcf7de9", 72 | "0xe2a0366cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68801" 73 | ], 74 | "codes": [] 75 | } -------------------------------------------------------------------------------- /testdata/scroll_witness/euclid_v2/5.json: -------------------------------------------------------------------------------- 1 | { 2 | "chain_id": 333333, 3 | "header": { 4 | "parent_hash": "0x1bc1607664bb1e787970ac77936373cbbdddb4c36ca6cf3c0386fd65f4270fde", 5 | "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", 6 | "miner": "0x0000000000000000000000000000000000000000", 7 | "state_root": "0xe4e38a43c38d355f97069120b2c409a96bf25e46adf848b8bbe638975afe810b", 8 | "transactions_root": "0x7bf20d6073fe075609f461add78ca6e8290afa47d86be85b039599d91171c4d1", 9 | "receipts_root": "0x036c7d20420edbce24b5062148bce80563b48fd4532fb1c068b2c96a53117019", 10 | "logs_bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 11 | "difficulty": "0x2", 12 | "number": "0x5", 13 | "gas_limit": "0x989680", 14 | "gas_used": "0x8fc0", 15 | "timestamp": "0x67b439b7", 16 | "extra_data": "0xd883050808846765746888676f312e32312e31856c696e75780000000000000059d328056c7d3e59a110428f642ba74b08cf06d6e9f988e2a9f5bc788b99a09312201362c632d36781bd9db0f9fa8abbb7f0ae6152dbd32deeb6a56ee61d116800", 17 | "mix_hash": "0x0000000000000000000000000000000000000000000000000000000000000000", 18 | "nonce": "0x0000000000000000", 19 | "base_fee_per_gas": "0x2562500" 20 | }, 21 | "pre_state_root": "0xaf6696afb2e11052490051f0f9f6444be6e9f5bb82beb3c3dae846cfa59ed6e0", 22 | "transaction": [ 23 | { 24 | "hash": "0xb545c9e76a493d88c1133d60cd1cd3d5a7bb58196fd8186f00bcdfbc534efbef", 25 | "nonce": "0xc", 26 | "from": "0x6f4c950442e1af093bcff730381e63ae9171b87a", 27 | "to": "0x0000000000000000000000000000000000000000", 28 | "value": "0x0", 29 | "gas": "0x186a0", 30 | "max_fee_per_gas": "0x3b9aca00", 31 | "max_priority_fee_per_gas": "0x1", 32 | "input": "0x", 33 | "chain_id": "0x51615", 34 | "access_list": [], 35 | "authorization_list": [ 36 | { 37 | "yParity": "0x1", 38 | "r": "0x4ccb90d1b3df98057fd248b0c6625a5d0deba57bb7ee5bd9d2080eb2c4979da6", 39 | "s": "0x38d124cd9051080ee3d0ddd2aedf4427afe92730e253b7e3ac8cae16b69e3a94", 40 | "inner": { 41 | "chain_id": "0x51615", 42 | "address": "0x1111111111111111111111111111111111111111", 43 | "nonce": "0xd" 44 | } 45 | } 46 | ], 47 | "transaction_type": 4, 48 | "signature": { 49 | "r": "0x616ed4ee45177a8c63447f401d239947da5877afbc4d49144d09c58d2136f3bc", 50 | "s": "0x29e168f0595b7712cf4b533fcc435480879a3d3988d2a0699a20fcd3aa58a0dc", 51 | "y_parity": true 52 | } 53 | } 54 | ], 55 | "withdrawals": null, 56 | "states": [ 57 | "0xf851808080808080a032d1c4a6391dd38d71841fa7d5b3e4305e5ffb91227d7b303321d575e413b8f18080808080808080a03123f9aaae9cba26fb0f2407f39e8bce12878d354fc7370ffd4e9b4979411e1780", 58 | "0xf891a08857639e3944eefb53fc927fd82a371bd6fd6ada8f9fe9cbc2b6381c1aeb5fec80a01cd100c375b88cd4909b69a1c187a22169124e1dcbf910d68d391b79814a45c080808080808080a0c5d54b915b56a888eee4e6eeb3141e778f9b674d1d322962eed900f02c29990a80808080a03340bbaeafcda3a8672eb83099231dbbfab8dae02a1e8ec2f7180538fac207e080", 59 | "0xf8518080808080a0bf4b905dfa4f1ca908ec7dec42d9a8c611d52fb85b6a769f3fea3caffec99c9c80808080808080a0287664cf97c16c4b122747303c68961cc96ae5c178066dd9e57d7ee84042913b808080", 60 | "0xe2a02052222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01", 61 | "0xf869a03b18a0b8246d45a5a396db3b461bf4a56bd646d9274adeadb5471dd31e30574fb846f8448080a0c12326139a2ceb734a1d1042b13dd94b7a2ea2b13cbd7fa0e2944790f4f70d7da019e0db18cf25c98a25c9e8eac4c99e0653cd8c395303140b41425e9fa3f9bb65", 62 | "0xf86fa020f884cdcd164a6b73972dc6625ecb078be7e607b54459f3edfaada99be20644b84cf84a80861b36193c416ca0f83a2f0b95b00ea04100cadcd981b0cc2edbf64dd961adf01fc51c9169394880a03733510decd4cdde078e264666d17cac208f9f9e93fc0a43b02921be5cf5726f", 63 | "0xf8709f3d1857aacd2be4f5447c6ce16b3d0c2c5aa79ebf97f920200333b536e86861b84ef84c0c888ac707ce70abbe94a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0e482d7d56e32d9788cf0465f4138b9afc972a01a50565e6044d04c2185b191eb", 64 | "0xe213a02b55ec4541afd2ef49fd27ef6d1ac9128dab604400aa725477e54bd08b58ad6c", 65 | "0xf838a120a9144a5e7efd259b8b0d55467f4696ed47ec83317d61501b76366dbcca65ce7395946f4c950442e1af093bcff730381e63ae9171b87a", 66 | "0xe2a0366cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68801", 67 | "0xe21ba05e56e3347565c4f1aed8369789b49a5fec2bb4cfda21c1de954ecee3b91ce3c2", 68 | "0xf8d180a09a045ca08da6d06e71dacf08b3c77f28f722d4111155ca3e4b6c97041ee3d5e180a082cc85168147c333f706c2b5691598335b1cd5cdb2e9ceb2549ada975b361aba80a09436dad4617ee25e911f93c7843e1905035f7a2d25a3ef42ab77fbc7931f31d1808080a0394b537dac70183c9ae73cdc9170d23f63e2eab2c9c0558d8168c4a9b60882a78080a009df5c8a1852e34c7e6ac68cbde5c0e51d453cd81e73413944d64fe8e3e464bd8080a019f31f2ec09a6c71f568934c6b0a9d12198b8f2ef22d82a3283b4bca1b8ee60a80", 69 | "0xf8689f3067596300e542c7cd2a1c5fca601da42a2739f790ad422b4f59fba0844e61b846f8448080a0762f18853f9b095d3cbb34af725cf2ec2be8a969a7b8d4cf70b8ede38d457a5da07f6f0daf66a63b4d504fabde8e9fa491ff678bf22082d8fee03ac3064fcf7de9", 70 | "0xf851808080a0a87d9bb950836582673aa0eecc0ff64aac607870637a2dd2012b8b1b31981f698080a08da6d5c36a404670c553a2c9052df7cd604f04e3863c4c7b9e0027bfd54206d680808080808080808080", 71 | "0xe2a0336b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db001", 72 | "0xf85180808080a0560612dd5084534955c8acd6735b1a39404731685411196bb093f2fd280a0cd6808080a0aae809664601487c8ce4c127ff74497983e8183aae5a17760bd024f62f2731668080808080808080" 73 | ], 74 | "codes": [ 75 | "0xef01006f4c950442e1af093bcff730381e63ae9171b87a" 76 | ] 77 | } -------------------------------------------------------------------------------- /testdata/scroll_witness/euclid_v2/6.json: -------------------------------------------------------------------------------- 1 | { 2 | "chain_id": 333333, 3 | "header": { 4 | "parent_hash": "0x92486020a876b003bca9c9c256deb09bdca89e81b4f7044dcdf75ce6a5578c9d", 5 | "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", 6 | "miner": "0x0000000000000000000000000000000000000000", 7 | "state_root": "0x2f61833e1f8556d6673abfa20909fc3bfd7767ef49e864a1ef3c7ccaf24d931c", 8 | "transactions_root": "0xe841ea3340e758ae5ac3fd5875f2078468e0dbdd0439d6830c530ecb3d6bb567", 9 | "receipts_root": "0x036c7d20420edbce24b5062148bce80563b48fd4532fb1c068b2c96a53117019", 10 | "logs_bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 11 | "difficulty": "0x2", 12 | "number": "0x6", 13 | "gas_limit": "0x989680", 14 | "gas_used": "0x8fc0", 15 | "timestamp": "0x67b439ba", 16 | "extra_data": "0xd883050808846765746888676f312e32312e31856c696e757800000000000000c69bd4414e3268938c2ff735ea414010ce2ff18bfcb965fb925396dea8879ca60d35997de317218289e26ba1a129d245fdbf12d930c50e1346a511e348db07ab01", 17 | "mix_hash": "0x0000000000000000000000000000000000000000000000000000000000000000", 18 | "nonce": "0x0000000000000000", 19 | "base_fee_per_gas": "0x2562500" 20 | }, 21 | "pre_state_root": "0xe4e38a43c38d355f97069120b2c409a96bf25e46adf848b8bbe638975afe810b", 22 | "transaction": [ 23 | { 24 | "hash": "0xa2444b9a2c325c612c757d881666c2cfc7573092b585b4b97972340f111fe388", 25 | "nonce": "0xe", 26 | "from": "0x6f4c950442e1af093bcff730381e63ae9171b87a", 27 | "to": "0x0000000000000000000000000000000000000000", 28 | "value": "0x0", 29 | "gas": "0x186a0", 30 | "max_fee_per_gas": "0x3b9aca00", 31 | "max_priority_fee_per_gas": "0x1", 32 | "input": "0x", 33 | "chain_id": "0x51615", 34 | "access_list": [], 35 | "authorization_list": [ 36 | { 37 | "yParity": "0x0", 38 | "r": "0x859d91855104b6d36e2fe1013ddf265a7fc13e3ee9e144b00ec79d54da7df575", 39 | "s": "0x6eb518f81d06f041e2e6b7ce9b5adf845319f946ea20b54cbb973e959c460dd1", 40 | "inner": { 41 | "chain_id": "0x51615", 42 | "address": "0xd4b75dfda0a9ab5bbcd7969dafc0bc19589157af", 43 | "nonce": "0xf" 44 | } 45 | } 46 | ], 47 | "transaction_type": 4, 48 | "signature": { 49 | "r": "0xf1d8199bdf49e2e9e8b562d14c18edfe4c38c80d28637f9452841c18b75e5426", 50 | "s": "0x3672d35f23d51f442d327fa0e8dc85dd1a8b6984b4af44e843ca44333e599c22", 51 | "y_parity": true 52 | } 53 | } 54 | ], 55 | "withdrawals": null, 56 | "states": [ 57 | "0xf8d180a0f9bfe1a1baf5e9571668baca8470a04ffc78da2fc455ef19f0afbf775fa1fbf780a082cc85168147c333f706c2b5691598335b1cd5cdb2e9ceb2549ada975b361aba80a0d559dd4bc5accfbecf0134d5176e995c960f4faf12dad846f6e8a7a0d55ef945808080a0394b537dac70183c9ae73cdc9170d23f63e2eab2c9c0558d8168c4a9b60882a78080a009df5c8a1852e34c7e6ac68cbde5c0e51d453cd81e73413944d64fe8e3e464bd8080a019f31f2ec09a6c71f568934c6b0a9d12198b8f2ef22d82a3283b4bca1b8ee60a80", 58 | "0xe21ba05e56e3347565c4f1aed8369789b49a5fec2bb4cfda21c1de954ecee3b91ce3c2", 59 | "0xf8709f3d1857aacd2be4f5447c6ce16b3d0c2c5aa79ebf97f920200333b536e86861b84ef84c0e888ac7067e91646ed4a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0dfe7c7677e3d245aaff2e3e94db902f5fb37475eb611959c71ee641508dcf49a", 60 | "0xf869a03b18a0b8246d45a5a396db3b461bf4a56bd646d9274adeadb5471dd31e30574fb846f8448080a0c12326139a2ceb734a1d1042b13dd94b7a2ea2b13cbd7fa0e2944790f4f70d7da019e0db18cf25c98a25c9e8eac4c99e0653cd8c395303140b41425e9fa3f9bb65", 61 | "0xe2a0336b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db001", 62 | "0xf8689f3067596300e542c7cd2a1c5fca601da42a2739f790ad422b4f59fba0844e61b846f8448080a0762f18853f9b095d3cbb34af725cf2ec2be8a969a7b8d4cf70b8ede38d457a5da07f6f0daf66a63b4d504fabde8e9fa491ff678bf22082d8fee03ac3064fcf7de9", 63 | "0xf838a120a9144a5e7efd259b8b0d55467f4696ed47ec83317d61501b76366dbcca65ce7395946f4c950442e1af093bcff730381e63ae9171b87a", 64 | "0xf8518080808080a0799e287824a99ba18c024c8b38f2b2602c190a0f770afb90dbb3b84dfd4452ad80808080808080a0287664cf97c16c4b122747303c68961cc96ae5c178066dd9e57d7ee84042913b808080", 65 | "0xe2a0366cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68801", 66 | "0xf851808080808080a032d1c4a6391dd38d71841fa7d5b3e4305e5ffb91227d7b303321d575e413b8f18080808080808080a03123f9aaae9cba26fb0f2407f39e8bce12878d354fc7370ffd4e9b4979411e1780", 67 | "0xe213a02bd9b8c9ad8ed8c08efa642fd60e6846f2d7cbb1cb02dd1a1f040a542d5b2a3e", 68 | "0xe2a02052222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01", 69 | "0xf891a08857639e3944eefb53fc927fd82a371bd6fd6ada8f9fe9cbc2b6381c1aeb5fec80a01cd100c375b88cd4909b69a1c187a22169124e1dcbf910d68d391b79814a45c080808080808080a0c5d54b915b56a888eee4e6eeb3141e778f9b674d1d322962eed900f02c29990a80808080a03340bbaeafcda3a8672eb83099231dbbfab8dae02a1e8ec2f7180538fac207e080", 70 | "0xf86fa020f884cdcd164a6b73972dc6625ecb078be7e607b54459f3edfaada99be20644b84cf84a80861c85f883912ca0f83a2f0b95b00ea04100cadcd981b0cc2edbf64dd961adf01fc51c9169394880a03733510decd4cdde078e264666d17cac208f9f9e93fc0a43b02921be5cf5726f", 71 | "0xf85180808080a05cb2d028c7a81890a5e993e5a8142f995cd7cba4339dc6b2e619b5dd206fd644808080a0aae809664601487c8ce4c127ff74497983e8183aae5a17760bd024f62f2731668080808080808080", 72 | "0xf851808080a0a87d9bb950836582673aa0eecc0ff64aac607870637a2dd2012b8b1b31981f698080a08da6d5c36a404670c553a2c9052df7cd604f04e3863c4c7b9e0027bfd54206d680808080808080808080" 73 | ], 74 | "codes": [ 75 | "0xef01001111111111111111111111111111111111111111" 76 | ] 77 | } -------------------------------------------------------------------------------- /testdata/scroll_witness/euclid_v2/7.json: -------------------------------------------------------------------------------- 1 | { 2 | "chain_id": 333333, 3 | "header": { 4 | "parent_hash": "0x6396eff3d28aa09b1a324f2749b0752fc9114a0e24800e483cd143e360d8d014", 5 | "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", 6 | "miner": "0x0000000000000000000000000000000000000000", 7 | "state_root": "0xcc5c46a57e4568bc592a1baee72972c853eb27301620e66b1e7cf01a08dbff84", 8 | "transactions_root": "0xaebcc998e2228dfa6b02a550348ca26f810e6919803a86459bb1af80a2ad4eec", 9 | "receipts_root": "0xf6184b8c39b1fdaadf4846ea0cc2efcb2d5341c37fecaeb7f6deeb20ac3c77ff", 10 | "logs_bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 11 | "difficulty": "0x2", 12 | "number": "0x7", 13 | "gas_limit": "0x989680", 14 | "gas_used": "0xdc76", 15 | "timestamp": "0x67b439bd", 16 | "extra_data": "0xd883050808846765746888676f312e32312e31856c696e75780000000000000045f7a7faf750d9fc098284da995a92184380ee663a0121ebbbc8d3455303159e58a890033cfc065bfaca0d913e829c0db7221bf06b0f0ace09e102606a2882f700", 17 | "mix_hash": "0x0000000000000000000000000000000000000000000000000000000000000000", 18 | "nonce": "0x0000000000000000", 19 | "base_fee_per_gas": "0x2562500" 20 | }, 21 | "pre_state_root": "0x2f61833e1f8556d6673abfa20909fc3bfd7767ef49e864a1ef3c7ccaf24d931c", 22 | "transaction": [ 23 | { 24 | "hash": "0x2ba82e9ab001bb7c76a907d11e9f00613cde56d1defff9fe80af51072256b9a6", 25 | "nonce": "0x10", 26 | "from": "0x6f4c950442e1af093bcff730381e63ae9171b87a", 27 | "to": "0x6f4c950442e1af093bcff730381e63ae9171b87a", 28 | "value": "0x2386f26fc10000", 29 | "gas": "0x186a0", 30 | "max_fee_per_gas": "0x3b9aca00", 31 | "max_priority_fee_per_gas": "0x1", 32 | "input": "0xd50f6bf00000000000000000000000001111111111111111111111111111111111111111", 33 | "chain_id": "0x51615", 34 | "access_list": [], 35 | "transaction_type": 2, 36 | "signature": { 37 | "r": "0x2bdc8c5db0c98ecd677847152493512fe702fec10ef2d2fefad9a9b39f7f2869", 38 | "s": "0x35803ef4be52ed8247d25ef46fd25774f029129ab159ace266a70b593780b0a0", 39 | "y_parity": true 40 | } 41 | } 42 | ], 43 | "withdrawals": null, 44 | "states": [ 45 | "0xf8d180a0bac92e793e488b9a33d131006ceff80adb8d61c9a996756b3411277bf0580b8180a082cc85168147c333f706c2b5691598335b1cd5cdb2e9ceb2549ada975b361aba80a0ee7ec727963c098355044f5b0f0726623b6f3e084821b36b06c55fb2b9991a75808080a0394b537dac70183c9ae73cdc9170d23f63e2eab2c9c0558d8168c4a9b60882a78080a009df5c8a1852e34c7e6ac68cbde5c0e51d453cd81e73413944d64fe8e3e464bd8080a019f31f2ec09a6c71f568934c6b0a9d12198b8f2ef22d82a3283b4bca1b8ee60a80", 46 | "0xe21ba05e56e3347565c4f1aed8369789b49a5fec2bb4cfda21c1de954ecee3b91ce3c2", 47 | "0xe2a0366cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68801", 48 | "0xe213a0d692d76b80d37a68aa8c081c9c763dc4fd87edd3924957e62f536f672f94bdc4", 49 | "0xf838a120a9144a5e7efd259b8b0d55467f4696ed47ec83317d61501b76366dbcca65ce7395946f4c950442e1af093bcff730381e63ae9171b87a", 50 | "0xe2a02052222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01", 51 | "0xf851808080808080a032d1c4a6391dd38d71841fa7d5b3e4305e5ffb91227d7b303321d575e413b8f18080808080808080a03123f9aaae9cba26fb0f2407f39e8bce12878d354fc7370ffd4e9b4979411e1780", 52 | "0xf8709f3d1857aacd2be4f5447c6ce16b3d0c2c5aa79ebf97f920200333b536e86861b84ef84c10888ac7052eb21d1f14a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0192bc833fc6393d5aea4ab5ae4bbdab138f2046c429cf7e97a7fce370b0b996e", 53 | "0xf86fa020f884cdcd164a6b73972dc6625ecb078be7e607b54459f3edfaada99be20644b84cf84a80861dd5d7cae0eca0f83a2f0b95b00ea04100cadcd981b0cc2edbf64dd961adf01fc51c9169394880a03733510decd4cdde078e264666d17cac208f9f9e93fc0a43b02921be5cf5726f", 54 | "0xf869a03b18a0b8246d45a5a396db3b461bf4a56bd646d9274adeadb5471dd31e30574fb846f8448080a0c12326139a2ceb734a1d1042b13dd94b7a2ea2b13cbd7fa0e2944790f4f70d7da019e0db18cf25c98a25c9e8eac4c99e0653cd8c395303140b41425e9fa3f9bb65", 55 | "0xf85180808080a0828ddc2cb308ace45200a84287af6356e1b07e4a3609cb4d7303af91e6267ae5808080a0aae809664601487c8ce4c127ff74497983e8183aae5a17760bd024f62f2731668080808080808080", 56 | "0xf8689f3067596300e542c7cd2a1c5fca601da42a2739f790ad422b4f59fba0844e61b846f8448080a0762f18853f9b095d3cbb34af725cf2ec2be8a969a7b8d4cf70b8ede38d457a5da07f6f0daf66a63b4d504fabde8e9fa491ff678bf22082d8fee03ac3064fcf7de9", 57 | "0xe2a0336b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db001", 58 | "0xf8689f39c0befd63dff0c2b7666f97f3d6a7387110e6242db6c5b6c2ac1f4cbbe325b846f8440180a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a04c42a7df4f05ad0bdc4a9d78393fc1b4dfd68da450ecc92ea5ecf5a2551059ef", 59 | "0xf8518080808080a0e8e407580651f6288e717ddce118218a03ebd65ccd1c6e2cf9edb4fc9f2b190380808080808080a0287664cf97c16c4b122747303c68961cc96ae5c178066dd9e57d7ee84042913b808080", 60 | "0xf891a08857639e3944eefb53fc927fd82a371bd6fd6ada8f9fe9cbc2b6381c1aeb5fec80a01cd100c375b88cd4909b69a1c187a22169124e1dcbf910d68d391b79814a45c080808080808080a0c5d54b915b56a888eee4e6eeb3141e778f9b674d1d322962eed900f02c29990a80808080a03340bbaeafcda3a8672eb83099231dbbfab8dae02a1e8ec2f7180538fac207e080", 61 | "0xf851808080a0a87d9bb950836582673aa0eecc0ff64aac607870637a2dd2012b8b1b31981f698080a08da6d5c36a404670c553a2c9052df7cd604f04e3863c4c7b9e0027bfd54206d680808080808080808080" 62 | ], 63 | "codes": [ 64 | "0xef0100d4b75dfda0a9ab5bbcd7969dafc0bc19589157af", 65 | "0x60806040526004361061001d575f3560e01c8063d50f6bf014610021575b5f80fd5b61003b60048036038101906100369190610147565b61003d565b005b5f8173ffffffffffffffffffffffffffffffffffffffff16346040516100629061019f565b5f6040518083038185875af1925050503d805f811461009c576040519150601f19603f3d011682016040523d82523d5f602084013e6100a1565b606091505b50509050806100e5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100dc9061020d565b60405180910390fd5b5050565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610116826100ed565b9050919050565b6101268161010c565b8114610130575f80fd5b50565b5f813590506101418161011d565b92915050565b5f6020828403121561015c5761015b6100e9565b5b5f61016984828501610133565b91505092915050565b5f81905092915050565b50565b5f61018a5f83610172565b91506101958261017c565b5f82019050919050565b5f6101a98261017f565b9150819050919050565b5f82825260208201905092915050565b7f5472616e73666572206661696c656400000000000000000000000000000000005f82015250565b5f6101f7600f836101b3565b9150610202826101c3565b602082019050919050565b5f6020820190508181035f830152610224816101eb565b905091905056fea26469706673582212202a5c055e4367f030f500411364630895f8cd9dc26305098bfa0300261421697564736f6c63430008190033" 66 | ] 67 | } -------------------------------------------------------------------------------- /testdata/scroll_witness/euclid_v2/8.json: -------------------------------------------------------------------------------- 1 | { 2 | "chain_id": 333333, 3 | "header": { 4 | "parent_hash": "0x96d68d553da5389cde05e0af0e96948acbadeb9a2577d47e2dbf48b844d40d9e", 5 | "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", 6 | "miner": "0x0000000000000000000000000000000000000000", 7 | "state_root": "0xd7e90d5d7ac719823498cc515b48909e666d3ad75aba94c325d79a6e8a98c2e3", 8 | "transactions_root": "0x6756fb65d7abb21442c291b3017baba716578b54b39fd12005aec8f1973e5a19", 9 | "receipts_root": "0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa", 10 | "logs_bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 11 | "difficulty": "0x2", 12 | "number": "0x8", 13 | "gas_limit": "0x989680", 14 | "gas_used": "0x5208", 15 | "timestamp": "0x67b439c0", 16 | "extra_data": "0xd883050808846765746888676f312e32312e31856c696e75780000000000000025bf531f8ea145d3f81321c1c6923fe02dcc3a29633699955561d9b868e15bfd29af59d18bb1cd9df436d43f266a7c81dc958377a3abe32df4764ca3230774dc00", 17 | "mix_hash": "0x0000000000000000000000000000000000000000000000000000000000000000", 18 | "nonce": "0x0000000000000000", 19 | "base_fee_per_gas": "0x2562500" 20 | }, 21 | "pre_state_root": "0xcc5c46a57e4568bc592a1baee72972c853eb27301620e66b1e7cf01a08dbff84", 22 | "transaction": [ 23 | { 24 | "hash": "0xe50eb713311a6af1c875b07e1bbb6c17765ad07fb0110bf15864b1b3dc9fd72b", 25 | "nonce": "0x11", 26 | "from": "0x6f4c950442e1af093bcff730381e63ae9171b87a", 27 | "to": "0x1111111111111111111111111111111111111111", 28 | "value": "0x11c37937e08000", 29 | "gas": "0x186a0", 30 | "max_fee_per_gas": "0x3b9aca00", 31 | "max_priority_fee_per_gas": "0x1", 32 | "input": "0x", 33 | "chain_id": "0x51615", 34 | "access_list": [], 35 | "transaction_type": 2, 36 | "signature": { 37 | "r": "0x15c0b59f613d197d01f67cee9ac2300f7ad624b73dac16677aa87b6b2bb4129b", 38 | "s": "0x38a580d316edc8f28f6e9512f4f355b2dbaa0863406e39d1e911576be1d049b7", 39 | "y_parity": false 40 | } 41 | } 42 | ], 43 | "withdrawals": null, 44 | "states": [ 45 | "0xe2a02052222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01", 46 | "0xf8518080808080a0bb3fb5b441e3d83b04d7e021e6a84a3710dd6b031a91df7ff8ef59f50505137780808080808080a0287664cf97c16c4b122747303c68961cc96ae5c178066dd9e57d7ee84042913b808080", 47 | "0xe2a0366cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68801", 48 | "0xf851808080808080a032d1c4a6391dd38d71841fa7d5b3e4305e5ffb91227d7b303321d575e413b8f18080808080808080a03123f9aaae9cba26fb0f2407f39e8bce12878d354fc7370ffd4e9b4979411e1780", 49 | "0xf869a03b18a0b8246d45a5a396db3b461bf4a56bd646d9274adeadb5471dd31e30574fb846f8448080a0c12326139a2ceb734a1d1042b13dd94b7a2ea2b13cbd7fa0e2944790f4f70d7da019e0db18cf25c98a25c9e8eac4c99e0653cd8c395303140b41425e9fa3f9bb65", 50 | "0xf86fa020f884cdcd164a6b73972dc6625ecb078be7e607b54459f3edfaada99be20644b84cf84a80861fd8f34ccb62a0f83a2f0b95b00ea04100cadcd981b0cc2edbf64dd961adf01fc51c9169394880a03733510decd4cdde078e264666d17cac208f9f9e93fc0a43b02921be5cf5726f", 51 | "0xf851808080a0a87d9bb950836582673aa0eecc0ff64aac607870637a2dd2012b8b1b31981f698080a08da6d5c36a404670c553a2c9052df7cd604f04e3863c4c7b9e0027bfd54206d680808080808080808080", 52 | "0xf891a08857639e3944eefb53fc927fd82a371bd6fd6ada8f9fe9cbc2b6381c1aeb5fec80a01cd100c375b88cd4909b69a1c187a22169124e1dcbf910d68d391b79814a45c080808080808080a0c5d54b915b56a888eee4e6eeb3141e778f9b674d1d322962eed900f02c29990a80808080a03340bbaeafcda3a8672eb83099231dbbfab8dae02a1e8ec2f7180538fac207e080", 53 | "0xf838a120a9144a5e7efd259b8b0d55467f4696ed47ec83317d61501b76366dbcca65ce7395946f4c950442e1af093bcff730381e63ae9171b87a", 54 | "0xe213a04b7b590e37404d4383746cffe401a6e596fe88d52eb7db781b82b364c1f5c1c1", 55 | "0xe2a0336b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db001", 56 | "0xf85180808080a00d81a206e1dc598ace114c1b8bd11fa0a952a425d1893e1fccc1afa3a653b34d808080a0aae809664601487c8ce4c127ff74497983e8183aae5a17760bd024f62f2731668080808080808080", 57 | "0xf8709f3d1857aacd2be4f5447c6ce16b3d0c2c5aa79ebf97f920200333b536e86861b84ef84c11888aa37c3926da349ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0192bc833fc6393d5aea4ab5ae4bbdab138f2046c429cf7e97a7fce370b0b996e", 58 | "0xf870a032c07404b8c1df4c46226425cac68c28d27a766bbddce62309f36724839b22c0b84df84b80872386f26fc10000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", 59 | "0xe21ba05e56e3347565c4f1aed8369789b49a5fec2bb4cfda21c1de954ecee3b91ce3c2", 60 | "0xf8f180a056e11ed5eb137aeac85e717c735be4848b55bc2bbe61a14a9241d0667758185b80a082cc85168147c333f706c2b5691598335b1cd5cdb2e9ceb2549ada975b361aba80a0bd5630193c5190dd8610f2be3f60089cbc850cda50970d6c136b0ca5cb6c7886808080a0394b537dac70183c9ae73cdc9170d23f63e2eab2c9c0558d8168c4a9b60882a78080a009df5c8a1852e34c7e6ac68cbde5c0e51d453cd81e73413944d64fe8e3e464bd80a0af097bfcd798c3219f4cb3845135fdeebd087bcc899a4a290fd5d902c2b4904fa019f31f2ec09a6c71f568934c6b0a9d12198b8f2ef22d82a3283b4bca1b8ee60a80", 61 | "0xf8689f3067596300e542c7cd2a1c5fca601da42a2739f790ad422b4f59fba0844e61b846f8448080a0762f18853f9b095d3cbb34af725cf2ec2be8a969a7b8d4cf70b8ede38d457a5da07f6f0daf66a63b4d504fabde8e9fa491ff678bf22082d8fee03ac3064fcf7de9" 62 | ], 63 | "codes": [ 64 | "0xef0100d4b75dfda0a9ab5bbcd7969dafc0bc19589157af" 65 | ] 66 | } --------------------------------------------------------------------------------