diff --git a/zebra-state/src/service/tests.rs b/zebra-state/src/service/tests.rs index d1b34d4bbeb..c082f16760f 100644 --- a/zebra-state/src/service/tests.rs +++ b/zebra-state/src/service/tests.rs @@ -5,13 +5,16 @@ use tower::{buffer::Buffer, util::BoxService, Service, ServiceExt}; use zebra_chain::{ block::Block, parameters::{Network, NetworkUpgrade}, - serialization::ZcashDeserializeInto, + serialization::{ZcashDeserialize, ZcashDeserializeInto}, transaction, transparent, }; use zebra_test::{prelude::*, transcript::Transcript}; use crate::{ - constants, init_test, tests::setup::partial_nu5_chain_strategy, BoxError, Request, Response, + arbitrary::Prepare, + constants, init_test, + tests::setup::{partial_nu5_chain_strategy, transaction_v4_from_coinbase}, + BoxError, FinalizedBlock, PreparedBlock, Request, Response, }; const LAST_BLOCK_HEIGHT: u32 = 10; @@ -275,3 +278,49 @@ proptest! { prop_assert_eq!(response, Ok(())); } } + +/// Test strategy to generate a chain split in two from the test vectors. +/// +/// Selects either the mainnet or testnet chain test vector and randomly splits the chain in two +/// lists of blocks. The first containing the blocks to be finalized (which always includes at +/// least the genesis blocks) and the blocks to be stored in the non-finalized state. +fn continuous_empty_blocks_from_test_vectors( +) -> impl Strategy, Vec)> { + any::() + .prop_flat_map(|network| { + // Select the test vector based on the network + let raw_blocks = match network { + Network::Mainnet => &*zebra_test::vectors::CONTINUOUS_MAINNET_BLOCKS, + Network::Testnet => &*zebra_test::vectors::CONTINUOUS_TESTNET_BLOCKS, + }; + + // Transform the test vector's block bytes into a vector of `PreparedBlock`s. + let blocks: Vec<_> = raw_blocks + .iter() + .map(|(_height, &block_bytes)| { + let mut block_reader: &[u8] = block_bytes; + let mut block = Block::zcash_deserialize(&mut block_reader) + .expect("Failed to deserialize block from test vector"); + + let coinbase = transaction_v4_from_coinbase(&block.transactions[0]); + block.transactions = vec![Arc::new(coinbase)]; + + Arc::new(block).prepare() + }) + .collect(); + + // Always finalize the genesis block + let finalized_blocks_count = 1..blocks.len(); + + (Just(network), Just(blocks), finalized_blocks_count) + }) + .prop_map(|(network, mut blocks, finalized_blocks_count)| { + let non_finalized_blocks = blocks.split_off(finalized_blocks_count); + let finalized_blocks: Vec<_> = blocks + .into_iter() + .map(|prepared_block| FinalizedBlock::from(prepared_block.block.clone())) + .collect(); + + (network, finalized_blocks, non_finalized_blocks) + }) +}