diff --git a/zebra-consensus/src/block.rs b/zebra-consensus/src/block.rs index dfb8b3e9d..010993d2c 100644 --- a/zebra-consensus/src/block.rs +++ b/zebra-consensus/src/block.rs @@ -1,4 +1,4 @@ -//! Block verification and chain state updates for Zebra. +//! Block verification for Zebra. //! //! Verification occurs in multiple stages: //! - getting blocks (disk- or network-bound) @@ -20,12 +20,14 @@ use std::{ sync::Arc, task::{Context, Poll}, }; -use tower::{buffer::Buffer, Service, ServiceExt}; +use tower::{buffer::Buffer, Service}; use zebra_chain::block::{Block, BlockHeaderHash}; struct BlockVerifier { /// The underlying `ZebraState`, possibly wrapped in other services. + // TODO: contextual verification + #[allow(dead_code)] state_service: S, } @@ -35,7 +37,8 @@ type Error = Box; /// The BlockVerifier service implementation. /// -/// After verification, blocks are added to the underlying state service. +/// The state service is only used for contextual verification. +/// (The `ChainVerifier` updates the state.) impl Service> for BlockVerifier where S: Service @@ -50,17 +53,14 @@ where 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. + // We use the state for contextual verification, and we expect those + // queries to be fast. 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 @@ -71,18 +71,11 @@ where 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 }); + // TODO: + // - header verification + // - contextual verification - match add_block.await? { - zebra_state::Response::Added { hash } => Ok(hash), - _ => Err("adding block to zebra-state failed".into()), - } + Ok(block.as_ref().into()) } .boxed() } @@ -91,19 +84,16 @@ where /// 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. +/// block validation. 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)] +/// the result be shared, cloning if needed). Constructing multiple services +/// from the same underlying state might cause synchronisation bugs. pub fn init( state_service: S, ) -> impl Service< diff --git a/zebra-consensus/src/block/tests.rs b/zebra-consensus/src/block/tests.rs index 53296129b..3bc0613f6 100644 --- a/zebra-consensus/src/block/tests.rs +++ b/zebra-consensus/src/block/tests.rs @@ -3,8 +3,8 @@ use super::*; use chrono::{Duration, Utc}; +use color_eyre::eyre::eyre; use color_eyre::eyre::Report; -use color_eyre::eyre::{bail, eyre}; use std::sync::Arc; use tower::{util::ServiceExt, Service}; @@ -42,125 +42,6 @@ async fn verify() -> Result<(), Report> { Ok(()) } -#[tokio::test] -async fn round_trip_test() -> Result<(), Report> { - round_trip().await -} - -#[spandoc::spandoc] -async fn round_trip() -> Result<(), Report> { - zebra_test::init(); - - let block = - Arc::::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_415000_BYTES[..])?; - let hash: BlockHeaderHash = block.as_ref().into(); - - let mut state_service = zebra_state::in_memory::init(); - let mut block_verifier = super::init(state_service.clone()); - - /// SPANDOC: Make sure the verifier service is ready - let ready_verifier_service = block_verifier.ready_and().await.map_err(|e| eyre!(e))?; - /// SPANDOC: Verify the block - let verify_response = ready_verifier_service - .call(block.clone()) - .await - .map_err(|e| eyre!(e))?; - - assert_eq!(verify_response, hash); - - /// SPANDOC: Make sure the state service is ready - let ready_state_service = state_service.ready_and().await.map_err(|e| eyre!(e))?; - /// SPANDOC: Make sure the block was added to the state - let state_response = ready_state_service - .call(zebra_state::Request::GetBlock { hash }) - .await - .map_err(|e| eyre!(e))?; - - if let zebra_state::Response::Block { - block: returned_block, - } = state_response - { - assert_eq!(block, returned_block); - } else { - bail!("unexpected response kind: {:?}", state_response); - } - - Ok(()) -} - -#[tokio::test] -async fn verify_fail_add_block_test() -> Result<(), Report> { - verify_fail_add_block().await -} - -#[spandoc::spandoc] -async fn verify_fail_add_block() -> Result<(), Report> { - zebra_test::init(); - - let block = - Arc::::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_415000_BYTES[..])?; - let hash: BlockHeaderHash = block.as_ref().into(); - - let mut state_service = zebra_state::in_memory::init(); - let mut block_verifier = super::init(state_service.clone()); - - /// SPANDOC: Make sure the verifier service is ready (1/2) - let ready_verifier_service = block_verifier.ready_and().await.map_err(|e| eyre!(e))?; - /// SPANDOC: Verify the block for the first time - let verify_response = ready_verifier_service - .call(block.clone()) - .await - .map_err(|e| eyre!(e))?; - - assert_eq!(verify_response, hash); - - /// SPANDOC: Make sure the state service is ready (1/2) - let ready_state_service = state_service.ready_and().await.map_err(|e| eyre!(e))?; - /// SPANDOC: Make sure the block was added to the state - let state_response = ready_state_service - .call(zebra_state::Request::GetBlock { hash }) - .await - .map_err(|e| eyre!(e))?; - - if let zebra_state::Response::Block { - block: returned_block, - } = state_response - { - assert_eq!(block, returned_block); - } else { - bail!("unexpected response kind: {:?}", state_response); - } - - /// SPANDOC: Make sure the verifier service is ready (2/2) - let ready_verifier_service = block_verifier.ready_and().await.map_err(|e| eyre!(e))?; - /// SPANDOC: Now try to add the block again, verify should fail - // TODO(teor): ignore duplicate block verifies? - // TODO(teor || jlusby): check error kind - ready_verifier_service - .call(block.clone()) - .await - .unwrap_err(); - - /// SPANDOC: Make sure the state service is ready (2/2) - let ready_state_service = state_service.ready_and().await.map_err(|e| eyre!(e))?; - /// SPANDOC: But the state should still return the original block we added - let state_response = ready_state_service - .call(zebra_state::Request::GetBlock { hash }) - .await - .map_err(|e| eyre!(e))?; - - if let zebra_state::Response::Block { - block: returned_block, - } = state_response - { - assert_eq!(block, returned_block); - } else { - bail!("unexpected response kind: {:?}", state_response); - } - - Ok(()) -} - #[tokio::test] async fn verify_fail_future_time_test() -> Result<(), Report> { verify_fail_future_time().await @@ -173,7 +54,7 @@ async fn verify_fail_future_time() -> Result<(), Report> { let mut block = ::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_415000_BYTES[..])?; - let mut state_service = zebra_state::in_memory::init(); + let state_service = zebra_state::in_memory::init(); let mut block_verifier = super::init(state_service.clone()); // Modify the block's time @@ -197,17 +78,6 @@ async fn verify_fail_future_time() -> Result<(), Report> { .await .unwrap_err(); - /// SPANDOC: Make sure the state service is ready (2/2) - let ready_state_service = state_service.ready_and().await.map_err(|e| eyre!(e))?; - /// SPANDOC: Now make sure the block isn't in the state - // TODO(teor || jlusby): check error kind - ready_state_service - .call(zebra_state::Request::GetBlock { - hash: arc_block.as_ref().into(), - }) - .await - .unwrap_err(); - Ok(()) }