diff --git a/tools/cycle_bench/Cargo.toml b/tools/cycle_bench/Cargo.toml index 6847b0c5..1a4a9db3 100644 --- a/tools/cycle_bench/Cargo.toml +++ b/tools/cycle_bench/Cargo.toml @@ -16,6 +16,7 @@ ppe = ["prove"] [dependencies] nssa = { workspace = true } nssa_core = { workspace = true, features = ["host"] } +authenticated_transfer_core.workspace = true clock_core.workspace = true token_core.workspace = true amm_core.workspace = true diff --git a/tools/cycle_bench/src/lib.rs b/tools/cycle_bench/src/lib.rs new file mode 100644 index 00000000..7091cf84 --- /dev/null +++ b/tools/cycle_bench/src/lib.rs @@ -0,0 +1,23 @@ +//! `cycle_bench` library: per-program executor/prover cycle measurement helpers +//! shared between the `cycle_bench` binary and the `verify` criterion bench. + +#![expect( + clippy::arithmetic_side_effects, + clippy::as_conversions, + clippy::cast_precision_loss, + clippy::float_arithmetic, + clippy::print_literal, + clippy::print_stdout, + reason = "Bench library: stats arithmetic and table printing are bench-style" +)] +#![cfg_attr( + feature = "ppe", + expect( + clippy::arbitrary_source_item_ordering, + clippy::print_stderr, + reason = "PPE module: re-export ordering and eprintln progress trip strict lints" + ) +)] + +pub mod ppe; +pub mod stats; diff --git a/tools/cycle_bench/src/main.rs b/tools/cycle_bench/src/main.rs index acdd9eff..bb44fb4f 100644 --- a/tools/cycle_bench/src/main.rs +++ b/tools/cycle_bench/src/main.rs @@ -9,15 +9,11 @@ #![expect( clippy::arithmetic_side_effects, - clippy::as_conversions, - clippy::cast_precision_loss, clippy::float_arithmetic, clippy::missing_const_for_fn, clippy::non_ascii_literal, - clippy::print_literal, clippy::print_stderr, clippy::print_stdout, - clippy::ref_patterns, reason = "Bench tool: matches test-style fixture code" )] @@ -31,6 +27,7 @@ use clock_core::{ CLOCK_01_PROGRAM_ACCOUNT_ID, CLOCK_10_PROGRAM_ACCOUNT_ID, CLOCK_50_PROGRAM_ACCOUNT_ID, ClockAccountData, }; +use cycle_bench::{ppe, stats::Stats}; use nssa::program_methods::{ AMM_ELF, AMM_ID, ASSOCIATED_TOKEN_ACCOUNT_ELF, ASSOCIATED_TOKEN_ACCOUNT_ID, AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, CLOCK_ELF, CLOCK_ID, TOKEN_ELF, @@ -43,12 +40,8 @@ use nssa_core::{ }; use risc0_zkvm::{ExecutorEnv, default_executor, default_prover}; use serde::Serialize; -use stats::Stats; use token_core::{TokenDefinition, TokenHolding}; -mod ppe; -mod stats; - #[derive(Parser, Debug)] #[command(about = "Per-program executor and (optionally) prover cycle measurements")] struct Cli { @@ -62,16 +55,6 @@ struct Cli { #[arg(long)] ppe: bool, - /// After running --ppe-style proving once for auth_transfer-in-PPE, time - /// `receipt.verify(PRIVACY_PRESERVING_CIRCUIT_ID)` over many iterations. - /// Produces `G_verify` for the fee model. Requires --features ppe. - #[arg(long)] - verify: bool, - - /// Iterations for --verify. Default matches the fee-model handoff target. - #[arg(long, default_value_t = 1000)] - verify_iters: usize, - /// Iterations for executor wall-time sampling per case. First iter is /// discarded as warmup, remaining N feed the stats. #[arg(long, default_value_t = 5)] @@ -428,7 +411,7 @@ fn main() -> Result<()> { AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, authenticated_transfer_transfer(), - &5_000_u128, + &authenticated_transfer_core::Instruction::Transfer { amount: 5_000 }, )?, Case::new( "authenticated_transfer", @@ -436,7 +419,7 @@ fn main() -> Result<()> { AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, authenticated_transfer_init(), - &0_u128, + &authenticated_transfer_core::Instruction::Initialize, )?, Case::new( "token", @@ -532,23 +515,6 @@ fn main() -> Result<()> { ppe::print_table(&ppe_results); } - #[cfg(feature = "ppe")] - let verify_result = if cli.verify { - Some(ppe::run_verify(cli.verify_iters)?) - } else { - None - }; - #[cfg(not(feature = "ppe"))] - let verify_result: Option = { - if cli.verify { - eprintln!("cycle_bench: --verify requires --features ppe at build time. Ignoring."); - } - None - }; - if let Some(ref vr) = verify_result { - ppe::print_verify(vr); - } - let workspace_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("..") .join("..") @@ -560,7 +526,6 @@ fn main() -> Result<()> { let combined = serde_json::json!({ "standalone": results, "ppe": ppe_results, - "verify": verify_result, }); std::fs::write(&out_path, serde_json::to_string_pretty(&combined)?)?; println!("\nJSON written to {}", out_path.display()); diff --git a/tools/cycle_bench/src/ppe.rs b/tools/cycle_bench/src/ppe.rs index e3bc4747..77ce52dc 100644 --- a/tools/cycle_bench/src/ppe.rs +++ b/tools/cycle_bench/src/ppe.rs @@ -5,24 +5,23 @@ //! that wraps the same program in the privacy circuit. Chained-call depth sweep //! uses the `chain_caller` test program (loaded from artifacts/) with N=1, 3, 5, 9. //! -//! `run_verify` produces `G_verify` for the fee model: it generates one PPE -//! receipt (`auth_transfer` Transfer in PPE) and times `Receipt::verify` over -//! `iters` iterations. The proof bytes captured here are also the on-wire -//! "outer proof" payload (`S_agg` in the fee model). +//! `Receipt::verify(PRIVACY_PRESERVING_CIRCUIT_ID)` timings (the `G_verify` fee-model +//! parameter) are measured by the `verify` criterion bench under `benches/verify.rs`, +//! which reuses the `prove_auth_transfer_in_ppe` setup helper re-exported below. #![allow( dead_code, reason = "Stubs are used when the `ppe` feature is disabled." )] -use anyhow::Result; use serde::Serialize; -use crate::stats::Stats; - #[cfg(feature = "ppe")] mod ppe_impl; +#[cfg(feature = "ppe")] +pub use ppe_impl::prove_auth_transfer_in_ppe; + #[derive(Debug, Serialize, Clone)] pub struct PpeBenchResult { pub label: String, @@ -33,20 +32,14 @@ pub struct PpeBenchResult { pub error: Option, } -#[derive(Debug, Serialize, Clone)] -pub struct VerifyBenchResult { - pub label: String, - pub stats: Stats, - pub proof_bytes: usize, - pub journal_bytes: usize, -} - #[cfg(not(feature = "ppe"))] -pub fn run_all() -> Vec { +#[must_use] +pub const fn run_all() -> Vec { Vec::new() } #[cfg(feature = "ppe")] +#[must_use] pub fn run_all() -> Vec { let mut results = Vec::new(); @@ -61,16 +54,6 @@ pub fn run_all() -> Vec { results } -#[cfg(not(feature = "ppe"))] -pub fn run_verify(_iters: usize) -> Result { - anyhow::bail!("--verify requires --features ppe at build time") -} - -#[cfg(feature = "ppe")] -pub fn run_verify(iters: usize) -> Result { - ppe_impl::run_verify(iters) -} - pub fn print_table(results: &[PpeBenchResult]) { let lw = results .iter() @@ -109,14 +92,3 @@ pub fn print_table(results: &[PpeBenchResult]) { ); } } - -pub fn print_verify(r: &VerifyBenchResult) { - println!("\nVerify (G_verify):"); - println!(" case : {}", r.label); - println!( - " proof_bytes : {} (borsh InnerReceipt, S_agg)", - r.proof_bytes - ); - println!(" journal_bytes : {}", r.journal_bytes); - println!(" verify_ms : {}", r.stats); -} diff --git a/tools/cycle_bench/src/ppe/ppe_impl.rs b/tools/cycle_bench/src/ppe/ppe_impl.rs index c20db9ac..433c4aa4 100644 --- a/tools/cycle_bench/src/ppe/ppe_impl.rs +++ b/tools/cycle_bench/src/ppe/ppe_impl.rs @@ -1,4 +1,7 @@ -//! Feature-gated implementation of PPE composition and verify benches. +//! Feature-gated implementation of PPE composition benches. +//! +//! `prove_auth_transfer_in_ppe` is reused by the `verify` criterion bench under +//! `benches/verify.rs` (re-exported via `super::prove_auth_transfer_in_ppe`). use std::{collections::HashMap, time::Instant}; @@ -6,17 +9,15 @@ use nssa::{ execute_and_prove, privacy_preserving_transaction::circuit::{ProgramWithDependencies, Proof}, program::Program, - program_methods::PRIVACY_PRESERVING_CIRCUIT_ID, }; use nssa_core::{ InputAccountIdentity, PrivacyPreservingCircuitOutput, account::{Account, AccountId, AccountWithMetadata}, program::ProgramId, }; -use risc0_zkvm::{InnerReceipt, Receipt, serde::to_vec}; +use risc0_zkvm::serde::to_vec; -use super::{PpeBenchResult, VerifyBenchResult}; -use crate::stats::Stats; +use super::PpeBenchResult; const AUTH_TRANSFER_ID: ProgramId = nssa::program_methods::AUTHENTICATED_TRANSFER_ID; const AUTH_TRANSFER_ELF: &[u8] = nssa::program_methods::AUTHENTICATED_TRANSFER_ELF; @@ -50,7 +51,7 @@ pub fn run_auth_transfer_in_ppe() -> PpeBenchResult { } } -fn prove_auth_transfer_in_ppe() -> anyhow::Result<(PrivacyPreservingCircuitOutput, Proof)> { +pub fn prove_auth_transfer_in_ppe() -> anyhow::Result<(PrivacyPreservingCircuitOutput, Proof)> { let program = Program::new(AUTH_TRANSFER_ELF.to_vec())?; let pwd = ProgramWithDependencies::from(program); @@ -73,8 +74,8 @@ fn prove_auth_transfer_in_ppe() -> anyhow::Result<(PrivacyPreservingCircuitOutpu }; let pre_states = vec![sender, recipient]; - let balance_to_move: u128 = 5_000; - let instruction_data = to_vec(&balance_to_move)?; + let instruction = authenticated_transfer_core::Instruction::Transfer { amount: 5_000 }; + let instruction_data = to_vec(&instruction)?; let account_identities = vec![InputAccountIdentity::Public; pre_states.len()]; @@ -156,39 +157,3 @@ fn prove_chain_caller( &pwd, )?) } - -pub fn run_verify(iters: usize) -> anyhow::Result { - eprintln!("verify: generating PPE receipt for auth_transfer Transfer (~1 prove)"); - let (output, proof) = prove_auth_transfer_in_ppe()?; - let journal = output.to_bytes(); - let journal_bytes = journal.len(); - let proof_bytes_vec = proof.into_inner(); - let proof_bytes = proof_bytes_vec.len(); - - let inner: InnerReceipt = borsh::from_slice(&proof_bytes_vec) - .map_err(|e| anyhow::anyhow!("InnerReceipt deserialize: {e}"))?; - let receipt = Receipt::new(inner, journal); - - // Sanity-check before the timing loop so we don't measure 1000 failures. - receipt - .verify(PRIVACY_PRESERVING_CIRCUIT_ID) - .map_err(|e| anyhow::anyhow!("verify sanity check failed: {e}"))?; - - eprintln!("verify: timing {iters} iters of receipt.verify(...)"); - let mut samples = Vec::with_capacity(iters); - for _ in 0..iters { - let started = Instant::now(); - receipt - .verify(PRIVACY_PRESERVING_CIRCUIT_ID) - .map_err(|e| anyhow::anyhow!("verify failed mid-loop: {e}"))?; - samples.push(started.elapsed().as_secs_f64() * 1_000.0); - } - let stats = Stats::from_samples(&samples); - - Ok(VerifyBenchResult { - label: "auth_transfer Transfer in PPE".to_owned(), - stats, - proof_bytes, - journal_bytes, - }) -}