mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-06-29 18:39:30 +00:00
minor optimization
This commit is contained in:
parent
32813f606b
commit
36239e9f4d
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -962,13 +962,15 @@ mod tests {
|
||||
|
||||
let mut env_builder = ExecutorEnv::builder();
|
||||
env_builder.write(&PRIVACY_PRESERVING_CIRCUIT_ID).unwrap();
|
||||
env_builder.write(&(proofs.len() as u32)).unwrap();
|
||||
|
||||
// Write journals first, then add assumptions — ordering matters for the guest.
|
||||
// Outputs are written once as a word-native `Vec<&PrivacyPreservingCircuitOutput>`
|
||||
// (matching `aggregator_circuit`'s `AggregatorCircuitInput`) instead of N raw
|
||||
// `Vec<u8>` journal buffers — see the ppe_aggregation guest for why.
|
||||
let outputs: Vec<&PrivacyPreservingCircuitOutput> =
|
||||
proofs.iter().map(|(o, _)| o).collect();
|
||||
env_builder.write(&outputs).unwrap();
|
||||
|
||||
let journals: Vec<Vec<u8>> = proofs.iter().map(|(o, _)| o.to_bytes()).collect();
|
||||
for journal in &journals {
|
||||
env_builder.write(journal).unwrap();
|
||||
}
|
||||
for ((_, proof), journal) in proofs.iter().zip(&journals) {
|
||||
let inner: InnerReceipt = borsh::from_slice(&proof.0).unwrap();
|
||||
env_builder.add_assumption(Receipt::new(inner, journal.clone()));
|
||||
@ -1035,14 +1037,19 @@ mod tests {
|
||||
}
|
||||
|
||||
env_builder.write(&PRIVACY_PRESERVING_CIRCUIT_ID).unwrap();
|
||||
env_builder
|
||||
.write(&u32::try_from(fixtures.len()).expect("fixture count fits in u32"))
|
||||
.unwrap();
|
||||
|
||||
// Journals must be written before assumptions (guest reads them in order).
|
||||
for f in &fixtures {
|
||||
env_builder.write(&f.output_bytes).unwrap();
|
||||
}
|
||||
// Outputs are written once as a word-native `Vec<PrivacyPreservingCircuitOutput>`
|
||||
// (matching `aggregator_circuit`'s `AggregatorCircuitInput`) instead of N raw
|
||||
// `Vec<u8>` journal buffers — see the ppe_aggregation guest for why.
|
||||
let outputs: Vec<PrivacyPreservingCircuitOutput> = fixtures
|
||||
.iter()
|
||||
.map(|f| {
|
||||
let words: &[u32] = bytemuck::cast_slice(&f.output_bytes);
|
||||
risc0_zkvm::serde::from_slice(words).expect("fixture output_bytes invalid")
|
||||
})
|
||||
.collect();
|
||||
env_builder.write(&outputs).unwrap();
|
||||
|
||||
for f in &fixtures {
|
||||
let inner: InnerReceipt = borsh::from_slice(&f.proof_bytes)
|
||||
.expect("fixture proof_bytes is not a valid InnerReceipt");
|
||||
@ -1058,10 +1065,14 @@ mod tests {
|
||||
|
||||
let proof_size = borsh::to_vec(&prove_info.receipt.inner).unwrap().len();
|
||||
eprintln!(
|
||||
"[lee::analytics] ppe_aggregation n={} proving_ms={} proof_size_bytes={}",
|
||||
"[lee::analytics] ppe_aggregation n={} proving_ms={} proof_size_bytes={} \
|
||||
segments={} total_cycles={} user_cycles={}",
|
||||
fixtures.len(),
|
||||
proving_ms,
|
||||
proof_size,
|
||||
prove_info.stats.segments,
|
||||
prove_info.stats.total_cycles,
|
||||
prove_info.stats.user_cycles,
|
||||
);
|
||||
|
||||
prove_info
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
//! The full `PrivacyPreservingCircuitOutput` for each transaction is committed to the
|
||||
//! journal so observers can perform state-dependent checks independently.
|
||||
|
||||
use std::{collections::HashSet, convert::Infallible};
|
||||
use std::convert::Infallible;
|
||||
|
||||
use lee_core::{AggregatorCircuitInput, AggregatorCircuitOutput, Commitment, Nullifier, account::AccountId};
|
||||
use risc0_zkvm::{guest::env, serde::to_vec};
|
||||
@ -27,36 +27,41 @@ fn main() {
|
||||
.unwrap_or_else(|_: Infallible| unreachable!("Infallible error is never constructed"));
|
||||
}
|
||||
|
||||
let mut seen_nullifiers: HashSet<Nullifier> = HashSet::new();
|
||||
// Linear-scan dedup: batches are small (n is bounded), so a `Vec` + `contains` check
|
||||
// avoids the per-element hashing cost of `HashSet` in the zkVM.
|
||||
let mut seen_nullifiers: Vec<Nullifier> = Vec::new();
|
||||
for output in &circuit_outputs {
|
||||
for (nullifier, _) in &output.new_nullifiers {
|
||||
assert!(
|
||||
seen_nullifiers.insert(*nullifier),
|
||||
!seen_nullifiers.contains(nullifier),
|
||||
"Duplicate nullifier across transactions in batch"
|
||||
);
|
||||
seen_nullifiers.push(*nullifier);
|
||||
}
|
||||
}
|
||||
|
||||
let mut seen_commitments: HashSet<Commitment> = HashSet::new();
|
||||
let mut seen_commitments: Vec<Commitment> = Vec::new();
|
||||
for output in &circuit_outputs {
|
||||
for commitment in &output.new_commitments {
|
||||
assert!(
|
||||
seen_commitments.insert(commitment.clone()),
|
||||
!seen_commitments.contains(commitment),
|
||||
"Duplicate commitment across transactions in batch"
|
||||
);
|
||||
seen_commitments.push(commitment.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let mut seen_updated_account_ids: HashSet<AccountId> = HashSet::new();
|
||||
let mut seen_updated_account_ids: Vec<AccountId> = Vec::new();
|
||||
for output in &circuit_outputs {
|
||||
for (pre_state, post_state) in
|
||||
output.public_pre_states.iter().zip(output.public_post_states.iter())
|
||||
{
|
||||
if pre_state.account != *post_state {
|
||||
assert!(
|
||||
seen_updated_account_ids.insert(pre_state.account_id),
|
||||
!seen_updated_account_ids.contains(&pre_state.account_id),
|
||||
"Public account updated by multiple transactions in batch"
|
||||
);
|
||||
seen_updated_account_ids.push(pre_state.account_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
//! Extends the core aggregator circuit with one additional check proven inside RISC0:
|
||||
//! - Each transaction's validity window contains the provided `block_id` and `timestamp`.
|
||||
|
||||
use std::{collections::HashSet, convert::Infallible};
|
||||
use std::convert::Infallible;
|
||||
|
||||
use lee_core::{
|
||||
AggregatorCircuitInput, AggregatorCircuitOutput, Commitment, Nullifier, account::AccountId,
|
||||
@ -25,23 +25,27 @@ fn main() {
|
||||
.unwrap_or_else(|_: Infallible| unreachable!("Infallible error is never constructed"));
|
||||
}
|
||||
|
||||
let mut seen_nullifiers: HashSet<Nullifier> = HashSet::new();
|
||||
// Linear-scan dedup: batches are small (n is bounded), so a `Vec` + `contains` check
|
||||
// avoids the per-element hashing cost of `HashSet` in the zkVM.
|
||||
let mut seen_nullifiers: Vec<Nullifier> = Vec::new();
|
||||
for output in &circuit_outputs {
|
||||
for (nullifier, _) in &output.new_nullifiers {
|
||||
assert!(
|
||||
seen_nullifiers.insert(*nullifier),
|
||||
!seen_nullifiers.contains(nullifier),
|
||||
"Duplicate nullifier across transactions in batch"
|
||||
);
|
||||
seen_nullifiers.push(*nullifier);
|
||||
}
|
||||
}
|
||||
|
||||
let mut seen_commitments: HashSet<Commitment> = HashSet::new();
|
||||
let mut seen_commitments: Vec<Commitment> = Vec::new();
|
||||
for output in &circuit_outputs {
|
||||
for commitment in &output.new_commitments {
|
||||
assert!(
|
||||
seen_commitments.insert(commitment.clone()),
|
||||
!seen_commitments.contains(commitment),
|
||||
"Duplicate commitment across transactions in batch"
|
||||
);
|
||||
seen_commitments.push(commitment.clone());
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,16 +60,17 @@ fn main() {
|
||||
);
|
||||
}
|
||||
|
||||
let mut seen_updated_account_ids: HashSet<AccountId> = HashSet::new();
|
||||
let mut seen_updated_account_ids: Vec<AccountId> = Vec::new();
|
||||
for output in &circuit_outputs {
|
||||
for (pre_state, post_state) in
|
||||
output.public_pre_states.iter().zip(output.public_post_states.iter())
|
||||
{
|
||||
if pre_state.account != *post_state {
|
||||
assert!(
|
||||
seen_updated_account_ids.insert(pre_state.account_id),
|
||||
!seen_updated_account_ids.contains(&pre_state.account_id),
|
||||
"Public account updated by multiple transactions in batch"
|
||||
);
|
||||
seen_updated_account_ids.push(pre_state.account_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
use lee_core::PrivacyPreservingCircuitOutput;
|
||||
use risc0_zkvm::guest::env;
|
||||
use risc0_zkvm::{guest::env, serde::to_vec};
|
||||
|
||||
/// Aggregation circuit for N privacy-preserving execution proofs.
|
||||
///
|
||||
/// The host writes:
|
||||
/// 1. The PPE circuit image ID (`[u32; 8]`)
|
||||
/// 2. The count N (`u32`)
|
||||
/// 3. N journal byte-buffers (each produced by `PrivacyPreservingCircuitOutput::to_bytes()`)
|
||||
/// 2. `Vec<PrivacyPreservingCircuitOutput>` — the N outputs to verify and re-commit
|
||||
///
|
||||
/// It also loads each PPE receipt as an assumption before running this guest.
|
||||
/// `env::verify` checks each assumption cryptographically; if any proof is
|
||||
@ -14,25 +13,24 @@ use risc0_zkvm::guest::env;
|
||||
///
|
||||
/// Journal: `Vec<PrivacyPreservingCircuitOutput>` — the verifier recovers all
|
||||
/// circuit outputs from the single aggregated proof.
|
||||
///
|
||||
/// Outputs are read once as a word-native `Vec<...>` and re-serialized per-output via
|
||||
/// `to_vec()` for `env::verify`, mirroring `aggregator_circuit`. This replaced reading
|
||||
/// each journal as a raw `env::read::<Vec<u8>>()`: risc0's default serde deserializes
|
||||
/// `Vec<u8>` one byte at a time (each unpacked from a word), which costs more guest
|
||||
/// cycles than the word-native path. `to_vec(output)` and `output.to_bytes()` produce
|
||||
/// identical bytes, so the assumption journal digest is unchanged.
|
||||
fn main() {
|
||||
// The host passes the PPE circuit image ID so the guest stays independent
|
||||
// of the host-only `lee` crate.
|
||||
let ppe_image_id: [u32; 8] = env::read();
|
||||
let count: u32 = env::read();
|
||||
let outputs: Vec<PrivacyPreservingCircuitOutput> = env::read();
|
||||
|
||||
let mut outputs = Vec::with_capacity(count as usize);
|
||||
|
||||
for _ in 0..count {
|
||||
let journal: Vec<u8> = env::read();
|
||||
|
||||
env::verify(ppe_image_id, &journal)
|
||||
for output in &outputs {
|
||||
let output_words =
|
||||
to_vec(output).expect("PrivacyPreservingCircuitOutput serialization should not fail");
|
||||
env::verify(ppe_image_id, &output_words)
|
||||
.expect("PPE_aggregation: a PPE proof failed verification");
|
||||
|
||||
let word_slice: &[u32] = bytemuck::cast_slice(&journal);
|
||||
let output: PrivacyPreservingCircuitOutput =
|
||||
risc0_zkvm::serde::from_slice(word_slice)
|
||||
.expect("PPE_aggregation: failed to deserialise circuit output");
|
||||
outputs.push(output);
|
||||
}
|
||||
|
||||
env::commit(&outputs);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user