Add tests for `Chain` implementation (#1093)

* Begin work on RFC5 implementation

* I think this is necessary

* holy shit supertrait implemented via subtrait

* implement most of the chain functions

* change to slightly better name

* implement fork

* fix outpoint handling in Chain struct

* update expect for work

* resolve review comment

* split utxo into two sets

* update the Chain definition

* just a little more

* update comment

* Apply suggestions from code review

Co-authored-by: teor <teor@riseup.net>

* apply changes from code review

* remove allow attribute in zebra-state/lib.rs

* Update zebra-state/src/memory_state.rs

Co-authored-by: teor <teor@riseup.net>

* merge ChainSet type into MemoryState

* rename state impl types

* Add error messages to asserts

* checkpoint so I can split off arbitrary changes into a PR

* export proptest impls for use in downstream crates

* add testjob for disabled feature in zebra-chain

* run rustfmt

* try to fix github actions syntax

* differentiate name

* prove that github action tests zebra-chain build without features

* revert change from last commit now that test is running

* remove accidentally introduced newline

* checkpoint

* add module doc comment

* update RFC for utxos

* add missing header

* working proptest for Chain

* apply change from chain impl PR

* setup config for proptests

* Update zebra-chain/src/block/arbitrary.rs

Co-authored-by: teor <teor@riseup.net>

* run rustfmt

Co-authored-by: teor <teor@riseup.net>
This commit is contained in:
Jane Lusby 2020-10-02 15:51:51 -07:00 committed by GitHub
parent 1b528404cd
commit 86ed13060f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 447 additions and 138 deletions

1
Cargo.lock generated
View File

@ -3262,6 +3262,7 @@ dependencies = [
"lazy_static",
"metrics",
"once_cell",
"proptest",
"serde",
"sled",
"spandoc",

View File

@ -800,8 +800,10 @@ Returns
Implemented by querying:
- (non-finalized) if any `Chains` contain an `OutPoint` in their `created_utxos` and not their `spent_utxo` get the `transparent::Output` from `OutPoint`'s transaction
- (finalized) else if `OutPoint` is in `utxos_by_outpoint` return the associated `transparent::Output`.
- (non-finalized) if any `Chains` contain `OutPoint` in their `created_utxos`
get the `transparent::Output` from `OutPoint`'s transaction
- (finalized) else if `OutPoint` is in `utxos_by_outpoint` return the
associated `transparent::Output`.
- else wait for `OutPoint` to be created as described in [RFC0004]
[RFC0004]: https://zebra.zfnd.org/dev/rfcs/0004-asynchronous-script-verification.html

View File

@ -27,12 +27,8 @@ use serde::{Deserialize, Serialize};
use crate::{parameters::Network, transaction::Transaction, transparent};
#[cfg(any(test, feature = "proptest-impl"))]
use proptest_derive::Arbitrary;
/// A Zcash block, containing a header and a list of transactions.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
pub struct Block {
/// The block header, containing block metadata.
pub header: Header,

View File

@ -1,14 +1,50 @@
use std::sync::Arc;
use crate::parameters::Network;
use crate::work::{difficulty::CompactDifficulty, equihash};
use super::*;
use crate::LedgerState;
use chrono::{TimeZone, Utc};
use proptest::{
arbitrary::{any, Arbitrary},
prelude::*,
};
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);
(any::<Header>(), transactions_strategy)
.prop_map(|(header, transactions)| Self {
header,
transactions,
})
.boxed()
}
type Strategy = BoxedStrategy<Self>;
}
impl Block {
pub fn partial_chain_strategy(
init: LedgerState,
count: usize,
) -> BoxedStrategy<Vec<Arc<Self>>> {
let mut current = init;
let mut vec = Vec::with_capacity(count);
for _ in 0..count {
vec.push(Block::arbitrary_with(current).prop_map(Arc::new));
current.tip_height.0 += 1;
}
vec.boxed()
}
}
impl Arbitrary for RootHash {
type Parameters = ();
@ -35,7 +71,6 @@ impl Arbitrary for Header {
any::<[u8; 32]>(),
// time is interpreted as u32 in the spec, but rust timestamps are i64
(0i64..(u32::MAX as i64)),
any::<CompactDifficulty>(),
any::<[u8; 32]>(),
any::<equihash::Solution>(),
)
@ -46,7 +81,6 @@ impl Arbitrary for Header {
merkle_root_hash,
root_bytes,
timestamp,
difficulty_threshold,
nonce,
solution,
)| Header {
@ -55,7 +89,8 @@ impl Arbitrary for Header {
merkle_root: merkle_root_hash,
root_bytes,
time: Utc.timestamp(timestamp, 0),
difficulty_threshold,
// TODO: replace with `ExpandedDifficulty.to_compact` when that method is implemented
difficulty_threshold: CompactDifficulty(545259519),
nonce,
solution,
},

View File

@ -2,9 +2,10 @@ use std::env;
use std::io::ErrorKind;
use proptest::{arbitrary::any, prelude::*, test_runner::Config};
use zebra_test::prelude::*;
use crate::parameters::Network;
use crate::serialization::{SerializationError, ZcashDeserializeInto, ZcashSerialize};
use crate::{block, parameters::Network, LedgerState};
use super::super::{serialize::MAX_BLOCK_BYTES, *};
@ -88,3 +89,23 @@ proptest! {
}
}
}
#[test]
fn blocks_have_coinbase() -> Result<()> {
zebra_test::init();
let strategy = any::<block::Height>()
.prop_map(|tip_height| LedgerState {
tip_height,
is_coinbase: true,
network: Network::Mainnet,
})
.prop_flat_map(Block::arbitrary_with);
proptest!(|(block in strategy)| {
let has_coinbase = block.coinbase_height().is_some();
prop_assert!(has_coinbase);
});
Ok(())
}

View File

@ -6,7 +6,7 @@
#![doc(html_favicon_url = "https://www.zfnd.org/images/zebra-favicon-128.png")]
#![doc(html_logo_url = "https://www.zfnd.org/images/zebra-icon.png")]
#![doc(html_root_url = "https://doc.zebra.zfnd.org/zebra_chain")]
#![deny(missing_docs)]
// #![deny(missing_docs)]
#![allow(clippy::try_err)]
#[macro_use]
@ -22,3 +22,27 @@ pub mod sprout;
pub mod transaction;
pub mod transparent;
pub mod work;
#[derive(Debug, Clone, Copy)]
#[cfg(any(test, feature = "proptest-impl"))]
pub struct LedgerState {
pub tip_height: block::Height,
pub is_coinbase: bool,
pub network: parameters::Network,
}
#[cfg(any(test, feature = "proptest-impl"))]
impl Default for LedgerState {
fn default() -> Self {
let network = parameters::Network::Mainnet;
let tip_height = parameters::NetworkUpgrade::Sapling
.activation_height(network)
.unwrap();
Self {
tip_height,
is_coinbase: true,
network,
}
}
}

View File

@ -1,8 +1,9 @@
use jubjub::AffinePoint;
use proptest::{arbitrary::any, array, collection::vec, prelude::*};
use crate::primitives::Groth16Proof;
use super::{commitment, keys, note, tree, Output, Spend};
use super::{keys, note, tree, NoteCommitment, Output, Spend, ValueCommitment};
impl Arbitrary for Spend {
type Parameters = ();
@ -10,26 +11,23 @@ impl Arbitrary for Spend {
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
(
any::<tree::Root>(),
any::<commitment::ValueCommitment>(),
any::<note::Nullifier>(),
array::uniform32(any::<u8>()),
any::<Groth16Proof>(),
vec(any::<u8>(), 64),
)
.prop_map(
|(anchor, cv, nullifier, rpk_bytes, proof, sig_bytes)| Self {
anchor,
cv,
nullifier,
rk: redjubjub::VerificationKeyBytes::from(rpk_bytes),
zkproof: proof,
spend_auth_sig: redjubjub::Signature::from({
let mut b = [0u8; 64];
b.copy_from_slice(sig_bytes.as_slice());
b
}),
},
)
.prop_map(|(anchor, nullifier, rpk_bytes, proof, sig_bytes)| Self {
anchor,
cv: ValueCommitment(AffinePoint::identity()),
nullifier,
rk: redjubjub::VerificationKeyBytes::from(rpk_bytes),
zkproof: proof,
spend_auth_sig: redjubjub::Signature::from({
let mut b = [0u8; 64];
b.copy_from_slice(sig_bytes.as_slice());
b
}),
})
.boxed()
}
@ -41,23 +39,18 @@ impl Arbitrary for Output {
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
(
any::<commitment::ValueCommitment>(),
any::<commitment::NoteCommitment>(),
any::<keys::EphemeralPublicKey>(),
any::<note::EncryptedNote>(),
any::<note::WrappedNoteKey>(),
any::<Groth16Proof>(),
)
.prop_map(
|(cv, cm, ephemeral_key, enc_ciphertext, out_ciphertext, zkproof)| Self {
cv,
cm_u: cm.extract_u(),
ephemeral_key,
enc_ciphertext,
out_ciphertext,
zkproof,
},
)
.prop_map(|(enc_ciphertext, out_ciphertext, zkproof)| Self {
cv: ValueCommitment(AffinePoint::identity()),
cm_u: NoteCommitment(AffinePoint::identity()).extract_u(),
ephemeral_key: keys::EphemeralPublicKey(AffinePoint::identity()),
enc_ciphertext,
out_ciphertext,
zkproof,
})
.boxed()
}

View File

@ -1,29 +1 @@
use std::convert::TryFrom;
use proptest::{arbitrary::any, array, prelude::*};
use super::super::commitment;
impl Arbitrary for commitment::NoteCommitment {
type Parameters = ();
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
array::uniform32(any::<u8>())
.prop_filter_map("Valid jubjub::AffinePoint", |b| Self::try_from(b).ok())
.boxed()
}
type Strategy = BoxedStrategy<Self>;
}
impl Arbitrary for commitment::ValueCommitment {
type Parameters = ();
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
array::uniform32(any::<u8>())
.prop_filter_map("Valid jubjub::AffinePoint", |b| Self::try_from(b).ok())
.boxed()
}
type Strategy = BoxedStrategy<Self>;
}

View File

@ -869,7 +869,9 @@ impl FromStr for FullViewingKey {
///
/// https://zips.z.cash/protocol/canopy.pdf#concretesaplingkeyagreement
#[derive(Copy, Clone, Deserialize, PartialEq, Serialize)]
pub struct EphemeralPublicKey(#[serde(with = "serde_helpers::AffinePoint")] jubjub::AffinePoint);
pub struct EphemeralPublicKey(
#[serde(with = "serde_helpers::AffinePoint")] pub jubjub::AffinePoint,
);
impl fmt::Debug for EphemeralPublicKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

View File

@ -1,17 +1 @@
use std::convert::TryFrom;
use proptest::{arbitrary::any, array, prelude::*};
use super::*;
impl Arbitrary for EphemeralPublicKey {
type Parameters = ();
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
array::uniform32(any::<u8>())
.prop_filter_map("Valid jubjub::AffinePoint", |b| Self::try_from(b).ok())
.boxed()
}
type Strategy = BoxedStrategy<Self>;
}

View File

@ -1,10 +1,15 @@
use std::sync::Arc;
use block::Height;
use chrono::{TimeZone, Utc};
use futures::future::Either;
use proptest::{arbitrary::any, array, collection::vec, option, prelude::*};
use crate::LedgerState;
use crate::{
amount::Amount,
block,
parameters::NetworkUpgrade,
primitives::{Bctv14Proof, Groth16Proof, ZkSnarkProof},
sapling, sprout, transparent,
};
@ -13,9 +18,9 @@ use super::{JoinSplitData, LockTime, Memo, ShieldedData, Transaction};
impl Transaction {
/// Generate a proptest strategy for V1 Transactions
pub fn v1_strategy() -> impl Strategy<Value = Self> {
pub fn v1_strategy(ledger_state: LedgerState) -> BoxedStrategy<Self> {
(
vec(any::<transparent::Input>(), 0..10),
transparent::Input::vec_strategy(ledger_state, 10),
vec(any::<transparent::Output>(), 0..10),
any::<LockTime>(),
)
@ -28,9 +33,9 @@ impl Transaction {
}
/// Generate a proptest strategy for V2 Transactions
pub fn v2_strategy() -> impl Strategy<Value = Self> {
pub fn v2_strategy(ledger_state: LedgerState) -> BoxedStrategy<Self> {
(
vec(any::<transparent::Input>(), 0..10),
transparent::Input::vec_strategy(ledger_state, 10),
vec(any::<transparent::Output>(), 0..10),
any::<LockTime>(),
option::of(any::<JoinSplitData<Bctv14Proof>>()),
@ -47,9 +52,9 @@ impl Transaction {
}
/// Generate a proptest strategy for V3 Transactions
pub fn v3_strategy() -> impl Strategy<Value = Self> {
pub fn v3_strategy(ledger_state: LedgerState) -> BoxedStrategy<Self> {
(
vec(any::<transparent::Input>(), 0..10),
transparent::Input::vec_strategy(ledger_state, 10),
vec(any::<transparent::Output>(), 0..10),
any::<LockTime>(),
any::<block::Height>(),
@ -68,9 +73,9 @@ impl Transaction {
}
/// Generate a proptest strategy for V4 Transactions
pub fn v4_strategy() -> impl Strategy<Value = Self> {
pub fn v4_strategy(ledger_state: LedgerState) -> BoxedStrategy<Self> {
(
vec(any::<transparent::Input>(), 0..10),
transparent::Input::vec_strategy(ledger_state, 10),
vec(any::<transparent::Output>(), 0..10),
any::<LockTime>(),
any::<block::Height>(),
@ -99,6 +104,25 @@ impl Transaction {
)
.boxed()
}
pub fn vec_strategy(
mut ledger_state: LedgerState,
len: usize,
) -> BoxedStrategy<Vec<Arc<Self>>> {
let coinbase = Transaction::arbitrary_with(ledger_state).prop_map(Arc::new);
ledger_state.is_coinbase = false;
let remainder = vec(
Transaction::arbitrary_with(ledger_state).prop_map(Arc::new),
len,
);
(coinbase, remainder)
.prop_map(|(first, mut remainder)| {
remainder.insert(0, first);
remainder
})
.boxed()
}
}
impl Arbitrary for Memo {
@ -189,16 +213,28 @@ impl Arbitrary for ShieldedData {
}
impl Arbitrary for Transaction {
type Parameters = ();
type Parameters = LedgerState;
fn arbitrary_with(_args: ()) -> Self::Strategy {
prop_oneof![
Self::v1_strategy(),
Self::v2_strategy(),
Self::v3_strategy(),
Self::v4_strategy()
]
.boxed()
fn arbitrary_with(ledger_state: Self::Parameters) -> Self::Strategy {
let LedgerState {
tip_height,
network,
..
} = ledger_state;
let height = Height(tip_height.0 + 1);
let network_upgrade = NetworkUpgrade::current(network, height);
match network_upgrade {
NetworkUpgrade::Genesis | NetworkUpgrade::BeforeOverwinter => {
Self::v1_strategy(ledger_state)
}
NetworkUpgrade::Overwinter => Self::v2_strategy(ledger_state),
NetworkUpgrade::Sapling => Self::v3_strategy(ledger_state),
NetworkUpgrade::Blossom | NetworkUpgrade::Heartwood | NetworkUpgrade::Canopy => {
Self::v4_strategy(ledger_state)
}
}
}
type Strategy = BoxedStrategy<Self>;

View File

@ -9,11 +9,14 @@ mod serialize;
pub use address::Address;
pub use script::Script;
#[cfg(any(test, feature = "proptest-impl"))]
mod arbitrary;
#[cfg(any(test, feature = "proptest-impl"))]
use proptest_derive::Arbitrary;
#[cfg(any(test, feature = "proptest-impl"))]
mod arbitrary;
#[cfg(test)]
mod prop;
use crate::{
amount::{Amount, NonNegative},
block, transaction,

View File

@ -1,38 +1,44 @@
use proptest::{arbitrary::any, collection::vec, prelude::*};
use crate::block;
use crate::{block, LedgerState};
use super::{CoinbaseData, Input, OutPoint, Script};
impl Arbitrary for Input {
type Parameters = ();
impl Input {
/// Construct a strategy for creating validish vecs of Inputs.
pub fn vec_strategy(ledger_state: LedgerState, max_size: usize) -> BoxedStrategy<Vec<Self>> {
if ledger_state.is_coinbase {
let height = block::Height(ledger_state.tip_height.0 + 1);
Self::arbitrary_with(Some(height))
.prop_map(|input| vec![input])
.boxed()
} else {
vec(Self::arbitrary_with(None), max_size).boxed()
}
}
}
fn arbitrary_with(_args: ()) -> Self::Strategy {
prop_oneof![
impl Arbitrary for Input {
type Parameters = Option<block::Height>;
fn arbitrary_with(height: Self::Parameters) -> Self::Strategy {
if let Some(height) = height {
(vec(any::<u8>(), 0..95), any::<u32>())
.prop_map(move |(data, sequence)| Input::Coinbase {
height,
data: CoinbaseData(data),
sequence,
})
.boxed()
} else {
(any::<OutPoint>(), any::<Script>(), any::<u32>())
.prop_map(|(outpoint, unlock_script, sequence)| {
Input::PrevOut {
outpoint,
unlock_script,
sequence,
}
.prop_map(|(outpoint, unlock_script, sequence)| Input::PrevOut {
outpoint,
unlock_script,
sequence,
})
.boxed(),
(
any::<block::Height>(),
vec(any::<u8>(), 0..95),
any::<u32>()
)
.prop_map(|(height, data, sequence)| {
Input::Coinbase {
height,
data: CoinbaseData(data),
sequence,
}
})
.boxed(),
]
.boxed()
.boxed()
}
}
type Strategy = BoxedStrategy<Self>;

View File

@ -0,0 +1,49 @@
use zebra_test::prelude::*;
use crate::{block, parameters::Network, LedgerState};
use super::Input;
#[test]
fn coinbase_has_height() -> Result<()> {
zebra_test::init();
let strategy =
any::<block::Height>().prop_flat_map(|height| Input::arbitrary_with(Some(height)));
proptest!(|(input in strategy)| {
let is_coinbase = matches!(input, Input::Coinbase { .. });
prop_assert!(is_coinbase);
});
Ok(())
}
#[test]
fn input_coinbase_vecs_only_have_coinbase_input() -> Result<()> {
zebra_test::init();
let max_size = 100;
let strategy = any::<block::Height>()
.prop_map(|tip_height| LedgerState {
tip_height,
is_coinbase: true,
network: Network::Mainnet,
})
.prop_flat_map(|ledger_state| Input::vec_strategy(ledger_state, max_size));
proptest!(|(inputs in strategy)| {
let len = inputs.len();
for (ind, input) in inputs.into_iter().enumerate() {
let is_coinbase = matches!(input, Input::Coinbase { .. });
if ind == 0 {
prop_assert!(is_coinbase);
prop_assert_eq!(1, len);
} else {
prop_assert!(!is_coinbase);
}
}
});
Ok(())
}

View File

@ -20,7 +20,8 @@ use primitive_types::U256;
#[cfg(any(test, feature = "proptest-impl"))]
use proptest_derive::Arbitrary;
#[cfg(tests)]
#[cfg(test)]
mod tests;
/// A 32-bit "compact bits" value, which represents the difficulty threshold for

View File

@ -33,3 +33,4 @@ once_cell = "1.4"
spandoc = "0.2"
tempdir = "0.3.7"
tokio = { version = "0.2.22", features = ["full"] }
proptest = "0.10.1"

View File

@ -4,8 +4,8 @@
#![allow(dead_code)]
use std::{
cmp::Ordering,
collections::BTreeSet,
collections::{BTreeMap, HashMap, HashSet},
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
fmt,
ops::Deref,
sync::Arc,
};
@ -20,7 +20,7 @@ use zebra_chain::{
use crate::service::QueuedBlock;
/// The state of the chains in memory, incuding queued blocks.
#[derive(Debug, Default)]
#[derive(Default)]
pub struct NonFinalizedState {
/// Verified, non-finalized chains.
chain_set: BTreeSet<Chain>,
@ -29,7 +29,7 @@ pub struct NonFinalizedState {
}
/// A queue of blocks, awaiting the arrival of parent blocks.
#[derive(Debug, Default)]
#[derive(Default)]
struct QueuedBlocks {
/// Blocks awaiting their parent blocks for contextual verification.
blocks: HashMap<block::Hash, QueuedBlock>,
@ -57,7 +57,7 @@ impl NonFinalizedState {
}
}
#[derive(Debug, Default, Clone)]
#[derive(Default, Clone)]
struct Chain {
blocks: BTreeMap<block::Height, Arc<Block>>,
height_by_hash: HashMap<block::Hash, block::Height>,
@ -459,3 +459,187 @@ impl Ord for Chain {
}
}
}
#[cfg(test)]
mod tests {
use transaction::Transaction;
use std::{env, mem};
use zebra_chain::serialization::ZcashDeserializeInto;
use zebra_chain::{
parameters::{Network, NetworkUpgrade},
LedgerState,
};
use zebra_test::prelude::*;
use self::assert_eq;
use super::*;
/// Helper trait for constructing "valid" looking chains of blocks
trait FakeChainHelper {
fn make_fake_child(&self) -> Arc<Block>;
}
impl FakeChainHelper for Block {
fn make_fake_child(&self) -> Arc<Block> {
let parent_hash = self.hash();
let mut child = Block::clone(self);
let mut transactions = mem::take(&mut child.transactions);
let mut tx = transactions.remove(0);
let input = match Arc::make_mut(&mut tx) {
Transaction::V1 { inputs, .. } => &mut inputs[0],
Transaction::V2 { inputs, .. } => &mut inputs[0],
Transaction::V3 { inputs, .. } => &mut inputs[0],
Transaction::V4 { inputs, .. } => &mut inputs[0],
};
match input {
transparent::Input::Coinbase { height, .. } => height.0 += 1,
_ => panic!("block must have a coinbase height to create a child"),
}
child.transactions.push(tx);
child.header.previous_block_hash = parent_hash;
Arc::new(child)
}
}
#[test]
fn construct_empty() {
zebra_test::init();
let _chain = Chain::default();
}
#[test]
fn construct_single() -> Result<()> {
zebra_test::init();
let block = zebra_test::vectors::BLOCK_MAINNET_434873_BYTES.zcash_deserialize_into()?;
let mut chain = Chain::default();
chain.push(block);
assert_eq!(1, chain.blocks.len());
Ok(())
}
#[test]
fn construct_many() -> Result<()> {
zebra_test::init();
let mut block: Arc<Block> =
zebra_test::vectors::BLOCK_MAINNET_434873_BYTES.zcash_deserialize_into()?;
let mut blocks = vec![];
while blocks.len() < 100 {
let next_block = block.make_fake_child();
blocks.push(block);
block = next_block;
}
let mut chain = Chain::default();
for block in blocks {
chain.push(block);
}
assert_eq!(100, chain.blocks.len());
Ok(())
}
fn arbitrary_chain(height: block::Height) -> BoxedStrategy<Vec<Arc<Block>>> {
Block::partial_chain_strategy(
LedgerState {
tip_height: height,
is_coinbase: true,
network: Network::Mainnet,
},
100,
)
}
prop_compose! {
fn arbitrary_chain_and_count()
(chain in arbitrary_chain(NetworkUpgrade::Blossom.activation_height(Network::Mainnet).unwrap()))
(count in 1..chain.len(), chain in Just(chain)) -> (NoDebug<Vec<Arc<Block>>>, usize)
{
(NoDebug(chain), count)
}
}
#[test]
fn forked_equals_pushed() -> Result<()> {
zebra_test::init();
proptest!(ProptestConfig::with_cases(env::var("PROPTEST_CASES")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(1)),
|((chain, count) in arbitrary_chain_and_count())| {
let chain = chain.0;
let fork_tip_hash = chain[count - 1].hash();
let mut full_chain = Chain::default();
let mut partial_chain = Chain::default();
for block in chain.iter().take(count) {
partial_chain.push(block.clone());
}
for block in chain {
full_chain.push(block);
}
let forked = full_chain.fork(fork_tip_hash).expect("hash is present");
prop_assert_eq!(forked.blocks.len(), partial_chain.blocks.len());
});
Ok(())
}
#[test]
fn finalized_equals_pushed() -> Result<()> {
zebra_test::init();
proptest!(ProptestConfig::with_cases(env::var("PROPTEST_CASES")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(1)),
|((chain, end_count) in arbitrary_chain_and_count())| {
let chain = chain.0;
let finalized_count = chain.len() - end_count;
let mut full_chain = Chain::default();
let mut partial_chain = Chain::default();
for block in chain.iter().skip(finalized_count) {
partial_chain.push(block.clone());
}
for block in chain {
full_chain.push(block);
}
for _ in 0..finalized_count {
let _finalized = full_chain.pop_root();
}
prop_assert_eq!(full_chain.blocks.len(), partial_chain.blocks.len());
});
Ok(())
}
}
struct NoDebug<T>(T);
impl<T> fmt::Debug for NoDebug<Vec<T>> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}, len={}", std::any::type_name::<T>(), self.0.len())
}
}

View File

@ -72,7 +72,6 @@ pub fn init() {
})
});
}))
.display_env_section(false)
.panic_message(SkipTestReturnedErrPanicMessages)
.install()
.unwrap();