From c56af60445d481e412fa81da28b3d39b0d566019 Mon Sep 17 00:00:00 2001 From: Bruno Deferrari Date: Mon, 7 Oct 2024 16:33:15 -0300 Subject: [PATCH] feat(proofs): Batch verification of completed works in blocks --- ledger/src/proofs/accumulator_check.rs | 66 +++++++----- ledger/src/proofs/verification.rs | 116 ++++++++++++++++++++-- ledger/src/staged_ledger/staged_ledger.rs | 6 +- 3 files changed, 153 insertions(+), 35 deletions(-) diff --git a/ledger/src/proofs/accumulator_check.rs b/ledger/src/proofs/accumulator_check.rs index 782697c1c3..c40908b6a0 100644 --- a/ledger/src/proofs/accumulator_check.rs +++ b/ledger/src/proofs/accumulator_check.rs @@ -9,38 +9,52 @@ use super::urs_utils; pub fn accumulator_check( urs: &SRS, - proof: &PicklesProofProofsVerified2ReprStableV2, + proofs: &[&PicklesProofProofsVerified2ReprStableV2], ) -> Result { // accumulator check + // https://github.com/MinaProtocol/mina/blob/fb1c3c0a408c344810140bdbcedacc532a11be91/src/lib/pickles/common.ml#L191-L204 // Note: // comms: statement.proof_state.messages_for_next_wrap_proof.challenge_polynomial_commitment + // Array.of_list_map comm_chals ~f:(fun (comm, _) -> Or_infinity.Finite comm ) // chals: statement.proof_state.deferred_values.bulletproof_challenges + // Array.concat @@ List.map comm_chals ~f:(fun (_, chals) -> Vector.to_array chals) - let deferred_values = &proof.statement.proof_state.deferred_values; - let bulletproof_challenges = &deferred_values.bulletproof_challenges; - let bulletproof_challenges: Vec = bulletproof_challenges - .iter() - .map(|chal| { - let prechallenge = &chal.prechallenge.inner; - let prechallenge: [u64; 2] = prechallenge.each_ref().map(|c| c.as_u64()); - - ScalarChallenge::limbs_to_field(&prechallenge) - }) - .collect(); - - let of_coord = - |(x, y): &(BigInt, BigInt)| Ok(Vesta::of_coordinates(x.to_field()?, y.to_field()?)); - - // statement.proof_state.messages_for_next_wrap_proof.challenge_polynomial_commitment - let acc_comm = &proof - .statement - .proof_state - .messages_for_next_wrap_proof - .challenge_polynomial_commitment; - let acc_comm: Vesta = of_coord(acc_comm)?; - - let acc_check = - urs_utils::batch_dlog_accumulator_check(urs, &[acc_comm], &bulletproof_challenges); + let mut comms = Vec::with_capacity(proofs.len()); + let mut bulletproof_challenges = vec![]; + + for proof in proofs { + let chals = &proof + .statement + .proof_state + .deferred_values + .bulletproof_challenges; + let mut chals: Vec = chals + .iter() + .map(|chal| { + let prechallenge = &chal.prechallenge.inner; + let prechallenge: [u64; 2] = prechallenge.each_ref().map(|c| c.as_u64()); + + ScalarChallenge::limbs_to_field(&prechallenge) + }) + .collect(); + + bulletproof_challenges.append(&mut chals); + + let of_coord = + |(x, y): &(BigInt, BigInt)| Ok(Vesta::of_coordinates(x.to_field()?, y.to_field()?)); + + // statement.proof_state.messages_for_next_wrap_proof.challenge_polynomial_commitment + let acc_comm = &proof + .statement + .proof_state + .messages_for_next_wrap_proof + .challenge_polynomial_commitment; + let acc_comm: Vesta = of_coord(acc_comm)?; + + comms.push(acc_comm); + } + + let acc_check = urs_utils::batch_dlog_accumulator_check(urs, &comms, &bulletproof_challenges); if !acc_check { println!("accumulator_check failed"); diff --git a/ledger/src/proofs/verification.rs b/ledger/src/proofs/verification.rs index a3a27bd02d..cf23f67216 100644 --- a/ledger/src/proofs/verification.rs +++ b/ledger/src/proofs/verification.rs @@ -3,6 +3,7 @@ use std::rc::Rc; use ark_ff::fields::arithmetic::InvalidBigInt; use ark_poly::{EvaluationDomain, Radix2EvaluationDomain}; use ark_serialize::Write; +use itertools::Itertools; use poly_commitment::srs::SRS; use crate::{ @@ -519,6 +520,38 @@ fn verify_with( ) } +pub struct VerificationContext<'a> { + pub verifier_index: &'a VerifierIndex, + pub proof: &'a ProverProof, + pub public_input: &'a [Fq], +} + +fn batch_verify(proofs: &[VerificationContext]) -> Result<(), VerifyError> { + use kimchi::groupmap::GroupMap; + use kimchi::mina_curves::pasta::PallasParameters; + use kimchi::verifier::Context; + use mina_poseidon::sponge::{DefaultFqSponge, DefaultFrSponge}; + use poly_commitment::evaluation_proof::OpeningProof; + + type SpongeParams = mina_poseidon::constants::PlonkSpongeConstantsKimchi; + type EFqSponge = DefaultFqSponge; + type EFrSponge = DefaultFrSponge; + + let group_map = GroupMap::::setup(); + let proofs = proofs + .iter() + .map(|p| Context { + verifier_index: p.verifier_index, + proof: p.proof, + public_input: p.public_input, + }) + .collect_vec(); + + kimchi::verifier::batch_verify::>( + &group_map, &proofs, + ) +} + fn run_checks( proof: &PicklesProofProofsVerified2ReprStableV2, verifier_index: &VerifierIndex, @@ -713,7 +746,7 @@ pub fn verify_block( let protocol_state_hash = MinaHash::hash(&protocol_state); let accum_check = - accumulator_check::accumulator_check(srs, protocol_state_proof).unwrap_or(false); + accumulator_check::accumulator_check(srs, &[protocol_state_proof]).unwrap_or(false); let verified = verify_impl(&protocol_state_hash, protocol_state_proof, &vk).unwrap_or(false); accum_check && verified @@ -730,12 +763,27 @@ pub fn verify_transaction<'a>( data: (), }; - proofs.into_iter().all(|(statement, transaction_proof)| { - let accum_check = - accumulator_check::accumulator_check(srs, transaction_proof).unwrap_or(false); - let verified = verify_impl(statement, transaction_proof, &vk).unwrap_or(false); - accum_check && verified - }) + let mut inputs: Vec<( + &Statement, + &PicklesProofProofsVerified2ReprStableV2, + &VK, + )> = Vec::with_capacity(128); + + let mut accum_check_proofs: Vec<&PicklesProofProofsVerified2ReprStableV2> = + Vec::with_capacity(128); + + proofs + .into_iter() + .for_each(|(statement, transaction_proof)| { + accum_check_proofs.push(transaction_proof); + inputs.push((statement, transaction_proof, &vk)); + }); + + let accum_check = + accumulator_check::accumulator_check(srs, &accum_check_proofs).unwrap_or(false); + + let verified = batch_verify_impl(inputs.as_slice()).unwrap_or(false); + accum_check && verified } /// https://github.com/MinaProtocol/mina/blob/bfd1009abdbee78979ff0343cc73a3480e862f58/src/lib/crypto/kimchi_bindings/stubs/src/pasta_fq_plonk_proof.rs#L116 @@ -753,7 +801,8 @@ pub fn verify_zkapp( data: (), }; - let accum_check = accumulator_check::accumulator_check(srs, sideloaded_proof).unwrap_or(false); + let accum_check = + accumulator_check::accumulator_check(srs, &[sideloaded_proof]).unwrap_or(false); let verified = verify_impl(&zkapp_statement, sideloaded_proof, &vk).unwrap_or(false); let ok = accum_check && verified; @@ -811,6 +860,57 @@ where Ok(result.is_ok() && checks) } +fn batch_verify_impl( + proofs: &[(&AppState, &PicklesProofProofsVerified2ReprStableV2, &VK)], +) -> Result +where + AppState: ToFieldElements, +{ + let mut verification_contexts = Vec::with_capacity(proofs.len()); + let mut checks = true; + + for (app_state, proof, vk) in proofs { + let deferred_values = compute_deferred_values(proof)?; + checks = checks && run_checks(proof, vk.index); + + let message_for_next_step_proof = get_message_for_next_step_proof( + &proof.statement.messages_for_next_step_proof, + &vk.commitments, + app_state, + )?; + + let message_for_next_wrap_proof = get_message_for_next_wrap_proof( + &proof.statement.proof_state.messages_for_next_wrap_proof, + )?; + + let prepared_statement = get_prepared_statement( + &message_for_next_step_proof, + &message_for_next_wrap_proof, + deferred_values, + &proof.statement.proof_state.sponge_digest_before_evaluations, + ); + + let npublic_input = vk.index.public; + let public_inputs = prepared_statement.to_public_input(npublic_input)?; + let proof_padded = make_padded_proof_from_p2p(proof)?; + + verification_contexts.push((vk.index, proof_padded, public_inputs)); + } + + let proofs: Vec = verification_contexts + .iter() + .map(|(vk, proof, public_input)| VerificationContext { + verifier_index: vk, + proof, + public_input, + }) + .collect(); + + let result = batch_verify(&proofs); + + Ok(result.is_ok() && checks) +} + /// Dump data when it fails, to reproduce and compare in OCaml fn dump_zkapp_verification( verification_key: &VerificationKey, diff --git a/ledger/src/staged_ledger/staged_ledger.rs b/ledger/src/staged_ledger/staged_ledger.rs index 7029c192a8..9c18088ef8 100644 --- a/ledger/src/staged_ledger/staged_ledger.rs +++ b/ledger/src/staged_ledger/staged_ledger.rs @@ -1247,12 +1247,16 @@ impl StagedLedger { supercharge_coinbase: bool, ) -> Result { let work = witness.completed_works(); + let works_count = work.len(); let now = redux::Instant::now(); if skip_verification.is_none() { Self::check_completed_works(logger, verifier, &self.scan_state, work)?; } - eprintln!("verification time={:?}", now.elapsed()); + eprintln!( + "verification time={:?} ({works_count} completed works)", + now.elapsed() + ); let prediff = witness.get( |cmd| Self::check_commands(self.ledger.clone(), verifier, cmd, skip_verification),