From 979b642c46c5ad0b5d0c2e337d22c1b4f158394e Mon Sep 17 00:00:00 2001 From: Jun Song Date: Sun, 2 Mar 2025 17:51:02 +0900 Subject: [PATCH 01/15] chore: rename program name --- Cargo.lock | 2 +- program/Cargo.toml | 2 +- script/src/bin/main.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9e67d7c..372f49b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3782,7 +3782,7 @@ dependencies = [ ] [[package]] -name = "ream-program" +name = "ream-operations" version = "0.1.0" dependencies = [ "ethereum_ssz", diff --git a/program/Cargo.toml b/program/Cargo.toml index 7eb1d27..1c07149 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -1,6 +1,6 @@ [package] version = "0.1.0" -name = "ream-program" +name = "ream-operations" edition = "2021" [dependencies] diff --git a/script/src/bin/main.rs b/script/src/bin/main.rs index 5f49d56..31ee5bf 100644 --- a/script/src/bin/main.rs +++ b/script/src/bin/main.rs @@ -15,7 +15,7 @@ use ream_consensus::deneb::{beacon_block::BeaconBlock, beacon_state::BeaconState use sp1_sdk::{include_elf, ProverClient, SP1Stdin}; /// The ELF (executable and linkable format) file for the Succinct RISC-V zkVM. -pub const REAM_ELF: &[u8] = include_elf!("ream-program"); +pub const REAM_ELF: &[u8] = include_elf!("ream-operations"); /// The arguments for the command. #[derive(Parser, Debug)] From fa0dcf83ab2c3a756fe68aa849aafcbe2b13a37e Mon Sep 17 00:00:00 2001 From: Jun Song Date: Sun, 2 Mar 2025 18:00:37 +0900 Subject: [PATCH 02/15] feat: add download script for ef tests --- .gitignore | 5 +++- script/Makefile | 23 ++++++++++++++++ script/subscripts/download_ef_data.sh | 39 +++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 script/Makefile create mode 100755 script/subscripts/download_ef_data.sh diff --git a/.gitignore b/.gitignore index 174aa94..f734130 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,7 @@ pgo-data.profdata **/proof-with-io.json # Env -.env \ No newline at end of file +.env + +# EF test data +script/mainnet/ \ No newline at end of file diff --git a/script/Makefile b/script/Makefile new file mode 100644 index 0000000..65fd31a --- /dev/null +++ b/script/Makefile @@ -0,0 +1,23 @@ +TARGET = mainnet.tar.gz +EXTRACT_DIR = mainnet +SCRIPT = ./subscripts/download_ef_data.sh + +.PHONY: all download test clean + +all: download test + +download: + @echo "Running download script..." + @chmod +x $(SCRIPT) + @$(SCRIPT) + +test: $(EXTRACT_DIR) + @echo "Running tests..." + @cargo test --release --features ef-tests + @echo "Tests complete." + +clean: + @echo "Cleaning up downloaded and extracted files..." + @rm -f $(TARGET) + @rm -rf $(EXTRACT_DIR) + @echo "Clean up complete." \ No newline at end of file diff --git a/script/subscripts/download_ef_data.sh b/script/subscripts/download_ef_data.sh new file mode 100755 index 0000000..abb6aa7 --- /dev/null +++ b/script/subscripts/download_ef_data.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +TARGET="mainnet.tar.gz" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PARENT_DIR="$(dirname "$SCRIPT_DIR")" +EXTRACT_DIR="$PARENT_DIR/mainnet" +LATEST_RELEASE_URL="https://api.github.com/repos/ethereum/consensus-spec-tests/releases/latest" + +download_and_extract() { + if [ -d "$EXTRACT_DIR" ]; then + echo "$EXTRACT_DIR already exists. Skipping extraction." + return 0 + fi + + echo "Fetching the latest release URL for $TARGET..." + DOWNLOAD_URL=$(curl -s "$LATEST_RELEASE_URL" | jq -r ".assets[] | select(.name == \"$TARGET\") | .browser_download_url") + + if [ -z "$DOWNLOAD_URL" ] || [ "$DOWNLOAD_URL" == "null" ]; then + echo "Failed to fetch download URL for $TARGET." + exit 1 + fi + + echo "Downloading $TARGET..." + echo "URL: $DOWNLOAD_URL" + curl -L "$DOWNLOAD_URL" -o "$SCRIPT_DIR/$TARGET" + + if [ ! -f "$SCRIPT_DIR/$TARGET" ]; then + echo "Download failed. Exiting." + exit 1 + fi + + echo "Extracting $TARGET into $EXTRACT_DIR..." + mkdir -p "$EXTRACT_DIR" + tar -xzf "$SCRIPT_DIR/$TARGET" -C "$EXTRACT_DIR" + rm -f "$SCRIPT_DIR/$TARGET" + echo "Extraction complete." +} + +download_and_extract \ No newline at end of file From 194783a3cda290541d8bc07d9be4064687bdbfac Mon Sep 17 00:00:00 2001 From: Jun Song Date: Sun, 2 Mar 2025 23:20:43 +0900 Subject: [PATCH 03/15] feat: add support for each operation --- .vscode/settings.json | 1 + Cargo.lock | 76 +++++++++--- lib/Cargo.toml | 4 + lib/src/file.rs | 18 +++ lib/src/input.rs | 28 +++++ lib/src/lib.rs | 2 + program/src/main.rs | 61 ++++++++- script/Cargo.toml | 1 + script/src/bin/cli/fork.rs | 19 +++ script/src/bin/cli/mod.rs | 2 + script/src/bin/cli/operation.rs | 50 ++++++++ script/src/bin/main.rs | 214 ++++++++++++++++++++++++-------- 12 files changed, 400 insertions(+), 76 deletions(-) create mode 100644 lib/src/file.rs create mode 100644 lib/src/input.rs create mode 100644 script/src/bin/cli/fork.rs create mode 100644 script/src/bin/cli/mod.rs create mode 100644 script/src/bin/cli/operation.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index ca6a515..e815802 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -34,4 +34,5 @@ "editor.formatOnSave": true, "editor.hover.enabled": true }, + "rust-analyzer.trace.server": "verbose", } \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 372f49b..b0b9622 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,7 +68,7 @@ dependencies = [ "alloy-trie", "auto_impl", "c-kzg", - "derive_more", + "derive_more 1.0.0", "serde", ] @@ -83,7 +83,7 @@ dependencies = [ "alloy-rlp", "alloy-trie", "auto_impl", - "derive_more", + "derive_more 1.0.0", ] [[package]] @@ -131,7 +131,7 @@ checksum = "4c986539255fb839d1533c128e190e557e52ff652c9ef62939e233a81dd93f7e" dependencies = [ "alloy-primitives", "alloy-rlp", - "derive_more", + "derive_more 1.0.0", "serde", ] @@ -143,7 +143,7 @@ checksum = "cabf647eb4650c91a9d38cb6f972bb320009e7e9d61765fb688a86f1563b33e8" dependencies = [ "alloy-primitives", "alloy-rlp", - "derive_more", + "derive_more 1.0.0", ] [[package]] @@ -158,7 +158,7 @@ dependencies = [ "alloy-rlp", "alloy-serde", "c-kzg", - "derive_more", + "derive_more 1.0.0", "once_cell", "serde", "sha2", @@ -176,7 +176,7 @@ dependencies = [ "alloy-primitives", "alloy-rlp", "auto_impl", - "derive_more", + "derive_more 1.0.0", "sha2", ] @@ -254,7 +254,7 @@ dependencies = [ "bytes", "cfg-if", "const-hex", - "derive_more", + "derive_more 1.0.0", "foldhash", "hashbrown 0.15.2", "indexmap 2.7.1", @@ -318,7 +318,7 @@ dependencies = [ "alloy-rlp", "alloy-serde", "alloy-sol-types", - "derive_more", + "derive_more 1.0.0", "itertools 0.13.0", "serde", "serde_json", @@ -444,7 +444,7 @@ dependencies = [ "alloy-primitives", "alloy-rlp", "arrayvec", - "derive_more", + "derive_more 1.0.0", "nybbles", "serde", "smallvec", @@ -847,7 +847,7 @@ dependencies = [ "bitflags", "cexpr", "clang-sys", - "itertools 0.12.1", + "itertools 0.13.0", "log", "prettyplease", "proc-macro2", @@ -1196,6 +1196,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +[[package]] +name = "convert_case" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.10.0" @@ -1458,7 +1467,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" dependencies = [ - "derive_more-impl", + "derive_more-impl 1.0.0", +] + +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl 2.0.1", ] [[package]] @@ -1473,6 +1491,19 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "syn 2.0.98", + "unicode-xid", +] + [[package]] name = "digest" version = "0.9.0" @@ -1644,7 +1675,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2631,7 +2662,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -3582,7 +3613,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.13.0", "proc-macro2", "quote", "syn 2.0.98", @@ -3643,7 +3674,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3777,6 +3808,8 @@ name = "ream-lib" version = "0.1.0" dependencies = [ "ethereum_ssz", + "ream-consensus", + "serde", "snap", "tracing", ] @@ -3796,6 +3829,7 @@ name = "ream-script" version = "0.1.0" dependencies = [ "clap", + "derive_more 2.0.1", "dotenv", "ethereum_ssz", "hex", @@ -4062,7 +4096,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4152,7 +4186,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "346a3b32eba2640d17a9cb5927056b08f3de90f65b72fe09402c2ad07d684d0b" dependencies = [ "cfg-if", - "derive_more", + "derive_more 1.0.0", "parity-scale-codec", "scale-info-derive", ] @@ -5125,7 +5159,7 @@ dependencies = [ "getrandom 0.3.1", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -5619,6 +5653,12 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unicode-width" version = "0.2.0" diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 9306e61..69f4e67 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -7,3 +7,7 @@ edition = "2021" ethereum_ssz = { workspace = true } snap = "1.1.1" tracing = { workspace = true } +serde = { version = "1.0.200", default-features = false, features = ["derive"] } + +# Ream dependencies +ream-consensus = { workspace = true } diff --git a/lib/src/file.rs b/lib/src/file.rs new file mode 100644 index 0000000..e9df77d --- /dev/null +++ b/lib/src/file.rs @@ -0,0 +1,18 @@ +use std::fs; +use std::path::PathBuf; + +pub fn get_test_cases(base_dir: &PathBuf) -> Vec { + let mut test_cases = Vec::new(); + + if let Ok(entries) = fs::read_dir(base_dir) { + for entry in entries.flatten() { + if entry.file_type().map(|t| t.is_dir()).unwrap_or(false) { + if let Some(folder_name) = entry.file_name().to_str() { + test_cases.push(folder_name.to_string()); + } + } + } + } + + test_cases +} diff --git a/lib/src/input.rs b/lib/src/input.rs new file mode 100644 index 0000000..d8e99ac --- /dev/null +++ b/lib/src/input.rs @@ -0,0 +1,28 @@ +use ream_consensus::{ + attestation::Attestation, + attester_slashing::AttesterSlashing, + bls_to_execution_change::SignedBLSToExecutionChange, + deneb::{ + beacon_block::BeaconBlock, beacon_block_body::BeaconBlockBody, + execution_payload::ExecutionPayload, + }, + deposit::Deposit, + proposer_slashing::ProposerSlashing, + sync_aggregate::SyncAggregate, + voluntary_exit::SignedVoluntaryExit, +}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +pub enum OperationInput { + Attestation(Attestation), + AttesterSlashing(AttesterSlashing), + BeaconBlock(BeaconBlock), + SignedBLSToExecutionChange(SignedBLSToExecutionChange), + Deposit(Deposit), + BeaconBlockBody(BeaconBlockBody), + ProposerSlashing(ProposerSlashing), + SyncAggregate(SyncAggregate), + SignedVoluntaryExit(SignedVoluntaryExit), + ExecutionPayload(ExecutionPayload), +} diff --git a/lib/src/lib.rs b/lib/src/lib.rs index b319429..5a886ba 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -1 +1,3 @@ +pub mod file; +pub mod input; pub mod snappy; diff --git a/program/src/main.rs b/program/src/main.rs index 22d006b..e3b705c 100644 --- a/program/src/main.rs +++ b/program/src/main.rs @@ -5,7 +5,8 @@ #![no_main] sp1_zkvm::entrypoint!(main); -use ream_consensus::deneb::{beacon_block::BeaconBlock, beacon_state::BeaconState}; +use ream_consensus::deneb::beacon_state::BeaconState; +use ream_lib::input::OperationInput; use ssz::Encode; pub fn main() { @@ -20,15 +21,65 @@ pub fn main() { println!("cycle-tracker-end: read-pre-state"); println!("cycle-tracker-start: read-block"); - let block = sp1_zkvm::io::read::(); + let input = sp1_zkvm::io::read::(); println!("cycle-tracker-end: read-block"); // Main logic of the program. // State transition of the beacon state. - println!("cycle-tracker-start: process-block-header"); - let _ = pre_state.process_block_header(&block); - println!("cycle-tracker-end: process-block-header"); + match input { + OperationInput::Attestation(attestation) => { + println!("cycle-tracker-start: process-attestation"); + let _ = pre_state.process_attestation(&attestation); + println!("cycle-tracker-end: process-attestation"); + } + OperationInput::AttesterSlashing(attester_slashing) => { + println!("cycle-tracker-start: process-attester-slashing"); + let _ = pre_state.process_attester_slashing(&attester_slashing); + println!("cycle-tracker-end: process-attester-slashing"); + } + OperationInput::BeaconBlock(block) => { + println!("cycle-tracker-start: process-block-header"); + let _ = pre_state.process_block_header(&block); + println!("cycle-tracker-end: process-block-header"); + } + OperationInput::SignedBLSToExecutionChange(bls_change) => { + println!("cycle-tracker-start: process-bls-to-execution-change"); + let _ = pre_state.process_bls_to_execution_change(&bls_change); + println!("cycle-tracker-end: process-bls-to-execution-change"); + } + OperationInput::Deposit(deposit) => { + println!("cycle-tracker-start: process-deposit"); + let _ = pre_state.process_deposit(&deposit); + println!("cycle-tracker-end: process-deposit"); + } + OperationInput::BeaconBlockBody(_block_body) => { + panic!("Not implemented"); + // println!("cycle-tracker-start: process-execution-payload"); + // let _ = pre_state.process_execution_payload(&block_body); + // println!("cycle-tracker-end: process-execution-payload"); + } + OperationInput::ProposerSlashing(proposer_slashing) => { + println!("cycle-tracker-start: process-proposer-slashing"); + let _ = pre_state.process_proposer_slashing(&proposer_slashing); + println!("cycle-tracker-end: process-proposer-slashing"); + } + OperationInput::SyncAggregate(sync_aggregate) => { + println!("cycle-tracker-start: process-sync-aggregate"); + let _ = pre_state.process_sync_aggregate(&sync_aggregate); + println!("cycle-tracker-end: process-sync-aggregate"); + } + OperationInput::SignedVoluntaryExit(voluntary_exit) => { + println!("cycle-tracker-start: process-voluntary-exit"); + let _ = pre_state.process_voluntary_exit(&voluntary_exit); + println!("cycle-tracker-end: process-voluntary-exit"); + } + OperationInput::ExecutionPayload(execution_payload) => { + println!("cycle-tracker-start: process-withdrawals"); + let _ = pre_state.process_withdrawals(&execution_payload); + println!("cycle-tracker-end: process-withdrawals"); + } + } // Commit to the public values of the program. The final proof will have a commitment to all the // bytes that were committed to. diff --git a/script/Cargo.toml b/script/Cargo.toml index fd95726..1f1fa82 100644 --- a/script/Cargo.toml +++ b/script/Cargo.toml @@ -12,6 +12,7 @@ path = "src/bin/main.rs" sp1-sdk = "4.0.0" serde_json = { version = "1.0", default-features = false, features = ["alloc"] } serde = { version = "1.0.200", default-features = false, features = ["derive"] } +derive_more = { version = "2.0.1", features = ["full"] } clap = { version = "4.0", features = ["derive", "env"] } tracing = { workspace = true } hex = "0.4.3" diff --git a/script/src/bin/cli/fork.rs b/script/src/bin/cli/fork.rs new file mode 100644 index 0000000..47a62be --- /dev/null +++ b/script/src/bin/cli/fork.rs @@ -0,0 +1,19 @@ +use clap::{Parser, ValueEnum}; +use derive_more::Display; + +#[derive(Debug, Clone, Parser)] +pub struct ForkArgs { + #[clap(long, short, default_value_t = Fork::Deneb)] + pub fork: Fork, +} + +#[derive(ValueEnum, Debug, Clone, Default, Display)] +#[clap(rename_all = "lowercase")] +pub enum Fork { + #[default] + #[display("deneb")] + Deneb, + + #[display("electra")] + Electra, +} diff --git a/script/src/bin/cli/mod.rs b/script/src/bin/cli/mod.rs new file mode 100644 index 0000000..45bcd56 --- /dev/null +++ b/script/src/bin/cli/mod.rs @@ -0,0 +1,2 @@ +pub mod fork; +pub mod operation; diff --git a/script/src/bin/cli/operation.rs b/script/src/bin/cli/operation.rs new file mode 100644 index 0000000..5dfeb62 --- /dev/null +++ b/script/src/bin/cli/operation.rs @@ -0,0 +1,50 @@ +use clap::{Parser, ValueEnum}; +use derive_more::Display; + +#[derive(Debug, Clone, Parser)] +pub struct OperationArgs { + #[clap(long, short)] + pub operation_name: OperationName, +} + +#[derive(ValueEnum, Debug, Clone, Display)] +#[clap(rename_all = "snake_case")] +pub enum OperationName { + #[display("attestation")] + Attestation, + #[display("attester_slashing")] + AttesterSlashing, + #[display("block_header")] + BlockHeader, + #[display("bls_to_execution_change")] + BLSToExecutionChange, + #[display("deposit")] + Deposit, + #[display("execution_payload")] + ExecutionPayload, + #[display("proposer_slashing")] + ProposerSlashing, + #[display("sync_aggregate")] + SyncAggregate, + #[display("voluntary_exit")] + VoluntaryExit, + #[display("withdrawals")] + Withdrawals, +} + +impl OperationName { + pub fn to_input_name(&self) -> String { + match self { + OperationName::Attestation => "attestation".to_string(), + OperationName::AttesterSlashing => "attester_slashing".to_string(), + OperationName::BlockHeader => "block".to_string(), + OperationName::BLSToExecutionChange => "address_change".to_string(), + OperationName::Deposit => "deposit".to_string(), + OperationName::ExecutionPayload => "body".to_string(), + OperationName::ProposerSlashing => "proposer_slashing".to_string(), + OperationName::SyncAggregate => "sync_aggregate".to_string(), + OperationName::VoluntaryExit => "voluntary_exit".to_string(), + OperationName::Withdrawals => "execution_payload".to_string(), + } + } +} diff --git a/script/src/bin/main.rs b/script/src/bin/main.rs index 31ee5bf..73ba741 100644 --- a/script/src/bin/main.rs +++ b/script/src/bin/main.rs @@ -11,9 +11,26 @@ //! ``` use clap::Parser; -use ream_consensus::deneb::{beacon_block::BeaconBlock, beacon_state::BeaconState}; +use cli::operation::OperationName; +use ream_consensus::{ + attestation::Attestation, + attester_slashing::AttesterSlashing, + bls_to_execution_change::SignedBLSToExecutionChange, + deneb::{ + beacon_block::BeaconBlock, beacon_block_body::BeaconBlockBody, beacon_state::BeaconState, + execution_payload::ExecutionPayload, + }, + deposit::Deposit, + proposer_slashing::ProposerSlashing, + sync_aggregate::SyncAggregate, + voluntary_exit::SignedVoluntaryExit, +}; use sp1_sdk::{include_elf, ProverClient, SP1Stdin}; +mod cli; + +use ream_lib::input::OperationInput; + /// The ELF (executable and linkable format) file for the Succinct RISC-V zkVM. pub const REAM_ELF: &[u8] = include_elf!("ream-operations"); @@ -21,14 +38,33 @@ pub const REAM_ELF: &[u8] = include_elf!("ream-operations"); #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] struct Args { + /// Argument for zkVMs + #[clap(long)] execute: bool, #[clap(long)] prove: bool, + + /// Argument for STFs + + #[clap(flatten)] + fork: cli::fork::ForkArgs, + + #[clap(flatten)] + operation: cli::operation::OperationArgs, } fn main() { + let test_case_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("mainnet") + .join("tests") + .join("mainnet"); + if !std::path::Path::new(&test_case_dir).exists() { + eprintln!("Error: You must first download test data via `make download`"); + std::process::exit(1); + } + if std::env::var("RUST_LOG").is_err() { std::env::set_var("RUST_LOG", "info"); } @@ -45,58 +81,130 @@ fn main() { std::process::exit(1); } + let fork: cli::fork::Fork = args.fork.fork; + let operation_name: cli::operation::OperationName = args.operation.operation_name; + // Load the test assets. - // This asset is from consensus-specs repo. - // Path: tests/mainnet/deneb/operations/block_header/pyspec_tests/basic_block_header - let base_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("src") - .join("data"); - - let pre_state: BeaconState = - ream_lib::snappy::read_ssz_snappy(&base_dir.join("pre.ssz_snappy")) - .expect("cannot find test asset(pre.ssz_snappy) or decode it"); - let block: BeaconBlock = ream_lib::snappy::read_ssz_snappy(&base_dir.join("block.ssz_snappy")) - .expect("cannot find test asset(block.ssz_snappy) or decode it"); - let post_state: BeaconState = - ream_lib::snappy::read_ssz_snappy(&base_dir.join("post.ssz_snappy")) - .expect("cannot find test asset(post.ssz_snappy) or decode it"); - - // Setup the prover client. - let client = ProverClient::from_env(); - - // Setup the inputs. - let mut stdin = SP1Stdin::new(); - stdin.write(&pre_state); - stdin.write(&block); - - if args.execute { - // Execute the program - let (output, report) = client.execute(REAM_ELF, &stdin).run().unwrap(); - println!("Program executed successfully."); - - // Decode the output - let result: BeaconState = ssz::Decode::from_ssz_bytes(output.as_slice()).unwrap(); - - // Compare the output with the expected post state. - assert_eq!(result, post_state); - println!("Execution is correct!"); - - // Record the number of cycles executed. - println!("Number of cycles: {}", report.total_instruction_count()); - } else { - // Setup the program for proving. - let (pk, vk) = client.setup(REAM_ELF); - - // Generate the proof - let proof = client - .prove(&pk, &stdin) - .run() - .expect("failed to generate proof"); - - println!("Successfully generated proof!"); - - // Verify the proof. - client.verify(&proof, &vk).expect("failed to verify proof"); - println!("Successfully verified proof!"); + // These assets are from consensus-specs repo. + let base_dir = test_case_dir + .join(format!("{}", fork)) + .join("operations") + .join(format!("{}", operation_name)) + .join("pyspec_tests"); + + let test_cases = ream_lib::file::get_test_cases(&base_dir); + for test_case in test_cases { + println!("[{}] Test case: {}", operation_name, test_case); + + let case_dir = &base_dir.join(test_case); + let input_path = &case_dir.join(format!("{}.ssz_snappy", operation_name.to_input_name())); + + let pre_state: BeaconState = + ream_lib::snappy::read_ssz_snappy(&case_dir.join("pre.ssz_snappy")) + .expect("cannot find test asset(pre.ssz_snappy) or decode it"); + let input = match operation_name { + OperationName::Attestation => { + let input: Attestation = ream_lib::snappy::read_ssz_snappy(input_path) + .expect("cannot find input asset or decode it"); + OperationInput::Attestation(input) + } + OperationName::AttesterSlashing => { + let input: AttesterSlashing = ream_lib::snappy::read_ssz_snappy(input_path) + .expect("cannot find input asset or decode it"); + OperationInput::AttesterSlashing(input) + } + OperationName::BlockHeader => { + let input: BeaconBlock = ream_lib::snappy::read_ssz_snappy(input_path) + .expect("cannot find input asset or decode it"); + OperationInput::BeaconBlock(input) + } + OperationName::BLSToExecutionChange => { + let input: SignedBLSToExecutionChange = + ream_lib::snappy::read_ssz_snappy(input_path) + .expect("cannot find input asset or decode it"); + OperationInput::SignedBLSToExecutionChange(input) + } + OperationName::Deposit => { + let input: Deposit = ream_lib::snappy::read_ssz_snappy(input_path) + .expect("cannot find input asset or decode it"); + OperationInput::Deposit(input) + } + OperationName::ExecutionPayload => { + let input: BeaconBlockBody = ream_lib::snappy::read_ssz_snappy(input_path) + .expect("cannot find input asset or decode it"); + OperationInput::BeaconBlockBody(input) + } + OperationName::ProposerSlashing => { + let input: ProposerSlashing = ream_lib::snappy::read_ssz_snappy(input_path) + .expect("cannot find input asset or decode it"); + OperationInput::ProposerSlashing(input) + } + OperationName::SyncAggregate => { + let input: SyncAggregate = ream_lib::snappy::read_ssz_snappy(input_path) + .expect("cannot find input asset or decode it"); + OperationInput::SyncAggregate(input) + } + OperationName::VoluntaryExit => { + let input: SignedVoluntaryExit = ream_lib::snappy::read_ssz_snappy(input_path) + .expect("cannot find input asset or decode it"); + OperationInput::SignedVoluntaryExit(input) + } + OperationName::Withdrawals => { + let input: ExecutionPayload = ream_lib::snappy::read_ssz_snappy(input_path) + .expect("cannot find input asset or decode it"); + OperationInput::ExecutionPayload(input) + } + }; + let post_state_opt: Option = { + if case_dir.join("pre.ssz_snappy").exists() { + let post_state: BeaconState = + ream_lib::snappy::read_ssz_snappy(&case_dir.join("pre.ssz_snappy")) + .expect("cannot find test asset(pre.ssz_snappy) or decode it"); + Some(post_state) + } else { + None + } + }; + + // Setup the prover client. + let client = ProverClient::from_env(); + + // Setup the inputs. + let mut stdin = SP1Stdin::new(); + stdin.write(&pre_state); + stdin.write(&input); + + if args.execute { + // Execute the program + let (output, report) = client.execute(REAM_ELF, &stdin).run().unwrap(); + println!("Program executed successfully."); + + // Decode the output + let result: BeaconState = ssz::Decode::from_ssz_bytes(output.as_slice()).unwrap(); + + // Compare the output with the expected post state. + if let Some(post_state) = post_state_opt { + assert_eq!(result, post_state); + println!("Execution is correct!"); + } + + // Record the number of cycles executed. + println!("Number of cycles: {}", report.total_instruction_count()); + } else { + // Setup the program for proving. + let (pk, vk) = client.setup(REAM_ELF); + + // Generate the proof + let proof = client + .prove(&pk, &stdin) + .run() + .expect("failed to generate proof"); + + println!("Successfully generated proof!"); + + // Verify the proof. + client.verify(&proof, &vk).expect("failed to verify proof"); + println!("Successfully verified proof!"); + } } } From f08b1d61280c6443a6e0e4df1104e149236e1a26 Mon Sep 17 00:00:00 2001 From: Jun Song Date: Mon, 3 Mar 2025 08:05:35 +0900 Subject: [PATCH 04/15] chore: add sp1_derive --- Cargo.lock | 1 + program/Cargo.toml | 1 + program/src/main.rs | 1 + 3 files changed, 3 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index b0b9622..380dc98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3821,6 +3821,7 @@ dependencies = [ "ethereum_ssz", "ream-consensus", "ream-lib", + "sp1-derive", "sp1-zkvm", ] diff --git a/program/Cargo.toml b/program/Cargo.toml index 1c07149..a02386d 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] # SP1 dependencies sp1-zkvm = "4.0.0" +sp1-derive = "4.0.0" # Ethereum dependencies ethereum_ssz = { workspace = true } diff --git a/program/src/main.rs b/program/src/main.rs index e3b705c..6ec4a1e 100644 --- a/program/src/main.rs +++ b/program/src/main.rs @@ -9,6 +9,7 @@ use ream_consensus::deneb::beacon_state::BeaconState; use ream_lib::input::OperationInput; use ssz::Encode; +#[sp1_derive::cycle_tracker] pub fn main() { // Read an input to the program. // From cff04157d168847dbfc372bba99c83a1fc426d06 Mon Sep 17 00:00:00 2001 From: Jun Song Date: Mon, 3 Mar 2025 08:06:59 +0900 Subject: [PATCH 05/15] fix: match for post_state_opt --- script/src/bin/main.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/script/src/bin/main.rs b/script/src/bin/main.rs index 73ba741..293493d 100644 --- a/script/src/bin/main.rs +++ b/script/src/bin/main.rs @@ -156,7 +156,7 @@ fn main() { } }; let post_state_opt: Option = { - if case_dir.join("pre.ssz_snappy").exists() { + if case_dir.join("post.ssz_snappy").exists() { let post_state: BeaconState = ream_lib::snappy::read_ssz_snappy(&case_dir.join("pre.ssz_snappy")) .expect("cannot find test asset(pre.ssz_snappy) or decode it"); @@ -182,10 +182,15 @@ fn main() { // Decode the output let result: BeaconState = ssz::Decode::from_ssz_bytes(output.as_slice()).unwrap(); - // Compare the output with the expected post state. - if let Some(post_state) = post_state_opt { + match post_state_opt { + Some(post_state) => { assert_eq!(result, post_state); - println!("Execution is correct!"); + println!("Execution is correct!: State mutated"); + } + None => { + assert_eq!(result, pre_state); + println!("Execution is correct!: State should not be mutated"); + } } // Record the number of cycles executed. From ded1edc78bc7b6aa2577e8e05e5f0e133a42f9ca Mon Sep 17 00:00:00 2001 From: Jun Song Date: Mon, 3 Mar 2025 08:07:11 +0900 Subject: [PATCH 06/15] fix: add syscall count print --- script/src/bin/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/script/src/bin/main.rs b/script/src/bin/main.rs index 293493d..655d2b1 100644 --- a/script/src/bin/main.rs +++ b/script/src/bin/main.rs @@ -195,6 +195,7 @@ fn main() { // Record the number of cycles executed. println!("Number of cycles: {}", report.total_instruction_count()); + println!("Number of syscall count: {}", report.total_syscall_count()); } else { // Setup the program for proving. let (pk, vk) = client.setup(REAM_ELF); From 618370b17db3ee73dc080aa1e8b376d8b7055593 Mon Sep 17 00:00:00 2001 From: Jun Song Date: Mon, 3 Mar 2025 08:30:28 +0900 Subject: [PATCH 07/15] refactor: main script --- lib/src/file.rs | 16 +++++++- lib/src/lib.rs | 1 + lib/src/snappy.rs | 8 +--- lib/src/ssz.rs | 3 ++ script/src/bin/main.rs | 91 ++++++++---------------------------------- 5 files changed, 37 insertions(+), 82 deletions(-) create mode 100644 lib/src/ssz.rs diff --git a/lib/src/file.rs b/lib/src/file.rs index e9df77d..2330d82 100644 --- a/lib/src/file.rs +++ b/lib/src/file.rs @@ -1,5 +1,19 @@ use std::fs; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; + +use crate::snappy::decode_snappy; +use crate::ssz::to_ssz; + +pub fn read_file(path: &Path) -> T { + let raw_bytes = + std::fs::read(path).unwrap_or_else(|e| panic!("Could not read file: {:?}: {}", path, e)); + let ssz_bytes = decode_snappy(&raw_bytes).unwrap_or_else(|e| { + panic!("Could not decode snappy {:?}: {}", path, e); + }); + to_ssz(&ssz_bytes).unwrap_or_else(|| { + panic!("Could not decode ssz {:?}", path); + }) +} pub fn get_test_cases(base_dir: &PathBuf) -> Vec { let mut test_cases = Vec::new(); diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 5a886ba..3f71878 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -1,3 +1,4 @@ pub mod file; pub mod input; pub mod snappy; +pub mod ssz; diff --git a/lib/src/snappy.rs b/lib/src/snappy.rs index 35c563f..91db1ae 100644 --- a/lib/src/snappy.rs +++ b/lib/src/snappy.rs @@ -1,10 +1,6 @@ -use std::path::Path; - use snap::raw::Decoder; -pub fn read_ssz_snappy(path: &Path) -> Option { - let ssz_snappy = std::fs::read(path).ok()?; +pub fn decode_snappy(raw_bytes: &[u8]) -> Result, snap::Error> { let mut decoder = Decoder::new(); - let ssz = decoder.decompress_vec(&ssz_snappy).unwrap(); - T::from_ssz_bytes(&ssz).ok() + decoder.decompress_vec(raw_bytes) } diff --git a/lib/src/ssz.rs b/lib/src/ssz.rs new file mode 100644 index 0000000..36d9f6a --- /dev/null +++ b/lib/src/ssz.rs @@ -0,0 +1,3 @@ +pub fn to_ssz(ssz_bytes: &[u8]) -> Option { + T::from_ssz_bytes(ssz_bytes).ok() +} diff --git a/script/src/bin/main.rs b/script/src/bin/main.rs index 655d2b1..059e6c2 100644 --- a/script/src/bin/main.rs +++ b/script/src/bin/main.rs @@ -1,35 +1,11 @@ -//! An end-to-end example of using the SP1 SDK to generate a proof of a program that can be executed -//! or have a core proof generated. -//! -//! You can run this script using the following command: -//! ```shell -//! RUST_LOG=info cargo run --release -- --execute -//! ``` -//! or -//! ```shell -//! RUST_LOG=info cargo run --release -- --prove -//! ``` - use clap::Parser; use cli::operation::OperationName; -use ream_consensus::{ - attestation::Attestation, - attester_slashing::AttesterSlashing, - bls_to_execution_change::SignedBLSToExecutionChange, - deneb::{ - beacon_block::BeaconBlock, beacon_block_body::BeaconBlockBody, beacon_state::BeaconState, - execution_payload::ExecutionPayload, - }, - deposit::Deposit, - proposer_slashing::ProposerSlashing, - sync_aggregate::SyncAggregate, - voluntary_exit::SignedVoluntaryExit, -}; +use ream_consensus::deneb::beacon_state::BeaconState; use sp1_sdk::{include_elf, ProverClient, SP1Stdin}; mod cli; -use ream_lib::input::OperationInput; +use ream_lib::{file::read_file, input::OperationInput}; /// The ELF (executable and linkable format) file for the Succinct RISC-V zkVM. pub const REAM_ELF: &[u8] = include_elf!("ream-operations"); @@ -99,68 +75,32 @@ fn main() { let case_dir = &base_dir.join(test_case); let input_path = &case_dir.join(format!("{}.ssz_snappy", operation_name.to_input_name())); - let pre_state: BeaconState = - ream_lib::snappy::read_ssz_snappy(&case_dir.join("pre.ssz_snappy")) - .expect("cannot find test asset(pre.ssz_snappy) or decode it"); + let pre_state: BeaconState = read_file(&case_dir.join("pre.ssz_snappy")); let input = match operation_name { - OperationName::Attestation => { - let input: Attestation = ream_lib::snappy::read_ssz_snappy(input_path) - .expect("cannot find input asset or decode it"); - OperationInput::Attestation(input) - } + OperationName::Attestation => OperationInput::Attestation(read_file(input_path)), OperationName::AttesterSlashing => { - let input: AttesterSlashing = ream_lib::snappy::read_ssz_snappy(input_path) - .expect("cannot find input asset or decode it"); - OperationInput::AttesterSlashing(input) - } - OperationName::BlockHeader => { - let input: BeaconBlock = ream_lib::snappy::read_ssz_snappy(input_path) - .expect("cannot find input asset or decode it"); - OperationInput::BeaconBlock(input) + OperationInput::AttesterSlashing(read_file(input_path)) } + OperationName::BlockHeader => OperationInput::BeaconBlock(read_file(input_path)), OperationName::BLSToExecutionChange => { - let input: SignedBLSToExecutionChange = - ream_lib::snappy::read_ssz_snappy(input_path) - .expect("cannot find input asset or decode it"); - OperationInput::SignedBLSToExecutionChange(input) - } - OperationName::Deposit => { - let input: Deposit = ream_lib::snappy::read_ssz_snappy(input_path) - .expect("cannot find input asset or decode it"); - OperationInput::Deposit(input) + OperationInput::SignedBLSToExecutionChange(read_file(input_path)) } + OperationName::Deposit => OperationInput::Deposit(read_file(input_path)), OperationName::ExecutionPayload => { - let input: BeaconBlockBody = ream_lib::snappy::read_ssz_snappy(input_path) - .expect("cannot find input asset or decode it"); - OperationInput::BeaconBlockBody(input) + OperationInput::BeaconBlockBody(read_file(input_path)) } OperationName::ProposerSlashing => { - let input: ProposerSlashing = ream_lib::snappy::read_ssz_snappy(input_path) - .expect("cannot find input asset or decode it"); - OperationInput::ProposerSlashing(input) - } - OperationName::SyncAggregate => { - let input: SyncAggregate = ream_lib::snappy::read_ssz_snappy(input_path) - .expect("cannot find input asset or decode it"); - OperationInput::SyncAggregate(input) + OperationInput::ProposerSlashing(read_file(input_path)) } + OperationName::SyncAggregate => OperationInput::SyncAggregate(read_file(input_path)), OperationName::VoluntaryExit => { - let input: SignedVoluntaryExit = ream_lib::snappy::read_ssz_snappy(input_path) - .expect("cannot find input asset or decode it"); - OperationInput::SignedVoluntaryExit(input) - } - OperationName::Withdrawals => { - let input: ExecutionPayload = ream_lib::snappy::read_ssz_snappy(input_path) - .expect("cannot find input asset or decode it"); - OperationInput::ExecutionPayload(input) + OperationInput::SignedVoluntaryExit(read_file(input_path)) } + OperationName::Withdrawals => OperationInput::ExecutionPayload(read_file(input_path)), }; let post_state_opt: Option = { if case_dir.join("post.ssz_snappy").exists() { - let post_state: BeaconState = - ream_lib::snappy::read_ssz_snappy(&case_dir.join("pre.ssz_snappy")) - .expect("cannot find test asset(pre.ssz_snappy) or decode it"); - Some(post_state) + Some(read_file(&case_dir.join("post.ssz_snappy"))) } else { None } @@ -182,9 +122,10 @@ fn main() { // Decode the output let result: BeaconState = ssz::Decode::from_ssz_bytes(output.as_slice()).unwrap(); + // Match `post_state_opt`: some test cases should not mutate beacon state. match post_state_opt { Some(post_state) => { - assert_eq!(result, post_state); + assert_eq!(result, post_state); println!("Execution is correct!: State mutated"); } None => { From 42f0b1c237a2743877d85d2e071e4ff8e5a7fec3 Mon Sep 17 00:00:00 2001 From: Jun Song Date: Mon, 3 Mar 2025 08:30:51 +0900 Subject: [PATCH 08/15] chore: nuke existing data --- script/src/data/block.ssz_snappy | Bin 353 -> 0 bytes script/src/data/post.ssz_snappy | Bin 173428 -> 0 bytes script/src/data/pre.ssz_snappy | Bin 173446 -> 0 bytes 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 script/src/data/block.ssz_snappy delete mode 100644 script/src/data/post.ssz_snappy delete mode 100644 script/src/data/pre.ssz_snappy diff --git a/script/src/data/block.ssz_snappy b/script/src/data/block.ssz_snappy deleted file mode 100644 index 5e620e6455992eec7206e3cd8f421c799a14fd59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 353 zcmV-n0iOQs2Lu5C2>}4E2?u}x3@2i{`kp%ALqStkns_1IR?OeAwJ;ZAh_ySYGrb-3 z4JTp&0Ptm00001w9(Z1x=@`ZP(PcA!p`RNj05d)9Onzr4Kv|1)GHh*KML+xb&i$>w zc-t_~KFPQVjq3!fD^Tjgn}53ab7BQf`d!G^Iqj6FFYf26YP`O*faW<`G<5=OhX2Z+ zPl4wE0Ahat2>}t+`T+n4hyegX1OWa4006-LK>&II00FcJm;(R>1boyku?0u9F$l|; zG5XEsyiWO|=yH-)du8SeL+kIPDW6VD001C*0RVg*%uVRt$6b1>g|%n4%+wmw5=9%7 zigXk3p+fyY)RRX}Q~>?~0R900{s9240RRBndI11Yn+Slz$q@?$0RRjI1}Fdk5CQ-I z0Lq#>0BQjMe0fp7vbDSz9rXDb2z|^6Pj-(w{4Lu diff --git a/script/src/data/post.ssz_snappy b/script/src/data/post.ssz_snappy deleted file mode 100644 index 9c58e8c232ede2ad9f187b0951501201440fd475..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 173428 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 z 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 bwU1NtFxf^FJ~%0$$yWOd;8y^=A%Oo2YS>=x diff --git a/script/src/data/pre.ssz_snappy b/script/src/data/pre.ssz_snappy deleted file mode 100644 index 00f56074d521785efedd7399ec250157aacbe8ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 173446 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 UD12~IK$ETZ7r?IoctZgH7dY^4#Q*>R From b070d449c63a847335ce6aa9b95e31cd5eb6eecf Mon Sep 17 00:00:00 2001 From: Jun Song Date: Mon, 3 Mar 2025 09:11:14 +0900 Subject: [PATCH 09/15] feat: parsing log ang nits --- .gitignore | 5 +- program/src/main.rs | 40 ++++--------- script/Makefile | 26 +++++---- script/src/bin/main.rs | 36 +++++++----- script/subscripts/parse_log_to_table.sh | 75 +++++++++++++++++++++++++ 5 files changed, 128 insertions(+), 54 deletions(-) create mode 100755 script/subscripts/parse_log_to_table.sh diff --git a/.gitignore b/.gitignore index f734130..ecbf18c 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,7 @@ pgo-data.profdata .env # EF test data -script/mainnet/ \ No newline at end of file +script/mainnet/ + +# Logs +**/logs/** \ No newline at end of file diff --git a/program/src/main.rs b/program/src/main.rs index 6ec4a1e..2aa055e 100644 --- a/program/src/main.rs +++ b/program/src/main.rs @@ -15,82 +15,64 @@ pub fn main() { // // Behind the scenes, this compiles down to a custom system call which handles reading inputs // from the prover. - // NOTE: BeaconState/BeaconBlock should implement Serialize & Deserialize trait. + // NOTE: BeaconState/OperationInput should implement Serialize & Deserialize trait. - println!("cycle-tracker-start: read-pre-state"); + println!("cycle-tracker-report-start: read-pre-state"); let mut pre_state = sp1_zkvm::io::read::(); - println!("cycle-tracker-end: read-pre-state"); + println!("cycle-tracker-report-end: read-pre-state"); - println!("cycle-tracker-start: read-block"); + println!("cycle-tracker-report-start: read-operation-input"); let input = sp1_zkvm::io::read::(); - println!("cycle-tracker-end: read-block"); + println!("cycle-tracker-report-end: read-operation-input"); // Main logic of the program. // State transition of the beacon state. + println!("cycle-tracker-report-start: process-operation"); match input { OperationInput::Attestation(attestation) => { - println!("cycle-tracker-start: process-attestation"); let _ = pre_state.process_attestation(&attestation); - println!("cycle-tracker-end: process-attestation"); } OperationInput::AttesterSlashing(attester_slashing) => { - println!("cycle-tracker-start: process-attester-slashing"); let _ = pre_state.process_attester_slashing(&attester_slashing); - println!("cycle-tracker-end: process-attester-slashing"); } OperationInput::BeaconBlock(block) => { - println!("cycle-tracker-start: process-block-header"); let _ = pre_state.process_block_header(&block); - println!("cycle-tracker-end: process-block-header"); } OperationInput::SignedBLSToExecutionChange(bls_change) => { - println!("cycle-tracker-start: process-bls-to-execution-change"); let _ = pre_state.process_bls_to_execution_change(&bls_change); - println!("cycle-tracker-end: process-bls-to-execution-change"); } OperationInput::Deposit(deposit) => { - println!("cycle-tracker-start: process-deposit"); let _ = pre_state.process_deposit(&deposit); - println!("cycle-tracker-end: process-deposit"); } OperationInput::BeaconBlockBody(_block_body) => { panic!("Not implemented"); - // println!("cycle-tracker-start: process-execution-payload"); // let _ = pre_state.process_execution_payload(&block_body); - // println!("cycle-tracker-end: process-execution-payload"); } OperationInput::ProposerSlashing(proposer_slashing) => { - println!("cycle-tracker-start: process-proposer-slashing"); let _ = pre_state.process_proposer_slashing(&proposer_slashing); - println!("cycle-tracker-end: process-proposer-slashing"); } OperationInput::SyncAggregate(sync_aggregate) => { - println!("cycle-tracker-start: process-sync-aggregate"); let _ = pre_state.process_sync_aggregate(&sync_aggregate); - println!("cycle-tracker-end: process-sync-aggregate"); } OperationInput::SignedVoluntaryExit(voluntary_exit) => { - println!("cycle-tracker-start: process-voluntary-exit"); let _ = pre_state.process_voluntary_exit(&voluntary_exit); - println!("cycle-tracker-end: process-voluntary-exit"); } OperationInput::ExecutionPayload(execution_payload) => { - println!("cycle-tracker-start: process-withdrawals"); let _ = pre_state.process_withdrawals(&execution_payload); - println!("cycle-tracker-end: process-withdrawals"); } } + println!("cycle-tracker-report-end: process-operation"); // Commit to the public values of the program. The final proof will have a commitment to all the // bytes that were committed to. // NOTE: BeaconState should implement Serialize & Deserialize trait. - println!("cycle-tracker-start: convert-to-ssz-bytes"); + println!("cycle-tracker-report-start: convert-to-ssz-bytes"); let pre_state_bytes = pre_state.as_ssz_bytes(); - println!("cycle-tracker-end: convert-to-ssz-bytes"); + println!("cycle-tracker-report-end: convert-to-ssz-bytes"); - println!("cycle-tracker-start: commit"); + println!("cycle-tracker-report-start: commit"); sp1_zkvm::io::commit_slice(&pre_state_bytes); - println!("cycle-tracker-end: commit"); + println!("cycle-tracker-report-end: commit"); } diff --git a/script/Makefile b/script/Makefile index 65fd31a..777df3b 100644 --- a/script/Makefile +++ b/script/Makefile @@ -1,23 +1,29 @@ TARGET = mainnet.tar.gz EXTRACT_DIR = mainnet -SCRIPT = ./subscripts/download_ef_data.sh +LOGS_DIR = logs -.PHONY: all download test clean +DOWNLOAD_SCRIPT = ./subscripts/download_ef_data.sh +PARSE_SCRIPT = ./subscripts/parse_log_to_table.sh -all: download test +.PHONY: all download run clean + +all: download run download: @echo "Running download script..." - @chmod +x $(SCRIPT) - @$(SCRIPT) + @chmod +x $(DOWNLOAD_SCRIPT) + @$(DOWNLOAD_SCRIPT) -test: $(EXTRACT_DIR) - @echo "Running tests..." - @cargo test --release --features ef-tests - @echo "Tests complete." +run: $(EXTRACT_DIR) + @mkdir -p $(LOGS_DIR) + @echo "Running benchmarks..." + @NO_COLOR=1 cargo run --release -- --execute -o attestation &> logs/execution_attestation.log + @echo "Execution complete." + @$(PARSE_SCRIPT) attestation clean: - @echo "Cleaning up downloaded and extracted files..." + @echo "Cleaning up downloaded/execution files..." @rm -f $(TARGET) @rm -rf $(EXTRACT_DIR) + @rm -rf $(LOGS_DIR) @echo "Clean up complete." \ No newline at end of file diff --git a/script/src/bin/main.rs b/script/src/bin/main.rs index 059e6c2..895fc8c 100644 --- a/script/src/bin/main.rs +++ b/script/src/bin/main.rs @@ -1,12 +1,13 @@ use clap::Parser; -use cli::operation::OperationName; -use ream_consensus::deneb::beacon_state::BeaconState; use sp1_sdk::{include_elf, ProverClient, SP1Stdin}; +use tracing::{error, info}; -mod cli; - +use ream_consensus::deneb::beacon_state::BeaconState; use ream_lib::{file::read_file, input::OperationInput}; +mod cli; +use cli::operation::OperationName; + /// The ELF (executable and linkable format) file for the Succinct RISC-V zkVM. pub const REAM_ELF: &[u8] = include_elf!("ream-operations"); @@ -53,7 +54,7 @@ fn main() { let args = Args::parse(); if args.execute == args.prove { - eprintln!("Error: You must specify either --execute or --prove"); + error!("Error: You must specify either --execute or --prove"); std::process::exit(1); } @@ -70,9 +71,9 @@ fn main() { let test_cases = ream_lib::file::get_test_cases(&base_dir); for test_case in test_cases { - println!("[{}] Test case: {}", operation_name, test_case); + info!("{}", "-".repeat(50)); - let case_dir = &base_dir.join(test_case); + let case_dir = &base_dir.join(&test_case); let input_path = &case_dir.join(format!("{}.ssz_snappy", operation_name.to_input_name())); let pre_state: BeaconState = read_file(&case_dir.join("pre.ssz_snappy")); @@ -117,7 +118,7 @@ fn main() { if args.execute { // Execute the program let (output, report) = client.execute(REAM_ELF, &stdin).run().unwrap(); - println!("Program executed successfully."); + info!("Program executed successfully."); // Decode the output let result: BeaconState = ssz::Decode::from_ssz_bytes(output.as_slice()).unwrap(); @@ -126,17 +127,23 @@ fn main() { match post_state_opt { Some(post_state) => { assert_eq!(result, post_state); - println!("Execution is correct!: State mutated"); + info!("Execution is correct!: State mutated"); } None => { assert_eq!(result, pre_state); - println!("Execution is correct!: State should not be mutated"); + info!("Execution is correct!: State should not be mutated"); } } // Record the number of cycles executed. - println!("Number of cycles: {}", report.total_instruction_count()); - println!("Number of syscall count: {}", report.total_syscall_count()); + info!("----- Cycle Tracker -----"); + info!("[{}] Test case: {}", operation_name, test_case); + info!("Number of cycles: {}", report.total_instruction_count()); + info!("Number of syscall count: {}", report.total_syscall_count()); + for (key, value) in report.cycle_tracker.iter() { + info!("{}: {}", key, value); + } + info!("----- Cycle Tracker End -----"); } else { // Setup the program for proving. let (pk, vk) = client.setup(REAM_ELF); @@ -147,11 +154,12 @@ fn main() { .run() .expect("failed to generate proof"); - println!("Successfully generated proof!"); + info!("Successfully generated proof!"); // Verify the proof. client.verify(&proof, &vk).expect("failed to verify proof"); - println!("Successfully verified proof!"); + info!("Successfully verified proof!"); } + info!("{}", "-".repeat(50)); } } diff --git a/script/subscripts/parse_log_to_table.sh b/script/subscripts/parse_log_to_table.sh new file mode 100755 index 0000000..83b1600 --- /dev/null +++ b/script/subscripts/parse_log_to_table.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +OPERATION=$1 + +LOG_FILE="logs/execution_$OPERATION.log" +OUTPUT_FILE="summary_$OPERATION.md" + +# Table Header +echo '| Operation | Test Case | Total Cycles | Syscall Count | Read Pre-State | Read Operation | Process | Convert SSZ | Commit |' > $OUTPUT_FILE +echo '|-----------|-----------|--------------|--------------|----------------|----------------|---------|-------------|--------|' >> $OUTPUT_FILE + +awk ' +BEGIN { + op = ""; + test_case = ""; + total_cycles = 0; + syscall_count = 0; + read_pre_state = 0; + read_operation = 0; + process_op = 0; + convert_ssz = 0; + commit = 0; +} + +/INFO \[.*\] Test case:/ { + op = $3; + gsub(/[\[\]]/, "", op) + test_case = $NF; +} + +/INFO Number of cycles:/ { + total_cycles = $NF; +} + +/INFO Number of syscall count:/ { + syscall_count = $NF; +} + +/INFO read-pre-state:/ { + read_pre_state = $NF; +} + +/INFO read-operation-input:/ { + read_operation = $NF; +} + +/INFO process-operation:/ { + process_op = $NF; +} + +/INFO convert-to-ssz-bytes:/ { + convert_ssz = $NF; +} + +/INFO commit:/ { + commit = $NF; +} + +/INFO ----- Cycle Tracker End -----/ { + printf "%s | %s | %d | %d | %d | %d | %d | %d | %d\n", op, test_case, total_cycles, syscall_count, read_pre_state, read_operation, process_op, convert_ssz, commit >> "'$OUTPUT_FILE'" + + # Initialize + op = ""; + test_case = ""; + total_cycles = 0; + syscall_count = 0; + read_pre_state = 0; + read_operation = 0; + process_op = 0; + convert_ssz = 0; + commit = 0; +} +' $LOG_FILE + +cat $OUTPUT_FILE \ No newline at end of file From bb0ba0013fae1b13c7dcd9581fedd8356f27fa1d Mon Sep 17 00:00:00 2001 From: Jun Song Date: Mon, 3 Mar 2025 09:44:20 +0900 Subject: [PATCH 10/15] feat: add excluded_cases --- script/src/bin/main.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/script/src/bin/main.rs b/script/src/bin/main.rs index 895fc8c..5b6a1f2 100644 --- a/script/src/bin/main.rs +++ b/script/src/bin/main.rs @@ -30,6 +30,9 @@ struct Args { #[clap(flatten)] operation: cli::operation::OperationArgs, + + #[clap(long)] + excluded_cases: Vec, } fn main() { @@ -58,8 +61,9 @@ fn main() { std::process::exit(1); } - let fork: cli::fork::Fork = args.fork.fork; - let operation_name: cli::operation::OperationName = args.operation.operation_name; + let fork = args.fork.fork; + let operation_name = args.operation.operation_name; + let excluded_cases = args.excluded_cases; // Load the test assets. // These assets are from consensus-specs repo. @@ -71,7 +75,13 @@ fn main() { let test_cases = ream_lib::file::get_test_cases(&base_dir); for test_case in test_cases { + if excluded_cases.contains(&test_case) { + info!("Skipping test case: {}", test_case); + continue; + } + info!("{}", "-".repeat(50)); + info!("[{}] Test case: {}", operation_name, test_case); let case_dir = &base_dir.join(&test_case); let input_path = &case_dir.join(format!("{}.ssz_snappy", operation_name.to_input_name())); From 1073de079acd6193c54a046d22533b50f70e3059 Mon Sep 17 00:00:00 2001 From: Jun Song Date: Mon, 3 Mar 2025 09:44:42 +0900 Subject: [PATCH 11/15] fix: tidy scripts --- script/Makefile | 6 +++++- script/subscripts/parse_log_to_table.sh | 4 +--- script/subscripts/sort_table.sh | 11 +++++++++++ 3 files changed, 17 insertions(+), 4 deletions(-) create mode 100755 script/subscripts/sort_table.sh diff --git a/script/Makefile b/script/Makefile index 777df3b..5779cce 100644 --- a/script/Makefile +++ b/script/Makefile @@ -1,9 +1,11 @@ TARGET = mainnet.tar.gz EXTRACT_DIR = mainnet LOGS_DIR = logs +SUMMARIES_DIR = summaries DOWNLOAD_SCRIPT = ./subscripts/download_ef_data.sh PARSE_SCRIPT = ./subscripts/parse_log_to_table.sh +SORT_SCRIPT = ./subscripts/sort_table.sh .PHONY: all download run clean @@ -16,10 +18,12 @@ download: run: $(EXTRACT_DIR) @mkdir -p $(LOGS_DIR) + @mkdir -p $(SUMMARIES_DIR) @echo "Running benchmarks..." - @NO_COLOR=1 cargo run --release -- --execute -o attestation &> logs/execution_attestation.log + @NO_COLOR=1 cargo run --release -- --execute -o attestation --excluded-cases multi_proposer_index_iterations &> logs/execution_attestation.log @echo "Execution complete." @$(PARSE_SCRIPT) attestation + @$(SORT_SCRIPT) $(SUMMARIES_DIR)/summary_attestation.md clean: @echo "Cleaning up downloaded/execution files..." diff --git a/script/subscripts/parse_log_to_table.sh b/script/subscripts/parse_log_to_table.sh index 83b1600..fda5c51 100755 --- a/script/subscripts/parse_log_to_table.sh +++ b/script/subscripts/parse_log_to_table.sh @@ -3,7 +3,7 @@ OPERATION=$1 LOG_FILE="logs/execution_$OPERATION.log" -OUTPUT_FILE="summary_$OPERATION.md" +OUTPUT_FILE="summaries/summary_$OPERATION.md" # Table Header echo '| Operation | Test Case | Total Cycles | Syscall Count | Read Pre-State | Read Operation | Process | Convert SSZ | Commit |' > $OUTPUT_FILE @@ -71,5 +71,3 @@ BEGIN { commit = 0; } ' $LOG_FILE - -cat $OUTPUT_FILE \ No newline at end of file diff --git a/script/subscripts/sort_table.sh b/script/subscripts/sort_table.sh new file mode 100755 index 0000000..d8aff27 --- /dev/null +++ b/script/subscripts/sort_table.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +INPUT_FILE=$1 +OUTPUT_FILE= echo $INPUT_FILE | awk -F/ '{print $1 "/" "sorted_" $2}' + +HEADER=$(head -n 2 "$INPUT_FILE") + +echo "$HEADER" > "$OUTPUT_FILE" +tail -n +3 "$INPUT_FILE" | sort -t '|' -k1,1 -k2,2 >> "$OUTPUT_FILE" + +rm $INPUT_FILE From b019cefb723026ab884eaeaffd67fc6aceb62694 Mon Sep 17 00:00:00 2001 From: Jun Song Date: Mon, 3 Mar 2025 10:08:41 +0900 Subject: [PATCH 12/15] fix: tidy scripts 2 --- .gitignore | 5 +++-- script/subscripts/sort_table.sh | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index ecbf18c..f931161 100644 --- a/.gitignore +++ b/.gitignore @@ -21,5 +21,6 @@ pgo-data.profdata # EF test data script/mainnet/ -# Logs -**/logs/** \ No newline at end of file +# Logs and Summaries +**/logs/** +**/summaries/** \ No newline at end of file diff --git a/script/subscripts/sort_table.sh b/script/subscripts/sort_table.sh index d8aff27..c4111a1 100755 --- a/script/subscripts/sort_table.sh +++ b/script/subscripts/sort_table.sh @@ -1,7 +1,7 @@ #!/bin/bash INPUT_FILE=$1 -OUTPUT_FILE= echo $INPUT_FILE | awk -F/ '{print $1 "/" "sorted_" $2}' +OUTPUT_FILE=$(echo $INPUT_FILE | awk -F/ '{print $1 "/" "sorted_" $2}') HEADER=$(head -n 2 "$INPUT_FILE") From 1c5c0976cd69ad0bda2e4c0e6294f746f8b3d177 Mon Sep 17 00:00:00 2001 From: Jun Song Date: Mon, 3 Mar 2025 10:17:58 +0900 Subject: [PATCH 13/15] fix: generalize Makefile run --- script/Makefile | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/script/Makefile b/script/Makefile index 5779cce..3ea45e0 100644 --- a/script/Makefile +++ b/script/Makefile @@ -7,23 +7,29 @@ DOWNLOAD_SCRIPT = ./subscripts/download_ef_data.sh PARSE_SCRIPT = ./subscripts/parse_log_to_table.sh SORT_SCRIPT = ./subscripts/sort_table.sh -.PHONY: all download run clean +OPERATIONS = attestation attester_slashing block_header bls_to_execution_change deposit execution_payload proposer_slashing sync_aggregate voluntary_exit withdrawals -all: download run +.PHONY: all download run clean $(addprefix run-, $(OPERATIONS)) + +all: download run-attestation download: @echo "Running download script..." @chmod +x $(DOWNLOAD_SCRIPT) @$(DOWNLOAD_SCRIPT) -run: $(EXTRACT_DIR) +run: + @echo "Specify an operation: $(OPERATIONS)" + @exit 1 + +$(addprefix run-, $(OPERATIONS)): run-%: $(EXTRACT_DIR) @mkdir -p $(LOGS_DIR) @mkdir -p $(SUMMARIES_DIR) - @echo "Running benchmarks..." - @NO_COLOR=1 cargo run --release -- --execute -o attestation --excluded-cases multi_proposer_index_iterations &> logs/execution_attestation.log - @echo "Execution complete." - @$(PARSE_SCRIPT) attestation - @$(SORT_SCRIPT) $(SUMMARIES_DIR)/summary_attestation.md + @echo "Running benchmarks for $*..." + @NO_COLOR=1 cargo run --release -- --execute -o $* --excluded-cases multi_proposer_index_iterations &> $(LOGS_DIR)/execution_$*.log + @echo "Execution complete for $*." + @$(PARSE_SCRIPT) $* + @$(SORT_SCRIPT) $(SUMMARIES_DIR)/summary_$*.md clean: @echo "Cleaning up downloaded/execution files..." From cb592c9c987880b8bb026657a43832fc3157a36f Mon Sep 17 00:00:00 2001 From: Jun Song Date: Mon, 3 Mar 2025 18:02:39 +0900 Subject: [PATCH 14/15] chore: add some filters --- script/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/script/Makefile b/script/Makefile index 3ea45e0..ede6959 100644 --- a/script/Makefile +++ b/script/Makefile @@ -26,7 +26,10 @@ $(addprefix run-, $(OPERATIONS)): run-%: $(EXTRACT_DIR) @mkdir -p $(LOGS_DIR) @mkdir -p $(SUMMARIES_DIR) @echo "Running benchmarks for $*..." - @NO_COLOR=1 cargo run --release -- --execute -o $* --excluded-cases multi_proposer_index_iterations &> $(LOGS_DIR)/execution_$*.log + @NO_COLOR=1 cargo run --release -- --execute -o $* \ + --excluded-cases multi_proposer_index_iterations \ # attestation + --excluded-cases random_with_exits_with_duplicates \ # sync_aggregate + &> $(LOGS_DIR)/execution_$*.log @echo "Execution complete for $*." @$(PARSE_SCRIPT) $* @$(SORT_SCRIPT) $(SUMMARIES_DIR)/summary_$*.md From 83bd9fab8377b57480ca37f2cb87074a09d48880 Mon Sep 17 00:00:00 2001 From: Jun Song Date: Mon, 3 Mar 2025 18:07:20 +0900 Subject: [PATCH 15/15] docs: fix README --- README.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9e6eceb..c67adc3 100644 --- a/README.md +++ b/README.md @@ -27,16 +27,35 @@ To run the program without generating a proof: ```sh cd script -cargo run --release -- --execute +make download +cargo run --release -- --execute --operation_name +``` + +``` +possible values for OPERATION_NAME: attestation, attester_slashing, block_header, bls_to_execution_change, deposit, execution_payload, proposer_slashing, sync_aggregate, voluntary_exit, withdrawals ``` This will execute the program and display the output. +### Generate benchmarks for execution + +```sh +cd script +make download +make run- +``` + +```sh +OPERATIONS = attestation attester_slashing block_header bls_to_execution_change deposit execution_payload proposer_slashing sync_aggregate voluntary_exit withdrawals +``` + +This will execute the program and generate benchmarks (especially for cycles) in `./script/summaries` directory. + ### Generate a Core Proof To generate a core proof for your program: ```sh cd script -cargo run --release -- --prove +cargo run --release -- --prove --operation_name ```