use proptest::{ num::usize::BinarySearch, strategy::{NewTree, ValueTree}, test_runner::TestRunner, }; use std::sync::Arc; use zebra_chain::{ block::{Block, Height}, fmt::SummaryDebug, parameters::NetworkUpgrade, LedgerState, }; use zebra_test::prelude::*; use crate::tests::Prepare; use super::*; const MAX_PARTIAL_CHAIN_BLOCKS: usize = 102; #[derive(Debug)] pub struct PreparedChainTree { chain: Arc>>, count: BinarySearch, network: Network, } impl ValueTree for PreparedChainTree { type Value = ( Arc>>, ::Value, Network, ); fn current(&self) -> Self::Value { (self.chain.clone(), self.count.current(), self.network) } fn simplify(&mut self) -> bool { self.count.simplify() } fn complicate(&mut self) -> bool { self.count.complicate() } } #[derive(Debug, Default)] pub struct PreparedChain { // the proptests are threaded (not async), so we want to use a threaded mutex here chain: std::sync::Mutex>>)>>, } impl Strategy for PreparedChain { type Tree = PreparedChainTree; type Value = ::Value; fn new_tree(&self, runner: &mut TestRunner) -> NewTree { let mut chain = self.chain.lock().unwrap(); if chain.is_none() { // TODO: use the latest network upgrade (#1974) let ledger_strategy = LedgerState::genesis_strategy(NetworkUpgrade::Nu5, None, false); let (network, blocks) = ledger_strategy .prop_flat_map(|ledger| { ( Just(ledger.network), Block::partial_chain_strategy(ledger, MAX_PARTIAL_CHAIN_BLOCKS), ) }) .prop_map(|(network, vec)| { ( network, vec.iter() .map(|blk| blk.clone().prepare()) .collect::>(), ) }) .new_tree(runner)? .current(); *chain = Some((network, Arc::new(SummaryDebug(blocks)))); } let chain = chain.clone().expect("should be generated"); let count = (1..chain.1.len()).new_tree(runner)?; Ok(PreparedChainTree { chain: chain.1, count, network: chain.0, }) } } /// Generate a chain that allows us to make tests for the legacy chain rules. /// /// Arguments: /// - `transaction_version_override`: See `LedgerState::height_strategy` for details. /// - `transaction_has_valid_network_upgrade`: See `LedgerState::height_strategy` for details. /// - `blocks_after_nu_activation`: The number of blocks the strategy will generate /// after the provided `network_upgrade`. /// - `network_upgrade` - The network upgrade that we are using to simulate from where the /// legacy chain checks should start to apply. /// /// Returns: /// A generated arbitrary strategy for the provided arguments. pub(crate) fn partial_nu5_chain_strategy( transaction_version_override: u32, transaction_has_valid_network_upgrade: bool, blocks_after_nu_activation: u32, // TODO: This argument can be removed and just use Nu5 after we have an activation height #1841 network_upgrade: NetworkUpgrade, ) -> impl Strategy< Value = ( Network, Height, zebra_chain::fmt::SummaryDebug>>, ), > { ( any::(), NetworkUpgrade::reduced_branch_id_strategy(), ) .prop_flat_map(move |(network, random_nu)| { // TODO: update this to Nu5 after we have a height #1841 let mut nu = network_upgrade; let nu_activation = nu.activation_height(network).unwrap(); let height = Height(nu_activation.0 + blocks_after_nu_activation); // The `network_upgrade_override` will not be enough as when it is `None`, // current network upgrade will be used (`NetworkUpgrade::Canopy`) which will be valid. if !transaction_has_valid_network_upgrade { nu = random_nu; } zebra_chain::block::LedgerState::height_strategy( height, Some(nu), Some(transaction_version_override), transaction_has_valid_network_upgrade, ) .prop_flat_map(move |init| { Block::partial_chain_strategy(init, blocks_after_nu_activation as usize) }) .prop_map(move |partial_chain| (network, nu_activation, partial_chain)) }) }