Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions .github/workflows/zilkworm-benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ on:
type: string
required: false
description: Git ref of axiom-crypto/zilkworm-stateless to build the guest from
default: feat/openvm-rv64
default: feat/openvm-pairing
input_source:
type: string
required: false
Expand Down Expand Up @@ -104,7 +104,7 @@ on:
type: string
required: false
description: Git ref of axiom-crypto/zilkworm-stateless to build the guest from
default: feat/openvm-rv64
default: feat/openvm-pairing
input_source:
type: string
required: false
Expand Down
9 changes: 5 additions & 4 deletions bin/zilkworm-benchmark/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@
Benchmark host for [Zilkworm](https://github.com/eth-act/zilkworm-stateless)
(mirrored at [axiom-crypto/zilkworm-stateless](https://github.com/axiom-crypto/zilkworm-stateless)), a bare-metal C++
stateless Ethereum block validator compiled to an RV64IM ELF. The guest runs
on OpenVM's RV64 target (`rv64i + rv64m + io + keccak + sha2` extensions) and
goes through the same execute / prove pipeline as the Reth benchmark.
on OpenVM's RV64 target (`rv64i + rv64m + io + keccak + sha2` plus the
modular / Fp2 / short-Weierstrass / pairing extensions) and goes through the
same execute / prove pipeline as the Reth benchmark.

## Guest

Build the guest ELF from the `feat/openvm-rv64` branch of
Build the guest ELF from the `feat/openvm-pairing` branch of
`axiom-crypto/zilkworm-stateless` (requires cmake >= 3.28 and the xPack
`riscv-none-elf-gcc` toolchain, see that repo's README):

```bash
git clone https://github.com/axiom-crypto/zilkworm-stateless.git
cd zilkworm-stateless
git checkout feat/openvm-rv64
git checkout feat/openvm-pairing
make guest_openvm # produces build/openvm/z6m_guest.elf
```

Expand Down
50 changes: 0 additions & 50 deletions bin/zilkworm-benchmark/openvm.toml

This file was deleted.

54 changes: 40 additions & 14 deletions bin/zilkworm-benchmark/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,12 @@ use tracing::{info, info_span};

const VM_MAX_CONSTRAINT_DEGREE: usize = 4;

/// Capacity of the user public-values address space in bytes. Must be at
/// least `z6m::MAX_PUBLIC_VALUES_SIZE` (256) so the guest's largest SSZ
/// `StatelessValidationResult` fits.
const PUBLIC_VALUES_BYTES: usize = 256;
/// Capacity of the user public-values address space in bytes. The guest's SSZ
/// `StatelessValidationResult` is 105 bytes for mainnet blocks (root[32] ||
/// success[1] || offset[4] || chain_config), so 128 is the smallest power of
/// two that fits it. (z6m's MAX_PUBLIC_VALUES_SIZE buffer is 256, but only the
/// encoded prefix is revealed.)
const PUBLIC_VALUES_BYTES: usize = 128;

/// Enum representing the execution mode of the host executable.
#[derive(Debug, Clone, clap::ValueEnum)]
Expand Down Expand Up @@ -108,6 +110,23 @@ pub struct BenchmarkCli {
/// Estimated proving-memory cap per VM segment, in bytes
#[arg(long, default_value_t = DEFAULT_MAX_MEMORY)]
pub segment_max_memory: usize,

/// Whether the proving-memory model assumes the Reed-Solomon code matrix
/// stays resident after `stacked_commit`.
///
/// This is a segmentation *setting*, not a backend detail, and the two
/// engines disagree on it: the CPU engine passes `true`
/// (stark-backend `Engine::proving_memory_config`) while the CUDA engine
/// uses `GpuProverConfig::default()`, which is `false`. Caching leaves
/// less of `--segment-max-memory` for trace, so the same guest segments
/// differently: block 24001988 gives 119 segments under the CPU model and
/// 79 under the GPU model.
///
/// Defaults to the GPU model so local `execute-metered` estimates match
/// the GPU benchmark runs; pass `--cache-rs-code-matrix` to model a CPU
/// prover instead.
#[arg(long, default_value_t = false)]
pub cache_rs_code_matrix: bool,
}

/// The arguments for the host executable.
Expand Down Expand Up @@ -158,16 +177,21 @@ pub struct HostArgs {
pub agg_pk_path: Option<PathBuf>,
}

/// VM extension config matching the instructions the Zilkworm guest issues:
/// RV64IM + hint/reveal IO, the keccak-f[1600] and SHA-256 accelerators, and
/// the modular / Fp2 / short-Weierstrass extensions used by the guest's ECC
/// precompile hooks (secp256k1 ecrecover, bn254 field + curve ops). The
/// moduli/curve order in openvm.toml is ABI — it must match the funct7
/// indices in zilkworm's zkvm/openvm/src/include/openvm_ecc.hpp.
/// VM extension config for the Zilkworm guest.
///
/// Uses `SdkVmConfig::standard()` — byte-for-byte the same extension set the
/// Reth benchmark host uses (`reth_vm_config`) — so the two guests are proven
/// on an identical circuit and their segment counts are directly comparable.
/// `standard()` also fixes the modulus / Fp2 / curve ordering that the guest's
/// funct7 indices encode (see zilkworm's zkvm/openvm/src/include/openvm_ecc.hpp),
/// and it enables the Int256 (`bigint`) extension.
///
/// The one deliberate difference from Reth: public-value capacity. Reth reveals
/// a 32-byte block hash and configures 32 cells (64 bytes); the Zilkworm guest
/// reveals the SSZ `StatelessValidationResult`, which is 105 bytes for mainnet
/// blocks, so it needs 64 cells (128 bytes).
pub fn zilkworm_vm_config() -> SdkVmConfig {
let mut config = SdkVmConfig::from_toml(include_str!("../openvm.toml"))
.expect("invalid embedded openvm.toml")
.optimize();
let mut config = SdkVmConfig::standard();
config.system.config = config
.system
.config
Expand Down Expand Up @@ -374,6 +398,7 @@ pub fn run_zilkworm_benchmark(args: HostArgs) -> Result<()> {
});
let program_name = format!("zilkworm.{}.{}", args.mode, label);

let cache_rs_code_matrix = args.benchmark.cache_rs_code_matrix;
let stdin: StdIn = vec![input_bytes].into();

run_with_metric_collection("OUTPUT_PATH", move || {
Expand All @@ -389,7 +414,8 @@ pub fn run_zilkworm_benchmark(args: HostArgs) -> Result<()> {
}
}
BenchMode::ExecuteMetered => {
let compiled = sdk.compile_metered(exe)?;
let mut compiled = sdk.compile_metered(exe)?;
compiled.ctx.set_cache_rs_code_matrix(cache_rs_code_matrix);
let (public_values, segments) = sdk.execute_metered(&compiled, stdin)?;
info!("Execute metered completed, {} segments", segments.len());
report_public_values(&public_values);
Expand Down
Loading