//! Tests for block verification use super::*; use chrono::{Duration, Utc}; use color_eyre::eyre::Report; use color_eyre::eyre::{bail, eyre}; use std::sync::Arc; use tower::{util::ServiceExt, Service}; use zebra_chain::block::Block; use zebra_chain::block::BlockHeader; use zebra_chain::serialization::ZcashDeserialize; use zebra_chain::transaction::Transaction; #[tokio::test] async fn verify_test() -> Result<(), Report> { verify().await } #[spandoc::spandoc] async fn verify() -> 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 state_service = Box::new(zebra_state::in_memory::init()); let mut block_verifier = super::init(state_service); /// 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); 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 } #[spandoc::spandoc] async fn verify_fail_future_time() -> Result<(), Report> { zebra_test::init(); let mut block = ::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_415000_BYTES[..])?; let mut state_service = zebra_state::in_memory::init(); let mut block_verifier = super::init(state_service.clone()); // Modify the block's time // Changing the block header also invalidates the header hashes, but // those checks should be performed later in validation, because they // are more expensive. let three_hours_in_the_future = Utc::now() .checked_add_signed(Duration::hours(3)) .ok_or("overflow when calculating 3 hours in the future") .map_err(|e| eyre!(e))?; block.header.time = three_hours_in_the_future; let arc_block: Arc = block.into(); /// SPANDOC: Make sure the verifier service is ready let ready_verifier_service = block_verifier.ready_and().await.map_err(|e| eyre!(e))?; /// SPANDOC: Try to add the block, and expect failure // TODO(teor || jlusby): check error kind ready_verifier_service .call(arc_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: 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(()) } #[tokio::test] async fn header_solution_test() -> Result<(), Report> { header_solution().await } #[spandoc::spandoc] async fn header_solution() -> Result<(), Report> { zebra_test::init(); // Service variables let state_service = Box::new(zebra_state::in_memory::init()); let mut block_verifier = super::init(state_service.clone()); let ready_verifier_service = block_verifier.ready_and().await.map_err(|e| eyre!(e))?; // Get a valid block let mut block = Block::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_415000_BYTES[..]) .expect("block test vector should deserialize"); // This should be ok ready_verifier_service .call(Arc::new(block.clone())) .await .map_err(|e| eyre!(e))?; // Change nonce to something invalid block.header.nonce = [0; 32]; let ready_verifier_service = block_verifier.ready_and().await.map_err(|e| eyre!(e))?; // Error: invalid equihash solution for BlockHeader ready_verifier_service .call(Arc::new(block.clone())) .await .expect_err("expected the equihash solution to be invalid"); Ok(()) } #[tokio::test] #[spandoc::spandoc] async fn coinbase() -> Result<(), Report> { zebra_test::init(); // Service variables let state_service = Box::new(zebra_state::in_memory::init()); let mut block_verifier = super::init(state_service.clone()); // Get a header of a block let header = BlockHeader::zcash_deserialize(&zebra_test::vectors::DUMMY_HEADER[..]).unwrap(); let ready_verifier_service = block_verifier.ready_and().await.map_err(|e| eyre!(e))?; // Test 1: Empty transaction let block = Block { header, transactions: Vec::new(), }; // Error: no coinbase transaction in block ready_verifier_service .call(Arc::new(block.clone())) .await .expect_err("fail with no coinbase transaction in block"); let ready_verifier_service = block_verifier.ready_and().await.map_err(|e| eyre!(e))?; // Test 2: Transaction at first position is not coinbase let mut transactions = Vec::new(); let tx = Transaction::zcash_deserialize(&zebra_test::vectors::DUMMY_TX1[..]).unwrap(); transactions.push(Arc::new(tx)); let block = Block { header, transactions, }; // Error: no coinbase transaction in block ready_verifier_service .call(Arc::new(block)) .await .expect_err("fail with no coinbase transaction in block"); let ready_verifier_service = block_verifier.ready_and().await.map_err(|e| eyre!(e))?; // Test 3: Invalid coinbase position let mut block = Block::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_415000_BYTES[..])?; assert_eq!(block.transactions.len(), 1); // Extract the coinbase transaction from the block let coinbase_transaction = block.transactions.get(0).unwrap().clone(); // Add another coinbase transaction to block block.transactions.push(coinbase_transaction); assert_eq!(block.transactions.len(), 2); // Error: coinbase input found in additional transaction ready_verifier_service .call(Arc::new(block)) .await .expect_err("fail with coinbase input found in additional transaction"); Ok(()) }