Skip to content

Commit c7b139c

Browse files
MarcosNicolauJuArceMauroToscano
authored
feat/perf(aggregation-mode): verification & sp1 aggregation (#1955)
Co-authored-by: Julian Arce <[email protected]> Co-authored-by: Mauro Toscano <[email protected]>
1 parent 52d5c8c commit c7b139c

File tree

7 files changed

+122
-124
lines changed

7 files changed

+122
-124
lines changed

aggregation_mode/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

aggregation_mode/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ sha3 = "0.10.8"
1717
reqwest = { version = "0.12" }
1818
ciborium = "=0.2.2"
1919
lambdaworks-crypto = { git = "https://github.com/lambdaclass/lambdaworks.git", rev = "5f8f2cfcc8a1a22f77e8dff2d581f1166eefb80b", features = ["serde"]}
20+
rayon = "1.10.0"
2021
# Necessary for the VerificationData type
2122
aligned-sdk = { path = "../batcher/aligned-sdk/" }
2223
# zkvms

aggregation_mode/src/aggregators/mod.rs

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,9 @@ pub mod sp1_aggregator;
44
use std::fmt::Display;
55

66
use lambdaworks_crypto::merkle_tree::traits::IsMerkleTreeBackend;
7-
use risc0_aggregator::{
8-
AlignedRisc0VerificationError, Risc0AggregationError, Risc0ProofReceiptAndImageId,
9-
};
7+
use risc0_aggregator::{Risc0AggregationError, Risc0ProofReceiptAndImageId};
108
use sha3::{Digest, Keccak256};
11-
use sp1_aggregator::{
12-
AlignedSP1VerificationError, SP1AggregationError, SP1ProofWithPubValuesAndElf,
13-
};
9+
use sp1_aggregator::{SP1AggregationError, SP1ProofWithPubValuesAndElf};
1410
use tracing::info;
1511

1612
#[derive(Clone, Debug)]
@@ -219,24 +215,3 @@ impl IsMerkleTreeBackend for AlignedProof {
219215
hasher.finalize().into()
220216
}
221217
}
222-
223-
#[derive(Debug)]
224-
pub enum AlignedVerificationError {
225-
Sp1(AlignedSP1VerificationError),
226-
Risc0(AlignedRisc0VerificationError),
227-
}
228-
229-
impl AlignedProof {
230-
pub fn verify(&self) -> Result<(), AlignedVerificationError> {
231-
match self {
232-
AlignedProof::SP1(proof) => sp1_aggregator::verify(proof).map_err(
233-
|arg0: sp1_aggregator::AlignedSP1VerificationError| {
234-
AlignedVerificationError::Sp1(arg0)
235-
},
236-
),
237-
AlignedProof::Risc0(proof) => {
238-
risc0_aggregator::verify(proof).map_err(AlignedVerificationError::Risc0)
239-
}
240-
}
241-
}
242-
}

aggregation_mode/src/aggregators/risc0_aggregator.rs

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,32 @@ pub struct Risc0ProofReceiptAndImageId {
88
pub receipt: Receipt,
99
}
1010

11+
#[derive(Debug)]
12+
pub enum AlignedRisc0VerificationError {
13+
Verification(String),
14+
UnsupportedProof,
15+
}
16+
1117
impl Risc0ProofReceiptAndImageId {
18+
/// Constructs a new instance of the struct, verifying the provided receipt against the given image ID.
19+
pub fn new(
20+
image_id: [u8; 32],
21+
receipt: Receipt,
22+
) -> Result<Self, AlignedRisc0VerificationError> {
23+
let is_supported_proof =
24+
receipt.inner.composite().is_ok() || receipt.inner.succinct().is_ok();
25+
26+
if is_supported_proof {
27+
receipt
28+
.verify(image_id)
29+
.map_err(|e| AlignedRisc0VerificationError::Verification(e.to_string()))?;
30+
} else {
31+
return Err(AlignedRisc0VerificationError::UnsupportedProof);
32+
}
33+
34+
Ok(Self { image_id, receipt })
35+
}
36+
1237
pub fn public_inputs(&self) -> &Vec<u8> {
1338
&self.receipt.journal.bytes
1439
}
@@ -22,12 +47,6 @@ pub enum Risc0AggregationError {
2247
Verification(String),
2348
}
2449

25-
#[derive(Debug)]
26-
pub enum AlignedRisc0VerificationError {
27-
Verification(String),
28-
UnsupportedProof,
29-
}
30-
3150
/// Byte representation of the user proofs aggregator image_id, converted from `[u32; 8]` to `[u8; 32]`.
3251
pub const RISC0_USER_PROOFS_AGGREGATOR_PROGRAM_ID_BYTES: [u8; 32] = {
3352
let mut res = [0u8; 32];
@@ -169,17 +188,3 @@ pub(crate) fn run_chunk_aggregator(
169188

170189
Ok(proof)
171190
}
172-
173-
pub(crate) fn verify(
174-
proof: &Risc0ProofReceiptAndImageId,
175-
) -> Result<(), AlignedRisc0VerificationError> {
176-
// only stark proofs are supported for recursion
177-
if proof.receipt.inner.composite().is_ok() || proof.receipt.inner.succinct().is_ok() {
178-
proof
179-
.receipt
180-
.verify(proof.image_id)
181-
.map_err(|e| AlignedRisc0VerificationError::Verification(e.to_string()))
182-
} else {
183-
Err(AlignedRisc0VerificationError::UnsupportedProof)
184-
}
185-
}

aggregation_mode/src/aggregators/sp1_aggregator.rs

Lines changed: 49 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ use std::sync::LazyLock;
22

33
use alloy::primitives::Keccak256;
44
use sp1_aggregation_program::SP1VkAndPubInputs;
5+
#[cfg(feature = "prove")]
6+
use sp1_sdk::EnvProver;
57
use sp1_sdk::{
6-
EnvProver, HashableKey, Prover, ProverClient, SP1ProofWithPublicValues, SP1Stdin,
8+
CpuProver, HashableKey, Prover, ProverClient, SP1ProofWithPublicValues, SP1Stdin,
79
SP1VerifyingKey,
810
};
911

@@ -13,25 +15,59 @@ const CHUNK_PROGRAM_ELF: &[u8] =
1315
const USER_PROOFS_PROGRAM_ELF: &[u8] =
1416
include_bytes!("../../aggregation_programs/sp1/elf/sp1_user_proofs_aggregator_program");
1517

18+
#[cfg(feature = "prove")]
1619
static SP1_PROVER_CLIENT: LazyLock<EnvProver> = LazyLock::new(ProverClient::from_env);
1720

21+
/// Separate prover instance configured to always use the CPU.
22+
/// This is used for verification, which is performed in parallel and
23+
/// cannot be done on the GPU.
24+
static SP1_PROVER_CLIENT_CPU: LazyLock<CpuProver> =
25+
LazyLock::new(|| ProverClient::builder().cpu().build());
26+
1827
pub struct SP1ProofWithPubValuesAndElf {
1928
pub proof_with_pub_values: SP1ProofWithPublicValues,
2029
pub elf: Vec<u8>,
30+
pub vk: SP1VerifyingKey,
31+
}
32+
33+
#[derive(Debug)]
34+
pub enum AlignedSP1VerificationError {
35+
Verification(sp1_sdk::SP1VerificationError),
36+
UnsupportedProof,
2137
}
2238

2339
impl SP1ProofWithPubValuesAndElf {
40+
/// Constructs a new instance of the struct by verifying a given SP1 proof with its public values.
41+
pub fn new(
42+
proof_with_pub_values: SP1ProofWithPublicValues,
43+
elf: Vec<u8>,
44+
) -> Result<Self, AlignedSP1VerificationError> {
45+
let client = &*SP1_PROVER_CLIENT_CPU;
46+
47+
let (_pk, vk) = client.setup(&elf);
48+
49+
// only sp1 compressed proofs are supported for aggregation now
50+
match proof_with_pub_values.proof {
51+
sp1_sdk::SP1Proof::Compressed(_) => client
52+
.verify(&proof_with_pub_values, &vk)
53+
.map_err(AlignedSP1VerificationError::Verification),
54+
_ => Err(AlignedSP1VerificationError::UnsupportedProof),
55+
}?;
56+
57+
Ok(Self {
58+
proof_with_pub_values,
59+
elf,
60+
vk,
61+
})
62+
}
63+
2464
pub fn hash_vk_and_pub_inputs(&self) -> [u8; 32] {
2565
let mut hasher = Keccak256::new();
26-
let vk_bytes = &self.vk().hash_bytes();
66+
let vk_bytes = &self.vk.hash_bytes();
2767
hasher.update(vk_bytes);
2868
hasher.update(self.proof_with_pub_values.public_values.as_slice());
2969
hasher.finalize().into()
3070
}
31-
32-
pub fn vk(&self) -> SP1VerifyingKey {
33-
vk_from_elf(&self.elf)
34-
}
3571
}
3672

3773
#[derive(Debug)]
@@ -56,15 +92,15 @@ pub(crate) fn run_user_proofs_aggregator(
5692
.proofs_vk_and_pub_inputs
5793
.push(SP1VkAndPubInputs {
5894
public_inputs: proof.proof_with_pub_values.public_values.to_vec(),
59-
vk: proof.vk().hash_u32(),
95+
vk: proof.vk.hash_u32(),
6096
});
6197
}
6298

6399
stdin.write(&program_input);
64100

65101
// write proofs
66102
for input_proof in proofs.iter() {
67-
let vk = input_proof.vk().vk;
103+
let vk = input_proof.vk.vk.clone();
68104
// we only support sp1 Compressed proofs for now
69105
let sp1_sdk::SP1Proof::Compressed(proof) = input_proof.proof_with_pub_values.proof.clone()
70106
else {
@@ -96,6 +132,7 @@ pub(crate) fn run_user_proofs_aggregator(
96132
let proof_and_elf = SP1ProofWithPubValuesAndElf {
97133
proof_with_pub_values: proof,
98134
elf: USER_PROOFS_PROGRAM_ELF.to_vec(),
135+
vk,
99136
};
100137

101138
Ok(proof_and_elf)
@@ -115,7 +152,7 @@ pub(crate) fn run_chunk_aggregator(
115152
program_input.proofs_and_leaves_commitment.push((
116153
SP1VkAndPubInputs {
117154
public_inputs: proof.proof_with_pub_values.public_values.to_vec(),
118-
vk: proof.vk().hash_u32(),
155+
vk: proof.vk.hash_u32(),
119156
},
120157
leaves_commitment.clone(),
121158
));
@@ -125,7 +162,7 @@ pub(crate) fn run_chunk_aggregator(
125162

126163
// write proofs
127164
for (input_proof, _) in proofs.iter() {
128-
let vk = input_proof.vk().vk;
165+
let vk = input_proof.vk.vk.clone();
129166
// we only support sp1 Compressed proofs for now
130167
let sp1_sdk::SP1Proof::Compressed(proof) = input_proof.proof_with_pub_values.proof.clone()
131168
else {
@@ -168,41 +205,14 @@ pub(crate) fn run_chunk_aggregator(
168205
let proof_and_elf = SP1ProofWithPubValuesAndElf {
169206
proof_with_pub_values: proof,
170207
elf: CHUNK_PROGRAM_ELF.to_vec(),
208+
vk,
171209
};
172210

173211
Ok(proof_and_elf)
174212
}
175213

176-
#[derive(Debug)]
177-
pub enum AlignedSP1VerificationError {
178-
Verification(sp1_sdk::SP1VerificationError),
179-
UnsupportedProof,
180-
}
181-
182-
pub(crate) fn verify(
183-
sp1_proof_with_pub_values_and_elf: &SP1ProofWithPubValuesAndElf,
184-
) -> Result<(), AlignedSP1VerificationError> {
185-
let client = &*SP1_PROVER_CLIENT;
186-
187-
let (_pk, vk) = client.setup(&sp1_proof_with_pub_values_and_elf.elf);
188-
189-
// only sp1 compressed proofs are supported for aggregation now
190-
match sp1_proof_with_pub_values_and_elf
191-
.proof_with_pub_values
192-
.proof
193-
{
194-
sp1_sdk::SP1Proof::Compressed(_) => client
195-
.verify(
196-
&sp1_proof_with_pub_values_and_elf.proof_with_pub_values,
197-
&vk,
198-
)
199-
.map_err(AlignedSP1VerificationError::Verification),
200-
_ => Err(AlignedSP1VerificationError::UnsupportedProof),
201-
}
202-
}
203-
204214
pub fn vk_from_elf(elf: &[u8]) -> SP1VerifyingKey {
205-
let prover = &*SP1_PROVER_CLIENT;
215+
let prover = &*SP1_PROVER_CLIENT_CPU;
206216
let (_, vk) = prover.setup(elf);
207217
vk
208218
}

aggregation_mode/src/backend/fetcher.rs

Lines changed: 40 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use alloy::{
1616
primitives::Address,
1717
providers::{Provider, ProviderBuilder},
1818
};
19+
use rayon::prelude::*;
1920
use risc0_zkvm::Receipt;
2021
use tracing::{error, info};
2122

@@ -109,38 +110,49 @@ impl ProofsFetcher {
109110
// Filter compatible proofs to be aggregated and push to queue
110111
let proofs_to_add: Vec<AlignedProof> = match engine {
111112
ZKVMEngine::SP1 => data
112-
.into_iter()
113-
.filter_map(|p| match p.proving_system {
114-
ProvingSystemId::SP1 => {
115-
let elf = p.vm_program_code?;
116-
let proof_with_pub_values = bincode::deserialize(&p.proof).ok()?;
117-
let sp1_proof = SP1ProofWithPubValuesAndElf {
118-
proof_with_pub_values,
119-
elf,
120-
};
121-
122-
Some(AlignedProof::SP1(sp1_proof.into()))
113+
.into_par_iter()
114+
.filter_map(|p| {
115+
if p.proving_system != ProvingSystemId::SP1 {
116+
return None;
117+
};
118+
119+
let elf = p.vm_program_code?;
120+
let proof_with_pub_values = bincode::deserialize(&p.proof).ok()?;
121+
let sp1_proof =
122+
SP1ProofWithPubValuesAndElf::new(proof_with_pub_values, elf);
123+
124+
match sp1_proof {
125+
Ok(proof) => Some(AlignedProof::SP1(proof.into())),
126+
Err(err) => {
127+
error!("Could not add proof, verification failed: {:?}", err);
128+
None
129+
}
123130
}
124-
125-
_ => None,
126131
})
127132
.collect(),
128133
ZKVMEngine::RISC0 => data
129-
.into_iter()
130-
.filter_map(|p| match p.proving_system {
131-
ProvingSystemId::Risc0 => {
132-
let mut image_id = [0u8; 32];
133-
image_id.copy_from_slice(p.vm_program_code?.as_slice());
134-
let public_inputs = p.pub_input?;
135-
let inner_receipt: risc0_zkvm::InnerReceipt =
136-
bincode::deserialize(&p.proof).ok()?;
137-
138-
let receipt = Receipt::new(inner_receipt, public_inputs);
139-
let risc0_proof = Risc0ProofReceiptAndImageId { image_id, receipt };
140-
141-
Some(AlignedProof::Risc0(risc0_proof.into()))
134+
.into_par_iter()
135+
.filter_map(|p| {
136+
if p.proving_system != ProvingSystemId::Risc0 {
137+
return None;
138+
};
139+
140+
let mut image_id = [0u8; 32];
141+
image_id.copy_from_slice(p.vm_program_code?.as_slice());
142+
let public_inputs = p.pub_input?;
143+
let inner_receipt: risc0_zkvm::InnerReceipt =
144+
bincode::deserialize(&p.proof).ok()?;
145+
146+
let receipt = Receipt::new(inner_receipt, public_inputs);
147+
let risc0_proof = Risc0ProofReceiptAndImageId::new(image_id, receipt);
148+
149+
match risc0_proof {
150+
Ok(proof) => Some(AlignedProof::Risc0(proof.into())),
151+
Err(err) => {
152+
error!("Could not add proof, verification failed: {:?}", err);
153+
None
154+
}
142155
}
143-
_ => None,
144156
})
145157
.collect(),
146158
};
@@ -163,15 +175,7 @@ impl ProofsFetcher {
163175
return Ok(proofs);
164176
}
165177

166-
// try to add them to the queue
167-
for proof in proofs_to_add {
168-
if let Err(err) = proof.verify() {
169-
error!("Could not add proof, verification failed: {:?}", err);
170-
continue;
171-
};
172-
173-
proofs.push(proof);
174-
}
178+
proofs.extend(proofs_to_add);
175179
}
176180

177181
// Update last processed block after collecting logs

0 commit comments

Comments
 (0)