Skip to content

Commit 334d00d

Browse files
authored
Merge pull request #782 from openmina/feat/works-batch-verification
feat(proofs): Batch verification of completed works in blocks
2 parents 797f658 + c56af60 commit 334d00d

File tree

3 files changed

+153
-35
lines changed

3 files changed

+153
-35
lines changed

ledger/src/proofs/accumulator_check.rs

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,38 +9,52 @@ use super::urs_utils;
99

1010
pub fn accumulator_check(
1111
urs: &SRS<Vesta>,
12-
proof: &PicklesProofProofsVerified2ReprStableV2,
12+
proofs: &[&PicklesProofProofsVerified2ReprStableV2],
1313
) -> Result<bool, InvalidBigInt> {
1414
// accumulator check
15+
// https://github.com/MinaProtocol/mina/blob/fb1c3c0a408c344810140bdbcedacc532a11be91/src/lib/pickles/common.ml#L191-L204
1516
// Note:
1617
// comms: statement.proof_state.messages_for_next_wrap_proof.challenge_polynomial_commitment
18+
// Array.of_list_map comm_chals ~f:(fun (comm, _) -> Or_infinity.Finite comm )
1719
// chals: statement.proof_state.deferred_values.bulletproof_challenges
20+
// Array.concat @@ List.map comm_chals ~f:(fun (_, chals) -> Vector.to_array chals)
1821

19-
let deferred_values = &proof.statement.proof_state.deferred_values;
20-
let bulletproof_challenges = &deferred_values.bulletproof_challenges;
21-
let bulletproof_challenges: Vec<Fp> = bulletproof_challenges
22-
.iter()
23-
.map(|chal| {
24-
let prechallenge = &chal.prechallenge.inner;
25-
let prechallenge: [u64; 2] = prechallenge.each_ref().map(|c| c.as_u64());
26-
27-
ScalarChallenge::limbs_to_field(&prechallenge)
28-
})
29-
.collect();
30-
31-
let of_coord =
32-
|(x, y): &(BigInt, BigInt)| Ok(Vesta::of_coordinates(x.to_field()?, y.to_field()?));
33-
34-
// statement.proof_state.messages_for_next_wrap_proof.challenge_polynomial_commitment
35-
let acc_comm = &proof
36-
.statement
37-
.proof_state
38-
.messages_for_next_wrap_proof
39-
.challenge_polynomial_commitment;
40-
let acc_comm: Vesta = of_coord(acc_comm)?;
41-
42-
let acc_check =
43-
urs_utils::batch_dlog_accumulator_check(urs, &[acc_comm], &bulletproof_challenges);
22+
let mut comms = Vec::with_capacity(proofs.len());
23+
let mut bulletproof_challenges = vec![];
24+
25+
for proof in proofs {
26+
let chals = &proof
27+
.statement
28+
.proof_state
29+
.deferred_values
30+
.bulletproof_challenges;
31+
let mut chals: Vec<Fp> = chals
32+
.iter()
33+
.map(|chal| {
34+
let prechallenge = &chal.prechallenge.inner;
35+
let prechallenge: [u64; 2] = prechallenge.each_ref().map(|c| c.as_u64());
36+
37+
ScalarChallenge::limbs_to_field(&prechallenge)
38+
})
39+
.collect();
40+
41+
bulletproof_challenges.append(&mut chals);
42+
43+
let of_coord =
44+
|(x, y): &(BigInt, BigInt)| Ok(Vesta::of_coordinates(x.to_field()?, y.to_field()?));
45+
46+
// statement.proof_state.messages_for_next_wrap_proof.challenge_polynomial_commitment
47+
let acc_comm = &proof
48+
.statement
49+
.proof_state
50+
.messages_for_next_wrap_proof
51+
.challenge_polynomial_commitment;
52+
let acc_comm: Vesta = of_coord(acc_comm)?;
53+
54+
comms.push(acc_comm);
55+
}
56+
57+
let acc_check = urs_utils::batch_dlog_accumulator_check(urs, &comms, &bulletproof_challenges);
4458

4559
if !acc_check {
4660
println!("accumulator_check failed");

ledger/src/proofs/verification.rs

Lines changed: 108 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::rc::Rc;
33
use ark_ff::fields::arithmetic::InvalidBigInt;
44
use ark_poly::{EvaluationDomain, Radix2EvaluationDomain};
55
use ark_serialize::Write;
6+
use itertools::Itertools;
67
use poly_commitment::srs::SRS;
78

89
use crate::{
@@ -519,6 +520,38 @@ fn verify_with(
519520
)
520521
}
521522

523+
pub struct VerificationContext<'a> {
524+
pub verifier_index: &'a VerifierIndex<Fq>,
525+
pub proof: &'a ProverProof<Fq>,
526+
pub public_input: &'a [Fq],
527+
}
528+
529+
fn batch_verify(proofs: &[VerificationContext]) -> Result<(), VerifyError> {
530+
use kimchi::groupmap::GroupMap;
531+
use kimchi::mina_curves::pasta::PallasParameters;
532+
use kimchi::verifier::Context;
533+
use mina_poseidon::sponge::{DefaultFqSponge, DefaultFrSponge};
534+
use poly_commitment::evaluation_proof::OpeningProof;
535+
536+
type SpongeParams = mina_poseidon::constants::PlonkSpongeConstantsKimchi;
537+
type EFqSponge = DefaultFqSponge<PallasParameters, SpongeParams>;
538+
type EFrSponge = DefaultFrSponge<Fq, SpongeParams>;
539+
540+
let group_map = GroupMap::<Fp>::setup();
541+
let proofs = proofs
542+
.iter()
543+
.map(|p| Context {
544+
verifier_index: p.verifier_index,
545+
proof: p.proof,
546+
public_input: p.public_input,
547+
})
548+
.collect_vec();
549+
550+
kimchi::verifier::batch_verify::<Pallas, EFqSponge, EFrSponge, OpeningProof<Pallas>>(
551+
&group_map, &proofs,
552+
)
553+
}
554+
522555
fn run_checks(
523556
proof: &PicklesProofProofsVerified2ReprStableV2,
524557
verifier_index: &VerifierIndex<Fq>,
@@ -713,7 +746,7 @@ pub fn verify_block(
713746
let protocol_state_hash = MinaHash::hash(&protocol_state);
714747

715748
let accum_check =
716-
accumulator_check::accumulator_check(srs, protocol_state_proof).unwrap_or(false);
749+
accumulator_check::accumulator_check(srs, &[protocol_state_proof]).unwrap_or(false);
717750
let verified = verify_impl(&protocol_state_hash, protocol_state_proof, &vk).unwrap_or(false);
718751

719752
accum_check && verified
@@ -730,12 +763,27 @@ pub fn verify_transaction<'a>(
730763
data: (),
731764
};
732765

733-
proofs.into_iter().all(|(statement, transaction_proof)| {
734-
let accum_check =
735-
accumulator_check::accumulator_check(srs, transaction_proof).unwrap_or(false);
736-
let verified = verify_impl(statement, transaction_proof, &vk).unwrap_or(false);
737-
accum_check && verified
738-
})
766+
let mut inputs: Vec<(
767+
&Statement<SokDigest>,
768+
&PicklesProofProofsVerified2ReprStableV2,
769+
&VK,
770+
)> = Vec::with_capacity(128);
771+
772+
let mut accum_check_proofs: Vec<&PicklesProofProofsVerified2ReprStableV2> =
773+
Vec::with_capacity(128);
774+
775+
proofs
776+
.into_iter()
777+
.for_each(|(statement, transaction_proof)| {
778+
accum_check_proofs.push(transaction_proof);
779+
inputs.push((statement, transaction_proof, &vk));
780+
});
781+
782+
let accum_check =
783+
accumulator_check::accumulator_check(srs, &accum_check_proofs).unwrap_or(false);
784+
785+
let verified = batch_verify_impl(inputs.as_slice()).unwrap_or(false);
786+
accum_check && verified
739787
}
740788

741789
/// 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(
753801
data: (),
754802
};
755803

756-
let accum_check = accumulator_check::accumulator_check(srs, sideloaded_proof).unwrap_or(false);
804+
let accum_check =
805+
accumulator_check::accumulator_check(srs, &[sideloaded_proof]).unwrap_or(false);
757806
let verified = verify_impl(&zkapp_statement, sideloaded_proof, &vk).unwrap_or(false);
758807

759808
let ok = accum_check && verified;
@@ -811,6 +860,57 @@ where
811860
Ok(result.is_ok() && checks)
812861
}
813862

863+
fn batch_verify_impl<AppState>(
864+
proofs: &[(&AppState, &PicklesProofProofsVerified2ReprStableV2, &VK)],
865+
) -> Result<bool, InvalidBigInt>
866+
where
867+
AppState: ToFieldElements<Fp>,
868+
{
869+
let mut verification_contexts = Vec::with_capacity(proofs.len());
870+
let mut checks = true;
871+
872+
for (app_state, proof, vk) in proofs {
873+
let deferred_values = compute_deferred_values(proof)?;
874+
checks = checks && run_checks(proof, vk.index);
875+
876+
let message_for_next_step_proof = get_message_for_next_step_proof(
877+
&proof.statement.messages_for_next_step_proof,
878+
&vk.commitments,
879+
app_state,
880+
)?;
881+
882+
let message_for_next_wrap_proof = get_message_for_next_wrap_proof(
883+
&proof.statement.proof_state.messages_for_next_wrap_proof,
884+
)?;
885+
886+
let prepared_statement = get_prepared_statement(
887+
&message_for_next_step_proof,
888+
&message_for_next_wrap_proof,
889+
deferred_values,
890+
&proof.statement.proof_state.sponge_digest_before_evaluations,
891+
);
892+
893+
let npublic_input = vk.index.public;
894+
let public_inputs = prepared_statement.to_public_input(npublic_input)?;
895+
let proof_padded = make_padded_proof_from_p2p(proof)?;
896+
897+
verification_contexts.push((vk.index, proof_padded, public_inputs));
898+
}
899+
900+
let proofs: Vec<VerificationContext> = verification_contexts
901+
.iter()
902+
.map(|(vk, proof, public_input)| VerificationContext {
903+
verifier_index: vk,
904+
proof,
905+
public_input,
906+
})
907+
.collect();
908+
909+
let result = batch_verify(&proofs);
910+
911+
Ok(result.is_ok() && checks)
912+
}
913+
814914
/// Dump data when it fails, to reproduce and compare in OCaml
815915
fn dump_zkapp_verification(
816916
verification_key: &VerificationKey,

ledger/src/staged_ledger/staged_ledger.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1247,12 +1247,16 @@ impl StagedLedger {
12471247
supercharge_coinbase: bool,
12481248
) -> Result<DiffResult, StagedLedgerError> {
12491249
let work = witness.completed_works();
1250+
let works_count = work.len();
12501251

12511252
let now = redux::Instant::now();
12521253
if skip_verification.is_none() {
12531254
Self::check_completed_works(logger, verifier, &self.scan_state, work)?;
12541255
}
1255-
eprintln!("verification time={:?}", now.elapsed());
1256+
eprintln!(
1257+
"verification time={:?} ({works_count} completed works)",
1258+
now.elapsed()
1259+
);
12561260

12571261
let prediff = witness.get(
12581262
|cmd| Self::check_commands(self.ledger.clone(), verifier, cmd, skip_verification),

0 commit comments

Comments
 (0)