Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add proptest-impl feature to zebra-state #2529

Merged
merged 8 commits into from
Jul 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion zebra-state/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ authors = ["Zcash Foundation <[email protected]>"]
license = "MIT OR Apache-2.0"
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
proptest-impl = ["proptest", "zebra-test"]

[dependencies]
zebra-chain = { path = "../zebra-chain" }
Expand All @@ -28,6 +29,9 @@ tempdir = "0.3.7"
chrono = "0.4.19"
rlimit = "0.5.4"

proptest = { version = "0.10.1", optional = true }
zebra-test = { path = "../zebra-test/", optional = true }

[dev-dependencies]
zebra-chain = { path = "../zebra-chain", features = ["proptest-impl"] }
zebra-test = { path = "../zebra-test/" }
Expand Down
28 changes: 28 additions & 0 deletions zebra-state/src/arbitrary.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use std::sync::Arc;

use zebra_chain::{block::Block, transparent};

use crate::PreparedBlock;

/// Mocks computation done during semantic validation
pub trait Prepare {
fn prepare(self) -> PreparedBlock;
}

impl Prepare for Arc<Block> {
fn prepare(self) -> PreparedBlock {
let block = self;
let hash = block.hash();
let height = block.coinbase_height().unwrap();
let transaction_hashes: Vec<_> = block.transactions.iter().map(|tx| tx.hash()).collect();
let new_outputs = transparent::new_ordered_outputs(&block, transaction_hashes.as_slice());

PreparedBlock {
block,
hash,
height,
new_outputs,
transaction_hashes,
}
}
}
2 changes: 2 additions & 0 deletions zebra-state/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#![deny(clippy::await_holding_lock)]
#![forbid(unsafe_code)]

#[cfg(any(test, feature = "proptest-impl"))]
mod arbitrary;
mod config;
pub mod constants;
mod error;
Expand Down
6 changes: 3 additions & 3 deletions zebra-state/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ pub type QueuedFinalized = (
oneshot::Sender<Result<block::Hash, BoxError>>,
);

struct StateService {
pub(crate) struct StateService {
/// Holds data relating to finalized chain state.
disk: FinalizedState,
pub(crate) disk: FinalizedState,
/// Holds data relating to non-finalized chain state.
mem: NonFinalizedState,
/// Blocks awaiting their parent blocks for contextual verification.
Expand Down Expand Up @@ -500,7 +500,7 @@ impl StateService {
}
}

struct Iter<'a> {
pub(crate) struct Iter<'a> {
service: &'a StateService,
state: IterState,
}
Expand Down
108 changes: 2 additions & 106 deletions zebra-state/src/service/arbitrary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,9 @@ use proptest::{
test_runner::TestRunner,
};

use zebra_chain::{
block::{Block, Height},
fmt::SummaryDebug,
parameters::{Network::*, NetworkUpgrade},
serialization::ZcashDeserializeInto,
LedgerState,
};
use zebra_chain::{block::Block, fmt::SummaryDebug, parameters::NetworkUpgrade, LedgerState};

use crate::tests::Prepare;
use crate::arbitrary::Prepare;

use super::*;

Expand Down Expand Up @@ -93,101 +87,3 @@ impl Strategy for PreparedChain {
})
}
}

/// 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))
})
}

/// Return a new `StateService` containing the mainnet genesis block.
/// Also returns the finalized genesis block itself.
pub(super) fn new_state_with_mainnet_genesis() -> (StateService, FinalizedBlock) {
let genesis = zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES
.zcash_deserialize_into::<Arc<Block>>()
.expect("block should deserialize");

let mut state = StateService::new(Config::ephemeral(), Mainnet);

assert_eq!(None, state.best_tip());

let genesis = FinalizedBlock::from(genesis);
state
.disk
.commit_finalized_direct(genesis.clone(), "test")
.expect("unexpected invalid genesis block test vector");

assert_eq!(Some((Height(0), genesis.hash)), state.best_tip());

(state, genesis)
}

/// Return a `Transaction::V4` with the coinbase data from `coinbase`.
///
/// Used to convert a coinbase transaction to a version that the non-finalized state will accept.
pub(super) fn transaction_v4_from_coinbase(coinbase: &Transaction) -> Transaction {
assert!(
!coinbase.has_sapling_shielded_data(),
"conversion assumes sapling shielded data is None"
);

Transaction::V4 {
inputs: coinbase.inputs().to_vec(),
outputs: coinbase.outputs().to_vec(),
lock_time: coinbase.lock_time(),
// `Height(0)` means that the expiry height is ignored
expiry_height: coinbase.expiry_height().unwrap_or(Height(0)),
// invalid for coinbase transactions
joinsplit_data: None,
sapling_shielded_data: None,
}
}
4 changes: 2 additions & 2 deletions zebra-state/src/service/check/tests/nullifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ use zebra_chain::{
};

use crate::{
service::arbitrary::{new_state_with_mainnet_genesis, transaction_v4_from_coinbase},
tests::Prepare,
arbitrary::Prepare,
tests::setup::{new_state_with_mainnet_genesis, transaction_v4_from_coinbase},
FinalizedBlock,
ValidateContextError::{
DuplicateOrchardNullifier, DuplicateSaplingNullifier, DuplicateSproutNullifier,
Expand Down
8 changes: 3 additions & 5 deletions zebra-state/src/service/check/tests/utxo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@ use zebra_chain::{
};

use crate::{
service::{
arbitrary::{new_state_with_mainnet_genesis, transaction_v4_from_coinbase},
StateService,
},
tests::Prepare,
arbitrary::Prepare,
service::StateService,
tests::setup::{new_state_with_mainnet_genesis, transaction_v4_from_coinbase},
FinalizedBlock,
ValidateContextError::{
DuplicateTransparentSpend, EarlyTransparentSpend, MissingTransparentOutput,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ mod tests {
use zebra_chain::{block::Block, serialization::ZcashDeserializeInto};
use zebra_test::prelude::*;

use crate::tests::{FakeChainHelper, Prepare};
use crate::{arbitrary::Prepare, tests::FakeChainHelper};

use self::assert_eq;
use super::*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ use zebra_test::prelude::*;
use zebra_chain::{block::Block, fmt::DisplayToDebug, parameters::NetworkUpgrade::*, LedgerState};

use crate::{
arbitrary::Prepare,
service::{
arbitrary::PreparedChain,
finalized_state::FinalizedState,
non_finalized_state::{Chain, NonFinalizedState},
},
tests::Prepare,
Config,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ use zebra_chain::{block::Block, parameters::Network, serialization::ZcashDeseria
use zebra_test::prelude::*;

use crate::{
arbitrary::Prepare,
service::{
finalized_state::FinalizedState,
non_finalized_state::{Chain, NonFinalizedState},
},
tests::{FakeChainHelper, Prepare},
tests::FakeChainHelper,
Config,
};

Expand Down
10 changes: 5 additions & 5 deletions zebra-state/src/service/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use zebra_chain::{
};
use zebra_test::{prelude::*, transcript::Transcript};

use crate::{init, service::arbitrary, BoxError, Config, Request, Response};
use crate::{init, tests::setup::partial_nu5_chain_strategy, BoxError, Config, Request, Response};

const LAST_BLOCK_HEIGHT: u32 = 10;

Expand Down Expand Up @@ -197,7 +197,7 @@ proptest! {
/// Test blocks that are less than the NU5 activation height.
#[test]
fn some_block_less_than_network_upgrade(
(network, nu_activation_height, chain) in arbitrary::partial_nu5_chain_strategy(4, true, BLOCKS_AFTER_NU5/2, NetworkUpgrade::Canopy)
(network, nu_activation_height, chain) in partial_nu5_chain_strategy(4, true, BLOCKS_AFTER_NU5/2, NetworkUpgrade::Canopy)
) {
let response = crate::service::legacy_chain_check(nu_activation_height, chain.into_iter().rev(), network)
.map_err(|error| error.to_string());
Expand All @@ -208,7 +208,7 @@ proptest! {
/// Test the maximum amount of blocks to check before chain is declared to be legacy.
#[test]
fn no_transaction_with_network_upgrade(
(network, nu_activation_height, chain) in arbitrary::partial_nu5_chain_strategy(4, true, BLOCKS_AFTER_NU5, NetworkUpgrade::Canopy)
(network, nu_activation_height, chain) in partial_nu5_chain_strategy(4, true, BLOCKS_AFTER_NU5, NetworkUpgrade::Canopy)
) {
let response = crate::service::legacy_chain_check(nu_activation_height, chain.into_iter().rev(), network)
.map_err(|error| error.to_string());
Expand All @@ -222,7 +222,7 @@ proptest! {
/// Test the `Block.check_transaction_network_upgrade()` error inside the legacy check.
#[test]
fn at_least_one_transaction_with_inconsistent_network_upgrade(
(network, nu_activation_height, chain) in arbitrary::partial_nu5_chain_strategy(5, false, BLOCKS_AFTER_NU5, NetworkUpgrade::Canopy)
(network, nu_activation_height, chain) in partial_nu5_chain_strategy(5, false, BLOCKS_AFTER_NU5, NetworkUpgrade::Canopy)
) {
// this test requires that an invalid block is encountered
// before a valid block (and before the check gives up),
Expand Down Expand Up @@ -262,7 +262,7 @@ proptest! {
/// Test there is at least one transaction with a valid `network_upgrade` in the legacy check.
#[test]
fn at_least_one_transaction_with_valid_network_upgrade(
(network, nu_activation_height, chain) in arbitrary::partial_nu5_chain_strategy(5, true, BLOCKS_AFTER_NU5/2, NetworkUpgrade::Canopy)
(network, nu_activation_height, chain) in partial_nu5_chain_strategy(5, true, BLOCKS_AFTER_NU5/2, NetworkUpgrade::Canopy)
) {
let response = crate::service::legacy_chain_check(nu_activation_height, chain.into_iter().rev(), network)
.map_err(|error| error.to_string());
Expand Down
23 changes: 1 addition & 22 deletions zebra-state/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,7 @@ use zebra_chain::{

use super::*;

/// Mocks computation done during semantic validation
pub trait Prepare {
fn prepare(self) -> PreparedBlock;
}

impl Prepare for Arc<Block> {
fn prepare(self) -> PreparedBlock {
let block = self;
let hash = block.hash();
let height = block.coinbase_height().unwrap();
let transaction_hashes: Vec<_> = block.transactions.iter().map(|tx| tx.hash()).collect();
let new_outputs = transparent::new_ordered_outputs(&block, transaction_hashes.as_slice());

PreparedBlock {
block,
hash,
height,
new_outputs,
transaction_hashes,
}
}
}
pub mod setup;

/// Helper trait for constructing "valid" looking chains of blocks
pub trait FakeChainHelper {
Expand Down
Loading