From a49b9d44f6aa5b7de9f2b62c3a8b5a586468dcc1 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Sun, 25 Apr 2021 19:32:21 -0300 Subject: [PATCH] Enable more Transaction v5 tests (#2063) * Use NU5 and Transaction v5 in most proptests * Stop skipping post-Canopy blocks in the block subsidy tests Co-authored-by: teor --- zebra-chain/src/block/arbitrary.rs | 94 ++++++++++++++++--- zebra-chain/src/block/tests/prop.rs | 10 +- zebra-chain/src/transaction/arbitrary.rs | 13 +-- zebra-chain/src/transparent/arbitrary.rs | 4 +- zebra-chain/src/transparent/prop.rs | 9 +- zebra-consensus/src/block/tests.rs | 6 +- .../service/non_finalized_state/arbitrary.rs | 20 +++- 7 files changed, 106 insertions(+), 50 deletions(-) diff --git a/zebra-chain/src/block/arbitrary.rs b/zebra-chain/src/block/arbitrary.rs index ba3f3de8..7a34755a 100644 --- a/zebra-chain/src/block/arbitrary.rs +++ b/zebra-chain/src/block/arbitrary.rs @@ -17,39 +17,107 @@ use super::*; #[non_exhaustive] /// The configuration data for proptest when generating arbitrary chains pub struct LedgerState { - /// The tip height of the block or start of the chain + /// The tip height of the block or start of the chain. + /// + /// To get the network upgrade, use the `network_upgrade` method. pub tip_height: Height, - /// The network to generate fake blocks for + + /// The network to generate fake blocks for. pub network: Network, - /// Make this fake transaction a coinbase transaction - pub(crate) is_coinbase: bool, + /// Overrides the network upgrade calculated from `tip_height` and `network`. + /// + /// To get the network upgrade, use the `network_upgrade` method. + pub network_upgrade_override: Option, + + /// Generate coinbase transactions. + /// + /// In a block or transaction vector, make the first transaction a coinbase + /// transaction. + /// + /// For an individual transaction, make the transaction a coinbase + /// transaction. + pub(crate) has_coinbase: bool, } impl LedgerState { - /// Construct a new ledger state for generating arbitrary chains via proptest - pub fn new(tip_height: Height, network: Network) -> Self { - Self { - tip_height, - is_coinbase: true, - network, + /// Returns the network upgrade for this ledger state. + /// + /// If `network_upgrade_override` is set, it replaces the upgrade calculated + /// using `tip_height` and `network`. + pub fn network_upgrade(&self) -> NetworkUpgrade { + if let Some(network_upgrade_override) = self.network_upgrade_override { + network_upgrade_override + } else { + NetworkUpgrade::current(self.network, self.tip_height) } } + + /// Returns a strategy for creating `LedgerState`s that always have coinbase + /// transactions. + pub fn coinbase_strategy() -> BoxedStrategy { + Self::arbitrary_with(true) + } } impl Default for LedgerState { fn default() -> Self { let network = Network::Mainnet; - let tip_height = NetworkUpgrade::Canopy.activation_height(network).unwrap(); + let most_recent_nu = NetworkUpgrade::current(network, Height::MAX); + let most_recent_activation_height = most_recent_nu.activation_height(network).unwrap(); + + // TODO: dynamically select any future network upgrade (#1974) + let nu5_activation_height = NetworkUpgrade::Nu5.activation_height(network); + let nu5_override = if nu5_activation_height.is_some() { + None + } else { + Some(NetworkUpgrade::Nu5) + }; Self { - tip_height, - is_coinbase: true, + tip_height: most_recent_activation_height, network, + network_upgrade_override: nu5_override, + has_coinbase: true, } } } +impl Arbitrary for LedgerState { + type Parameters = bool; + + /// Generate an arbitrary `LedgerState`. + /// + /// The default strategy arbitrarily skips some coinbase transactions. To + /// override, use `LedgerState::coinbase_strategy`. + fn arbitrary_with(require_coinbase: Self::Parameters) -> Self::Strategy { + ( + any::(), + any::(), + any::(), + any::(), + ) + .prop_map(move |(tip_height, network, nu5_override, has_coinbase)| { + // TODO: dynamically select any future network upgrade (#1974) + let network_upgrade_override = if nu5_override { + Some(NetworkUpgrade::Nu5) + } else { + None + }; + + LedgerState { + tip_height, + network, + network_upgrade_override, + has_coinbase: require_coinbase || has_coinbase, + } + }) + .boxed() + } + + type Strategy = BoxedStrategy; +} + impl Arbitrary for Block { type Parameters = LedgerState; diff --git a/zebra-chain/src/block/tests/prop.rs b/zebra-chain/src/block/tests/prop.rs index 83c22928..b4678bd9 100644 --- a/zebra-chain/src/block/tests/prop.rs +++ b/zebra-chain/src/block/tests/prop.rs @@ -5,7 +5,7 @@ use proptest::{arbitrary::any, prelude::*, test_runner::Config}; use zebra_test::prelude::*; use crate::serialization::{SerializationError, ZcashDeserializeInto, ZcashSerialize}; -use crate::{block, parameters::Network, LedgerState}; +use crate::{parameters::Network, LedgerState}; use super::super::{serialize::MAX_BLOCK_BYTES, *}; @@ -121,13 +121,7 @@ proptest! { fn blocks_have_coinbase() -> Result<()> { zebra_test::init(); - let strategy = any::() - .prop_map(|tip_height| LedgerState { - tip_height, - is_coinbase: true, - network: Network::Mainnet, - }) - .prop_flat_map(Block::arbitrary_with); + let strategy = LedgerState::coinbase_strategy().prop_flat_map(Block::arbitrary_with); proptest!(|(block in strategy)| { let has_coinbase = block.coinbase_height().is_some(); diff --git a/zebra-chain/src/transaction/arbitrary.rs b/zebra-chain/src/transaction/arbitrary.rs index ab582382..623de13d 100644 --- a/zebra-chain/src/transaction/arbitrary.rs +++ b/zebra-chain/src/transaction/arbitrary.rs @@ -130,7 +130,7 @@ impl Transaction { len: usize, ) -> BoxedStrategy>> { let coinbase = Transaction::arbitrary_with(ledger_state).prop_map(Arc::new); - ledger_state.is_coinbase = false; + ledger_state.has_coinbase = false; let remainder = vec( Transaction::arbitrary_with(ledger_state).prop_map(Arc::new), len, @@ -302,16 +302,7 @@ impl Arbitrary for Transaction { type Parameters = LedgerState; fn arbitrary_with(ledger_state: Self::Parameters) -> Self::Strategy { - let LedgerState { - tip_height, - network, - .. - } = ledger_state; - - let height = block::Height(tip_height.0 + 1); - let network_upgrade = NetworkUpgrade::current(network, height); - - match network_upgrade { + match ledger_state.network_upgrade() { NetworkUpgrade::Genesis | NetworkUpgrade::BeforeOverwinter => { Self::v1_strategy(ledger_state) } diff --git a/zebra-chain/src/transparent/arbitrary.rs b/zebra-chain/src/transparent/arbitrary.rs index 4bcb84cb..a77a1f06 100644 --- a/zebra-chain/src/transparent/arbitrary.rs +++ b/zebra-chain/src/transparent/arbitrary.rs @@ -5,9 +5,9 @@ use crate::{block, LedgerState}; use super::{CoinbaseData, Input, OutPoint, Script}; impl Input { - /// Construct a strategy for creating validish vecs of Inputs. + /// Construct a strategy for creating valid-ish vecs of Inputs. pub fn vec_strategy(ledger_state: LedgerState, max_size: usize) -> BoxedStrategy> { - if ledger_state.is_coinbase { + if ledger_state.has_coinbase { let height = block::Height(ledger_state.tip_height.0 + 1); Self::arbitrary_with(Some(height)) .prop_map(|input| vec![input]) diff --git a/zebra-chain/src/transparent/prop.rs b/zebra-chain/src/transparent/prop.rs index 6af6fdd2..b2ec896b 100644 --- a/zebra-chain/src/transparent/prop.rs +++ b/zebra-chain/src/transparent/prop.rs @@ -1,6 +1,6 @@ use zebra_test::prelude::*; -use crate::{block, parameters::Network, LedgerState}; +use crate::{block, LedgerState}; use super::Input; @@ -24,12 +24,7 @@ fn input_coinbase_vecs_only_have_coinbase_input() -> Result<()> { zebra_test::init(); let max_size = 100; - let strategy = any::() - .prop_map(|tip_height| LedgerState { - tip_height, - is_coinbase: true, - network: Network::Mainnet, - }) + let strategy = LedgerState::coinbase_strategy() .prop_flat_map(|ledger_state| Input::vec_strategy(ledger_state, max_size)); proptest!(|(inputs in strategy)| { diff --git a/zebra-consensus/src/block/tests.rs b/zebra-consensus/src/block/tests.rs index e9675ce7..2c1ceb56 100644 --- a/zebra-consensus/src/block/tests.rs +++ b/zebra-consensus/src/block/tests.rs @@ -13,7 +13,7 @@ use tower::buffer::Buffer; use zebra_chain::{ block::{self, Block, Height}, - parameters::{Network, NetworkUpgrade}, + parameters::Network, serialization::{ZcashDeserialize, ZcashDeserializeInto}, work::difficulty::{ExpandedDifficulty, INVALID_COMPACT_DIFFICULTY}, }; @@ -283,9 +283,7 @@ fn subsidy_is_valid_for_network(network: Network) -> Result<(), Report> { .expect("block is structurally valid"); // TODO: first halving, second halving, third halving, and very large halvings - if block::Height(height) > SLOW_START_INTERVAL - && block::Height(height) < NetworkUpgrade::Canopy.activation_height(network).unwrap() - { + if block::Height(height) > SLOW_START_INTERVAL { check::subsidy_is_valid(&block, network).expect("subsidies should pass for this block"); } } diff --git a/zebra-state/src/service/non_finalized_state/arbitrary.rs b/zebra-state/src/service/non_finalized_state/arbitrary.rs index 61ae4f66..ad44dc2c 100644 --- a/zebra-state/src/service/non_finalized_state/arbitrary.rs +++ b/zebra-state/src/service/non_finalized_state/arbitrary.rs @@ -49,11 +49,21 @@ impl Strategy for PreparedChain { fn new_tree(&self, runner: &mut TestRunner) -> NewTree { let mut chain = self.chain.lock().unwrap(); if chain.is_none() { - let blocks = - Block::partial_chain_strategy(LedgerState::default(), MAX_PARTIAL_CHAIN_BLOCKS) - .prop_map(|vec| vec.into_iter().map(|blk| blk.prepare()).collect::>()) - .new_tree(runner)? - .current(); + let ledger_strategy = LedgerState::coinbase_strategy(); + + // Disable the NU5 override until UpdateWith is implemented for Tx v5 (#1982) + let ledger_strategy = ledger_strategy.prop_map(|mut ledger_state| { + ledger_state.network_upgrade_override = None; + ledger_state + }); + + let blocks = ledger_strategy + .prop_flat_map(|ledger_state| { + Block::partial_chain_strategy(ledger_state, MAX_PARTIAL_CHAIN_BLOCKS) + }) + .prop_map(|vec| vec.into_iter().map(|blk| blk.prepare()).collect::>()) + .new_tree(runner)? + .current(); *chain = Some(Arc::new(blocks)); }