2021-07-28 21:23:50 -07:00
|
|
|
|
//! Randomised property testing for [`Block`]s.
|
|
|
|
|
|
2023-01-17 05:57:22 -08:00
|
|
|
|
use std::{collections::HashMap, sync::Arc};
|
|
|
|
|
|
2020-08-15 15:20:11 -07:00
|
|
|
|
use proptest::{
|
|
|
|
|
arbitrary::{any, Arbitrary},
|
|
|
|
|
prelude::*,
|
|
|
|
|
};
|
|
|
|
|
|
2021-04-07 01:08:02 -07:00
|
|
|
|
use crate::{
|
2021-08-12 05:38:16 -07:00
|
|
|
|
amount::NonNegative,
|
2021-05-28 05:48:27 -07:00
|
|
|
|
block,
|
2023-01-17 05:57:22 -08:00
|
|
|
|
fmt::{HexDebug, SummaryDebug},
|
2021-08-23 07:17:33 -07:00
|
|
|
|
history_tree::HistoryTree,
|
2021-06-04 06:31:47 -07:00
|
|
|
|
parameters::{
|
|
|
|
|
Network,
|
|
|
|
|
NetworkUpgrade::{self, *},
|
|
|
|
|
GENESIS_PREVIOUS_BLOCK_HASH,
|
|
|
|
|
},
|
2021-05-24 08:10:07 -07:00
|
|
|
|
serialization,
|
2021-08-04 13:48:15 -07:00
|
|
|
|
transaction::arbitrary::MAX_ARBITRARY_ITEMS,
|
2021-08-12 05:38:16 -07:00
|
|
|
|
transparent::{
|
|
|
|
|
new_transaction_ordered_outputs, CoinbaseSpendRestriction,
|
|
|
|
|
MIN_TRANSPARENT_COINBASE_MATURITY,
|
|
|
|
|
},
|
2021-04-07 01:08:02 -07:00
|
|
|
|
work::{difficulty::CompactDifficulty, equihash},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
2021-08-12 05:38:16 -07:00
|
|
|
|
/// The chain length for most zebra-chain proptests.
|
2021-07-28 21:23:50 -07:00
|
|
|
|
///
|
2021-07-30 10:49:25 -07:00
|
|
|
|
/// Most generated chains will contain transparent spends at or before this height.
|
2021-07-28 21:23:50 -07:00
|
|
|
|
///
|
2021-07-30 10:49:25 -07:00
|
|
|
|
/// This height was chosen a tradeoff between chains with no spends,
|
|
|
|
|
/// and chains which spend outputs created by previous spends.
|
|
|
|
|
///
|
|
|
|
|
/// The raw probability of having no spends during a test run is:
|
2021-07-28 21:23:50 -07:00
|
|
|
|
/// ```text
|
|
|
|
|
/// shielded_input = shielded_pool_count / pool_count
|
|
|
|
|
/// expected_transactions = expected_inputs = MAX_ARBITRARY_ITEMS/2
|
2021-07-30 10:49:25 -07:00
|
|
|
|
/// shielded_input^(expected_transactions * expected_inputs * (PREVOUTS_CHAIN_HEIGHT - 1))
|
2021-07-28 21:23:50 -07:00
|
|
|
|
/// ```
|
|
|
|
|
///
|
2021-07-30 10:49:25 -07:00
|
|
|
|
/// This probability is approximately 3%. However, proptest generation and
|
|
|
|
|
/// minimisation strategies can create additional chains with no transparent spends.
|
|
|
|
|
///
|
|
|
|
|
/// To increase the proportion of test runs with proptest spends, increase `PREVOUTS_CHAIN_HEIGHT`.
|
|
|
|
|
pub const PREVOUTS_CHAIN_HEIGHT: usize = 4;
|
2021-07-28 21:23:50 -07:00
|
|
|
|
|
2021-08-12 05:38:16 -07:00
|
|
|
|
/// The chain length for most zebra-state proptests.
|
|
|
|
|
///
|
|
|
|
|
/// Most generated chains will contain transparent spends at or before this height.
|
|
|
|
|
///
|
|
|
|
|
/// This height was chosen as a tradeoff between chains with no transparent spends,
|
|
|
|
|
/// and chains which spend outputs created by previous spends.
|
|
|
|
|
///
|
|
|
|
|
/// See [`block::arbitrary::PREVOUTS_CHAIN_HEIGHT`] for details.
|
|
|
|
|
pub const MAX_PARTIAL_CHAIN_BLOCKS: usize =
|
|
|
|
|
MIN_TRANSPARENT_COINBASE_MATURITY as usize + PREVOUTS_CHAIN_HEIGHT;
|
|
|
|
|
|
2022-01-27 06:34:15 -08:00
|
|
|
|
impl Arbitrary for Height {
|
|
|
|
|
type Parameters = ();
|
|
|
|
|
|
|
|
|
|
fn arbitrary_with(_args: ()) -> Self::Strategy {
|
|
|
|
|
(Height::MIN.0..=Height::MAX.0).prop_map(Height).boxed()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Strategy = BoxedStrategy<Self>;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
2021-06-18 10:40:08 -07:00
|
|
|
|
/// Overrides the previous block hashes in blocks generated by this ledger.
|
|
|
|
|
previous_block_hash_override: Option<block::Hash>,
|
|
|
|
|
|
|
|
|
|
/// Regardless of tip height and network, every transaction is this version.
|
|
|
|
|
transaction_version_override: Option<u32>,
|
|
|
|
|
|
|
|
|
|
/// Every V5 and later transaction has a valid `network_upgrade` field.
|
|
|
|
|
///
|
2021-07-01 16:21:22 -07:00
|
|
|
|
/// If `false`, zero or more transactions may have invalid network upgrades.
|
2021-06-18 10:40:08 -07:00
|
|
|
|
transaction_has_valid_network_upgrade: bool,
|
|
|
|
|
|
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-28 05:48:27 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Overrides for arbitrary [`LedgerState`]s.
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
|
pub struct LedgerStateOverride {
|
2021-06-18 10:40:08 -07:00
|
|
|
|
/// 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-05-28 05:48:27 -07:00
|
|
|
|
/// Regardless of tip height and network, every block has features from this
|
|
|
|
|
/// network upgrade.
|
|
|
|
|
pub network_upgrade_override: Option<NetworkUpgrade>,
|
|
|
|
|
|
2021-06-18 10:40:08 -07:00
|
|
|
|
/// Regardless of tip height and network, every transaction is this version.
|
|
|
|
|
pub transaction_version_override: Option<u32>,
|
|
|
|
|
|
|
|
|
|
/// Every V5 and later transaction has a valid `network_upgrade` field.
|
|
|
|
|
///
|
2021-07-01 16:21:22 -07:00
|
|
|
|
/// If `false`, zero or more transactions may have invalid network upgrades.
|
2021-06-18 10:40:08 -07:00
|
|
|
|
pub transaction_has_valid_network_upgrade: bool,
|
|
|
|
|
|
2021-05-28 05:48:27 -07:00
|
|
|
|
/// Every block has exactly one coinbase transaction.
|
|
|
|
|
/// Transactions are always coinbase transactions.
|
|
|
|
|
pub always_has_coinbase: bool,
|
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 {
|
|
|
|
|
height_override: None,
|
|
|
|
|
previous_block_hash_override: None,
|
2021-06-18 10:40:08 -07:00
|
|
|
|
network_upgrade_override: None,
|
|
|
|
|
transaction_version_override: None,
|
|
|
|
|
transaction_has_valid_network_upgrade: false,
|
|
|
|
|
always_has_coinbase: false,
|
2021-05-28 05:48:27 -07:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns a strategy for creating `LedgerState`s with features from
|
|
|
|
|
/// `network_upgrade_override`.
|
|
|
|
|
///
|
2021-11-12 11:30:22 -08:00
|
|
|
|
/// These features ignore the actual tip height and network.
|
2021-05-28 05:48:27 -07:00
|
|
|
|
pub fn network_upgrade_strategy(
|
|
|
|
|
network_upgrade_override: NetworkUpgrade,
|
2021-06-18 10:40:08 -07:00
|
|
|
|
transaction_version_override: impl Into<Option<u32>>,
|
|
|
|
|
transaction_has_valid_network_upgrade: bool,
|
2021-05-28 05:48:27 -07:00
|
|
|
|
) -> BoxedStrategy<Self> {
|
|
|
|
|
Self::arbitrary_with(LedgerStateOverride {
|
|
|
|
|
height_override: None,
|
|
|
|
|
previous_block_hash_override: None,
|
2021-06-18 10:40:08 -07:00
|
|
|
|
network_upgrade_override: Some(network_upgrade_override),
|
|
|
|
|
transaction_version_override: transaction_version_override.into(),
|
|
|
|
|
transaction_has_valid_network_upgrade,
|
|
|
|
|
always_has_coinbase: false,
|
2021-05-28 05:48:27 -07:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 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>>,
|
2021-06-18 10:40:08 -07:00
|
|
|
|
transaction_version_override: impl Into<Option<u32>>,
|
|
|
|
|
transaction_has_valid_network_upgrade: bool,
|
2021-05-28 05:48:27 -07:00
|
|
|
|
) -> BoxedStrategy<Self> {
|
|
|
|
|
Self::arbitrary_with(LedgerStateOverride {
|
|
|
|
|
height_override: None,
|
|
|
|
|
previous_block_hash_override: None,
|
2021-06-18 10:40:08 -07:00
|
|
|
|
network_upgrade_override: network_upgrade_override.into(),
|
|
|
|
|
transaction_version_override: transaction_version_override.into(),
|
|
|
|
|
transaction_has_valid_network_upgrade,
|
|
|
|
|
always_has_coinbase: true,
|
2021-05-28 05:48:27 -07:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 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>>,
|
2021-06-18 10:40:08 -07:00
|
|
|
|
transaction_version_override: impl Into<Option<u32>>,
|
|
|
|
|
transaction_has_valid_network_upgrade: bool,
|
2021-05-28 05:48:27 -07:00
|
|
|
|
) -> BoxedStrategy<Self> {
|
|
|
|
|
Self::arbitrary_with(LedgerStateOverride {
|
|
|
|
|
height_override: Some(Height(0)),
|
|
|
|
|
previous_block_hash_override: Some(GENESIS_PREVIOUS_BLOCK_HASH),
|
2021-06-18 10:40:08 -07:00
|
|
|
|
network_upgrade_override: network_upgrade_override.into(),
|
|
|
|
|
transaction_version_override: transaction_version_override.into(),
|
|
|
|
|
transaction_has_valid_network_upgrade,
|
|
|
|
|
always_has_coinbase: true,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns a strategy for creating `LedgerState`s that start at `height`.
|
|
|
|
|
///
|
|
|
|
|
/// These strategies also have coinbase transactions, and an optional network
|
|
|
|
|
/// upgrade override.
|
|
|
|
|
pub fn height_strategy(
|
|
|
|
|
height: Height,
|
|
|
|
|
network_upgrade_override: impl Into<Option<NetworkUpgrade>>,
|
|
|
|
|
transaction_version_override: impl Into<Option<u32>>,
|
|
|
|
|
transaction_has_valid_network_upgrade: bool,
|
|
|
|
|
) -> BoxedStrategy<Self> {
|
|
|
|
|
Self::arbitrary_with(LedgerStateOverride {
|
|
|
|
|
height_override: Some(height),
|
|
|
|
|
previous_block_hash_override: None,
|
|
|
|
|
network_upgrade_override: network_upgrade_override.into(),
|
|
|
|
|
transaction_version_override: transaction_version_override.into(),
|
|
|
|
|
transaction_has_valid_network_upgrade,
|
|
|
|
|
always_has_coinbase: true,
|
2021-05-28 05:48:27 -07:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
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-06-18 10:40:08 -07:00
|
|
|
|
|
|
|
|
|
/// Returns the transaction version override.
|
|
|
|
|
pub fn transaction_version_override(&self) -> Option<u32> {
|
|
|
|
|
self.transaction_version_override
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns `true` if all transactions have valid network upgrade fields.
|
|
|
|
|
///
|
|
|
|
|
/// If `false`, some transactions have invalid network upgrades.
|
|
|
|
|
pub fn transaction_has_valid_network_upgrade(&self) -> bool {
|
|
|
|
|
self.transaction_has_valid_network_upgrade
|
|
|
|
|
}
|
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();
|
|
|
|
|
|
2021-06-18 10:40:08 -07:00
|
|
|
|
LedgerState {
|
2021-05-28 05:48:27 -07:00
|
|
|
|
height: most_recent_activation_height,
|
|
|
|
|
network: default_network,
|
|
|
|
|
network_upgrade_override: default_override.network_upgrade_override,
|
|
|
|
|
previous_block_hash_override: default_override.previous_block_hash_override,
|
2021-06-18 10:40:08 -07:00
|
|
|
|
transaction_version_override: default_override.transaction_version_override,
|
|
|
|
|
transaction_has_valid_network_upgrade: default_override
|
|
|
|
|
.transaction_has_valid_network_upgrade,
|
|
|
|
|
has_coinbase: default_override.always_has_coinbase,
|
2021-05-28 05:48:27 -07:00
|
|
|
|
}
|
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-06-04 06:31:47 -07:00
|
|
|
|
let nu5_activation_height = Nu5.activation_height(default_network);
|
2021-04-25 15:32:21 -07:00
|
|
|
|
let nu5_override = if nu5_activation_height.is_some() {
|
|
|
|
|
None
|
|
|
|
|
} else {
|
2021-06-04 06:31:47 -07:00
|
|
|
|
Some(Nu5)
|
2021-04-25 15:32:21 -07:00
|
|
|
|
};
|
2021-04-22 20:19:33 -07:00
|
|
|
|
|
2021-05-28 05:48:27 -07:00
|
|
|
|
LedgerStateOverride {
|
|
|
|
|
height_override: None,
|
|
|
|
|
previous_block_hash_override: None,
|
2021-06-18 10:40:08 -07:00
|
|
|
|
network_upgrade_override: nu5_override,
|
|
|
|
|
transaction_version_override: None,
|
|
|
|
|
transaction_has_valid_network_upgrade: false,
|
|
|
|
|
always_has_coinbase: true,
|
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
|
|
|
|
|
2021-06-18 10:40:08 -07:00
|
|
|
|
/// Generate an arbitrary [`LedgerState`].
|
2021-04-25 15:32:21 -07:00
|
|
|
|
///
|
2021-05-28 05:48:27 -07:00
|
|
|
|
/// The default strategy arbitrarily skips some coinbase transactions, and
|
2022-06-02 08:07:35 -07:00
|
|
|
|
/// has an arbitrary start height. To override, use a specific [`LedgerState`]
|
2021-06-18 10:40:08 -07:00
|
|
|
|
/// strategy method.
|
2021-05-28 05:48:27 -07:00
|
|
|
|
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-06-18 10:40:08 -07:00
|
|
|
|
.prop_map(
|
|
|
|
|
move |(height, network, transaction_has_valid_network_upgrade, has_coinbase)| {
|
|
|
|
|
LedgerState {
|
|
|
|
|
height: ledger_override.height_override.unwrap_or(height),
|
|
|
|
|
network,
|
|
|
|
|
network_upgrade_override: ledger_override.network_upgrade_override,
|
|
|
|
|
previous_block_hash_override: ledger_override.previous_block_hash_override,
|
|
|
|
|
transaction_version_override: ledger_override.transaction_version_override,
|
|
|
|
|
transaction_has_valid_network_upgrade: ledger_override
|
|
|
|
|
.transaction_has_valid_network_upgrade
|
|
|
|
|
|| transaction_has_valid_network_upgrade,
|
|
|
|
|
has_coinbase: ledger_override.always_has_coinbase || has_coinbase,
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
)
|
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 {
|
2021-08-04 13:48:15 -07:00
|
|
|
|
let transactions_strategy =
|
2023-07-19 14:20:37 -07:00
|
|
|
|
// Generate a random number transactions. A coinbase tx is always generated, so if
|
|
|
|
|
// `transaction_count` is zero, the block will contain only the coinbase tx.
|
|
|
|
|
(0..MAX_ARBITRARY_ITEMS).prop_flat_map(move |transaction_count| {
|
2021-08-04 13:48:15 -07:00
|
|
|
|
Transaction::vec_strategy(ledger_state, transaction_count)
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// TODO: if needed, fixup:
|
|
|
|
|
// - history and authorizing data commitments
|
|
|
|
|
// - the transaction merkle root
|
2020-10-02 15:51:51 -07:00
|
|
|
|
|
2021-05-28 05:48:27 -07:00
|
|
|
|
(Header::arbitrary_with(ledger_state), transactions_strategy)
|
|
|
|
|
.prop_map(move |(header, transactions)| Self {
|
2022-07-21 16:15:22 -07:00
|
|
|
|
header: header.into(),
|
2021-05-28 05:48:27 -07:00
|
|
|
|
transactions,
|
2020-10-02 15:51:51 -07:00
|
|
|
|
})
|
|
|
|
|
.boxed()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Strategy = BoxedStrategy<Self>;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-28 21:23:50 -07:00
|
|
|
|
/// Skip checking transparent coinbase spends in [`Block::partial_chain_strategy`].
|
|
|
|
|
#[allow(clippy::result_unit_err)]
|
|
|
|
|
pub fn allow_all_transparent_coinbase_spends(
|
|
|
|
|
_: transparent::OutPoint,
|
|
|
|
|
_: transparent::CoinbaseSpendRestriction,
|
2023-04-17 20:43:39 -07:00
|
|
|
|
_: &transparent::Utxo,
|
2021-07-28 21:23:50 -07:00
|
|
|
|
) -> Result<(), ()> {
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-02 15:51:51 -07:00
|
|
|
|
impl Block {
|
2021-07-28 21:23:50 -07:00
|
|
|
|
/// Returns a strategy for creating vectors of blocks with increasing height.
|
|
|
|
|
///
|
|
|
|
|
/// Each vector is `count` blocks long.
|
|
|
|
|
///
|
|
|
|
|
/// `check_transparent_coinbase_spend` is used to check if
|
|
|
|
|
/// transparent coinbase UTXOs are valid, before using them in blocks.
|
|
|
|
|
/// Use [`allow_all_transparent_coinbase_spends`] to disable this check.
|
2021-08-23 07:17:33 -07:00
|
|
|
|
///
|
|
|
|
|
/// `generate_valid_commitments` specifies if the generated blocks
|
|
|
|
|
/// should have valid commitments. This makes it much slower so it's better
|
|
|
|
|
/// to enable only when needed.
|
2023-04-17 20:43:39 -07:00
|
|
|
|
pub fn partial_chain_strategy<F, E>(
|
2021-05-28 05:48:27 -07:00
|
|
|
|
mut current: LedgerState,
|
2020-10-02 15:51:51 -07:00
|
|
|
|
count: usize,
|
2021-07-28 21:23:50 -07:00
|
|
|
|
check_transparent_coinbase_spend: F,
|
2021-08-23 07:17:33 -07:00
|
|
|
|
generate_valid_commitments: bool,
|
2021-07-28 21:23:50 -07:00
|
|
|
|
) -> BoxedStrategy<SummaryDebug<Vec<Arc<Self>>>>
|
|
|
|
|
where
|
|
|
|
|
F: Fn(
|
|
|
|
|
transparent::OutPoint,
|
|
|
|
|
transparent::CoinbaseSpendRestriction,
|
2023-04-17 20:43:39 -07:00
|
|
|
|
&transparent::Utxo,
|
|
|
|
|
) -> Result<(), E>
|
2021-07-28 21:23:50 -07:00
|
|
|
|
+ Copy
|
|
|
|
|
+ 'static,
|
|
|
|
|
{
|
2020-10-02 15:51:51 -07:00
|
|
|
|
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-07-28 21:23:50 -07:00
|
|
|
|
vec.push((Just(current.height), Block::arbitrary_with(current)));
|
2021-05-28 05:48:27 -07:00
|
|
|
|
current.height.0 += 1;
|
2020-10-02 15:51:51 -07:00
|
|
|
|
}
|
|
|
|
|
|
2021-07-22 16:40:15 -07:00
|
|
|
|
// after the vec strategy generates blocks, fixup invalid parts of the blocks
|
2021-07-28 21:23:50 -07:00
|
|
|
|
vec.prop_map(move |mut vec| {
|
2021-05-28 05:48:27 -07:00
|
|
|
|
let mut previous_block_hash = None;
|
2021-07-28 21:23:50 -07:00
|
|
|
|
let mut utxos = HashMap::new();
|
2021-08-12 05:38:16 -07:00
|
|
|
|
let mut chain_value_pools = ValueBalance::zero();
|
2021-08-23 07:17:33 -07:00
|
|
|
|
let mut sapling_tree = sapling::tree::NoteCommitmentTree::default();
|
|
|
|
|
let mut orchard_tree = orchard::tree::NoteCommitmentTree::default();
|
|
|
|
|
// The history tree usually takes care of "creating itself". But this
|
|
|
|
|
// only works when blocks are pushed into it starting from genesis
|
|
|
|
|
// (or at least pre-Heartwood, where the tree is not required).
|
|
|
|
|
// However, this strategy can generate blocks from an arbitrary height,
|
|
|
|
|
// so we must wait for the first block to create the history tree from it.
|
|
|
|
|
// This is why `Option` is used here.
|
|
|
|
|
let mut history_tree: Option<HistoryTree> = None;
|
2021-07-22 16:40:15 -07:00
|
|
|
|
|
2021-07-28 21:23:50 -07:00
|
|
|
|
for (height, block) in vec.iter_mut() {
|
2021-07-22 16:40:15 -07:00
|
|
|
|
// fixup the previous block hash
|
2021-05-28 05:48:27 -07:00
|
|
|
|
if let Some(previous_block_hash) = previous_block_hash {
|
2022-07-21 16:15:22 -07:00
|
|
|
|
Arc::make_mut(&mut block.header).previous_block_hash = previous_block_hash;
|
2021-05-28 05:48:27 -07:00
|
|
|
|
}
|
2021-07-22 16:40:15 -07:00
|
|
|
|
|
|
|
|
|
let mut new_transactions = Vec::new();
|
2021-07-28 21:23:50 -07:00
|
|
|
|
for (tx_index_in_block, transaction) in block.transactions.drain(..).enumerate() {
|
|
|
|
|
if let Some(transaction) = fix_generated_transaction(
|
|
|
|
|
(*transaction).clone(),
|
|
|
|
|
tx_index_in_block,
|
|
|
|
|
*height,
|
2021-08-12 05:38:16 -07:00
|
|
|
|
&mut chain_value_pools,
|
2021-07-28 21:23:50 -07:00
|
|
|
|
&mut utxos,
|
|
|
|
|
check_transparent_coinbase_spend,
|
|
|
|
|
) {
|
2021-08-23 07:17:33 -07:00
|
|
|
|
// The FinalizedState does not update the note commitment trees with the genesis block,
|
|
|
|
|
// because it doesn't need to (the trees are not used at that point) and updating them
|
2023-03-15 12:40:54 -07:00
|
|
|
|
// would be awkward since the genesis block is handled separately there.
|
2021-08-23 07:17:33 -07:00
|
|
|
|
// This forces us to skip the genesis block here too in order to able to use
|
|
|
|
|
// this to test the finalized state.
|
2022-07-22 09:19:11 -07:00
|
|
|
|
//
|
|
|
|
|
// TODO: run note commitment tree updates in parallel rayon threads,
|
|
|
|
|
// using `NoteCommitmentTrees::update_trees_parallel()`
|
2021-08-23 07:17:33 -07:00
|
|
|
|
if generate_valid_commitments && *height != Height(0) {
|
|
|
|
|
for sapling_note_commitment in transaction.sapling_note_commitments() {
|
|
|
|
|
sapling_tree.append(*sapling_note_commitment).unwrap();
|
|
|
|
|
}
|
|
|
|
|
for orchard_note_commitment in transaction.orchard_note_commitments() {
|
|
|
|
|
orchard_tree.append(*orchard_note_commitment).unwrap();
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-22 16:40:15 -07:00
|
|
|
|
new_transactions.push(Arc::new(transaction));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// delete invalid transactions
|
|
|
|
|
block.transactions = new_transactions;
|
|
|
|
|
|
2021-08-23 07:17:33 -07:00
|
|
|
|
// fix commitment (must be done after finishing changing the block)
|
|
|
|
|
if generate_valid_commitments {
|
|
|
|
|
let current_height = block.coinbase_height().unwrap();
|
|
|
|
|
let heartwood_height = NetworkUpgrade::Heartwood
|
|
|
|
|
.activation_height(current.network)
|
|
|
|
|
.unwrap();
|
|
|
|
|
let nu5_height = NetworkUpgrade::Nu5.activation_height(current.network);
|
2022-07-21 16:15:22 -07:00
|
|
|
|
|
2021-08-23 07:17:33 -07:00
|
|
|
|
match current_height.cmp(&heartwood_height) {
|
2022-02-17 12:10:13 -08:00
|
|
|
|
std::cmp::Ordering::Less => {
|
|
|
|
|
// In pre-Heartwood blocks this is the Sapling note commitment tree root.
|
|
|
|
|
// We don't validate it since we checkpoint on Canopy, but it
|
|
|
|
|
// needs to be well-formed, i.e. smaller than 𝑞_J, so we
|
|
|
|
|
// arbitrarily set it to 1.
|
2022-07-21 16:15:22 -07:00
|
|
|
|
let block_header = Arc::make_mut(&mut block.header);
|
2023-01-17 05:57:22 -08:00
|
|
|
|
block_header.commitment_bytes = [0u8; 32].into();
|
2022-07-21 16:15:22 -07:00
|
|
|
|
block_header.commitment_bytes[0] = 1;
|
2022-02-17 12:10:13 -08:00
|
|
|
|
}
|
2021-08-23 07:17:33 -07:00
|
|
|
|
std::cmp::Ordering::Equal => {
|
2022-02-17 12:10:13 -08:00
|
|
|
|
// The Heartwood activation block has a hardcoded all-zeroes commitment.
|
2022-07-21 16:15:22 -07:00
|
|
|
|
let block_header = Arc::make_mut(&mut block.header);
|
2023-01-17 05:57:22 -08:00
|
|
|
|
block_header.commitment_bytes = [0u8; 32].into();
|
2021-08-23 07:17:33 -07:00
|
|
|
|
}
|
|
|
|
|
std::cmp::Ordering::Greater => {
|
2022-02-17 12:10:13 -08:00
|
|
|
|
// Set the correct commitment bytes according to the network upgrade.
|
2021-08-23 07:17:33 -07:00
|
|
|
|
let history_tree_root = match &history_tree {
|
|
|
|
|
Some(tree) => tree.hash().unwrap_or_else(|| [0u8; 32].into()),
|
|
|
|
|
None => [0u8; 32].into(),
|
|
|
|
|
};
|
|
|
|
|
if nu5_height.is_some() && current_height >= nu5_height.unwrap() {
|
|
|
|
|
// From zebra-state/src/service/check.rs
|
|
|
|
|
let auth_data_root = block.auth_data_root();
|
|
|
|
|
let hash_block_commitments =
|
|
|
|
|
ChainHistoryBlockTxAuthCommitmentHash::from_commitments(
|
|
|
|
|
&history_tree_root,
|
|
|
|
|
&auth_data_root,
|
|
|
|
|
);
|
2022-07-21 16:15:22 -07:00
|
|
|
|
let block_header = Arc::make_mut(&mut block.header);
|
2023-01-17 05:57:22 -08:00
|
|
|
|
block_header.commitment_bytes =
|
|
|
|
|
hash_block_commitments.bytes_in_serialized_order().into();
|
2021-08-23 07:17:33 -07:00
|
|
|
|
} else {
|
2022-07-21 16:15:22 -07:00
|
|
|
|
let block_header = Arc::make_mut(&mut block.header);
|
2023-01-17 05:57:22 -08:00
|
|
|
|
block_header.commitment_bytes =
|
|
|
|
|
history_tree_root.bytes_in_serialized_order().into();
|
2021-08-23 07:17:33 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// update history tree for the next block
|
2023-09-19 07:49:36 -07:00
|
|
|
|
if let Some(history_tree) = history_tree.as_mut() {
|
|
|
|
|
history_tree
|
|
|
|
|
.push(
|
|
|
|
|
current.network,
|
|
|
|
|
Arc::new(block.clone()),
|
|
|
|
|
sapling_tree.root(),
|
|
|
|
|
orchard_tree.root(),
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
} else {
|
2021-08-23 07:17:33 -07:00
|
|
|
|
history_tree = Some(
|
|
|
|
|
HistoryTree::from_block(
|
|
|
|
|
current.network,
|
|
|
|
|
Arc::new(block.clone()),
|
|
|
|
|
&sapling_tree.root(),
|
|
|
|
|
&orchard_tree.root(),
|
|
|
|
|
)
|
|
|
|
|
.unwrap(),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-28 21:23:50 -07:00
|
|
|
|
|
|
|
|
|
// now that we've made all the changes, calculate our block hash,
|
|
|
|
|
// so the next block can use it
|
|
|
|
|
previous_block_hash = Some(block.hash());
|
2021-05-28 05:48:27 -07:00
|
|
|
|
}
|
2021-07-28 21:23:50 -07:00
|
|
|
|
SummaryDebug(
|
|
|
|
|
vec.into_iter()
|
|
|
|
|
.map(|(_height, block)| Arc::new(block))
|
|
|
|
|
.collect(),
|
|
|
|
|
)
|
2021-05-28 05:48:27 -07:00
|
|
|
|
})
|
|
|
|
|
.boxed()
|
2020-10-02 15:51:51 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-28 21:23:50 -07:00
|
|
|
|
/// Fix `transaction` so it obeys more consensus rules.
|
|
|
|
|
///
|
2022-06-02 08:07:35 -07:00
|
|
|
|
/// Spends [`transparent::OutPoint`]s from `utxos`, and adds newly created outputs.
|
2021-07-28 21:23:50 -07:00
|
|
|
|
///
|
|
|
|
|
/// If the transaction can't be fixed, returns `None`.
|
2023-04-17 20:43:39 -07:00
|
|
|
|
pub fn fix_generated_transaction<F, E>(
|
2021-07-28 21:23:50 -07:00
|
|
|
|
mut transaction: Transaction,
|
|
|
|
|
tx_index_in_block: usize,
|
|
|
|
|
height: Height,
|
2021-08-12 05:38:16 -07:00
|
|
|
|
chain_value_pools: &mut ValueBalance<NonNegative>,
|
2021-07-28 21:23:50 -07:00
|
|
|
|
utxos: &mut HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
|
|
|
|
|
check_transparent_coinbase_spend: F,
|
|
|
|
|
) -> Option<Transaction>
|
|
|
|
|
where
|
|
|
|
|
F: Fn(
|
|
|
|
|
transparent::OutPoint,
|
|
|
|
|
transparent::CoinbaseSpendRestriction,
|
2023-04-17 20:43:39 -07:00
|
|
|
|
&transparent::Utxo,
|
|
|
|
|
) -> Result<(), E>
|
2021-07-28 21:23:50 -07:00
|
|
|
|
+ Copy
|
|
|
|
|
+ 'static,
|
|
|
|
|
{
|
|
|
|
|
let mut spend_restriction = transaction.coinbase_spend_restriction(height);
|
|
|
|
|
let mut new_inputs = Vec::new();
|
2021-08-09 10:22:26 -07:00
|
|
|
|
let mut spent_outputs = HashMap::new();
|
2021-07-28 21:23:50 -07:00
|
|
|
|
|
|
|
|
|
// fixup the transparent spends
|
2021-12-20 23:58:24 -08:00
|
|
|
|
let original_inputs = transaction.inputs().to_vec();
|
|
|
|
|
for mut input in original_inputs.into_iter() {
|
2021-07-28 21:23:50 -07:00
|
|
|
|
if input.outpoint().is_some() {
|
2021-08-12 05:38:16 -07:00
|
|
|
|
// the transparent chain value pool is the sum of unspent UTXOs,
|
|
|
|
|
// so we don't need to check it separately, because we only spend unspent UTXOs
|
2021-07-28 21:23:50 -07:00
|
|
|
|
if let Some(selected_outpoint) = find_valid_utxo_for_spend(
|
|
|
|
|
&mut transaction,
|
|
|
|
|
&mut spend_restriction,
|
|
|
|
|
height,
|
|
|
|
|
utxos,
|
|
|
|
|
check_transparent_coinbase_spend,
|
|
|
|
|
) {
|
|
|
|
|
input.set_outpoint(selected_outpoint);
|
|
|
|
|
new_inputs.push(input);
|
|
|
|
|
|
2021-08-09 10:22:26 -07:00
|
|
|
|
let spent_utxo = utxos
|
|
|
|
|
.remove(&selected_outpoint)
|
|
|
|
|
.expect("selected outpoint must have a UTXO");
|
|
|
|
|
spent_outputs.insert(selected_outpoint, spent_utxo.utxo.output);
|
2021-07-28 21:23:50 -07:00
|
|
|
|
}
|
|
|
|
|
// otherwise, drop the invalid input, because it has no valid UTXOs to spend
|
|
|
|
|
} else {
|
|
|
|
|
// preserve coinbase inputs
|
|
|
|
|
new_inputs.push(input.clone());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// delete invalid inputs
|
|
|
|
|
*transaction.inputs_mut() = new_inputs;
|
|
|
|
|
|
2021-08-12 05:38:16 -07:00
|
|
|
|
let (_remaining_transaction_value, new_chain_value_pools) = transaction
|
|
|
|
|
.fix_chain_value_pools(*chain_value_pools, &spent_outputs)
|
|
|
|
|
.expect("value fixes produce valid chain value pools and remaining transaction values");
|
2021-08-09 10:22:26 -07:00
|
|
|
|
|
|
|
|
|
// TODO: if needed, check output count here as well
|
2021-07-28 21:23:50 -07:00
|
|
|
|
if transaction.has_transparent_or_shielded_inputs() {
|
2021-08-12 05:38:16 -07:00
|
|
|
|
// consensus rule: skip genesis created UTXOs
|
|
|
|
|
// Zebra implementation: also skip shielded chain value pool changes
|
2021-07-28 21:23:50 -07:00
|
|
|
|
if height > Height(0) {
|
2021-08-12 05:38:16 -07:00
|
|
|
|
*chain_value_pools = new_chain_value_pools;
|
|
|
|
|
|
2021-07-28 21:23:50 -07:00
|
|
|
|
utxos.extend(new_transaction_ordered_outputs(
|
|
|
|
|
&transaction,
|
|
|
|
|
transaction.hash(),
|
|
|
|
|
tx_index_in_block,
|
|
|
|
|
height,
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Some(transaction)
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-02 08:07:35 -07:00
|
|
|
|
/// Find a valid [`transparent::OutPoint`] in `utxos` to spend in `transaction`.
|
2021-07-28 21:23:50 -07:00
|
|
|
|
///
|
|
|
|
|
/// Modifies `transaction` and updates `spend_restriction` if needed.
|
|
|
|
|
///
|
|
|
|
|
/// If there is no valid output, or many search attempts have failed, returns `None`.
|
2023-04-17 20:43:39 -07:00
|
|
|
|
pub fn find_valid_utxo_for_spend<F, E>(
|
2021-07-28 21:23:50 -07:00
|
|
|
|
transaction: &mut Transaction,
|
|
|
|
|
spend_restriction: &mut CoinbaseSpendRestriction,
|
|
|
|
|
spend_height: Height,
|
|
|
|
|
utxos: &HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
|
|
|
|
|
check_transparent_coinbase_spend: F,
|
|
|
|
|
) -> Option<transparent::OutPoint>
|
|
|
|
|
where
|
|
|
|
|
F: Fn(
|
|
|
|
|
transparent::OutPoint,
|
|
|
|
|
transparent::CoinbaseSpendRestriction,
|
2023-04-17 20:43:39 -07:00
|
|
|
|
&transparent::Utxo,
|
|
|
|
|
) -> Result<(), E>
|
2021-07-28 21:23:50 -07:00
|
|
|
|
+ Copy
|
|
|
|
|
+ 'static,
|
|
|
|
|
{
|
|
|
|
|
let has_shielded_outputs = transaction.has_shielded_outputs();
|
|
|
|
|
let delete_transparent_outputs = CoinbaseSpendRestriction::OnlyShieldedOutputs { spend_height };
|
|
|
|
|
let mut attempts: usize = 0;
|
|
|
|
|
|
|
|
|
|
// choose an arbitrary spendable UTXO, in hash set order
|
|
|
|
|
while let Some((candidate_outpoint, candidate_utxo)) = utxos.iter().next() {
|
|
|
|
|
attempts += 1;
|
|
|
|
|
|
|
|
|
|
// Avoid O(n^2) algorithmic complexity by giving up early,
|
2023-10-30 13:06:35 -07:00
|
|
|
|
// rather than exhaustively checking the entire UTXO set
|
2021-07-28 21:23:50 -07:00
|
|
|
|
if attempts > 100 {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// try the utxo as-is, then try it with deleted transparent outputs
|
|
|
|
|
if check_transparent_coinbase_spend(
|
|
|
|
|
*candidate_outpoint,
|
|
|
|
|
*spend_restriction,
|
2023-04-17 20:43:39 -07:00
|
|
|
|
candidate_utxo.as_ref(),
|
2021-07-28 21:23:50 -07:00
|
|
|
|
)
|
|
|
|
|
.is_ok()
|
|
|
|
|
{
|
|
|
|
|
return Some(*candidate_outpoint);
|
|
|
|
|
} else if has_shielded_outputs
|
|
|
|
|
&& check_transparent_coinbase_spend(
|
|
|
|
|
*candidate_outpoint,
|
|
|
|
|
delete_transparent_outputs,
|
2023-04-17 20:43:39 -07:00
|
|
|
|
candidate_utxo.as_ref(),
|
2021-07-28 21:23:50 -07:00
|
|
|
|
)
|
|
|
|
|
.is_ok()
|
|
|
|
|
{
|
|
|
|
|
*transaction.outputs_mut() = Vec::new();
|
|
|
|
|
*spend_restriction = delete_transparent_outputs;
|
|
|
|
|
|
|
|
|
|
return Some(*candidate_outpoint);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
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-06-04 06:31:47 -07:00
|
|
|
|
if block_height == Heartwood.activation_height(network).unwrap() {
|
|
|
|
|
Commitment::ChainHistoryActivationReserved
|
|
|
|
|
} else {
|
|
|
|
|
Commitment::from_bytes(commitment_bytes, network, block_height)
|
|
|
|
|
.expect("unexpected failure in from_bytes parsing")
|
2021-04-06 03:19:28 -07:00
|
|
|
|
}
|
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>(),
|
2023-01-17 05:57:22 -08:00
|
|
|
|
any::<HexDebug<[u8; 32]>>(),
|
2021-05-24 08:10:07 -07:00
|
|
|
|
serialization::arbitrary::datetime_u32(),
|
2020-10-29 18:36:59 -07:00
|
|
|
|
any::<CompactDifficulty>(),
|
2023-01-17 05:57:22 -08:00
|
|
|
|
any::<HexDebug<[u8; 32]>>(),
|
2020-08-15 15:20:11 -07:00
|
|
|
|
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>;
|
|
|
|
|
}
|