Remove unreliable generated chain prevouts tests (#2548)

And adjust the chain lengths for better coverage.
This commit is contained in:
teor 2021-07-31 03:49:25 +10:00 committed by GitHub
parent 0325aa2517
commit f9c0620576
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 23 additions and 41 deletions

View File

@ -22,23 +22,25 @@ use crate::{
use super::*; use super::*;
/// The chain height used to test for prevout inputs. /// The chain length for zebra-chain proptests.
/// ///
/// This impacts the probability of `has_prevouts` failures in /// Most generated chains will contain transparent spends at or before this height.
/// `arbitrary_height_partial_chain_strategy`.
/// ///
/// The failure probability calculation is: /// 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:
/// ```text /// ```text
/// shielded_input = shielded_pool_count / pool_count /// shielded_input = shielded_pool_count / pool_count
/// expected_transactions = expected_inputs = MAX_ARBITRARY_ITEMS/2 /// expected_transactions = expected_inputs = MAX_ARBITRARY_ITEMS/2
/// proptest_cases = 256 /// shielded_input^(expected_transactions * expected_inputs * (PREVOUTS_CHAIN_HEIGHT - 1))
/// number_of_proptests = 5 as of July 2021 (PREVOUTS_CHAIN_HEIGHT and PartialChain tests)
/// shielded_input^(expected_transactions * expected_inputs * PREVOUTS_CHAIN_HEIGHT) * proptest_cases * number_of_proptests
/// ``` /// ```
/// ///
/// `PREVOUTS_CHAIN_HEIGHT` should be increased, and `proptest_cases` should be reduced, /// This probability is approximately 3%. However, proptest generation and
/// so that the failure probability is less than 1 in 1 million. /// minimisation strategies can create additional chains with no transparent spends.
pub const PREVOUTS_CHAIN_HEIGHT: usize = 20; ///
/// To increase the proportion of test runs with proptest spends, increase `PREVOUTS_CHAIN_HEIGHT`.
pub const PREVOUTS_CHAIN_HEIGHT: usize = 4;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
#[non_exhaustive] #[non_exhaustive]
@ -400,8 +402,10 @@ impl Block {
block.transactions = new_transactions; block.transactions = new_transactions;
// TODO: if needed, fixup: // TODO: if needed, fixup:
// - transaction output counts (currently 0..=16, consensus rules require 1..) // - transparent values and shielded value balances
// - transaction outputs (currently 0..=16 outputs, consensus rules require 1..)
// - history and authorizing data commitments // - history and authorizing data commitments
// - the transaction merkle root
// now that we've made all the changes, calculate our block hash, // now that we've made all the changes, calculate our block hash,
// so the next block can use it // so the next block can use it

View File

@ -7,7 +7,6 @@ use zebra_test::prelude::*;
use crate::{ use crate::{
parameters::{Network, GENESIS_PREVIOUS_BLOCK_HASH}, parameters::{Network, GENESIS_PREVIOUS_BLOCK_HASH},
serialization::{SerializationError, ZcashDeserializeInto, ZcashSerialize}, serialization::{SerializationError, ZcashDeserializeInto, ZcashSerialize},
transaction::arbitrary::MAX_ARBITRARY_ITEMS,
LedgerState, LedgerState,
}; };
@ -172,7 +171,7 @@ fn genesis_partial_chain_strategy() -> Result<()> {
let strategy = LedgerState::genesis_strategy(None, None, false).prop_flat_map(|init| { let strategy = LedgerState::genesis_strategy(None, None, false).prop_flat_map(|init| {
Block::partial_chain_strategy( Block::partial_chain_strategy(
init, init,
MAX_ARBITRARY_ITEMS, PREVOUTS_CHAIN_HEIGHT,
allow_all_transparent_coinbase_spends, allow_all_transparent_coinbase_spends,
) )
}); });
@ -212,7 +211,6 @@ fn genesis_partial_chain_strategy() -> Result<()> {
/// Make sure our block height strategy generates a chain with: /// Make sure our block height strategy generates a chain with:
/// - correct coinbase heights /// - correct coinbase heights
/// - correct previous block hashes /// - correct previous block hashes
/// - at least one transparent PrevOut input in the entire chain
#[test] #[test]
fn arbitrary_height_partial_chain_strategy() -> Result<()> { fn arbitrary_height_partial_chain_strategy() -> Result<()> {
zebra_test::init(); zebra_test::init();
@ -230,7 +228,6 @@ fn arbitrary_height_partial_chain_strategy() -> Result<()> {
proptest!(|(chain in strategy)| { proptest!(|(chain in strategy)| {
let mut height = None; let mut height = None;
let mut previous_block_hash = None; let mut previous_block_hash = None;
let mut has_prevouts = false;
for block in chain { for block in chain {
if height.is_none() { if height.is_none() {
@ -242,18 +239,8 @@ fn arbitrary_height_partial_chain_strategy() -> Result<()> {
prop_assert_eq!(Some(block.header.previous_block_hash), previous_block_hash); prop_assert_eq!(Some(block.header.previous_block_hash), previous_block_hash);
} }
has_prevouts |= block
.transactions
.iter()
.flat_map(|t| t.inputs())
.find_map(|i| i.outpoint())
.is_some();
previous_block_hash = Some(block.hash()); previous_block_hash = Some(block.hash());
} }
// this assertion checks that we're covering transparent spends
prop_assert!(has_prevouts, "unexpected missing prevouts in entire chain");
}); });
Ok(()) Ok(())

View File

@ -20,7 +20,10 @@ use super::*;
/// The chain length for state proptests. /// The chain length for state proptests.
/// ///
/// Shorter lengths increase the probability of proptest failures. /// 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. /// See [`block::arbitrary::PREVOUTS_CHAIN_HEIGHT`] for details.
pub const MAX_PARTIAL_CHAIN_BLOCKS: usize = pub const MAX_PARTIAL_CHAIN_BLOCKS: usize =

View File

@ -25,7 +25,6 @@ const DEFAULT_PARTIAL_CHAIN_PROPTEST_CASES: u32 = 1;
/// ///
/// Also check for: /// Also check for:
/// - no transparent spends in the genesis block, because genesis transparent outputs are ignored /// - no transparent spends in the genesis block, because genesis transparent outputs are ignored
/// - at least one transparent PrevOut input in the entire chain
#[test] #[test]
fn forked_equals_pushed() -> Result<()> { fn forked_equals_pushed() -> Result<()> {
zebra_test::init(); zebra_test::init();
@ -40,7 +39,6 @@ fn forked_equals_pushed() -> Result<()> {
let mut full_chain = Chain::new(Default::default(), Default::default()); let mut full_chain = Chain::new(Default::default(), Default::default());
let mut partial_chain = Chain::new(Default::default(), Default::default()); let mut partial_chain = Chain::new(Default::default(), Default::default());
let mut has_prevouts = false;
for block in chain.iter().take(fork_at_count) { for block in chain.iter().take(fork_at_count) {
partial_chain = partial_chain.push(block.clone())?; partial_chain = partial_chain.push(block.clone())?;
@ -59,18 +57,12 @@ fn forked_equals_pushed() -> Result<()> {
.filter_map(|i| i.outpoint()) .filter_map(|i| i.outpoint())
.count(), .count(),
0, 0,
"unexpected transparent prevout inputs at height {:?}: genesis transparent outputs are ignored", "unexpected transparent prevout input at height {:?}: \
genesis transparent outputs must be ignored, \
so there can not be any spends in the genesis block",
block.height, block.height,
); );
} }
has_prevouts |= block
.block
.transactions
.iter()
.flat_map(|t| t.inputs())
.find_map(|i| i.outpoint())
.is_some();
} }
let forked = full_chain let forked = full_chain
@ -85,10 +77,6 @@ fn forked_equals_pushed() -> Result<()> {
// the first check is redundant, but it's useful for debugging // the first check is redundant, but it's useful for debugging
prop_assert_eq!(forked.blocks.len(), partial_chain.blocks.len()); prop_assert_eq!(forked.blocks.len(), partial_chain.blocks.len());
prop_assert!(forked.eq_internal_state(&partial_chain)); prop_assert!(forked.eq_internal_state(&partial_chain));
// this assertion checks that we're still generating some transparent spends,
// after proptests remove unshielded and immature transparent coinbase spends
prop_assert!(has_prevouts, "no blocks in chain had prevouts");
}); });
Ok(()) Ok(())