Skip to content
Open
Show file tree
Hide file tree
Changes from 71 commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
a19019f
add `ClientInput` struct
kevaundray May 17, 2025
decb023
RecoveredBlock -> Block
kevaundray May 17, 2025
4e82527
cargo fmt
kevaundray May 17, 2025
e86c066
clippy
kevaundray May 17, 2025
d2e0768
toml format
kevaundray May 17, 2025
337ff5e
downgrade to 1.85
kevaundray May 17, 2025
75bfb3a
expose certain functions and properties in ef-tests
kevaundray May 17, 2025
177578a
Merge branch 'kw/client-input' into kw/zkevm-benchmark-workload-repo
kevaundray May 17, 2025
568a441
add track_cycles macro
kevaundray May 17, 2025
d224ccd
add back fork_spec
kevaundray May 18, 2025
e213cb0
more comments
kevaundray May 18, 2025
3c4bf14
fix
kevaundray May 19, 2025
8147d38
Merge branch 'kw/crecovered-block-to-block' into kw/zkevm-benchmark-w…
kevaundray May 19, 2025
81299a6
fix missing post-state check
kevaundray May 19, 2025
d026cee
fix
kevaundray May 19, 2025
8455748
add verify-witness track_cycles
kevaundray May 20, 2025
f1540ee
add track_cycles annotations
kevaundray May 20, 2025
74eb7cd
Merge branch 'main' into kw/zkevm-benchmark-workload-repo
kevaundray May 20, 2025
235e49e
cargo lock
kevaundray May 20, 2025
089bdf6
update cargo.lock
kevaundray May 20, 2025
e0e205d
add tracing
kevaundray May 20, 2025
2da36a8
switch recoverBlock -> block in ClientInput
kevaundray May 20, 2025
d26d736
Merge branch 'main' into kw/zkevm-benchmark-workload-repo
kevaundray May 21, 2025
d441be8
update cargo.lock
kevaundray May 21, 2025
8912fad
Merge branch 'main' into kw/zkevm-benchmark-workload-repo
kevaundray May 22, 2025
5117f87
Merge branch 'main' into kw/zkevm-benchmark-workload-repo
kevaundray May 23, 2025
ed524d0
cargo.lock
kevaundray May 23, 2025
927c90c
stateless: use snake case for cycle region names
jsign Jun 20, 2025
e461764
Merge pull request #9 from jsign/jsign-cycle-naming-convention
kevaundray Jun 22, 2025
44292e2
add missing import
jsign Jun 25, 2025
e7410f9
Merge pull request #10 from jsign/jsign-fix-missing-use
kevaundray Jun 25, 2025
1d79d9a
Merge branch 'main' into kw/zkevm-benchmark-workload-repo
kevaundray Jun 25, 2025
ac39dcc
update Cargo.lock
kevaundray Jun 25, 2025
11c2776
fix
kevaundray Jun 25, 2025
03364a8
Merge branch 'main' into kw/zkevm-benchmark-workload-repo
kevaundray Jun 27, 2025
75092d6
Merge branch 'main' into kw/zkevm-benchmark-workload-repo
kevaundray Jul 14, 2025
0771183
implement stateless chainspec
jsign Jul 28, 2025
263361e
Merge pull request #15 from kevaundray/jsign-stateless-chainspec
jsign Jul 30, 2025
641a10f
missing empty genesis
jsign Jul 30, 2025
3e468e4
fix
jsign Jul 30, 2025
e0a902c
Merge pull request #16 from kevaundray/jsign-ffix
jsign Jul 30, 2025
5b46a08
Merge commit '48941e6db50f0c8654a391d167b465827f36dc8d' into jsign-up…
jsign Aug 5, 2025
3ff0dbe
Merge pull request #17 from kevaundray/jsign-update-reth
jsign Aug 5, 2025
acc318e
first step change
jsign Aug 8, 2025
8cee4ee
fill chainconfig for tests
jsign Aug 8, 2025
ca2e722
add chain config
jsign Aug 8, 2025
39c7e54
export genesis
jsign Aug 8, 2025
4c51e81
update
jsign Aug 8, 2025
65a6585
more changes
jsign Aug 8, 2025
8d348c8
more work
jsign Aug 8, 2025
b4bde84
more changes
jsign Aug 8, 2025
6eda062
remove stateless chainspec
jsign Aug 11, 2025
cb47abf
improvements
jsign Aug 11, 2025
e5eac83
add glacier forks
jsign Aug 11, 2025
8444a3e
add missing dao fork
jsign Aug 11, 2025
f17c70c
not required activation since we build from mainnet
jsign Aug 11, 2025
0880801
Merge pull request #20 from kevaundray/jsign-chainconfig-take-2
jsign Aug 11, 2025
0898f54
refactor and support partial witness
jsign Aug 22, 2025
d0b39b2
fix
jsign Aug 22, 2025
ce02bb3
more fixes
jsign Aug 22, 2025
bda8644
fix
jsign Aug 22, 2025
e203403
consensus failure fix
jsign Aug 22, 2025
dd3adc3
improvements
jsign Aug 22, 2025
304d73a
improvement
jsign Aug 22, 2025
0f3b7fd
feedback review
jsign Aug 25, 2025
fa3efe1
Merge pull request #21 from kevaundray/jsign-partial-witness-
jsign Aug 25, 2025
17aea38
Merge branch 'main' into jsign-update-reth-3
jsign Sep 10, 2025
63469e9
fixes
jsign Sep 10, 2025
6de1d83
Merge pull request #23 from kevaundray/jsign-update-reth-3
jsign Sep 10, 2025
18237ad
fmt to reduce diff noise
jsign Sep 19, 2025
307dd30
Merge pull request #24 from kevaundray/jsign-fmt
jsign Sep 19, 2025
4d903ab
Merge branch 'main' into jsign-update-reth-4
jsign Sep 30, 2025
72ec056
fix merge nit
jsign Sep 30, 2025
9dc204b
Merge pull request #25 from kevaundray/jsign-update-reth-4
jsign Sep 30, 2025
54f77a1
nightly fmt
jsign Sep 30, 2025
cca0d60
remove nit differences
jsign Sep 30, 2025
bb7a98e
fmt
jsign Sep 30, 2025
7bc2c4f
Merge branch 'main' into jsign-update-fork-1
jsign Oct 16, 2025
e855733
Merge pull request #26 from kevaundray/jsign-update-fork-1
jsign Oct 16, 2025
be392e6
Merge branch 'main' into jsign-update-1
jsign Oct 18, 2025
5b4c3a6
Merge pull request #27 from kevaundray/jsign-update-1
jsign Oct 18, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

123 changes: 113 additions & 10 deletions crates/chainspec/src/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ use alloy_consensus::{
Header,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Changes in this file are related to the opened issue https://github.com/ethereum/execution-spec-tests/issues/2031

};
use alloy_eips::{
eip1559::INITIAL_BASE_FEE, eip7685::EMPTY_REQUESTS_HASH, eip7892::BlobScheduleBlobParams,
eip1559::INITIAL_BASE_FEE, eip6110::MAINNET_DEPOSIT_CONTRACT_ADDRESS,
eip7685::EMPTY_REQUESTS_HASH, eip7892::BlobScheduleBlobParams,
};
use alloy_genesis::Genesis;
use alloy_genesis::{ChainConfig, Genesis};
use alloy_primitives::{address, b256, Address, BlockNumber, B256, U256};
use alloy_trie::root::state_root_ref_unhashed;
use core::fmt::Debug;
Expand Down Expand Up @@ -87,9 +88,15 @@ pub fn make_genesis_header(genesis: &Genesis, hardforks: &ChainHardforks) -> Hea

/// The Ethereum mainnet spec
pub static MAINNET: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
let genesis = serde_json::from_str(include_str!("../res/genesis/mainnet.json"))
let mut genesis: Genesis = serde_json::from_str(include_str!("../res/genesis/mainnet.json"))
.expect("Can't deserialize Mainnet genesis json");
let hardforks = EthereumHardfork::mainnet().into();
fill_chainconfig(
&mut genesis.config,
&hardforks,
Some(Chain::mainnet()),
Some(MAINNET_DEPOSIT_CONTRACT_ADDRESS),
);
let mut spec = ChainSpec {
chain: Chain::mainnet(),
genesis_header: SealedHeader::new(
Expand Down Expand Up @@ -381,7 +388,7 @@ impl ChainSpec {
// given timestamp.
for (fork, params) in bf_params.iter().rev() {
if self.hardforks.is_fork_active_at_timestamp(fork.clone(), timestamp) {
return *params
return *params;
}
}

Expand Down Expand Up @@ -495,7 +502,7 @@ impl ChainSpec {
} else {
// we can return here because this block fork is not active, so we set the
// `next` value
return ForkId { hash: forkhash, next: block }
return ForkId { hash: forkhash, next: block };
}
}
}
Expand All @@ -517,7 +524,7 @@ impl ChainSpec {
// can safely return here because we have already handled all block forks and
// have handled all active timestamp forks, and set the next value to the
// timestamp that is known but not active yet
return ForkId { hash: forkhash, next: timestamp }
return ForkId { hash: forkhash, next: timestamp };
}
}

Expand Down Expand Up @@ -767,6 +774,7 @@ pub struct ChainSpecBuilder {
chain: Option<Chain>,
genesis: Option<Genesis>,
hardforks: ChainHardforks,
fill_genesis_config: bool,
}

impl ChainSpecBuilder {
Expand All @@ -776,6 +784,7 @@ impl ChainSpecBuilder {
chain: Some(MAINNET.chain),
genesis: Some(MAINNET.genesis.clone()),
hardforks: MAINNET.hardforks.clone(),
fill_genesis_config: true,
}
}
}
Expand Down Expand Up @@ -833,9 +842,16 @@ impl ChainSpecBuilder {
self
}

/// Enable Dao at genesis.
pub fn dao_activated(mut self) -> Self {
self = self.frontier_activated();
self.hardforks.insert(EthereumHardfork::Dao, ForkCondition::Block(0));
self
}

/// Enable Homestead at genesis.
pub fn homestead_activated(mut self) -> Self {
self = self.frontier_activated();
self = self.dao_activated();
self.hardforks.insert(EthereumHardfork::Homestead, ForkCondition::Block(0));
self
}
Expand Down Expand Up @@ -882,9 +898,16 @@ impl ChainSpecBuilder {
self
}

/// Enable Muir Glacier at genesis.
pub fn muirglacier_activated(mut self) -> Self {
self = self.istanbul_activated();
self.hardforks.insert(EthereumHardfork::MuirGlacier, ForkCondition::Block(0));
self
}

/// Enable Berlin at genesis.
pub fn berlin_activated(mut self) -> Self {
self = self.istanbul_activated();
self = self.muirglacier_activated();
self.hardforks.insert(EthereumHardfork::Berlin, ForkCondition::Block(0));
self
}
Expand All @@ -896,9 +919,23 @@ impl ChainSpecBuilder {
self
}

/// Enable Arrow Glacier at genesis.
pub fn arrowglacier_activated(mut self) -> Self {
self = self.london_activated();
self.hardforks.insert(EthereumHardfork::ArrowGlacier, ForkCondition::Block(0));
self
}

/// Enable Gray Glacier at genesis.
pub fn grayglacier_activated(mut self) -> Self {
self = self.arrowglacier_activated();
self.hardforks.insert(EthereumHardfork::GrayGlacier, ForkCondition::Block(0));
self
}

/// Enable Paris at genesis.
pub fn paris_activated(mut self) -> Self {
self = self.london_activated();
self = self.grayglacier_activated();
self.hardforks.insert(
EthereumHardfork::Paris,
ForkCondition::TTD {
Expand Down Expand Up @@ -944,6 +981,12 @@ impl ChainSpecBuilder {
self
}

/// Enable overriding genesis chain configuration from defined hardforks.
pub const fn fill_genesis_config(mut self, enable: bool) -> Self {
self.fill_genesis_config = enable;
self
}

/// Enable Osaka at the given timestamp.
pub fn with_osaka_at(mut self, timestamp: u64) -> Self {
self.hardforks.insert(EthereumHardfork::Osaka, ForkCondition::Timestamp(timestamp));
Expand All @@ -966,7 +1009,10 @@ impl ChainSpecBuilder {
}
})
};
let genesis = self.genesis.expect("The genesis is required");
let mut genesis = self.genesis.expect("The genesis is required");
if self.fill_genesis_config {
fill_chainconfig(&mut genesis.config, &self.hardforks, self.chain, None);
}
ChainSpec {
chain: self.chain.expect("The chain is required"),
genesis_header: SealedHeader::new_unhashed(make_genesis_header(
Expand All @@ -982,12 +1028,69 @@ impl ChainSpecBuilder {
}
}

fn fill_chainconfig(
cfg: &mut ChainConfig,
hardforks: &ChainHardforks,
chain: Option<Chain>,
deposit_contract: Option<Address>,
) {
cfg.chain_id = chain.map(|c| c.id()).unwrap_or_default();
cfg.deposit_contract_address = deposit_contract;
// Helpers to extract activation values from ForkCondition
let get_block = |hf: EthereumHardfork| -> Option<u64> {
match hardforks.fork(hf) {
ForkCondition::Block(b) => Some(b),
ForkCondition::TTD { activation_block_number, .. } => Some(activation_block_number),
_ => None,
}
};
let get_time = |hf: EthereumHardfork| -> Option<u64> {
match hardforks.fork(hf) {
ForkCondition::Timestamp(t) => Some(t),
_ => None,
}
};

// Legacy block-based forks
cfg.homestead_block = get_block(EthereumHardfork::Homestead);
cfg.dao_fork_block = get_block(EthereumHardfork::Dao);
cfg.dao_fork_support = cfg.dao_fork_block.is_some();
cfg.eip150_block = get_block(EthereumHardfork::Tangerine);
cfg.eip158_block = get_block(EthereumHardfork::Tangerine);
cfg.eip155_block = get_block(EthereumHardfork::SpuriousDragon);
cfg.byzantium_block = get_block(EthereumHardfork::Byzantium);
cfg.constantinople_block = get_block(EthereumHardfork::Constantinople);
cfg.petersburg_block = get_block(EthereumHardfork::Petersburg);
cfg.istanbul_block = get_block(EthereumHardfork::Istanbul);
cfg.muir_glacier_block = get_block(EthereumHardfork::MuirGlacier);
cfg.berlin_block = get_block(EthereumHardfork::Berlin);
cfg.london_block = get_block(EthereumHardfork::London);
cfg.arrow_glacier_block = get_block(EthereumHardfork::ArrowGlacier);
cfg.gray_glacier_block = get_block(EthereumHardfork::GrayGlacier);

// Merge (Paris) via TTD
if let ForkCondition::TTD { total_difficulty, fork_block, .. } =
hardforks.fork(EthereumHardfork::Paris)
{
cfg.terminal_total_difficulty = Some(total_difficulty);
cfg.terminal_total_difficulty_passed = true;
cfg.merge_netsplit_block = fork_block;
}

// Timestamp-based forks
cfg.shanghai_time = get_time(EthereumHardfork::Shanghai);
cfg.cancun_time = get_time(EthereumHardfork::Cancun);
cfg.prague_time = get_time(EthereumHardfork::Prague);
cfg.osaka_time = get_time(EthereumHardfork::Osaka);
}

impl From<&Arc<ChainSpec>> for ChainSpecBuilder {
fn from(value: &Arc<ChainSpec>) -> Self {
Self {
chain: Some(value.chain),
genesis: Some(value.genesis.clone()),
hardforks: value.hardforks.clone(),
fill_genesis_config: false,
}
}
}
Expand Down
17 changes: 17 additions & 0 deletions crates/evm/evm/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,23 @@ pub trait Executor<DB: Database>: Sized {
Ok(BlockExecutionOutput { state: state.take_bundle(), result })
}

/// Executes the EVM with the given input and accepts a state closure that is always invoked
Copy link
Collaborator

Choose a reason for hiding this comment

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

Required for invalid block execution witnes-gen/proving

/// with the EVM state after execution, even after failure.
fn execute_with_state_closure_always<F>(
mut self,
block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
mut f: F,
) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
where
F: FnMut(&State<DB>),
{
let result = self.execute_one(block);
let mut state = self.into_state();
f(&state);

Ok(BlockExecutionOutput { state: state.take_bundle(), result: result? })
}

/// Executes the EVM with the given input and accepts a state hook closure that is invoked with
/// the EVM state after execution.
fn execute_with_state_hook<F>(
Expand Down
2 changes: 2 additions & 0 deletions crates/stateless/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ alloy-rlp.workspace = true
alloy-trie.workspace = true
alloy-consensus.workspace = true
alloy-rpc-types-debug.workspace = true
alloy-genesis = { workspace = true, features = ["serde-bincode-compat"] }

# reth
reth-ethereum-consensus.workspace = true
Expand All @@ -36,3 +37,4 @@ thiserror.workspace = true
itertools.workspace = true
serde.workspace = true
serde_with.workspace = true
tracing.workspace = true
24 changes: 24 additions & 0 deletions crates/stateless/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,14 @@ extern crate alloc;
/// Sparse trie implementation for stateless validation
pub mod trie;

use alloy_genesis::ChainConfig;
#[doc(inline)]
pub use trie::StatelessTrie;
#[doc(inline)]
pub use validation::stateless_validation_with_trie;

pub use alloy_genesis::Genesis;

/// Implementation of stateless validation
pub mod validation;
pub(crate) mod witness_db;
Expand All @@ -64,4 +67,25 @@ pub struct StatelessInput {
pub block: Block,
/// `ExecutionWitness` for the stateless validation function
pub witness: ExecutionWitness,
/// Chain configuration for the stateless validation function
#[serde_as(as = "alloy_genesis::serde_bincode_compat::ChainConfig<'_>")]
pub chain_config: ChainConfig,
}

/// Tracks the amount of cycles a region of code takes up
/// in a zkvm environment and is no-op otherwise.
#[macro_export]
macro_rules! track_cycles {
($name:expr, $body:expr) => {{
#[cfg(target_os = "zkvm")]
{
tracing::info!("cycle-tracker-report-start: {}", $name);
let result = $body;
tracing::info!("cycle-tracker-report-end: {}", $name);
result
}

#[cfg(not(target_os = "zkvm"))]
$body
}};
Comment on lines +80 to +93
Copy link
Collaborator

Choose a reason for hiding this comment

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

This change, and the ones in the next file are mainly SP1 track cycle instrumentation.

I usually find these cycles tracking for SP1 helpful to quickly see optimizations impact, etc. But from another angle, cycles for some kind of optimizations are quite brittle. They're only meaningful for non-crypto related opts.

Prob these changes will never be upstreamed as they are. I guess we could think about creating a trait that can be provided for instrumentation, and the client can impl for different zkVMs -- but definitely is the last upstream effort I would leave pending (if ever happens). 😅

cc @kevaundray if you have any thought

}
32 changes: 20 additions & 12 deletions crates/stateless/src/validation.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{
track_cycles,
trie::{StatelessSparseTrie, StatelessTrie},
witness_db::WitnessDatabase,
ExecutionWitness,
Expand Down Expand Up @@ -190,30 +191,37 @@ where
validate_block_consensus(chain_spec.clone(), &current_block, parent)?;

// First verify that the pre-state reads are correct
let (mut trie, bytecode) = T::new(&witness, parent.state_root)?;
let (mut trie, bytecode) =
track_cycles!("verify_witness", T::new(&witness, parent.state_root)?);

// Create an in-memory database that will use the reads to validate the block
let db = WitnessDatabase::new(&trie, bytecode, ancestor_hashes);

// Execute the block
let executor = evm_config.executor(db);
let output = executor
.execute(&current_block)
.map_err(|e| StatelessValidationError::StatelessExecutionFailed(e.to_string()))?;
let output = track_cycles!(
"block_execution",
executor
.execute(&current_block)
.map_err(|e| StatelessValidationError::StatelessExecutionFailed(e.to_string()))?
);

// Post validation checks
validate_block_post_execution(&current_block, &chain_spec, &output.receipts, &output.requests)
.map_err(StatelessValidationError::ConsensusValidationFailed)?;

// Compute and check the post state root
let hashed_state = HashedPostState::from_bundle_state::<KeccakKeyHasher>(&output.state.state);
let state_root = trie.calculate_state_root(hashed_state)?;
if state_root != current_block.state_root {
return Err(StatelessValidationError::PostStateRootMismatch {
got: state_root,
expected: current_block.state_root,
});
}
track_cycles!("post_state_compute", {
let hashed_state =
HashedPostState::from_bundle_state::<KeccakKeyHasher>(&output.state.state);
let state_root = trie.calculate_state_root(hashed_state)?;
if state_root != current_block.state_root {
return Err(StatelessValidationError::PostStateRootMismatch {
got: state_root,
expected: current_block.state_root,
});
}
});

// Return block hash
Ok(current_block.hash_slow())
Expand Down
Loading
Loading