zebra_consensus/primitives/
groth16.rs1use std::fmt;
4
5use bellman::{
6 gadgets::multipack,
7 groth16::{batch, PreparedVerifyingKey, VerifyingKey},
8 VerificationError,
9};
10use bls12_381::Bls12;
11use futures::{future::BoxFuture, FutureExt};
12use once_cell::sync::Lazy;
13
14use tokio::sync::watch;
15use tower::util::ServiceFn;
16
17use tower_batch_control::RequestWeight;
18use tower_fallback::BoxedError;
19
20use zebra_chain::{
21 primitives::{
22 ed25519::{self, VerificationKeyBytes},
23 Groth16Proof,
24 },
25 sapling::{Output, PerSpendAnchor, Spend},
26 sprout::{JoinSplit, Nullifier, RandomSeed},
27};
28
29use crate::BoxError;
30
31use super::spawn_fifo_and_convert;
32
33mod params;
34#[cfg(test)]
35mod tests;
36#[cfg(test)]
37mod vectors;
38
39pub use params::{SAPLING, SPROUT};
40
41use crate::error::TransactionError;
42
43type VerifyResult = Result<(), VerificationError>;
45
46type Sender = watch::Sender<Option<VerifyResult>>;
48
49#[derive(Clone, Debug)]
52pub struct Item(batch::Item<Bls12>);
53
54impl RequestWeight for Item {}
55
56impl<T: Into<batch::Item<Bls12>>> From<T> for Item {
57 fn from(value: T) -> Self {
58 Self(value.into())
59 }
60}
61
62impl Item {
63 pub fn verify_single(self, pvk: &PreparedVerifyingKey<Bls12>) -> VerifyResult {
65 self.0.verify_single(pvk)
66 }
67}
68
69pub type BatchVerifyingKey = VerifyingKey<Bls12>;
72
73pub type ItemVerifyingKey = PreparedVerifyingKey<Bls12>;
76
77pub static JOINSPLIT_VERIFIER: Lazy<
86 ServiceFn<fn(Item) -> BoxFuture<'static, Result<(), BoxedError>>>,
87> = Lazy::new(|| {
88 tower::service_fn(
92 (|item: Item| {
93 Verifier::verify_single_spawning(item, SPROUT.prepared_verifying_key())
95 .map(|result| {
96 result
97 .map_err(|e| TransactionError::Groth16(e.to_string()))
98 .map_err(tower_fallback::BoxedError::from)
99 })
100 .boxed()
101 }) as fn(_) -> _,
102 )
103});
104
105pub trait Description {
108 fn proof(&self) -> &Groth16Proof;
110 fn primary_inputs(&self) -> Vec<jubjub::Fq>;
112}
113
114impl Description for Spend<PerSpendAnchor> {
115 fn primary_inputs(&self) -> Vec<jubjub::Fq> {
123 let mut inputs = vec![];
124
125 let rk_affine = jubjub::AffinePoint::from_bytes(self.rk.clone().into()).unwrap();
126 inputs.push(rk_affine.get_u());
127 inputs.push(rk_affine.get_v());
128
129 let cv_affine = jubjub::AffinePoint::from(self.cv);
130 inputs.push(cv_affine.get_u());
131 inputs.push(cv_affine.get_v());
132
133 inputs.push(jubjub::Fq::from_bytes(&self.per_spend_anchor.into()).unwrap());
135
136 let nullifier_limbs: [jubjub::Fq; 2] = self.nullifier.into();
137
138 inputs.push(nullifier_limbs[0]);
139 inputs.push(nullifier_limbs[1]);
140
141 inputs
142 }
143
144 fn proof(&self) -> &Groth16Proof {
145 &self.zkproof
146 }
147}
148
149impl Description for Output {
150 fn primary_inputs(&self) -> Vec<jubjub::Fq> {
158 let mut inputs = vec![];
159
160 let cv_affine = jubjub::AffinePoint::from(self.cv);
161 inputs.push(cv_affine.get_u());
162 inputs.push(cv_affine.get_v());
163
164 let epk_affine = jubjub::AffinePoint::from_bytes(self.ephemeral_key.into()).unwrap();
165 inputs.push(epk_affine.get_u());
166 inputs.push(epk_affine.get_v());
167
168 inputs.push(self.cm_u);
169
170 inputs
171 }
172
173 fn proof(&self) -> &Groth16Proof {
174 &self.zkproof
175 }
176}
177
178pub(super) fn h_sig(
187 random_seed: &RandomSeed,
188 nf1: &Nullifier,
189 nf2: &Nullifier,
190 joinsplit_pub_key: &VerificationKeyBytes,
191) -> [u8; 32] {
192 let h_sig: [u8; 32] = blake2b_simd::Params::new()
193 .hash_length(32)
194 .personal(b"ZcashComputehSig")
195 .to_state()
196 .update(&(<[u8; 32]>::from(random_seed))[..])
197 .update(&(<[u8; 32]>::from(nf1))[..])
198 .update(&(<[u8; 32]>::from(nf2))[..])
199 .update(joinsplit_pub_key.as_ref())
200 .finalize()
201 .as_bytes()
202 .try_into()
203 .expect("32 byte array");
204 h_sig
205}
206
207impl Description for (&JoinSplit<Groth16Proof>, &ed25519::VerificationKeyBytes) {
208 #[allow(clippy::needless_borrow)]
222 fn primary_inputs(&self) -> Vec<jubjub::Fq> {
223 let (joinsplit, joinsplit_pub_key) = self;
224
225 let rt: [u8; 32] = joinsplit.anchor.into();
226 let mac1: [u8; 32] = (&joinsplit.vmacs[0]).into();
227 let mac2: [u8; 32] = (&joinsplit.vmacs[1]).into();
228 let nf1: [u8; 32] = (&joinsplit.nullifiers[0]).into();
229 let nf2: [u8; 32] = (&joinsplit.nullifiers[1]).into();
230 let cm1: [u8; 32] = (&joinsplit.commitments[0]).into();
231 let cm2: [u8; 32] = (&joinsplit.commitments[1]).into();
232 let vpub_old = joinsplit.vpub_old.to_bytes();
233 let vpub_new = joinsplit.vpub_new.to_bytes();
234
235 let h_sig = h_sig(
236 &joinsplit.random_seed,
237 &joinsplit.nullifiers[0],
238 &joinsplit.nullifiers[1],
239 joinsplit_pub_key,
240 );
241
242 let mut public_input = Vec::with_capacity((32 * 8) + (8 * 2));
244 public_input.extend(rt);
245 public_input.extend(h_sig);
246 public_input.extend(nf1);
247 public_input.extend(mac1);
248 public_input.extend(nf2);
249 public_input.extend(mac2);
250 public_input.extend(cm1);
251 public_input.extend(cm2);
252 public_input.extend(vpub_old);
253 public_input.extend(vpub_new);
254
255 let public_input = multipack::bytes_to_bits(&public_input);
256
257 multipack::compute_multipacking(&public_input)
258 }
259
260 fn proof(&self) -> &Groth16Proof {
261 &self.0.zkproof
262 }
263}
264
265pub struct DescriptionWrapper<T>(pub T);
269
270impl<T> TryFrom<DescriptionWrapper<&T>> for Item
271where
272 T: Description,
273{
274 type Error = TransactionError;
275
276 fn try_from(input: DescriptionWrapper<&T>) -> Result<Self, Self::Error> {
277 Ok(Item::from((
286 bellman::groth16::Proof::read(&input.0.proof().0[..])
287 .map_err(|e| TransactionError::MalformedGroth16(e.to_string()))?,
288 input.0.primary_inputs(),
289 )))
290 }
291}
292
293pub struct Verifier {
299 tx: Sender,
304}
305
306impl Verifier {
307 async fn verify_single_spawning(
309 item: Item,
310 pvk: &'static ItemVerifyingKey,
311 ) -> Result<(), BoxError> {
312 spawn_fifo_and_convert(move || item.verify_single(pvk)).await
314 }
315}
316
317impl fmt::Debug for Verifier {
318 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
319 let name = "Verifier";
320 f.debug_struct(name)
321 .field("batch", &"..")
322 .field("tx", &self.tx)
323 .finish()
324 }
325}