use std::env;
use zebra_chain::{
block::Height,
parameters::{
testnet::{ConfiguredActivationHeights, ParametersBuilder},
NetworkUpgrade,
},
LedgerState,
};
use zebra_test::prelude::*;
use crate::{
config::Config,
service::{
arbitrary::PreparedChain,
finalized_state::{CheckpointVerifiedBlock, FinalizedState},
},
tests::FakeChainHelper,
};
const DEFAULT_PARTIAL_CHAIN_PROPTEST_CASES: u32 = 1;
#[test]
fn blocks_with_v5_transactions() -> Result<()> {
let _init_guard = zebra_test::init();
proptest!(ProptestConfig::with_cases(env::var("PROPTEST_CASES")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(DEFAULT_PARTIAL_CHAIN_PROPTEST_CASES)),
|((chain, count, network, _history_tree) in PreparedChain::default())| {
let mut state = FinalizedState::new(&Config::ephemeral(), &network, #[cfg(feature = "elasticsearch")] false).expect("opening an ephemeral database should succeed");
let mut height = Height(0);
for block in chain.iter().take(count) {
let checkpoint_verified = CheckpointVerifiedBlock::from(block.block.clone());
let (hash, _) = state.commit_finalized_direct(
checkpoint_verified.into(),
None,
"blocks_with_v5_transactions test"
).unwrap();
prop_assert_eq!(Some(height), state.finalized_tip_height());
prop_assert_eq!(hash, block.hash);
height = Height(height.0 + 1);
}
});
Ok(())
}
#[test]
#[allow(clippy::print_stderr)]
fn all_upgrades_and_wrong_commitments_with_fake_activation_heights() -> Result<()> {
let _init_guard = zebra_test::init();
let network = ParametersBuilder::default()
.with_slow_start_interval(zebra_chain::block::Height::MIN)
.with_activation_heights(ConfiguredActivationHeights {
before_overwinter: Some(1),
overwinter: Some(10),
sapling: Some(15),
blossom: Some(20),
heartwood: Some(25),
canopy: Some(30),
nu5: Some(35),
nu6: Some(40),
nu6_1: Some(45),
nu6_2: Some(47),
nu6_3: Some(48),
nu7: Some(50),
})
.expect("failed to set activation heights")
.with_nsm_reissuance_height(Height(50))
.clear_funding_streams()
.with_lockbox_disbursements(vec![]);
let initial_nsm_value_balance =
zebra_chain::amount::Amount::try_from(10 * zebra_chain::amount::COIN)
.expect("valid amount");
let network = network.with_initial_nsm_value_balance(initial_nsm_value_balance);
let network = network
.to_network()
.expect("failed to build configured network");
let ledger_strategy =
LedgerState::genesis_strategy(Some(network), NetworkUpgrade::Nu5, None, false);
proptest!(ProptestConfig::with_cases(env::var("PROPTEST_CASES")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(DEFAULT_PARTIAL_CHAIN_PROPTEST_CASES)),
|((chain, _count, network, _history_tree) in PreparedChain::default().with_ledger_strategy(ledger_strategy).with_valid_commitments().no_shrink())| {
let mut state = FinalizedState::new(&Config::ephemeral(), &network, #[cfg(feature = "elasticsearch")] false).expect("opening an ephemeral database should succeed");
let mut height = Height(0);
let heartwood_height = NetworkUpgrade::Heartwood.activation_height(&network).unwrap();
let heartwood_height_plus1 = (heartwood_height + 1).unwrap();
let nu5_height = NetworkUpgrade::Nu5.activation_height(&network).unwrap();
let nu5_height_plus1 = (nu5_height + 1).unwrap();
let mut failure_count = 0;
for block in chain.iter() {
let block_hash = block.hash;
let current_height = block.block.coinbase_height().unwrap();
match current_height {
h if h == heartwood_height ||
h == heartwood_height_plus1 ||
h == nu5_height ||
h == nu5_height_plus1 => {
let block = block.block.clone().set_block_commitment([0x42; 32]);
let checkpoint_verified = CheckpointVerifiedBlock::from(block);
state.commit_finalized_direct(
checkpoint_verified.into(),
None,
"all_upgrades test"
).expect_err("Must fail commitment check");
failure_count += 1;
},
_ => {},
}
let checkpoint_verified = CheckpointVerifiedBlock::from(block.block.clone());
let (hash, _) = state.commit_finalized_direct(
checkpoint_verified.into(),
None,
"all_upgrades test"
).unwrap();
prop_assert_eq!(Some(height), state.finalized_tip_height());
prop_assert_eq!(hash, block_hash);
height = Height(height.0 + 1);
}
prop_assert_eq!(failure_count, 4);
{
use zebra_chain::{
parameters::subsidy::{additional_block_subsidy, nsm_fee_contribution},
transparent::utxos_from_ordered_utxos,
};
let utxos = utxos_from_ordered_utxos(
chain.iter().flat_map(|block| block.new_outputs.iter().map(|(outpoint, utxo)| (*outpoint, utxo.clone()))),
);
let nu7_height = NetworkUpgrade::Nu7.activation_height(&network).unwrap();
let tip_height = state.finalized_tip_height().unwrap();
prop_assert!(tip_height >= nu7_height, "the chain must reach NU7");
let mut expected = initial_nsm_value_balance;
for height in (nu7_height.0..=tip_height.0).map(Height) {
let fees = chain[height.0 as usize].block.transaction_fees(&utxos).expect("valid fees");
expected = (expected - additional_block_subsidy(height, &network, expected))
.expect("the balance never goes negative");
expected = (expected + nsm_fee_contribution(height, &network, fees))
.expect("the balance stays a valid amount");
}
prop_assert_eq!(state.finalized_value_pool().nsm_amount(), expected);
prop_assert!(expected > zebra_chain::amount::Amount::<zebra_chain::amount::NonNegative>::zero());
}
});
Ok(())
}