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