From 49bfc2c6e657335bdc348392bd4805eda6e44c8b Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Fri, 30 May 2025 09:45:14 -0300 Subject: [PATCH 01/13] perf: pararellize proofs verification --- aggregation_mode/Cargo.lock | 1 + aggregation_mode/Cargo.toml | 1 + .../src/aggregators/sp1_aggregator.rs | 11 +++++--- aggregation_mode/src/backend/fetcher.rs | 27 ++++++++++++------- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/aggregation_mode/Cargo.lock b/aggregation_mode/Cargo.lock index ff66359b2a..cb802c1662 100644 --- a/aggregation_mode/Cargo.lock +++ b/aggregation_mode/Cargo.lock @@ -6195,6 +6195,7 @@ dependencies = [ "c-kzg", "ciborium", "lambdaworks-crypto", + "rayon", "reqwest 0.12.15", "risc0-build", "risc0-ethereum-contracts", diff --git a/aggregation_mode/Cargo.toml b/aggregation_mode/Cargo.toml index fe40bbd5ac..875a90b0ca 100644 --- a/aggregation_mode/Cargo.toml +++ b/aggregation_mode/Cargo.toml @@ -17,6 +17,7 @@ sha3 = "0.10.8" reqwest = { version = "0.12" } ciborium = "=0.2.2" lambdaworks-crypto = { git = "https://github.com/lambdaclass/lambdaworks.git", rev = "5f8f2cfcc8a1a22f77e8dff2d581f1166eefb80b", features = ["serde"]} +rayon = "1.10.0" # Necessary for the VerificationData type aligned-sdk = { path = "../batcher/aligned-sdk/" } # zkvms diff --git a/aggregation_mode/src/aggregators/sp1_aggregator.rs b/aggregation_mode/src/aggregators/sp1_aggregator.rs index 33c7001a22..e9923da229 100644 --- a/aggregation_mode/src/aggregators/sp1_aggregator.rs +++ b/aggregation_mode/src/aggregators/sp1_aggregator.rs @@ -3,7 +3,7 @@ use std::sync::LazyLock; use alloy::primitives::Keccak256; use sp1_aggregation_program::SP1VkAndPubInputs; use sp1_sdk::{ - EnvProver, HashableKey, Prover, ProverClient, SP1ProofWithPublicValues, SP1Stdin, + CpuProver, EnvProver, HashableKey, Prover, ProverClient, SP1ProofWithPublicValues, SP1Stdin, SP1VerifyingKey, }; @@ -14,6 +14,11 @@ const USER_PROOFS_PROGRAM_ELF: &[u8] = include_bytes!("../../aggregation_programs/sp1/elf/sp1_user_proofs_aggregator_program"); static SP1_PROVER_CLIENT: LazyLock = LazyLock::new(ProverClient::from_env); +/// Separate prover instance configured to always use the CPU. +/// This is used for verification, which is performed in parallel and +/// cannot be done on the GPU. +static SP1_PROVER_CLIENT_CPU: LazyLock = + LazyLock::new(|| ProverClient::builder().cpu().build()); pub struct SP1ProofWithPubValuesAndElf { pub proof_with_pub_values: SP1ProofWithPublicValues, @@ -182,7 +187,7 @@ pub enum AlignedSP1VerificationError { pub(crate) fn verify( sp1_proof_with_pub_values_and_elf: &SP1ProofWithPubValuesAndElf, ) -> Result<(), AlignedSP1VerificationError> { - let client = &*SP1_PROVER_CLIENT; + let client = &*SP1_PROVER_CLIENT_CPU; let (_pk, vk) = client.setup(&sp1_proof_with_pub_values_and_elf.elf); @@ -202,7 +207,7 @@ pub(crate) fn verify( } pub fn vk_from_elf(elf: &[u8]) -> SP1VerifyingKey { - let prover = &*SP1_PROVER_CLIENT; + let prover = &*SP1_PROVER_CLIENT_CPU; let (_, vk) = prover.setup(elf); vk } diff --git a/aggregation_mode/src/backend/fetcher.rs b/aggregation_mode/src/backend/fetcher.rs index b264c50217..d6b66f8f18 100644 --- a/aggregation_mode/src/backend/fetcher.rs +++ b/aggregation_mode/src/backend/fetcher.rs @@ -16,6 +16,7 @@ use alloy::{ primitives::Address, providers::{Provider, ProviderBuilder}, }; +use rayon::prelude::*; use risc0_zkvm::Receipt; use tracing::{error, info}; @@ -109,7 +110,7 @@ impl ProofsFetcher { info!("Data downloaded from S3, number of proofs {}", data.len()); // Filter compatible proofs to be aggregated and push to queue - let proofs_to_add: Vec = match engine { + let mut proofs_to_add: Vec = match engine { ZKVMEngine::SP1 => data .into_iter() .filter_map(|p| match p.proving_system { @@ -153,15 +154,21 @@ impl ProofsFetcher { proofs_to_add.len() ); - // try to add them to the queue - for proof in proofs_to_add { - if let Err(err) = proof.verify() { - error!("Could not add proof, verification failed: {:?}", err); - continue; - }; - - proofs.push(proof); - } + // Try to add them to the queue + // We do this in parallel, as SP1 can take quite some time in verifying + // because of the overhead of setting up the prover + proofs_to_add = proofs_to_add + .into_par_iter() + .filter(|proof| match proof.verify() { + Ok(_) => true, + Err(err) => { + error!("Could not add proof, verification failed: {:?}", err); + return false; + } + }) + .collect(); + + proofs.extend(proofs_to_add); } Ok(proofs) From e38c80a56f2f478370e488c7da8344621bce3116 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Fri, 30 May 2025 09:45:47 -0300 Subject: [PATCH 02/13] perf: sp1 obtain vk from proof instead of setting up the prover client --- aggregation_mode/src/aggregators/sp1_aggregator.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/aggregation_mode/src/aggregators/sp1_aggregator.rs b/aggregation_mode/src/aggregators/sp1_aggregator.rs index e9923da229..823be47e9a 100644 --- a/aggregation_mode/src/aggregators/sp1_aggregator.rs +++ b/aggregation_mode/src/aggregators/sp1_aggregator.rs @@ -35,7 +35,16 @@ impl SP1ProofWithPubValuesAndElf { } pub fn vk(&self) -> SP1VerifyingKey { - vk_from_elf(&self.elf) + // it is safe to unwrap here as we only support compressed proofs for sp1 + let vk = self + .proof_with_pub_values + .proof + .try_as_compressed_ref() + .unwrap() + .vk + .clone(); + + SP1VerifyingKey { vk } } } From 7e406538fd3dff5cd508feebedc0520d2f1193a9 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Fri, 30 May 2025 09:51:27 -0300 Subject: [PATCH 03/13] feat: pre-verificatoin-enabled flag to avoid verification in the fetching process --- aggregation_mode/src/backend/config.rs | 1 + aggregation_mode/src/backend/fetcher.rs | 30 +++++++++++-------- ...fig-proof-aggregator-ethereum-package.yaml | 1 + ...roof-aggregator-mock-ethereum-package.yaml | 1 + .../config-proof-aggregator-mock.yaml | 1 + config-files/config-proof-aggregator.yaml | 1 + 6 files changed, 22 insertions(+), 13 deletions(-) diff --git a/aggregation_mode/src/backend/config.rs b/aggregation_mode/src/backend/config.rs index 0fb47d2775..c9d12e0470 100644 --- a/aggregation_mode/src/backend/config.rs +++ b/aggregation_mode/src/backend/config.rs @@ -22,6 +22,7 @@ pub struct Config { pub last_aggregated_block_filepath: String, pub ecdsa: ECDSAConfig, pub proofs_per_chunk: u16, + pub pre_verification_enabled: bool, } impl Config { diff --git a/aggregation_mode/src/backend/fetcher.rs b/aggregation_mode/src/backend/fetcher.rs index d6b66f8f18..1ee12e5a71 100644 --- a/aggregation_mode/src/backend/fetcher.rs +++ b/aggregation_mode/src/backend/fetcher.rs @@ -30,6 +30,7 @@ pub struct ProofsFetcher { rpc_provider: RPCProvider, aligned_service_manager: AlignedLayerServiceManagerContract, last_aggregated_block: u64, + pre_verification_enabled: bool, } impl ProofsFetcher { @@ -48,6 +49,7 @@ impl ProofsFetcher { rpc_provider, aligned_service_manager, last_aggregated_block, + pre_verification_enabled: config.pre_verification_enabled, } } @@ -154,19 +156,21 @@ impl ProofsFetcher { proofs_to_add.len() ); - // Try to add them to the queue - // We do this in parallel, as SP1 can take quite some time in verifying - // because of the overhead of setting up the prover - proofs_to_add = proofs_to_add - .into_par_iter() - .filter(|proof| match proof.verify() { - Ok(_) => true, - Err(err) => { - error!("Could not add proof, verification failed: {:?}", err); - return false; - } - }) - .collect(); + if self.pre_verification_enabled { + // Try to add them to the queue + // We do this in parallel, as SP1 can take quite some time in verifying + // because of the overhead of setting up the prover + proofs_to_add = proofs_to_add + .into_par_iter() + .filter(|proof| match proof.verify() { + Ok(_) => true, + Err(err) => { + error!("Could not add proof, verification failed: {:?}", err); + return false; + } + }) + .collect(); + } proofs.extend(proofs_to_add); } diff --git a/config-files/config-proof-aggregator-ethereum-package.yaml b/config-files/config-proof-aggregator-ethereum-package.yaml index 73f3f94305..7153ca8a0c 100644 --- a/config-files/config-proof-aggregator-ethereum-package.yaml +++ b/config-files/config-proof-aggregator-ethereum-package.yaml @@ -5,6 +5,7 @@ eth_ws_url: "ws://localhost:8546" max_proofs_in_queue: 1000 last_aggregated_block_filepath: config-files/proof-aggregator.last_aggregated_block.json proofs_per_chunk: 512 # Amount of proofs to process per chunk +pre_verification_enabled: true ecdsa: private_key_store_path: "config-files/anvil.proof-aggregator.ecdsa.key.json" diff --git a/config-files/config-proof-aggregator-mock-ethereum-package.yaml b/config-files/config-proof-aggregator-mock-ethereum-package.yaml index e805af2b86..6450eef458 100644 --- a/config-files/config-proof-aggregator-mock-ethereum-package.yaml +++ b/config-files/config-proof-aggregator-mock-ethereum-package.yaml @@ -5,6 +5,7 @@ eth_ws_url: "ws://localhost:8546" max_proofs_in_queue: 1000 last_aggregated_block_filepath: config-files/proof-aggregator.last_aggregated_block.json proofs_per_chunk: 512 # Amount of proofs to process per chunk +pre_verification_enabled: true ecdsa: private_key_store_path: "config-files/anvil.proof-aggregator.ecdsa.key.json" diff --git a/config-files/config-proof-aggregator-mock.yaml b/config-files/config-proof-aggregator-mock.yaml index 644ed44b9c..0427b9813c 100644 --- a/config-files/config-proof-aggregator-mock.yaml +++ b/config-files/config-proof-aggregator-mock.yaml @@ -5,6 +5,7 @@ eth_ws_url: "ws://localhost:8545" max_proofs_in_queue: 1000 last_aggregated_block_filepath: config-files/proof-aggregator.last_aggregated_block.json proofs_per_chunk: 512 # Amount of proofs to process per chunk +pre_verification_enabled: true ecdsa: private_key_store_path: "config-files/anvil.proof-aggregator.ecdsa.key.json" diff --git a/config-files/config-proof-aggregator.yaml b/config-files/config-proof-aggregator.yaml index f60b9dc902..2406f08b1f 100644 --- a/config-files/config-proof-aggregator.yaml +++ b/config-files/config-proof-aggregator.yaml @@ -5,6 +5,7 @@ eth_ws_url: "ws://localhost:8545" max_proofs_in_queue: 1000 last_aggregated_block_filepath: config-files/proof-aggregator.last_aggregated_block.json proofs_per_chunk: 512 # Amount of proofs to process per chunk +pre_verification_enabled: true ecdsa: private_key_store_path: "config-files/anvil.proof-aggregator.ecdsa.key.json" From 7bc4dec335e7222b7dcd49b7e77eb801b03aa00d Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Fri, 30 May 2025 09:52:11 -0300 Subject: [PATCH 04/13] chore: ignore sp1 cuda logs --- aggregation_mode/src/main.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/aggregation_mode/src/main.rs b/aggregation_mode/src/main.rs index 064a0951a0..e1793337c4 100644 --- a/aggregation_mode/src/main.rs +++ b/aggregation_mode/src/main.rs @@ -1,7 +1,7 @@ use std::env; use proof_aggregator::backend::{config::Config, ProofAggregator}; -use tracing_subscriber::FmtSubscriber; +use tracing_subscriber::{EnvFilter, FmtSubscriber}; fn read_config_filepath_from_args() -> String { let args: Vec = env::args().collect(); @@ -17,8 +17,9 @@ fn read_config_filepath_from_args() -> String { #[tokio::main] async fn main() { - let subscriber = FmtSubscriber::builder().finish(); - tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); + // ignore spi_cude info logs + let filter = EnvFilter::new("info,sp1_cuda=warn"); + let subscriber = FmtSubscriber::builder().with_env_filter(filter).finish(); // load config let config_file_path = read_config_filepath_from_args(); From bb1bfdc2dc87c787ccf92592bb66eefc99c1eb1c Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Fri, 30 May 2025 10:00:38 -0300 Subject: [PATCH 05/13] fix: tracing subscriber --- aggregation_mode/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/aggregation_mode/src/main.rs b/aggregation_mode/src/main.rs index e1793337c4..67aea7b88d 100644 --- a/aggregation_mode/src/main.rs +++ b/aggregation_mode/src/main.rs @@ -20,6 +20,7 @@ async fn main() { // ignore spi_cude info logs let filter = EnvFilter::new("info,sp1_cuda=warn"); let subscriber = FmtSubscriber::builder().with_env_filter(filter).finish(); + tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); // load config let config_file_path = read_config_filepath_from_args(); From 0dd8dee7d10e4cebcf50452724c55e8c39fc95d2 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Fri, 30 May 2025 11:02:17 -0300 Subject: [PATCH 06/13] fix: vk compute it once --- .../src/aggregators/sp1_aggregator.rs | 36 +++++++++---------- aggregation_mode/src/backend/fetcher.rs | 6 ++-- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/aggregation_mode/src/aggregators/sp1_aggregator.rs b/aggregation_mode/src/aggregators/sp1_aggregator.rs index 823be47e9a..a3cc535ff8 100644 --- a/aggregation_mode/src/aggregators/sp1_aggregator.rs +++ b/aggregation_mode/src/aggregators/sp1_aggregator.rs @@ -23,29 +23,27 @@ static SP1_PROVER_CLIENT_CPU: LazyLock = pub struct SP1ProofWithPubValuesAndElf { pub proof_with_pub_values: SP1ProofWithPublicValues, pub elf: Vec, + pub vk: SP1VerifyingKey, } impl SP1ProofWithPubValuesAndElf { + pub fn new(proof_with_pub_values: SP1ProofWithPublicValues, elf: Vec) -> Self { + let vk = vk_from_elf(&elf); + + Self { + proof_with_pub_values, + elf, + vk, + } + } + pub fn hash_vk_and_pub_inputs(&self) -> [u8; 32] { let mut hasher = Keccak256::new(); - let vk_bytes = &self.vk().hash_bytes(); + let vk_bytes = &self.vk.hash_bytes(); hasher.update(vk_bytes); hasher.update(self.proof_with_pub_values.public_values.as_slice()); hasher.finalize().into() } - - pub fn vk(&self) -> SP1VerifyingKey { - // it is safe to unwrap here as we only support compressed proofs for sp1 - let vk = self - .proof_with_pub_values - .proof - .try_as_compressed_ref() - .unwrap() - .vk - .clone(); - - SP1VerifyingKey { vk } - } } #[derive(Debug)] @@ -70,7 +68,7 @@ pub(crate) fn run_user_proofs_aggregator( .proofs_vk_and_pub_inputs .push(SP1VkAndPubInputs { public_inputs: proof.proof_with_pub_values.public_values.to_vec(), - vk: proof.vk().hash_u32(), + vk: proof.vk.hash_u32(), }); } @@ -78,7 +76,7 @@ pub(crate) fn run_user_proofs_aggregator( // write proofs for input_proof in proofs.iter() { - let vk = input_proof.vk().vk; + let vk = input_proof.vk.vk.clone(); // we only support sp1 Compressed proofs for now let sp1_sdk::SP1Proof::Compressed(proof) = input_proof.proof_with_pub_values.proof.clone() else { @@ -110,6 +108,7 @@ pub(crate) fn run_user_proofs_aggregator( let proof_and_elf = SP1ProofWithPubValuesAndElf { proof_with_pub_values: proof, elf: USER_PROOFS_PROGRAM_ELF.to_vec(), + vk, }; Ok(proof_and_elf) @@ -129,7 +128,7 @@ pub(crate) fn run_chunk_aggregator( program_input.proofs_and_leaves_commitment.push(( SP1VkAndPubInputs { public_inputs: proof.proof_with_pub_values.public_values.to_vec(), - vk: proof.vk().hash_u32(), + vk: proof.vk.hash_u32(), }, leaves_commitment.clone(), )); @@ -139,7 +138,7 @@ pub(crate) fn run_chunk_aggregator( // write proofs for (input_proof, _) in proofs.iter() { - let vk = input_proof.vk().vk; + let vk = input_proof.vk.vk.clone(); // we only support sp1 Compressed proofs for now let sp1_sdk::SP1Proof::Compressed(proof) = input_proof.proof_with_pub_values.proof.clone() else { @@ -182,6 +181,7 @@ pub(crate) fn run_chunk_aggregator( let proof_and_elf = SP1ProofWithPubValuesAndElf { proof_with_pub_values: proof, elf: CHUNK_PROGRAM_ELF.to_vec(), + vk, }; Ok(proof_and_elf) diff --git a/aggregation_mode/src/backend/fetcher.rs b/aggregation_mode/src/backend/fetcher.rs index 1ee12e5a71..45b3190151 100644 --- a/aggregation_mode/src/backend/fetcher.rs +++ b/aggregation_mode/src/backend/fetcher.rs @@ -119,10 +119,8 @@ impl ProofsFetcher { ProvingSystemId::SP1 => { let elf = p.vm_program_code?; let proof_with_pub_values = bincode::deserialize(&p.proof).ok()?; - let sp1_proof = SP1ProofWithPubValuesAndElf { - proof_with_pub_values, - elf, - }; + let sp1_proof = + SP1ProofWithPubValuesAndElf::new(proof_with_pub_values, elf); Some(AlignedProof::SP1(sp1_proof.into())) } From 82ffe4e31181d5ab8448d201777c9e198787a320 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Fri, 30 May 2025 13:52:15 -0300 Subject: [PATCH 07/13] feat: move verification to filter to compute vk only once and in parallel --- aggregation_mode/src/aggregators/mod.rs | 21 ----- .../src/aggregators/risc0_aggregator.rs | 49 ++++++---- .../src/aggregators/sp1_aggregator.rs | 63 +++++++------ aggregation_mode/src/backend/fetcher.rs | 91 ++++++++++--------- 4 files changed, 113 insertions(+), 111 deletions(-) diff --git a/aggregation_mode/src/aggregators/mod.rs b/aggregation_mode/src/aggregators/mod.rs index d9c45cf3de..a3568c710f 100644 --- a/aggregation_mode/src/aggregators/mod.rs +++ b/aggregation_mode/src/aggregators/mod.rs @@ -219,24 +219,3 @@ impl IsMerkleTreeBackend for AlignedProof { hasher.finalize().into() } } - -#[derive(Debug)] -pub enum AlignedVerificationError { - Sp1(AlignedSP1VerificationError), - Risc0(AlignedRisc0VerificationError), -} - -impl AlignedProof { - pub fn verify(&self) -> Result<(), AlignedVerificationError> { - match self { - AlignedProof::SP1(proof) => sp1_aggregator::verify(proof).map_err( - |arg0: sp1_aggregator::AlignedSP1VerificationError| { - AlignedVerificationError::Sp1(arg0) - }, - ), - AlignedProof::Risc0(proof) => { - risc0_aggregator::verify(proof).map_err(AlignedVerificationError::Risc0) - } - } - } -} diff --git a/aggregation_mode/src/aggregators/risc0_aggregator.rs b/aggregation_mode/src/aggregators/risc0_aggregator.rs index 2baf102640..6e1187ced3 100644 --- a/aggregation_mode/src/aggregators/risc0_aggregator.rs +++ b/aggregation_mode/src/aggregators/risc0_aggregator.rs @@ -8,7 +8,36 @@ pub struct Risc0ProofReceiptAndImageId { pub receipt: Receipt, } +#[derive(Debug)] +pub enum AlignedRisc0VerificationError { + Verification(String), + UnsupportedProof, +} + impl Risc0ProofReceiptAndImageId { + pub fn new(image_id: [u8; 32], receipt: Receipt) -> Self { + Self { image_id, receipt } + } + + pub fn new_with_verification( + image_id: [u8; 32], + receipt: Receipt, + ) -> Result { + let is_supported_proof = + proof.receipt.inner.composite().is_ok() || proof.receipt.inner.succinct().is_ok(); + + if is_supported_proof { + proof + .receipt + .verify(proof.image_id) + .map_err(|e| AlignedRisc0VerificationError::Verification(e.to_string()))?; + } else { + return Err(AlignedRisc0VerificationError::UnsupportedProof); + } + + Ok(Self { image_id, receipt }) + } + pub fn public_inputs(&self) -> &Vec { &self.receipt.journal.bytes } @@ -22,12 +51,6 @@ pub enum Risc0AggregationError { Verification(String), } -#[derive(Debug)] -pub enum AlignedRisc0VerificationError { - Verification(String), - UnsupportedProof, -} - /// Byte representation of the user proofs aggregator image_id, converted from `[u32; 8]` to `[u8; 32]`. pub const RISC0_USER_PROOFS_AGGREGATOR_PROGRAM_ID_BYTES: [u8; 32] = { let mut res = [0u8; 32]; @@ -169,17 +192,3 @@ pub(crate) fn run_chunk_aggregator( Ok(proof) } - -pub(crate) fn verify( - proof: &Risc0ProofReceiptAndImageId, -) -> Result<(), AlignedRisc0VerificationError> { - // only stark proofs are supported for recursion - if proof.receipt.inner.composite().is_ok() || proof.receipt.inner.succinct().is_ok() { - proof - .receipt - .verify(proof.image_id) - .map_err(|e| AlignedRisc0VerificationError::Verification(e.to_string())) - } else { - Err(AlignedRisc0VerificationError::UnsupportedProof) - } -} diff --git a/aggregation_mode/src/aggregators/sp1_aggregator.rs b/aggregation_mode/src/aggregators/sp1_aggregator.rs index a3cc535ff8..dcb7755b8b 100644 --- a/aggregation_mode/src/aggregators/sp1_aggregator.rs +++ b/aggregation_mode/src/aggregators/sp1_aggregator.rs @@ -26,6 +26,12 @@ pub struct SP1ProofWithPubValuesAndElf { pub vk: SP1VerifyingKey, } +#[derive(Debug)] +pub enum AlignedSP1VerificationError { + Verification(sp1_sdk::SP1VerificationError), + UnsupportedProof, +} + impl SP1ProofWithPubValuesAndElf { pub fn new(proof_with_pub_values: SP1ProofWithPublicValues, elf: Vec) -> Self { let vk = vk_from_elf(&elf); @@ -37,6 +43,35 @@ impl SP1ProofWithPubValuesAndElf { } } + pub fn new_with_verification( + proof_with_pub_values: SP1ProofWithPublicValues, + elf: Vec, + ) -> Result { + let client = &*SP1_PROVER_CLIENT_CPU; + + let (_pk, vk) = client.setup(&sp1_proof_with_pub_values_and_elf.elf); + + // only sp1 compressed proofs are supported for aggregation now + match sp1_proof_with_pub_values_and_elf + .proof_with_pub_values + .proof + { + sp1_sdk::SP1Proof::Compressed(_) => client + .verify( + &sp1_proof_with_pub_values_and_elf.proof_with_pub_values, + &vk, + ) + .map_err(AlignedSP1VerificationError::Verification), + _ => Err(AlignedSP1VerificationError::UnsupportedProof), + }?; + + Ok(Self { + proof_with_pub_values, + elf, + vk, + }) + } + pub fn hash_vk_and_pub_inputs(&self) -> [u8; 32] { let mut hasher = Keccak256::new(); let vk_bytes = &self.vk.hash_bytes(); @@ -187,34 +222,6 @@ pub(crate) fn run_chunk_aggregator( Ok(proof_and_elf) } -#[derive(Debug)] -pub enum AlignedSP1VerificationError { - Verification(sp1_sdk::SP1VerificationError), - UnsupportedProof, -} - -pub(crate) fn verify( - sp1_proof_with_pub_values_and_elf: &SP1ProofWithPubValuesAndElf, -) -> Result<(), AlignedSP1VerificationError> { - let client = &*SP1_PROVER_CLIENT_CPU; - - let (_pk, vk) = client.setup(&sp1_proof_with_pub_values_and_elf.elf); - - // only sp1 compressed proofs are supported for aggregation now - match sp1_proof_with_pub_values_and_elf - .proof_with_pub_values - .proof - { - sp1_sdk::SP1Proof::Compressed(_) => client - .verify( - &sp1_proof_with_pub_values_and_elf.proof_with_pub_values, - &vk, - ) - .map_err(AlignedSP1VerificationError::Verification), - _ => Err(AlignedSP1VerificationError::UnsupportedProof), - } -} - pub fn vk_from_elf(elf: &[u8]) -> SP1VerifyingKey { let prover = &*SP1_PROVER_CLIENT_CPU; let (_, vk) = prover.setup(elf); diff --git a/aggregation_mode/src/backend/fetcher.rs b/aggregation_mode/src/backend/fetcher.rs index 45b3190151..e2af8387a4 100644 --- a/aggregation_mode/src/backend/fetcher.rs +++ b/aggregation_mode/src/backend/fetcher.rs @@ -112,38 +112,61 @@ impl ProofsFetcher { info!("Data downloaded from S3, number of proofs {}", data.len()); // Filter compatible proofs to be aggregated and push to queue - let mut proofs_to_add: Vec = match engine { + let proofs_to_add: Vec = match engine { ZKVMEngine::SP1 => data - .into_iter() - .filter_map(|p| match p.proving_system { - ProvingSystemId::SP1 => { - let elf = p.vm_program_code?; - let proof_with_pub_values = bincode::deserialize(&p.proof).ok()?; - let sp1_proof = - SP1ProofWithPubValuesAndElf::new(proof_with_pub_values, elf); - - Some(AlignedProof::SP1(sp1_proof.into())) + .into_par_iter() + .filter_map(|p| { + if p.proving_system != ProvingSystemId::SP1 { + return None; + }; + + let elf = p.vm_program_code?; + let proof_with_pub_values = bincode::deserialize(&p.proof).ok()?; + let sp1_proof = if self.pre_verification_enabled { + SP1ProofWithPubValuesAndElf::new_with_verification( + proof_with_pub_values, + elf, + ) + } else { + Ok(SP1ProofWithPubValuesAndElf::new(proof_with_pub_values, elf)) + }; + + match sp1_proof { + Ok(proof) => Some(AlignedProof::SP1(sp1_proof)), + Err(err) => { + error!("Could not add proof, verification failed: {:?}", err); + None + } } - - _ => None, }) .collect(), ZKVMEngine::RISC0 => data - .into_iter() - .filter_map(|p| match p.proving_system { - ProvingSystemId::Risc0 => { - let mut image_id = [0u8; 32]; - image_id.copy_from_slice(p.vm_program_code?.as_slice()); - let public_inputs = p.pub_input?; - let inner_receipt: risc0_zkvm::InnerReceipt = - bincode::deserialize(&p.proof).ok()?; - - let receipt = Receipt::new(inner_receipt, public_inputs); - let risc0_proof = Risc0ProofReceiptAndImageId { image_id, receipt }; - - Some(AlignedProof::Risc0(risc0_proof.into())) + .into_par_iter() + .filter_map(|p| { + if p.proving_system != ProvingSystemId::Risc0 { + return None; + }; + + let mut image_id = [0u8; 32]; + image_id.copy_from_slice(p.vm_program_code?.as_slice()); + let public_inputs = p.pub_input?; + let inner_receipt: risc0_zkvm::InnerReceipt = + bincode::deserialize(&p.proof).ok()?; + + let receipt = Receipt::new(inner_receipt, public_inputs); + let risc0_proof = if self.pre_verification_enabled { + Risc0ProofReceiptAndImageId::new_with_verification(image_id, receipt) + } else { + Ok(Risc0ProofReceiptAndImageId::new(image_id, receipt)) + }; + + match risc0_proof { + Ok(proof) => Some(AlignedProof::Risc0(risc0_proof)), + Err(err) => { + error!("Could not add proof, verification failed: {:?}", err); + None + } } - _ => None, }) .collect(), }; @@ -154,22 +177,6 @@ impl ProofsFetcher { proofs_to_add.len() ); - if self.pre_verification_enabled { - // Try to add them to the queue - // We do this in parallel, as SP1 can take quite some time in verifying - // because of the overhead of setting up the prover - proofs_to_add = proofs_to_add - .into_par_iter() - .filter(|proof| match proof.verify() { - Ok(_) => true, - Err(err) => { - error!("Could not add proof, verification failed: {:?}", err); - return false; - } - }) - .collect(); - } - proofs.extend(proofs_to_add); } From 3f483e02e659045f1842d653f1394e0a7b52ff13 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Fri, 30 May 2025 14:03:13 -0300 Subject: [PATCH 08/13] fix: address clippy warnings --- aggregation_mode/src/aggregators/mod.rs | 8 ++------ .../src/aggregators/risc0_aggregator.rs | 7 +++---- aggregation_mode/src/aggregators/sp1_aggregator.rs | 14 +++++--------- aggregation_mode/src/backend/fetcher.rs | 4 ++-- 4 files changed, 12 insertions(+), 21 deletions(-) diff --git a/aggregation_mode/src/aggregators/mod.rs b/aggregation_mode/src/aggregators/mod.rs index a3568c710f..72d22edfdb 100644 --- a/aggregation_mode/src/aggregators/mod.rs +++ b/aggregation_mode/src/aggregators/mod.rs @@ -4,13 +4,9 @@ pub mod sp1_aggregator; use std::fmt::Display; use lambdaworks_crypto::merkle_tree::traits::IsMerkleTreeBackend; -use risc0_aggregator::{ - AlignedRisc0VerificationError, Risc0AggregationError, Risc0ProofReceiptAndImageId, -}; +use risc0_aggregator::{Risc0AggregationError, Risc0ProofReceiptAndImageId}; use sha3::{Digest, Keccak256}; -use sp1_aggregator::{ - AlignedSP1VerificationError, SP1AggregationError, SP1ProofWithPubValuesAndElf, -}; +use sp1_aggregator::{SP1AggregationError, SP1ProofWithPubValuesAndElf}; use tracing::info; #[derive(Clone, Debug)] diff --git a/aggregation_mode/src/aggregators/risc0_aggregator.rs b/aggregation_mode/src/aggregators/risc0_aggregator.rs index 6e1187ced3..c4ac33d7fc 100644 --- a/aggregation_mode/src/aggregators/risc0_aggregator.rs +++ b/aggregation_mode/src/aggregators/risc0_aggregator.rs @@ -24,12 +24,11 @@ impl Risc0ProofReceiptAndImageId { receipt: Receipt, ) -> Result { let is_supported_proof = - proof.receipt.inner.composite().is_ok() || proof.receipt.inner.succinct().is_ok(); + receipt.inner.composite().is_ok() || receipt.inner.succinct().is_ok(); if is_supported_proof { - proof - .receipt - .verify(proof.image_id) + receipt + .verify(image_id) .map_err(|e| AlignedRisc0VerificationError::Verification(e.to_string()))?; } else { return Err(AlignedRisc0VerificationError::UnsupportedProof); diff --git a/aggregation_mode/src/aggregators/sp1_aggregator.rs b/aggregation_mode/src/aggregators/sp1_aggregator.rs index dcb7755b8b..4aa449755e 100644 --- a/aggregation_mode/src/aggregators/sp1_aggregator.rs +++ b/aggregation_mode/src/aggregators/sp1_aggregator.rs @@ -13,7 +13,9 @@ const CHUNK_PROGRAM_ELF: &[u8] = const USER_PROOFS_PROGRAM_ELF: &[u8] = include_bytes!("../../aggregation_programs/sp1/elf/sp1_user_proofs_aggregator_program"); +#[allow(dead_code)] static SP1_PROVER_CLIENT: LazyLock = LazyLock::new(ProverClient::from_env); + /// Separate prover instance configured to always use the CPU. /// This is used for verification, which is performed in parallel and /// cannot be done on the GPU. @@ -49,18 +51,12 @@ impl SP1ProofWithPubValuesAndElf { ) -> Result { let client = &*SP1_PROVER_CLIENT_CPU; - let (_pk, vk) = client.setup(&sp1_proof_with_pub_values_and_elf.elf); + let (_pk, vk) = client.setup(&elf); // only sp1 compressed proofs are supported for aggregation now - match sp1_proof_with_pub_values_and_elf - .proof_with_pub_values - .proof - { + match proof_with_pub_values.proof { sp1_sdk::SP1Proof::Compressed(_) => client - .verify( - &sp1_proof_with_pub_values_and_elf.proof_with_pub_values, - &vk, - ) + .verify(&proof_with_pub_values, &vk) .map_err(AlignedSP1VerificationError::Verification), _ => Err(AlignedSP1VerificationError::UnsupportedProof), }?; diff --git a/aggregation_mode/src/backend/fetcher.rs b/aggregation_mode/src/backend/fetcher.rs index e2af8387a4..162e7473ea 100644 --- a/aggregation_mode/src/backend/fetcher.rs +++ b/aggregation_mode/src/backend/fetcher.rs @@ -132,7 +132,7 @@ impl ProofsFetcher { }; match sp1_proof { - Ok(proof) => Some(AlignedProof::SP1(sp1_proof)), + Ok(proof) => Some(AlignedProof::SP1(proof.into())), Err(err) => { error!("Could not add proof, verification failed: {:?}", err); None @@ -161,7 +161,7 @@ impl ProofsFetcher { }; match risc0_proof { - Ok(proof) => Some(AlignedProof::Risc0(risc0_proof)), + Ok(proof) => Some(AlignedProof::Risc0(proof.into())), Err(err) => { error!("Could not add proof, verification failed: {:?}", err); None From ba048a57a9de069f0a6a667b7fb0b302e6aa7bde Mon Sep 17 00:00:00 2001 From: Marcos Nicolau <76252340+MarcosNicolau@users.noreply.github.com> Date: Fri, 30 May 2025 18:05:26 -0300 Subject: [PATCH 09/13] Update aggregation_mode/src/main.rs Co-authored-by: Julian Arce <52429267+JuArce@users.noreply.github.com> --- aggregation_mode/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aggregation_mode/src/main.rs b/aggregation_mode/src/main.rs index 67aea7b88d..382628cafd 100644 --- a/aggregation_mode/src/main.rs +++ b/aggregation_mode/src/main.rs @@ -17,7 +17,7 @@ fn read_config_filepath_from_args() -> String { #[tokio::main] async fn main() { - // ignore spi_cude info logs + // ignore sp1_cuda info logs let filter = EnvFilter::new("info,sp1_cuda=warn"); let subscriber = FmtSubscriber::builder().with_env_filter(filter).finish(); tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); From b9bc28ca9333b803b87e6d0ed66ed7ae5de791ea Mon Sep 17 00:00:00 2001 From: Marcos Nicolau <76252340+MarcosNicolau@users.noreply.github.com> Date: Mon, 2 Jun 2025 11:03:23 -0300 Subject: [PATCH 10/13] Update aggregation_mode/src/aggregators/sp1_aggregator.rs Co-authored-by: Mauro Toscano <12560266+MauroToscano@users.noreply.github.com> --- aggregation_mode/src/aggregators/sp1_aggregator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aggregation_mode/src/aggregators/sp1_aggregator.rs b/aggregation_mode/src/aggregators/sp1_aggregator.rs index 4aa449755e..eb5b29406a 100644 --- a/aggregation_mode/src/aggregators/sp1_aggregator.rs +++ b/aggregation_mode/src/aggregators/sp1_aggregator.rs @@ -13,7 +13,7 @@ const CHUNK_PROGRAM_ELF: &[u8] = const USER_PROOFS_PROGRAM_ELF: &[u8] = include_bytes!("../../aggregation_programs/sp1/elf/sp1_user_proofs_aggregator_program"); -#[allow(dead_code)] +#[cfg(feature = "prove")] static SP1_PROVER_CLIENT: LazyLock = LazyLock::new(ProverClient::from_env); /// Separate prover instance configured to always use the CPU. From b561071d04bb30d955e7a18f7e81e40f422bc0ab Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Mon, 2 Jun 2025 13:30:59 -0300 Subject: [PATCH 11/13] chore: address clippy warnings --- aggregation_mode/src/aggregators/sp1_aggregator.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aggregation_mode/src/aggregators/sp1_aggregator.rs b/aggregation_mode/src/aggregators/sp1_aggregator.rs index eb5b29406a..f0a8f2c19e 100644 --- a/aggregation_mode/src/aggregators/sp1_aggregator.rs +++ b/aggregation_mode/src/aggregators/sp1_aggregator.rs @@ -2,8 +2,10 @@ use std::sync::LazyLock; use alloy::primitives::Keccak256; use sp1_aggregation_program::SP1VkAndPubInputs; +#[cfg(feature = "prove")] +use sp1_sdk::EnvProver; use sp1_sdk::{ - CpuProver, EnvProver, HashableKey, Prover, ProverClient, SP1ProofWithPublicValues, SP1Stdin, + CpuProver, HashableKey, Prover, ProverClient, SP1ProofWithPublicValues, SP1Stdin, SP1VerifyingKey, }; From 2dc3550c413753abc9ba7c6a12e63100015c5ec8 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Tue, 3 Jun 2025 14:33:16 -0300 Subject: [PATCH 12/13] refactor: remove pre-verification flag --- .../src/aggregators/risc0_aggregator.rs | 7 ++----- .../src/aggregators/sp1_aggregator.rs | 13 ++---------- aggregation_mode/src/backend/config.rs | 1 - aggregation_mode/src/backend/fetcher.rs | 21 ++++++------------- ...fig-proof-aggregator-ethereum-package.yaml | 1 - ...roof-aggregator-mock-ethereum-package.yaml | 1 - .../config-proof-aggregator-mock.yaml | 1 - config-files/config-proof-aggregator.yaml | 1 - 8 files changed, 10 insertions(+), 36 deletions(-) diff --git a/aggregation_mode/src/aggregators/risc0_aggregator.rs b/aggregation_mode/src/aggregators/risc0_aggregator.rs index c4ac33d7fc..30ffff8568 100644 --- a/aggregation_mode/src/aggregators/risc0_aggregator.rs +++ b/aggregation_mode/src/aggregators/risc0_aggregator.rs @@ -15,11 +15,8 @@ pub enum AlignedRisc0VerificationError { } impl Risc0ProofReceiptAndImageId { - pub fn new(image_id: [u8; 32], receipt: Receipt) -> Self { - Self { image_id, receipt } - } - - pub fn new_with_verification( + /// Constructs a new instance of the struct, verifying the provided receipt against the given image ID. + pub fn new( image_id: [u8; 32], receipt: Receipt, ) -> Result { diff --git a/aggregation_mode/src/aggregators/sp1_aggregator.rs b/aggregation_mode/src/aggregators/sp1_aggregator.rs index f0a8f2c19e..528cd82131 100644 --- a/aggregation_mode/src/aggregators/sp1_aggregator.rs +++ b/aggregation_mode/src/aggregators/sp1_aggregator.rs @@ -37,17 +37,8 @@ pub enum AlignedSP1VerificationError { } impl SP1ProofWithPubValuesAndElf { - pub fn new(proof_with_pub_values: SP1ProofWithPublicValues, elf: Vec) -> Self { - let vk = vk_from_elf(&elf); - - Self { - proof_with_pub_values, - elf, - vk, - } - } - - pub fn new_with_verification( + /// Constructs a new instance of the struct by verifying a given SP1 proof with its public values. + pub fn new( proof_with_pub_values: SP1ProofWithPublicValues, elf: Vec, ) -> Result { diff --git a/aggregation_mode/src/backend/config.rs b/aggregation_mode/src/backend/config.rs index 0a26c0085b..c56c02aaf2 100644 --- a/aggregation_mode/src/backend/config.rs +++ b/aggregation_mode/src/backend/config.rs @@ -22,7 +22,6 @@ pub struct Config { pub last_aggregated_block_filepath: String, pub ecdsa: ECDSAConfig, pub proofs_per_chunk: u16, - pub pre_verification_enabled: bool, pub total_proofs_limit: u16, } diff --git a/aggregation_mode/src/backend/fetcher.rs b/aggregation_mode/src/backend/fetcher.rs index eab669e309..79a3fbed80 100644 --- a/aggregation_mode/src/backend/fetcher.rs +++ b/aggregation_mode/src/backend/fetcher.rs @@ -30,7 +30,6 @@ pub struct ProofsFetcher { rpc_provider: RPCProvider, aligned_service_manager: AlignedLayerServiceManagerContract, last_aggregated_block: u64, - pre_verification_enabled: bool, } impl ProofsFetcher { @@ -49,7 +48,6 @@ impl ProofsFetcher { rpc_provider, aligned_service_manager, last_aggregated_block, - pre_verification_enabled: config.pre_verification_enabled, } } @@ -120,14 +118,10 @@ impl ProofsFetcher { let elf = p.vm_program_code?; let proof_with_pub_values = bincode::deserialize(&p.proof).ok()?; - let sp1_proof = if self.pre_verification_enabled { - SP1ProofWithPubValuesAndElf::new_with_verification( - proof_with_pub_values, - elf, - ) - } else { - Ok(SP1ProofWithPubValuesAndElf::new(proof_with_pub_values, elf)) - }; + let sp1_proof = SP1ProofWithPubValuesAndElf::new_with_verification( + proof_with_pub_values, + elf, + ); match sp1_proof { Ok(proof) => Some(AlignedProof::SP1(proof.into())), @@ -152,11 +146,8 @@ impl ProofsFetcher { bincode::deserialize(&p.proof).ok()?; let receipt = Receipt::new(inner_receipt, public_inputs); - let risc0_proof = if self.pre_verification_enabled { - Risc0ProofReceiptAndImageId::new_with_verification(image_id, receipt) - } else { - Ok(Risc0ProofReceiptAndImageId::new(image_id, receipt)) - }; + let risc0_proof = + Risc0ProofReceiptAndImageId::new_with_verification(image_id, receipt); match risc0_proof { Ok(proof) => Some(AlignedProof::Risc0(proof.into())), diff --git a/config-files/config-proof-aggregator-ethereum-package.yaml b/config-files/config-proof-aggregator-ethereum-package.yaml index b38ddd241d..00692ec0ff 100644 --- a/config-files/config-proof-aggregator-ethereum-package.yaml +++ b/config-files/config-proof-aggregator-ethereum-package.yaml @@ -5,7 +5,6 @@ eth_ws_url: "ws://localhost:8546" max_proofs_in_queue: 1000 last_aggregated_block_filepath: config-files/proof-aggregator.last_aggregated_block.json proofs_per_chunk: 512 # Amount of proofs to process per chunk -pre_verification_enabled: true # This number comes from the blob data limit # Since each blob has a capacity of (4096 * 32) = 131.072 bytes # But to not surpass the field modulus we pad with a 0xo byte so we have (4096 * 31) = 126.976 bytes diff --git a/config-files/config-proof-aggregator-mock-ethereum-package.yaml b/config-files/config-proof-aggregator-mock-ethereum-package.yaml index c073a79522..0effe2823e 100644 --- a/config-files/config-proof-aggregator-mock-ethereum-package.yaml +++ b/config-files/config-proof-aggregator-mock-ethereum-package.yaml @@ -5,7 +5,6 @@ eth_ws_url: "ws://localhost:8546" max_proofs_in_queue: 1000 last_aggregated_block_filepath: config-files/proof-aggregator.last_aggregated_block.json proofs_per_chunk: 512 # Amount of proofs to process per chunk -pre_verification_enabled: true # This number comes from the blob data limit # Since each blob has a capacity of (4096 * 32) = 131.072 bytes # But to not surpass the field modulus we pad with a 0xo byte so we have (4096 * 31) = 126.976 bytes diff --git a/config-files/config-proof-aggregator-mock.yaml b/config-files/config-proof-aggregator-mock.yaml index 2adfd66b24..35451eee27 100644 --- a/config-files/config-proof-aggregator-mock.yaml +++ b/config-files/config-proof-aggregator-mock.yaml @@ -5,7 +5,6 @@ eth_ws_url: "ws://localhost:8545" max_proofs_in_queue: 1000 last_aggregated_block_filepath: config-files/proof-aggregator.last_aggregated_block.json proofs_per_chunk: 512 # Amount of proofs to process per chunk -pre_verification_enabled: true # This number comes from the blob data limit # Since each blob has a capacity of (4096 * 32) = 131.072 bytes # But to not surpass the field modulus we pad with a 0xo byte so we have (4096 * 31) = 126.976 bytes diff --git a/config-files/config-proof-aggregator.yaml b/config-files/config-proof-aggregator.yaml index 7fba459881..6ac560bf91 100644 --- a/config-files/config-proof-aggregator.yaml +++ b/config-files/config-proof-aggregator.yaml @@ -5,7 +5,6 @@ eth_ws_url: "ws://localhost:8545" max_proofs_in_queue: 1000 last_aggregated_block_filepath: config-files/proof-aggregator.last_aggregated_block.json proofs_per_chunk: 512 # Amount of proofs to process per chunk -pre_verification_enabled: true # This number comes from the blob data limit # Since each blob has a capacity of (4096 * 32) = 131.072 bytes # But to not surpass the field modulus we pad with a 0xo byte so we have (4096 * 31) = 126.976 bytes From 42001ed0928723331c8fe0522879cd2103832d2d Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Tue, 3 Jun 2025 15:19:35 -0300 Subject: [PATCH 13/13] fix: compilation --- aggregation_mode/src/backend/fetcher.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/aggregation_mode/src/backend/fetcher.rs b/aggregation_mode/src/backend/fetcher.rs index 79a3fbed80..ddfaa00580 100644 --- a/aggregation_mode/src/backend/fetcher.rs +++ b/aggregation_mode/src/backend/fetcher.rs @@ -118,10 +118,8 @@ impl ProofsFetcher { let elf = p.vm_program_code?; let proof_with_pub_values = bincode::deserialize(&p.proof).ok()?; - let sp1_proof = SP1ProofWithPubValuesAndElf::new_with_verification( - proof_with_pub_values, - elf, - ); + let sp1_proof = + SP1ProofWithPubValuesAndElf::new(proof_with_pub_values, elf); match sp1_proof { Ok(proof) => Some(AlignedProof::SP1(proof.into())), @@ -146,8 +144,7 @@ impl ProofsFetcher { bincode::deserialize(&p.proof).ok()?; let receipt = Receipt::new(inner_receipt, public_inputs); - let risc0_proof = - Risc0ProofReceiptAndImageId::new_with_verification(image_id, receipt); + let risc0_proof = Risc0ProofReceiptAndImageId::new(image_id, receipt); match risc0_proof { Ok(proof) => Some(AlignedProof::Risc0(proof.into())),