From e4685cc9dba1f1b5a628c90e5c4d4289ab59e50a Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Tue, 19 Jul 2022 21:27:34 +0300 Subject: [PATCH 1/9] Introduce proptest in chainstate --- Cargo.lock | 1 + chainstate/Cargo.toml | 1 + chainstate/src/detail/mod.rs | 2 + .../src/detail/tests/double_spend_tests.rs | 94 ++++++++++++++----- chainstate/src/detail/tests/events_tests.rs | 20 ++-- .../src/detail/tests/processing_tests.rs | 11 ++- chainstate/src/detail/tests/syncing_tests.rs | 84 ++++++++--------- chainstate/src/lib.rs | 9 +- 8 files changed, 134 insertions(+), 88 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 80e00586c4..888e248a76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -523,6 +523,7 @@ dependencies = [ "logging", "mockall", "num", + "proptest", "replace_with", "rpc", "serde", diff --git a/chainstate/Cargo.toml b/chainstate/Cargo.toml index 59e163ef83..18df9979c9 100644 --- a/chainstate/Cargo.toml +++ b/chainstate/Cargo.toml @@ -31,3 +31,4 @@ mockall = "0.11" serde_json = "1.0" static_assertions = "1.1" tokio = "1.19" +proptest = "1.0.0" diff --git a/chainstate/src/detail/mod.rs b/chainstate/src/detail/mod.rs index 22051a9c3d..c19001677f 100644 --- a/chainstate/src/detail/mod.rs +++ b/chainstate/src/detail/mod.rs @@ -72,6 +72,7 @@ pub enum BlockSource { } impl Chainstate { + #[allow(dead_code)] pub fn wait_for_all_events(&self) { self.events_controller.wait_for_all_events(); } @@ -276,6 +277,7 @@ impl Chainstate { self.make_db_tx_ro().get_best_block_id() } + #[allow(dead_code)] pub fn get_header_from_height( &self, height: &BlockHeight, diff --git a/chainstate/src/detail/tests/double_spend_tests.rs b/chainstate/src/detail/tests/double_spend_tests.rs index 34b39b2296..73ab5590ea 100644 --- a/chainstate/src/detail/tests/double_spend_tests.rs +++ b/chainstate/src/detail/tests/double_spend_tests.rs @@ -24,7 +24,10 @@ use common::{ }, primitives::{time, Amount, Id}, }; -use crypto::random::{self, Rng}; +use proptest::prelude::*; + +proptest! { +#![proptest_config(ProptestConfig::with_cases(1))] // Process a block where the second transaction uses the first one as input. // @@ -39,12 +42,12 @@ use crypto::random::{self, Rng}; // | +-------------------+ | // +-----------------------+ #[test] -fn spend_output_in_the_same_block() { - common::concurrency::model(|| { +fn spend_output_in_the_same_block(atoms1 in 100_000..200_000u128, atoms2 in 1000..2000u128) { + common::concurrency::model(move || { let mut chainstate = setup_chainstate(); - let first_tx = tx_from_genesis(&chainstate); - let second_tx = tx_from_tx(&first_tx); + let first_tx = tx_from_genesis(&chainstate, atoms1); + let second_tx = tx_from_tx(&first_tx, atoms2); let block = Block::new( vec![first_tx, second_tx], @@ -80,12 +83,12 @@ fn spend_output_in_the_same_block() { // | +-------------------+ | // +-----------------------+ #[test] -fn spend_output_in_the_same_block_invalid_order() { - common::concurrency::model(|| { +fn spend_output_in_the_same_block_invalid_order(atoms1 in 100_000..200_000u128, atoms2 in 1000..2000u128) { + common::concurrency::model(move || { let mut chainstate = setup_chainstate(); - let first_tx = tx_from_genesis(&chainstate); - let second_tx = tx_from_tx(&first_tx); + let first_tx = tx_from_genesis(&chainstate, atoms1); + let second_tx = tx_from_tx(&first_tx, atoms2); let block = Block::new( vec![second_tx, first_tx], @@ -126,13 +129,13 @@ fn spend_output_in_the_same_block_invalid_order() { // | +-------------------+ | // +-----------------------+ #[test] -fn double_spend_tx_in_the_same_block() { - common::concurrency::model(|| { +fn double_spend_tx_in_the_same_block(atoms1 in 100_000..200_000u128, atoms2 in 1000..2000u128) { + common::concurrency::model(move || { let mut chainstate = setup_chainstate(); - let first_tx = tx_from_genesis(&chainstate); - let second_tx = tx_from_tx(&first_tx); - let third_tx = tx_from_tx(&first_tx); + let first_tx = tx_from_genesis(&chainstate, atoms1); + let second_tx = tx_from_tx(&first_tx, atoms2); + let third_tx = tx_from_tx(&first_tx, atoms2); let block = Block::new( vec![first_tx, second_tx, third_tx], @@ -177,11 +180,11 @@ fn double_spend_tx_in_the_same_block() { // | +-------------------+ | // +-----------------------+ #[test] -fn double_spend_tx_in_another_block() { - common::concurrency::model(|| { +fn double_spend_tx_in_another_block(atoms1 in 100_000..200_000u128) { + common::concurrency::model(move || { let mut chainstate = setup_chainstate(); - let first_tx = tx_from_genesis(&chainstate); + let first_tx = tx_from_genesis(&chainstate, atoms1); let first_block = Block::new( vec![first_tx.clone()], Some(Id::new(chainstate.chain_config.genesis_block_id().get())), @@ -199,7 +202,7 @@ fn double_spend_tx_in_another_block() { Some(first_block_id.clone()) ); - let second_tx = tx_from_genesis(&chainstate); + let second_tx = tx_from_genesis(&chainstate, atoms1); let second_block = Block::new( vec![second_tx], Some(first_block_id.clone()), @@ -224,8 +227,55 @@ fn double_spend_tx_in_another_block() { }); } +// Try to process a block where the second transaction's input is more then first output. +// +// +--Block----------------+ +// | | +// | +-------tx-1--------+ | +// | |input = prev_block | | +// | +-------------------+ | +// | | +// | +-------tx-2--------+ | +// | |input = tx1 | | +// | +-------------------+ | +// +-----------------------+ +#[test] +fn spend_bigger_output_in_the_same_block(atoms1 in 1000..2000u128, atoms2 in 100_000..200_000u128){ + common::concurrency::model(move || { + let mut chainstate = setup_chainstate(); + + let first_tx = tx_from_genesis(&chainstate, atoms1); + let second_tx = tx_from_tx(&first_tx, atoms2); + + let block = Block::new( + vec![first_tx, second_tx], + Some(Id::new(chainstate.chain_config.genesis_block_id().get())), + BlockTimestamp::from_duration_since_epoch(time::get()).unwrap(), + ConsensusData::None, + ) + .expect(ERR_CREATE_BLOCK_FAIL); + + assert_eq!( + chainstate.process_block(block, BlockSource::Local).unwrap_err(), + BlockError::StateUpdateFailed(StateUpdateError::AttemptToPrintMoney( + Amount::from_atoms(atoms1), + Amount::from_atoms(atoms2) + )) + ); + assert_eq!( + chainstate + .chainstate_storage + .get_best_block_id() + .expect(ERR_BEST_BLOCK_NOT_FOUND) + .expect(ERR_STORAGE_FAIL), + chainstate.chain_config.genesis_block_id() + ); + }); +} +} + // Creates a transaction with an input based on the first transaction from the genesis block. -fn tx_from_genesis(chainstate: &Chainstate) -> Transaction { +fn tx_from_genesis(chainstate: &Chainstate, atoms: u128) -> Transaction { let genesis_block_tx_id = chainstate.chain_config.genesis_block().transactions().get(0).unwrap().get_id(); let input = TxInput::new( @@ -234,17 +284,17 @@ fn tx_from_genesis(chainstate: &Chainstate) -> Transaction { empty_witness(), ); let output = TxOutput::new( - Amount::from_atoms(random::make_pseudo_rng().gen_range(100_000..200_000)), + Amount::from_atoms(atoms), OutputPurpose::Transfer(anyonecanspend_address()), ); Transaction::new(0, vec![input], vec![output], 0).expect(ERR_CREATE_TX_FAIL) } // Creates a transaction with an input based on the specified transaction id. -fn tx_from_tx(tx: &Transaction) -> Transaction { +fn tx_from_tx(tx: &Transaction, atoms: u128) -> Transaction { let input = TxInput::new(tx.get_id().into(), 0, InputWitness::NoSignature(None)); let output = TxOutput::new( - Amount::from_atoms(random::make_pseudo_rng().gen_range(1000..2000)), + Amount::from_atoms(atoms), OutputPurpose::Transfer(anyonecanspend_address()), ); Transaction::new(0, vec![input], vec![output], 0).expect(ERR_CREATE_TX_FAIL) diff --git a/chainstate/src/detail/tests/events_tests.rs b/chainstate/src/detail/tests/events_tests.rs index 515d47ba8e..7743d4a8fb 100644 --- a/chainstate/src/detail/tests/events_tests.rs +++ b/chainstate/src/detail/tests/events_tests.rs @@ -19,7 +19,7 @@ use std::sync::Arc; use crate::detail::tests::*; use chainstate_storage::Store; -use crypto::random::{self, Rng}; +use proptest::prelude::*; type ErrorList = Arc>>; @@ -61,14 +61,15 @@ fn simple_subscribe() { }); } +proptest! { +#![proptest_config(ProptestConfig::with_cases(1))] + // Subscribe to events several times, then process a block. #[test] -fn several_subscribers() { - common::concurrency::model(|| { +fn several_subscribers(subscribers in 8..256usize) { + common::concurrency::model(move || { let mut chainstate = setup_chainstate(); - let mut rng = random::make_pseudo_rng(); - let subscribers = rng.gen_range(8..256); let events = subscribe(&mut chainstate, subscribers); let block = produce_test_block(chainstate.chain_config.genesis_block(), false); @@ -86,14 +87,10 @@ fn several_subscribers() { } #[test] -fn several_subscribers_several_events() { - common::concurrency::model(|| { +fn several_subscribers_several_events(subscribers in 4..16usize, blocks in 8..128usize) { + common::concurrency::model(move || { let mut chainstate = setup_chainstate(); - let mut rng = random::make_pseudo_rng(); - let subscribers = rng.gen_range(4..16); - let blocks = rng.gen_range(8..128); - let events = subscribe(&mut chainstate, subscribers); assert!(!chainstate.events_controller.subscribers().is_empty()); @@ -115,6 +112,7 @@ fn several_subscribers_several_events() { assert_eq!(blocks * subscribers, events.lock().unwrap().len()); }); } +} // An orphan block is rejected during processing, so it shouldn't trigger the new tip event. #[test] diff --git a/chainstate/src/detail/tests/processing_tests.rs b/chainstate/src/detail/tests/processing_tests.rs index b25bd4582e..3d24bad2eb 100644 --- a/chainstate/src/detail/tests/processing_tests.rs +++ b/chainstate/src/detail/tests/processing_tests.rs @@ -36,7 +36,7 @@ use common::{ Uint256, }; use crypto::key::{KeyKind, PrivateKey}; -use crypto::random::{self, Rng}; +use proptest::prelude::*; // Check that the genesis block cannot have the `Peer` source. #[test] @@ -238,10 +238,12 @@ fn spend_inputs_simple() { }); } +proptest! { +#![proptest_config(ProptestConfig::with_cases(1))] // Produce and process some blocks. #[test] -fn straight_chain() { - common::concurrency::model(|| { +fn straight_chain(i in 100..200) { + common::concurrency::model(move || { let chain_config = Arc::new(create_unit_test_config()); let chainstate_config = ChainstateConfig::new(); let storage = Store::new_empty().unwrap(); @@ -279,7 +281,7 @@ fn straight_chain() { let mut prev_block = chainstate.chain_config.genesis_block().clone(); let mut prev_block_index = genesis_index; - for _ in 0..random::make_pseudo_rng().gen_range(100..200) { + for _ in 0..i { assert_eq!( chainstate.chainstate_storage.get_best_block_id().ok().flatten().unwrap(), prev_block.get_id() @@ -304,6 +306,7 @@ fn straight_chain() { } }); } +} #[test] fn get_ancestor_invalid_height() { diff --git a/chainstate/src/detail/tests/syncing_tests.rs b/chainstate/src/detail/tests/syncing_tests.rs index 456891af6f..dae70164a8 100644 --- a/chainstate/src/detail/tests/syncing_tests.rs +++ b/chainstate/src/detail/tests/syncing_tests.rs @@ -19,12 +19,15 @@ use std::iter; use crate::detail::tests::{test_framework::BlockTestFramework, *}; use chainstate_storage::BlockchainStorageRead; -use crypto::random::{self, Rng}; +use proptest::prelude::*; + +proptest! { +#![proptest_config(ProptestConfig::with_cases(1))] // Generate some blocks and check that a locator is of expected length. #[test] -fn get_locator() { - common::concurrency::model(|| { +fn get_locator(new_blocks in 1..2000usize) { + common::concurrency::model(move || { let mut btf = BlockTestFramework::new(); // There is only one (genesis) block. @@ -33,11 +36,9 @@ fn get_locator() { assert_eq!(btf.genesis().header(), &locator[0]); // Expand the chain several times. - let mut rng = random::make_pseudo_rng(); let mut blocks = 1; let mut last_block = btf.genesis().clone(); for _ in 0..8 { - let new_blocks = rng.gen_range(1..2000); last_block = btf.create_chain(&last_block.get_id(), new_blocks).unwrap(); blocks += new_blocks; @@ -64,14 +65,13 @@ fn get_locator() { // Check that new blocks (produced after a locator is created) are returned. #[test] -fn get_headers() { - common::concurrency::model(|| { - let mut rng = random::make_pseudo_rng(); - let header_limit = i64::from(HEADER_LIMIT).try_into().unwrap(); - +fn get_headers(initial_blocks in 1000usize..2000, + blocks in 1000usize..i64::from(HEADER_LIMIT).try_into().unwrap()) { + common::concurrency::model(move || { + let header_limit:usize = i64::from(HEADER_LIMIT).try_into().unwrap(); let mut btf = BlockTestFramework::new(); let mut last_block = btf.genesis().clone(); - last_block = btf.create_chain(&last_block.get_id(), rng.gen_range(1000..2000)).unwrap(); + last_block = btf.create_chain(&last_block.get_id(), initial_blocks).unwrap(); // The locator is from this exact chain, so `get_headers` should return an empty sequence. let locator = btf.chainstate().get_locator().unwrap(); @@ -90,7 +90,7 @@ fn get_headers() { .unwrap(); Some(last_block.header().clone()) }) - .take(rng.gen_range(1000..header_limit)) + .take(blocks) .collect(); let headers = btf.chainstate().get_headers(locator.clone()).unwrap(); @@ -100,7 +100,7 @@ fn get_headers() { assert_eq!(expected[0].prev_block_id(), &Some(locator[0].get_id())); // Produce more blocks than `HEADER_LIMIT`, so get_headers is truncated. - btf.create_chain(&last_block.get_id(), header_limit - expected.len()).unwrap(); + btf.create_chain(&last_block.get_id(), header_limit- expected.len()).unwrap(); let headers = btf.chainstate().get_headers(locator).unwrap(); assert_eq!(headers.len(), header_limit); }); @@ -109,42 +109,38 @@ fn get_headers() { // Create two chains that only share the genesis block and verify that the header is attached to // the genesis. #[test] -fn get_headers_genesis() { - common::concurrency::model(|| { - let mut rng = random::make_pseudo_rng(); - +fn get_headers_genesis(chain1_length in 64..128usize, chain2_length in 1000..2000usize) { + common::concurrency::model(move || { let mut btf = BlockTestFramework::new(); let genesis = btf.genesis().clone(); - btf.create_chain(&genesis.get_id(), rng.gen_range(64..128)).unwrap(); + btf.create_chain(&genesis.get_id(), chain1_length).unwrap(); let locator_1 = btf.chainstate.get_locator().unwrap(); - let chain_length = rng.gen_range(1200..2000); - btf.create_chain(&genesis.get_id(), chain_length).unwrap(); + btf.create_chain(&genesis.get_id(), chain2_length).unwrap(); let locator_2 = btf.chainstate.get_locator().unwrap(); assert_ne!(locator_1, locator_2); assert!(locator_1.len() < locator_2.len()); let headers = btf.chainstate.get_headers(locator_1).unwrap(); assert_eq!(headers[0].prev_block_id(), &Some(genesis.get_id())); - assert_eq!(headers.len(), chain_length); + assert_eq!(headers.len(), chain2_length); }); } // Create two chains that branch at some point, both with some unique blocks. Verify that the first // returned header is attached to a block that is known to both chains. #[test] -fn get_headers_branching_chains() { - common::concurrency::model(|| { - let mut rng = random::make_pseudo_rng(); - let common_height = rng.gen_range(100..10_000); - +fn get_headers_branching_chains(common_height in 100..10_000usize, + chain1_length in 100..2500usize, + chain2_length in 2500..5000usize) { + common::concurrency::model(move || { let mut btf = BlockTestFramework::new(); let common_block = btf.create_chain(&btf.genesis().get_id(), common_height).unwrap(); - btf.create_chain(&common_block.get_id(), rng.gen_range(100..2500)).unwrap(); + btf.create_chain(&common_block.get_id(), chain1_length).unwrap(); let locator = btf.chainstate.get_locator().unwrap(); - btf.create_chain(&common_block.get_id(), rng.gen_range(2500..5000)).unwrap(); + btf.create_chain(&common_block.get_id(), chain2_length).unwrap(); let headers = btf.chainstate.get_headers(locator).unwrap(); let id = headers[0].prev_block_id().as_ref().unwrap(); @@ -155,16 +151,14 @@ fn get_headers_branching_chains() { // Create two separate chains that share some blocks. Verify that the first returned header is // attached to some block known for both chains. #[test] -fn get_headers_different_chains() { - common::concurrency::model(|| { - let mut rng = random::make_pseudo_rng(); - +fn get_headers_different_chains(i in 100..250, chain1_length in 32..256usize, chain2_length in 256..512usize) { + common::concurrency::model(move || { let mut btf1 = BlockTestFramework::new(); let mut btf2 = BlockTestFramework::new(); let mut prev = btf1.genesis().clone(); assert_eq!(&prev, btf2.genesis()); - for _ in 0..rng.gen_range(100..250) { + for _ in 0..i { prev = btf1.random_block(&prev, None); btf1.add_special_block(prev.clone()).unwrap(); btf2.add_special_block(prev.clone()).unwrap(); @@ -174,8 +168,8 @@ fn get_headers_different_chains() { ); } - btf1.create_chain(&prev.get_id(), rng.gen_range(32..256)).unwrap(); - btf2.create_chain(&prev.get_id(), rng.gen_range(256..512)).unwrap(); + btf1.create_chain(&prev.get_id(), chain1_length).unwrap(); + btf2.create_chain(&prev.get_id(), chain2_length).unwrap(); let locator = btf1.chainstate.get_locator().unwrap(); let headers = btf2.chainstate.get_headers(locator).unwrap(); @@ -190,15 +184,13 @@ fn get_headers_different_chains() { } #[test] -fn filter_already_existing_blocks() { - common::concurrency::model(|| { - let mut rng = random::make_pseudo_rng(); - +fn filter_already_existing_blocks(special_blocks in 8..16, limit in 32..256) { + common::concurrency::model(move || { let mut btf1 = BlockTestFramework::new(); let mut btf2 = BlockTestFramework::new(); let mut prev1 = btf1.genesis().clone(); - for _ in 0..rng.gen_range(8..16) { + for _ in 0..special_blocks { prev1 = btf1.random_block(&prev1, None); btf1.add_special_block(prev1.clone()).unwrap(); btf2.add_special_block(prev1.clone()).unwrap(); @@ -208,7 +200,6 @@ fn filter_already_existing_blocks() { ); } - let limit = rng.gen_range(32..256); let mut prev2 = prev1.clone(); let mut headers1 = vec![]; let mut headers2 = vec![]; @@ -243,15 +234,13 @@ fn filter_already_existing_blocks() { // Try to use headers that aren't attached to the chain. #[test] -fn filter_already_existing_blocks_detached_headers() { - common::concurrency::model(|| { - let mut rng = random::make_pseudo_rng(); - +fn filter_already_existing_blocks_detached_headers(special_blocks in 8..16, headers_limit in 3..10) { + common::concurrency::model(move || { let mut btf1 = BlockTestFramework::new(); let mut btf2 = BlockTestFramework::new(); let mut prev = btf1.genesis().clone(); - for _ in 0..rng.gen_range(8..16) { + for _ in 0..special_blocks { prev = btf1.random_block(&prev, None); btf1.add_special_block(prev.clone()).unwrap(); btf2.add_special_block(prev.clone()).unwrap(); @@ -261,7 +250,7 @@ fn filter_already_existing_blocks_detached_headers() { ); } - let headers = (0..rng.gen_range(3..10)) + let headers = (0..headers_limit) .map(|_| { prev = btf2.random_block(&prev, None); btf2.add_special_block(prev.clone()).unwrap(); @@ -279,3 +268,4 @@ fn filter_already_existing_blocks_detached_headers() { ); }); } +} diff --git a/chainstate/src/lib.rs b/chainstate/src/lib.rs index b599fa750c..55606eef67 100644 --- a/chainstate/src/lib.rs +++ b/chainstate/src/lib.rs @@ -18,24 +18,25 @@ mod config; mod detail; +mod chainstate_interface_impl; + pub mod chainstate_interface; -pub mod chainstate_interface_impl; pub mod rpc; pub use crate::{ - chainstate_interface_impl::ChainstateInterfaceImpl, config::ChainstateConfig, - detail::{ban_score, BlockError, BlockSource, Chainstate}, + detail::{ban_score, BlockError, BlockSource}, }; use std::sync::Arc; use chainstate_interface::ChainstateInterface; +use chainstate_interface_impl::ChainstateInterfaceImpl; use common::{ chain::{block::Block, ChainConfig}, primitives::{BlockHeight, Id}, }; -use detail::{time_getter::TimeGetter, PropertyQueryError}; +use detail::{time_getter::TimeGetter, Chainstate, PropertyQueryError}; #[derive(Debug, Clone)] pub enum ChainstateEvent { From b1aedcacc46eda55fea764fd04d41d8307f42a7b Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Thu, 21 Jul 2022 11:34:37 +0300 Subject: [PATCH 2/9] Fix review comments --- chainstate/Cargo.toml | 2 +- chainstate/src/chainstate_interface_impl.rs | 4 +- chainstate/src/detail/mod.rs | 34 ++++++++-------- .../src/detail/tests/double_spend_tests.rs | 34 ++++++++-------- .../src/detail/tests/processing_tests.rs | 4 +- chainstate/src/detail/tests/syncing_tests.rs | 10 ++--- chainstate/src/detail/tests/test_framework.rs | 40 ++++++++++++------- 7 files changed, 69 insertions(+), 59 deletions(-) diff --git a/chainstate/Cargo.toml b/chainstate/Cargo.toml index 18df9979c9..251d864cce 100644 --- a/chainstate/Cargo.toml +++ b/chainstate/Cargo.toml @@ -31,4 +31,4 @@ mockall = "0.11" serde_json = "1.0" static_assertions = "1.1" tokio = "1.19" -proptest = "1.0.0" +proptest = "1.0" diff --git a/chainstate/src/chainstate_interface_impl.rs b/chainstate/src/chainstate_interface_impl.rs index 5d4ef29ae7..88425ea390 100644 --- a/chainstate/src/chainstate_interface_impl.rs +++ b/chainstate/src/chainstate_interface_impl.rs @@ -24,12 +24,12 @@ use crate::{ ChainstateError, ChainstateEvent, ChainstateInterface, }; -pub struct ChainstateInterfaceImpl { +pub(crate) struct ChainstateInterfaceImpl { chainstate: detail::Chainstate, } impl ChainstateInterfaceImpl { - pub fn new(chainstate: detail::Chainstate) -> Self { + pub(crate) fn new(chainstate: detail::Chainstate) -> Self { Self { chainstate } } } diff --git a/chainstate/src/detail/mod.rs b/chainstate/src/detail/mod.rs index c19001677f..accbf6844d 100644 --- a/chainstate/src/detail/mod.rs +++ b/chainstate/src/detail/mod.rs @@ -55,7 +55,7 @@ pub mod time_getter; use time_getter::TimeGetter; #[must_use] -pub struct Chainstate { +pub(crate) struct Chainstate { chain_config: Arc, chainstate_config: ChainstateConfig, chainstate_storage: chainstate_storage::Store, @@ -73,7 +73,7 @@ pub enum BlockSource { impl Chainstate { #[allow(dead_code)] - pub fn wait_for_all_events(&self) { + pub(crate) fn wait_for_all_events(&self) { self.events_controller.wait_for_all_events(); } @@ -101,11 +101,11 @@ impl Chainstate { ) } - pub fn subscribe_to_events(&mut self, handler: ChainstateEventHandler) { + pub(crate) fn subscribe_to_events(&mut self, handler: ChainstateEventHandler) { self.events_controller.subscribe_to_events(handler); } - pub fn new( + pub(crate) fn new( chain_config: Arc, chainstate_config: ChainstateConfig, chainstate_storage: chainstate_storage::Store, @@ -208,7 +208,7 @@ impl Chainstate { } } - pub fn attempt_to_process_block( + pub(crate) fn attempt_to_process_block( &mut self, block: Block, block_source: BlockSource, @@ -259,7 +259,7 @@ impl Chainstate { } /// returns the block index of the new tip - pub fn process_block( + pub(crate) fn process_block( &mut self, block: Block, block_source: BlockSource, @@ -267,47 +267,47 @@ impl Chainstate { self.attempt_to_process_block(block, block_source, 0) } - pub fn preliminary_block_check(&self, block: Block) -> Result { + pub(crate) fn preliminary_block_check(&self, block: Block) -> Result { let chainstate_ref = self.make_db_tx_ro(); chainstate_ref.check_block(&block)?; Ok(block) } - pub fn get_best_block_id(&self) -> Result>, PropertyQueryError> { + pub(crate) fn get_best_block_id(&self) -> Result>, PropertyQueryError> { self.make_db_tx_ro().get_best_block_id() } #[allow(dead_code)] - pub fn get_header_from_height( + pub(crate) fn get_header_from_height( &self, height: &BlockHeight, ) -> Result, PropertyQueryError> { self.make_db_tx_ro().get_header_from_height(height) } - pub fn get_block_id_from_height( + pub(crate) fn get_block_id_from_height( &self, height: &BlockHeight, ) -> Result>, PropertyQueryError> { self.make_db_tx_ro().get_block_id_by_height(height) } - pub fn get_block(&self, id: Id) -> Result, PropertyQueryError> { + pub(crate) fn get_block(&self, id: Id) -> Result, PropertyQueryError> { self.make_db_tx_ro().get_block(id) } - pub fn get_block_index( + pub(crate) fn get_block_index( &self, id: &Id, ) -> Result, PropertyQueryError> { self.make_db_tx_ro().get_block_index(id) } - pub fn get_best_block_index(&self) -> Result, PropertyQueryError> { + pub(crate) fn get_best_block_index(&self) -> Result, PropertyQueryError> { self.make_db_tx_ro().get_best_block_index() } - pub fn get_locator(&self) -> Result, PropertyQueryError> { + pub(crate) fn get_locator(&self) -> Result, PropertyQueryError> { let chainstate_ref = self.make_db_tx_ro(); let best_block_index = chainstate_ref .get_best_block_index()? @@ -325,14 +325,14 @@ impl Chainstate { itertools::process_results(headers, |iter| iter.flatten().collect::>()) } - pub fn get_block_height_in_main_chain( + pub(crate) fn get_block_height_in_main_chain( &self, id: &Id, ) -> Result, PropertyQueryError> { self.make_db_tx_ro().get_block_height_in_main_chain(id) } - pub fn get_headers( + pub(crate) fn get_headers( &self, locator: Vec, ) -> Result, PropertyQueryError> { @@ -366,7 +366,7 @@ impl Chainstate { itertools::process_results(headers, |iter| iter.flatten().collect::>()) } - pub fn filter_already_existing_blocks( + pub(crate) fn filter_already_existing_blocks( &self, headers: Vec, ) -> Result, PropertyQueryError> { diff --git a/chainstate/src/detail/tests/double_spend_tests.rs b/chainstate/src/detail/tests/double_spend_tests.rs index 73ab5590ea..758b492450 100644 --- a/chainstate/src/detail/tests/double_spend_tests.rs +++ b/chainstate/src/detail/tests/double_spend_tests.rs @@ -42,11 +42,11 @@ proptest! { // | +-------------------+ | // +-----------------------+ #[test] -fn spend_output_in_the_same_block(atoms1 in 100_000..200_000u128, atoms2 in 1000..2000u128) { +fn spend_output_in_the_same_block(tx1_output_value in 100_000..200_000u128, atoms2 in 1000..2000u128) { common::concurrency::model(move || { let mut chainstate = setup_chainstate(); - let first_tx = tx_from_genesis(&chainstate, atoms1); + let first_tx = tx_from_genesis(&chainstate, tx1_output_value); let second_tx = tx_from_tx(&first_tx, atoms2); let block = Block::new( @@ -83,11 +83,11 @@ fn spend_output_in_the_same_block(atoms1 in 100_000..200_000u128, atoms2 in 1000 // | +-------------------+ | // +-----------------------+ #[test] -fn spend_output_in_the_same_block_invalid_order(atoms1 in 100_000..200_000u128, atoms2 in 1000..2000u128) { +fn spend_output_in_the_same_block_invalid_order(tx1_output_value in 100_000..200_000u128, atoms2 in 1000..2000u128) { common::concurrency::model(move || { let mut chainstate = setup_chainstate(); - let first_tx = tx_from_genesis(&chainstate, atoms1); + let first_tx = tx_from_genesis(&chainstate, tx1_output_value); let second_tx = tx_from_tx(&first_tx, atoms2); let block = Block::new( @@ -129,11 +129,11 @@ fn spend_output_in_the_same_block_invalid_order(atoms1 in 100_000..200_000u128, // | +-------------------+ | // +-----------------------+ #[test] -fn double_spend_tx_in_the_same_block(atoms1 in 100_000..200_000u128, atoms2 in 1000..2000u128) { +fn double_spend_tx_in_the_same_block(tx1_output_value in 100_000..200_000u128, atoms2 in 1000..2000u128) { common::concurrency::model(move || { let mut chainstate = setup_chainstate(); - let first_tx = tx_from_genesis(&chainstate, atoms1); + let first_tx = tx_from_genesis(&chainstate, tx1_output_value); let second_tx = tx_from_tx(&first_tx, atoms2); let third_tx = tx_from_tx(&first_tx, atoms2); @@ -180,13 +180,13 @@ fn double_spend_tx_in_the_same_block(atoms1 in 100_000..200_000u128, atoms2 in 1 // | +-------------------+ | // +-----------------------+ #[test] -fn double_spend_tx_in_another_block(atoms1 in 100_000..200_000u128) { +fn double_spend_tx_in_another_block(tx1_output_value in 100_000..200_000u128) { common::concurrency::model(move || { let mut chainstate = setup_chainstate(); - let first_tx = tx_from_genesis(&chainstate, atoms1); + let first_tx = tx_from_genesis(&chainstate, tx1_output_value); let first_block = Block::new( - vec![first_tx.clone()], + vec![first_tx.clone()], Some(Id::new(chainstate.chain_config.genesis_block_id().get())), BlockTimestamp::from_duration_since_epoch(time::get()).unwrap(), ConsensusData::None, @@ -202,7 +202,7 @@ fn double_spend_tx_in_another_block(atoms1 in 100_000..200_000u128) { Some(first_block_id.clone()) ); - let second_tx = tx_from_genesis(&chainstate, atoms1); + let second_tx = tx_from_genesis(&chainstate, tx1_output_value); let second_block = Block::new( vec![second_tx], Some(first_block_id.clone()), @@ -240,11 +240,11 @@ fn double_spend_tx_in_another_block(atoms1 in 100_000..200_000u128) { // | +-------------------+ | // +-----------------------+ #[test] -fn spend_bigger_output_in_the_same_block(atoms1 in 1000..2000u128, atoms2 in 100_000..200_000u128){ +fn spend_bigger_output_in_the_same_block(tx1_output_value in 1000..2000u128, atoms2 in 100_000..200_000u128){ common::concurrency::model(move || { let mut chainstate = setup_chainstate(); - let first_tx = tx_from_genesis(&chainstate, atoms1); + let first_tx = tx_from_genesis(&chainstate, tx1_output_value); let second_tx = tx_from_tx(&first_tx, atoms2); let block = Block::new( @@ -258,7 +258,7 @@ fn spend_bigger_output_in_the_same_block(atoms1 in 1000..2000u128, atoms2 in 100 assert_eq!( chainstate.process_block(block, BlockSource::Local).unwrap_err(), BlockError::StateUpdateFailed(StateUpdateError::AttemptToPrintMoney( - Amount::from_atoms(atoms1), + Amount::from_atoms(tx1_output_value), Amount::from_atoms(atoms2) )) ); @@ -275,7 +275,7 @@ fn spend_bigger_output_in_the_same_block(atoms1 in 1000..2000u128, atoms2 in 100 } // Creates a transaction with an input based on the first transaction from the genesis block. -fn tx_from_genesis(chainstate: &Chainstate, atoms: u128) -> Transaction { +fn tx_from_genesis(chainstate: &Chainstate, output_value: u128) -> Transaction { let genesis_block_tx_id = chainstate.chain_config.genesis_block().transactions().get(0).unwrap().get_id(); let input = TxInput::new( @@ -284,17 +284,17 @@ fn tx_from_genesis(chainstate: &Chainstate, atoms: u128) -> Transaction { empty_witness(), ); let output = TxOutput::new( - Amount::from_atoms(atoms), + Amount::from_atoms(output_value), OutputPurpose::Transfer(anyonecanspend_address()), ); Transaction::new(0, vec![input], vec![output], 0).expect(ERR_CREATE_TX_FAIL) } // Creates a transaction with an input based on the specified transaction id. -fn tx_from_tx(tx: &Transaction, atoms: u128) -> Transaction { +fn tx_from_tx(tx: &Transaction, output_value: u128) -> Transaction { let input = TxInput::new(tx.get_id().into(), 0, InputWitness::NoSignature(None)); let output = TxOutput::new( - Amount::from_atoms(atoms), + Amount::from_atoms(output_value), OutputPurpose::Transfer(anyonecanspend_address()), ); Transaction::new(0, vec![input], vec![output], 0).expect(ERR_CREATE_TX_FAIL) diff --git a/chainstate/src/detail/tests/processing_tests.rs b/chainstate/src/detail/tests/processing_tests.rs index 3d24bad2eb..d763655e9e 100644 --- a/chainstate/src/detail/tests/processing_tests.rs +++ b/chainstate/src/detail/tests/processing_tests.rs @@ -242,7 +242,7 @@ proptest! { #![proptest_config(ProptestConfig::with_cases(1))] // Produce and process some blocks. #[test] -fn straight_chain(i in 100..200) { +fn straight_chain(blocks_count in 100..200) { common::concurrency::model(move || { let chain_config = Arc::new(create_unit_test_config()); let chainstate_config = ChainstateConfig::new(); @@ -281,7 +281,7 @@ fn straight_chain(i in 100..200) { let mut prev_block = chainstate.chain_config.genesis_block().clone(); let mut prev_block_index = genesis_index; - for _ in 0..i { + for _ in 0..blocks_count { assert_eq!( chainstate.chainstate_storage.get_best_block_id().ok().flatten().unwrap(), prev_block.get_id() diff --git a/chainstate/src/detail/tests/syncing_tests.rs b/chainstate/src/detail/tests/syncing_tests.rs index dae70164a8..f550295b93 100644 --- a/chainstate/src/detail/tests/syncing_tests.rs +++ b/chainstate/src/detail/tests/syncing_tests.rs @@ -65,13 +65,13 @@ fn get_locator(new_blocks in 1..2000usize) { // Check that new blocks (produced after a locator is created) are returned. #[test] -fn get_headers(initial_blocks in 1000usize..2000, - blocks in 1000usize..i64::from(HEADER_LIMIT).try_into().unwrap()) { +fn get_headers(initial_blocks_count in 1000usize..2000, + blocks_count in 1000usize..i64::from(HEADER_LIMIT).try_into().unwrap()) { common::concurrency::model(move || { let header_limit:usize = i64::from(HEADER_LIMIT).try_into().unwrap(); let mut btf = BlockTestFramework::new(); let mut last_block = btf.genesis().clone(); - last_block = btf.create_chain(&last_block.get_id(), initial_blocks).unwrap(); + last_block = btf.create_chain(&last_block.get_id(), initial_blocks_count).unwrap(); // The locator is from this exact chain, so `get_headers` should return an empty sequence. let locator = btf.chainstate().get_locator().unwrap(); @@ -90,7 +90,7 @@ fn get_headers(initial_blocks in 1000usize..2000, .unwrap(); Some(last_block.header().clone()) }) - .take(blocks) + .take(blocks_count) .collect(); let headers = btf.chainstate().get_headers(locator.clone()).unwrap(); @@ -100,7 +100,7 @@ fn get_headers(initial_blocks in 1000usize..2000, assert_eq!(expected[0].prev_block_id(), &Some(locator[0].get_id())); // Produce more blocks than `HEADER_LIMIT`, so get_headers is truncated. - btf.create_chain(&last_block.get_id(), header_limit- expected.len()).unwrap(); + btf.create_chain(&last_block.get_id(), header_limit - expected.len()).unwrap(); let headers = btf.chainstate().get_headers(locator).unwrap(); assert_eq!(headers.len(), header_limit); }); diff --git a/chainstate/src/detail/tests/test_framework.rs b/chainstate/src/detail/tests/test_framework.rs index 0aed0859aa..5b5ea81528 100644 --- a/chainstate/src/detail/tests/test_framework.rs +++ b/chainstate/src/detail/tests/test_framework.rs @@ -26,7 +26,7 @@ use common::{ }; #[derive(Debug, Eq, PartialEq)] -pub enum TestSpentStatus { +pub(crate) enum TestSpentStatus { Spent, Unspent, NotInMainchain, @@ -34,7 +34,7 @@ pub enum TestSpentStatus { // TODO: See https://github.com/mintlayer/mintlayer-core/issues/274 for details. #[allow(dead_code)] -pub enum TestBlockParams { +pub(crate) enum TestBlockParams { NoErrors, TxCount(usize), Fee(Amount), @@ -42,13 +42,13 @@ pub enum TestBlockParams { SpendFrom(Id), } -pub struct BlockTestFramework { +pub(crate) struct BlockTestFramework { pub chainstate: Chainstate, pub block_indexes: Vec, } impl BlockTestFramework { - pub fn with_chainstate(chainstate: Chainstate) -> Self { + pub(crate) fn with_chainstate(chainstate: Chainstate) -> Self { let genesis_index = chainstate .chainstate_storage .get_block_index(&chainstate.chain_config.genesis_block_id()) @@ -60,12 +60,16 @@ impl BlockTestFramework { } } - pub fn new() -> Self { + pub(crate) fn new() -> Self { let chainstate = setup_chainstate(); Self::with_chainstate(chainstate) } - pub fn random_block(&self, parent_block: &Block, params: Option<&[TestBlockParams]>) -> Block { + pub(crate) fn random_block( + &self, + parent_block: &Block, + params: Option<&[TestBlockParams]>, + ) -> Block { let (mut inputs, outputs): (Vec, Vec) = parent_block.transactions().iter().flat_map(create_new_outputs).unzip(); @@ -104,16 +108,16 @@ impl BlockTestFramework { .expect(ERR_CREATE_BLOCK_FAIL) } - pub fn genesis(&self) -> &Block { + pub(crate) fn genesis(&self) -> &Block { self.chainstate.chain_config.genesis_block() } - pub fn get_block_index(&self, id: &Id) -> BlockIndex { + pub(crate) fn get_block_index(&self, id: &Id) -> BlockIndex { self.chainstate.chainstate_storage.get_block_index(id).ok().flatten().unwrap() } /// Creates and processes a given amount of blocks. Returns the last produced block. - pub fn create_chain( + pub(crate) fn create_chain( &mut self, parent_block_id: &Id, count_blocks: usize, @@ -133,7 +137,10 @@ impl BlockTestFramework { Ok(block) } - pub fn add_special_block(&mut self, block: Block) -> Result, BlockError> { + pub(crate) fn add_special_block( + &mut self, + block: Block, + ) -> Result, BlockError> { let id = block.get_id(); let block_index = self.chainstate.process_block(block, BlockSource::Local)?; self.block_indexes.push(block_index.clone().unwrap_or_else(|| { @@ -142,7 +149,7 @@ impl BlockTestFramework { Ok(block_index) } - pub fn get_spent_status( + pub(crate) fn get_spent_status( &self, tx_id: &Id, output_index: u32, @@ -181,7 +188,7 @@ impl BlockTestFramework { } } - pub fn test_block( + pub(crate) fn test_block( &self, block_id: &Id, prev_block_id: Option<&Id>, @@ -220,7 +227,7 @@ impl BlockTestFramework { self.check_block_at_height(block_index.block_height().next_height(), next_block_id); } - pub fn is_block_in_main_chain(&self, block_id: &Id) -> bool { + pub(crate) fn is_block_in_main_chain(&self, block_id: &Id) -> bool { let block_index = self .chainstate .chainstate_storage @@ -237,11 +244,14 @@ impl BlockTestFramework { } } - pub fn get_block(&self, block_id: Id) -> Result, PropertyQueryError> { + pub(crate) fn get_block( + &self, + block_id: Id, + ) -> Result, PropertyQueryError> { self.chainstate.get_block(block_id) } - pub fn chainstate(&mut self) -> &mut Chainstate { + pub(crate) fn chainstate(&mut self) -> &mut Chainstate { &mut self.chainstate } } From 70b45e7fdf60200c335b11531705549842037c26 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Thu, 21 Jul 2022 22:58:25 +0300 Subject: [PATCH 3/9] Replace prop test wiht make_seedable_rng --- Cargo.lock | 2 +- chainstate-storage/src/mock.rs | 6 +- chainstate/Cargo.toml | 3 +- .../src/detail/tests/double_spend_tests.rs | 54 +++++++------ chainstate/src/detail/tests/events_tests.rs | 19 ++--- chainstate/src/detail/tests/mod.rs | 16 +++- .../src/detail/tests/processing_tests.rs | 10 +-- chainstate/src/detail/tests/syncing_tests.rs | 81 ++++++++++--------- 8 files changed, 107 insertions(+), 84 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 888e248a76..54c8dc7461 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -523,7 +523,7 @@ dependencies = [ "logging", "mockall", "num", - "proptest", + "rand_chacha 0.3.1", "replace_with", "rpc", "serde", diff --git a/chainstate-storage/src/mock.rs b/chainstate-storage/src/mock.rs index 2e2cb709ef..32a83e2d36 100644 --- a/chainstate-storage/src/mock.rs +++ b/chainstate-storage/src/mock.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! A mock version of the blockchian storage. +//! A mock version of the blockchain storage. use chainstate_types::block_index::BlockIndex; use common::chain::block::Block; @@ -83,7 +83,7 @@ mockall::mock! { } mockall::mock! { - /// A mock object for blockcain storage transaction + /// A mock object for blockchain storage transaction pub StoreTxRo {} impl crate::BlockchainStorageRead for StoreTxRo { @@ -115,7 +115,7 @@ mockall::mock! { } mockall::mock! { - /// A mock object for blockcain storage transaction + /// A mock object for blockchain storage transaction pub StoreTxRw {} impl crate::BlockchainStorageRead for StoreTxRw { diff --git a/chainstate/Cargo.toml b/chainstate/Cargo.toml index 251d864cce..0f89118835 100644 --- a/chainstate/Cargo.toml +++ b/chainstate/Cargo.toml @@ -31,4 +31,5 @@ mockall = "0.11" serde_json = "1.0" static_assertions = "1.1" tokio = "1.19" -proptest = "1.0" +rand_chacha = "0.3.1" +hex = "0.4" diff --git a/chainstate/src/detail/tests/double_spend_tests.rs b/chainstate/src/detail/tests/double_spend_tests.rs index 758b492450..dcffab88b1 100644 --- a/chainstate/src/detail/tests/double_spend_tests.rs +++ b/chainstate/src/detail/tests/double_spend_tests.rs @@ -24,10 +24,6 @@ use common::{ }, primitives::{time, Amount, Id}, }; -use proptest::prelude::*; - -proptest! { -#![proptest_config(ProptestConfig::with_cases(1))] // Process a block where the second transaction uses the first one as input. // @@ -42,12 +38,13 @@ proptest! { // | +-------------------+ | // +-----------------------+ #[test] -fn spend_output_in_the_same_block(tx1_output_value in 100_000..200_000u128, atoms2 in 1000..2000u128) { - common::concurrency::model(move || { +fn spend_output_in_the_same_block() { + common::concurrency::model(|| { let mut chainstate = setup_chainstate(); - let first_tx = tx_from_genesis(&chainstate, tx1_output_value); - let second_tx = tx_from_tx(&first_tx, atoms2); + let mut rng = make_seedable_rng(None); + let first_tx = tx_from_genesis(&chainstate, rng.gen_range(100_000..200_000)); + let second_tx = tx_from_tx(&first_tx, rng.gen_range(1000..2000)); let block = Block::new( vec![first_tx, second_tx], @@ -83,12 +80,13 @@ fn spend_output_in_the_same_block(tx1_output_value in 100_000..200_000u128, atom // | +-------------------+ | // +-----------------------+ #[test] -fn spend_output_in_the_same_block_invalid_order(tx1_output_value in 100_000..200_000u128, atoms2 in 1000..2000u128) { - common::concurrency::model(move || { +fn spend_output_in_the_same_block_invalid_order() { + common::concurrency::model(|| { let mut chainstate = setup_chainstate(); - let first_tx = tx_from_genesis(&chainstate, tx1_output_value); - let second_tx = tx_from_tx(&first_tx, atoms2); + let mut rng = make_seedable_rng(None); + let first_tx = tx_from_genesis(&chainstate, rng.gen_range(100_000..200_000)); + let second_tx = tx_from_tx(&first_tx, rng.gen_range(1000..2000)); let block = Block::new( vec![second_tx, first_tx], @@ -129,13 +127,14 @@ fn spend_output_in_the_same_block_invalid_order(tx1_output_value in 100_000..200 // | +-------------------+ | // +-----------------------+ #[test] -fn double_spend_tx_in_the_same_block(tx1_output_value in 100_000..200_000u128, atoms2 in 1000..2000u128) { - common::concurrency::model(move || { +fn double_spend_tx_in_the_same_block() { + common::concurrency::model(|| { let mut chainstate = setup_chainstate(); - let first_tx = tx_from_genesis(&chainstate, tx1_output_value); - let second_tx = tx_from_tx(&first_tx, atoms2); - let third_tx = tx_from_tx(&first_tx, atoms2); + let mut rng = make_seedable_rng(None); + let first_tx = tx_from_genesis(&chainstate, rng.gen_range(100_000..200_000)); + let second_tx = tx_from_tx(&first_tx, rng.gen_range(1000..2000)); + let third_tx = tx_from_tx(&first_tx, rng.gen_range(1000..2000)); let block = Block::new( vec![first_tx, second_tx, third_tx], @@ -180,13 +179,14 @@ fn double_spend_tx_in_the_same_block(tx1_output_value in 100_000..200_000u128, a // | +-------------------+ | // +-----------------------+ #[test] -fn double_spend_tx_in_another_block(tx1_output_value in 100_000..200_000u128) { - common::concurrency::model(move || { +fn double_spend_tx_in_another_block() { + common::concurrency::model(|| { let mut chainstate = setup_chainstate(); - let first_tx = tx_from_genesis(&chainstate, tx1_output_value); + let mut rng = make_seedable_rng(None); + let first_tx = tx_from_genesis(&chainstate, rng.gen_range(100_000..200_000)); let first_block = Block::new( - vec![first_tx.clone()], + vec![first_tx.clone()], Some(Id::new(chainstate.chain_config.genesis_block_id().get())), BlockTimestamp::from_duration_since_epoch(time::get()).unwrap(), ConsensusData::None, @@ -202,7 +202,7 @@ fn double_spend_tx_in_another_block(tx1_output_value in 100_000..200_000u128) { Some(first_block_id.clone()) ); - let second_tx = tx_from_genesis(&chainstate, tx1_output_value); + let second_tx = tx_from_genesis(&chainstate, rng.gen_range(100_000..200_000)); let second_block = Block::new( vec![second_tx], Some(first_block_id.clone()), @@ -240,12 +240,15 @@ fn double_spend_tx_in_another_block(tx1_output_value in 100_000..200_000u128) { // | +-------------------+ | // +-----------------------+ #[test] -fn spend_bigger_output_in_the_same_block(tx1_output_value in 1000..2000u128, atoms2 in 100_000..200_000u128){ +fn spend_bigger_output_in_the_same_block() { common::concurrency::model(move || { let mut chainstate = setup_chainstate(); + let mut rng = make_seedable_rng(None); + let tx1_output_value = rng.gen_range(1000..2000); + let tx2_output_value = rng.gen_range(100_000..200_000); let first_tx = tx_from_genesis(&chainstate, tx1_output_value); - let second_tx = tx_from_tx(&first_tx, atoms2); + let second_tx = tx_from_tx(&first_tx, tx2_output_value); let block = Block::new( vec![first_tx, second_tx], @@ -259,7 +262,7 @@ fn spend_bigger_output_in_the_same_block(tx1_output_value in 1000..2000u128, ato chainstate.process_block(block, BlockSource::Local).unwrap_err(), BlockError::StateUpdateFailed(StateUpdateError::AttemptToPrintMoney( Amount::from_atoms(tx1_output_value), - Amount::from_atoms(atoms2) + Amount::from_atoms(tx2_output_value) )) ); assert_eq!( @@ -272,7 +275,6 @@ fn spend_bigger_output_in_the_same_block(tx1_output_value in 1000..2000u128, ato ); }); } -} // Creates a transaction with an input based on the first transaction from the genesis block. fn tx_from_genesis(chainstate: &Chainstate, output_value: u128) -> Transaction { diff --git a/chainstate/src/detail/tests/events_tests.rs b/chainstate/src/detail/tests/events_tests.rs index 7743d4a8fb..8e591badfe 100644 --- a/chainstate/src/detail/tests/events_tests.rs +++ b/chainstate/src/detail/tests/events_tests.rs @@ -19,7 +19,6 @@ use std::sync::Arc; use crate::detail::tests::*; use chainstate_storage::Store; -use proptest::prelude::*; type ErrorList = Arc>>; @@ -61,15 +60,14 @@ fn simple_subscribe() { }); } -proptest! { -#![proptest_config(ProptestConfig::with_cases(1))] - // Subscribe to events several times, then process a block. #[test] -fn several_subscribers(subscribers in 8..256usize) { - common::concurrency::model(move || { +fn several_subscribers() { + common::concurrency::model(|| { let mut chainstate = setup_chainstate(); + let mut rng = make_seedable_rng(None); + let subscribers = rng.gen_range(8..256); let events = subscribe(&mut chainstate, subscribers); let block = produce_test_block(chainstate.chain_config.genesis_block(), false); @@ -87,10 +85,14 @@ fn several_subscribers(subscribers in 8..256usize) { } #[test] -fn several_subscribers_several_events(subscribers in 4..16usize, blocks in 8..128usize) { - common::concurrency::model(move || { +fn several_subscribers_several_events() { + common::concurrency::model(|| { let mut chainstate = setup_chainstate(); + let mut rng = make_seedable_rng(None); + let subscribers = rng.gen_range(4..16); + let blocks = rng.gen_range(8..128); + let events = subscribe(&mut chainstate, subscribers); assert!(!chainstate.events_controller.subscribers().is_empty()); @@ -112,7 +114,6 @@ fn several_subscribers_several_events(subscribers in 4..16usize, blocks in 8..12 assert_eq!(blocks * subscribers, events.lock().unwrap().len()); }); } -} // An orphan block is rejected during processing, so it shouldn't trigger the new tip event. #[test] diff --git a/chainstate/src/detail/tests/mod.rs b/chainstate/src/detail/tests/mod.rs index ee748d3a05..e6e8a52a5f 100644 --- a/chainstate/src/detail/tests/mod.rs +++ b/chainstate/src/detail/tests/mod.rs @@ -15,6 +15,7 @@ // // Author(s): S. Afach, A. Sinitsyn, S. Tkach +use std::env; use std::sync::Mutex; use crate::detail::{tests::test_framework::BlockTestFramework, *}; @@ -29,7 +30,9 @@ use common::{ primitives::{time, Amount, Id, H256}, Uint256, }; -use crypto::random::{Rng, SliceRandom}; +use crypto::random::{Rng, SeedableRng, SliceRandom}; +use hex::{FromHex, ToHex}; +use rand_chacha::ChaChaRng; use serialization::Encode; mod double_spend_tests; @@ -58,6 +61,16 @@ fn anyonecanspend_address() -> Destination { Destination::AnyoneCanSpend } +#[must_use] +fn make_seedable_rng(seed_opt: Option<&str>) -> impl Rng { + let seed = match seed_opt { + Some(s) => FromHex::from_hex(s).expect("seed is invalid"), + None => crypto::random::make_true_rng().gen::<[u8; 32]>(), + }; + log::warn!("Seed for the range is: {}", seed.encode_hex_upper::()); + ChaChaRng::from_seed(seed) +} + fn create_utxo_data( tx_id: &Id, index: usize, @@ -83,6 +96,7 @@ fn create_utxo_data( } fn setup_chainstate() -> Chainstate { + logging::init_logging::<&std::path::Path>(None); chainstate_with_config(create_unit_test_config(), ChainstateConfig::new()) } diff --git a/chainstate/src/detail/tests/processing_tests.rs b/chainstate/src/detail/tests/processing_tests.rs index d763655e9e..a624ddb61c 100644 --- a/chainstate/src/detail/tests/processing_tests.rs +++ b/chainstate/src/detail/tests/processing_tests.rs @@ -36,7 +36,6 @@ use common::{ Uint256, }; use crypto::key::{KeyKind, PrivateKey}; -use proptest::prelude::*; // Check that the genesis block cannot have the `Peer` source. #[test] @@ -238,12 +237,10 @@ fn spend_inputs_simple() { }); } -proptest! { -#![proptest_config(ProptestConfig::with_cases(1))] // Produce and process some blocks. #[test] -fn straight_chain(blocks_count in 100..200) { - common::concurrency::model(move || { +fn straight_chain() { + common::concurrency::model(|| { let chain_config = Arc::new(create_unit_test_config()); let chainstate_config = ChainstateConfig::new(); let storage = Store::new_empty().unwrap(); @@ -281,7 +278,7 @@ fn straight_chain(blocks_count in 100..200) { let mut prev_block = chainstate.chain_config.genesis_block().clone(); let mut prev_block_index = genesis_index; - for _ in 0..blocks_count { + for _ in 0..make_seedable_rng(None).gen_range(100..200) { assert_eq!( chainstate.chainstate_storage.get_best_block_id().ok().flatten().unwrap(), prev_block.get_id() @@ -306,7 +303,6 @@ fn straight_chain(blocks_count in 100..200) { } }); } -} #[test] fn get_ancestor_invalid_height() { diff --git a/chainstate/src/detail/tests/syncing_tests.rs b/chainstate/src/detail/tests/syncing_tests.rs index f550295b93..a6fa655a66 100644 --- a/chainstate/src/detail/tests/syncing_tests.rs +++ b/chainstate/src/detail/tests/syncing_tests.rs @@ -19,15 +19,11 @@ use std::iter; use crate::detail::tests::{test_framework::BlockTestFramework, *}; use chainstate_storage::BlockchainStorageRead; -use proptest::prelude::*; - -proptest! { -#![proptest_config(ProptestConfig::with_cases(1))] // Generate some blocks and check that a locator is of expected length. #[test] -fn get_locator(new_blocks in 1..2000usize) { - common::concurrency::model(move || { +fn get_locator() { + common::concurrency::model(|| { let mut btf = BlockTestFramework::new(); // There is only one (genesis) block. @@ -36,9 +32,11 @@ fn get_locator(new_blocks in 1..2000usize) { assert_eq!(btf.genesis().header(), &locator[0]); // Expand the chain several times. + let mut rng = make_seedable_rng(None); let mut blocks = 1; let mut last_block = btf.genesis().clone(); for _ in 0..8 { + let new_blocks = rng.gen_range(1..2000); last_block = btf.create_chain(&last_block.get_id(), new_blocks).unwrap(); blocks += new_blocks; @@ -65,13 +63,14 @@ fn get_locator(new_blocks in 1..2000usize) { // Check that new blocks (produced after a locator is created) are returned. #[test] -fn get_headers(initial_blocks_count in 1000usize..2000, - blocks_count in 1000usize..i64::from(HEADER_LIMIT).try_into().unwrap()) { - common::concurrency::model(move || { - let header_limit:usize = i64::from(HEADER_LIMIT).try_into().unwrap(); +fn get_headers() { + common::concurrency::model(|| { + let mut rng = make_seedable_rng(None); + let header_limit = i64::from(HEADER_LIMIT).try_into().unwrap(); + let mut btf = BlockTestFramework::new(); let mut last_block = btf.genesis().clone(); - last_block = btf.create_chain(&last_block.get_id(), initial_blocks_count).unwrap(); + last_block = btf.create_chain(&last_block.get_id(), rng.gen_range(1000..2000)).unwrap(); // The locator is from this exact chain, so `get_headers` should return an empty sequence. let locator = btf.chainstate().get_locator().unwrap(); @@ -90,7 +89,7 @@ fn get_headers(initial_blocks_count in 1000usize..2000, .unwrap(); Some(last_block.header().clone()) }) - .take(blocks_count) + .take(rng.gen_range(1000..header_limit)) .collect(); let headers = btf.chainstate().get_headers(locator.clone()).unwrap(); @@ -109,38 +108,42 @@ fn get_headers(initial_blocks_count in 1000usize..2000, // Create two chains that only share the genesis block and verify that the header is attached to // the genesis. #[test] -fn get_headers_genesis(chain1_length in 64..128usize, chain2_length in 1000..2000usize) { - common::concurrency::model(move || { +fn get_headers_genesis() { + common::concurrency::model(|| { + let mut rng = make_seedable_rng(None); + let mut btf = BlockTestFramework::new(); let genesis = btf.genesis().clone(); - btf.create_chain(&genesis.get_id(), chain1_length).unwrap(); + btf.create_chain(&genesis.get_id(), rng.gen_range(64..128)).unwrap(); let locator_1 = btf.chainstate.get_locator().unwrap(); - btf.create_chain(&genesis.get_id(), chain2_length).unwrap(); + let chain_length = rng.gen_range(1200..2000); + btf.create_chain(&genesis.get_id(), chain_length).unwrap(); let locator_2 = btf.chainstate.get_locator().unwrap(); assert_ne!(locator_1, locator_2); assert!(locator_1.len() < locator_2.len()); let headers = btf.chainstate.get_headers(locator_1).unwrap(); assert_eq!(headers[0].prev_block_id(), &Some(genesis.get_id())); - assert_eq!(headers.len(), chain2_length); + assert_eq!(headers.len(), chain_length); }); } // Create two chains that branch at some point, both with some unique blocks. Verify that the first // returned header is attached to a block that is known to both chains. #[test] -fn get_headers_branching_chains(common_height in 100..10_000usize, - chain1_length in 100..2500usize, - chain2_length in 2500..5000usize) { - common::concurrency::model(move || { +fn get_headers_branching_chains() { + common::concurrency::model(|| { + let mut rng = make_seedable_rng(None); + let common_height = rng.gen_range(100..10_000); + let mut btf = BlockTestFramework::new(); let common_block = btf.create_chain(&btf.genesis().get_id(), common_height).unwrap(); - btf.create_chain(&common_block.get_id(), chain1_length).unwrap(); + btf.create_chain(&common_block.get_id(), rng.gen_range(100..2500)).unwrap(); let locator = btf.chainstate.get_locator().unwrap(); - btf.create_chain(&common_block.get_id(), chain2_length).unwrap(); + btf.create_chain(&common_block.get_id(), rng.gen_range(2500..5000)).unwrap(); let headers = btf.chainstate.get_headers(locator).unwrap(); let id = headers[0].prev_block_id().as_ref().unwrap(); @@ -151,14 +154,16 @@ fn get_headers_branching_chains(common_height in 100..10_000usize, // Create two separate chains that share some blocks. Verify that the first returned header is // attached to some block known for both chains. #[test] -fn get_headers_different_chains(i in 100..250, chain1_length in 32..256usize, chain2_length in 256..512usize) { - common::concurrency::model(move || { +fn get_headers_different_chains() { + common::concurrency::model(|| { + let mut rng = make_seedable_rng(None); + let mut btf1 = BlockTestFramework::new(); let mut btf2 = BlockTestFramework::new(); let mut prev = btf1.genesis().clone(); assert_eq!(&prev, btf2.genesis()); - for _ in 0..i { + for _ in 0..rng.gen_range(100..250) { prev = btf1.random_block(&prev, None); btf1.add_special_block(prev.clone()).unwrap(); btf2.add_special_block(prev.clone()).unwrap(); @@ -168,8 +173,8 @@ fn get_headers_different_chains(i in 100..250, chain1_length in 32..256usize, ch ); } - btf1.create_chain(&prev.get_id(), chain1_length).unwrap(); - btf2.create_chain(&prev.get_id(), chain2_length).unwrap(); + btf1.create_chain(&prev.get_id(), rng.gen_range(32..256)).unwrap(); + btf2.create_chain(&prev.get_id(), rng.gen_range(256..512)).unwrap(); let locator = btf1.chainstate.get_locator().unwrap(); let headers = btf2.chainstate.get_headers(locator).unwrap(); @@ -184,13 +189,15 @@ fn get_headers_different_chains(i in 100..250, chain1_length in 32..256usize, ch } #[test] -fn filter_already_existing_blocks(special_blocks in 8..16, limit in 32..256) { - common::concurrency::model(move || { +fn filter_already_existing_blocks() { + common::concurrency::model(|| { + let mut rng = make_seedable_rng(None); + let mut btf1 = BlockTestFramework::new(); let mut btf2 = BlockTestFramework::new(); let mut prev1 = btf1.genesis().clone(); - for _ in 0..special_blocks { + for _ in 0..rng.gen_range(8..16) { prev1 = btf1.random_block(&prev1, None); btf1.add_special_block(prev1.clone()).unwrap(); btf2.add_special_block(prev1.clone()).unwrap(); @@ -200,6 +207,7 @@ fn filter_already_existing_blocks(special_blocks in 8..16, limit in 32..256) { ); } + let limit = rng.gen_range(32..256); let mut prev2 = prev1.clone(); let mut headers1 = vec![]; let mut headers2 = vec![]; @@ -234,13 +242,15 @@ fn filter_already_existing_blocks(special_blocks in 8..16, limit in 32..256) { // Try to use headers that aren't attached to the chain. #[test] -fn filter_already_existing_blocks_detached_headers(special_blocks in 8..16, headers_limit in 3..10) { - common::concurrency::model(move || { +fn filter_already_existing_blocks_detached_headers() { + common::concurrency::model(|| { + let mut rng = make_seedable_rng(None); + let mut btf1 = BlockTestFramework::new(); let mut btf2 = BlockTestFramework::new(); let mut prev = btf1.genesis().clone(); - for _ in 0..special_blocks { + for _ in 0..rng.gen_range(8..16) { prev = btf1.random_block(&prev, None); btf1.add_special_block(prev.clone()).unwrap(); btf2.add_special_block(prev.clone()).unwrap(); @@ -250,7 +260,7 @@ fn filter_already_existing_blocks_detached_headers(special_blocks in 8..16, head ); } - let headers = (0..headers_limit) + let headers = (0..rng.gen_range(3..10)) .map(|_| { prev = btf2.random_block(&prev, None); btf2.add_special_block(prev.clone()).unwrap(); @@ -268,4 +278,3 @@ fn filter_already_existing_blocks_detached_headers(special_blocks in 8..16, head ); }); } -} From 5704598f786fb1b912a1ee0c43128b541a1e3e8e Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Fri, 22 Jul 2022 10:41:42 +0300 Subject: [PATCH 4/9] Minor cleanup --- chainstate/src/detail/tests/mod.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/chainstate/src/detail/tests/mod.rs b/chainstate/src/detail/tests/mod.rs index e6e8a52a5f..220b66e029 100644 --- a/chainstate/src/detail/tests/mod.rs +++ b/chainstate/src/detail/tests/mod.rs @@ -15,7 +15,6 @@ // // Author(s): S. Afach, A. Sinitsyn, S. Tkach -use std::env; use std::sync::Mutex; use crate::detail::{tests::test_framework::BlockTestFramework, *}; @@ -50,6 +49,19 @@ const ERR_STORAGE_FAIL: &str = "Storage failure"; const ERR_CREATE_BLOCK_FAIL: &str = "Creating block caused fail"; const ERR_CREATE_TX_FAIL: &str = "Creating tx caused fail"; +#[must_use] +fn make_seedable_rng(seed_opt: Option<&str>) -> impl Rng { + let seed = match seed_opt { + Some(s) => FromHex::from_hex(s).expect("seed is invalid"), + None => crypto::random::make_true_rng().gen::<[u8; 32]>(), + }; + log::warn!( + "Seed for the range is: {}", + seed.encode_hex_upper::() + ); + ChaChaRng::from_seed(seed) +} + fn empty_witness() -> InputWitness { let mut rng = crypto::random::make_pseudo_rng(); let mut msg: Vec = (1..100).collect(); @@ -61,16 +73,6 @@ fn anyonecanspend_address() -> Destination { Destination::AnyoneCanSpend } -#[must_use] -fn make_seedable_rng(seed_opt: Option<&str>) -> impl Rng { - let seed = match seed_opt { - Some(s) => FromHex::from_hex(s).expect("seed is invalid"), - None => crypto::random::make_true_rng().gen::<[u8; 32]>(), - }; - log::warn!("Seed for the range is: {}", seed.encode_hex_upper::()); - ChaChaRng::from_seed(seed) -} - fn create_utxo_data( tx_id: &Id, index: usize, From 92741f8b53750322c6740798d564840748830cce Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Mon, 25 Jul 2022 13:30:19 +0300 Subject: [PATCH 5/9] Introduce test-utils and make_seedable_rng macro --- Cargo.lock | 30 ++++--- Cargo.toml | 1 + chainstate-storage/Cargo.toml | 1 + chainstate-storage/src/store.rs | 19 ++-- chainstate-storage/src/utxo_db.rs | 8 +- chainstate/Cargo.toml | 3 +- chainstate/src/chainstate_interface_impl.rs | 4 +- chainstate/src/detail/mod.rs | 31 +++---- chainstate/src/detail/orphan_blocks/pool.rs | 90 ++++++++++--------- .../src/detail/tests/double_spend_tests.rs | 31 ++++--- chainstate/src/detail/tests/events_tests.rs | 36 +++++--- chainstate/src/detail/tests/mod.rs | 52 +++++------ .../src/detail/tests/processing_tests.rs | 81 ++++++++++++----- chainstate/src/detail/tests/reorgs_tests.rs | 73 +++++++++------ chainstate/src/detail/tests/syncing_tests.rs | 62 +++++++------ chainstate/src/detail/tests/test_framework.rs | 34 ++++--- p2p/Cargo.toml | 4 +- p2p/{test-utils => p2p-test-utils}/Cargo.toml | 2 +- p2p/{test-utils => p2p-test-utils}/src/lib.rs | 0 p2p/src/net/libp2p/backend.rs | 8 +- p2p/src/net/libp2p/tests/frontend.rs | 14 +-- p2p/src/net/libp2p/tests/gossipsub.rs | 2 +- p2p/src/net/libp2p/tests/identify.rs | 9 +- p2p/src/net/libp2p/tests/mdns.rs | 2 +- p2p/src/net/libp2p/tests/ping.rs | 2 +- p2p/src/net/libp2p/tests/swarm.rs | 2 +- p2p/src/swarm/tests/ban.rs | 2 +- p2p/src/swarm/tests/tmp.rs | 2 +- p2p/src/sync/tests/block_response.rs | 10 +-- p2p/src/sync/tests/connection.rs | 2 +- p2p/src/sync/tests/header_response.rs | 12 +-- p2p/src/sync/tests/request_response.rs | 2 +- p2p/tests/ban.rs | 12 +-- p2p/tests/libp2p-gossipsub.rs | 4 +- p2p/tests/libp2p-mdns.rs | 2 +- p2p/tests/sync.rs | 64 ++++++------- test-utils/Cargo.toml | 11 +++ test-utils/src/lib.rs | 16 ++++ test-utils/src/random.rs | 64 +++++++++++++ 39 files changed, 490 insertions(+), 314 deletions(-) rename p2p/{test-utils => p2p-test-utils}/Cargo.toml (96%) rename p2p/{test-utils => p2p-test-utils}/src/lib.rs (100%) create mode 100644 test-utils/Cargo.toml create mode 100644 test-utils/src/lib.rs create mode 100644 test-utils/src/random.rs diff --git a/Cargo.lock b/Cargo.lock index 4029c23032..1b910cad72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -523,7 +523,6 @@ dependencies = [ "logging", "mockall", "num", - "rand_chacha 0.3.1", "replace_with", "rpc", "serde", @@ -531,6 +530,7 @@ dependencies = [ "serialization", "static_assertions", "subsystem", + "test-utils", "thiserror", "tokio", "utils", @@ -548,6 +548,7 @@ dependencies = [ "num-traits", "serialization", "storage", + "test-utils", "thiserror", "utxo", ] @@ -2606,6 +2607,7 @@ dependencies = [ "jsonrpsee", "libp2p", "logging", + "p2p-test-utils", "parity-scale-codec", "portpicker", "rpc", @@ -2613,13 +2615,27 @@ dependencies = [ "serialization", "sscanf", "subsystem", - "test-utils", "thiserror", "tokio", "utils", "void", ] +[[package]] +name = "p2p-test-utils" +version = "0.1.0" +dependencies = [ + "chainstate", + "chainstate-storage", + "common", + "crypto", + "libp2p", + "p2p", + "portpicker", + "subsystem", + "tokio", +] + [[package]] name = "parity-scale-codec" version = "3.1.5" @@ -4068,15 +4084,9 @@ checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" name = "test-utils" version = "0.1.0" dependencies = [ - "chainstate", - "chainstate-storage", - "common", "crypto", - "libp2p", - "p2p", - "portpicker", - "subsystem", - "tokio", + "logging", + "rand_chacha 0.3.1", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 842c82ebb9..45f61105e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ members = [ "utils", # various utilities "utxo", # utxo and related utilities (cache, undo, etc.) "test", # integration tests + "test-utils", # various utilities for tests ] default-members = [ diff --git a/chainstate-storage/Cargo.toml b/chainstate-storage/Cargo.toml index 1517acb959..f8272395d8 100644 --- a/chainstate-storage/Cargo.toml +++ b/chainstate-storage/Cargo.toml @@ -21,6 +21,7 @@ crypto = { path = '../crypto' } itertools = "0.10" mockall = "0.11" num-traits = "0.2" +test-utils = {path = '../test-utils'} [features] mock = [ 'mockall' ] diff --git a/chainstate-storage/src/store.rs b/chainstate-storage/src/store.rs index 0449dfa45d..f43a8b9796 100644 --- a/chainstate-storage/src/store.rs +++ b/chainstate-storage/src/store.rs @@ -412,7 +412,8 @@ pub(crate) mod test { use common::chain::{Destination, OutputPurpose, TxOutput}; use common::primitives::{Amount, H256}; use crypto::key::{KeyKind, PrivateKey}; - use crypto::random::{make_pseudo_rng, Rng}; + use crypto::random::Rng; + use test_utils::{make_seedable_rng, random::*}; use utxo::{BlockUndo, TxUndo}; #[test] @@ -643,9 +644,9 @@ pub(crate) mod test { } /// returns a tuple of utxo and outpoint, for testing. - fn create_rand_utxo(block_height: u64) -> Utxo { + fn create_rand_utxo(rng: &mut impl Rng, block_height: u64) -> Utxo { // just a random value generated, and also a random `is_block_reward` value. - let random_value = make_pseudo_rng().gen_range(0..(u128::MAX - 1)); + let random_value = rng.gen_range(0..(u128::MAX - 1)); let (_, pub_key) = PrivateKey::new(KeyKind::RistrettoSchnorr); let output = TxOutput::new( Amount::from_atoms(random_value), @@ -663,6 +664,7 @@ pub(crate) mod test { /// `max_lim_of_utxos` - sets the maximum limit of utxos of a random TxUndo. /// `max_lim_of_tx_undos` - the maximum limit of TxUndos in the BlockUndo. pub fn create_rand_block_undo( + rng: &mut impl Rng, max_lim_of_utxos: u8, max_lim_of_tx_undos: u8, block_height: BlockHeight, @@ -671,15 +673,15 @@ pub(crate) mod test { let mut block_undo: Vec = vec![]; - let undo_rng = make_pseudo_rng().gen_range(1..max_lim_of_tx_undos); + let undo_rng = rng.gen_range(1..max_lim_of_tx_undos); for _ in 0..undo_rng { let mut tx_undo = vec![]; - let utxo_rng = make_pseudo_rng().gen_range(1..max_lim_of_utxos); + let utxo_rng = rng.gen_range(1..max_lim_of_utxos); for i in 0..utxo_rng { counter += u64::from(i); - tx_undo.push(create_rand_utxo(counter)); + tx_undo.push(create_rand_utxo(rng, counter)); } block_undo.push(TxUndo::new(tx_undo)); @@ -691,7 +693,8 @@ pub(crate) mod test { #[cfg(not(loom))] #[test] fn undo_test() { - let block_undo0 = create_rand_block_undo(10, 5, BlockHeight::new(1)); + let mut rng = make_seedable_rng!(Seed::from_entropy()); + let block_undo0 = create_rand_block_undo(&mut rng, 10, 5, BlockHeight::new(1)); // create id: let id0: Id = Id::new(H256::random()); @@ -710,7 +713,7 @@ pub(crate) mod test { // insert, remove, and reinsert the next block_undo - let block_undo1 = create_rand_block_undo(5, 10, BlockHeight::new(2)); + let block_undo1 = create_rand_block_undo(&mut rng, 5, 10, BlockHeight::new(2)); // create id: let id1: Id = Id::new(H256::random()); diff --git a/chainstate-storage/src/utxo_db.rs b/chainstate-storage/src/utxo_db.rs index c101a10084..46ad6e8e6e 100644 --- a/chainstate-storage/src/utxo_db.rs +++ b/chainstate-storage/src/utxo_db.rs @@ -74,11 +74,12 @@ mod test { use common::chain::{Destination, OutPoint, OutPointSourceId, OutputPurpose, TxOutput}; use common::primitives::{Amount, BlockHeight, H256}; use crypto::key::{KeyKind, PrivateKey}; - use crypto::random::{make_pseudo_rng, Rng}; + use crypto::random::Rng; + use test_utils::{make_seedable_rng, random::*}; fn create_utxo(block_height: u64) -> (Utxo, OutPoint) { // just a random value generated, and also a random `is_block_reward` value. - let random_value = make_pseudo_rng().gen_range(0..u128::MAX); + let random_value = make_seedable_rng!(Seed::from_entropy()).gen_range(0..u128::MAX); let (_, pub_key) = PrivateKey::new(KeyKind::RistrettoSchnorr); let output = TxOutput::new( Amount::from_atoms(random_value), @@ -120,7 +121,8 @@ mod test { ); // undo checking - let undo = create_rand_block_undo(10, 10, BlockHeight::new(10)); + let mut rng = make_seedable_rng!(Seed::from_entropy()); + let undo = create_rand_block_undo(&mut rng, 10, 10, BlockHeight::new(10)); assert!(db_interface.set_undo_data(block_id.clone(), &undo).is_ok()); assert_eq!(db_interface.get_undo_data(block_id.clone()), Ok(Some(undo))); diff --git a/chainstate/Cargo.toml b/chainstate/Cargo.toml index 0f89118835..16d844ddd1 100644 --- a/chainstate/Cargo.toml +++ b/chainstate/Cargo.toml @@ -30,6 +30,5 @@ serde = { version = "1", features = ["derive"] } mockall = "0.11" serde_json = "1.0" static_assertions = "1.1" +test-utils = {path = '../test-utils'} tokio = "1.19" -rand_chacha = "0.3.1" -hex = "0.4" diff --git a/chainstate/src/chainstate_interface_impl.rs b/chainstate/src/chainstate_interface_impl.rs index 083ba1f030..8ae3273c45 100644 --- a/chainstate/src/chainstate_interface_impl.rs +++ b/chainstate/src/chainstate_interface_impl.rs @@ -24,12 +24,12 @@ use crate::{ ChainstateError, ChainstateEvent, ChainstateInterface, Locator, }; -pub(crate) struct ChainstateInterfaceImpl { +pub struct ChainstateInterfaceImpl { chainstate: detail::Chainstate, } impl ChainstateInterfaceImpl { - pub(crate) fn new(chainstate: detail::Chainstate) -> Self { + pub fn new(chainstate: detail::Chainstate) -> Self { Self { chainstate } } } diff --git a/chainstate/src/detail/mod.rs b/chainstate/src/detail/mod.rs index fceb38b45d..a0b86eea31 100644 --- a/chainstate/src/detail/mod.rs +++ b/chainstate/src/detail/mod.rs @@ -60,7 +60,7 @@ pub mod time_getter; use time_getter::TimeGetter; #[must_use] -pub(crate) struct Chainstate { +pub struct Chainstate { chain_config: Arc, chainstate_config: ChainstateConfig, chainstate_storage: chainstate_storage::Store, @@ -78,7 +78,7 @@ pub enum BlockSource { impl Chainstate { #[allow(dead_code)] - pub(crate) fn wait_for_all_events(&self) { + pub fn wait_for_all_events(&self) { self.events_controller.wait_for_all_events(); } @@ -106,11 +106,11 @@ impl Chainstate { ) } - pub(crate) fn subscribe_to_events(&mut self, handler: ChainstateEventHandler) { + pub fn subscribe_to_events(&mut self, handler: ChainstateEventHandler) { self.events_controller.subscribe_to_events(handler); } - pub(crate) fn new( + pub fn new( chain_config: Arc, chainstate_config: ChainstateConfig, chainstate_storage: chainstate_storage::Store, @@ -206,7 +206,7 @@ impl Chainstate { } } - pub(crate) fn attempt_to_process_block( + pub fn attempt_to_process_block( &mut self, block: Block, block_source: BlockSource, @@ -253,7 +253,7 @@ impl Chainstate { } /// returns the block index of the new tip - pub(crate) fn process_block( + pub fn process_block( &mut self, block: Block, block_source: BlockSource, @@ -297,14 +297,14 @@ impl Chainstate { } #[allow(dead_code)] - pub(crate) fn get_header_from_height( + pub fn get_header_from_height( &self, height: &BlockHeight, ) -> Result, PropertyQueryError> { self.make_db_tx_ro().get_header_from_height(height) } - pub(crate) fn get_block_id_from_height( + pub fn get_block_id_from_height( &self, height: &BlockHeight, ) -> Result>, PropertyQueryError> { @@ -313,11 +313,11 @@ impl Chainstate { .map(|res| res.map(Into::into)) } - pub(crate) fn get_block(&self, id: Id) -> Result, PropertyQueryError> { + pub fn get_block(&self, id: Id) -> Result, PropertyQueryError> { self.make_db_tx_ro().get_block(id) } - pub(crate) fn get_block_index( + pub fn get_block_index( &self, id: &Id, ) -> Result, PropertyQueryError> { @@ -332,7 +332,7 @@ impl Chainstate { itertools::iterate(0, |&i| std::cmp::max(1, i * 2)).map(BlockDistance::new) } - pub(crate) fn get_locator(&self) -> Result { + pub fn get_locator(&self) -> Result { let chainstate_ref = self.make_db_tx_ro(); let best_block_index = chainstate_ref .get_best_block_index()? @@ -347,17 +347,14 @@ impl Chainstate { .map(Locator::new) } - pub(crate) fn get_block_height_in_main_chain( + pub fn get_block_height_in_main_chain( &self, id: &Id, ) -> Result, PropertyQueryError> { self.make_db_tx_ro().get_block_height_in_main_chain(id) } - pub(crate) fn get_headers( - &self, - locator: Locator, - ) -> Result, PropertyQueryError> { + pub fn get_headers(&self, locator: Locator) -> Result, PropertyQueryError> { // use genesis block if no common ancestor with better block height is found let chainstate_ref = self.make_db_tx_ro(); let mut best = BlockHeight::new(0); @@ -388,7 +385,7 @@ impl Chainstate { itertools::process_results(headers, |iter| iter.flatten().collect::>()) } - pub(crate) fn filter_already_existing_blocks( + pub fn filter_already_existing_blocks( &self, headers: Vec, ) -> Result, PropertyQueryError> { diff --git a/chainstate/src/detail/orphan_blocks/pool.rs b/chainstate/src/detail/orphan_blocks/pool.rs index 5d1cd08910..54d3286891 100644 --- a/chainstate/src/detail/orphan_blocks/pool.rs +++ b/chainstate/src/detail/orphan_blocks/pool.rs @@ -164,6 +164,7 @@ mod tests { use checkers::*; use common::{chain::block::Block, primitives::Id}; use helpers::*; + use test_utils::{make_seedable_rng, random::*}; const MAX_ORPHAN_BLOCKS: usize = 512; @@ -175,17 +176,15 @@ mod tests { use common::primitives::H256; use crypto::random::Rng; - pub fn gen_random_blocks(count: u32) -> Vec { - (0..count).into_iter().map(|_| gen_random_block()).collect::>() + pub fn gen_random_blocks(rng: &mut impl Rng, count: u32) -> Vec { + (0..count).into_iter().map(|_| gen_random_block(rng)).collect::>() } - pub fn gen_random_block() -> Block { - gen_block_from_id(None) + pub fn gen_random_block(rng: &mut impl Rng) -> Block { + gen_block_from_id(rng, None) } - pub fn gen_block_from_id(prev_block_id: Option>) -> Block { - let mut rng = crypto::random::make_pseudo_rng(); - + pub fn gen_block_from_id(rng: &mut impl Rng, prev_block_id: Option>) -> Block { let tx = Transaction::new(0, Vec::new(), Vec::new(), 0).unwrap(); Block::new( @@ -197,40 +196,40 @@ mod tests { .unwrap() } - pub fn gen_blocks_chain(count: u32) -> Vec { - gen_blocks_chain_starting_from_id(count, None) + pub fn gen_blocks_chain(rng: &mut impl Rng, count: u32) -> Vec { + gen_blocks_chain_starting_from_id(rng, count, None) } pub fn gen_blocks_chain_starting_from_id( + rng: &mut impl Rng, count: u32, prev_block_id: Option>, ) -> Vec { - let mut blocks = vec![gen_block_from_id(prev_block_id)]; + let mut blocks = vec![gen_block_from_id(rng, prev_block_id)]; (1..count).into_iter().for_each(|_| { let prev_block_id = blocks.last().map(|block| block.get_id()); - blocks.push(gen_block_from_id(prev_block_id.map(Into::into))); + blocks.push(gen_block_from_id(rng, prev_block_id.map(Into::into))); }); blocks } - pub fn gen_blocks_with_common_parent(count: u32) -> Vec { - gen_blocks_with_common_parent_id(count, None) + pub fn gen_blocks_with_common_parent(rng: &mut impl Rng, count: u32) -> Vec { + gen_blocks_with_common_parent_id(rng, count, None) } pub fn gen_blocks_with_common_parent_id( + rng: &mut impl Rng, count: u32, prev_block_id: Option>, ) -> Vec { - let mut rng = crypto::random::make_pseudo_rng(); - let prev_block_id = prev_block_id.unwrap_or_else(|| H256::from_low_u64_be(rng.gen()).into()); (0..count) .into_iter() - .map(|_| gen_block_from_id(Some(prev_block_id.clone().into()))) + .map(|_| gen_block_from_id(rng, Some(prev_block_id.clone().into()))) .collect() } } @@ -288,7 +287,8 @@ mod tests { let mut orphans_pool = OrphanBlocksPool::new(MAX_ORPHAN_BLOCKS); // add a random block - let block = gen_random_block(); + let mut rng = make_seedable_rng!(Seed::from_entropy()); + let block = gen_random_block(&mut rng); assert!(orphans_pool.add_block(block.clone()).is_ok()); // check if block was really inserted @@ -305,14 +305,15 @@ mod tests { let mut orphans_pool = OrphanBlocksPool::new(MAX_ORPHAN_BLOCKS); // add a random block - let block = gen_random_block(); + let mut rng = make_seedable_rng!(Seed::from_entropy()); + let block = gen_random_block(&mut rng); assert!(orphans_pool.add_block(block.clone()).is_ok()); assert_eq!(orphans_pool.len(), 1); check_block_existence_and_pool_length(&orphans_pool, &block, 1); // add another block that connects to the first one - let conn_block = gen_block_from_id(Some(block.get_id().into())); + let conn_block = gen_block_from_id(&mut rng, Some(block.get_id().into())); assert!(orphans_pool.add_block(conn_block.clone()).is_ok()); check_block_existence_and_pool_length(&orphans_pool, &conn_block, 2); assert_eq!(orphans_pool.len(), 2); @@ -320,8 +321,6 @@ mod tests { // check that there is only 2 key-value pair in `orphans_by_prev_id` assert_eq!(orphans_pool.orphan_by_prev_id.len(), 2); - let mut rng = crypto::random::make_pseudo_rng(); - // add another block with the parent id of any of the 2 blocks above let rand_block = { let rand_id = orphans_pool @@ -334,7 +333,7 @@ mod tests { .expect("it should return the block specified by `rand_id`") }; - let sim_block = gen_block_from_id(Some(rand_block.prev_block_id())); + let sim_block = gen_block_from_id(&mut rng, Some(rand_block.prev_block_id())); assert!(orphans_pool.add_block(sim_block.clone()).is_ok()); check_block_existence_and_pool_length(&orphans_pool, &sim_block, 3); @@ -350,7 +349,8 @@ mod tests { fn test_add_block_exceeds_max() { let max_orphans = 3; let mut orphans_pool = OrphanBlocksPool::new(max_orphans); - let blocks = gen_random_blocks(max_orphans as u32 + 2); + let mut rng = make_seedable_rng!(Seed::from_entropy()); + let blocks = gen_random_blocks(&mut rng, max_orphans as u32 + 2); blocks.into_iter().for_each(|block| { assert!(orphans_pool.add_block(block).is_ok()); @@ -362,14 +362,13 @@ mod tests { #[test] fn test_add_block_repeated() { let mut orphans_pool = OrphanBlocksPool::new(MAX_ORPHAN_BLOCKS); - let blocks = gen_random_blocks(50); + let mut rng = make_seedable_rng!(Seed::from_entropy()); + let blocks = gen_random_blocks(&mut rng, 50); blocks.iter().for_each(|block| { assert!(orphans_pool.add_block(block.clone()).is_ok()); }); - let mut rng = crypto::random::make_pseudo_rng(); - let rand_block = blocks.choose(&mut rng).expect("this should return any block"); assert_eq!( @@ -381,15 +380,14 @@ mod tests { #[test] fn test_pool_drop_block() { let mut orphans_pool = OrphanBlocksPool::new(MAX_ORPHAN_BLOCKS); - let blocks = gen_random_blocks(5); + let mut rng = make_seedable_rng!(Seed::from_entropy()); + let blocks = gen_random_blocks(&mut rng, 5); blocks.iter().for_each(|block| { assert!(orphans_pool.add_block(block.clone()).is_ok()); }); check_pool_length(&orphans_pool, blocks.len()); - let mut rng = crypto::random::make_pseudo_rng(); - let rand_block = blocks.choose(&mut rng).expect("this should return any block"); // dropping the rand_block @@ -404,6 +402,8 @@ mod tests { fn test_deepest_child_in_chain() { let mut orphans_pool = OrphanBlocksPool::new(MAX_ORPHAN_BLOCKS); + let mut rng = make_seedable_rng!(Seed::from_entropy()); + // In `orphans_by_prev_id`: // [ // ( a, b ), @@ -411,7 +411,7 @@ mod tests { // ( c, d ), // ( d, e ), // ] - let blocks = gen_blocks_chain(4); + let blocks = gen_blocks_chain(&mut rng, 4); blocks.iter().for_each(|block| { assert!(orphans_pool.add_block(block.clone()).is_ok()); @@ -453,11 +453,12 @@ mod tests { #[test] fn test_deepest_child_common_parent() { let mut orphans_pool = OrphanBlocksPool::new(MAX_ORPHAN_BLOCKS); + let mut rng = make_seedable_rng!(Seed::from_entropy()); // In `orphans_by_prev_id`: // [ // ( a, (b,c,d,e,f) ), // ] - let blocks = gen_blocks_with_common_parent(5); + let blocks = gen_blocks_with_common_parent(&mut rng, 5); blocks.iter().enumerate().for_each(|(idx, b)| { let block_id = b.get_id(); @@ -478,8 +479,6 @@ mod tests { assert_eq!(orphans_pool.orphan_by_prev_id.len(), 1); assert_eq!(orphans_pool.orphan_ids.len(), blocks.len()); - let mut rng = crypto::random::make_pseudo_rng(); - // delete a random block let random_block = blocks.choose(&mut rng).expect("returns any block"); orphans_pool.del_one_deepest_child(&random_block.get_id()); @@ -502,18 +501,19 @@ mod tests { #[test] fn test_prune() { let mut orphans_pool = OrphanBlocksPool::new(12); + let mut rng = make_seedable_rng!(Seed::from_entropy()); // in `orphans_by_prev_id`: // [ // ( a, (b,c,d,e) ) // ] - let sim_blocks = gen_blocks_with_common_parent(4); + let sim_blocks = gen_blocks_with_common_parent(&mut rng, 4); // [ // ( f, g ), // ( g, h ), // ( h, i ), // ] - let conn_blocks = gen_blocks_chain(3); + let conn_blocks = gen_blocks_chain(&mut rng, 3); // generate a chain using one of sim's blocks. let sim_block_id = sim_blocks.last().expect("it should return the last element").get_id(); @@ -521,14 +521,15 @@ mod tests { // ( e, j ), // ( j, k ) // ] - let extra_sim_blocks = gen_blocks_chain_starting_from_id(2, Some(sim_block_id.into())); + let extra_sim_blocks = + gen_blocks_chain_starting_from_id(&mut rng, 2, Some(sim_block_id.into())); // generate blocks with conn's block id as parent let conn_block_id = conn_blocks.last().expect("it should return last element").get_id(); // [ // ( i, (l,m,n) ) // ] - let extra_conn_blocks = gen_blocks_with_common_parent_id(3, Some(conn_block_id)); + let extra_conn_blocks = gen_blocks_with_common_parent_id(&mut rng, 3, Some(conn_block_id)); //[ // ( a, (b,c,d,e) ), @@ -558,7 +559,7 @@ mod tests { check_pool_length(&orphans_pool, orphans_pool.max_orphans - 1); // add a random block - let random_block = gen_random_block(); + let random_block = gen_random_block(&mut rng); assert!(orphans_pool.add_block(random_block.clone()).is_ok()); check_block_existence_and_pool_length( &orphans_pool, @@ -574,19 +575,20 @@ mod tests { #[test] fn test_simple_take_all_children_of() { let mut orphans_pool = OrphanBlocksPool::new(20); + let mut rng = make_seedable_rng!(Seed::from_entropy()); let count = 9; // in `orphans_by_prev_id`: // [ // ( a, (b,c,d,e,f,g,h,i,j) ) // ] - let sim_blocks = gen_blocks_with_common_parent(count); + let sim_blocks = gen_blocks_with_common_parent(&mut rng, count); // in `orphans_by_prev_id`: // [ // (k,l), (l,m), (m,n), (n,o), (o,p), (p,q), (q,r), (r,s), (s,t)) // ] - let conn_blocks = gen_blocks_chain(count); + let conn_blocks = gen_blocks_chain(&mut rng, count); let conn_blocks_len = conn_blocks.len(); // alternate adding of blocks @@ -615,13 +617,14 @@ mod tests { #[test] fn test_mix_chain_take_all_children_of() { let mut orphans_pool = OrphanBlocksPool::new(20); + let mut rng = make_seedable_rng!(Seed::from_entropy()); let count = 9; // in `orphans_by_prev_id`: // [ // ( a, (b,c,d,e,f,g,h,i,j) ) // ] - let sim_blocks = gen_blocks_with_common_parent(count); + let sim_blocks = gen_blocks_with_common_parent(&mut rng, count); let mut conn_blocks: Vec = vec![]; // let's use 2 random blocks of sim_blocks to generate a chain of blocks @@ -636,11 +639,12 @@ mod tests { // ] for _ in 0..2 { let rand_block_id = sim_blocks - .choose(&mut crypto::random::make_pseudo_rng()) + .choose(&mut rng) .expect("should return any block in sim_blocks") .get_id(); // generate a chain of 3 blocks for `rand_block_id` as parent. - let mut blocks = gen_blocks_chain_starting_from_id(3, Some(rand_block_id.into())); + let mut blocks = + gen_blocks_chain_starting_from_id(&mut rng, 3, Some(rand_block_id.into())); conn_blocks.append(&mut blocks); } diff --git a/chainstate/src/detail/tests/double_spend_tests.rs b/chainstate/src/detail/tests/double_spend_tests.rs index 196c4fc539..9d13faed02 100644 --- a/chainstate/src/detail/tests/double_spend_tests.rs +++ b/chainstate/src/detail/tests/double_spend_tests.rs @@ -42,8 +42,9 @@ fn spend_output_in_the_same_block() { common::concurrency::model(|| { let mut chainstate = setup_chainstate(); - let mut rng = make_seedable_rng(None); - let first_tx = tx_from_genesis(&chainstate, rng.gen_range(100_000..200_000)); + let mut rng = make_seedable_rng!(Seed::from_entropy()); + let tx1_output_value = rng.gen_range(100_000..200_000); + let first_tx = tx_from_genesis(&chainstate, &mut rng, tx1_output_value); let second_tx = tx_from_tx(&first_tx, rng.gen_range(1000..2000)); let block = Block::new( @@ -84,8 +85,9 @@ fn spend_output_in_the_same_block_invalid_order() { common::concurrency::model(|| { let mut chainstate = setup_chainstate(); - let mut rng = make_seedable_rng(None); - let first_tx = tx_from_genesis(&chainstate, rng.gen_range(100_000..200_000)); + let mut rng = make_seedable_rng!(Seed::from_entropy()); + let tx1_output_value = rng.gen_range(100_000..200_000); + let first_tx = tx_from_genesis(&chainstate, &mut rng, tx1_output_value); let second_tx = tx_from_tx(&first_tx, rng.gen_range(1000..2000)); let block = Block::new( @@ -131,8 +133,9 @@ fn double_spend_tx_in_the_same_block() { common::concurrency::model(|| { let mut chainstate = setup_chainstate(); - let mut rng = make_seedable_rng(None); - let first_tx = tx_from_genesis(&chainstate, rng.gen_range(100_000..200_000)); + let mut rng = make_seedable_rng!(Seed::from_entropy()); + let tx1_output_value = rng.gen_range(100_000..200_000); + let first_tx = tx_from_genesis(&chainstate, &mut rng, tx1_output_value); let second_tx = tx_from_tx(&first_tx, rng.gen_range(1000..2000)); let third_tx = tx_from_tx(&first_tx, rng.gen_range(1000..2000)); @@ -183,8 +186,9 @@ fn double_spend_tx_in_another_block() { common::concurrency::model(|| { let mut chainstate = setup_chainstate(); - let mut rng = make_seedable_rng(None); - let first_tx = tx_from_genesis(&chainstate, rng.gen_range(100_000..200_000)); + let mut rng = make_seedable_rng!(Seed::from_entropy()); + let tx1_output_value = rng.gen_range(100_000..200_000); + let first_tx = tx_from_genesis(&chainstate, &mut rng, tx1_output_value); let first_block = Block::new( vec![first_tx.clone()], chainstate.chain_config.genesis_block_id(), @@ -202,7 +206,8 @@ fn double_spend_tx_in_another_block() { Some(first_block_id.clone().into()) ); - let second_tx = tx_from_genesis(&chainstate, rng.gen_range(100_000..200_000)); + let tx2_output_value = rng.gen_range(100_000..200_000); + let second_tx = tx_from_genesis(&chainstate, &mut rng, tx2_output_value); let second_block = Block::new( vec![second_tx], first_block_id.clone().into(), @@ -244,10 +249,10 @@ fn spend_bigger_output_in_the_same_block() { common::concurrency::model(move || { let mut chainstate = setup_chainstate(); - let mut rng = make_seedable_rng(None); + let mut rng = make_seedable_rng!(Seed::from_entropy()); let tx1_output_value = rng.gen_range(1000..2000); let tx2_output_value = rng.gen_range(100_000..200_000); - let first_tx = tx_from_genesis(&chainstate, tx1_output_value); + let first_tx = tx_from_genesis(&chainstate, &mut rng, tx1_output_value); let second_tx = tx_from_tx(&first_tx, tx2_output_value); let block = Block::new( @@ -277,12 +282,12 @@ fn spend_bigger_output_in_the_same_block() { } // Creates a transaction with an input based on the first transaction from the genesis block. -fn tx_from_genesis(chainstate: &Chainstate, output_value: u128) -> Transaction { +fn tx_from_genesis(chainstate: &Chainstate, rng: &mut impl Rng, output_value: u128) -> Transaction { let genesis_block_id = chainstate.chain_config.genesis_block_id(); let input = TxInput::new( OutPointSourceId::BlockReward(genesis_block_id), 0, - empty_witness(), + empty_witness(rng), ); let output = TxOutput::new( Amount::from_atoms(output_value), diff --git a/chainstate/src/detail/tests/events_tests.rs b/chainstate/src/detail/tests/events_tests.rs index 1777291ddf..c00bc22f7e 100644 --- a/chainstate/src/detail/tests/events_tests.rs +++ b/chainstate/src/detail/tests/events_tests.rs @@ -29,10 +29,12 @@ fn simple_subscribe() { let mut chainstate = setup_chainstate(); let events = subscribe(&mut chainstate, 1); + let mut rng = make_seedable_rng!(Seed::from_entropy()); // Produce and process a block. - let first_block = produce_test_block(TestBlockInfo::from_genesis( - chainstate.chain_config.genesis_block(), - )); + let first_block = produce_test_block( + TestBlockInfo::from_genesis(chainstate.chain_config.genesis_block()), + &mut rng, + ); assert!(!chainstate.events_controller.subscribers().is_empty()); chainstate.process_block(first_block.clone(), BlockSource::Local).unwrap(); chainstate.wait_for_all_events(); @@ -47,7 +49,7 @@ fn simple_subscribe() { } // Process one more block. - let second_block = produce_test_block(TestBlockInfo::from_block(&first_block)); + let second_block = produce_test_block(TestBlockInfo::from_block(&first_block), &mut rng); chainstate.process_block(second_block.clone(), BlockSource::Local).unwrap(); chainstate.wait_for_all_events(); @@ -67,13 +69,15 @@ fn simple_subscribe() { fn several_subscribers() { common::concurrency::model(|| { let mut chainstate = setup_chainstate(); - let mut rng = make_seedable_rng(None); + + let mut rng = make_seedable_rng!(Seed::from_entropy()); let subscribers = rng.gen_range(8..256); let events = subscribe(&mut chainstate, subscribers); - let block = produce_test_block(TestBlockInfo::from_genesis( - chainstate.chain_config.genesis_block(), - )); + let block = produce_test_block( + TestBlockInfo::from_genesis(chainstate.chain_config.genesis_block()), + &mut rng, + ); assert!(!chainstate.events_controller.subscribers().is_empty()); chainstate.process_block(block.clone(), BlockSource::Local).unwrap(); @@ -93,7 +97,7 @@ fn several_subscribers_several_events() { common::concurrency::model(|| { let mut chainstate = setup_chainstate(); - let mut rng = make_seedable_rng(None); + let mut rng = make_seedable_rng!(Seed::from_entropy()); let subscribers = rng.gen_range(4..16); let blocks = rng.gen_range(8..128); @@ -102,7 +106,7 @@ fn several_subscribers_several_events() { let mut block_info = TestBlockInfo::from_genesis(chainstate.chain_config.genesis_block()); for _ in 0..blocks { - let block = produce_test_block(block_info); + let block = produce_test_block(block_info, &mut rng); block_info = TestBlockInfo::from_block(&block); let index = chainstate .process_block(block.clone(), BlockSource::Local) @@ -140,8 +144,11 @@ fn orphan_block() { let events = subscribe(&mut chainstate, 1); assert!(!chainstate.events_controller.subscribers().is_empty()); + let mut rng = make_seedable_rng!(Seed::from_entropy()); + let block = produce_test_block( TestBlockInfo::from_genesis(chainstate.chain_config.genesis_block()).orphan(), + &mut rng, ); assert_eq!( chainstate.process_block(block, BlockSource::Local).unwrap_err(), @@ -172,9 +179,12 @@ fn custom_orphan_error_hook() { let events = subscribe(&mut chainstate, 1); assert!(!chainstate.events_controller.subscribers().is_empty()); - let first_block = produce_test_block(TestBlockInfo::from_genesis( - chainstate.chain_config.genesis_block(), - )); + let mut rng = make_seedable_rng!(Seed::from_entropy()); + + let first_block = produce_test_block( + TestBlockInfo::from_genesis(chainstate.chain_config.genesis_block()), + &mut rng, + ); // Produce a block with a bad timestamp. let timestamp = chainstate.chain_config.genesis_block().timestamp().as_int_seconds() + chainstate.chain_config.max_future_block_time_offset().as_secs(); diff --git a/chainstate/src/detail/tests/mod.rs b/chainstate/src/detail/tests/mod.rs index b3550bdb8a..e103f5884b 100644 --- a/chainstate/src/detail/tests/mod.rs +++ b/chainstate/src/detail/tests/mod.rs @@ -30,11 +30,11 @@ use common::{ primitives::{time, Amount, BlockHeight, Id, H256}, Uint256, }; -use crypto::random::{Rng, SeedableRng, SliceRandom}; -use hex::{FromHex, ToHex}; -use rand_chacha::ChaChaRng; +use crypto::random::{Rng, SliceRandom}; use serialization::Encode; +use test_utils::{make_seedable_rng, random::*}; + mod double_spend_tests; mod events_tests; mod processing_tests; @@ -50,23 +50,9 @@ const ERR_STORAGE_FAIL: &str = "Storage failure"; const ERR_CREATE_BLOCK_FAIL: &str = "Creating block caused fail"; const ERR_CREATE_TX_FAIL: &str = "Creating tx caused fail"; -#[must_use] -fn make_seedable_rng(seed_opt: Option<&str>) -> impl Rng { - let seed = match seed_opt { - Some(s) => FromHex::from_hex(s).expect("seed is invalid"), - None => crypto::random::make_true_rng().gen::<[u8; 32]>(), - }; - log::warn!( - "Seed for the range is: {}", - seed.encode_hex_upper::() - ); - ChaChaRng::from_seed(seed) -} - -fn empty_witness() -> InputWitness { - let mut rng = crypto::random::make_pseudo_rng(); +fn empty_witness(rng: &mut impl Rng) -> InputWitness { let mut msg: Vec = (1..100).collect(); - msg.shuffle(&mut rng); + msg.shuffle(rng); InputWitness::NoSignature(Some(msg)) } @@ -78,19 +64,18 @@ fn create_utxo_data( outsrc: OutPointSourceId, index: usize, output: &TxOutput, + rng: &mut impl Rng, ) -> Option<(TxInput, TxOutput)> { - let mut rng = crypto::random::make_pseudo_rng(); let spent_value = Amount::from_atoms(rng.gen_range(0..output.value().into_atoms())); let new_value = (output.value() - spent_value).unwrap(); utils::ensure!(new_value >= Amount::from_atoms(1)); Some(( - TxInput::new(outsrc, index as u32, empty_witness()), + TxInput::new(outsrc, index as u32, empty_witness(rng)), TxOutput::new(new_value, OutputPurpose::Transfer(anyonecanspend_address())), )) } fn setup_chainstate() -> Chainstate { - logging::init_logging::<&std::path::Path>(None); chainstate_with_config(create_unit_test_config(), ChainstateConfig::new()) } @@ -154,17 +139,21 @@ impl TestBlockInfo { } } -fn produce_test_block(prev_block: TestBlockInfo) -> Block { - produce_test_block_with_consensus_data(prev_block, ConsensusData::None) +fn produce_test_block(prev_block: TestBlockInfo, rng: &mut impl Rng) -> Block { + produce_test_block_with_consensus_data(prev_block, ConsensusData::None, rng) } fn produce_test_block_with_consensus_data( prev_block: TestBlockInfo, consensus_data: ConsensusData, + rng: &mut impl Rng, ) -> Block { // The value of each output is decreased by a random amount to produce a new input and output. - let (inputs, outputs): (Vec, Vec) = - prev_block.txns.into_iter().flat_map(|(s, o)| create_new_outputs(s, &o)).unzip(); + let (inputs, outputs): (Vec, Vec) = prev_block + .txns + .into_iter() + .flat_map(|(s, o)| create_new_outputs(s, &o, rng)) + .unzip(); Block::new( vec![Transaction::new(0, inputs, outputs, 0).expect(ERR_CREATE_TX_FAIL)], @@ -175,10 +164,14 @@ fn produce_test_block_with_consensus_data( .expect(ERR_CREATE_BLOCK_FAIL) } -fn create_new_outputs(srcid: OutPointSourceId, outs: &[TxOutput]) -> Vec<(TxInput, TxOutput)> { +fn create_new_outputs( + srcid: OutPointSourceId, + outs: &[TxOutput], + rng: &mut impl Rng, +) -> Vec<(TxInput, TxOutput)> { outs.iter() .enumerate() - .filter_map(move |(index, output)| create_utxo_data(srcid.clone(), index, output)) + .filter_map(move |(index, output)| create_utxo_data(srcid.clone(), index, output, rng)) .collect() } @@ -195,8 +188,9 @@ fn generate_blocks_for_functional_tests() { let difficulty = Uint256([0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF]); + let mut rng = make_seedable_rng!(Seed::from_entropy()); for _ in 1..6 { - let mut mined_block = btf.random_block(prev_block, None); + let mut mined_block = btf.random_block(prev_block, None, &mut rng); let bits = difficulty.into(); assert!( crate::detail::pow::work::mine(&mut mined_block, u128::MAX, bits, vec![]) diff --git a/chainstate/src/detail/tests/processing_tests.rs b/chainstate/src/detail/tests/processing_tests.rs index 92a52a72f2..eceab8371d 100644 --- a/chainstate/src/detail/tests/processing_tests.rs +++ b/chainstate/src/detail/tests/processing_tests.rs @@ -75,14 +75,16 @@ fn orphans_chains() { ); // Process the orphan block + let mut rng = make_seedable_rng!(Seed::from_entropy()); let genesis_block = chainstate.chain_config.genesis_block().clone(); - let missing_block = produce_test_block(TestBlockInfo::from_genesis(&genesis_block)); + let missing_block = + produce_test_block(TestBlockInfo::from_genesis(&genesis_block), &mut rng); // Create and process orphan blocks. const MAX_ORPHANS_COUNT_IN_TEST: usize = 100; let mut current_block = missing_block.clone(); for orphan_count in 1..MAX_ORPHANS_COUNT_IN_TEST { - current_block = produce_test_block(TestBlockInfo::from_block(¤t_block)); + current_block = produce_test_block(TestBlockInfo::from_block(¤t_block), &mut rng); assert_eq!( chainstate.process_block(current_block.clone(), BlockSource::Local).unwrap_err(), BlockError::OrphanCheckFailed(OrphanCheckError::LocalOrphan) @@ -144,9 +146,11 @@ fn spend_inputs_simple() { let mut chainstate = setup_chainstate(); // Create a new block - let block = produce_test_block(TestBlockInfo::from_genesis( - chainstate.chain_config.genesis_block(), - )); + let mut rng = make_seedable_rng!(Seed::from_entropy()); + let block = produce_test_block( + TestBlockInfo::from_genesis(chainstate.chain_config.genesis_block()), + &mut rng, + ); // Check that all tx not in the main chain for tx in block.transactions() { @@ -203,6 +207,7 @@ fn straight_chain() { ) .unwrap(); + let mut rng = make_seedable_rng!(Seed::from_entropy()); let genesis_index = chainstate .make_db_tx_ro() .get_gen_block_index(&chainstate.chain_config.genesis_block_id()) @@ -223,7 +228,7 @@ fn straight_chain() { let mut block_index = GenBlockIndex::Genesis(&chain_config_clone); let mut prev_block = TestBlockInfo::from_genesis(chainstate.chain_config.genesis_block()); - for _ in 0..make_seedable_rng(None).gen_range(100..200) { + for _ in 0..rng.gen_range(100..200) { assert_eq!( chainstate.chainstate_storage.get_best_block_id().unwrap().unwrap(), prev_block.id @@ -236,7 +241,7 @@ fn straight_chain() { .flatten() .expect("Unable to get best block ID"); assert_eq!(best_block_id, block_index.block_id()); - let new_block = produce_test_block(prev_block); + let new_block = produce_test_block(prev_block, &mut rng); let new_block_index = chainstate .process_block(new_block.clone(), BlockSource::Peer) .ok() @@ -260,7 +265,8 @@ fn straight_chain() { fn get_ancestor_invalid_height() { let mut btf = BlockTestFramework::new(); let height = 1; - btf.create_chain(&btf.genesis().get_id().into(), height).unwrap(); + let mut rng = make_seedable_rng!(Seed::from_entropy()); + btf.create_chain(&btf.genesis().get_id().into(), height, &mut rng).unwrap(); let last_block_index = btf.block_indexes.last().expect("last block in first chain").clone(); let invalid_height = height + 1; @@ -288,7 +294,8 @@ fn get_ancestor() { const ANCESTOR_HEIGHT: usize = 50; const FIRST_CHAIN_HEIGHT: usize = 500; const SECOND_CHAIN_LENGTH: usize = 300; - btf.create_chain(&btf.genesis().get_id().into(), SPLIT_HEIGHT) + let mut rng = make_seedable_rng!(Seed::from_entropy()); + btf.create_chain(&btf.genesis().get_id().into(), SPLIT_HEIGHT, &mut rng) .expect("Chain creation to succeed"); let ancestor = GenBlockIndex::Block(btf.index_at(ANCESTOR_HEIGHT).clone()); @@ -313,8 +320,12 @@ fn get_ancestor() { } // Create the first chain and test get_ancestor for this chain's last block - btf.create_chain(&split.block_id(), FIRST_CHAIN_HEIGHT - SPLIT_HEIGHT) - .expect("second chain"); + btf.create_chain( + &split.block_id(), + FIRST_CHAIN_HEIGHT - SPLIT_HEIGHT, + &mut rng, + ) + .expect("second chain"); let last_block_in_first_chain = GenBlockIndex::Block(btf.block_indexes.last().expect("last block in first chain").clone()); @@ -363,8 +374,12 @@ fn get_ancestor() { ); // Create a second chain and test get_ancestor for this chain's last block - btf.create_chain(&split.block_id(), SECOND_CHAIN_LENGTH - SPLIT_HEIGHT) - .expect("second chain"); + btf.create_chain( + &split.block_id(), + SECOND_CHAIN_LENGTH - SPLIT_HEIGHT, + &mut rng, + ) + .expect("second chain"); let last_block_in_second_chain = GenBlockIndex::Block(btf.block_indexes.last().expect("last block in first chain").clone()); assert_eq!( @@ -389,21 +404,30 @@ fn last_common_ancestor() { const FIRST_CHAIN_HEIGHT: usize = 500; const SECOND_CHAIN_LENGTH: usize = 300; - btf.create_chain(&btf.genesis().get_id().into(), SPLIT_HEIGHT) + let mut rng = make_seedable_rng!(Seed::from_entropy()); + btf.create_chain(&btf.genesis().get_id().into(), SPLIT_HEIGHT, &mut rng) .expect("Chain creation to succeed"); let config_clone = btf.chainstate.chain_config.clone(); let genesis = GenBlockIndex::Genesis(&config_clone); let split = GenBlockIndex::Block(btf.index_at(SPLIT_HEIGHT).clone()); // First branch of fork - btf.create_chain(&split.block_id(), FIRST_CHAIN_HEIGHT - SPLIT_HEIGHT) - .expect("Chain creation to succeed"); + btf.create_chain( + &split.block_id(), + FIRST_CHAIN_HEIGHT - SPLIT_HEIGHT, + &mut rng, + ) + .expect("Chain creation to succeed"); let last_block_in_first_chain = GenBlockIndex::Block(btf.block_indexes.last().expect("last block in first chain").clone()); // Second branch of fork - btf.create_chain(&split.block_id(), SECOND_CHAIN_LENGTH - SPLIT_HEIGHT) - .expect("second chain"); + btf.create_chain( + &split.block_id(), + SECOND_CHAIN_LENGTH - SPLIT_HEIGHT, + &mut rng, + ) + .expect("second chain"); let last_block_in_second_chain = GenBlockIndex::Block(btf.block_indexes.last().expect("last block in first chain").clone()); @@ -488,12 +512,14 @@ fn consensus_type() { let chainstate = chainstate_with_config(chain_config, chainstate_config); let mut btf = BlockTestFramework::with_chainstate(chainstate); + let mut rng = make_seedable_rng!(Seed::from_entropy()); // The next block will have height 1. At this height, we are still under IgnoreConsensus, so // processing a block with PoWData will fail let pow_block = produce_test_block_with_consensus_data( TestBlockInfo::from_genesis(btf.genesis()), ConsensusData::PoW(PoWData::new(Compact(0), 0, vec![])), + &mut rng, ); assert!(matches!( @@ -504,7 +530,8 @@ fn consensus_type() { )); // Create 4 more blocks with Consensus Nonw - btf.create_chain(&btf.genesis().get_id().into(), 4).expect("chain creation"); + btf.create_chain(&btf.genesis().get_id().into(), 4, &mut rng) + .expect("chain creation"); // The next block will be at height 5, so it is expected to be a PoW block. Let's crate a block // with ConsensusData::None and see that adding it fails @@ -513,6 +540,7 @@ fn consensus_type() { &btf.get_block(btf.index_at(4).block_id().clone()).unwrap().unwrap(), ), ConsensusData::None, + &mut rng, ); assert!(matches!( @@ -525,7 +553,8 @@ fn consensus_type() { // Mine blocks 5-9 with minimal difficulty, as expected by net upgrades for i in 5..10 { let prev_block = btf.get_block(btf.index_at(i - 1).block_id().clone()).unwrap().unwrap(); - let mut mined_block = btf.random_block(TestBlockInfo::from_block(&prev_block), None); + let mut mined_block = + btf.random_block(TestBlockInfo::from_block(&prev_block), None, &mut rng); let bits = min_difficulty.into(); let (_, pub_key) = PrivateKey::new(KeyKind::RistrettoSchnorr); assert!(crate::detail::pow::work::mine( @@ -544,7 +573,7 @@ fn consensus_type() { // Block 10 should ignore consensus according to net upgrades. The following Pow block should // fail. let prev_block = btf.get_block(btf.index_at(9).block_id().clone()).unwrap().unwrap(); - let mut mined_block = btf.random_block(TestBlockInfo::from_block(&prev_block), None); + let mut mined_block = btf.random_block(TestBlockInfo::from_block(&prev_block), None, &mut rng); let bits = min_difficulty.into(); assert!( crate::detail::pow::work::mine(&mut mined_block, u128::MAX, bits, vec![]) @@ -559,13 +588,15 @@ fn consensus_type() { )); // Create blocks 10-14 without consensus data as required by net_upgrades - btf.create_chain(&prev_block.get_id().into(), 5).expect("chain creation"); + btf.create_chain(&prev_block.get_id().into(), 5, &mut rng) + .expect("chain creation"); // At height 15 we are again proof of work, ignoring consensus should fail let prev_block = btf.get_block(btf.index_at(14).block_id().clone()).unwrap().unwrap(); let block_without_consensus_data = produce_test_block_with_consensus_data( TestBlockInfo::from_block(&prev_block), ConsensusData::None, + &mut rng, ); assert!(matches!( @@ -578,7 +609,8 @@ fn consensus_type() { // Mining should work for i in 15..20 { let prev_block = btf.get_block(btf.index_at(i - 1).block_id().clone()).unwrap().unwrap(); - let mut mined_block = btf.random_block(TestBlockInfo::from_block(&prev_block), None); + let mut mined_block = + btf.random_block(TestBlockInfo::from_block(&prev_block), None, &mut rng); let bits = min_difficulty.into(); let (_, pub_key) = PrivateKey::new(KeyKind::RistrettoSchnorr); assert!(crate::detail::pow::work::mine( @@ -625,12 +657,13 @@ fn pow() { let chainstate_config = ChainstateConfig::new(); let chainstate = chainstate_with_config(chain_config, chainstate_config); + let mut rng = make_seedable_rng!(Seed::from_entropy()); let mut btf = BlockTestFramework::with_chainstate(chainstate); // Let's create a block with random (invalid) PoW data and see that it fails the consensus // checks let prev_block = TestBlockInfo::from_genesis(btf.genesis()); - let mut random_invalid_block = btf.random_block(prev_block, None); + let mut random_invalid_block = btf.random_block(prev_block, None, &mut rng); make_invalid_pow_block(&mut random_invalid_block, u128::MAX, difficulty.into()) .expect("generate invalid block"); let res = btf.add_special_block(random_invalid_block.clone()); diff --git a/chainstate/src/detail/tests/reorgs_tests.rs b/chainstate/src/detail/tests/reorgs_tests.rs index b0829d04f8..aa04cd8cd9 100644 --- a/chainstate/src/detail/tests/reorgs_tests.rs +++ b/chainstate/src/detail/tests/reorgs_tests.rs @@ -31,6 +31,7 @@ use common::chain::config::create_unit_test_config; #[test] fn reorg_simple() { common::concurrency::model(|| { + let mut rng = make_seedable_rng!(Seed::from_entropy()); let chain_config = Arc::new(create_unit_test_config()); let chainstate_config = ChainstateConfig::new(); let storage = Store::new_empty().unwrap(); @@ -51,9 +52,10 @@ fn reorg_simple() { Some(chainstate.chain_config.genesis_block_id()) ); - let block_a = produce_test_block(TestBlockInfo::from_genesis( - chainstate.chain_config.genesis_block(), - )); + let block_a = produce_test_block( + TestBlockInfo::from_genesis(chainstate.chain_config.genesis_block()), + &mut rng, + ); chainstate.process_block(block_a.clone(), BlockSource::Local).unwrap(); assert_eq!( chainstate @@ -64,9 +66,10 @@ fn reorg_simple() { ); // Produce the parallel chain. - let block_b = produce_test_block(TestBlockInfo::from_genesis( - chainstate.chain_config.genesis_block(), - )); + let block_b = produce_test_block( + TestBlockInfo::from_genesis(chainstate.chain_config.genesis_block()), + &mut rng, + ); assert_ne!(block_a.get_id(), block_b.get_id()); chainstate.process_block(block_b.clone(), BlockSource::Local).unwrap(); assert_ne!( @@ -85,7 +88,7 @@ fn reorg_simple() { ); // Produce one more block that causes a reorg. - let block_c = produce_test_block(TestBlockInfo::from_block(&block_b)); + let block_c = produce_test_block(TestBlockInfo::from_block(&block_b), &mut rng); assert!(chainstate.process_block(block_c.clone(), BlockSource::Local).is_ok()); assert_eq!( chainstate @@ -104,12 +107,13 @@ fn test_very_long_reorgs() { let events: EventList = Arc::new(Mutex::new(Vec::new())); subscribe_to_events(&mut btf, &events); - check_simple_fork(&mut btf, &events); - check_make_alternative_chain_longer(&mut btf, &events); - check_reorg_to_first_chain(&mut btf, &events); - check_spend_tx_in_failed_block(&mut btf, &events); - check_spend_tx_in_other_fork(&mut btf); - check_fork_that_double_spends(&mut btf); + let mut rng = make_seedable_rng!(Seed::from_entropy()); + check_simple_fork(&mut btf, &events, &mut rng); + check_make_alternative_chain_longer(&mut btf, &events, &mut rng); + check_reorg_to_first_chain(&mut btf, &events, &mut rng); + check_spend_tx_in_failed_block(&mut btf, &events, &mut rng); + check_spend_tx_in_other_fork(&mut btf, &mut rng); + check_fork_that_double_spends(&mut btf, &mut rng); // Try to create a block that has too much fee // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) @@ -128,7 +132,11 @@ fn test_very_long_reorgs() { }); } -fn check_spend_tx_in_failed_block(btf: &mut BlockTestFramework, events: &EventList) { +fn check_spend_tx_in_failed_block( + btf: &mut BlockTestFramework, + events: &EventList, + rng: &mut impl Rng, +) { // Check spending of a transaction in a block which failed to connect // //+-- 0x07e3…6fe4 (H:8,M,B:10) @@ -142,6 +150,7 @@ fn check_spend_tx_in_failed_block(btf: &mut BlockTestFramework, events: &EventLi .create_chain( &btf.index_at(NEW_CHAIN_START_ON).block_id().clone().into(), 5, + rng ) .is_ok()); check_last_event(btf, events); @@ -156,13 +165,14 @@ fn check_spend_tx_in_failed_block(btf: &mut BlockTestFramework, events: &EventLi let double_spend_block = btf.random_block( TestBlockInfo::from_block(&block), Some(&[TestBlockParams::SpendFrom(btf.index_at(NEW_CHAIN_END_ON).block_id().clone())]), + rng, ); assert!(btf.add_special_block(double_spend_block).is_ok()); // Cause reorg on a failed block - assert!(btf.create_chain(&btf.index_at(12).block_id().clone().into(), 1).is_err()); + assert!(btf.create_chain(&btf.index_at(12).block_id().clone().into(), 1, rng).is_err()); } -fn check_spend_tx_in_other_fork(btf: &mut BlockTestFramework) { +fn check_spend_tx_in_other_fork(btf: &mut BlockTestFramework, rng: &mut impl Rng) { // # Attempt to spend a transaction created on a different fork // // +-- 0x4273…c93c (H:7,M,B:10) @@ -178,7 +188,8 @@ fn check_spend_tx_in_other_fork(btf: &mut BlockTestFramework) { assert!(btf .create_chain( &btf.index_at(NEW_CHAIN_START_ON).block_id().clone().into(), - 1 + 1, + rng ) .is_ok()); let block = btf @@ -190,14 +201,15 @@ fn check_spend_tx_in_other_fork(btf: &mut BlockTestFramework) { let double_spend_block = btf.random_block( TestBlockInfo::from_block(&block), Some(&[TestBlockParams::SpendFrom(btf.index_at(3).block_id().clone())]), + rng, ); let block_id = double_spend_block.get_id(); assert!(btf.add_special_block(double_spend_block).is_ok()); // Cause reorg on a failed block - assert!(btf.create_chain(&block_id.into(), 10).is_err()); + assert!(btf.create_chain(&block_id.into(), 10, rng).is_err()); } -fn check_fork_that_double_spends(btf: &mut BlockTestFramework) { +fn check_fork_that_double_spends(btf: &mut BlockTestFramework, rng: &mut impl Rng) { // # Try to create a fork that double-spends // +-- 0x6e45…e8e8 (H:0,P:0) // +-- 0xe090…995e (H:1,M,P:1) @@ -219,11 +231,16 @@ fn check_fork_that_double_spends(btf: &mut BlockTestFramework) { let double_spend_block = btf.random_block( TestBlockInfo::from_block(&block), Some(&[TestBlockParams::SpendFrom(btf.index_at(6).block_id().clone())]), + rng, ); assert!(btf.add_special_block(double_spend_block).is_err()); } -fn check_reorg_to_first_chain(btf: &mut BlockTestFramework, events: &EventList) { +fn check_reorg_to_first_chain( + btf: &mut BlockTestFramework, + events: &EventList, + rng: &mut impl Rng, +) { // ... and back to the first chain. // // +-- 0x6e45…e8e8 (H:0,B:0) @@ -236,7 +253,7 @@ fn check_reorg_to_first_chain(btf: &mut BlockTestFramework, events: &EventList) // > H - Height, M - main chain, B - block // let block_id: Id = btf.index_at(2).block_id().clone().into(); - assert!(btf.create_chain(&block_id, 2).is_ok()); + assert!(btf.create_chain(&block_id, 2, rng).is_ok()); check_last_event(btf, events); // b3 @@ -277,7 +294,11 @@ fn check_reorg_to_first_chain(btf: &mut BlockTestFramework, events: &EventList) assert!(btf.is_block_in_main_chain(btf.index_at(6).block_id())); } -fn check_make_alternative_chain_longer(btf: &mut BlockTestFramework, events: &EventList) { +fn check_make_alternative_chain_longer( + btf: &mut BlockTestFramework, + events: &EventList, + rng: &mut impl Rng, +) { // Now we add another block to make the alternative chain longer. // // +-- 0x6e45…e8e8 (H:0,B:0) @@ -295,7 +316,7 @@ fn check_make_alternative_chain_longer(btf: &mut BlockTestFramework, events: &Ev .get_block(btf.block_indexes.last().unwrap().block_id().clone()) .unwrap() .unwrap(); - let block = btf.random_block(TestBlockInfo::from_block(&block), None); + let block = btf.random_block(TestBlockInfo::from_block(&block), None, rng); assert!(btf.add_special_block(block).is_ok()); check_last_event(btf, events); // b3 @@ -318,7 +339,7 @@ fn check_make_alternative_chain_longer(btf: &mut BlockTestFramework, events: &Ev assert!(btf.is_block_in_main_chain(btf.index_at(4).block_id())); } -fn check_simple_fork(btf: &mut BlockTestFramework, events: &EventList) { +fn check_simple_fork(btf: &mut BlockTestFramework, events: &EventList, rng: &mut impl Rng) { // Fork like this: // // +-- 0x6e45…e8e8 (H:0,B:0) = genesis @@ -329,9 +350,9 @@ fn check_simple_fork(btf: &mut BlockTestFramework, events: &EventList) { // // Nothing should happen at this point. We saw B2 first so it takes priority. // Don't reorg to a chain of the same length - assert!(btf.create_chain(&btf.genesis().get_id().into(), 2).is_ok()); + assert!(btf.create_chain(&btf.genesis().get_id().into(), 2, rng).is_ok()); check_last_event(btf, events); - assert!(btf.create_chain(&btf.index_at(1).block_id().clone().into(), 1).is_ok()); + assert!(btf.create_chain(&btf.index_at(1).block_id().clone().into(), 1, rng).is_ok()); check_last_event(btf, events); btf.test_block( diff --git a/chainstate/src/detail/tests/syncing_tests.rs b/chainstate/src/detail/tests/syncing_tests.rs index 04a49be577..c0a67688e7 100644 --- a/chainstate/src/detail/tests/syncing_tests.rs +++ b/chainstate/src/detail/tests/syncing_tests.rs @@ -45,12 +45,12 @@ fn get_locator() { assert_eq!(&locator[0], &btf.genesis().get_id()); // Expand the chain several times. - let mut rng = make_seedable_rng(None); + let mut rng = make_seedable_rng!(Seed::from_entropy()); let mut blocks = 1; let mut last_block_id: Id = btf.genesis().get_id().into(); for _ in 0..8 { let new_blocks = rng.gen_range(1..2000); - last_block_id = btf.create_chain(&last_block_id, new_blocks).unwrap(); + last_block_id = btf.create_chain(&last_block_id, new_blocks, &mut rng).unwrap(); blocks += new_blocks; // Check the locator length. @@ -78,12 +78,14 @@ fn get_locator() { #[test] fn get_headers() { common::concurrency::model(|| { - let mut rng = make_seedable_rng(None); + let mut rng = make_seedable_rng!(Seed::from_entropy()); let header_limit = i64::from(HEADER_LIMIT).try_into().unwrap(); + let headers_count = rng.gen_range(1000..header_limit); + let blocks_count = rng.gen_range(1000..2000); let mut btf = BlockTestFramework::new(); let mut last_block_id = btf.genesis().get_id().into(); - last_block_id = btf.create_chain(&last_block_id, rng.gen_range(1000..2000)).unwrap(); + last_block_id = btf.create_chain(&last_block_id, blocks_count, &mut rng).unwrap(); // The locator is from this exact chain, so `get_headers` should return an empty sequence. let locator = btf.chainstate().get_locator().unwrap(); @@ -94,16 +96,16 @@ fn get_headers() { // Produce more blocks. Now `get_headers` should return these blocks. let expected: Vec<_> = iter::from_fn(|| { - let block = produce_test_block(TestBlockInfo::from_id( - btf.chainstate(), - last_block_id.clone(), - )); + let block = produce_test_block( + TestBlockInfo::from_id(btf.chainstate(), last_block_id.clone()), + &mut rng, + ); last_block_id = block.get_id().into(); let header = block.header().clone(); btf.chainstate().process_block(block, BlockSource::Peer).unwrap().unwrap(); Some(header) }) - .take(rng.gen_range(1000..header_limit)) + .take(headers_count) .collect(); let headers = btf.chainstate().get_headers(locator.clone()).unwrap(); @@ -113,7 +115,8 @@ fn get_headers() { assert_eq!(expected[0].prev_block_id(), &locator[0]); // Produce more blocks than `HEADER_LIMIT`, so get_headers is truncated. - btf.create_chain(&last_block_id, header_limit - expected.len()).unwrap(); + btf.create_chain(&last_block_id, header_limit - expected.len(), &mut rng) + .unwrap(); let headers = btf.chainstate().get_headers(locator).unwrap(); assert_eq!(headers.len(), header_limit); }); @@ -124,16 +127,16 @@ fn get_headers() { #[test] fn get_headers_genesis() { common::concurrency::model(|| { - let mut rng = make_seedable_rng(None); + let mut rng = make_seedable_rng!(Seed::from_entropy()); let mut btf = BlockTestFramework::new(); let genesis_id: Id = btf.genesis().get_id().into(); - btf.create_chain(&genesis_id, rng.gen_range(64..128)).unwrap(); + btf.create_chain(&genesis_id, rng.gen_range(64..128), &mut rng).unwrap(); let locator_1 = btf.chainstate.get_locator().unwrap(); let chain_length = rng.gen_range(1200..2000); - btf.create_chain(&genesis_id, chain_length).unwrap(); + btf.create_chain(&genesis_id, chain_length, &mut rng).unwrap(); let locator_2 = btf.chainstate.get_locator().unwrap(); assert_ne!(locator_1, locator_2); assert!(locator_1.len() < locator_2.len()); @@ -149,16 +152,17 @@ fn get_headers_genesis() { #[test] fn get_headers_branching_chains() { common::concurrency::model(|| { - let mut rng = make_seedable_rng(None); + let mut rng = make_seedable_rng!(Seed::from_entropy()); let common_height = rng.gen_range(100..10_000); let mut btf = BlockTestFramework::new(); - let common_block_id = - btf.create_chain(&btf.genesis().get_id().into(), common_height).unwrap(); + let common_block_id = btf + .create_chain(&btf.genesis().get_id().into(), common_height, &mut rng) + .unwrap(); - btf.create_chain(&common_block_id, rng.gen_range(100..2500)).unwrap(); + btf.create_chain(&common_block_id, rng.gen_range(100..2500), &mut rng).unwrap(); let locator = btf.chainstate.get_locator().unwrap(); - btf.create_chain(&common_block_id, rng.gen_range(2500..5000)).unwrap(); + btf.create_chain(&common_block_id, rng.gen_range(2500..5000), &mut rng).unwrap(); let headers = btf.chainstate.get_headers(locator).unwrap(); let id = headers[0].prev_block_id(); @@ -171,7 +175,7 @@ fn get_headers_branching_chains() { #[test] fn get_headers_different_chains() { common::concurrency::model(|| { - let mut rng = make_seedable_rng(None); + let mut rng = make_seedable_rng!(Seed::from_entropy()); let mut btf1 = BlockTestFramework::new(); let mut btf2 = BlockTestFramework::new(); @@ -179,7 +183,7 @@ fn get_headers_different_chains() { let mut prev = TestBlockInfo::from_genesis(btf1.genesis()); assert_eq!(&prev, &TestBlockInfo::from_genesis(btf2.genesis())); for _ in 0..rng.gen_range(100..250) { - let block = btf1.random_block(prev, None); + let block = btf1.random_block(prev, None, &mut rng); prev = TestBlockInfo::from_block(&block); btf1.add_special_block(block.clone()).unwrap(); btf2.add_special_block(block.clone()).unwrap(); @@ -189,8 +193,8 @@ fn get_headers_different_chains() { ); } - btf1.create_chain(&prev.id, rng.gen_range(32..256)).unwrap(); - btf2.create_chain(&prev.id, rng.gen_range(256..512)).unwrap(); + btf1.create_chain(&prev.id, rng.gen_range(32..256), &mut rng).unwrap(); + btf2.create_chain(&prev.id, rng.gen_range(256..512), &mut rng).unwrap(); let locator = btf1.chainstate.get_locator().unwrap(); let headers = btf2.chainstate.get_headers(locator).unwrap(); @@ -207,14 +211,14 @@ fn get_headers_different_chains() { #[test] fn filter_already_existing_blocks() { common::concurrency::model(|| { - let mut rng = make_seedable_rng(None); + let mut rng = make_seedable_rng!(Seed::from_entropy()); let mut btf1 = BlockTestFramework::new(); let mut btf2 = BlockTestFramework::new(); let mut prev1 = TestBlockInfo::from_genesis(btf1.genesis()); for _ in 0..rng.gen_range(8..16) { - let block = btf1.random_block(prev1, None); + let block = btf1.random_block(prev1, None, &mut rng); prev1 = TestBlockInfo::from_block(&block); btf1.add_special_block(block.clone()).unwrap(); btf2.add_special_block(block.clone()).unwrap(); @@ -232,13 +236,13 @@ fn filter_already_existing_blocks() { // Add random blocks to both chains. for i in 0..(limit * 2) { if i <= limit { - let block = btf1.random_block(prev1, None); + let block = btf1.random_block(prev1, None, &mut rng); prev1 = TestBlockInfo::from_block(&block); headers1.push(block.header().clone()); btf1.add_special_block(block).unwrap(); } - let block = btf1.random_block(prev2, None); + let block = btf1.random_block(prev2, None, &mut rng); prev2 = TestBlockInfo::from_block(&block); headers2.push(block.header().clone()); btf2.add_special_block(block).unwrap(); @@ -263,14 +267,14 @@ fn filter_already_existing_blocks() { #[test] fn filter_already_existing_blocks_detached_headers() { common::concurrency::model(|| { - let mut rng = make_seedable_rng(None); + let mut rng = make_seedable_rng!(Seed::from_entropy()); let mut btf1 = BlockTestFramework::new(); let mut btf2 = BlockTestFramework::new(); let mut prev = TestBlockInfo::from_genesis(btf1.genesis()); for _ in 0..rng.gen_range(8..16) { - let block = btf1.random_block(prev, None); + let block = btf1.random_block(prev, None, &mut rng); prev = TestBlockInfo::from_block(&block); btf1.add_special_block(block.clone()).unwrap(); btf2.add_special_block(block.clone()).unwrap(); @@ -282,7 +286,7 @@ fn filter_already_existing_blocks_detached_headers() { let mut headers = Vec::new(); for _ in 0..rng.gen_range(3..10) { - let block = btf2.random_block(prev, None); + let block = btf2.random_block(prev, None, &mut rng); prev = TestBlockInfo::from_block(&block); headers.push(block.header().clone()); btf2.add_special_block(block).unwrap(); diff --git a/chainstate/src/detail/tests/test_framework.rs b/chainstate/src/detail/tests/test_framework.rs index 69769d6bf3..fe97ec914c 100644 --- a/chainstate/src/detail/tests/test_framework.rs +++ b/chainstate/src/detail/tests/test_framework.rs @@ -26,7 +26,7 @@ use common::{ }; #[derive(Debug, Eq, PartialEq)] -pub(crate) enum TestSpentStatus { +pub enum TestSpentStatus { Spent, Unspent, NotInMainchain, @@ -34,7 +34,7 @@ pub(crate) enum TestSpentStatus { // TODO: See https://github.com/mintlayer/mintlayer-core/issues/274 for details. #[allow(dead_code)] -pub(crate) enum TestBlockParams { +pub enum TestBlockParams { NoErrors, TxCount(usize), Fee(Amount), @@ -42,20 +42,20 @@ pub(crate) enum TestBlockParams { SpendFrom(Id), } -pub(crate) struct BlockTestFramework { +pub struct BlockTestFramework { pub chainstate: Chainstate, pub block_indexes: Vec, } impl BlockTestFramework { - pub(crate) fn with_chainstate(chainstate: Chainstate) -> Self { + pub fn with_chainstate(chainstate: Chainstate) -> Self { Self { chainstate, block_indexes: Vec::new(), } } - pub(crate) fn new() -> Self { + pub fn new() -> Self { let chainstate = setup_chainstate(); Self::with_chainstate(chainstate) } @@ -64,11 +64,12 @@ impl BlockTestFramework { &self, parent_info: TestBlockInfo, params: Option<&[TestBlockParams]>, + rng: &mut impl Rng, ) -> Block { let (mut inputs, outputs): (Vec, Vec) = parent_info .txns .into_iter() - .flat_map(|(s, o)| create_new_outputs(s, &o)) + .flat_map(|(s, o)| create_new_outputs(s, &o, rng)) .unzip(); let mut prev_block_hash = parent_info.id; @@ -119,21 +120,19 @@ impl BlockTestFramework { &mut self, parent_block_id: &Id, count_blocks: usize, + rng: &mut impl Rng, ) -> Result, BlockError> { let mut test_block_info = TestBlockInfo::from_id(&self.chainstate, parent_block_id.clone()); for _ in 0..count_blocks { - let block = produce_test_block(test_block_info); + let block = produce_test_block(test_block_info, rng); test_block_info = TestBlockInfo::from_block(&block); self.add_special_block(block.clone())?; } Ok(test_block_info.id) } - pub(crate) fn add_special_block( - &mut self, - block: Block, - ) -> Result, BlockError> { + pub fn add_special_block(&mut self, block: Block) -> Result, BlockError> { let id = block.get_id(); let block_index = self.chainstate.process_block(block, BlockSource::Local)?; self.block_indexes.push(block_index.clone().unwrap_or_else(|| { @@ -142,7 +141,7 @@ impl BlockTestFramework { Ok(block_index) } - pub(crate) fn get_spent_status( + pub fn get_spent_status( &self, tx_id: &Id, output_index: u32, @@ -183,7 +182,7 @@ impl BlockTestFramework { } } - pub(crate) fn test_block( + pub fn test_block( &self, block_id: &Id, prev_block_id: &Id, @@ -217,7 +216,7 @@ impl BlockTestFramework { self.check_block_at_height(block_index.block_height().next_height(), next_block_id); } - pub(crate) fn is_block_in_main_chain(&self, block_id: &Id) -> bool { + pub fn is_block_in_main_chain(&self, block_id: &Id) -> bool { let block_index = self .chainstate .chainstate_storage @@ -234,14 +233,11 @@ impl BlockTestFramework { } } - pub(crate) fn get_block( - &self, - block_id: Id, - ) -> Result, PropertyQueryError> { + pub fn get_block(&self, block_id: Id) -> Result, PropertyQueryError> { self.chainstate.get_block(block_id) } - pub(crate) fn chainstate(&mut self) -> &mut Chainstate { + pub fn chainstate(&mut self) -> &mut Chainstate { &mut self.chainstate } diff --git a/p2p/Cargo.toml b/p2p/Cargo.toml index 10571144be..a0b314aa38 100644 --- a/p2p/Cargo.toml +++ b/p2p/Cargo.toml @@ -42,6 +42,6 @@ portpicker = "0.1" chainstate-storage = { path = "../chainstate-storage" } crypto = { path = "../crypto/" } -[dev-dependencies.test-utils] +[dev-dependencies.p2p-test-utils] version = "0.1" -path = "test-utils" +path = "p2p-test-utils" diff --git a/p2p/test-utils/Cargo.toml b/p2p/p2p-test-utils/Cargo.toml similarity index 96% rename from p2p/test-utils/Cargo.toml rename to p2p/p2p-test-utils/Cargo.toml index 81c4787ad6..faa616ef42 100644 --- a/p2p/test-utils/Cargo.toml +++ b/p2p/p2p-test-utils/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "test-utils" +name = "p2p-test-utils" version = "0.1.0" edition = "2021" license = "MIT" diff --git a/p2p/test-utils/src/lib.rs b/p2p/p2p-test-utils/src/lib.rs similarity index 100% rename from p2p/test-utils/src/lib.rs rename to p2p/p2p-test-utils/src/lib.rs diff --git a/p2p/src/net/libp2p/backend.rs b/p2p/src/net/libp2p/backend.rs index b2832746da..cc141fdac7 100644 --- a/p2p/src/net/libp2p/backend.rs +++ b/p2p/src/net/libp2p/backend.rs @@ -336,7 +336,7 @@ mod tests { let (tx, rx) = oneshot::channel(); let res = cmd_tx .send(types::Command::Listen { - addr: test_utils::make_libp2p_addr(), + addr: p2p_test_utils::make_libp2p_addr(), response: tx, }) .await; @@ -347,7 +347,7 @@ mod tests { assert!(res.unwrap().is_ok()); } - // verify that binding twice to the same network inteface fails + // verify that binding twice to the same network interface fails #[ignore] #[tokio::test] async fn test_command_listen_addrinuse() { @@ -363,7 +363,7 @@ mod tests { let (tx, rx) = oneshot::channel(); let res = cmd_tx .send(types::Command::Listen { - addr: test_utils::make_libp2p_addr(), + addr: p2p_test_utils::make_libp2p_addr(), response: tx, }) .await; @@ -377,7 +377,7 @@ mod tests { let (tx, rx) = oneshot::channel(); let res = cmd_tx .send(types::Command::Listen { - addr: test_utils::make_libp2p_addr(), + addr: p2p_test_utils::make_libp2p_addr(), response: tx, }) .await; diff --git a/p2p/src/net/libp2p/tests/frontend.rs b/p2p/src/net/libp2p/tests/frontend.rs index 2eb2e11289..81ba9ff1c5 100644 --- a/p2p/src/net/libp2p/tests/frontend.rs +++ b/p2p/src/net/libp2p/tests/frontend.rs @@ -38,7 +38,7 @@ struct Transaction { async fn test_connect_new() { let config = Arc::new(common::chain::config::create_mainnet()); let service = Libp2pService::start( - test_utils::make_libp2p_addr(), + p2p_test_utils::make_libp2p_addr(), &[], config, Duration::from_secs(10), @@ -53,7 +53,7 @@ async fn test_connect_new() { async fn test_connect_new_addrinuse() { let config = Arc::new(common::chain::config::create_mainnet()); let service = Libp2pService::start( - test_utils::make_libp2p_addr(), + p2p_test_utils::make_libp2p_addr(), &[], Arc::clone(&config), Duration::from_secs(10), @@ -62,7 +62,7 @@ async fn test_connect_new_addrinuse() { assert!(service.is_ok()); let service = Libp2pService::start( - test_utils::make_libp2p_addr(), + p2p_test_utils::make_libp2p_addr(), &[], config, Duration::from_secs(10), @@ -86,14 +86,14 @@ async fn test_connect_new_addrinuse() { async fn test_connect_accept() { let config = Arc::new(common::chain::config::create_mainnet()); let service1 = Libp2pService::start( - test_utils::make_libp2p_addr(), + p2p_test_utils::make_libp2p_addr(), &[], Arc::clone(&config), Duration::from_secs(10), ) .await; let service2 = Libp2pService::start( - test_utils::make_libp2p_addr(), + p2p_test_utils::make_libp2p_addr(), &[], Arc::clone(&config), Duration::from_secs(10), @@ -120,7 +120,7 @@ async fn test_connect_peer_id_missing() { let config = Arc::new(common::chain::config::create_mainnet()); let addr: Multiaddr = "/ip6/::1/tcp/8904".parse().unwrap(); let (mut service, _, _) = Libp2pService::start( - test_utils::make_libp2p_addr(), + p2p_test_utils::make_libp2p_addr(), &[], config, Duration::from_secs(10), @@ -306,7 +306,7 @@ fn test_parse_peers_valid_3_peers_1_valid() { async fn test_connect_with_timeout() { let config = Arc::new(common::chain::config::create_mainnet()); let (mut service, _, _) = Libp2pService::start( - test_utils::make_libp2p_addr(), + p2p_test_utils::make_libp2p_addr(), &[], config, Duration::from_secs(2), diff --git a/p2p/src/net/libp2p/tests/gossipsub.rs b/p2p/src/net/libp2p/tests/gossipsub.rs index be6ff9c2db..f89dd9dbfc 100644 --- a/p2p/src/net/libp2p/tests/gossipsub.rs +++ b/p2p/src/net/libp2p/tests/gossipsub.rs @@ -19,8 +19,8 @@ use super::*; use crate::net::libp2p::{behaviour, types::*}; use futures::StreamExt; use libp2p::gossipsub::IdentTopic as Topic; +use p2p_test_utils::make_libp2p_addr; use serialization::Encode; -use test_utils::make_libp2p_addr; impl PartialEq for types::PubSubEvent { fn eq(&self, other: &Self) -> bool { diff --git a/p2p/src/net/libp2p/tests/identify.rs b/p2p/src/net/libp2p/tests/identify.rs index de22d0348c..3b7f0160e4 100644 --- a/p2p/src/net/libp2p/tests/identify.rs +++ b/p2p/src/net/libp2p/tests/identify.rs @@ -22,8 +22,13 @@ use std::time::Duration; #[tokio::test] async fn test_identify_not_supported() { let config = common::chain::config::create_mainnet(); - let (mut backend1, _cmd1, _conn1, _gossip1, _sync1) = - make_libp2p(config.clone(), test_utils::make_libp2p_addr(), &[], false).await; + let (mut backend1, _cmd1, _conn1, _gossip1, _sync1) = make_libp2p( + config.clone(), + p2p_test_utils::make_libp2p_addr(), + &[], + false, + ) + .await; let (transport, peer_id, _id_keys) = make_transport_and_keys(); let mut swarm = SwarmBuilder::new( diff --git a/p2p/src/net/libp2p/tests/mdns.rs b/p2p/src/net/libp2p/tests/mdns.rs index 8f545127e3..088ba0531a 100644 --- a/p2p/src/net/libp2p/tests/mdns.rs +++ b/p2p/src/net/libp2p/tests/mdns.rs @@ -21,7 +21,7 @@ use crate::net::libp2p::{ }; use futures::StreamExt; use libp2p::swarm::SwarmEvent; -use test_utils::make_libp2p_addr; +use p2p_test_utils::make_libp2p_addr; #[tokio::test] async fn test_discovered_and_expired() { diff --git a/p2p/src/net/libp2p/tests/ping.rs b/p2p/src/net/libp2p/tests/ping.rs index 2135471c6d..2bc8be8b03 100644 --- a/p2p/src/net/libp2p/tests/ping.rs +++ b/p2p/src/net/libp2p/tests/ping.rs @@ -21,8 +21,8 @@ use libp2p::{ ping, swarm::{SwarmBuilder, SwarmEvent}, }; +use p2p_test_utils::make_libp2p_addr; use std::time::Duration; -use test_utils::make_libp2p_addr; #[tokio::test] async fn test_remote_doesnt_respond() { diff --git a/p2p/src/net/libp2p/tests/swarm.rs b/p2p/src/net/libp2p/tests/swarm.rs index 09826c0a63..d767854114 100644 --- a/p2p/src/net/libp2p/tests/swarm.rs +++ b/p2p/src/net/libp2p/tests/swarm.rs @@ -24,7 +24,7 @@ use libp2p::{ tcp::TcpConfig, PeerId, Swarm, Transport, }; -use test_utils::make_libp2p_addr; +use p2p_test_utils::make_libp2p_addr; // TODO: add more tests at some point diff --git a/p2p/src/swarm/tests/ban.rs b/p2p/src/swarm/tests/ban.rs index baaba72ae9..4a13b49459 100644 --- a/p2p/src/swarm/tests/ban.rs +++ b/p2p/src/swarm/tests/ban.rs @@ -22,8 +22,8 @@ use crate::{ }; use common::chain::config; use libp2p::{Multiaddr, PeerId}; +use p2p_test_utils::make_libp2p_addr; use std::sync::Arc; -use test_utils::make_libp2p_addr; // ban peer whose connected to us #[tokio::test] diff --git a/p2p/src/swarm/tests/tmp.rs b/p2p/src/swarm/tests/tmp.rs index 5ffcef7fb4..3d3a9ba65b 100644 --- a/p2p/src/swarm/tests/tmp.rs +++ b/p2p/src/swarm/tests/tmp.rs @@ -23,8 +23,8 @@ use crate::{ use common::chain::config; use libp2p::{multiaddr::Protocol, Multiaddr, PeerId}; use logging::log; +use p2p_test_utils::make_libp2p_addr; use std::{net::SocketAddr, sync::Arc}; -use test_utils::make_libp2p_addr; // try to connect to an address that no one listening on and verify it fails #[tokio::test] diff --git a/p2p/src/sync/tests/block_response.rs b/p2p/src/sync/tests/block_response.rs index b6d1dc70cb..2b7f669236 100644 --- a/p2p/src/sync/tests/block_response.rs +++ b/p2p/src/sync/tests/block_response.rs @@ -17,7 +17,7 @@ use super::*; use chainstate::ChainstateError; use common::chain::block::consensus_data::PoWData; -use test_utils::{make_libp2p_addr, TestBlockInfo}; +use p2p_test_utils::{make_libp2p_addr, TestBlockInfo}; // peer doesn't exist #[tokio::test] @@ -41,7 +41,7 @@ async fn valid_block() { let peer_id = PeerId::random(); mgr.register_peer(peer_id).await.unwrap(); - let blocks = test_utils::create_n_blocks( + let blocks = p2p_test_utils::create_n_blocks( Arc::clone(&config), TestBlockInfo::from_genesis(config.genesis_block()), 1, @@ -68,7 +68,7 @@ async fn valid_block_invalid_state() { let peer_id = PeerId::random(); mgr.register_peer(peer_id).await.unwrap(); - let blocks = test_utils::create_n_blocks( + let blocks = p2p_test_utils::create_n_blocks( Arc::clone(&config), TestBlockInfo::from_genesis(config.genesis_block()), 1, @@ -90,7 +90,7 @@ async fn valid_block_resubmitted_chainstate() { let peer_id = PeerId::random(); mgr.register_peer(peer_id).await.unwrap(); - let blocks = test_utils::create_n_blocks( + let blocks = p2p_test_utils::create_n_blocks( Arc::clone(&config), TestBlockInfo::from_genesis(config.genesis_block()), 1, @@ -121,7 +121,7 @@ async fn invalid_block() { let peer_id = PeerId::random(); mgr.register_peer(peer_id).await.unwrap(); - let mut blocks = test_utils::create_n_blocks( + let mut blocks = p2p_test_utils::create_n_blocks( Arc::clone(&config), TestBlockInfo::from_genesis(config.genesis_block()), 1, diff --git a/p2p/src/sync/tests/connection.rs b/p2p/src/sync/tests/connection.rs index c39a0a6113..107bb3250f 100644 --- a/p2p/src/sync/tests/connection.rs +++ b/p2p/src/sync/tests/connection.rs @@ -15,7 +15,7 @@ // // Author(s): A. Altonen use super::*; -use test_utils::make_libp2p_addr; +use p2p_test_utils::make_libp2p_addr; // handle peer connection event #[tokio::test] diff --git a/p2p/src/sync/tests/header_response.rs b/p2p/src/sync/tests/header_response.rs index 15bb9446e7..07e0a6db52 100644 --- a/p2p/src/sync/tests/header_response.rs +++ b/p2p/src/sync/tests/header_response.rs @@ -16,7 +16,7 @@ // Author(s): A. Altonen use super::*; use crypto::random::{Rng, SliceRandom}; -use test_utils::{make_libp2p_addr, TestBlockInfo}; +use p2p_test_utils::{make_libp2p_addr, TestBlockInfo}; // response contains more than 2000 headers #[tokio::test] @@ -27,7 +27,7 @@ async fn too_many_headers() { let peer_id = PeerId::random(); mgr.register_peer(peer_id).await.unwrap(); - let headers = test_utils::create_n_blocks( + let headers = p2p_test_utils::create_n_blocks( Arc::clone(&config), TestBlockInfo::from_genesis(config.genesis_block()), 2001, @@ -67,7 +67,7 @@ async fn valid_response() { let peer_id = PeerId::random(); mgr.register_peer(peer_id).await.unwrap(); - let headers = test_utils::create_n_blocks( + let headers = p2p_test_utils::create_n_blocks( Arc::clone(&config), TestBlockInfo::from_genesis(config.genesis_block()), rng.gen_range(1..100), @@ -94,7 +94,7 @@ async fn header_doesnt_attach_to_local_chain() { let peer_id = PeerId::random(); mgr.register_peer(peer_id).await.unwrap(); - let headers = test_utils::create_n_blocks( + let headers = p2p_test_utils::create_n_blocks( Arc::clone(&config), TestBlockInfo::from_genesis(config.genesis_block()), rng.gen_range(2..100), @@ -120,7 +120,7 @@ async fn headers_not_in_order() { let peer_id = PeerId::random(); mgr.register_peer(peer_id).await.unwrap(); - let mut headers = test_utils::create_n_blocks( + let mut headers = p2p_test_utils::create_n_blocks( Arc::clone(&config), TestBlockInfo::from_genesis(config.genesis_block()), rng.gen_range(5..100), @@ -149,7 +149,7 @@ async fn invalid_state() { mgr.peers.get_mut(&peer_id).unwrap().set_state(peer::PeerSyncState::Unknown); - let headers = test_utils::create_n_blocks( + let headers = p2p_test_utils::create_n_blocks( Arc::clone(&config), TestBlockInfo::from_genesis(config.genesis_block()), rng.gen_range(1..100), diff --git a/p2p/src/sync/tests/request_response.rs b/p2p/src/sync/tests/request_response.rs index 86dfc0ce83..866e989773 100644 --- a/p2p/src/sync/tests/request_response.rs +++ b/p2p/src/sync/tests/request_response.rs @@ -16,8 +16,8 @@ // Author(s): A. Altonen use super::*; use crate::message::*; +use p2p_test_utils::make_libp2p_addr; use std::{collections::HashSet, time::Duration}; -use test_utils::make_libp2p_addr; use tokio::time::timeout; #[tokio::test] diff --git a/p2p/tests/ban.rs b/p2p/tests/ban.rs index 7a07dc840e..4056bdb28f 100644 --- a/p2p/tests/ban.rs +++ b/p2p/tests/ban.rs @@ -15,7 +15,7 @@ // // Author(s): A. Altonen #![allow(unused)] -extern crate test_utils; +extern crate p2p_test_utils; use libp2p::Multiaddr; use p2p::{ @@ -29,8 +29,8 @@ use p2p::{ pubsub::PubSubMessageHandler, sync::SyncManager, }; +use p2p_test_utils::{make_libp2p_addr, TestBlockInfo}; use std::sync::Arc; -use test_utils::{make_libp2p_addr, TestBlockInfo}; use tokio::sync::mpsc; async fn connect_services(conn1: &mut T::ConnectivityHandle, conn2: &mut T::ConnectivityHandle) @@ -56,7 +56,7 @@ async fn invalid_pubsub_block() { let (tx_pubsub, rx_pubsub) = mpsc::channel(16); let (tx_swarm, mut rx_swarm) = mpsc::channel(16); let config = Arc::new(common::chain::config::create_unit_test_config()); - let handle = test_utils::start_chainstate(Arc::clone(&config)).await; + let handle = p2p_test_utils::start_chainstate(Arc::clone(&config)).await; let (mut conn1, pubsub, _sync) = Libp2pService::start( make_libp2p_addr(), @@ -91,7 +91,7 @@ async fn invalid_pubsub_block() { // create few blocks so `pubsub2` has something to send to `pubsub1` let best_block = TestBlockInfo::from_genesis(config.genesis_block()); - let blocks = test_utils::create_n_blocks(Arc::clone(&config), best_block, 3); + let blocks = p2p_test_utils::create_n_blocks(Arc::clone(&config), best_block, 3); tokio::spawn(async move { tx_pubsub.send(PubSubControlEvent::InitialBlockDownloadDone).await.unwrap(); @@ -132,7 +132,7 @@ async fn invalid_sync_block() { let (tx_pubsub, rx_pubsub) = mpsc::channel(16); let (tx_swarm, mut rx_swarm) = mpsc::channel(16); let config = Arc::new(common::chain::config::create_unit_test_config()); - let handle = test_utils::start_chainstate(Arc::clone(&config)).await; + let handle = p2p_test_utils::start_chainstate(Arc::clone(&config)).await; let (mut conn1, _, sync1) = Libp2pService::start( make_libp2p_addr(), @@ -154,7 +154,7 @@ async fn invalid_sync_block() { // create few blocks and offer an orphan block to the `SyncManager` let best_block = TestBlockInfo::from_genesis(config.genesis_block()); - let blocks = test_utils::create_n_blocks(Arc::clone(&config), best_block, 3); + let blocks = p2p_test_utils::create_n_blocks(Arc::clone(&config), best_block, 3); // register random peer to the `SyncManager`, process a block response // and verify the `PeerManager` is notified of the protocol violation diff --git a/p2p/tests/libp2p-gossipsub.rs b/p2p/tests/libp2p-gossipsub.rs index ceba8e0050..11fff09d68 100644 --- a/p2p/tests/libp2p-gossipsub.rs +++ b/p2p/tests/libp2p-gossipsub.rs @@ -14,8 +14,8 @@ // limitations under the License. // // Author(s): A. Altonen -extern crate test_utils; -use test_utils::make_libp2p_addr; +extern crate p2p_test_utils; +use p2p_test_utils::make_libp2p_addr; use common::chain::{ block::{consensus_data::ConsensusData, timestamp::BlockTimestamp, Block}, diff --git a/p2p/tests/libp2p-mdns.rs b/p2p/tests/libp2p-mdns.rs index d96833be4a..3606ded44e 100644 --- a/p2p/tests/libp2p-mdns.rs +++ b/p2p/tests/libp2p-mdns.rs @@ -21,8 +21,8 @@ use p2p::net::{ types::ConnectivityEvent, ConnectivityService, NetworkingService, }; +use p2p_test_utils::make_libp2p_addr; use std::sync::Arc; -use test_utils::make_libp2p_addr; // verify that libp2p mdns peer discovery works #[tokio::test] diff --git a/p2p/tests/sync.rs b/p2p/tests/sync.rs index a2b8578b52..30d7b7e2a7 100644 --- a/p2p/tests/sync.rs +++ b/p2p/tests/sync.rs @@ -30,11 +30,11 @@ use p2p::{ sync::SyncManager, sync::SyncState, }; +use p2p_test_utils::{make_libp2p_addr, TestBlockInfo}; use std::{ collections::{HashSet, VecDeque}, sync::Arc, }; -use test_utils::{make_libp2p_addr, TestBlockInfo}; use tokio::sync::mpsc; async fn make_sync_manager( @@ -117,16 +117,16 @@ async fn init_chainstate_2( subsystem::Handle>, subsystem::Handle>, ) { - let handle1 = test_utils::start_chainstate(Arc::clone(&config)).await; - let handle2 = test_utils::start_chainstate(Arc::clone(&config)).await; - let blocks = test_utils::create_n_blocks( + let handle1 = p2p_test_utils::start_chainstate(Arc::clone(&config)).await; + let handle2 = p2p_test_utils::start_chainstate(Arc::clone(&config)).await; + let blocks = p2p_test_utils::create_n_blocks( Arc::clone(&config), TestBlockInfo::from_genesis(config.genesis_block()), num_blocks, ); - test_utils::import_blocks(&handle1, blocks.clone()).await; - test_utils::import_blocks(&handle2, blocks).await; + p2p_test_utils::import_blocks(&handle1, blocks.clone()).await; + p2p_test_utils::import_blocks(&handle2, blocks).await; (handle1, handle2) } @@ -139,18 +139,18 @@ async fn init_chainstate_3( subsystem::Handle>, subsystem::Handle>, ) { - let handle1 = test_utils::start_chainstate(Arc::clone(&config)).await; - let handle2 = test_utils::start_chainstate(Arc::clone(&config)).await; - let handle3 = test_utils::start_chainstate(Arc::clone(&config)).await; - let blocks = test_utils::create_n_blocks( + let handle1 = p2p_test_utils::start_chainstate(Arc::clone(&config)).await; + let handle2 = p2p_test_utils::start_chainstate(Arc::clone(&config)).await; + let handle3 = p2p_test_utils::start_chainstate(Arc::clone(&config)).await; + let blocks = p2p_test_utils::create_n_blocks( Arc::clone(&config), TestBlockInfo::from_genesis(config.genesis_block()), num_blocks, ); - test_utils::import_blocks(&handle1, blocks.clone()).await; - test_utils::import_blocks(&handle2, blocks.clone()).await; - test_utils::import_blocks(&handle3, blocks).await; + p2p_test_utils::import_blocks(&handle1, blocks.clone()).await; + p2p_test_utils::import_blocks(&handle2, blocks.clone()).await; + p2p_test_utils::import_blocks(&handle3, blocks).await; (handle1, handle2, handle3) } @@ -292,7 +292,7 @@ async fn remote_ahead_by_7_blocks() { // add 7 more blocks on top of the best block (which is also known by mgr1) assert!(same_tip(&mgr1_handle, &mgr2_handle).await); - test_utils::add_more_blocks(Arc::clone(&config), &mgr2_handle, 7).await; + p2p_test_utils::add_more_blocks(Arc::clone(&config), &mgr2_handle, 7).await; assert!(!same_tip(&mgr1_handle, &mgr2_handle).await); // add peer to the hashmap of known peers and send getheaders request to them @@ -383,7 +383,7 @@ async fn local_ahead_by_12_blocks() { // add 12 more blocks on top of the best block (which is also known by mgr2) assert!(same_tip(&mgr1_handle, &mgr2_handle).await); - test_utils::add_more_blocks(Arc::clone(&config), &mgr1_handle, 12).await; + p2p_test_utils::add_more_blocks(Arc::clone(&config), &mgr1_handle, 12).await; assert!(!same_tip(&mgr1_handle, &mgr2_handle).await); // add peer to the hashmap of known peers and send getheaders request to them @@ -500,10 +500,10 @@ async fn remote_local_diff_chains_local_higher() { // add 14 more blocks to local chain and 7 more blocks to remote chain assert!(same_tip(&mgr1_handle, &mgr2_handle).await); - test_utils::add_more_blocks(Arc::clone(&config), &mgr1_handle, 14).await; + p2p_test_utils::add_more_blocks(Arc::clone(&config), &mgr1_handle, 14).await; assert!(!same_tip(&mgr1_handle, &mgr2_handle).await); - test_utils::add_more_blocks(Arc::clone(&config), &mgr2_handle, 7).await; + p2p_test_utils::add_more_blocks(Arc::clone(&config), &mgr2_handle, 7).await; // save local and remote tips so we can verify who did a reorg let local_tip = get_tip(&mgr1_handle).await; @@ -642,10 +642,10 @@ async fn remote_local_diff_chains_remote_higher() { // add 5 more blocks to local chain and 12 more blocks to remote chain assert!(same_tip(&mgr1_handle, &mgr2_handle).await); - test_utils::add_more_blocks(Arc::clone(&config), &mgr1_handle, 5).await; + p2p_test_utils::add_more_blocks(Arc::clone(&config), &mgr1_handle, 5).await; assert!(!same_tip(&mgr1_handle, &mgr2_handle).await); - test_utils::add_more_blocks(Arc::clone(&config), &mgr2_handle, 12).await; + p2p_test_utils::add_more_blocks(Arc::clone(&config), &mgr2_handle, 12).await; // save local and remote tips so we can verify who did a reorg let local_tip = get_tip(&mgr1_handle).await; @@ -784,8 +784,8 @@ async fn two_remote_nodes_different_chains() { make_sync_manager::(make_libp2p_addr(), handle3).await; // add 5 more blocks for first remote and 7 blocks to second remote - test_utils::add_more_blocks(Arc::clone(&config), &mgr2_handle, 5).await; - test_utils::add_more_blocks(Arc::clone(&config), &mgr3_handle, 7).await; + p2p_test_utils::add_more_blocks(Arc::clone(&config), &mgr2_handle, 5).await; + p2p_test_utils::add_more_blocks(Arc::clone(&config), &mgr3_handle, 7).await; // save local and remote tips so we can verify who did a reorg let mgr2_tip = get_tip(&mgr2_handle).await; @@ -890,14 +890,14 @@ async fn two_remote_nodes_same_chains() { make_sync_manager::(make_libp2p_addr(), handle3).await; // add the same 32 new blocks for both mgr2 and mgr3 - let blocks = test_utils::create_n_blocks( + let blocks = p2p_test_utils::create_n_blocks( Arc::clone(&config), TestBlockInfo::from_tip(&mgr2_handle, &config).await, 32, ); - test_utils::import_blocks(&mgr2_handle, blocks.clone()).await; - test_utils::import_blocks(&mgr3_handle, blocks).await; + p2p_test_utils::import_blocks(&mgr2_handle, blocks.clone()).await; + p2p_test_utils::import_blocks(&mgr3_handle, blocks).await; // save local and remote tips so we can verify who did a reorg let mgr2_tip = get_tip(&mgr2_handle).await; @@ -1012,14 +1012,14 @@ async fn two_remote_nodes_same_chains_new_blocks() { make_sync_manager::(make_libp2p_addr(), handle3).await; // add the same 32 new blocks for both mgr2 and mgr3 - let blocks = test_utils::create_n_blocks( + let blocks = p2p_test_utils::create_n_blocks( Arc::clone(&config), TestBlockInfo::from_tip(&mgr2_handle, &config).await, 32, ); - test_utils::import_blocks(&mgr2_handle, blocks.clone()).await; - test_utils::import_blocks(&mgr3_handle, blocks).await; + p2p_test_utils::import_blocks(&mgr2_handle, blocks.clone()).await; + p2p_test_utils::import_blocks(&mgr3_handle, blocks).await; // connect remote peers to local peer connect_services::(&mut conn1, &mut conn2).await; @@ -1075,7 +1075,7 @@ async fn two_remote_nodes_same_chains_new_blocks() { if gethdr_received.insert(dest_peer_id) { if blocks.is_empty() { - blocks = test_utils::create_n_blocks( + blocks = p2p_test_utils::create_n_blocks( Arc::clone(&config), TestBlockInfo::from_tip(&mgr2_handle, &config).await, 10, @@ -1083,9 +1083,9 @@ async fn two_remote_nodes_same_chains_new_blocks() { } if dest_peer_id == conn2.peer_id() { - test_utils::import_blocks(&mgr2_handle, blocks.clone()).await; + p2p_test_utils::import_blocks(&mgr2_handle, blocks.clone()).await; } else { - test_utils::import_blocks(&mgr3_handle, blocks.clone()).await; + p2p_test_utils::import_blocks(&mgr3_handle, blocks.clone()).await; } } } @@ -1168,8 +1168,8 @@ async fn test_connect_disconnect_resyncing() { )); let parent_info = TestBlockInfo::from_tip(&mgr1_handle, &config).await; - let blocks = test_utils::create_n_blocks(Arc::clone(&config), parent_info, 7); - test_utils::import_blocks(&mgr2_handle, blocks.clone()).await; + let blocks = p2p_test_utils::create_n_blocks(Arc::clone(&config), parent_info, 7); + p2p_test_utils::import_blocks(&mgr2_handle, blocks.clone()).await; connect_services::(&mut conn1, &mut conn2).await; assert_eq!(mgr1.register_peer(*conn2.peer_id()).await, Ok(())); diff --git a/test-utils/Cargo.toml b/test-utils/Cargo.toml new file mode 100644 index 0000000000..38c57d6e19 --- /dev/null +++ b/test-utils/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "test-utils" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +crypto = {path = '../crypto'} +logging = {path = '../logging'} +rand_chacha = "0.3.1" diff --git a/test-utils/src/lib.rs b/test-utils/src/lib.rs new file mode 100644 index 0000000000..861ddb68bd --- /dev/null +++ b/test-utils/src/lib.rs @@ -0,0 +1,16 @@ +// Copyright (c) 2022 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://spdx.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub mod random; diff --git a/test-utils/src/random.rs b/test-utils/src/random.rs new file mode 100644 index 0000000000..cc7b27bc6d --- /dev/null +++ b/test-utils/src/random.rs @@ -0,0 +1,64 @@ +// Copyright (c) 2022 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://spdx.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crypto::random::{Rng, SeedableRng}; +use rand_chacha::ChaChaRng; + +pub struct Seed(pub u64); + +impl Seed { + pub fn from_entropy() -> Self { + Seed(crypto::random::make_true_rng().gen::()) + } + + pub fn from_u64(v: u64) -> Self { + Seed(v) + } +} + +#[must_use] +pub fn make_seedable_rng(seed: Seed) -> impl Rng { + ChaChaRng::seed_from_u64(seed.0) +} + +/// Makes PRNG that should be used in unit tests to get deterministic values from non-deterministic seed. +/// +/// # Example +/// +/// ``` +/// use test_utils::{make_seedable_rng, random::*}; +/// let mut rng = make_seedable_rng!(Seed::from_entropy()); +/// ``` +/// If the test case fails a seed will be printed to std out, e.g: +/// +/// `chainstate/src/detail/tests/double_spend_tests.rs:45 Using seed '4862969352335513650' for the PRNG` +/// +/// That output can be used to reproduce the fail by passing the seed from printed integer instead of entropy: +/// ``` +/// use test_utils::{make_seedable_rng, random::*}; +/// let mut rng = make_seedable_rng!(Seed::from_u64(4862969352335513650)); +/// ``` +#[macro_export] +macro_rules! make_seedable_rng { + ($seed:expr) => {{ + println!( + "{}:{} Using seed '{}' for the PRNG", + file!(), + line!(), + $seed.0 + ); + make_seedable_rng($seed) + }}; +} From 2f2a673d0b5a7a68a4be8f8dbfcf4d1179b0cbbd Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Tue, 26 Jul 2022 12:40:24 +0300 Subject: [PATCH 6/9] Move to rstest --- Cargo.lock | 27 +++++- chainstate-storage/src/store.rs | 11 ++- chainstate-storage/src/utxo_db.rs | 17 ++-- chainstate/Cargo.toml | 1 + chainstate/src/detail/orphan_blocks/pool.rs | 84 ++++++++++++------- .../src/detail/tests/double_spend_tests.rs | 49 ++++++----- chainstate/src/detail/tests/events_tests.rs | 52 +++++++----- chainstate/src/detail/tests/mod.rs | 12 +-- .../src/detail/tests/processing_tests.rs | 72 +++++++++------- chainstate/src/detail/tests/reorgs_tests.rs | 20 +++-- chainstate/src/detail/tests/syncing_tests.rs | 70 +++++++++------- test-utils/Cargo.toml | 1 - test-utils/src/random.rs | 31 +------ 13 files changed, 261 insertions(+), 186 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1b910cad72..f2ea118440 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -525,6 +525,7 @@ dependencies = [ "num", "replace_with", "rpc", + "rstest", "serde", "serde_json", "serialization", @@ -3314,6 +3315,31 @@ dependencies = [ "tokio", ] +[[package]] +name = "rstest" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9c9dc66cc29792b663ffb5269be669f1613664e69ad56441fdb895c2347b930" +dependencies = [ + "futures", + "futures-timer", + "rstest_macros", + "rustc_version 0.4.0", +] + +[[package]] +name = "rstest_macros" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5015e68a0685a95ade3eee617ff7101ab6a3fc689203101ca16ebc16f2b89c66" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "rustc_version 0.4.0", + "syn", +] + [[package]] name = "rtnetlink" version = "0.9.1" @@ -4085,7 +4111,6 @@ name = "test-utils" version = "0.1.0" dependencies = [ "crypto", - "logging", "rand_chacha 0.3.1", ] diff --git a/chainstate-storage/src/store.rs b/chainstate-storage/src/store.rs index f43a8b9796..958e1653c2 100644 --- a/chainstate-storage/src/store.rs +++ b/chainstate-storage/src/store.rs @@ -413,7 +413,8 @@ pub(crate) mod test { use common::primitives::{Amount, H256}; use crypto::key::{KeyKind, PrivateKey}; use crypto::random::Rng; - use test_utils::{make_seedable_rng, random::*}; + use rstest::*; + use test_utils::random::*; use utxo::{BlockUndo, TxUndo}; #[test] @@ -691,9 +692,11 @@ pub(crate) mod test { } #[cfg(not(loom))] - #[test] - fn undo_test() { - let mut rng = make_seedable_rng!(Seed::from_entropy()); + #[rstest] + #[trace] + #[case(Seed::from_entropy())] + fn undo_test(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); let block_undo0 = create_rand_block_undo(&mut rng, 10, 5, BlockHeight::new(1)); // create id: let id0: Id = Id::new(H256::random()); diff --git a/chainstate-storage/src/utxo_db.rs b/chainstate-storage/src/utxo_db.rs index 46ad6e8e6e..3a40718aca 100644 --- a/chainstate-storage/src/utxo_db.rs +++ b/chainstate-storage/src/utxo_db.rs @@ -74,12 +74,11 @@ mod test { use common::chain::{Destination, OutPoint, OutPointSourceId, OutputPurpose, TxOutput}; use common::primitives::{Amount, BlockHeight, H256}; use crypto::key::{KeyKind, PrivateKey}; - use crypto::random::Rng; - use test_utils::{make_seedable_rng, random::*}; + use rstest::*; + use test_utils::random::*; - fn create_utxo(block_height: u64) -> (Utxo, OutPoint) { + fn create_utxo(block_height: u64, output_value: u128) -> (Utxo, OutPoint) { // just a random value generated, and also a random `is_block_reward` value. - let random_value = make_seedable_rng!(Seed::from_entropy()).gen_range(0..u128::MAX); let (_, pub_key) = PrivateKey::new(KeyKind::RistrettoSchnorr); let output = TxOutput::new( Amount::from_atoms(random_value), @@ -96,13 +95,16 @@ mod test { } #[cfg(not(loom))] - #[test] - fn db_impl_test() { + #[rstest] + #[trace] + #[case(Seed::from_entropy())] + fn db_impl_test(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); let store = Store::new_empty().expect("should create a store"); let mut db_interface = UtxoDBImpl::new(store); // utxo checking - let (utxo, outpoint) = create_utxo(1); + let (utxo, outpoint) = create_utxo(1, rng.gen_range(0..u128::MAX)); assert!(db_interface.set_utxo(&outpoint, utxo.clone()).is_ok()); assert_eq!(db_interface.get_utxo(&outpoint), Ok(Some(utxo))); assert!(db_interface.del_utxo(&outpoint).is_ok()); @@ -121,7 +123,6 @@ mod test { ); // undo checking - let mut rng = make_seedable_rng!(Seed::from_entropy()); let undo = create_rand_block_undo(&mut rng, 10, 10, BlockHeight::new(10)); assert!(db_interface.set_undo_data(block_id.clone(), &undo).is_ok()); diff --git a/chainstate/Cargo.toml b/chainstate/Cargo.toml index 16d844ddd1..dad1618750 100644 --- a/chainstate/Cargo.toml +++ b/chainstate/Cargo.toml @@ -28,6 +28,7 @@ serde = { version = "1", features = ["derive"] } [dev-dependencies] mockall = "0.11" +rstest = "0.15" serde_json = "1.0" static_assertions = "1.1" test-utils = {path = '../test-utils'} diff --git a/chainstate/src/detail/orphan_blocks/pool.rs b/chainstate/src/detail/orphan_blocks/pool.rs index 54d3286891..bb8a6937f1 100644 --- a/chainstate/src/detail/orphan_blocks/pool.rs +++ b/chainstate/src/detail/orphan_blocks/pool.rs @@ -164,7 +164,8 @@ mod tests { use checkers::*; use common::{chain::block::Block, primitives::Id}; use helpers::*; - use test_utils::{make_seedable_rng, random::*}; + use rstest::*; + use test_utils::random::*; const MAX_ORPHAN_BLOCKS: usize = 512; @@ -282,12 +283,14 @@ mod tests { check_empty_pool(&orphans_pool); } - #[test] - fn test_add_one_block_and_clear() { + #[rstest] + #[trace] + #[case(Seed::from_entropy())] + fn test_add_one_block_and_clear(#[case] seed: Seed) { let mut orphans_pool = OrphanBlocksPool::new(MAX_ORPHAN_BLOCKS); // add a random block - let mut rng = make_seedable_rng!(Seed::from_entropy()); + let mut rng = make_seedable_rng(seed); let block = gen_random_block(&mut rng); assert!(orphans_pool.add_block(block.clone()).is_ok()); @@ -300,12 +303,14 @@ mod tests { check_empty_pool(&orphans_pool); } - #[test] - fn test_add_blocks_and_clear() { + #[rstest] + #[trace] + #[case(Seed::from_entropy())] + fn test_add_blocks_and_clear(#[case] seed: Seed) { let mut orphans_pool = OrphanBlocksPool::new(MAX_ORPHAN_BLOCKS); // add a random block - let mut rng = make_seedable_rng!(Seed::from_entropy()); + let mut rng = make_seedable_rng(seed); let block = gen_random_block(&mut rng); assert!(orphans_pool.add_block(block.clone()).is_ok()); assert_eq!(orphans_pool.len(), 1); @@ -345,11 +350,13 @@ mod tests { check_empty_pool(&orphans_pool); } - #[test] - fn test_add_block_exceeds_max() { + #[rstest] + #[trace] + #[case(Seed::from_entropy())] + fn test_add_block_exceeds_max(#[case] seed: Seed) { let max_orphans = 3; let mut orphans_pool = OrphanBlocksPool::new(max_orphans); - let mut rng = make_seedable_rng!(Seed::from_entropy()); + let mut rng = make_seedable_rng(seed); let blocks = gen_random_blocks(&mut rng, max_orphans as u32 + 2); blocks.into_iter().for_each(|block| { @@ -359,10 +366,12 @@ mod tests { check_pool_length(&orphans_pool, max_orphans); } - #[test] - fn test_add_block_repeated() { + #[rstest] + #[trace] + #[case(Seed::from_entropy())] + fn test_add_block_repeated(#[case] seed: Seed) { let mut orphans_pool = OrphanBlocksPool::new(MAX_ORPHAN_BLOCKS); - let mut rng = make_seedable_rng!(Seed::from_entropy()); + let mut rng = make_seedable_rng(seed); let blocks = gen_random_blocks(&mut rng, 50); blocks.iter().for_each(|block| { @@ -377,10 +386,12 @@ mod tests { ); } - #[test] - fn test_pool_drop_block() { + #[rstest] + #[trace] + #[case(Seed::from_entropy())] + fn test_pool_drop_block(#[case] seed: Seed) { let mut orphans_pool = OrphanBlocksPool::new(MAX_ORPHAN_BLOCKS); - let mut rng = make_seedable_rng!(Seed::from_entropy()); + let mut rng = make_seedable_rng(seed); let blocks = gen_random_blocks(&mut rng, 5); blocks.iter().for_each(|block| { @@ -398,11 +409,12 @@ mod tests { assert!(!orphans_pool.orphan_by_prev_id.contains_key(&rand_block.prev_block_id())); } - #[test] - fn test_deepest_child_in_chain() { + #[rstest] + #[trace] + #[case(Seed::from_entropy())] + fn test_deepest_child_in_chain(#[case] seed: Seed) { let mut orphans_pool = OrphanBlocksPool::new(MAX_ORPHAN_BLOCKS); - - let mut rng = make_seedable_rng!(Seed::from_entropy()); + let mut rng = make_seedable_rng(seed); // In `orphans_by_prev_id`: // [ @@ -450,10 +462,12 @@ mod tests { check_block_existence(&orphans_pool, first_block); } - #[test] - fn test_deepest_child_common_parent() { + #[rstest] + #[trace] + #[case(Seed::from_entropy())] + fn test_deepest_child_common_parent(#[case] seed: Seed) { let mut orphans_pool = OrphanBlocksPool::new(MAX_ORPHAN_BLOCKS); - let mut rng = make_seedable_rng!(Seed::from_entropy()); + let mut rng = make_seedable_rng(seed); // In `orphans_by_prev_id`: // [ // ( a, (b,c,d,e,f) ), @@ -498,10 +512,12 @@ mod tests { } } - #[test] - fn test_prune() { + #[rstest] + #[trace] + #[case(Seed::from_entropy())] + fn test_prune(#[case] seed: Seed) { let mut orphans_pool = OrphanBlocksPool::new(12); - let mut rng = make_seedable_rng!(Seed::from_entropy()); + let mut rng = make_seedable_rng(seed); // in `orphans_by_prev_id`: // [ // ( a, (b,c,d,e) ) @@ -572,10 +588,12 @@ mod tests { check_pool_length(&orphans_pool, orphans_pool.max_orphans - 1); } - #[test] - fn test_simple_take_all_children_of() { + #[rstest] + #[trace] + #[case(Seed::from_entropy())] + fn test_simple_take_all_children_of(#[case] seed: Seed) { let mut orphans_pool = OrphanBlocksPool::new(20); - let mut rng = make_seedable_rng!(Seed::from_entropy()); + let mut rng = make_seedable_rng(seed); let count = 9; // in `orphans_by_prev_id`: @@ -614,10 +632,12 @@ mod tests { check_pool_length(&orphans_pool, conn_blocks_len); } - #[test] - fn test_mix_chain_take_all_children_of() { + #[rstest] + #[trace] + #[case(Seed::from_entropy())] + fn test_mix_chain_take_all_children_of(#[case] seed: Seed) { let mut orphans_pool = OrphanBlocksPool::new(20); - let mut rng = make_seedable_rng!(Seed::from_entropy()); + let mut rng = make_seedable_rng(seed); let count = 9; // in `orphans_by_prev_id`: diff --git a/chainstate/src/detail/tests/double_spend_tests.rs b/chainstate/src/detail/tests/double_spend_tests.rs index 9d13faed02..be647929a5 100644 --- a/chainstate/src/detail/tests/double_spend_tests.rs +++ b/chainstate/src/detail/tests/double_spend_tests.rs @@ -37,12 +37,14 @@ use common::{ // | |input = tx1 | | // | +-------------------+ | // +-----------------------+ -#[test] -fn spend_output_in_the_same_block() { - common::concurrency::model(|| { +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn spend_output_in_the_same_block(#[case] seed: Seed) { + common::concurrency::model(move || { let mut chainstate = setup_chainstate(); - let mut rng = make_seedable_rng!(Seed::from_entropy()); + let mut rng = make_seedable_rng(seed); let tx1_output_value = rng.gen_range(100_000..200_000); let first_tx = tx_from_genesis(&chainstate, &mut rng, tx1_output_value); let second_tx = tx_from_tx(&first_tx, rng.gen_range(1000..2000)); @@ -64,6 +66,7 @@ fn spend_output_in_the_same_block() { .expect(ERR_BEST_BLOCK_NOT_FOUND), Some(>::from(block_id)) ); + panic!(); }); } @@ -80,12 +83,14 @@ fn spend_output_in_the_same_block() { // | |input = prev_block | | // | +-------------------+ | // +-----------------------+ -#[test] -fn spend_output_in_the_same_block_invalid_order() { - common::concurrency::model(|| { +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn spend_output_in_the_same_block_invalid_order(#[case] seed: Seed) { + common::concurrency::model(move || { let mut chainstate = setup_chainstate(); - let mut rng = make_seedable_rng!(Seed::from_entropy()); + let mut rng = make_seedable_rng(seed); let tx1_output_value = rng.gen_range(100_000..200_000); let first_tx = tx_from_genesis(&chainstate, &mut rng, tx1_output_value); let second_tx = tx_from_tx(&first_tx, rng.gen_range(1000..2000)); @@ -128,12 +133,14 @@ fn spend_output_in_the_same_block_invalid_order() { // | |input = tx1 | | // | +-------------------+ | // +-----------------------+ -#[test] -fn double_spend_tx_in_the_same_block() { - common::concurrency::model(|| { +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn double_spend_tx_in_the_same_block(#[case] seed: Seed) { + common::concurrency::model(move || { let mut chainstate = setup_chainstate(); - let mut rng = make_seedable_rng!(Seed::from_entropy()); + let mut rng = make_seedable_rng(seed); let tx1_output_value = rng.gen_range(100_000..200_000); let first_tx = tx_from_genesis(&chainstate, &mut rng, tx1_output_value); let second_tx = tx_from_tx(&first_tx, rng.gen_range(1000..2000)); @@ -181,12 +188,14 @@ fn double_spend_tx_in_the_same_block() { // | |input = genesis | | // | +-------------------+ | // +-----------------------+ -#[test] -fn double_spend_tx_in_another_block() { - common::concurrency::model(|| { +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn double_spend_tx_in_another_block(#[case] seed: Seed) { + common::concurrency::model(move || { let mut chainstate = setup_chainstate(); - let mut rng = make_seedable_rng!(Seed::from_entropy()); + let mut rng = make_seedable_rng(seed); let tx1_output_value = rng.gen_range(100_000..200_000); let first_tx = tx_from_genesis(&chainstate, &mut rng, tx1_output_value); let first_block = Block::new( @@ -244,12 +253,14 @@ fn double_spend_tx_in_another_block() { // | |input = tx1 | | // | +-------------------+ | // +-----------------------+ -#[test] -fn spend_bigger_output_in_the_same_block() { +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn spend_bigger_output_in_the_same_block(#[case] seed: Seed) { common::concurrency::model(move || { let mut chainstate = setup_chainstate(); - let mut rng = make_seedable_rng!(Seed::from_entropy()); + let mut rng = make_seedable_rng(seed); let tx1_output_value = rng.gen_range(1000..2000); let tx2_output_value = rng.gen_range(100_000..200_000); let first_tx = tx_from_genesis(&chainstate, &mut rng, tx1_output_value); diff --git a/chainstate/src/detail/tests/events_tests.rs b/chainstate/src/detail/tests/events_tests.rs index c00bc22f7e..e5e0a76dc1 100644 --- a/chainstate/src/detail/tests/events_tests.rs +++ b/chainstate/src/detail/tests/events_tests.rs @@ -23,13 +23,15 @@ use chainstate_storage::Store; type ErrorList = Arc>>; // Subscribe to events, process a block and check that the `NewTip` event is triggered. -#[test] -fn simple_subscribe() { - common::concurrency::model(|| { +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn simple_subscribe(#[case] seed: Seed) { + common::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); let mut chainstate = setup_chainstate(); let events = subscribe(&mut chainstate, 1); - let mut rng = make_seedable_rng!(Seed::from_entropy()); // Produce and process a block. let first_block = produce_test_block( TestBlockInfo::from_genesis(chainstate.chain_config.genesis_block()), @@ -65,12 +67,14 @@ fn simple_subscribe() { } // Subscribe to events several times, then process a block. -#[test] -fn several_subscribers() { - common::concurrency::model(|| { +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn several_subscribers(#[case] seed: Seed) { + common::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); let mut chainstate = setup_chainstate(); - let mut rng = make_seedable_rng!(Seed::from_entropy()); let subscribers = rng.gen_range(8..256); let events = subscribe(&mut chainstate, subscribers); @@ -92,12 +96,14 @@ fn several_subscribers() { }); } -#[test] -fn several_subscribers_several_events() { - common::concurrency::model(|| { +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn several_subscribers_several_events(#[case] seed: Seed) { + common::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); let mut chainstate = setup_chainstate(); - let mut rng = make_seedable_rng!(Seed::from_entropy()); let subscribers = rng.gen_range(4..16); let blocks = rng.gen_range(8..128); @@ -125,9 +131,12 @@ fn several_subscribers_several_events() { } // An orphan block is rejected during processing, so it shouldn't trigger the new tip event. -#[test] -fn orphan_block() { - common::concurrency::model(|| { +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn orphan_block(#[case] seed: Seed) { + common::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); let chain_config = Arc::new(create_unit_test_config()); let chainstate_config = ChainstateConfig::new(); let storage = Store::new_empty().unwrap(); @@ -144,8 +153,6 @@ fn orphan_block() { let events = subscribe(&mut chainstate, 1); assert!(!chainstate.events_controller.subscribers().is_empty()); - let mut rng = make_seedable_rng!(Seed::from_entropy()); - let block = produce_test_block( TestBlockInfo::from_genesis(chainstate.chain_config.genesis_block()).orphan(), &mut rng, @@ -160,9 +167,12 @@ fn orphan_block() { }); } -#[test] -fn custom_orphan_error_hook() { - common::concurrency::model(|| { +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn custom_orphan_error_hook(#[case] seed: Seed) { + common::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); let chain_config = Arc::new(create_unit_test_config()); let chainstate_config = ChainstateConfig::new(); let storage = Store::new_empty().unwrap(); @@ -179,8 +189,6 @@ fn custom_orphan_error_hook() { let events = subscribe(&mut chainstate, 1); assert!(!chainstate.events_controller.subscribers().is_empty()); - let mut rng = make_seedable_rng!(Seed::from_entropy()); - let first_block = produce_test_block( TestBlockInfo::from_genesis(chainstate.chain_config.genesis_block()), &mut rng, diff --git a/chainstate/src/detail/tests/mod.rs b/chainstate/src/detail/tests/mod.rs index e103f5884b..8e89fb2590 100644 --- a/chainstate/src/detail/tests/mod.rs +++ b/chainstate/src/detail/tests/mod.rs @@ -31,9 +31,9 @@ use common::{ Uint256, }; use crypto::random::{Rng, SliceRandom}; +use rstest::*; use serialization::Encode; - -use test_utils::{make_seedable_rng, random::*}; +use test_utils::random::*; mod double_spend_tests; mod events_tests; @@ -178,8 +178,11 @@ fn create_new_outputs( // Generate 5 regtest blocks and print their hex encoding, which is useful for functional tests. // TODO: remove when block production is ready #[ignore] -#[test] -fn generate_blocks_for_functional_tests() { +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn generate_blocks_for_functional_tests(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); let chain_config = create_regtest(); let mut prev_block = TestBlockInfo::from_genesis(chain_config.genesis_block()); let chainstate_config = ChainstateConfig::new(); @@ -188,7 +191,6 @@ fn generate_blocks_for_functional_tests() { let difficulty = Uint256([0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF]); - let mut rng = make_seedable_rng!(Seed::from_entropy()); for _ in 1..6 { let mut mined_block = btf.random_block(prev_block, None, &mut rng); let bits = difficulty.into(); diff --git a/chainstate/src/detail/tests/processing_tests.rs b/chainstate/src/detail/tests/processing_tests.rs index eceab8371d..8fd388d460 100644 --- a/chainstate/src/detail/tests/processing_tests.rs +++ b/chainstate/src/detail/tests/processing_tests.rs @@ -65,9 +65,12 @@ fn process_genesis_block() { }); } -#[test] -fn orphans_chains() { - common::concurrency::model(|| { +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn orphans_chains(#[case] seed: Seed) { + common::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); let mut chainstate = setup_chainstate(); assert_eq!( chainstate.get_best_block_id().unwrap(), @@ -75,7 +78,6 @@ fn orphans_chains() { ); // Process the orphan block - let mut rng = make_seedable_rng!(Seed::from_entropy()); let genesis_block = chainstate.chain_config.genesis_block().clone(); let missing_block = produce_test_block(TestBlockInfo::from_genesis(&genesis_block), &mut rng); @@ -140,13 +142,15 @@ fn empty_chainstate_no_genesis() { }) } -#[test] -fn spend_inputs_simple() { - common::concurrency::model(|| { +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn spend_inputs_simple(#[case] seed: Seed) { + common::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); let mut chainstate = setup_chainstate(); // Create a new block - let mut rng = make_seedable_rng!(Seed::from_entropy()); let block = produce_test_block( TestBlockInfo::from_genesis(chainstate.chain_config.genesis_block()), &mut rng, @@ -192,9 +196,12 @@ fn spend_inputs_simple() { } // Produce and process some blocks. -#[test] -fn straight_chain() { - common::concurrency::model(|| { +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn straight_chain(#[case] seed: Seed) { + common::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); let chain_config = Arc::new(create_unit_test_config()); let chainstate_config = ChainstateConfig::new(); let storage = Store::new_empty().unwrap(); @@ -207,7 +214,6 @@ fn straight_chain() { ) .unwrap(); - let mut rng = make_seedable_rng!(Seed::from_entropy()); let genesis_index = chainstate .make_db_tx_ro() .get_gen_block_index(&chainstate.chain_config.genesis_block_id()) @@ -261,11 +267,13 @@ fn straight_chain() { }); } -#[test] -fn get_ancestor_invalid_height() { +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn get_ancestor_invalid_height(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); let mut btf = BlockTestFramework::new(); let height = 1; - let mut rng = make_seedable_rng!(Seed::from_entropy()); btf.create_chain(&btf.genesis().get_id().into(), height, &mut rng).unwrap(); let last_block_index = btf.block_indexes.last().expect("last block in first chain").clone(); @@ -285,8 +293,11 @@ fn get_ancestor_invalid_height() { ); } -#[test] -fn get_ancestor() { +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn get_ancestor(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); let mut btf = BlockTestFramework::new(); // We will create two chains that split at height 100 @@ -294,7 +305,6 @@ fn get_ancestor() { const ANCESTOR_HEIGHT: usize = 50; const FIRST_CHAIN_HEIGHT: usize = 500; const SECOND_CHAIN_LENGTH: usize = 300; - let mut rng = make_seedable_rng!(Seed::from_entropy()); btf.create_chain(&btf.genesis().get_id().into(), SPLIT_HEIGHT, &mut rng) .expect("Chain creation to succeed"); @@ -396,15 +406,17 @@ fn get_ancestor() { } // Create two chains that split at height 100. -#[test] -fn last_common_ancestor() { +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn last_common_ancestor(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); let mut btf = BlockTestFramework::new(); const SPLIT_HEIGHT: usize = 100; const FIRST_CHAIN_HEIGHT: usize = 500; const SECOND_CHAIN_LENGTH: usize = 300; - let mut rng = make_seedable_rng!(Seed::from_entropy()); btf.create_chain(&btf.genesis().get_id().into(), SPLIT_HEIGHT, &mut rng) .expect("Chain creation to succeed"); let config_clone = btf.chainstate.chain_config.clone(); @@ -468,8 +480,11 @@ fn last_common_ancestor() { ); } -#[test] -fn consensus_type() { +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn consensus_type(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); let ignore_consensus = BlockHeight::new(0); let pow = BlockHeight::new(5); let ignore_again = BlockHeight::new(10); @@ -506,13 +521,12 @@ fn consensus_type() { // Internally this calls Consensus::new, which processes the genesis block // This should succeed because config::Builder by default uses create_mainnet_genesis to // create the genesis_block, and this function creates a genesis block with - // ConsenssuData::None, which agreess with the net_upgrades we defined above. + // ConsenssuData::None, which agrees with the net_upgrades we defined above. let chain_config = ConfigBuilder::test_chain().net_upgrades(net_upgrades).build(); let chainstate_config = ChainstateConfig::new(); let chainstate = chainstate_with_config(chain_config, chainstate_config); let mut btf = BlockTestFramework::with_chainstate(chainstate); - let mut rng = make_seedable_rng!(Seed::from_entropy()); // The next block will have height 1. At this height, we are still under IgnoreConsensus, so // processing a block with PoWData will fail @@ -627,8 +641,11 @@ fn consensus_type() { } } -#[test] -fn pow() { +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn pow(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); let ignore_consensus = BlockHeight::new(0); let pow_consensus = BlockHeight::new(1); let difficulty = @@ -657,7 +674,6 @@ fn pow() { let chainstate_config = ChainstateConfig::new(); let chainstate = chainstate_with_config(chain_config, chainstate_config); - let mut rng = make_seedable_rng!(Seed::from_entropy()); let mut btf = BlockTestFramework::with_chainstate(chainstate); // Let's create a block with random (invalid) PoW data and see that it fails the consensus diff --git a/chainstate/src/detail/tests/reorgs_tests.rs b/chainstate/src/detail/tests/reorgs_tests.rs index aa04cd8cd9..716f076ec9 100644 --- a/chainstate/src/detail/tests/reorgs_tests.rs +++ b/chainstate/src/detail/tests/reorgs_tests.rs @@ -28,10 +28,12 @@ use chainstate_storage::{BlockchainStorageRead, Store}; use common::chain::config::create_unit_test_config; // Produce `genesis -> a` chain, then a parallel `genesis -> b -> c` that should trigger a reorg. -#[test] -fn reorg_simple() { - common::concurrency::model(|| { - let mut rng = make_seedable_rng!(Seed::from_entropy()); +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn reorg_simple(#[case] seed: Seed) { + common::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); let chain_config = Arc::new(create_unit_test_config()); let chainstate_config = ChainstateConfig::new(); let storage = Store::new_empty().unwrap(); @@ -100,14 +102,16 @@ fn reorg_simple() { }); } -#[test] -fn test_very_long_reorgs() { - common::concurrency::model(|| { +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn test_very_long_reorgs(#[case] seed: Seed) { + common::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); let mut btf = BlockTestFramework::new(); let events: EventList = Arc::new(Mutex::new(Vec::new())); subscribe_to_events(&mut btf, &events); - let mut rng = make_seedable_rng!(Seed::from_entropy()); check_simple_fork(&mut btf, &events, &mut rng); check_make_alternative_chain_longer(&mut btf, &events, &mut rng); check_reorg_to_first_chain(&mut btf, &events, &mut rng); diff --git a/chainstate/src/detail/tests/syncing_tests.rs b/chainstate/src/detail/tests/syncing_tests.rs index c0a67688e7..b907da5b80 100644 --- a/chainstate/src/detail/tests/syncing_tests.rs +++ b/chainstate/src/detail/tests/syncing_tests.rs @@ -35,9 +35,12 @@ fn process_a_trivial_block() { } // Generate some blocks and check that a locator is of expected length. -#[test] -fn get_locator() { - common::concurrency::model(|| { +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn get_locator(#[case] seed: Seed) { + common::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); let mut btf = BlockTestFramework::new(); let locator = btf.chainstate().get_locator().unwrap(); @@ -45,7 +48,6 @@ fn get_locator() { assert_eq!(&locator[0], &btf.genesis().get_id()); // Expand the chain several times. - let mut rng = make_seedable_rng!(Seed::from_entropy()); let mut blocks = 1; let mut last_block_id: Id = btf.genesis().get_id().into(); for _ in 0..8 { @@ -75,10 +77,12 @@ fn get_locator() { } // Check that new blocks (produced after a locator is created) are returned. -#[test] -fn get_headers() { - common::concurrency::model(|| { - let mut rng = make_seedable_rng!(Seed::from_entropy()); +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn get_headers(#[case] seed: Seed) { + common::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); let header_limit = i64::from(HEADER_LIMIT).try_into().unwrap(); let headers_count = rng.gen_range(1000..header_limit); let blocks_count = rng.gen_range(1000..2000); @@ -124,10 +128,12 @@ fn get_headers() { // Create two chains that only share the genesis block and verify that the header is attached to // the genesis. -#[test] -fn get_headers_genesis() { - common::concurrency::model(|| { - let mut rng = make_seedable_rng!(Seed::from_entropy()); +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn get_headers_genesis(#[case] seed: Seed) { + common::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); let mut btf = BlockTestFramework::new(); let genesis_id: Id = btf.genesis().get_id().into(); @@ -149,10 +155,12 @@ fn get_headers_genesis() { // Create two chains that branch at some point, both with some unique blocks. Verify that the first // returned header is attached to a block that is known to both chains. -#[test] -fn get_headers_branching_chains() { - common::concurrency::model(|| { - let mut rng = make_seedable_rng!(Seed::from_entropy()); +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn get_headers_branching_chains(#[case] seed: Seed) { + common::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); let common_height = rng.gen_range(100..10_000); let mut btf = BlockTestFramework::new(); @@ -172,10 +180,12 @@ fn get_headers_branching_chains() { // Create two separate chains that share some blocks. Verify that the first returned header is // attached to some block known for both chains. -#[test] -fn get_headers_different_chains() { - common::concurrency::model(|| { - let mut rng = make_seedable_rng!(Seed::from_entropy()); +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn get_headers_different_chains(#[case] seed: Seed) { + common::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); let mut btf1 = BlockTestFramework::new(); let mut btf2 = BlockTestFramework::new(); @@ -208,10 +218,12 @@ fn get_headers_different_chains() { }); } -#[test] -fn filter_already_existing_blocks() { - common::concurrency::model(|| { - let mut rng = make_seedable_rng!(Seed::from_entropy()); +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn filter_already_existing_blocks(#[case] seed: Seed) { + common::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); let mut btf1 = BlockTestFramework::new(); let mut btf2 = BlockTestFramework::new(); @@ -264,10 +276,12 @@ fn filter_already_existing_blocks() { } // Try to use headers that aren't attached to the chain. -#[test] -fn filter_already_existing_blocks_detached_headers() { - common::concurrency::model(|| { - let mut rng = make_seedable_rng!(Seed::from_entropy()); +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn filter_already_existing_blocks_detached_headers(#[case] seed: Seed) { + common::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); let mut btf1 = BlockTestFramework::new(); let mut btf2 = BlockTestFramework::new(); diff --git a/test-utils/Cargo.toml b/test-utils/Cargo.toml index 38c57d6e19..ef2871d1e6 100644 --- a/test-utils/Cargo.toml +++ b/test-utils/Cargo.toml @@ -7,5 +7,4 @@ edition = "2021" [dependencies] crypto = {path = '../crypto'} -logging = {path = '../logging'} rand_chacha = "0.3.1" diff --git a/test-utils/src/random.rs b/test-utils/src/random.rs index cc7b27bc6d..f64f9fb2d6 100644 --- a/test-utils/src/random.rs +++ b/test-utils/src/random.rs @@ -16,6 +16,7 @@ use crypto::random::{Rng, SeedableRng}; use rand_chacha::ChaChaRng; +#[derive(Debug, Copy, Clone)] pub struct Seed(pub u64); impl Seed { @@ -32,33 +33,3 @@ impl Seed { pub fn make_seedable_rng(seed: Seed) -> impl Rng { ChaChaRng::seed_from_u64(seed.0) } - -/// Makes PRNG that should be used in unit tests to get deterministic values from non-deterministic seed. -/// -/// # Example -/// -/// ``` -/// use test_utils::{make_seedable_rng, random::*}; -/// let mut rng = make_seedable_rng!(Seed::from_entropy()); -/// ``` -/// If the test case fails a seed will be printed to std out, e.g: -/// -/// `chainstate/src/detail/tests/double_spend_tests.rs:45 Using seed '4862969352335513650' for the PRNG` -/// -/// That output can be used to reproduce the fail by passing the seed from printed integer instead of entropy: -/// ``` -/// use test_utils::{make_seedable_rng, random::*}; -/// let mut rng = make_seedable_rng!(Seed::from_u64(4862969352335513650)); -/// ``` -#[macro_export] -macro_rules! make_seedable_rng { - ($seed:expr) => {{ - println!( - "{}:{} Using seed '{}' for the PRNG", - file!(), - line!(), - $seed.0 - ); - make_seedable_rng($seed) - }}; -} From 1ea993c6bce49d2063fb3ac9f30c872d6e8ad81e Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Tue, 26 Jul 2022 13:31:48 +0300 Subject: [PATCH 7/9] Fix build --- Cargo.lock | 1 + chainstate-storage/Cargo.toml | 1 + chainstate-storage/src/utxo_db.rs | 3 ++- test-utils/Cargo.toml | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 24c124899f..644dabcc24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -547,6 +547,7 @@ dependencies = [ "itertools", "mockall", "num-traits", + "rstest", "serialization", "storage", "test-utils", diff --git a/chainstate-storage/Cargo.toml b/chainstate-storage/Cargo.toml index f8272395d8..5a769e8537 100644 --- a/chainstate-storage/Cargo.toml +++ b/chainstate-storage/Cargo.toml @@ -21,6 +21,7 @@ crypto = { path = '../crypto' } itertools = "0.10" mockall = "0.11" num-traits = "0.2" +rstest = "0.15" test-utils = {path = '../test-utils'} [features] diff --git a/chainstate-storage/src/utxo_db.rs b/chainstate-storage/src/utxo_db.rs index 25e92374aa..2b94f243c4 100644 --- a/chainstate-storage/src/utxo_db.rs +++ b/chainstate-storage/src/utxo_db.rs @@ -74,6 +74,7 @@ mod test { use common::chain::{Destination, OutPoint, OutPointSourceId, OutputPurpose, TxOutput}; use common::primitives::{Amount, BlockHeight, H256}; use crypto::key::{KeyKind, PrivateKey}; + use crypto::random::Rng; use rstest::*; use test_utils::random::*; @@ -81,7 +82,7 @@ mod test { // just a random value generated, and also a random `is_block_reward` value. let (_, pub_key) = PrivateKey::new(KeyKind::RistrettoSchnorr); let output = TxOutput::new( - Amount::from_atoms(random_value), + Amount::from_atoms(output_value), OutputPurpose::Transfer(Destination::PublicKey(pub_key)), ); let utxo = Utxo::new(output, true, BlockHeight::new(block_height)); diff --git a/test-utils/Cargo.toml b/test-utils/Cargo.toml index ef2871d1e6..9495b78a87 100644 --- a/test-utils/Cargo.toml +++ b/test-utils/Cargo.toml @@ -2,6 +2,7 @@ name = "test-utils" version = "0.1.0" edition = "2021" +license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html From 5aa617e0ebe4e352d3c063855874ed1f9859dd33 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Tue, 26 Jul 2022 16:51:34 +0300 Subject: [PATCH 8/9] Fix review comments --- chainstate-storage/src/store.rs | 4 ++-- chainstate-storage/src/utxo_db.rs | 4 ++-- chainstate/src/detail/orphan_blocks/pool.rs | 4 ++-- chainstate/src/detail/tests/mod.rs | 4 ++-- chainstate/src/lib.rs | 3 +-- test-utils/src/random.rs | 10 ++++++++++ 6 files changed, 19 insertions(+), 10 deletions(-) diff --git a/chainstate-storage/src/store.rs b/chainstate-storage/src/store.rs index 02c343a839..e73233497f 100644 --- a/chainstate-storage/src/store.rs +++ b/chainstate-storage/src/store.rs @@ -413,8 +413,8 @@ pub(crate) mod test { use common::primitives::{Amount, H256}; use crypto::key::{KeyKind, PrivateKey}; use crypto::random::Rng; - use rstest::*; - use test_utils::random::*; + use rstest::rstest; + use test_utils::random::{make_seedable_rng, Seed}; use utxo::{BlockUndo, TxUndo}; #[test] diff --git a/chainstate-storage/src/utxo_db.rs b/chainstate-storage/src/utxo_db.rs index 2b94f243c4..47ea92c87e 100644 --- a/chainstate-storage/src/utxo_db.rs +++ b/chainstate-storage/src/utxo_db.rs @@ -75,8 +75,8 @@ mod test { use common::primitives::{Amount, BlockHeight, H256}; use crypto::key::{KeyKind, PrivateKey}; use crypto::random::Rng; - use rstest::*; - use test_utils::random::*; + use rstest::rstest; + use test_utils::random::{make_seedable_rng, Seed}; fn create_utxo(block_height: u64, output_value: u128) -> (Utxo, OutPoint) { // just a random value generated, and also a random `is_block_reward` value. diff --git a/chainstate/src/detail/orphan_blocks/pool.rs b/chainstate/src/detail/orphan_blocks/pool.rs index d0bee00b28..e15aed1b9b 100644 --- a/chainstate/src/detail/orphan_blocks/pool.rs +++ b/chainstate/src/detail/orphan_blocks/pool.rs @@ -164,8 +164,8 @@ mod tests { use checkers::*; use common::{chain::block::Block, primitives::Id}; use helpers::*; - use rstest::*; - use test_utils::random::*; + use rstest::rstest; + use test_utils::random::{make_seedable_rng, Seed}; const MAX_ORPHAN_BLOCKS: usize = 512; diff --git a/chainstate/src/detail/tests/mod.rs b/chainstate/src/detail/tests/mod.rs index 13926d3d04..0c0cade5ad 100644 --- a/chainstate/src/detail/tests/mod.rs +++ b/chainstate/src/detail/tests/mod.rs @@ -31,9 +31,9 @@ use common::{ Uint256, }; use crypto::random::{Rng, SliceRandom}; -use rstest::*; +use rstest::rstest; use serialization::Encode; -use test_utils::random::*; +use test_utils::random::{make_seedable_rng, Seed}; mod double_spend_tests; mod events_tests; diff --git a/chainstate/src/lib.rs b/chainstate/src/lib.rs index 4d0b4d8da6..9d7d769cf6 100644 --- a/chainstate/src/lib.rs +++ b/chainstate/src/lib.rs @@ -15,11 +15,10 @@ // // Author(s): S. Afach, A. Sinitsyn +mod chainstate_interface_impl; mod config; mod detail; -mod chainstate_interface_impl; - pub mod chainstate_interface; pub mod rpc; diff --git a/test-utils/src/random.rs b/test-utils/src/random.rs index f64f9fb2d6..a4fe228d33 100644 --- a/test-utils/src/random.rs +++ b/test-utils/src/random.rs @@ -15,6 +15,7 @@ use crypto::random::{Rng, SeedableRng}; use rand_chacha::ChaChaRng; +use std::{num::ParseIntError, str::FromStr}; #[derive(Debug, Copy, Clone)] pub struct Seed(pub u64); @@ -29,6 +30,15 @@ impl Seed { } } +impl FromStr for Seed { + type Err = ParseIntError; + + fn from_str(s: &str) -> Result { + let v = s.parse::()?; + Ok(Seed::from_u64(v)) + } +} + #[must_use] pub fn make_seedable_rng(seed: Seed) -> impl Rng { ChaChaRng::seed_from_u64(seed.0) From 93bff2af9ecd1eab3a6b0063ccf9c65ddda84e9d Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Tue, 26 Jul 2022 19:09:58 +0300 Subject: [PATCH 9/9] Minor fixes --- p2p/README.md | 4 ++-- p2p/tests/ban.rs | 2 -- p2p/tests/libp2p-gossipsub.rs | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/p2p/README.md b/p2p/README.md index 1c7fa4984e..9681105a9e 100644 --- a/p2p/README.md +++ b/p2p/README.md @@ -64,7 +64,7 @@ In addition to maintaining each connection individually by exchanging `Ping`/`Po The peers exchange messages directly between each other and apart from blocks and transactions which are most likely published on a separate gossip topics (instead of being exchanged directly between peers), all messages form a request/response pairs. It's a violation of the protocol not to respond to a request with a proper response, even with an empty response if the data query could not be fulfilled. The transport channel is available for use while a request/response pair is still in progress meaning the communication is not blocked for other uses socket before the response for the request is heard. -Each message contains at least the header which indicates the message type it carries, the network this message originated from and whether the message carries any paylaod. Table below depicts the header format. +Each message contains at least the header which indicates the message type it carries, the network this message originated from and whether the message carries any payload. Table below depicts the header format. | Length | Description | Type | Comments | |--------|-------------|------|----------| @@ -116,7 +116,7 @@ The random nonce carried in the `Pong` message must be the same that was in the | Length | Description | Type | Comments | |--------|-------------|------|----------| | 1 bytes | Length | `u8` | Number of peer entries -| N bytes | Peers | `Vec` | Byte vector containing SCALE-encodeded vector of `(socket address, services)` tuples. +| N bytes | Peers | `Vec` | Byte vector containing SCALE-encoded vector of `(socket address, services)` tuples. #### PexAck diff --git a/p2p/tests/ban.rs b/p2p/tests/ban.rs index 8cebe687b4..bdd85ffa8a 100644 --- a/p2p/tests/ban.rs +++ b/p2p/tests/ban.rs @@ -15,8 +15,6 @@ // // Author(s): A. Altonen #![allow(unused)] -extern crate p2p_test_utils; - use libp2p::Multiaddr; use p2p::{ error::{P2pError, PublishError}, diff --git a/p2p/tests/libp2p-gossipsub.rs b/p2p/tests/libp2p-gossipsub.rs index 2cb40af6fb..d00c3a686a 100644 --- a/p2p/tests/libp2p-gossipsub.rs +++ b/p2p/tests/libp2p-gossipsub.rs @@ -14,7 +14,6 @@ // limitations under the License. // // Author(s): A. Altonen -extern crate p2p_test_utils; use p2p_test_utils::make_libp2p_addr; use common::chain::{