diff --git a/Cargo.lock b/Cargo.lock index 6b2f65d391..644dabcc24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -525,11 +525,13 @@ dependencies = [ "num", "replace_with", "rpc", + "rstest", "serde", "serde_json", "serialization", "static_assertions", "subsystem", + "test-utils", "thiserror", "tokio", "utils", @@ -545,8 +547,10 @@ dependencies = [ "itertools", "mockall", "num-traits", + "rstest", "serialization", "storage", + "test-utils", "thiserror", "utxo", ] @@ -2604,6 +2608,7 @@ dependencies = [ "jsonrpsee", "libp2p", "logging", + "p2p-test-utils", "parity-scale-codec", "portpicker", "rpc", @@ -2611,13 +2616,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" @@ -3296,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" @@ -4044,15 +4088,8 @@ checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" name = "test-utils" version = "0.1.0" dependencies = [ - "chainstate", - "chainstate-storage", - "common", "crypto", - "libp2p", - "p2p", - "portpicker", - "subsystem", - "tokio", + "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..5a769e8537 100644 --- a/chainstate-storage/Cargo.toml +++ b/chainstate-storage/Cargo.toml @@ -21,6 +21,8 @@ crypto = { path = '../crypto' } itertools = "0.10" mockall = "0.11" num-traits = "0.2" +rstest = "0.15" +test-utils = {path = '../test-utils'} [features] mock = [ 'mockall' ] diff --git a/chainstate-storage/src/mock.rs b/chainstate-storage/src/mock.rs index d7d28e90ff..31e80b8347 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::transaction::{ @@ -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-storage/src/store.rs b/chainstate-storage/src/store.rs index e5f58f3d14..e73233497f 100644 --- a/chainstate-storage/src/store.rs +++ b/chainstate-storage/src/store.rs @@ -412,7 +412,9 @@ 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 rstest::rstest; + use test_utils::random::{make_seedable_rng, Seed}; use utxo::{BlockUndo, TxUndo}; #[test] @@ -643,9 +645,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 +665,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 +674,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)); @@ -689,9 +692,12 @@ pub(crate) mod test { } #[cfg(not(loom))] - #[test] - fn undo_test() { - let block_undo0 = create_rand_block_undo(10, 5, BlockHeight::new(1)); + #[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()); @@ -710,7 +716,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 ac39b376e3..47ea92c87e 100644 --- a/chainstate-storage/src/utxo_db.rs +++ b/chainstate-storage/src/utxo_db.rs @@ -74,14 +74,15 @@ 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 rstest::rstest; + use test_utils::random::{make_seedable_rng, Seed}; - 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_pseudo_rng().gen_range(0..u128::MAX); 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)); @@ -95,13 +96,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()); @@ -120,7 +124,7 @@ mod test { ); // undo checking - let undo = create_rand_block_undo(10, 10, BlockHeight::new(10)); + let undo = create_rand_block_undo(&mut rng, 10, 10, BlockHeight::new(10)); assert!(db_interface.set_undo_data(block_id, &undo).is_ok()); assert_eq!(db_interface.get_undo_data(block_id), Ok(Some(undo))); diff --git a/chainstate/Cargo.toml b/chainstate/Cargo.toml index 59e163ef83..dad1618750 100644 --- a/chainstate/Cargo.toml +++ b/chainstate/Cargo.toml @@ -28,6 +28,8 @@ 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'} tokio = "1.19" diff --git a/chainstate/src/detail/mod.rs b/chainstate/src/detail/mod.rs index ef107751b2..d978d5834c 100644 --- a/chainstate/src/detail/mod.rs +++ b/chainstate/src/detail/mod.rs @@ -77,6 +77,7 @@ pub enum BlockSource { } impl Chainstate { + #[allow(dead_code)] pub fn wait_for_all_events(&self) { self.events_controller.wait_for_all_events(); } @@ -294,6 +295,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/orphan_blocks/pool.rs b/chainstate/src/detail/orphan_blocks/pool.rs index 7c7c07a034..e15aed1b9b 100644 --- a/chainstate/src/detail/orphan_blocks/pool.rs +++ b/chainstate/src/detail/orphan_blocks/pool.rs @@ -164,6 +164,8 @@ mod tests { use checkers::*; use common::{chain::block::Block, primitives::Id}; use helpers::*; + use rstest::rstest; + use test_utils::random::{make_seedable_rng, Seed}; const MAX_ORPHAN_BLOCKS: usize = 512; @@ -175,17 +177,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 +197,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.into()))) + .map(|_| gen_block_from_id(rng, Some(prev_block_id.into()))) .collect() } } @@ -283,12 +283,15 @@ 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 block = gen_random_block(); + let mut rng = make_seedable_rng(seed); + let block = gen_random_block(&mut rng); assert!(orphans_pool.add_block(block.clone()).is_ok()); // check if block was really inserted @@ -300,19 +303,22 @@ 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 block = gen_random_block(); + 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); 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 +326,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 +338,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); @@ -346,11 +350,14 @@ 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 blocks = gen_random_blocks(max_orphans as u32 + 2); + 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| { assert!(orphans_pool.add_block(block).is_ok()); @@ -359,17 +366,18 @@ 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 blocks = gen_random_blocks(50); + let mut rng = make_seedable_rng(seed); + 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!( @@ -378,18 +386,19 @@ 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 blocks = gen_random_blocks(5); + let mut rng = make_seedable_rng(seed); + 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 @@ -400,9 +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); // In `orphans_by_prev_id`: // [ @@ -411,7 +423,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()); @@ -450,14 +462,17 @@ 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); // 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 +493,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()); @@ -499,21 +512,24 @@ 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); // 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 +537,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 +575,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, @@ -571,22 +588,25 @@ 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); 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 @@ -612,16 +632,19 @@ 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); 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 +659,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 feed291bf1..361086f3d5 100644 --- a/chainstate/src/detail/tests/double_spend_tests.rs +++ b/chainstate/src/detail/tests/double_spend_tests.rs @@ -24,7 +24,6 @@ use common::{ }, primitives::{time, Amount, Id}, }; -use crypto::random::{self, Rng}; // Process a block where the second transaction uses the first one as input. // @@ -38,13 +37,17 @@ use crypto::random::{self, Rng}; // | |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 first_tx = tx_from_genesis(&chainstate); - let second_tx = tx_from_tx(&first_tx); + 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)); let block = Block::new( vec![first_tx, second_tx], @@ -79,13 +82,17 @@ 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 first_tx = tx_from_genesis(&chainstate); - let second_tx = tx_from_tx(&first_tx); + 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)); let block = Block::new( vec![second_tx, first_tx], @@ -125,14 +132,18 @@ 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 first_tx = tx_from_genesis(&chainstate); - let second_tx = tx_from_tx(&first_tx); - let third_tx = tx_from_tx(&first_tx); + 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)); + let third_tx = tx_from_tx(&first_tx, rng.gen_range(1000..2000)); let block = Block::new( vec![first_tx, second_tx, third_tx], @@ -176,12 +187,16 @@ 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 first_tx = tx_from_genesis(&chainstate); + 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( vec![first_tx.clone()], chainstate.chain_config.genesis_block_id(), @@ -199,7 +214,8 @@ fn double_spend_tx_in_another_block() { Some(first_block_id.into()) ); - let second_tx = tx_from_genesis(&chainstate); + 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.into(), @@ -224,26 +240,77 @@ 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 | | +// | +-------------------+ | +// +-----------------------+ +#[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); + 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); + let second_tx = tx_from_tx(&first_tx, tx2_output_value); + + let block = Block::new( + vec![first_tx, second_tx], + chainstate.chain_config.genesis_block_id(), + BlockTimestamp::from_duration_since_epoch(time::get()), + ConsensusData::None, + ) + .expect(ERR_CREATE_BLOCK_FAIL); + + assert_eq!( + chainstate.process_block(block, BlockSource::Local).unwrap_err(), + BlockError::StateUpdateFailed(StateUpdateError::AttemptToPrintMoney( + Amount::from_atoms(tx1_output_value), + Amount::from_atoms(tx2_output_value) + )) + ); + 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, 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(random::make_pseudo_rng().gen_range(100_000..200_000)), + 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) -> 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(random::make_pseudo_rng().gen_range(1000..2000)), + 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/events_tests.rs b/chainstate/src/detail/tests/events_tests.rs index ee42e05d46..e5e0a76dc1 100644 --- a/chainstate/src/detail/tests/events_tests.rs +++ b/chainstate/src/detail/tests/events_tests.rs @@ -19,21 +19,24 @@ use std::sync::Arc; use crate::detail::tests::*; use chainstate_storage::Store; -use crypto::random::{self, Rng}; 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); // 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(); @@ -48,7 +51,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(); @@ -64,17 +67,21 @@ 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 = random::make_pseudo_rng(); + 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(); @@ -89,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 = random::make_pseudo_rng(); let subscribers = rng.gen_range(4..16); let blocks = rng.gen_range(8..128); @@ -103,7 +112,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) @@ -122,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(); @@ -143,6 +155,7 @@ fn orphan_block() { 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(), @@ -154,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(); @@ -173,9 +189,10 @@ 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 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 42a7c35d25..0c0cade5ad 100644 --- a/chainstate/src/detail/tests/mod.rs +++ b/chainstate/src/detail/tests/mod.rs @@ -31,7 +31,9 @@ use common::{ Uint256, }; use crypto::random::{Rng, SliceRandom}; +use rstest::rstest; use serialization::Encode; +use test_utils::random::{make_seedable_rng, Seed}; mod double_spend_tests; mod events_tests; @@ -49,10 +51,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"; -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)) } @@ -64,13 +65,13 @@ 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())), )) } @@ -139,17 +140,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)], @@ -160,18 +165,25 @@ 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() } // 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(); @@ -181,7 +193,7 @@ fn generate_blocks_for_functional_tests() { Uint256([0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF]); 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 dde4e268c7..70de3d9a49 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 crypto::random::{self, Rng}; #[test] fn process_genesis_block() { @@ -66,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(), @@ -77,13 +79,14 @@ fn orphans_chains() { // Process the orphan block 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) @@ -139,15 +142,19 @@ 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 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, + ); // Check that all tx not in the main chain for tx in block.transactions() { @@ -189,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(); @@ -224,7 +234,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..random::make_pseudo_rng().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 @@ -237,7 +247,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() @@ -257,11 +267,14 @@ 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; - btf.create_chain(&btf.genesis().get_id().into(), height).unwrap(); + 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; @@ -280,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 @@ -289,7 +305,7 @@ 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) + 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()); @@ -314,8 +330,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()); @@ -364,8 +384,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!( @@ -382,29 +406,40 @@ 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; - btf.create_chain(&btf.genesis().get_id().into(), SPLIT_HEIGHT) + 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()); @@ -445,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); @@ -483,7 +521,7 @@ 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); @@ -495,6 +533,7 @@ fn consensus_type() { 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!( @@ -505,13 +544,15 @@ 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 let block_without_consensus_data = produce_test_block_with_consensus_data( TestBlockInfo::from_block(&btf.get_block(*btf.index_at(4).block_id()).unwrap().unwrap()), ConsensusData::None, + &mut rng, ); assert!(matches!( @@ -524,7 +565,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()).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( @@ -543,7 +585,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()).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![]) @@ -558,13 +600,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()).unwrap().unwrap(); let block_without_consensus_data = produce_test_block_with_consensus_data( TestBlockInfo::from_block(&prev_block), ConsensusData::None, + &mut rng, ); assert!(matches!( @@ -577,7 +621,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()).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( @@ -594,8 +639,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 = @@ -629,7 +677,7 @@ fn pow() { // 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 9459d21659..f3a9cf342a 100644 --- a/chainstate/src/detail/tests/reorgs_tests.rs +++ b/chainstate/src/detail/tests/reorgs_tests.rs @@ -28,9 +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(|| { +#[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(); @@ -51,9 +54,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 +68,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 +90,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 @@ -97,19 +102,22 @@ 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); - 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); + 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 +136,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) @@ -139,7 +151,11 @@ fn check_spend_tx_in_failed_block(btf: &mut BlockTestFramework, events: &EventLi const NEW_CHAIN_END_ON: usize = 11; assert!(btf - .create_chain(&(*btf.index_at(NEW_CHAIN_START_ON).block_id()).into(), 5,) + .create_chain( + &(*btf.index_at(NEW_CHAIN_START_ON).block_id()).into(), + 5, + rng + ) .is_ok()); check_last_event(btf, events); @@ -153,13 +169,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())]), + 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()).into(), 1).is_err()); + assert!(btf.create_chain(&(*btf.index_at(12).block_id()).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) @@ -173,7 +190,11 @@ fn check_spend_tx_in_other_fork(btf: &mut BlockTestFramework) { const NEW_CHAIN_START_ON: usize = 5; const NEW_CHAIN_END_ON: usize = 9; assert!(btf - .create_chain(&(*btf.index_at(NEW_CHAIN_START_ON).block_id()).into(), 1) + .create_chain( + &(*btf.index_at(NEW_CHAIN_START_ON).block_id()).into(), + 1, + rng + ) .is_ok()); let block = btf .chainstate @@ -184,14 +205,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())]), + 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) @@ -213,11 +235,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())]), + 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) @@ -230,7 +257,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()).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 @@ -271,7 +298,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) @@ -289,7 +320,7 @@ fn check_make_alternative_chain_longer(btf: &mut BlockTestFramework, events: &Ev .get_block(*btf.block_indexes.last().unwrap().block_id()) .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 @@ -312,7 +343,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 @@ -323,9 +354,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()).into(), 1).is_ok()); + assert!(btf.create_chain(&(*btf.index_at(1).block_id()).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 6a6c2871dc..eed5b5647e 100644 --- a/chainstate/src/detail/tests/syncing_tests.rs +++ b/chainstate/src/detail/tests/syncing_tests.rs @@ -18,7 +18,6 @@ use std::iter; use crate::detail::tests::{test_framework::BlockTestFramework, *}; -use crypto::random::{self, Rng}; #[test] fn locator_distances() { @@ -36,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(); @@ -46,12 +48,11 @@ fn get_locator() { assert_eq!(&locator[0], &btf.genesis().get_id()); // Expand the chain several times. - let mut rng = random::make_pseudo_rng(); 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. @@ -76,15 +77,19 @@ 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(); +#[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); 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(); @@ -95,13 +100,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)); + let block = produce_test_block( + TestBlockInfo::from_id(btf.chainstate(), last_block_id), + &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(); @@ -111,7 +119,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); }); @@ -119,19 +128,21 @@ 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(); +#[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(); - 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()); @@ -144,19 +155,22 @@ 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 = random::make_pseudo_rng(); +#[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(); - 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(); @@ -166,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 = random::make_pseudo_rng(); +#[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(); @@ -177,7 +193,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(); @@ -187,8 +203,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(); @@ -202,17 +218,19 @@ fn get_headers_different_chains() { }); } -#[test] -fn filter_already_existing_blocks() { - common::concurrency::model(|| { - let mut rng = random::make_pseudo_rng(); +#[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(); 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(); @@ -230,13 +248,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(); @@ -258,17 +276,19 @@ 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(); +#[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(); 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(); @@ -280,7 +300,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 259c2d8983..83209415d0 100644 --- a/chainstate/src/detail/tests/test_framework.rs +++ b/chainstate/src/detail/tests/test_framework.rs @@ -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,11 +120,12 @@ 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); 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())?; } diff --git a/chainstate/src/lib.rs b/chainstate/src/lib.rs index 1b695478f5..9d7d769cf6 100644 --- a/chainstate/src/lib.rs +++ b/chainstate/src/lib.rs @@ -15,27 +15,27 @@ // // Author(s): S. Afach, A. Sinitsyn +mod chainstate_interface_impl; mod config; mod detail; 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, Locator}, + detail::{ban_score, BlockError, BlockSource, Locator}, }; use std::sync::Arc; use chainstate_interface::ChainstateInterface; +use chainstate_interface_impl::ChainstateInterfaceImpl; use common::{ chain::{Block, ChainConfig, GenBlock}, primitives::{BlockHeight, Id}, }; -use detail::{time_getter::TimeGetter, PropertyQueryError}; +use detail::{time_getter::TimeGetter, Chainstate, PropertyQueryError}; #[derive(Debug, Clone)] pub enum ChainstateEvent { 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/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/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 2c348fcd4a..6dc656bd38 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 73f2a50e00..84f4a8a8d3 100644 --- a/p2p/src/net/libp2p/tests/frontend.rs +++ b/p2p/src/net/libp2p/tests/frontend.rs @@ -38,8 +38,12 @@ struct Transaction { #[tokio::test] async fn test_connect_new() { let config = Arc::new(common::chain::config::create_mainnet()); - let service = - Libp2pService::start(test_utils::make_libp2p_addr(), config, Default::default()).await; + let service = Libp2pService::start( + p2p_test_utils::make_libp2p_addr(), + config, + Default::default(), + ) + .await; assert!(service.is_ok()); } @@ -49,15 +53,19 @@ 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), Default::default(), ) .await; assert!(service.is_ok()); - let service = - Libp2pService::start(test_utils::make_libp2p_addr(), config, Default::default()).await; + let service = Libp2pService::start( + p2p_test_utils::make_libp2p_addr(), + config, + Default::default(), + ) + .await; match service { Err(e) => { @@ -76,13 +84,13 @@ 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), Default::default(), ) .await; let service2 = Libp2pService::start( - test_utils::make_libp2p_addr(), + p2p_test_utils::make_libp2p_addr(), Arc::clone(&config), Default::default(), ) @@ -107,10 +115,13 @@ async fn test_connect_accept() { 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(), config, Default::default()) - .await - .unwrap(); + let (mut service, _, _) = Libp2pService::start( + p2p_test_utils::make_libp2p_addr(), + config, + Default::default(), + ) + .await + .unwrap(); match service.connect(addr.clone()).await { Ok(_) => panic!("connect succeeded without peer id"), @@ -290,7 +301,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, Arc::new(config::P2pConfig { outbound_connection_timeout: 2, diff --git a/p2p/src/net/libp2p/tests/gossipsub.rs b/p2p/src/net/libp2p/tests/gossipsub.rs index f54aee40e0..9acadd6b84 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 956567e9e1..14afaaaf7a 100644 --- a/p2p/src/net/libp2p/tests/identify.rs +++ b/p2p/src/net/libp2p/tests/identify.rs @@ -25,7 +25,7 @@ async fn test_identify_not_supported() { let (mut backend1, _cmd1, _conn1, _gossip1, _sync1) = make_libp2p( config.clone(), Default::default(), - test_utils::make_libp2p_addr(), + p2p_test_utils::make_libp2p_addr(), &[], ) .await; diff --git a/p2p/src/net/libp2p/tests/mdns.rs b/p2p/src/net/libp2p/tests/mdns.rs index 57626b5911..afcc85aab6 100644 --- a/p2p/src/net/libp2p/tests/mdns.rs +++ b/p2p/src/net/libp2p/tests/mdns.rs @@ -24,7 +24,7 @@ use crate::{ }; 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 cdb9cba3bc..93aa3b94a4 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 16fa158c50..bdd85ffa8a 100644 --- a/p2p/tests/ban.rs +++ b/p2p/tests/ban.rs @@ -15,8 +15,6 @@ // // Author(s): A. Altonen #![allow(unused)] -extern crate test_utils; - use libp2p::Multiaddr; use p2p::{ error::{P2pError, PublishError}, @@ -29,8 +27,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 +54,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(), Arc::clone(&config), Default::default()) @@ -83,7 +81,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(); @@ -124,7 +122,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(), Arc::clone(&config), Default::default()) @@ -142,7 +140,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 8d44a24548..d00c3a686a 100644 --- a/p2p/tests/libp2p-gossipsub.rs +++ b/p2p/tests/libp2p-gossipsub.rs @@ -14,8 +14,7 @@ // limitations under the License. // // Author(s): A. Altonen -extern crate test_utils; -use test_utils::make_libp2p_addr; +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 d6cb0d910b..b3a9dc1ed3 100644 --- a/p2p/tests/libp2p-mdns.rs +++ b/p2p/tests/libp2p-mdns.rs @@ -22,8 +22,8 @@ use p2p::{ libp2p::Libp2pService, 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 d26fef5b59..391e17bd2e 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( @@ -110,16 +110,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) } @@ -132,18 +132,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) } @@ -285,7 +285,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 @@ -376,7 +376,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 @@ -493,10 +493,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; @@ -635,10 +635,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; @@ -777,8 +777,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; @@ -883,14 +883,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; @@ -1005,14 +1005,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; @@ -1068,7 +1068,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, @@ -1076,9 +1076,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; } } } @@ -1161,8 +1161,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..9495b78a87 --- /dev/null +++ b/test-utils/Cargo.toml @@ -0,0 +1,11 @@ +[package] +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 + +[dependencies] +crypto = {path = '../crypto'} +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..a4fe228d33 --- /dev/null +++ b/test-utils/src/random.rs @@ -0,0 +1,45 @@ +// 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; +use std::{num::ParseIntError, str::FromStr}; + +#[derive(Debug, Copy, Clone)] +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) + } +} + +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) +}