sapling_crypto/verifier/
batch.rs1use bellman::groth16;
2use bls12_381::Bls12;
3use group::GroupEncoding;
4use rand_core::{CryptoRng, RngCore};
5
6use super::SaplingVerificationContextInner;
7use crate::{
8 bundle::{Authorized, Bundle},
9 circuit::{OutputVerifyingKey, SpendVerifyingKey},
10};
11
12pub struct BatchValidator {
18 bundles_added: bool,
19 spend_proofs: groth16::batch::Verifier<Bls12>,
20 output_proofs: groth16::batch::Verifier<Bls12>,
21 signatures: redjubjub::batch::Verifier,
22}
23
24impl Default for BatchValidator {
25 fn default() -> Self {
26 Self::new()
27 }
28}
29
30impl BatchValidator {
31 pub fn new() -> Self {
33 BatchValidator {
34 bundles_added: false,
35 spend_proofs: groth16::batch::Verifier::new(),
36 output_proofs: groth16::batch::Verifier::new(),
37 signatures: redjubjub::batch::Verifier::new(),
38 }
39 }
40
41 pub fn check_bundle<V: Copy + Into<i64>>(
49 &mut self,
50 bundle: Bundle<Authorized, V>,
51 sighash: [u8; 32],
52 ) -> bool {
53 self.bundles_added = true;
54
55 let mut ctx = SaplingVerificationContextInner::new();
56
57 for spend in bundle.shielded_spends() {
58 let zkproof = match groth16::Proof::read(&spend.zkproof()[..]) {
60 Ok(p) => p,
61 Err(_) => return false,
62 };
63
64 let consensus_rules_passed = ctx.check_spend(
67 spend.cv(),
68 *spend.anchor(),
69 &spend.nullifier().0,
70 spend.rk(),
71 zkproof,
72 self,
73 |this, rk| {
74 this.signatures
75 .queue(((*rk).into(), *spend.spend_auth_sig(), &sighash));
76 true
77 },
78 |this, proof, public_inputs| {
79 this.spend_proofs.queue((proof, public_inputs.to_vec()));
80 true
81 },
82 );
83 if !consensus_rules_passed {
84 return false;
85 }
86 }
87
88 for output in bundle.shielded_outputs() {
89 let epk = match jubjub::ExtendedPoint::from_bytes(&output.ephemeral_key().0).into() {
91 Some(p) => p,
92 None => return false,
93 };
94
95 let zkproof = match groth16::Proof::read(&output.zkproof()[..]) {
97 Ok(p) => p,
98 Err(_) => return false,
99 };
100
101 let consensus_rules_passed = ctx.check_output(
103 output.cv(),
104 *output.cmu(),
105 epk,
106 zkproof,
107 |proof, public_inputs| {
108 self.output_proofs.queue((proof, public_inputs.to_vec()));
109 true
110 },
111 );
112 if !consensus_rules_passed {
113 return false;
114 }
115 }
116
117 ctx.final_check(*bundle.value_balance(), |bvk| {
119 self.signatures
120 .queue((bvk.into(), bundle.authorization().binding_sig, &sighash));
121 true
122 })
123 }
124
125 pub fn validate<R: RngCore + CryptoRng>(
132 self,
133 spend_vk: &SpendVerifyingKey,
134 output_vk: &OutputVerifyingKey,
135 mut rng: R,
136 ) -> bool {
137 if !self.bundles_added {
138 return true;
140 }
141
142 if let Err(e) = self.signatures.verify(&mut rng) {
143 #[cfg(feature = "std")]
144 tracing::debug!("Signature batch validation failed: {}", e);
145 #[cfg(not(feature = "std"))]
146 tracing::debug!("Signature batch validation failed: {:?}", e);
147 return false;
148 }
149
150 #[cfg(feature = "multicore")]
151 let verify_proofs = |batch: groth16::batch::Verifier<Bls12>, vk| batch.verify_multicore(vk);
152
153 #[cfg(not(feature = "multicore"))]
154 let mut verify_proofs =
155 |batch: groth16::batch::Verifier<Bls12>, vk| batch.verify(&mut rng, vk);
156
157 if verify_proofs(self.spend_proofs, &spend_vk.0).is_err() {
158 tracing::debug!("Spend proof batch validation failed");
159 return false;
160 }
161
162 if verify_proofs(self.output_proofs, &output_vk.0).is_err() {
163 tracing::debug!("Output proof batch validation failed");
164 return false;
165 }
166
167 true
168 }
169}