2020-08-15 15:20:11 -07:00
|
|
|
use proptest::{
|
|
|
|
arbitrary::{any, Arbitrary},
|
|
|
|
prelude::*,
|
|
|
|
};
|
|
|
|
|
2021-04-07 01:08:02 -07:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
use crate::{
|
2021-05-28 05:48:27 -07:00
|
|
|
block,
|
2021-05-27 08:41:20 -07:00
|
|
|
parameters::{Network, NetworkUpgrade, GENESIS_PREVIOUS_BLOCK_HASH},
|
2021-05-24 08:10:07 -07:00
|
|
|
serialization,
|
2021-04-07 01:08:02 -07:00
|
|
|
work::{difficulty::CompactDifficulty, equihash},
|
|
|
|
};
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
2021-04-22 20:19:33 -07:00
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
#[non_exhaustive]
|
|
|
|
/// The configuration data for proptest when generating arbitrary chains
|
|
|
|
pub struct LedgerState {
|
2021-05-28 05:48:27 -07:00
|
|
|
/// The height of the generated block, or the start height of the generated chain.
|
2021-04-25 15:32:21 -07:00
|
|
|
///
|
|
|
|
/// To get the network upgrade, use the `network_upgrade` method.
|
2021-05-27 08:41:20 -07:00
|
|
|
///
|
|
|
|
/// If `network_upgrade_override` is not set, the network upgrade is derived
|
2021-05-28 05:48:27 -07:00
|
|
|
/// from the `height` and `network`.
|
|
|
|
pub height: Height,
|
2021-04-25 15:32:21 -07:00
|
|
|
|
|
|
|
/// The network to generate fake blocks for.
|
2021-04-22 20:19:33 -07:00
|
|
|
pub network: Network,
|
|
|
|
|
2021-05-28 05:48:27 -07:00
|
|
|
/// Overrides the network upgrade calculated from `height` and `network`.
|
2021-04-25 15:32:21 -07:00
|
|
|
///
|
|
|
|
/// To get the network upgrade, use the `network_upgrade` method.
|
2021-05-28 05:48:27 -07:00
|
|
|
network_upgrade_override: Option<NetworkUpgrade>,
|
2021-04-25 15:32:21 -07:00
|
|
|
|
|
|
|
/// 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,
|
2021-05-27 08:41:20 -07:00
|
|
|
|
2021-05-28 05:48:27 -07:00
|
|
|
/// Overrides the previous block hashes in blocks generated by this ledger.
|
|
|
|
previous_block_hash_override: Option<block::Hash>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Overrides for arbitrary [`LedgerState`]s.
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
pub struct LedgerStateOverride {
|
|
|
|
/// Regardless of tip height and network, every block has features from this
|
|
|
|
/// network upgrade.
|
|
|
|
pub network_upgrade_override: Option<NetworkUpgrade>,
|
|
|
|
|
|
|
|
/// Every block has exactly one coinbase transaction.
|
|
|
|
/// Transactions are always coinbase transactions.
|
|
|
|
pub always_has_coinbase: bool,
|
|
|
|
|
|
|
|
/// Every chain starts at this block. Single blocks have this height.
|
|
|
|
pub height_override: Option<Height>,
|
|
|
|
|
|
|
|
/// Every chain starts with a block with this previous block hash.
|
|
|
|
/// Single blocks have this previous block hash.
|
|
|
|
pub previous_block_hash_override: Option<block::Hash>,
|
2021-04-22 20:19:33 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl LedgerState {
|
2021-05-28 05:48:27 -07:00
|
|
|
/// Returns the default strategy for creating arbitrary `LedgerState`s.
|
|
|
|
pub fn default_strategy() -> BoxedStrategy<Self> {
|
|
|
|
Self::arbitrary_with(LedgerStateOverride::default())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a strategy for creating arbitrary `LedgerState`s, without any
|
|
|
|
/// overrides.
|
|
|
|
pub fn no_override_strategy() -> BoxedStrategy<Self> {
|
|
|
|
Self::arbitrary_with(LedgerStateOverride {
|
|
|
|
network_upgrade_override: None,
|
|
|
|
always_has_coinbase: false,
|
|
|
|
height_override: None,
|
|
|
|
previous_block_hash_override: None,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a strategy for creating `LedgerState`s with features from
|
|
|
|
/// `network_upgrade_override`.
|
|
|
|
///
|
|
|
|
/// These featues ignore the actual tip height and network).
|
|
|
|
pub fn network_upgrade_strategy(
|
|
|
|
network_upgrade_override: NetworkUpgrade,
|
|
|
|
) -> BoxedStrategy<Self> {
|
|
|
|
Self::arbitrary_with(LedgerStateOverride {
|
|
|
|
network_upgrade_override: Some(network_upgrade_override),
|
|
|
|
always_has_coinbase: false,
|
|
|
|
height_override: None,
|
|
|
|
previous_block_hash_override: None,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a strategy for creating `LedgerState`s that always have coinbase
|
|
|
|
/// transactions.
|
|
|
|
///
|
|
|
|
/// Also applies `network_upgrade_override`, if present.
|
|
|
|
pub fn coinbase_strategy(
|
|
|
|
network_upgrade_override: impl Into<Option<NetworkUpgrade>>,
|
|
|
|
) -> BoxedStrategy<Self> {
|
|
|
|
Self::arbitrary_with(LedgerStateOverride {
|
|
|
|
network_upgrade_override: network_upgrade_override.into(),
|
|
|
|
always_has_coinbase: true,
|
|
|
|
height_override: None,
|
|
|
|
previous_block_hash_override: None,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a strategy for creating `LedgerState`s that start with a genesis
|
|
|
|
/// block.
|
|
|
|
///
|
|
|
|
/// These strategies also have coinbase transactions, and an optional network
|
|
|
|
/// upgrade override.
|
|
|
|
///
|
|
|
|
/// Use the `Genesis` network upgrade to get a random genesis block, with
|
|
|
|
/// Zcash genesis features.
|
|
|
|
pub fn genesis_strategy(
|
|
|
|
network_upgrade_override: impl Into<Option<NetworkUpgrade>>,
|
|
|
|
) -> BoxedStrategy<Self> {
|
|
|
|
Self::arbitrary_with(LedgerStateOverride {
|
|
|
|
network_upgrade_override: network_upgrade_override.into(),
|
|
|
|
always_has_coinbase: true,
|
|
|
|
height_override: Some(Height(0)),
|
|
|
|
previous_block_hash_override: Some(GENESIS_PREVIOUS_BLOCK_HASH),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-04-25 15:32:21 -07:00
|
|
|
/// Returns the network upgrade for this ledger state.
|
|
|
|
///
|
|
|
|
/// If `network_upgrade_override` is set, it replaces the upgrade calculated
|
2021-05-28 05:48:27 -07:00
|
|
|
/// using `height` and `network`.
|
2021-04-25 15:32:21 -07:00
|
|
|
pub fn network_upgrade(&self) -> NetworkUpgrade {
|
|
|
|
if let Some(network_upgrade_override) = self.network_upgrade_override {
|
|
|
|
network_upgrade_override
|
|
|
|
} else {
|
2021-05-28 05:48:27 -07:00
|
|
|
NetworkUpgrade::current(self.network, self.height)
|
2021-04-22 20:19:33 -07:00
|
|
|
}
|
|
|
|
}
|
2021-05-28 05:48:27 -07:00
|
|
|
}
|
2021-04-25 15:32:21 -07:00
|
|
|
|
2021-05-28 05:48:27 -07:00
|
|
|
impl Default for LedgerState {
|
|
|
|
fn default() -> Self {
|
|
|
|
// TODO: stop having a default network
|
|
|
|
let default_network = Network::default();
|
|
|
|
let default_override = LedgerStateOverride::default();
|
2021-05-27 08:41:20 -07:00
|
|
|
|
2021-05-28 05:48:27 -07:00
|
|
|
let most_recent_nu = NetworkUpgrade::current(default_network, Height::MAX);
|
|
|
|
let most_recent_activation_height =
|
|
|
|
most_recent_nu.activation_height(default_network).unwrap();
|
|
|
|
|
|
|
|
Self {
|
|
|
|
height: most_recent_activation_height,
|
|
|
|
network: default_network,
|
|
|
|
network_upgrade_override: default_override.network_upgrade_override,
|
|
|
|
has_coinbase: default_override.always_has_coinbase,
|
|
|
|
previous_block_hash_override: default_override.previous_block_hash_override,
|
|
|
|
}
|
2021-04-25 15:32:21 -07:00
|
|
|
}
|
2021-04-22 20:19:33 -07:00
|
|
|
}
|
|
|
|
|
2021-05-28 05:48:27 -07:00
|
|
|
impl Default for LedgerStateOverride {
|
2021-04-22 20:19:33 -07:00
|
|
|
fn default() -> Self {
|
2021-05-28 05:48:27 -07:00
|
|
|
let default_network = Network::default();
|
2021-04-25 15:32:21 -07:00
|
|
|
|
|
|
|
// TODO: dynamically select any future network upgrade (#1974)
|
2021-05-28 05:48:27 -07:00
|
|
|
let nu5_activation_height = NetworkUpgrade::Nu5.activation_height(default_network);
|
2021-04-25 15:32:21 -07:00
|
|
|
let nu5_override = if nu5_activation_height.is_some() {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(NetworkUpgrade::Nu5)
|
|
|
|
};
|
2021-04-22 20:19:33 -07:00
|
|
|
|
2021-05-28 05:48:27 -07:00
|
|
|
LedgerStateOverride {
|
2021-04-25 15:32:21 -07:00
|
|
|
network_upgrade_override: nu5_override,
|
2021-05-28 05:48:27 -07:00
|
|
|
always_has_coinbase: true,
|
|
|
|
height_override: None,
|
|
|
|
previous_block_hash_override: None,
|
2021-04-22 20:19:33 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-25 15:32:21 -07:00
|
|
|
impl Arbitrary for LedgerState {
|
2021-05-28 05:48:27 -07:00
|
|
|
type Parameters = LedgerStateOverride;
|
2021-04-25 15:32:21 -07:00
|
|
|
|
|
|
|
/// Generate an arbitrary `LedgerState`.
|
|
|
|
///
|
2021-05-28 05:48:27 -07:00
|
|
|
/// The default strategy arbitrarily skips some coinbase transactions, and
|
|
|
|
/// has an arbitrary start height. To override, use:
|
|
|
|
/// - [`LedgerState::coinbase_strategy`], or
|
|
|
|
/// - [`LedgerState::genesis_strategy`].
|
|
|
|
fn arbitrary_with(ledger_override: Self::Parameters) -> Self::Strategy {
|
2021-04-25 15:32:21 -07:00
|
|
|
(
|
|
|
|
any::<Height>(),
|
|
|
|
any::<Network>(),
|
|
|
|
any::<bool>(),
|
|
|
|
any::<bool>(),
|
|
|
|
)
|
2021-05-28 05:48:27 -07:00
|
|
|
.prop_map(move |(height, network, nu5_override, has_coinbase)| {
|
2021-04-25 15:32:21 -07:00
|
|
|
// TODO: dynamically select any future network upgrade (#1974)
|
2021-05-28 05:48:27 -07:00
|
|
|
let nu5_override = if nu5_override {
|
2021-04-25 15:32:21 -07:00
|
|
|
Some(NetworkUpgrade::Nu5)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
|
|
|
LedgerState {
|
2021-05-28 05:48:27 -07:00
|
|
|
height: ledger_override.height_override.unwrap_or(height),
|
2021-04-25 15:32:21 -07:00
|
|
|
network,
|
2021-05-28 05:48:27 -07:00
|
|
|
network_upgrade_override: ledger_override
|
|
|
|
.network_upgrade_override
|
|
|
|
.or(nu5_override),
|
|
|
|
has_coinbase: ledger_override.always_has_coinbase || has_coinbase,
|
|
|
|
previous_block_hash_override: ledger_override.previous_block_hash_override,
|
2021-04-25 15:32:21 -07:00
|
|
|
}
|
|
|
|
})
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
|
|
|
type Strategy = BoxedStrategy<Self>;
|
|
|
|
}
|
|
|
|
|
2020-10-02 15:51:51 -07:00
|
|
|
impl Arbitrary for Block {
|
|
|
|
type Parameters = LedgerState;
|
|
|
|
|
|
|
|
fn arbitrary_with(ledger_state: Self::Parameters) -> Self::Strategy {
|
|
|
|
let transactions_strategy = Transaction::vec_strategy(ledger_state, 2);
|
|
|
|
|
2021-05-28 05:48:27 -07:00
|
|
|
(Header::arbitrary_with(ledger_state), transactions_strategy)
|
|
|
|
.prop_map(move |(header, transactions)| Self {
|
|
|
|
header,
|
|
|
|
transactions,
|
2020-10-02 15:51:51 -07:00
|
|
|
})
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
|
|
|
type Strategy = BoxedStrategy<Self>;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Block {
|
2020-10-07 20:07:32 -07:00
|
|
|
/// Returns a strategy for creating Vecs of blocks with increasing height of
|
|
|
|
/// the given length.
|
2020-10-02 15:51:51 -07:00
|
|
|
pub fn partial_chain_strategy(
|
2021-05-28 05:48:27 -07:00
|
|
|
mut current: LedgerState,
|
2020-10-02 15:51:51 -07:00
|
|
|
count: usize,
|
|
|
|
) -> BoxedStrategy<Vec<Arc<Self>>> {
|
|
|
|
let mut vec = Vec::with_capacity(count);
|
2021-05-28 05:48:27 -07:00
|
|
|
|
|
|
|
// generate block strategies with the correct heights
|
2020-10-02 15:51:51 -07:00
|
|
|
for _ in 0..count {
|
2021-05-28 05:48:27 -07:00
|
|
|
vec.push(Block::arbitrary_with(current));
|
|
|
|
current.height.0 += 1;
|
2020-10-02 15:51:51 -07:00
|
|
|
}
|
|
|
|
|
2021-05-28 05:48:27 -07:00
|
|
|
// after the vec strategy generates blocks, update the previous block hashes
|
|
|
|
vec.prop_map(|mut vec| {
|
|
|
|
let mut previous_block_hash = None;
|
|
|
|
for block in vec.iter_mut() {
|
|
|
|
if let Some(previous_block_hash) = previous_block_hash {
|
|
|
|
block.header.previous_block_hash = previous_block_hash;
|
|
|
|
}
|
|
|
|
previous_block_hash = Some(block.hash());
|
|
|
|
}
|
|
|
|
vec.into_iter().map(Arc::new).collect()
|
|
|
|
})
|
|
|
|
.boxed()
|
2020-10-02 15:51:51 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-30 16:51:42 -07:00
|
|
|
impl Arbitrary for Commitment {
|
2020-08-15 15:20:11 -07:00
|
|
|
type Parameters = ();
|
|
|
|
|
|
|
|
fn arbitrary_with(_args: ()) -> Self::Strategy {
|
2020-08-16 11:42:02 -07:00
|
|
|
(any::<[u8; 32]>(), any::<Network>(), any::<Height>())
|
2021-03-30 16:51:42 -07:00
|
|
|
.prop_map(|(commitment_bytes, network, block_height)| {
|
2021-04-06 03:19:28 -07:00
|
|
|
match Commitment::from_bytes(commitment_bytes, network, block_height) {
|
|
|
|
Ok(commitment) => commitment,
|
|
|
|
// just fix up the reserved values when they fail
|
|
|
|
Err(_) => Commitment::from_bytes(
|
|
|
|
super::commitment::RESERVED_BYTES,
|
|
|
|
network,
|
|
|
|
block_height,
|
|
|
|
)
|
|
|
|
.expect("from_bytes only fails due to reserved bytes"),
|
|
|
|
}
|
2020-08-15 15:20:11 -07:00
|
|
|
})
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
|
|
|
type Strategy = BoxedStrategy<Self>;
|
|
|
|
}
|
|
|
|
|
2020-08-16 11:48:00 -07:00
|
|
|
impl Arbitrary for Header {
|
2021-05-28 05:48:27 -07:00
|
|
|
type Parameters = LedgerState;
|
2020-08-15 15:20:11 -07:00
|
|
|
|
2021-05-28 05:48:27 -07:00
|
|
|
fn arbitrary_with(ledger_state: Self::Parameters) -> Self::Strategy {
|
2020-08-15 15:20:11 -07:00
|
|
|
(
|
|
|
|
// version is interpreted as i32 in the spec, so we are limited to i32::MAX here
|
|
|
|
(4u32..(i32::MAX as u32)),
|
2020-08-15 23:20:01 -07:00
|
|
|
any::<Hash>(),
|
2020-08-16 11:54:38 -07:00
|
|
|
any::<merkle::Root>(),
|
2020-08-15 15:20:11 -07:00
|
|
|
any::<[u8; 32]>(),
|
2021-05-24 08:10:07 -07:00
|
|
|
serialization::arbitrary::datetime_u32(),
|
2020-10-29 18:36:59 -07:00
|
|
|
any::<CompactDifficulty>(),
|
2020-08-15 15:20:11 -07:00
|
|
|
any::<[u8; 32]>(),
|
|
|
|
any::<equihash::Solution>(),
|
|
|
|
)
|
|
|
|
.prop_map(
|
2021-05-28 05:48:27 -07:00
|
|
|
move |(
|
2020-08-15 15:20:11 -07:00
|
|
|
version,
|
2021-05-28 05:48:27 -07:00
|
|
|
mut previous_block_hash,
|
2020-10-29 18:36:59 -07:00
|
|
|
merkle_root,
|
2021-03-30 16:51:42 -07:00
|
|
|
commitment_bytes,
|
2021-05-24 08:10:07 -07:00
|
|
|
time,
|
2020-10-29 18:36:59 -07:00
|
|
|
difficulty_threshold,
|
2020-08-15 15:20:11 -07:00
|
|
|
nonce,
|
|
|
|
solution,
|
2021-05-28 05:48:27 -07:00
|
|
|
)| {
|
|
|
|
if let Some(previous_block_hash_override) =
|
|
|
|
ledger_state.previous_block_hash_override
|
|
|
|
{
|
|
|
|
previous_block_hash = previous_block_hash_override;
|
|
|
|
} else if ledger_state.height == Height(0) {
|
|
|
|
previous_block_hash = GENESIS_PREVIOUS_BLOCK_HASH;
|
|
|
|
}
|
|
|
|
|
|
|
|
Header {
|
|
|
|
version,
|
|
|
|
previous_block_hash,
|
|
|
|
merkle_root,
|
|
|
|
commitment_bytes,
|
|
|
|
time,
|
|
|
|
difficulty_threshold,
|
|
|
|
nonce,
|
|
|
|
solution,
|
|
|
|
}
|
2020-08-15 15:20:11 -07:00
|
|
|
},
|
|
|
|
)
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
|
|
|
type Strategy = BoxedStrategy<Self>;
|
|
|
|
}
|