179 lines
6.0 KiB
Rust
179 lines
6.0 KiB
Rust
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<SummaryDebug<Vec<PreparedBlock>>>,
|
|
count: BinarySearch,
|
|
network: Network,
|
|
}
|
|
|
|
impl ValueTree for PreparedChainTree {
|
|
type Value = (
|
|
Arc<SummaryDebug<Vec<PreparedBlock>>>,
|
|
<BinarySearch as ValueTree>::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<Option<(Network, Arc<SummaryDebug<Vec<PreparedBlock>>>)>>,
|
|
// the height from which to start the chain. If None, starts at the genesis block
|
|
start_height: Option<Height>,
|
|
}
|
|
|
|
impl PreparedChain {
|
|
/// Create a PreparedChain strategy with Heartwood-onward blocks.
|
|
pub(super) fn new_heartwood() -> Self {
|
|
// The history tree only works with Heartwood onward.
|
|
// Since the network will be chosen later, we pick the larger
|
|
// between the mainnet and testnet Heartwood activation heights.
|
|
let main_height = NetworkUpgrade::Heartwood
|
|
.activation_height(Network::Mainnet)
|
|
.expect("must have height");
|
|
let test_height = NetworkUpgrade::Heartwood
|
|
.activation_height(Network::Testnet)
|
|
.expect("must have height");
|
|
let height = (std::cmp::max(main_height, test_height) + 1).expect("must be valid");
|
|
|
|
PreparedChain {
|
|
start_height: Some(height),
|
|
..Default::default()
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Strategy for PreparedChain {
|
|
type Tree = PreparedChainTree;
|
|
type Value = <PreparedChainTree as ValueTree>::Value;
|
|
|
|
fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
|
|
let mut chain = self.chain.lock().unwrap();
|
|
if chain.is_none() {
|
|
// TODO: use the latest network upgrade (#1974)
|
|
let ledger_strategy = match self.start_height {
|
|
Some(start_height) => {
|
|
LedgerState::height_strategy(start_height, NetworkUpgrade::Nu5, None, false)
|
|
}
|
|
None => 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::<Vec<_>>(),
|
|
)
|
|
})
|
|
.new_tree(runner)?
|
|
.current();
|
|
*chain = Some((network, Arc::new(SummaryDebug(blocks))));
|
|
}
|
|
|
|
let chain = chain.clone().expect("should be generated");
|
|
// `count` must be 1 less since the first block is used to build the
|
|
// history tree.
|
|
let count = (1..chain.1.len() - 1).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.
|
|
/// Note: `false` allows zero or more invalid network upgrades.
|
|
/// - `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<Vec<Arc<Block>>>,
|
|
),
|
|
> {
|
|
(
|
|
any::<Network>(),
|
|
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))
|
|
})
|
|
}
|