//! Block verification and chain state updates for Zebra. //! //! Verification occurs in multiple stages: //! - getting blocks (disk- or network-bound) //! - context-free verification of signatures, proofs, and scripts (CPU-bound) //! - context-dependent verification of the chain state (awaits a verified parent block) //! //! Verification is provided via a `tower::Service`, to support backpressure and batch //! verification. #[cfg(test)] mod tests; use chrono::Utc; use futures_util::FutureExt; use std::{ error, future::Future, pin::Pin, sync::Arc, task::{Context, Poll}, }; use tower::{buffer::Buffer, Service, ServiceExt}; use zebra_chain::block::{Block, BlockHeaderHash}; struct BlockVerifier { /// The underlying `ZebraState`, possibly wrapped in other services. state_service: S, } /// The error type for the BlockVerifier Service. // TODO(jlusby): Error = Report ? type Error = Box; /// The BlockVerifier service implementation. /// /// After verification, blocks are added to the underlying state service. impl Service> for BlockVerifier where S: Service + Send + Clone + 'static, S::Future: Send + 'static, { type Response = BlockHeaderHash; type Error = Error; type Future = Pin> + Send + 'static>>; fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { // We don't expect the state to exert backpressure on verifier users, // so we don't need to call `state_service.poll_ready()` here. Poll::Ready(Ok(())) } fn call(&mut self, block: Arc) -> Self::Future { // TODO(jlusby): Error = Report, handle errors from state_service. // TODO(teor): // - handle chain reorgs // - adjust state_service "unique block height" conditions let mut state_service = self.state_service.clone(); async move { // Since errors cause an early exit, try to do the // quick checks first. let now = Utc::now(); block.header.is_time_valid_at(now)?; block.header.is_equihash_solution_valid()?; block.is_coinbase_first()?; // `Tower::Buffer` requires a 1:1 relationship between `poll()`s // and `call()`s, because it reserves a buffer slot in each // `call()`. let add_block = state_service .ready_and() .await? .call(zebra_state::Request::AddBlock { block }); match add_block.await? { zebra_state::Response::Added { hash } => Ok(hash), _ => Err("adding block to zebra-state failed".into()), } } .boxed() } } /// Return a block verification service, using the provided state service. /// /// The block verifier holds a state service of type `S`, used as context for /// block validation and to which newly verified blocks will be committed. This /// state is pluggable to allow for testing or instrumentation. /// /// The returned type is opaque to allow instrumentation or other wrappers, but /// can be boxed for storage. It is also `Clone` to allow sharing of a /// verification service. /// /// This function should be called only once for a particular state service (and /// the result be shared) rather than constructing multiple verification services /// backed by the same state layer. // // Only used by tests and other modules #[allow(dead_code)] pub fn init( state_service: S, ) -> impl Service< Arc, Response = BlockHeaderHash, Error = Error, Future = impl Future>, > + Send + Clone + 'static where S: Service + Send + Clone + 'static, S::Future: Send + 'static, { Buffer::new(BlockVerifier { state_service }, 1) }