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 sprout::{JoinSplit, Nullifier, RandomSeed},
26};
27
28use crate::BoxError;
29
30use super::spawn_fifo_and_convert;
31
32mod params;
33#[cfg(test)]
34mod tests;
35#[cfg(test)]
36mod vectors;
37
38pub use params::{SAPLING, SPROUT};
39
40use crate::error::TransactionError;
41
42type VerifyResult = Result<(), VerificationError>;
44
45type Sender = watch::Sender<Option<VerifyResult>>;
47
48#[derive(Clone, Debug)]
51pub struct Item(batch::Item<Bls12>);
52
53impl RequestWeight for Item {}
54
55impl<T: Into<batch::Item<Bls12>>> From<T> for Item {
56 fn from(value: T) -> Self {
57 Self(value.into())
58 }
59}
60
61impl Item {
62 pub fn verify_single(self, pvk: &PreparedVerifyingKey<Bls12>) -> VerifyResult {
64 self.0.verify_single(pvk)
65 }
66}
67
68pub type BatchVerifyingKey = VerifyingKey<Bls12>;
71
72pub type ItemVerifyingKey = PreparedVerifyingKey<Bls12>;
75
76pub static JOINSPLIT_VERIFIER: Lazy<
85 ServiceFn<fn(Item) -> BoxFuture<'static, Result<(), BoxedError>>>,
86> = Lazy::new(|| {
87 tower::service_fn(
91 (|item: Item| {
92 Verifier::verify_single_spawning(item, SPROUT.prepared_verifying_key())
94 .map(|result| {
95 result
96 .map_err(|e| TransactionError::Groth16(e.to_string()))
97 .map_err(tower_fallback::BoxedError::from)
98 })
99 .boxed()
100 }) as fn(_) -> _,
101 )
102});
103
104pub trait Description {
107 fn proof(&self) -> &Groth16Proof;
109 fn primary_inputs(&self) -> Vec<jubjub::Fq>;
111}
112
113pub(super) fn h_sig(
122 random_seed: &RandomSeed,
123 nf1: &Nullifier,
124 nf2: &Nullifier,
125 joinsplit_pub_key: &VerificationKeyBytes,
126) -> [u8; 32] {
127 let h_sig: [u8; 32] = blake2b_simd::Params::new()
128 .hash_length(32)
129 .personal(b"ZcashComputehSig")
130 .to_state()
131 .update(&(<[u8; 32]>::from(random_seed))[..])
132 .update(&(<[u8; 32]>::from(nf1))[..])
133 .update(&(<[u8; 32]>::from(nf2))[..])
134 .update(joinsplit_pub_key.as_ref())
135 .finalize()
136 .as_bytes()
137 .try_into()
138 .expect("32 byte array");
139 h_sig
140}
141
142impl Description for (&JoinSplit<Groth16Proof>, &ed25519::VerificationKeyBytes) {
143 #[allow(clippy::needless_borrow)]
157 fn primary_inputs(&self) -> Vec<jubjub::Fq> {
158 let (joinsplit, joinsplit_pub_key) = self;
159
160 let rt: [u8; 32] = joinsplit.anchor.into();
161 let mac1: [u8; 32] = (&joinsplit.vmacs[0]).into();
162 let mac2: [u8; 32] = (&joinsplit.vmacs[1]).into();
163 let nf1: [u8; 32] = (&joinsplit.nullifiers[0]).into();
164 let nf2: [u8; 32] = (&joinsplit.nullifiers[1]).into();
165 let cm1: [u8; 32] = (&joinsplit.commitments[0]).into();
166 let cm2: [u8; 32] = (&joinsplit.commitments[1]).into();
167 let vpub_old = joinsplit.vpub_old.to_bytes();
168 let vpub_new = joinsplit.vpub_new.to_bytes();
169
170 let h_sig = h_sig(
171 &joinsplit.random_seed,
172 &joinsplit.nullifiers[0],
173 &joinsplit.nullifiers[1],
174 joinsplit_pub_key,
175 );
176
177 let mut public_input = Vec::with_capacity((32 * 8) + (8 * 2));
179 public_input.extend(rt);
180 public_input.extend(h_sig);
181 public_input.extend(nf1);
182 public_input.extend(mac1);
183 public_input.extend(nf2);
184 public_input.extend(mac2);
185 public_input.extend(cm1);
186 public_input.extend(cm2);
187 public_input.extend(vpub_old);
188 public_input.extend(vpub_new);
189
190 let public_input = multipack::bytes_to_bits(&public_input);
191
192 multipack::compute_multipacking(&public_input)
193 }
194
195 fn proof(&self) -> &Groth16Proof {
196 &self.0.zkproof
197 }
198}
199
200pub struct DescriptionWrapper<T>(pub T);
204
205impl<T> TryFrom<DescriptionWrapper<&T>> for Item
206where
207 T: Description,
208{
209 type Error = TransactionError;
210
211 fn try_from(input: DescriptionWrapper<&T>) -> Result<Self, Self::Error> {
212 Ok(Item::from((
221 bellman::groth16::Proof::read(&input.0.proof().0[..])
222 .map_err(|e| TransactionError::MalformedGroth16(e.to_string()))?,
223 input.0.primary_inputs(),
224 )))
225 }
226}
227
228pub struct Verifier {
234 tx: Sender,
239}
240
241impl Verifier {
242 async fn verify_single_spawning(
244 item: Item,
245 pvk: &'static ItemVerifyingKey,
246 ) -> Result<(), BoxError> {
247 spawn_fifo_and_convert(move || item.verify_single(pvk)).await
249 }
250}
251
252impl fmt::Debug for Verifier {
253 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254 let name = "Verifier";
255 f.debug_struct(name)
256 .field("batch", &"..")
257 .field("tx", &self.tx)
258 .finish()
259 }
260}