zebra_consensus/primitives/
sapling.rs1use core::fmt;
4use std::{
5 future::Future,
6 mem,
7 pin::Pin,
8 task::{Context, Poll},
9};
10
11use futures::{future::BoxFuture, FutureExt};
12use once_cell::sync::Lazy;
13use rand::thread_rng;
14use tokio::sync::watch;
15use tower::{util::ServiceFn, Service};
16use tower_batch_control::{Batch, BatchControl, RequestWeight};
17use tower_fallback::Fallback;
18
19use sapling_crypto::{bundle::Authorized, BatchValidator, Bundle};
20use zcash_proofs::prover::LocalTxProver;
21use zcash_protocol::value::ZatBalance;
22use zebra_chain::transaction::SigHash;
23
24use crate::{error::TransactionError, BoxError};
25
26static SAPLING: Lazy<LocalTxProver> = Lazy::new(LocalTxProver::bundled);
33
34#[derive(Clone)]
35pub struct Item {
36 bundle: Bundle<Authorized, ZatBalance>,
38 sighash: SigHash,
40}
41
42impl Item {
43 pub fn new(bundle: Bundle<Authorized, ZatBalance>, sighash: SigHash) -> Self {
45 Self { bundle, sighash }
46 }
47}
48
49impl RequestWeight for Item {}
50
51#[derive(Default)]
55pub struct Verifier {
56 batch: BatchValidator,
58
59 tx: watch::Sender<Option<bool>>,
64}
65
66impl fmt::Debug for Verifier {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 f.debug_struct("Verifier")
69 .field("batch", &"..")
70 .field("tx", &self.tx)
71 .finish()
72 }
73}
74
75impl Drop for Verifier {
76 fn drop(&mut self) {
81 let batch = mem::take(&mut self.batch);
82 let tx = mem::take(&mut self.tx);
83
84 rayon::spawn_fifo(move || {
86 let (spend_vk, output_vk) = SAPLING.verifying_keys();
87
88 let res = batch.validate(&spend_vk, &output_vk, thread_rng());
90 let _ = tx.send(Some(res));
91 });
92 }
93}
94
95impl Service<BatchControl<Item>> for Verifier {
96 type Response = ();
97 type Error = Box<dyn std::error::Error + Send + Sync>;
98 type Future = Pin<Box<dyn Future<Output = Result<(), Self::Error>> + Send>>;
99
100 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
101 Poll::Ready(Ok(()))
102 }
103
104 fn call(&mut self, req: BatchControl<Item>) -> Self::Future {
105 match req {
106 BatchControl::Item(item) => {
107 let mut rx = self.tx.subscribe();
108
109 let bundle_check = self
110 .batch
111 .check_bundle(item.bundle, item.sighash.into())
112 .then_some(())
113 .ok_or(TransactionError::SaplingVerificationFailed);
114
115 async move {
116 bundle_check.map_err(BoxError::from)?;
117
118 rx.changed()
119 .await
120 .map_err(|_| BoxError::from("verifier was dropped without flushing"))?;
121
122 let is_valid = rx.borrow().ok_or_else(|| {
125 BoxError::from("threadpool unexpectedly dropped channel sender")
126 })?;
127
128 if is_valid {
129 metrics::counter!("proofs.sapling.verified").increment(1);
130 Ok(())
131 } else {
132 metrics::counter!("proofs.sapling.invalid").increment(1);
133 Err(BoxError::from(TransactionError::SaplingVerificationFailed))
134 }
135 }
136 .boxed()
137 }
138
139 BatchControl::Flush => {
140 let batch = mem::take(&mut self.batch);
141 let tx = mem::take(&mut self.tx);
142
143 async move {
144 let start = std::time::Instant::now();
145 let spawn_result = tokio::task::spawn_blocking(move || {
146 let (spend_vk, output_vk) = SAPLING.verifying_keys();
147 batch.validate(&spend_vk, &output_vk, thread_rng())
148 })
149 .await;
150 let duration = start.elapsed().as_secs_f64();
151
152 let result_label = match &spawn_result {
153 Ok(true) => "success",
154 _ => "failure",
155 };
156 metrics::histogram!(
157 "zebra.consensus.batch.duration_seconds",
158 "verifier" => "groth16_sapling",
159 "result" => result_label
160 )
161 .record(duration);
162
163 let is_valid = spawn_result.as_ref().ok().copied();
165 let _ = tx.send(is_valid);
166 spawn_result.map(|_| ()).map_err(Self::Error::from)
167 }
168 .boxed()
169 }
170 }
171 }
172}
173
174pub fn verify_single(
176 item: Item,
177) -> Pin<Box<dyn Future<Output = Result<(), Box<dyn std::error::Error + Send + Sync>>> + Send>> {
178 async move {
179 let mut verifier = Verifier::default();
180
181 let check = verifier
182 .batch
183 .check_bundle(item.bundle, item.sighash.into())
184 .then_some(())
185 .ok_or(TransactionError::SaplingVerificationFailed);
186 check.map_err(BoxError::from)?;
187
188 let is_valid = tokio::task::spawn_blocking(move || {
189 let (spend_vk, output_vk) = SAPLING.verifying_keys();
190
191 mem::take(&mut verifier.batch).validate(&spend_vk, &output_vk, thread_rng())
192 })
193 .await
194 .map_err(|_| BoxError::from("Sapling bundle validation thread panicked"))?;
195
196 if is_valid {
197 Ok(())
198 } else {
199 Err(BoxError::from(TransactionError::SaplingVerificationFailed))
200 }
201 }
202 .boxed()
203}
204
205pub static VERIFIER: Lazy<
207 Fallback<
208 Batch<Verifier, Item>,
209 ServiceFn<
210 fn(Item) -> BoxFuture<'static, Result<(), Box<dyn std::error::Error + Send + Sync>>>,
211 >,
212 >,
213> = Lazy::new(|| {
214 Fallback::new(
215 Batch::new(
216 Verifier::default(),
217 super::MAX_BATCH_SIZE,
218 None,
219 super::MAX_BATCH_LATENCY,
220 ),
221 tower::service_fn(verify_single),
222 )
223});