Skip to main content

zebra_consensus/
script.rs

1use std::{future::Future, pin::Pin, sync::Arc};
2
3use tracing::Instrument;
4
5use zebra_script::CachedFfiTransaction;
6
7use crate::{primitives::spawn_fifo_and_convert, BoxError};
8
9#[cfg(test)]
10mod tests;
11
12/// Asynchronous script verification.
13///
14/// The verifier asynchronously requests the UTXO a transaction attempts
15/// to use as an input, and verifies the script as soon as it becomes
16/// available.  This allows script verification to be performed
17/// asynchronously, rather than requiring that the entire chain up to
18/// the previous block is ready.
19///
20/// The asynchronous script verification design is documented in [RFC4].
21///
22/// [RFC4]: https://zebra.zfnd.org/dev/rfcs/0004-asynchronous-script-verification.html
23#[derive(Debug, Clone, Default, Copy, PartialEq, Eq)]
24pub struct Verifier;
25
26/// A script verification request.
27#[derive(Debug)]
28pub struct Request {
29    /// A cached transaction, in the format required by the script verifier FFI interface.
30    pub cached_ffi_transaction: Arc<CachedFfiTransaction>,
31    /// The index of an input in `cached_ffi_transaction`, used for verifying this request
32    ///
33    /// Coinbase inputs are rejected by the script verifier, because they do not spend a UTXO.
34    pub input_index: usize,
35}
36
37impl tower::Service<Request> for Verifier {
38    type Response = ();
39    type Error = BoxError;
40    type Future =
41        Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
42
43    fn poll_ready(
44        &mut self,
45        _cx: &mut std::task::Context<'_>,
46    ) -> std::task::Poll<Result<(), Self::Error>> {
47        std::task::Poll::Ready(Ok(()))
48    }
49
50    fn call(&mut self, req: Request) -> Self::Future {
51        use futures_util::FutureExt;
52
53        let Request {
54            cached_ffi_transaction,
55            input_index,
56        } = req;
57
58        let span = tracing::trace_span!("script");
59        async move {
60            // Script verification is CPU-bound so run in Rayon thread
61            spawn_fifo_and_convert(move || cached_ffi_transaction.is_valid(input_index)).await?;
62            tracing::trace!(input_index, "script verification succeeded");
63
64            Ok(())
65        }
66        .instrument(span)
67        .boxed()
68    }
69}