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 crate::BoxError;
21
22use super::spawn_fifo_and_convert;
23
24mod params;
25#[cfg(test)]
26mod tests;
27#[cfg(test)]
28mod vectors;
29
30pub use params::SPROUT;
31
32use crate::error::TransactionError;
33
34type VerifyResult = Result<(), VerificationError>;
36
37type Sender = watch::Sender<Option<VerifyResult>>;
39
40#[derive(Clone, Debug)]
43pub struct Item(batch::Item<Bls12>);
44
45impl RequestWeight for Item {}
46
47impl<T: Into<batch::Item<Bls12>>> From<T> for Item {
48 fn from(value: T) -> Self {
49 Self(value.into())
50 }
51}
52
53impl Item {
54 pub fn verify_single(self, pvk: &PreparedVerifyingKey<Bls12>) -> VerifyResult {
56 self.0.verify_single(pvk)
57 }
58}
59
60pub type BatchVerifyingKey = VerifyingKey<Bls12>;
63
64pub type ItemVerifyingKey = PreparedVerifyingKey<Bls12>;
67
68pub static JOINSPLIT_VERIFIER: Lazy<
77 ServiceFn<fn(Item) -> BoxFuture<'static, Result<(), BoxedError>>>,
78> = Lazy::new(|| {
79 tower::service_fn(
83 (|item: Item| {
84 Verifier::verify_single_spawning(item, SPROUT.prepared_verifying_key())
86 .map(|result| {
87 result
88 .map_err(|e| TransactionError::Groth16(e.to_string()))
89 .map_err(tower_fallback::BoxedError::from)
90 })
91 .boxed()
92 }) as fn(_) -> _,
93 )
94});
95
96pub(super) fn h_sig(
105 random_seed: &[u8; 32],
106 nf1: &[u8; 32],
107 nf2: &[u8; 32],
108 joinsplit_pub_key: &[u8; 32],
109) -> [u8; 32] {
110 let h_sig: [u8; 32] = blake2b_simd::Params::new()
111 .hash_length(32)
112 .personal(b"ZcashComputehSig")
113 .to_state()
114 .update(random_seed)
115 .update(nf1)
116 .update(nf2)
117 .update(joinsplit_pub_key)
118 .finalize()
119 .as_bytes()
120 .try_into()
121 .expect("32 byte array");
122 h_sig
123}
124
125pub fn joinsplit_to_item(
127 js: &zcash_primitives::transaction::components::sprout::JsDescription,
128 joinsplit_pub_key: &[u8; 32],
129) -> Result<Item, TransactionError> {
130 let rt = js.anchor();
131 let nf1 = &js.nullifiers()[0];
132 let nf2 = &js.nullifiers()[1];
133 let mac1 = &js.macs()[0];
134 let mac2 = &js.macs()[1];
135 let cm1 = &js.commitments()[0];
136 let cm2 = &js.commitments()[1];
137
138 let h_sig = h_sig(js.random_seed(), nf1, nf2, joinsplit_pub_key);
139
140 let vpub_old = js.vpub_old().to_i64_le_bytes();
141 let vpub_new = js.vpub_new().to_i64_le_bytes();
142
143 let mut public_input = Vec::with_capacity((32 * 8) + (8 * 2));
144 public_input.extend(rt);
145 public_input.extend(h_sig);
146 public_input.extend(nf1);
147 public_input.extend(mac1);
148 public_input.extend(nf2);
149 public_input.extend(mac2);
150 public_input.extend(cm1);
151 public_input.extend(cm2);
152 public_input.extend(vpub_old);
153 public_input.extend(vpub_new);
154
155 let public_input = multipack::bytes_to_bits(&public_input);
156 let primary_inputs = multipack::compute_multipacking(&public_input);
157
158 let proof_bytes = js.groth_proof_bytes().ok_or_else(|| {
163 TransactionError::MalformedGroth16(
164 "expected Groth16 proof in JoinSplit but found PHGR13 (wrong transaction version?)"
165 .into(),
166 )
167 })?;
168
169 let proof = bellman::groth16::Proof::read(&proof_bytes[..])
170 .map_err(|e| TransactionError::MalformedGroth16(e.to_string()))?;
171
172 Ok(Item::from((proof, primary_inputs)))
173}
174
175pub struct Verifier {
181 tx: Sender,
186}
187
188impl Verifier {
189 async fn verify_single_spawning(
191 item: Item,
192 pvk: &'static ItemVerifyingKey,
193 ) -> Result<(), BoxError> {
194 spawn_fifo_and_convert(move || item.verify_single(pvk)).await
196 }
197}
198
199impl fmt::Debug for Verifier {
200 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201 let name = "Verifier";
202 f.debug_struct(name)
203 .field("batch", &"..")
204 .field("tx", &self.tx)
205 .finish()
206 }
207}