1use std::{future::Future, pin::Pin, sync::Arc};
23use tracing::Instrument;
45use zebra_chain::{parameters::NetworkUpgrade, transparent};
6use zebra_script::CachedFfiTransaction;
78use crate::BoxError;
910/// Asynchronous script verification.
11///
12/// The verifier asynchronously requests the UTXO a transaction attempts
13/// to use as an input, and verifies the script as soon as it becomes
14/// available. This allows script verification to be performed
15/// asynchronously, rather than requiring that the entire chain up to
16/// the previous block is ready.
17///
18/// The asynchronous script verification design is documented in [RFC4].
19///
20/// [RFC4]: https://zebra.zfnd.org/dev/rfcs/0004-asynchronous-script-verification.html
21#[derive(Debug, Clone, Default, Copy, PartialEq, Eq)]
22pub struct Verifier;
2324/// A script verification request.
25#[derive(Debug)]
26pub struct Request {
27/// A cached transaction, in the format required by the script verifier FFI interface.
28pub cached_ffi_transaction: Arc<CachedFfiTransaction>,
29/// The index of an input in `cached_ffi_transaction`, used for verifying this request
30 ///
31 /// Coinbase inputs are rejected by the script verifier, because they do not spend a UTXO.
32pub input_index: usize,
33/// The network upgrade active in the context of this verification request.
34 ///
35 /// Because the consensus branch ID changes with each network upgrade,
36 /// it has to be specified on a per-request basis.
37pub upgrade: NetworkUpgrade,
38}
3940impl tower::Service<Request> for Verifier {
41type Response = ();
42type Error = BoxError;
43type Future =
44 Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
4546fn poll_ready(
47&mut self,
48 _cx: &mut std::task::Context<'_>,
49 ) -> std::task::Poll<Result<(), Self::Error>> {
50 std::task::Poll::Ready(Ok(()))
51 }
5253fn call(&mut self, req: Request) -> Self::Future {
54use futures_util::FutureExt;
5556let Request {
57 cached_ffi_transaction,
58 input_index,
59 upgrade,
60 } = req;
61let input = &cached_ffi_transaction.inputs()[input_index];
62match input {
63 transparent::Input::PrevOut { outpoint, .. } => {
64let outpoint = *outpoint;
6566// Avoid calling the state service if the utxo is already known
67let span = tracing::trace_span!("script", ?outpoint);
6869async move {
70 cached_ffi_transaction.is_valid(upgrade, input_index)?;
71tracing::trace!("script verification succeeded");
7273Ok(())
74 }
75 .instrument(span)
76 .boxed()
77 }
78 transparent::Input::Coinbase { .. } => {
79async { Err("unexpected coinbase input".into()) }.boxed()
80 }
81 }
82 }
83}