diff --git a/Cargo.lock b/Cargo.lock index 895061e7..9a9724c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2767,6 +2767,7 @@ dependencies = [ "futures", "futures-util", "metrics", + "once_cell", "rand 0.7.3", "redjubjub", "spandoc", diff --git a/zebra-consensus/Cargo.toml b/zebra-consensus/Cargo.toml index 127ff6ca..a975f2fa 100644 --- a/zebra-consensus/Cargo.toml +++ b/zebra-consensus/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" [dependencies] chrono = "0.4.13" color-eyre = "0.5" +once_cell = "1.4" rand = "0.7" redjubjub = "0.2" diff --git a/zebra-consensus/src/block/tests.rs b/zebra-consensus/src/block/tests.rs index 8f31002c..b75a87d0 100644 --- a/zebra-consensus/src/block/tests.rs +++ b/zebra-consensus/src/block/tests.rs @@ -4,181 +4,115 @@ use super::*; use chrono::Utc; use color_eyre::eyre::{eyre, Report}; +use once_cell::sync::Lazy; use zebra_chain::block::Block; use zebra_chain::block::BlockHeader; -use zebra_chain::serialization::ZcashDeserialize; -use zebra_chain::transaction::Transaction; +use zebra_chain::serialization::{ZcashDeserialize, ZcashDeserializeInto}; +use zebra_test::transcript::{TransError, Transcript}; -#[tokio::test] -async fn verify_test() -> Result<(), Report> { - verify().await -} +static VALID_BLOCK_TRANSCRIPT: Lazy, Result)>> = + Lazy::new(|| { + let block: Arc<_> = + Block::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES[..]) + .unwrap() + .into(); + let hash = Ok(block.as_ref().into()); + vec![(block, hash)] + }); -#[spandoc::spandoc] -async fn verify() -> Result<(), Report> { - zebra_test::init(); +static INVALID_TIME_BLOCK_TRANSCRIPT: Lazy, Result)>> = + Lazy::new(|| { + let mut block: Block = + Block::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES[..]) + .unwrap(); - let block = - Arc::::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES[..])?; - let hash: BlockHeaderHash = block.as_ref().into(); + // 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(chrono::Duration::hours(3)) + .ok_or_else(|| eyre!("overflow when calculating 3 hours in the future")) + .unwrap(); + block.header.time = three_hours_in_the_future; - let state_service = Box::new(zebra_state::in_memory::init()); - let mut block_verifier = super::init(state_service); + vec![(Arc::new(block), Err(TransError::Any))] + }); - /// 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 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_GENESIS_BYTES[..])?; - - let 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(chrono::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(); - - 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_GENESIS_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))?; +static INVALID_HEADER_SOLUTION_TRANSCRIPT: Lazy< + Vec<(Arc, Result)>, +> = Lazy::new(|| { + let mut block: Block = + Block::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES[..]).unwrap(); // Change nonce to something invalid block.header.nonce = [0; 32]; - let ready_verifier_service = block_verifier.ready_and().await.map_err(|e| eyre!(e))?; + vec![(Arc::new(block), Err(TransError::Any))] +}); - // Error: invalid equihash solution for BlockHeader - ready_verifier_service - .call(Arc::new(block.clone())) - .await - .expect_err("expected the equihash solution to be invalid"); +static INVALID_COINBASE_TRANSCRIPT: Lazy, Result)>> = + Lazy::new(|| { + let header = + BlockHeader::zcash_deserialize(&zebra_test::vectors::DUMMY_HEADER[..]).unwrap(); - Ok(()) -} + // Test 1: Empty transaction + let block1 = Block { + header, + transactions: Vec::new(), + }; + + // Test 2: Transaction at first position is not coinbase + let mut transactions = Vec::new(); + let tx = zebra_test::vectors::DUMMY_TX1 + .zcash_deserialize_into() + .unwrap(); + transactions.push(tx); + let block2 = Block { + header, + transactions, + }; + + // Test 3: Invalid coinbase position + let mut block3 = + Block::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES[..]) + .unwrap(); + assert_eq!(block3.transactions.len(), 1); + + // Extract the coinbase transaction from the block + let coinbase_transaction = block3.transactions.get(0).unwrap().clone(); + + // Add another coinbase transaction to block + block3.transactions.push(coinbase_transaction); + assert_eq!(block3.transactions.len(), 2); + + vec![ + (Arc::new(block1), Err(TransError::Any)), + (Arc::new(block2), Err(TransError::Any)), + (Arc::new(block3), Err(TransError::Any)), + ] + }); #[tokio::test] +async fn check_transcripts_test() -> Result<(), Report> { + check_transcripts().await +} + #[spandoc::spandoc] -async fn coinbase() -> Result<(), Report> { +async fn check_transcripts() -> Result<(), Report> { zebra_test::init(); + let state_service = zebra_state::in_memory::init(); + let block_verifier = super::init(state_service.clone()); - // 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_GENESIS_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"); - + for transcript_data in &[ + &VALID_BLOCK_TRANSCRIPT, + &INVALID_TIME_BLOCK_TRANSCRIPT, + &INVALID_HEADER_SOLUTION_TRANSCRIPT, + &INVALID_COINBASE_TRANSCRIPT, + ] { + let transcript = Transcript::from(transcript_data.iter().cloned()); + transcript.check(block_verifier.clone()).await.unwrap(); + } Ok(()) }