From c5ce76b7cc3c35bf71d34df31667a450f13cd6e3 Mon Sep 17 00:00:00 2001 From: sword_smith Date: Tue, 7 May 2024 11:56:38 +0200 Subject: [PATCH 01/13] Remove WARN log from function testing block's PoW validity This logging disturbs the test-output. And the caller's to this function already log correctly if appropriate. --- src/models/blockchain/block/mod.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/models/blockchain/block/mod.rs b/src/models/blockchain/block/mod.rs index 775bb90b..e2c5b1ba 100644 --- a/src/models/blockchain/block/mod.rs +++ b/src/models/blockchain/block/mod.rs @@ -618,15 +618,7 @@ impl Block { /// compare the hash of the current block against the difficulty determined by /// the previous. pub fn has_proof_of_work(&self, previous_block: &Block) -> bool { - // check that hash is below threshold - if self.hash() - > Self::difficulty_to_digest_threshold(previous_block.kernel.header.difficulty) - { - warn!("Block digest exceeds target difficulty"); - return false; - } - - true + self.hash() <= Self::difficulty_to_digest_threshold(previous_block.kernel.header.difficulty) } /// Converts `difficulty` to type `Digest` so that the hash of a block can be From 99abbdc269c6b596f19b3325a0b6eaf2a3589e94 Mon Sep 17 00:00:00 2001 From: Thorkil Vaerge Date: Tue, 7 May 2024 18:22:10 +0200 Subject: [PATCH 02/13] feat(archival_state): Add method to get parent block of tip We want to use this functionality when we set a new tip header since we are going to new the previous block's mutator set. --- src/models/state/archival_state.rs | 128 ++++++++++++++++++++--------- 1 file changed, 87 insertions(+), 41 deletions(-) diff --git a/src/models/state/archival_state.rs b/src/models/state/archival_state.rs index 11a48448..bd62b27b 100644 --- a/src/models/state/archival_state.rs +++ b/src/models/state/archival_state.rs @@ -417,6 +417,28 @@ impl ArchivalState { } } + /// Return parent of tip block. Returns `None` iff tip is genesis block. + pub async fn get_tip_parent(&self) -> Option { + let tip_digest = self + .block_index_db + .get(BlockIndexKey::BlockTipDigest) + .await?; + let tip_digest: Digest = tip_digest.as_tip_digest(); + let tip_header = self + .block_index_db + .get(BlockIndexKey::Block(tip_digest)) + .await + .map(|x| x.as_block_record().block_header) + .expect("Indicated block must exist in block record"); + + let parent = self + .get_block(tip_header.prev_block_digest) + .await + .expect("Fetching indicated block must succeed"); + + Some(parent.expect("Indicated block must exist")) + } + pub async fn get_block_header(&self, block_digest: Digest) -> Option { let mut ret = self .block_index_db @@ -1927,52 +1949,76 @@ mod archival_state_tests { #[traced_test] #[tokio::test] - async fn get_latest_block_test() -> Result<()> { - let mut rng = thread_rng(); - let network = Network::Alpha; - let mut archival_state: ArchivalState = make_test_archival_state(network).await; + async fn get_tip_block_test() -> Result<()> { + for network in [ + Network::Alpha, + Network::Beta, + Network::Main, + Network::RegTest, + Network::Testnet, + ] { + let mut archival_state: ArchivalState = make_test_archival_state(network).await; - let ret = archival_state.get_latest_block_from_disk().await?; - assert!( - ret.is_none(), - "Must return None when no block is stored in DB" - ); - - // Add a block to archival state and verify that this is returned - let own_wallet = WalletSecret::new_random(); - let own_receiving_address = own_wallet.nth_generation_spending_key(0).to_address(); - let genesis = *archival_state.genesis_block.clone(); - let (mock_block_1, _, _) = - make_mock_block_with_valid_pow(&genesis, None, own_receiving_address, rng.gen()); - add_block_to_archival_state(&mut archival_state, mock_block_1.clone()).await?; + assert!( + archival_state.get_tip_from_disk().await.unwrap().is_none(), + "Must return None when no block is stored in DB" + ); + assert_eq!( + archival_state.genesis_block(), + &archival_state.get_tip().await + ); + assert!( + archival_state.get_tip_parent().await.is_none(), + "Genesis tip has no parent" + ); - let ret1 = archival_state.get_latest_block_from_disk().await?; - assert!( - ret1.is_some(), - "Must return a block when one is stored to DB" - ); - assert_eq!( - mock_block_1, - ret1.unwrap(), - "Returned block must match the one inserted" - ); + // Add a block to archival state and verify that this is returned + let mut rng = thread_rng(); + let own_wallet = WalletSecret::new_random(); + let own_receiving_address = own_wallet.nth_generation_spending_key(0).to_address(); + let genesis = *archival_state.genesis_block.clone(); + let (mock_block_1, _, _) = + make_mock_block_with_valid_pow(&genesis, None, own_receiving_address, rng.gen()); + add_block_to_archival_state(&mut archival_state, mock_block_1.clone()) + .await + .unwrap(); - // Add a 2nd block and verify that this new block is now returned - let (mock_block_2, _, _) = - make_mock_block_with_valid_pow(&mock_block_1, None, own_receiving_address, rng.gen()); - add_block_to_archival_state(&mut archival_state, mock_block_2.clone()).await?; - let ret2 = archival_state.get_latest_block_from_disk().await?; - assert!( - ret2.is_some(), - "Must return a block when one is stored to DB" - ); + assert_eq!( + mock_block_1, + archival_state.get_tip_from_disk().await.unwrap().unwrap(), + "Returned block must match the one inserted" + ); + assert_eq!(mock_block_1, archival_state.get_tip().await); + assert_eq!( + archival_state.genesis_block(), + &archival_state.get_tip_parent().await.unwrap() + ); - assert_eq!( - mock_block_2, - ret2.unwrap(), - "Returned block must match the one inserted" - ); + // Add a 2nd block and verify that this new block is now returned + let (mock_block_2, _, _) = make_mock_block_with_valid_pow( + &mock_block_1, + None, + own_receiving_address, + rng.gen(), + ); + add_block_to_archival_state(&mut archival_state, mock_block_2.clone()) + .await + .unwrap(); + let ret2 = archival_state.get_tip_from_disk().await.unwrap(); + assert!( + ret2.is_some(), + "Must return a block when one is stored to DB" + ); + assert_eq!( + mock_block_2, + ret2.unwrap(), + "Returned block must match the one inserted" + ); + assert_eq!(mock_block_2, archival_state.get_tip().await); + assert_eq!(mock_block_1, archival_state.get_tip_parent().await.unwrap()); + } + // Ok(()) } From 09b7f1937a482d8ee4c3df23f6d05ec72a0c96ff Mon Sep 17 00:00:00 2001 From: Thorkil Vaerge Date: Tue, 7 May 2024 18:56:25 +0200 Subject: [PATCH 03/13] archival-state: Always set stored block as tip Always set the new block we are storing to disk and indexing in the database as the tip. Previously, we would check if this block exceeded the proof of work family number of the current tip but this responsibility is now delegated to the caller. This is needed to fix a bug in `store_block_internal_worker` where the value `previous_ms_accumulator` is set incorrectly whenever there is a fork, i.e. whenever `store_block_internal_worker` receives a block that is not the child of what the block currently considers the tip. In order to set `previous_ms_accumulator`, we need to know that the block we are storing now is considered the tip (by the `ArchivalState`) *and* we need a method to fetch the parent of the tip after this function `write_block_as_tip` has been called. Cf. #143. --- src/models/state/archival_state.rs | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/src/models/state/archival_state.rs b/src/models/state/archival_state.rs index bd62b27b..9be47d80 100644 --- a/src/models/state/archival_state.rs +++ b/src/models/state/archival_state.rs @@ -11,13 +11,12 @@ use tokio::io::AsyncSeekExt; use tokio::io::AsyncWriteExt; use tokio::io::SeekFrom; use tracing::{debug, warn}; -use twenty_first::amount::u32s::U32s; use twenty_first::math::digest::Digest; use super::shared::new_block_file_is_needed; use crate::config_models::data_directory::DataDirectory; use crate::database::{create_db_if_missing, NeptuneLevelDb, WriteBatchAsync}; -use crate::models::blockchain::block::block_header::{BlockHeader, PROOF_OF_WORK_COUNT_U32_SIZE}; +use crate::models::blockchain::block::block_header::BlockHeader; use crate::models::blockchain::block::{block_height::BlockHeight, Block}; use crate::models::database::{ BlockFileLocation, BlockIndexKey, BlockIndexValue, BlockRecord, FileRecord, LastFileRecord, @@ -217,12 +216,8 @@ impl ArchivalState { &self.genesis_block } - /// Write a newly found block to database and to disk. - pub async fn write_block( - &mut self, - new_block: &Block, - current_max_pow_family: Option>, - ) -> Result<()> { + /// Write a newly found block to database and to disk, and set it as tip. + pub async fn write_block_as_tip(&mut self, new_block: &Block) -> Result<()> { // Fetch last file record to find disk location to store block. // This record must exist in the DB already, unless this is the first block // stored on disk. @@ -334,15 +329,11 @@ impl ArchivalState { BlockIndexValue::Height(blocks_at_same_height), )); - // Mark block as tip if its PoW family is larger than current most canonical - if current_max_pow_family.is_none() - || current_max_pow_family.unwrap() < new_block.kernel.header.proof_of_work_family - { - block_index_entries.push(( - BlockIndexKey::BlockTipDigest, - BlockIndexValue::BlockTipDigest(new_block.hash()), - )); - } + // Mark block as tip + block_index_entries.push(( + BlockIndexKey::BlockTipDigest, + BlockIndexValue::BlockTipDigest(new_block.hash()), + )); let mut batch = WriteBatchAsync::new(); for (k, v) in block_index_entries.into_iter() { From 348748b5669e02fdca274328e329ea5f83c9b48a Mon Sep 17 00:00:00 2001 From: Thorkil Vaerge Date: Tue, 7 May 2024 19:16:06 +0200 Subject: [PATCH 04/13] Change RegTest time resolution to 10 hours --- src/config_models/network.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/config_models/network.rs b/src/config_models/network.rs index 67521588..e5f46a31 100644 --- a/src/config_models/network.rs +++ b/src/config_models/network.rs @@ -27,7 +27,7 @@ pub enum Network { /// Network for individual unit and integration tests. The timestamp for the /// RegTest genesis block is set to now, rounded down to the first block of - /// 10 minutes. As a result, there is a small probability that tests fail + /// 10 hours. As a result, there is a small probability that tests fail /// because they generate the genesis block twice on two opposite sides of a /// round timestamp. RegTest, @@ -38,8 +38,8 @@ impl Network { .duration_since(UNIX_EPOCH) .unwrap() .as_millis() as u64; - let ten_minutes = 1000 * 60 * 10; - let now_rounded = (now / ten_minutes) * ten_minutes; + const TEN_HOURS_AS_MS: u64 = 1000 * 60 * 60 * 10; + let now_rounded = (now / TEN_HOURS_AS_MS) * TEN_HOURS_AS_MS; match self { Network::RegTest => Timestamp(BFieldElement::new(now_rounded)), // 1 July 2024 (might be revised though) From 4e0e75d6d0c4c19470249301eb4fd8031bc495d1 Mon Sep 17 00:00:00 2001 From: Thorkil Vaerge Date: Tue, 7 May 2024 19:17:37 +0200 Subject: [PATCH 05/13] test: Delete bad helper function from test lib --- src/tests/shared.rs | 45 +++++++++++++++------------------------------ 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/src/tests/shared.rs b/src/tests/shared.rs index 1274f740..c2350dcc 100644 --- a/src/tests/shared.rs +++ b/src/tests/shared.rs @@ -133,34 +133,11 @@ pub fn get_dummy_version() -> String { "0.1.0".to_string() } -pub async fn get_dummy_latest_block( - input_block: Option, -) -> (Block, LatestBlockInfo, Arc>) { - let network = Network::RegTest; - let block = match input_block { - None => Block::genesis_block(network), - Some(block) => block, - }; - - let latest_block_info: LatestBlockInfo = block.clone().into(); - let block_header = block.kernel.header.clone(); - ( - block, - latest_block_info, - Arc::new(std::sync::Mutex::new(block_header)), - ) -} - /// Return a handshake object with a randomly set instance ID pub async fn get_dummy_handshake_data_for_genesis(network: Network) -> HandshakeData { HandshakeData { instance_id: rand::random(), - tip_header: get_dummy_latest_block(None) - .await - .2 - .lock() - .unwrap() - .to_owned(), + tip_header: Block::genesis_block(network).header().to_owned(), listen_port: Some(8080), network, version: get_dummy_version(), @@ -189,12 +166,12 @@ pub async fn get_dummy_peer_connection_data_genesis( /// Get a global state object for unit test purposes. This global state /// populated with state from the genesis block, e.g. in the archival mutator /// set and the wallet. -pub async fn get_mock_global_state( +pub async fn mock_genesis_global_state( network: Network, peer_count: u8, wallet: WalletSecret, ) -> GlobalStateLock { - let (archival_state, peer_db, _data_dir) = make_unit_test_archival_state(network).await; + let (archival_state, peer_db, _data_dir) = mock_genesis_archival_state(network).await; let syncing = false; let mut peer_map: HashMap = get_peer_map(); @@ -204,8 +181,16 @@ pub async fn get_mock_global_state( peer_map.insert(peer_address, get_dummy_peer(peer_address)); } let networking_state = NetworkingState::new(peer_map, peer_db, syncing); - let (block, _, _) = get_dummy_latest_block(None).await; - let light_state: LightState = LightState::from(block.clone()); + let genesis_block = archival_state.get_tip().await; + + // Sanity check + assert_eq!(archival_state.genesis_block().hash(), genesis_block.hash()); + + let light_state: LightState = LightState::from(genesis_block.to_owned()); + println!( + "Genesis light state MSA hash: {}", + light_state.body().mutator_set_accumulator.hash() + ); let blockchain_state = BlockchainState::Archival(BlockchainArchivalState { light_state, archival_state, @@ -216,7 +201,7 @@ pub async fn get_mock_global_state( ..Default::default() }; - let wallet_state = get_mock_wallet_state(wallet, network).await; + let wallet_state = mock_genesis_wallet_state(wallet, network).await; GlobalStateLock::new( wallet_state, @@ -250,7 +235,7 @@ pub async fn get_test_genesis_setup( let from_main_rx_clone = peer_broadcast_tx.subscribe(); let devnet_wallet = WalletSecret::devnet_wallet(); - let state = get_mock_global_state(network, peer_count, devnet_wallet).await; + let state = mock_genesis_global_state(network, peer_count, devnet_wallet).await; Ok(( peer_broadcast_tx, from_main_rx_clone, From d8d92986b5d08c2b5bedfe782948f3da016471f0 Mon Sep 17 00:00:00 2001 From: Thorkil Vaerge Date: Tue, 7 May 2024 19:19:15 +0200 Subject: [PATCH 06/13] Add note about valid UTXO for `add_expected_utxo` --- src/models/state/wallet/utxo_notification_pool.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/models/state/wallet/utxo_notification_pool.rs b/src/models/state/wallet/utxo_notification_pool.rs index da820f9e..2dfcccb6 100644 --- a/src/models/state/wallet/utxo_notification_pool.rs +++ b/src/models/state/wallet/utxo_notification_pool.rs @@ -192,7 +192,8 @@ impl UtxoNotificationPool { self.notifications.values().cloned().collect_vec() } - /// Add an expected incoming UTXO to this data model + /// Add an expected incoming UTXO that the wallet will register if it shows up in a later block. + /// It's the caller's responsibility that the UTXO is spendable. pub fn add_expected_utxo( &mut self, utxo: Utxo, From 45c73b57c06b33cd2ce29289dadaa86936ebc795 Mon Sep 17 00:00:00 2001 From: Thorkil Vaerge Date: Tue, 7 May 2024 19:21:11 +0200 Subject: [PATCH 07/13] Always consider newly added block as tip When the global state is updated with a new block, that new block is always considered the new tip. This change is reflected in a new name for this function also. This allows us to correctly handle state updates in the case of forks. Since the block is no longer carrying around the previous state of the mutator set accumulator, we have to get that state from somewhere else. We elect to get it from the archival state which can identify the parent of the current tip. Cf. #143 --- src/models/state/mod.rs | 346 ++++++++++++++-------------------------- 1 file changed, 121 insertions(+), 225 deletions(-) diff --git a/src/models/state/mod.rs b/src/models/state/mod.rs index 89ce29ba..547db75c 100644 --- a/src/models/state/mod.rs +++ b/src/models/state/mod.rs @@ -163,13 +163,13 @@ impl GlobalStateLock { ) -> Result<()> { self.lock_guard_mut() .await - .store_coinbase_block(new_block, coinbase_utxo_info) + .set_new_self_mined_tip(new_block, coinbase_utxo_info) .await } /// store a block (non coinbase) pub async fn store_block(&self, new_block: Block) -> Result<()> { - self.lock_guard_mut().await.store_block(new_block).await + self.lock_guard_mut().await.set_new_tip(new_block).await } /// resync membership proofs @@ -1111,7 +1111,7 @@ impl GlobalState { self.chain.archival_state_mut().block_index_db.flush().await; // persist archival_mutator_set, with sync label - let hash = self.chain.archival_state().get_latest_block().await.hash(); + let hash = self.chain.archival_state().get_tip().await.hash(); self.chain .archival_state_mut() .archival_mutator_set @@ -1132,22 +1132,27 @@ impl GlobalState { Ok(()) } - /// store a block (non-coinbase) - pub async fn store_block(&mut self, new_block: Block) -> Result<()> { - self.store_block_internal(new_block, None).await + /// Update client's state with a new block. Block is assumed to be valid, also wrt. to PoW. + /// The received block will be set as the new tip, regardless of its accumulated PoW. + pub async fn set_new_tip(&mut self, new_block: Block) -> Result<()> { + self.set_new_tip_internal(new_block, None).await } - /// store a coinbase (self-mined) block - pub async fn store_coinbase_block( + /// Update client's state with a new block that was mined locally. Block is assumed to be valid, + /// also wrt. to PoW. The received block will be set as the new tip, regardless of its + /// accumulated PoW. + pub async fn set_new_self_mined_tip( &mut self, new_block: Block, coinbase_utxo_info: ExpectedUtxo, ) -> Result<()> { - self.store_block_internal(new_block, Some(coinbase_utxo_info)) + self.set_new_tip_internal(new_block, Some(coinbase_utxo_info)) .await } - async fn store_block_internal( + /// Update client's state with a new block. Block is assumed to be valid, also wrt. to PoW. + /// The received block will be set as the new tip, regardless of its accumulated PoW. + async fn set_new_tip_internal( &mut self, new_block: Block, coinbase_utxo_info: Option, @@ -1160,26 +1165,11 @@ impl GlobalState { new_block: Block, coinbase_utxo_info: Option, ) -> Result<()> { - // get proof_of_work_family for tip - let tip_proof_of_work_family = myself - .chain - .light_state() - .kernel - .header - .proof_of_work_family; - let previous_mutator_set_accumulator = myself - .chain - .light_state() - .kernel - .body - .mutator_set_accumulator - .clone(); - // Apply the updates myself .chain .archival_state_mut() - .write_block(&new_block, Some(tip_proof_of_work_family)) + .write_block_as_tip(&new_block) .await?; // update the mutator set with the UTXOs from this block @@ -1204,17 +1194,33 @@ impl GlobalState { .expect("UTXO notification from miner must be accepted"); } + // Get parent of tip for mutator-set data needed for various updates + let tip_parent = myself + .chain + .archival_state() + .get_tip_parent() + .await + .expect("Parent must exist when storing a new block"); + + // Sanity check + assert_eq!( + tip_parent.hash(), + new_block.header().prev_block_digest, + "Tip parent has must match indicated parent hash" + ); + let previous_ms_accumulator = tip_parent.body().mutator_set_accumulator.clone(); + // update wallet state with relevant UTXOs from this block myself .wallet_state - .update_wallet_state_with_new_block(&previous_mutator_set_accumulator, &new_block) + .update_wallet_state_with_new_block(&previous_ms_accumulator, &new_block) .await?; // Update mempool with UTXOs from this block. This is done by removing all transaction // that became invalid/was mined by this block. myself .mempool - .update_with_block(previous_mutator_set_accumulator, &new_block) + .update_with_block(previous_ms_accumulator, &new_block) .await; myself.chain.light_state_mut().set_block(new_block); @@ -1273,8 +1279,8 @@ mod global_state_tests { config_models::network::Network, models::{blockchain::block::Block, state::wallet::utxo_notification_pool::UtxoNotifier}, tests::shared::{ - add_block, add_block_to_light_state, get_mock_global_state, get_mock_wallet_state, - make_mock_block, make_mock_block_with_valid_pow, + add_block_to_light_state, make_mock_block, make_mock_block_with_valid_pow, + mock_genesis_global_state, mock_genesis_wallet_state, }, }; use num_traits::{One, Zero}; @@ -1370,7 +1376,7 @@ mod global_state_tests { let network = Network::RegTest; let other_wallet = WalletSecret::new_random(); let global_state_lock = - get_mock_global_state(network, 2, WalletSecret::devnet_wallet()).await; + mock_genesis_global_state(network, 2, WalletSecret::devnet_wallet()).await; let genesis_block = Block::genesis_block(network); let twenty_neptune: NeptuneCoins = NeptuneCoins::new(20); let twenty_coins = twenty_neptune.to_native_coins(); @@ -1497,7 +1503,7 @@ mod global_state_tests { let mut rng = thread_rng(); let network = Network::RegTest; let devnet_wallet = WalletSecret::devnet_wallet(); - let global_state_lock = get_mock_global_state(network, 2, devnet_wallet).await; + let global_state_lock = mock_genesis_global_state(network, 2, devnet_wallet).await; let mut global_state = global_state_lock.lock_guard_mut().await; let other_receiver_address = WalletSecret::new_random() .nth_generation_spending_key(0) @@ -1574,7 +1580,7 @@ mod global_state_tests { let mut rng = thread_rng(); let network = Network::RegTest; let global_state_lock = - get_mock_global_state(network, 2, WalletSecret::devnet_wallet()).await; + mock_genesis_global_state(network, 2, WalletSecret::devnet_wallet()).await; let mut global_state = global_state_lock.lock_guard_mut().await; let other_receiver_wallet_secret = WalletSecret::new_random(); @@ -1592,10 +1598,7 @@ mod global_state_tests { global_state .chain .archival_state_mut() - .write_block( - &mock_block_1a, - Some(mock_block_1a.kernel.header.proof_of_work_family), - ) + .write_block_as_tip(&mock_block_1a) .await?; } @@ -1647,7 +1650,7 @@ mod global_state_tests { let mut rng = thread_rng(); let network = Network::RegTest; let global_state_lock = - get_mock_global_state(network, 2, WalletSecret::devnet_wallet()).await; + mock_genesis_global_state(network, 2, WalletSecret::devnet_wallet()).await; let mut global_state = global_state_lock.lock_guard_mut().await; let own_spending_key = global_state .wallet_state @@ -1656,37 +1659,21 @@ mod global_state_tests { let own_receiving_address = own_spending_key.to_address(); // 1. Create new block 1a where we receive a coinbase UTXO, store it - let genesis_block = global_state.chain.archival_state().get_latest_block().await; + let genesis_block = global_state.chain.archival_state().get_tip().await; let (mock_block_1a, coinbase_utxo, coinbase_output_randomness) = make_mock_block(&genesis_block, None, own_receiving_address, rng.gen()); - { - global_state - .chain - .archival_state_mut() - .write_block( - &mock_block_1a, - Some(mock_block_1a.kernel.header.proof_of_work_family), - ) - .await?; - global_state - .wallet_state - .expected_utxos - .add_expected_utxo( + global_state + .set_new_self_mined_tip( + mock_block_1a.clone(), + ExpectedUtxo::new( coinbase_utxo, coinbase_output_randomness, own_spending_key.privacy_preimage, UtxoNotifier::OwnMiner, - ) - .unwrap(); - global_state - .wallet_state - .update_wallet_state_with_new_block( - &genesis_block.kernel.body.mutator_set_accumulator, - &mock_block_1a, - ) - .await - .unwrap(); - } + ), + ) + .await + .unwrap(); // Verify that wallet has monitored UTXOs, from genesis and from block_1a let wallet_status = global_state @@ -1704,22 +1691,7 @@ mod global_state_tests { for _ in 0..5 { let (next_block, _, _) = make_mock_block(&parent_block, None, other_receiving_address, rng.gen()); - global_state - .chain - .archival_state_mut() - .write_block( - &next_block, - Some(next_block.kernel.header.proof_of_work_family), - ) - .await?; - global_state - .wallet_state - .update_wallet_state_with_new_block( - &parent_block.kernel.body.mutator_set_accumulator, - &next_block, - ) - .await - .unwrap(); + global_state.set_new_tip(next_block.clone()).await.unwrap(); parent_block = next_block; } @@ -1764,7 +1736,7 @@ mod global_state_tests { let mut rng = thread_rng(); let network = Network::RegTest; let global_state_lock = - get_mock_global_state(network, 2, WalletSecret::devnet_wallet()).await; + mock_genesis_global_state(network, 2, WalletSecret::devnet_wallet()).await; let mut global_state = global_state_lock.lock_guard_mut().await; let wallet_secret = global_state.wallet_state.wallet_secret.clone(); let own_spending_key = wallet_secret.nth_generation_spending_key(0); @@ -1775,34 +1747,20 @@ mod global_state_tests { .to_address(); // 1. Create new block 1a where we receive a coinbase UTXO, store it - let genesis_block = global_state.chain.archival_state().get_latest_block().await; + let genesis_block = global_state.chain.archival_state().get_tip().await; assert!(genesis_block.kernel.header.height.is_genesis()); let (mock_block_1a, coinbase_utxo_1a, cb_utxo_output_randomness_1a) = make_mock_block(&genesis_block, None, own_receiving_address, rng.gen()); { global_state - .chain - .archival_state_mut() - .write_block( - &mock_block_1a, - Some(mock_block_1a.kernel.header.proof_of_work_family), - ) - .await?; - global_state - .wallet_state - .expected_utxos - .add_expected_utxo( - coinbase_utxo_1a, - cb_utxo_output_randomness_1a, - own_spending_key.privacy_preimage, - UtxoNotifier::OwnMiner, - ) - .unwrap(); - global_state - .wallet_state - .update_wallet_state_with_new_block( - &genesis_block.kernel.body.mutator_set_accumulator, - &mock_block_1a, + .set_new_self_mined_tip( + mock_block_1a.clone(), + ExpectedUtxo::new( + coinbase_utxo_1a, + cb_utxo_output_randomness_1a, + own_spending_key.privacy_preimage, + UtxoNotifier::OwnMiner, + ), ) .await .unwrap(); @@ -1815,25 +1773,13 @@ mod global_state_tests { assert_eq!(2, wallet_status_after_1a.synced_unspent.len()); } - // Add 5 blocks on top of 1a + // Add 100 blocks on top of 1a, *not* mined by us let mut fork_a_block = mock_block_1a.clone(); for _ in 0..100 { let (next_a_block, _, _) = make_mock_block(&fork_a_block, None, other_receiving_address, rng.gen()); global_state - .chain - .archival_state_mut() - .write_block( - &next_a_block, - Some(next_a_block.kernel.header.proof_of_work_family), - ) - .await?; - global_state - .wallet_state - .update_wallet_state_with_new_block( - &fork_a_block.kernel.body.mutator_set_accumulator, - &next_a_block, - ) + .set_new_tip(next_a_block.clone()) .await .unwrap(); fork_a_block = next_a_block; @@ -1853,19 +1799,7 @@ mod global_state_tests { let (next_b_block, _, _) = make_mock_block(&fork_b_block, None, other_receiving_address, rng.gen()); global_state - .chain - .archival_state_mut() - .write_block( - &next_b_block, - Some(next_b_block.kernel.header.proof_of_work_family), - ) - .await?; - global_state - .wallet_state - .update_wallet_state_with_new_block( - &fork_b_block.kernel.body.mutator_set_accumulator, - &next_b_block, - ) + .set_new_tip(next_b_block.clone()) .await .unwrap(); fork_b_block = next_b_block; @@ -1908,19 +1842,7 @@ mod global_state_tests { let (next_c_block, _, _) = make_mock_block(&fork_c_block, None, other_receiving_address, rng.gen()); global_state - .chain - .archival_state_mut() - .write_block( - &next_c_block, - Some(next_c_block.kernel.header.proof_of_work_family), - ) - .await?; - global_state - .wallet_state - .update_wallet_state_with_new_block( - &fork_c_block.kernel.body.mutator_set_accumulator, - &next_c_block, - ) + .set_new_tip(next_c_block.clone()) .await .unwrap(); fork_c_block = next_c_block; @@ -1978,6 +1900,10 @@ mod global_state_tests { #[tokio::test] async fn flaky_mutator_set_test() { + // Test various parts of the state update when a block contains multiple inputs and outputs + // Scenario: Three parties: Alice, Bob, and Premine Receiver, mine blocks and pass coins + // around. + let seed = { let mut rng: StdRng = SeedableRng::from_rng(thread_rng()).expect("failure lifting thread_rng to StdRng"); @@ -1994,24 +1920,23 @@ mod global_state_tests { seed }; let mut rng: StdRng = SeedableRng::from_seed(seed); - - // Test various parts of the state update when a block contains multiple inputs and outputs let network = Network::RegTest; + let genesis_wallet_state = - get_mock_wallet_state(WalletSecret::devnet_wallet(), network).await; + mock_genesis_wallet_state(WalletSecret::devnet_wallet(), network).await; let genesis_spending_key = genesis_wallet_state .wallet_secret .nth_generation_spending_key(0); let genesis_state_lock = - get_mock_global_state(network, 3, genesis_wallet_state.wallet_secret).await; + mock_genesis_global_state(network, 3, genesis_wallet_state.wallet_secret).await; let wallet_secret_alice = WalletSecret::new_pseudorandom(rng.gen()); let alice_spending_key = wallet_secret_alice.nth_generation_spending_key(0); - let alice_state_lock = get_mock_global_state(network, 3, wallet_secret_alice).await; + let alice_state_lock = mock_genesis_global_state(network, 3, wallet_secret_alice).await; let wallet_secret_bob = WalletSecret::new_pseudorandom(rng.gen()); let bob_spending_key = wallet_secret_bob.nth_generation_spending_key(0); - let bob_state_lock = get_mock_global_state(network, 3, wallet_secret_bob).await; + let bob_state_lock = mock_genesis_global_state(network, 3, wallet_secret_bob).await; let genesis_block = Block::genesis_block(network); let launch = genesis_block.kernel.header.timestamp; @@ -2047,6 +1972,7 @@ mod global_state_tests { }, }, ]; + // Two outputs for Bob let receiver_data_for_bob = vec![ UtxoReceiverData { @@ -2100,99 +2026,69 @@ mod global_state_tests { block_1.kernel.body.transaction.kernel.outputs.len() ); - // Update chain states - for state_lock in [&genesis_state_lock, &alice_state_lock, &bob_state_lock] { - let mut state = state_lock.lock_guard_mut().await; - add_block(&mut state, block_1.clone()).await.unwrap(); - state - .chain - .archival_state_mut() - .update_mutator_set(&block_1) + // Update states with `block_1` + for rec_data in receiver_data_for_alice { + alice_state_lock + .lock_guard_mut() .await + .wallet_state + .expected_utxos + .add_expected_utxo( + rec_data.utxo.clone(), + rec_data.sender_randomness, + alice_spending_key.privacy_preimage, + UtxoNotifier::Cli, + ) .unwrap(); } - { - // Update wallets - let mut genesis_state = genesis_state_lock.lock_guard_mut().await; - genesis_state + for rec_data in receiver_data_for_bob { + bob_state_lock + .lock_guard_mut() + .await .wallet_state .expected_utxos .add_expected_utxo( + rec_data.utxo.clone(), + rec_data.sender_randomness, + bob_spending_key.privacy_preimage, + UtxoNotifier::Cli, + ) + .unwrap(); + } + + genesis_state_lock + .lock_guard_mut() + .await + .set_new_self_mined_tip( + block_1.clone(), + ExpectedUtxo::new( cb_utxo, cb_output_randomness, genesis_spending_key.privacy_preimage, UtxoNotifier::OwnMiner, - ) - .unwrap(); - genesis_state - .wallet_state - .update_wallet_state_with_new_block( - &genesis_block.kernel.body.mutator_set_accumulator, - &block_1, - ) - .await - .unwrap(); - assert_eq!( + ), + ) + .await + .unwrap(); + + for state_lock in [&alice_state_lock, &bob_state_lock] { + let mut state = state_lock.lock_guard_mut().await; + state.set_new_tip(block_1.clone()).await.unwrap(); + } + + assert_eq!( 3, - genesis_state + genesis_state_lock + .lock_guard_mut() + .await .wallet_state .wallet_db .monitored_utxos() .len().await, "Genesis receiver must have 3 UTXOs after block 1: change from transaction, coinbase from block 1, and the spent premine UTXO" ); - } - - { - let mut alice_state = alice_state_lock.lock_guard_mut().await; - for rec_data in receiver_data_for_alice { - alice_state - .wallet_state - .expected_utxos - .add_expected_utxo( - rec_data.utxo.clone(), - rec_data.sender_randomness, - alice_spending_key.privacy_preimage, - UtxoNotifier::Cli, - ) - .unwrap(); - } - alice_state - .wallet_state - .update_wallet_state_with_new_block( - &genesis_block.kernel.body.mutator_set_accumulator, - &block_1, - ) - .await - .unwrap(); - } - - { - let mut bob_state = bob_state_lock.lock_guard_mut().await; - for rec_data in receiver_data_for_bob { - bob_state - .wallet_state - .expected_utxos - .add_expected_utxo( - rec_data.utxo.clone(), - rec_data.sender_randomness, - bob_spending_key.privacy_preimage, - UtxoNotifier::Cli, - ) - .unwrap(); - } - bob_state - .wallet_state - .update_wallet_state_with_new_block( - &genesis_block.kernel.body.mutator_set_accumulator, - &block_1, - ) - .await - .unwrap(); - } // Now Alice should have a balance of 100 and Bob a balance of 200 - assert_eq!( NeptuneCoins::new(100), alice_state_lock @@ -2303,7 +2199,7 @@ mod global_state_tests { let mut rng = thread_rng(); let network = Network::RegTest; let global_state_lock = - get_mock_global_state(network, 2, WalletSecret::devnet_wallet()).await; + mock_genesis_global_state(network, 2, WalletSecret::devnet_wallet()).await; let mut global_state = global_state_lock.lock_guard_mut().await; let genesis_block = Block::genesis_block(network); let now = genesis_block.kernel.header.timestamp; @@ -2313,7 +2209,7 @@ mod global_state_tests { let (block_1, _cb_utxo, _cb_output_randomness) = make_mock_block_with_valid_pow(&genesis_block, None, receiving_address, rng.gen()); - add_block(&mut global_state, block_1).await.unwrap(); + global_state.set_new_tip(block_1).await.unwrap(); assert!(global_state .chain From c64b7905065803278434d8b9557627c7820c2d70 Mon Sep 17 00:00:00 2001 From: Thorkil Vaerge Date: Tue, 7 May 2024 19:27:10 +0200 Subject: [PATCH 08/13] refactor(test): Update state through `GlobalState`'s `set_new_tip_internal` Previously tests had their own helper functions for updating the global state. With this commit, most tests now go through the same state updater function as the main code. This shortens multiple tests and make us more likely to catch errors, like the one in #143. --- src/connect_to_peers.rs | 6 +- src/lib.rs | 2 +- src/main_loop.rs | 4 +- src/mine_loop.rs | 6 +- src/models/blockchain/block/mod.rs | 8 +- src/models/state/archival_state.rs | 293 ++++++++---------------- src/models/state/mempool.rs | 16 +- src/models/state/wallet/mod.rs | 42 ++-- src/models/state/wallet/wallet_state.rs | 100 ++------ src/peer_loop.rs | 109 ++++----- src/rpc_server.rs | 4 +- src/tests/shared.rs | 44 +--- 12 files changed, 198 insertions(+), 436 deletions(-) diff --git a/src/connect_to_peers.rs b/src/connect_to_peers.rs index a62443e3..28339800 100644 --- a/src/connect_to_peers.rs +++ b/src/connect_to_peers.rs @@ -473,9 +473,8 @@ mod connect_tests { ConnectionStatus, PeerInfo, PeerMessage, PeerSanctionReason, PeerStanding, }; use crate::tests::shared::{ - get_dummy_handshake_data_for_genesis, get_dummy_latest_block, - get_dummy_peer_connection_data_genesis, get_dummy_socket_address, get_test_genesis_setup, - to_bytes, + get_dummy_handshake_data_for_genesis, get_dummy_peer_connection_data_genesis, + get_dummy_socket_address, get_test_genesis_setup, to_bytes, }; use crate::{MAGIC_STRING_REQUEST, MAGIC_STRING_RESPONSE}; @@ -873,7 +872,6 @@ mod connect_tests { cli.max_peers = 2; state_lock.set_cli(cli).await; - let (_, _, _latest_block_header) = get_dummy_latest_block(None).await; let answer = answer_peer( mock, state_lock.clone(), diff --git a/src/lib.rs b/src/lib.rs index 0439e906..db1f85d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -109,7 +109,7 @@ pub async fn initialize(cli_args: cli_args::Args) -> Result<()> { .await; // Get latest block. Use hardcoded genesis block if nothing is in database. - let latest_block: Block = archival_state.get_latest_block().await; + let latest_block: Block = archival_state.get_tip().await; // Bind socket to port on this machine, to handle incoming connections from peers let incoming_peer_listener = TcpListener::bind((cli_args.listen_addr, cli_args.peer_port)) diff --git a/src/main_loop.rs b/src/main_loop.rs index 2a48211c..53e9d042 100644 --- a/src/main_loop.rs +++ b/src/main_loop.rs @@ -334,7 +334,7 @@ impl MainLoopHandler { } global_state_mut - .store_coinbase_block( + .set_new_self_mined_tip( new_block.as_ref().clone(), new_block_info.coinbase_utxo_info.as_ref().clone(), ) @@ -418,7 +418,7 @@ impl MainLoopHandler { new_block.kernel.header.timestamp.standard_format() ); - global_state_mut.store_block(new_block).await?; + global_state_mut.set_new_tip(new_block).await?; } } diff --git a/src/mine_loop.rs b/src/mine_loop.rs index 3fcc7900..4163a3a1 100644 --- a/src/mine_loop.rs +++ b/src/mine_loop.rs @@ -504,7 +504,7 @@ mod mine_loop_tests { use crate::{ config_models::network::Network, models::{consensus::timestamp::Timestamp, state::UtxoReceiverData}, - tests::shared::get_mock_global_state, + tests::shared::mock_genesis_global_state, }; use super::*; @@ -515,7 +515,7 @@ mod mine_loop_tests { // Verify that a block template made with transaction from the mempool is a valid block let network = Network::RegTest; let premine_receiver_global_state_lock = - get_mock_global_state(network, 2, WalletSecret::devnet_wallet()).await; + mock_genesis_global_state(network, 2, WalletSecret::devnet_wallet()).await; let mut premine_receiver_global_state = premine_receiver_global_state_lock.lock_guard_mut().await; assert!( @@ -635,7 +635,7 @@ mod mine_loop_tests { async fn mined_block_has_proof_of_work() -> Result<()> { let network = Network::RegTest; let global_state_lock = - get_mock_global_state(network, 2, WalletSecret::devnet_wallet()).await; + mock_genesis_global_state(network, 2, WalletSecret::devnet_wallet()).await; let (worker_thread_tx, worker_thread_rx) = oneshot::channel::(); diff --git a/src/models/blockchain/block/mod.rs b/src/models/blockchain/block/mod.rs index e2c5b1ba..a35bfcbd 100644 --- a/src/models/blockchain/block/mod.rs +++ b/src/models/blockchain/block/mod.rs @@ -681,7 +681,9 @@ mod block_tests { blockchain::transaction::PublicAnnouncement, state::wallet::WalletSecret, state::UtxoReceiverData, }, - tests::shared::{get_mock_global_state, make_mock_block, make_mock_block_with_valid_pow}, + tests::shared::{ + make_mock_block, make_mock_block_with_valid_pow, mock_genesis_global_state, + }, util_types::mutator_set::archival_mmr::ArchivalMmr, }; use strum::IntoEnumIterator; @@ -697,7 +699,7 @@ mod block_tests { // has a wallet which receives a premine-UTXO. let network = Network::RegTest; let global_state_lock = - get_mock_global_state(network, 2, WalletSecret::devnet_wallet()).await; + mock_genesis_global_state(network, 2, WalletSecret::devnet_wallet()).await; let spending_key = global_state_lock .lock_guard() .await @@ -1007,7 +1009,7 @@ mod block_tests { // note: we have to generate a block becau // TransferBlock::into() will panic if it // encounters the genesis block. let global_state_lock = - get_mock_global_state(Network::RegTest, 2, WalletSecret::devnet_wallet()).await; + mock_genesis_global_state(Network::RegTest, 2, WalletSecret::devnet_wallet()).await; let spending_key = global_state_lock .lock_guard() .await diff --git a/src/models/state/archival_state.rs b/src/models/state/archival_state.rs index 9be47d80..18135fac 100644 --- a/src/models/state/archival_state.rs +++ b/src/models/state/archival_state.rs @@ -374,8 +374,9 @@ impl ArchivalState { .await? } - /// return the latest block - async fn get_latest_block_from_disk(&self) -> Result> { + /// Return the latest block that was stored to disk. If no block has been stored to disk, i.e. + /// if tip is genesis, then `None` is returned + async fn get_tip_from_disk(&self) -> Result> { let tip_digest = self.block_index_db.get(BlockIndexKey::BlockTipDigest).await; let tip_digest: Digest = match tip_digest { Some(digest) => digest.as_tip_digest(), @@ -396,9 +397,9 @@ impl ArchivalState { /// Return latest block from database, or genesis block if no other block /// is known. - pub async fn get_latest_block(&self) -> Block { + pub async fn get_tip(&self) -> Block { let lookup_res_info: Option = self - .get_latest_block_from_disk() + .get_tip_from_disk() .await .expect("Failed to read block from disk"); @@ -445,7 +446,7 @@ impl ArchivalState { ret } - // Return the block with a given block digest, iff it's available in state somewhere + // Return the block with a given block digest, iff it's available in state somewhere. pub async fn get_block(&self, block_digest: Digest) -> Result> { let maybe_record: Option = self .block_index_db @@ -853,8 +854,8 @@ mod archival_state_tests { use crate::models::state::wallet::WalletSecret; use crate::models::state::UtxoReceiverData; use crate::tests::shared::{ - add_block, add_block_to_archival_state, get_mock_global_state, get_mock_wallet_state, - make_mock_block_with_valid_pow, make_unit_test_archival_state, unit_test_databases, + add_block_to_archival_state, make_mock_block_with_valid_pow, mock_genesis_archival_state, + mock_genesis_global_state, mock_genesis_wallet_state, unit_test_databases, }; use rand::rngs::StdRng; use rand::Rng; @@ -942,7 +943,7 @@ mod archival_state_tests { let network = Network::Alpha; let mut archival_state = make_test_archival_state(network).await; let genesis_wallet_state = - get_mock_wallet_state(WalletSecret::devnet_wallet(), network).await; + mock_genesis_wallet_state(WalletSecret::devnet_wallet(), network).await; let (mock_block_1, _, _) = make_mock_block_with_valid_pow( &archival_state.genesis_block, None, @@ -980,10 +981,11 @@ mod archival_state_tests { let network = Network::Alpha; let genesis_wallet_state = - get_mock_wallet_state(WalletSecret::devnet_wallet(), network).await; + mock_genesis_wallet_state(WalletSecret::devnet_wallet(), network).await; let wallet = genesis_wallet_state.wallet_secret; let own_receiving_address = wallet.nth_generation_spending_key(0).to_address(); - let genesis_receiver_global_state_lock = get_mock_global_state(network, 0, wallet).await; + let genesis_receiver_global_state_lock = + mock_genesis_global_state(network, 0, wallet).await; let mut genesis_receiver_global_state = genesis_receiver_global_state_lock.lock_guard_mut().await; @@ -998,27 +1000,10 @@ mod archival_state_tests { ); { - add_block(&mut genesis_receiver_global_state, mock_block_1.clone()).await?; genesis_receiver_global_state - .chain - .archival_state_mut() - .update_mutator_set(&mock_block_1) + .set_new_tip(mock_block_1.clone()) .await .unwrap(); - let msa = genesis_receiver_global_state - .chain - .archival_state_mut() - .genesis_block - .kernel - .body - .mutator_set_accumulator - .clone(); - genesis_receiver_global_state - .wallet_state - .update_wallet_state_with_new_block(&msa, &mock_block_1) - .await - .unwrap(); - let ams_ref = &genesis_receiver_global_state .chain .archival_state() @@ -1062,13 +1047,9 @@ mod archival_state_tests { .await; // Remove an element from the mutator set, verify that the active window DB is updated. - add_block(&mut genesis_receiver_global_state, mock_block_2.clone()).await?; genesis_receiver_global_state - .chain - .archival_state_mut() - .update_mutator_set(&mock_block_2) - .await - .unwrap(); + .set_new_tip(mock_block_2.clone()) + .await?; let ams_ref = &genesis_receiver_global_state .chain @@ -1086,7 +1067,7 @@ mod archival_state_tests { let mut rng = thread_rng(); let network = Network::Alpha; let (mut archival_state, _peer_db_lock, _data_dir) = - make_unit_test_archival_state(network).await; + mock_genesis_archival_state(network).await; let own_wallet = WalletSecret::new_random(); let own_receiving_address = own_wallet.nth_generation_spending_key(0).to_address(); @@ -1097,12 +1078,7 @@ mod archival_state_tests { own_receiving_address, rng.gen(), ); - archival_state - .write_block( - &mock_block_1a, - Some(mock_block_1a.kernel.header.proof_of_work_family), - ) - .await?; + archival_state.write_block_as_tip(&mock_block_1a).await?; // 2. Update mutator set with this archival_state @@ -1117,12 +1093,7 @@ mod archival_state_tests { own_receiving_address, rng.gen(), ); - archival_state - .write_block( - &mock_block_1a, - Some(mock_block_1b.kernel.header.proof_of_work_family), - ) - .await?; + archival_state.write_block_as_tip(&mock_block_1a).await?; // 4. Update mutator set with that archival_state @@ -1138,18 +1109,20 @@ mod archival_state_tests { #[traced_test] #[tokio::test] async fn update_mutator_set_rollback_ms_block_sync_multiple_inputs_outputs_in_block_test() { - let mut rng = thread_rng(); // Make a rollback of one block that contains multiple inputs and outputs. // This test is intended to verify that rollbacks work for non-trivial // blocks. + + let mut rng = thread_rng(); let network = Network::RegTest; let (mut archival_state, _peer_db_lock, _data_dir) = - make_unit_test_archival_state(network).await; + mock_genesis_archival_state(network).await; let genesis_wallet_state = - get_mock_wallet_state(WalletSecret::devnet_wallet(), network).await; + mock_genesis_wallet_state(WalletSecret::devnet_wallet(), network).await; let genesis_wallet = genesis_wallet_state.wallet_secret; let own_receiving_address = genesis_wallet.nth_generation_spending_key(0).to_address(); - let global_state_lock = get_mock_global_state(Network::RegTest, 42, genesis_wallet).await; + let global_state_lock = + mock_genesis_global_state(Network::RegTest, 42, genesis_wallet).await; let mut num_utxos = Block::premine_utxos(network).len(); // 1. Create new block 1 with one input and four outputs and store it to disk @@ -1205,10 +1178,7 @@ mod archival_state_tests { assert!(block_1a.is_valid(&genesis_block, now + seven_months)); { - archival_state - .write_block(&block_1a, Some(block_1a.kernel.header.proof_of_work_family)) - .await - .unwrap(); + archival_state.write_block_as_tip(&block_1a).await.unwrap(); // 2. Update mutator set with this archival_state.update_mutator_set(&block_1a).await.unwrap(); @@ -1221,11 +1191,7 @@ mod archival_state_tests { rng.gen(), ); archival_state - .write_block( - // &block_1a, - &mock_block_1b, - Some(mock_block_1b.kernel.header.proof_of_work_family), - ) + .write_block_as_tip(&mock_block_1b) .await .unwrap(); num_utxos += mock_block_1b.body().transaction.kernel.outputs.len(); @@ -1273,10 +1239,11 @@ mod archival_state_tests { // mutator set forwards. let network = Network::RegTest; let genesis_wallet_state = - get_mock_wallet_state(WalletSecret::devnet_wallet(), network).await; + mock_genesis_wallet_state(WalletSecret::devnet_wallet(), network).await; let genesis_wallet = genesis_wallet_state.wallet_secret; let own_receiving_address = genesis_wallet.nth_generation_spending_key(0).to_address(); - let global_state_lock = get_mock_global_state(Network::RegTest, 42, genesis_wallet).await; + let global_state_lock = + mock_genesis_global_state(Network::RegTest, 42, genesis_wallet).await; let mut global_state = global_state_lock.lock_guard_mut().await; let genesis_block: Block = *global_state.chain.archival_state().genesis_block.to_owned(); @@ -1335,22 +1302,19 @@ mod archival_state_tests { "next block ({i}) not valid for devnet" ); - // Store the produced block + // Store the produced block as tip { global_state .chain .archival_state_mut() - .write_block( - &next_block, - Some(next_block.kernel.header.proof_of_work_family), - ) + .write_block_as_tip(&next_block) .await?; global_state .chain .light_state_mut() .set_block(next_block.clone()); - // 2. Update mutator set with produced block + // 2. Update archival-mutator set with produced block global_state .chain .archival_state_mut() @@ -1382,7 +1346,7 @@ mod archival_state_tests { } { - // 3. Create competing block 1 and store it to DB + // 3. Create competing block 1 and treat it as new tip let (mock_block_1b, _, _) = make_mock_block_with_valid_pow( &genesis_block, None, @@ -1392,10 +1356,7 @@ mod archival_state_tests { global_state .chain .archival_state_mut() - .write_block( - &mock_block_1b, - Some(mock_block_1b.kernel.header.proof_of_work_family), - ) + .write_block_as_tip(&mock_block_1b) .await?; num_utxos += mock_block_1b.body().transaction.kernel.outputs.len(); @@ -1445,7 +1406,7 @@ mod archival_state_tests { let mut rng = thread_rng(); let network = Network::RegTest; let genesis_wallet_state = - get_mock_wallet_state(WalletSecret::devnet_wallet(), network).await; + mock_genesis_wallet_state(WalletSecret::devnet_wallet(), network).await; let genesis_wallet = genesis_wallet_state.wallet_secret; let own_receiving_address = genesis_wallet.nth_generation_spending_key(0).to_address(); let genesis_block = Block::genesis_block(network); @@ -1453,7 +1414,7 @@ mod archival_state_tests { let seven_months = Timestamp::months(7); let (mut block_1_a, _, _) = make_mock_block_with_valid_pow(&genesis_block, None, own_receiving_address, rng.gen()); - let global_state_lock = get_mock_global_state(network, 42, genesis_wallet).await; + let global_state_lock = mock_genesis_global_state(network, 42, genesis_wallet).await; // Verify that block_1 that only contains the coinbase output is valid assert!(block_1_a.has_proof_of_work(&genesis_block)); @@ -1493,24 +1454,25 @@ mod archival_state_tests { #[traced_test] #[tokio::test] async fn allow_multiple_inputs_and_outputs_in_block() { - let mut rng = thread_rng(); // Test various parts of the state update when a block contains multiple inputs and outputs + + let mut rng = thread_rng(); let network = Network::RegTest; let genesis_wallet_state = - get_mock_wallet_state(WalletSecret::devnet_wallet(), network).await; + mock_genesis_wallet_state(WalletSecret::devnet_wallet(), network).await; let genesis_spending_key = genesis_wallet_state .wallet_secret .nth_generation_spending_key(0); let genesis_state_lock = - get_mock_global_state(network, 3, genesis_wallet_state.wallet_secret).await; + mock_genesis_global_state(network, 3, genesis_wallet_state.wallet_secret).await; let wallet_secret_alice = WalletSecret::new_random(); let alice_spending_key = wallet_secret_alice.nth_generation_spending_key(0); - let alice_state_lock = get_mock_global_state(network, 3, wallet_secret_alice).await; + let alice_state_lock = mock_genesis_global_state(network, 3, wallet_secret_alice).await; let wallet_secret_bob = WalletSecret::new_random(); let bob_spending_key = wallet_secret_bob.nth_generation_spending_key(0); - let bob_state_lock = get_mock_global_state(network, 3, wallet_secret_bob).await; + let bob_state_lock = mock_genesis_global_state(network, 3, wallet_secret_bob).await; let genesis_block = Block::genesis_block(network); let launch = genesis_block.kernel.header.timestamp; @@ -1598,20 +1560,8 @@ mod archival_state_tests { block_1.kernel.body.transaction.kernel.outputs.len() ); - // Update chain states - for state_lock in [&genesis_state_lock, &alice_state_lock, &bob_state_lock] { - let mut state = state_lock.lock_guard_mut().await; - add_block(&mut state, block_1.clone()).await.unwrap(); - state - .chain - .archival_state_mut() - .update_mutator_set(&block_1) - .await - .unwrap(); - } - + // Expect incoming transactions { - // Update wallets let mut genesis_state = genesis_state_lock.lock_guard_mut().await; genesis_state .wallet_state @@ -1623,24 +1573,7 @@ mod archival_state_tests { UtxoNotifier::OwnMiner, ) .unwrap(); - genesis_state - .wallet_state - .update_wallet_state_with_new_block( - &genesis_block.kernel.body.mutator_set_accumulator, - &block_1, - ) - .await - .unwrap(); - assert_eq!( - 3, - genesis_state - .wallet_state - .wallet_db - .monitored_utxos() - .len().await, "Genesis receiver must have 3 UTXOs after block 1: change from transaction, coinbase from block 1, and the spent premine UTXO" - ); } - { let mut alice_state = alice_state_lock.lock_guard_mut().await; for rec_data in receiver_data_for_alice { @@ -1655,14 +1588,6 @@ mod archival_state_tests { ) .unwrap(); } - alice_state - .wallet_state - .update_wallet_state_with_new_block( - &genesis_block.kernel.body.mutator_set_accumulator, - &block_1, - ) - .await - .unwrap(); } { @@ -1679,17 +1604,27 @@ mod archival_state_tests { ) .unwrap(); } - bob_state + } + + // Update chain states + for state_lock in [&genesis_state_lock, &alice_state_lock, &bob_state_lock] { + let mut state = state_lock.lock_guard_mut().await; + state.set_new_tip(block_1.clone()).await.unwrap(); + } + + { + let genesis_state = genesis_state_lock.lock_guard_mut().await; + assert_eq!( + 3, + genesis_state .wallet_state - .update_wallet_state_with_new_block( - &genesis_block.kernel.body.mutator_set_accumulator, - &block_1, - ) - .await - .unwrap(); + .wallet_db + .monitored_utxos() + .len().await, "Genesis receiver must have 3 UTXOs after block 1: change from transaction, coinbase from block 1, and the spent premine UTXO" + ); } - // Now Alice should have a balance of 100 and Bob a balance of 200 + // Alice should have a balance of 100 and Bob a balance of 200 assert_eq!( NeptuneCoins::new(100), @@ -1710,7 +1645,8 @@ mod archival_state_tests { .synced_unspent_available_amount(launch + seven_months) ); - // Make two transactions: Alice sends two UTXOs to Genesis and Bob sends three UTXOs to genesis + // Make two transactions: Alice sends two UTXOs to Genesis (50 + 49 coins and 1 in fee) + // and Bob sends three UTXOs to genesis (50 + 50 + 98 and 2 in fee) let receiver_data_from_alice = vec![ UtxoReceiverData { utxo: Utxo { @@ -1805,56 +1741,7 @@ mod archival_state_tests { let now = block_1.kernel.header.timestamp; assert!(block_2.is_valid(&block_1, now)); - // Update chain states - for state_lock in [&genesis_state_lock, &alice_state_lock, &bob_state_lock] { - let mut state = state_lock.lock_guard_mut().await; - - add_block(&mut state, block_2.clone()).await.unwrap(); - state - .chain - .archival_state_mut() - .update_mutator_set(&block_2) - .await - .unwrap(); - } - - // Update wallets and verify that Alice and Bob's balances are zero - alice_state_lock - .lock_guard_mut() - .await - .wallet_state - .update_wallet_state_with_new_block( - &block_1.kernel.body.mutator_set_accumulator, - &block_2, - ) - .await - .unwrap(); - bob_state_lock - .lock_guard_mut() - .await - .wallet_state - .update_wallet_state_with_new_block( - &block_1.kernel.body.mutator_set_accumulator, - &block_2, - ) - .await - .unwrap(); - assert!(alice_state_lock - .lock_guard() - .await - .get_wallet_status_for_tip() - .await - .synced_unspent_available_amount(launch + seven_months) - .is_zero()); - assert!(bob_state_lock - .lock_guard() - .await - .get_wallet_status_for_tip() - .await - .synced_unspent_available_amount(launch + seven_months) - .is_zero()); - - // Update genesis wallet and verify that all ingoing UTXOs are recorded + // Expect incoming UTXOs for rec_data in receiver_data_from_alice { genesis_state_lock .lock_guard_mut() @@ -1869,6 +1756,7 @@ mod archival_state_tests { ) .unwrap(); } + for rec_data in receiver_data_from_bob { genesis_state_lock .lock_guard_mut() @@ -1883,6 +1771,7 @@ mod archival_state_tests { ) .unwrap(); } + genesis_state_lock .lock_guard_mut() .await @@ -1895,18 +1784,29 @@ mod archival_state_tests { UtxoNotifier::Cli, ) .unwrap(); - genesis_state_lock - .lock_guard_mut() + + // Update chain states + for state_lock in [&genesis_state_lock, &alice_state_lock, &bob_state_lock] { + let mut state = state_lock.lock_guard_mut().await; + state.set_new_tip(block_2.clone()).await.unwrap(); + } + + assert!(alice_state_lock + .lock_guard() .await - .wallet_state - .update_wallet_state_with_new_block( - &block_1.kernel.body.mutator_set_accumulator, - &block_2, - ) + .get_wallet_status_for_tip() .await - .unwrap(); + .synced_unspent_available_amount(launch + seven_months) + .is_zero()); + assert!(bob_state_lock + .lock_guard() + .await + .get_wallet_status_for_tip() + .await + .synced_unspent_available_amount(launch + seven_months) + .is_zero()); - // Verify that states and wallets can be updated successfully + // Verify that all ingoing UTXOs are recorded in wallet of receiver of genesis UTXO assert_eq!( 9, genesis_state_lock.lock_guard().await @@ -1931,9 +1831,10 @@ mod archival_state_tests { .await, "AMS must be correctly updated" ); + assert_eq!(block_2, state.chain.archival_state().get_tip().await); assert_eq!( - block_2, - state.chain.archival_state().get_latest_block().await + block_1, + state.chain.archival_state().get_tip_parent().await.unwrap() ); } } @@ -2829,12 +2730,7 @@ mod archival_state_tests { own_receiving_address, rng.gen(), ); - archival_state - .write_block( - &mock_block_1, - Some(genesis.kernel.header.proof_of_work_family), - ) - .await?; + archival_state.write_block_as_tip(&mock_block_1).await?; // Verify that `LastFile` value is stored correctly let read_last_file: LastFileRecord = archival_state @@ -2921,12 +2817,7 @@ mod archival_state_tests { own_receiving_address, rng.gen(), ); - archival_state - .write_block( - &mock_block_2, - Some(mock_block_1.kernel.header.proof_of_work_family), - ) - .await?; + archival_state.write_block_as_tip(&mock_block_2).await?; // Verify that `LastFile` value is updated correctly, unchanged let read_last_file_2: LastFileRecord = archival_state @@ -3017,7 +2908,7 @@ mod archival_state_tests { ); // Test `get_latest_block_from_disk` - let read_latest_block = archival_state.get_latest_block_from_disk().await?.unwrap(); + let read_latest_block = archival_state.get_tip_from_disk().await?.unwrap(); assert_eq!(mock_block_2, read_latest_block); // Test `get_block_from_block_record` diff --git a/src/models/state/mempool.rs b/src/models/state/mempool.rs index 69d4030b..d0a7e6ab 100644 --- a/src/models/state/mempool.rs +++ b/src/models/state/mempool.rs @@ -403,8 +403,8 @@ mod tests { }, }, tests::shared::{ - get_mock_global_state, get_mock_wallet_state, make_mock_block, - make_mock_transaction_with_wallet, + make_mock_block, make_mock_transaction_with_wallet, mock_genesis_global_state, + mock_genesis_wallet_state, }, }; use anyhow::Result; @@ -419,7 +419,7 @@ mod tests { pub async fn insert_then_get_then_remove_then_get() { let mut mempool = Mempool::new(ByteSize::gb(1)); let network = Network::Alpha; - let wallet_state = get_mock_wallet_state(WalletSecret::devnet_wallet(), network).await; + let wallet_state = mock_genesis_wallet_state(WalletSecret::devnet_wallet(), network).await; let transaction = make_mock_transaction_with_wallet( vec![], vec![], @@ -448,7 +448,7 @@ mod tests { // Create a mempool with n transactions. async fn setup(transactions_count: u32, network: Network) -> Mempool { let mut mempool = Mempool::new(ByteSize::gb(1)); - let wallet_state = get_mock_wallet_state(WalletSecret::devnet_wallet(), network).await; + let wallet_state = mock_genesis_wallet_state(WalletSecret::devnet_wallet(), network).await; for i in 0..transactions_count { let t = make_mock_transaction_with_wallet( vec![], @@ -497,7 +497,7 @@ mod tests { #[tokio::test] async fn prune_stale_transactions() { let wallet_state = - get_mock_wallet_state(WalletSecret::devnet_wallet(), Network::Alpha).await; + mock_genesis_wallet_state(WalletSecret::devnet_wallet(), Network::Alpha).await; let mut mempool = Mempool::new(ByteSize::gb(1)); assert!( mempool.is_empty(), @@ -558,7 +558,7 @@ mod tests { let network = Network::RegTest; let devnet_wallet = WalletSecret::devnet_wallet(); let premine_receiver_global_state_lock = - get_mock_global_state(network, 2, devnet_wallet).await; + mock_genesis_global_state(network, 2, devnet_wallet).await; let mut premine_receiver_global_state = premine_receiver_global_state_lock.lock_guard_mut().await; @@ -568,7 +568,7 @@ mod tests { let other_wallet_secret = WalletSecret::new_pseudorandom(rng.gen()); let other_global_state_lock = - get_mock_global_state(network, 2, other_wallet_secret.clone()).await; + mock_genesis_global_state(network, 2, other_wallet_secret.clone()).await; let mut other_global_state = other_global_state_lock.lock_guard_mut().await; let other_receiver_spending_key = other_wallet_secret.nth_generation_spending_key(0); let other_receiver_address = other_receiver_spending_key.to_address(); @@ -779,7 +779,7 @@ mod tests { // Create a global state object, controlled by a preminer who receives a premine-UTXO. let network = Network::RegTest; let preminer_state_lock = - get_mock_global_state(network, 2, WalletSecret::devnet_wallet()).await; + mock_genesis_global_state(network, 2, WalletSecret::devnet_wallet()).await; let now = Block::genesis_block(network).kernel.header.timestamp; let seven_months = Timestamp::months(7); let mut preminer_state = preminer_state_lock.lock_guard_mut().await; diff --git a/src/models/state/wallet/mod.rs b/src/models/state/wallet/mod.rs index 4d63f006..8f01dbbd 100644 --- a/src/models/state/wallet/mod.rs +++ b/src/models/state/wallet/mod.rs @@ -369,8 +369,8 @@ mod wallet_tests { use crate::models::state::wallet::utxo_notification_pool::UtxoNotifier; use crate::models::state::UtxoReceiverData; use crate::tests::shared::{ - add_block, get_mock_global_state, get_mock_wallet_state, make_mock_block, - make_mock_transaction_with_generation_key, + make_mock_block, make_mock_transaction_with_generation_key, mock_genesis_global_state, + mock_genesis_wallet_state, }; async fn get_monitored_utxos(wallet_state: &WalletState) -> Vec { @@ -385,7 +385,7 @@ mod wallet_tests { // to the wallet state at initialization. let network = Network::RegTest; let mut wallet_state_premine_recipient = - get_mock_wallet_state(WalletSecret::devnet_wallet(), network).await; + mock_genesis_wallet_state(WalletSecret::devnet_wallet(), network).await; let monitored_utxos_premine_wallet = get_monitored_utxos(&wallet_state_premine_recipient).await; assert_eq!( @@ -401,7 +401,7 @@ mod wallet_tests { ); let random_wallet = WalletSecret::new_random(); - let wallet_state_other = get_mock_wallet_state(random_wallet, network).await; + let wallet_state_other = mock_genesis_wallet_state(random_wallet, network).await; let monitored_utxos_other = get_monitored_utxos(&wallet_state_other).await; assert!( monitored_utxos_other.is_empty(), @@ -455,7 +455,8 @@ mod wallet_tests { let mut rng = thread_rng(); let network = Network::RegTest; let own_wallet_secret = WalletSecret::new_random(); - let mut own_wallet_state = get_mock_wallet_state(own_wallet_secret.clone(), network).await; + let mut own_wallet_state = + mock_genesis_wallet_state(own_wallet_secret.clone(), network).await; let other_wallet_secret = WalletSecret::new_random(); let other_recipient_address = other_wallet_secret .nth_generation_spending_key(0) @@ -589,7 +590,7 @@ mod wallet_tests { let mut rng = thread_rng(); let own_wallet_secret = WalletSecret::new_random(); let network = Network::RegTest; - let mut own_wallet_state = get_mock_wallet_state(own_wallet_secret, network).await; + let mut own_wallet_state = mock_genesis_wallet_state(own_wallet_secret, network).await; let own_spending_key = own_wallet_state .wallet_secret .nth_generation_spending_key(0); @@ -809,17 +810,17 @@ mod wallet_tests { // actually tested. let network = Network::RegTest; let own_wallet_secret = WalletSecret::new_random(); - let mut own_wallet_state = get_mock_wallet_state(own_wallet_secret, network).await; + let mut own_wallet_state = mock_genesis_wallet_state(own_wallet_secret, network).await; let own_spending_key = own_wallet_state .wallet_secret .nth_generation_spending_key(0); let own_address = own_spending_key.to_address(); let genesis_block = Block::genesis_block(network); - let premine_wallet = get_mock_wallet_state(WalletSecret::devnet_wallet(), network) + let premine_wallet = mock_genesis_wallet_state(WalletSecret::devnet_wallet(), network) .await .wallet_secret; let premine_receiver_global_state_lock = - get_mock_global_state(network, 2, premine_wallet).await; + mock_genesis_global_state(network, 2, premine_wallet).await; let mut premine_receiver_global_state = premine_receiver_global_state_lock.lock_guard_mut().await; let launch = genesis_block.kernel.header.timestamp; @@ -903,16 +904,10 @@ mod wallet_tests { ) .unwrap(); } - own_wallet_state - .update_wallet_state_with_new_block(&previous_msa, &block_1) - .await?; - add_block(&mut premine_receiver_global_state, block_1.clone()) + premine_receiver_global_state + .set_new_tip(block_1.clone()) .await .unwrap(); - premine_receiver_global_state - .wallet_state - .update_wallet_state_with_new_block(&previous_msa, &block_1) - .await?; assert_eq!( preminers_original_balance @@ -969,16 +964,10 @@ mod wallet_tests { &next_block, ) .await?; - add_block(&mut premine_receiver_global_state, block_1.clone()) + premine_receiver_global_state + .set_new_tip(next_block.clone()) .await .unwrap(); - premine_receiver_global_state - .wallet_state - .update_wallet_state_with_new_block( - &previous_block.kernel.body.mutator_set_accumulator, - &next_block, - ) - .await?; } let block_18 = next_block; @@ -1046,7 +1035,8 @@ mod wallet_tests { &block_2_b, ) .await?; - add_block(&mut premine_receiver_global_state, block_2_b.clone()) + premine_receiver_global_state + .set_new_tip(block_2_b.clone()) .await .unwrap(); premine_receiver_global_state diff --git a/src/models/state/wallet/wallet_state.rs b/src/models/state/wallet/wallet_state.rs index 0690f29b..2fec5eda 100644 --- a/src/models/state/wallet/wallet_state.rs +++ b/src/models/state/wallet/wallet_state.rs @@ -557,7 +557,9 @@ impl WalletState { assert_eq!( new_block.kernel.body.mutator_set_accumulator.clone().hash(), msa_state.hash(), - "Mutator set in wallet-handler must agree with that from applied block" + "\n\nMutator set in applied block:\n{}\n\nmust agree with that in wallet handler:\n{}\n\n", + new_block.kernel.body.mutator_set_accumulator.clone().hash(), + msa_state.hash(), ); changed_mps.sort(); @@ -774,7 +776,8 @@ mod tests { use crate::{ config_models::network::Network, - tests::shared::{get_mock_global_state, get_mock_wallet_state, make_mock_block}, + models::state::wallet::utxo_notification_pool::ExpectedUtxo, + tests::shared::{make_mock_block, mock_genesis_global_state, mock_genesis_wallet_state}, }; use super::*; @@ -782,7 +785,6 @@ mod tests { #[tokio::test] #[traced_test] async fn wallet_state_prune_abandoned_mutxos() { - let mut rng = thread_rng(); // Get genesis block. Verify wallet is empty // Add two blocks to state containing no UTXOs for own wallet // Add a UTXO (e.g. coinbase) in block 3a (height = 3) @@ -797,10 +799,11 @@ mod tests { // Prune // Verify that MUTXO *is* marked as abandoned + let mut rng = thread_rng(); let network = Network::RegTest; let own_wallet_secret = WalletSecret::new_random(); let own_spending_key = own_wallet_secret.nth_generation_spending_key(0); - let own_global_state_lock = get_mock_global_state(network, 0, own_wallet_secret).await; + let own_global_state_lock = mock_genesis_global_state(network, 0, own_wallet_secret).await; let mut own_global_state = own_global_state_lock.lock_guard_mut().await; let genesis_block = Block::genesis_block(network); let monitored_utxos_count_init = own_global_state @@ -835,10 +838,7 @@ mod tests { own_global_state .chain .archival_state_mut() - .write_block( - &new_block, - Some(latest_block.kernel.header.proof_of_work_family), - ) + .write_block_as_tip(&new_block) .await .unwrap(); own_global_state @@ -874,33 +874,17 @@ mod tests { rng.gen(), ); own_global_state - .wallet_state - .expected_utxos - .add_expected_utxo( - block_3a_coinbase_utxo.clone(), - block_3a_coinbase_sender_randomness, - own_spending_key.privacy_preimage, - UtxoNotifier::OwnMiner, - ) - .unwrap(); - own_global_state - .wallet_state - .update_wallet_state_with_new_block(&mutator_set_accumulator, &block_3a) - .await - .unwrap(); - own_global_state - .chain - .archival_state_mut() - .write_block( - &block_3a, - Some(latest_block.kernel.header.proof_of_work_family), + .set_new_self_mined_tip( + block_3a, + ExpectedUtxo::new( + block_3a_coinbase_utxo, + block_3a_coinbase_sender_randomness, + own_spending_key.privacy_preimage, + UtxoNotifier::OwnMiner, + ), ) .await .unwrap(); - own_global_state - .chain - .light_state_mut() - .set_block(block_3a.clone()); assert!( own_global_state @@ -933,23 +917,9 @@ mod tests { let (block_3b, _block_3b_coinbase_utxo, _block_3b_coinbase_sender_randomness) = make_mock_block(&latest_block, None, other_recipient_address, rng.gen()); own_global_state - .wallet_state - .update_wallet_state_with_new_block(&mutator_set_accumulator, &block_3b) - .await - .unwrap(); - own_global_state - .chain - .archival_state_mut() - .write_block( - &block_3b, - Some(latest_block.kernel.header.proof_of_work_family), - ) + .set_new_tip(block_3b.clone()) .await .unwrap(); - own_global_state - .chain - .light_state_mut() - .set_block(block_3b.clone()); assert!( own_global_state @@ -974,31 +944,15 @@ mod tests { // Mine nine blocks on top of 3b, update states latest_block = block_3b; - mutator_set_accumulator = latest_block.kernel.body.mutator_set_accumulator.clone(); for _ in 4..=11 { let (new_block, _new_block_coinbase_utxo, _new_block_coinbase_sender_randomness) = make_mock_block(&latest_block, None, other_recipient_address, rng.gen()); own_global_state - .wallet_state - .update_wallet_state_with_new_block(&mutator_set_accumulator, &new_block) - .await - .unwrap(); - own_global_state - .chain - .archival_state_mut() - .write_block( - &new_block, - Some(latest_block.kernel.header.proof_of_work_family), - ) + .set_new_tip(new_block.clone()) .await .unwrap(); - own_global_state - .chain - .light_state_mut() - .set_block(new_block.clone()); latest_block = new_block; - mutator_set_accumulator = latest_block.kernel.body.mutator_set_accumulator.clone(); } let prune_count_11 = own_global_state @@ -1026,23 +980,9 @@ mod tests { let (block_12, _, _) = make_mock_block(&latest_block, None, other_recipient_address, rng.gen()); own_global_state - .wallet_state - .update_wallet_state_with_new_block(&mutator_set_accumulator, &block_12) + .set_new_tip(block_12.clone()) .await .unwrap(); - own_global_state - .chain - .archival_state_mut() - .write_block( - &block_12, - Some(latest_block.kernel.header.proof_of_work_family), - ) - .await - .unwrap(); - own_global_state - .chain - .light_state_mut() - .set_block(block_12.clone()); assert!( own_global_state @@ -1089,7 +1029,7 @@ mod tests { let wallet = WalletSecret::devnet_wallet(); let genesis_block = Block::genesis_block(network); - let wallet_state = get_mock_wallet_state(wallet, network).await; + let wallet_state = mock_genesis_wallet_state(wallet, network).await; // are we synchronized to the genesis block? assert_eq!( diff --git a/src/peer_loop.rs b/src/peer_loop.rs index 57336379..e9b35b66 100644 --- a/src/peer_loop.rs +++ b/src/peer_loop.rs @@ -1187,7 +1187,7 @@ mod peer_loop_tests { config_models::network::Network, models::{peer::TransactionNotification, state::wallet::WalletSecret}, tests::shared::{ - add_block, get_dummy_peer_connection_data_genesis, get_dummy_socket_address, + get_dummy_peer_connection_data_genesis, get_dummy_socket_address, get_test_genesis_setup, make_mock_block_with_invalid_pow, make_mock_block_with_valid_pow, make_mock_transaction, Action, Mock, }, @@ -1288,7 +1288,7 @@ mod peer_loop_tests { .await .chain .archival_state() - .get_latest_block() + .get_tip() .await; let mut nonce = different_genesis_block.kernel.header.nonce; nonce[2].increment(); @@ -1370,7 +1370,7 @@ mod peer_loop_tests { .await .chain .archival_state() - .get_latest_block() + .get_tip() .await; // Make a with hash above what the implied threshold from @@ -1458,17 +1458,13 @@ mod peer_loop_tests { get_test_genesis_setup(network, 0).await?; let mut global_state_mut = state_lock.lock_guard_mut().await; let peer_address = get_dummy_socket_address(0); - let genesis_block: Block = global_state_mut - .chain - .archival_state() - .get_latest_block() - .await; + let genesis_block: Block = global_state_mut.chain.archival_state().get_tip().await; let a_wallet_secret = WalletSecret::new_random(); let a_recipient_address = a_wallet_secret.nth_generation_spending_key(0).to_address(); let (block_1, _, _) = make_mock_block_with_valid_pow(&genesis_block, None, a_recipient_address, rng.gen()); - add_block(&mut global_state_mut, block_1.clone()).await?; + global_state_mut.set_new_tip(block_1.clone()).await?; drop(global_state_mut); let mock_peer_messages = Mock::new(vec![ @@ -1515,19 +1511,16 @@ mod peer_loop_tests { #[traced_test] #[tokio::test] async fn block_request_batch_in_order_test() -> Result<()> { - let mut rng = thread_rng(); // Scenario: A fork began at block 2, node knows two blocks of height 2 and two of height 3. // A peer requests a batch of blocks starting from block 1. Ensure that the correct blocks // are returned. + + let mut rng = thread_rng(); let network = Network::Alpha; let (_peer_broadcast_tx, from_main_rx_clone, to_main_tx, _to_main_rx1, state_lock, hsd) = get_test_genesis_setup(network, 0).await?; let mut global_state_mut = state_lock.lock_guard_mut().await; - let genesis_block: Block = global_state_mut - .chain - .archival_state() - .get_latest_block() - .await; + let genesis_block: Block = global_state_mut.chain.archival_state().get_tip().await; let peer_address = get_dummy_socket_address(0); let a_wallet_secret = WalletSecret::new_random(); let a_recipient_address = a_wallet_secret.nth_generation_spending_key(0).to_address(); @@ -1542,11 +1535,11 @@ mod peer_loop_tests { let (block_3_b, _, _) = make_mock_block_with_valid_pow(&block_2_b, None, a_recipient_address, rng.gen()); - add_block(&mut global_state_mut, block_1.clone()).await?; - add_block(&mut global_state_mut, block_2_a.clone()).await?; - add_block(&mut global_state_mut, block_3_a.clone()).await?; - add_block(&mut global_state_mut, block_2_b.clone()).await?; - add_block(&mut global_state_mut, block_3_b.clone()).await?; + global_state_mut.set_new_tip(block_1.clone()).await?; + global_state_mut.set_new_tip(block_2_a.clone()).await?; + global_state_mut.set_new_tip(block_2_b.clone()).await?; + global_state_mut.set_new_tip(block_3_b.clone()).await?; + global_state_mut.set_new_tip(block_3_a.clone()).await?; drop(global_state_mut); @@ -1608,18 +1601,15 @@ mod peer_loop_tests { #[traced_test] #[tokio::test] async fn block_request_batch_out_of_order_test() -> Result<()> { - let mut rng = thread_rng(); // Scenario: Same as above, but the peer supplies their hashes in a wrong order. // Ensure that the correct blocks are returned, in the right order. + + let mut rng = thread_rng(); let network = Network::Alpha; let (_peer_broadcast_tx, from_main_rx_clone, to_main_tx, _to_main_rx1, state_lock, hsd) = get_test_genesis_setup(network, 0).await?; let mut global_state_mut = state_lock.lock_guard_mut().await; - let genesis_block: Block = global_state_mut - .chain - .archival_state() - .get_latest_block() - .await; + let genesis_block: Block = global_state_mut.chain.archival_state().get_tip().await; let peer_address = get_dummy_socket_address(0); let a_wallet_secret = WalletSecret::new_random(); let a_recipient_address = a_wallet_secret.nth_generation_spending_key(0).to_address(); @@ -1634,11 +1624,11 @@ mod peer_loop_tests { let (block_3_b, _, _) = make_mock_block_with_valid_pow(&block_2_b, None, a_recipient_address, rng.gen()); - add_block(&mut global_state_mut, block_1.clone()).await?; - add_block(&mut global_state_mut, block_2_a.clone()).await?; - add_block(&mut global_state_mut, block_3_a.clone()).await?; - add_block(&mut global_state_mut, block_2_b.clone()).await?; - add_block(&mut global_state_mut, block_3_b.clone()).await?; + global_state_mut.set_new_tip(block_1.clone()).await?; + global_state_mut.set_new_tip(block_2_a.clone()).await?; + global_state_mut.set_new_tip(block_3_a.clone()).await?; + global_state_mut.set_new_tip(block_2_b.clone()).await?; + global_state_mut.set_new_tip(block_3_b.clone()).await?; drop(global_state_mut); @@ -1681,11 +1671,7 @@ mod peer_loop_tests { let (_peer_broadcast_tx, from_main_rx_clone, to_main_tx, _to_main_rx1, state_lock, hsd) = get_test_genesis_setup(network, 0).await?; let mut global_state_mut = state_lock.lock_guard_mut().await; - let genesis_block: Block = global_state_mut - .chain - .archival_state() - .get_latest_block() - .await; + let genesis_block: Block = global_state_mut.chain.archival_state().get_tip().await; let peer_address = get_dummy_socket_address(0); let a_wallet_secret = WalletSecret::new_random(); let a_recipient_address = a_wallet_secret.nth_generation_spending_key(0).to_address(); @@ -1700,11 +1686,11 @@ mod peer_loop_tests { let (block_3_b, _, _) = make_mock_block_with_valid_pow(&block_2_b, None, a_recipient_address, rng.gen()); - add_block(&mut global_state_mut, block_1.clone()).await?; - add_block(&mut global_state_mut, block_2_a.clone()).await?; - add_block(&mut global_state_mut, block_3_a.clone()).await?; - add_block(&mut global_state_mut, block_2_b.clone()).await?; - add_block(&mut global_state_mut, block_3_b.clone()).await?; + global_state_mut.set_new_tip(block_1.clone()).await?; + global_state_mut.set_new_tip(block_2_a.clone()).await?; + global_state_mut.set_new_tip(block_3_a.clone()).await?; + global_state_mut.set_new_tip(block_2_b.clone()).await?; + global_state_mut.set_new_tip(block_3_b.clone()).await?; drop(global_state_mut); @@ -1750,7 +1736,7 @@ mod peer_loop_tests { .await .chain .archival_state() - .get_latest_block() + .get_tip() .await; let (mock_block_1, _, _) = @@ -1811,7 +1797,7 @@ mod peer_loop_tests { .await .chain .archival_state() - .get_latest_block() + .get_tip() .await; let a_wallet_secret = WalletSecret::new_random(); let a_recipient_address = a_wallet_secret.nth_generation_spending_key(0).to_address(); @@ -1893,11 +1879,7 @@ mod peer_loop_tests { let mut global_state_mut = state_lock.lock_guard_mut().await; let (hsd1, peer_address1) = get_dummy_peer_connection_data_genesis(Network::Alpha, 1).await; - let genesis_block: Block = global_state_mut - .chain - .archival_state() - .get_latest_block() - .await; + let genesis_block: Block = global_state_mut.chain.archival_state().get_tip().await; let own_recipient_address = global_state_mut .wallet_state .wallet_secret @@ -1927,7 +1909,7 @@ mod peer_loop_tests { own_recipient_address, rng.gen(), ); - add_block(&mut global_state_mut, block_1.clone()).await?; + global_state_mut.set_new_tip(block_1.clone()).await?; drop(global_state_mut); @@ -1992,11 +1974,7 @@ mod peer_loop_tests { get_test_genesis_setup(network, 0).await?; let mut global_state_mut = state_lock.lock_guard_mut().await; let peer_address: SocketAddr = get_dummy_socket_address(0); - let genesis_block: Block = global_state_mut - .chain - .archival_state() - .get_latest_block() - .await; + let genesis_block: Block = global_state_mut.chain.archival_state().get_tip().await; let a_wallet_secret = WalletSecret::new_random(); let a_recipient_address = a_wallet_secret.nth_generation_spending_key(0).to_address(); let (block_1, _, _) = make_mock_block_with_valid_pow( @@ -2011,7 +1989,7 @@ mod peer_loop_tests { make_mock_block_with_valid_pow(&block_2.clone(), None, a_recipient_address, rng.gen()); let (block_4, _, _) = make_mock_block_with_valid_pow(&block_3.clone(), None, a_recipient_address, rng.gen()); - add_block(&mut global_state_mut, block_1.clone()).await?; + global_state_mut.set_new_tip(block_1.clone()).await?; drop(global_state_mut); let mock = Mock::new(vec![ @@ -2079,7 +2057,7 @@ mod peer_loop_tests { let global_state = state_lock.lock_guard().await; let peer_address = get_dummy_socket_address(0); - let genesis_block: Block = global_state.chain.archival_state().get_latest_block().await; + let genesis_block: Block = global_state.chain.archival_state().get_tip().await; let a_wallet_secret = WalletSecret::new_random(); let a_recipient_address = a_wallet_secret.nth_generation_spending_key(0).to_address(); let (block_1, _, _) = make_mock_block_with_valid_pow( @@ -2162,11 +2140,7 @@ mod peer_loop_tests { let a_wallet_secret = WalletSecret::new_random(); let a_recipient_address = a_wallet_secret.nth_generation_spending_key(0).to_address(); let peer_socket_address: SocketAddr = get_dummy_socket_address(0); - let genesis_block: Block = global_state_mut - .chain - .archival_state() - .get_latest_block() - .await; + let genesis_block: Block = global_state_mut.chain.archival_state().get_tip().await; let (block_1, _, _) = make_mock_block_with_valid_pow( &genesis_block.clone(), None, @@ -2181,7 +2155,7 @@ mod peer_loop_tests { make_mock_block_with_valid_pow(&block_3.clone(), None, a_recipient_address, rng.gen()); let (block_5, _, _) = make_mock_block_with_valid_pow(&block_4.clone(), None, a_recipient_address, rng.gen()); - add_block(&mut global_state_mut, block_1.clone()).await?; + global_state_mut.set_new_tip(block_1.clone()).await?; drop(global_state_mut); let mock = Mock::new(vec![ @@ -2255,11 +2229,12 @@ mod peer_loop_tests { #[traced_test] #[tokio::test] async fn test_block_reconciliation_interrupted_by_peer_list_request() -> Result<()> { - let mut rng = thread_rng(); // In this scenario, the client knows the genesis block (block 0) and block 1, it // then receives block 4, meaning that block 3, 2, and 1 will have to be requested. // But the requests are interrupted by the peer sending another message: a request // for a list of peers. + + let mut rng = thread_rng(); let network = Network::RegTest; let (_peer_broadcast_tx, from_main_rx_clone, to_main_tx, mut to_main_rx1, state_lock, _hsd) = get_test_genesis_setup(network, 1).await?; @@ -2271,11 +2246,7 @@ mod peer_loop_tests { .into_values() .collect::>(); - let genesis_block: Block = global_state_mut - .chain - .archival_state() - .get_latest_block() - .await; + let genesis_block: Block = global_state_mut.chain.archival_state().get_tip().await; let a_wallet_secret = WalletSecret::new_random(); let a_recipient_address = a_wallet_secret.nth_generation_spending_key(0).to_address(); let (block_1, _, _) = make_mock_block_with_valid_pow( @@ -2290,7 +2261,7 @@ mod peer_loop_tests { make_mock_block_with_valid_pow(&block_2.clone(), None, a_recipient_address, rng.gen()); let (block_4, _, _) = make_mock_block_with_valid_pow(&block_3.clone(), None, a_recipient_address, rng.gen()); - add_block(&mut global_state_mut, block_1.clone()).await?; + global_state_mut.set_new_tip(block_1.clone()).await?; drop(global_state_mut); let (hsd_1, sa_1) = get_dummy_peer_connection_data_genesis(network, 1).await; diff --git a/src/rpc_server.rs b/src/rpc_server.rs index 6e669115..9eae9aa1 100644 --- a/src/rpc_server.rs +++ b/src/rpc_server.rs @@ -694,7 +694,7 @@ mod rpc_server_tests { config_models::network::Network, models::{peer::PeerSanctionReason, state::wallet::WalletSecret}, rpc_server::NeptuneRPCServer, - tests::shared::get_mock_global_state, + tests::shared::mock_genesis_global_state, RPC_CHANNEL_CAPACITY, }; use anyhow::Result; @@ -708,7 +708,7 @@ mod rpc_server_tests { wallet_secret: WalletSecret, peer_count: u8, ) -> (NeptuneRPCServer, GlobalStateLock) { - let global_state_lock = get_mock_global_state(network, peer_count, wallet_secret).await; + let global_state_lock = mock_genesis_global_state(network, peer_count, wallet_secret).await; let (dummy_tx, _rx) = tokio::sync::mpsc::channel::(RPC_CHANNEL_CAPACITY); ( NeptuneRPCServer { diff --git a/src/tests/shared.rs b/src/tests/shared.rs index c2350dcc..79984c27 100644 --- a/src/tests/shared.rs +++ b/src/tests/shared.rs @@ -264,26 +264,7 @@ pub async fn add_block_to_archival_state( archival_state: &mut ArchivalState, new_block: Block, ) -> Result<()> { - let tip_digest: Option = archival_state - .block_index_db - .get(BlockIndexKey::BlockTipDigest) - .await - .map(|x| x.as_tip_digest()); - let tip_header: Option = match tip_digest { - Some(digest) => Some( - archival_state - .block_index_db - .get(BlockIndexKey::Block(digest)) - .await - .unwrap() - .as_block_record() - .block_header, - ), - None => None, - }; - archival_state - .write_block(&new_block, tip_header.map(|x| x.proof_of_work_family)) - .await?; + archival_state.write_block_as_tip(&new_block).await?; archival_state.update_mutator_set(&new_block).await.unwrap(); @@ -304,21 +285,6 @@ pub fn unit_test_data_directory(network: Network) -> Result { DataDirectory::get(Some(tmp_root), network) } -/// Helper function for tests to update state with a new block -pub async fn add_block(state: &mut GlobalState, new_block: Block) -> Result<()> { - let previous_pow_family = state.chain.light_state().kernel.header.proof_of_work_family; - state - .chain - .archival_state_mut() - .write_block(&new_block, Some(previous_pow_family)) - .await?; - if previous_pow_family < new_block.kernel.header.proof_of_work_family { - state.chain.light_state_mut().set_block(new_block); - } - - Ok(()) -} - // Box> is unnecessary because Vec is already heap-allocated. // However, Box<...> is used here because Pin does not allow a &mut T, // So a Box (which also implements DerefMut) allows a pinned, mutable @@ -1007,7 +973,10 @@ pub fn make_mock_block_with_invalid_pow( /// Return a dummy-wallet used for testing. The returned wallet is populated with /// whatever UTXOs are present in the genesis block. -pub async fn get_mock_wallet_state(wallet_secret: WalletSecret, network: Network) -> WalletState { +pub async fn mock_genesis_wallet_state( + wallet_secret: WalletSecret, + network: Network, +) -> WalletState { let cli_args: cli_args::Args = cli_args::Args { number_of_mps_per_utxo: 30, network, @@ -1017,7 +986,8 @@ pub async fn get_mock_wallet_state(wallet_secret: WalletSecret, network: Network WalletState::new_from_wallet_secret(&data_dir, wallet_secret.clone(), &cli_args).await } -pub async fn make_unit_test_archival_state( +/// Return an archival state populated with the genesis block +pub async fn mock_genesis_archival_state( network: Network, ) -> (ArchivalState, PeerDatabases, DataDirectory) { let (block_index_db, peer_db, data_dir) = unit_test_databases(network).await.unwrap(); From 8ef7eb564b19c9abb0230bb9391204718fb9b9ea Mon Sep 17 00:00:00 2001 From: Thorkil Vaerge Date: Tue, 7 May 2024 19:45:37 +0200 Subject: [PATCH 09/13] Make compiler/linter happy --- src/bin/dashboard_src/receive_screen.rs | 4 ++++ src/bin/dashboard_src/send_screen.rs | 5 ++++- src/connect_to_peers.rs | 4 ++-- src/database/storage/storage_vec/traits.rs | 2 -- .../transaction/validity/removal_records_integrity.rs | 8 ++------ src/models/state/archival_state.rs | 3 ++- src/models/state/wallet/mod.rs | 3 ++- src/models/state/wallet/wallet_state.rs | 6 +++--- src/tests/shared.rs | 5 ++--- src/util_types/mutator_set/mutator_set_accumulator.rs | 2 +- src/util_types/test_shared/mutator_set.rs | 8 ++------ 11 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/bin/dashboard_src/receive_screen.rs b/src/bin/dashboard_src/receive_screen.rs index 5714673a..fdcaa785 100644 --- a/src/bin/dashboard_src/receive_screen.rs +++ b/src/bin/dashboard_src/receive_screen.rs @@ -188,9 +188,13 @@ impl Widget for ReceiveScreen { let width = max(0, inner.width as isize - 2) as usize; if width > 0 { let mut address_lines = vec![]; + + // TODO: Not sure how to handle this linting problem, as clippy suggestion doesn't work. + #[allow(clippy::assigning_clones)] while address.len() > width { let (line, remainder) = address.split_at(width); address_lines.push(line.to_owned()); + address = remainder.to_owned(); } address_lines.push(address); diff --git a/src/bin/dashboard_src/send_screen.rs b/src/bin/dashboard_src/send_screen.rs index ce20ee89..5406a0cf 100644 --- a/src/bin/dashboard_src/send_screen.rs +++ b/src/bin/dashboard_src/send_screen.rs @@ -266,7 +266,7 @@ impl SendScreen { } DashboardEvent::ConsoleMode(ConsoleIO::InputSupplied(string)) => { if let Ok(mut own_focus) = self.focus.try_lock() { - self.address = string.trim().to_owned(); + string.trim().clone_into(&mut self.address); *own_focus = SendScreenWidget::Amount; escalate_event = Some(DashboardEvent::RefreshScreen); } else { @@ -350,6 +350,9 @@ impl Widget for SendScreen { self.address.clone() }; let mut address_lines = vec![]; + + // TODO: Not sure how to handle this linting problem, as clippy suggestion doesn't work. + #[allow(clippy::assigning_clones)] while address.len() > width { let (line, remainder) = address.split_at(width); address_lines.push(line.to_owned()); diff --git a/src/connect_to_peers.rs b/src/connect_to_peers.rs index 28339800..0b1fe3f6 100644 --- a/src/connect_to_peers.rs +++ b/src/connect_to_peers.rs @@ -786,8 +786,8 @@ mod connect_tests { let mut own_handshake = state.get_own_handshakedata().await; // Set reported versions to something incompatible - own_handshake.version = "0.0.3".to_owned(); - other_handshake.version = "0.0.0".to_owned(); + "0.0.3".clone_into(&mut own_handshake.version); + "0.0.0".clone_into(&mut other_handshake.version); let peer_address = get_dummy_socket_address(55); let connection_status = check_if_connection_is_allowed( diff --git a/src/database/storage/storage_vec/traits.rs b/src/database/storage/storage_vec/traits.rs index da306843..d45eb011 100644 --- a/src/database/storage/storage_vec/traits.rs +++ b/src/database/storage/storage_vec/traits.rs @@ -230,8 +230,6 @@ pub trait StorageVecStream: StorageVecBase { pub trait StorageVec: StorageVecBase + StorageVecStream {} -pub(in super::super) trait StorageVecIterMut: StorageVec {} - #[cfg(test)] pub(in super::super) mod tests { use super::*; diff --git a/src/models/blockchain/transaction/validity/removal_records_integrity.rs b/src/models/blockchain/transaction/validity/removal_records_integrity.rs index 3390ed7e..c5fc4167 100644 --- a/src/models/blockchain/transaction/validity/removal_records_integrity.rs +++ b/src/models/blockchain/transaction/validity/removal_records_integrity.rs @@ -169,13 +169,9 @@ impl RemovalRecordsIntegrityWitness { working_indices.dedup(); for wi in working_indices { let wi_odd = wi | 1; - if nodes.get(&wi_odd).is_none() { - nodes.insert(wi_odd, rng.gen::()); - } + nodes.entry(wi_odd).or_insert_with(|| rng.gen::()); let wi_even = wi_odd ^ 1; - if nodes.get(&wi_even).is_none() { - nodes.insert(wi_even, rng.gen::()); - } + nodes.entry(wi_even).or_insert_with(|| rng.gen::()); let hash = Hash::hash_pair(nodes[&wi_even], nodes[&wi_odd]); nodes.insert(wi >> 1, hash); } diff --git a/src/models/state/archival_state.rs b/src/models/state/archival_state.rs index 18135fac..dd6f6db6 100644 --- a/src/models/state/archival_state.rs +++ b/src/models/state/archival_state.rs @@ -1841,6 +1841,8 @@ mod archival_state_tests { #[traced_test] #[tokio::test] + // Added due to clippy warning produced by `traced_test` test framework. + #[allow(clippy::needless_return)] async fn get_tip_block_test() -> Result<()> { for network in [ Network::Alpha, @@ -1910,7 +1912,6 @@ mod archival_state_tests { assert_eq!(mock_block_1, archival_state.get_tip_parent().await.unwrap()); } - // Ok(()) } diff --git a/src/models/state/wallet/mod.rs b/src/models/state/wallet/mod.rs index 8f01dbbd..e5de44b3 100644 --- a/src/models/state/wallet/mod.rs +++ b/src/models/state/wallet/mod.rs @@ -804,10 +804,11 @@ mod wallet_tests { #[traced_test] #[tokio::test] async fn wallet_state_maintanence_multiple_inputs_outputs_test() -> Result<()> { - let mut rng = thread_rng(); // An archival state is needed for how we currently add inputs to a transaction. // So it's just used to generate test data, not in any of the functions that are // actually tested. + + let mut rng = thread_rng(); let network = Network::RegTest; let own_wallet_secret = WalletSecret::new_random(); let mut own_wallet_state = mock_genesis_wallet_state(own_wallet_secret, network).await; diff --git a/src/models/state/wallet/wallet_state.rs b/src/models/state/wallet/wallet_state.rs index 2fec5eda..0674cfa7 100644 --- a/src/models/state/wallet/wallet_state.rs +++ b/src/models/state/wallet/wallet_state.rs @@ -427,9 +427,9 @@ impl WalletState { // If output UTXO belongs to us, add it to the list of monitored UTXOs and // add its membership proof to the list of managed membership proofs. if addition_record_to_utxo_info.contains_key(addition_record) { - let utxo = addition_record_to_utxo_info[&addition_record].0.clone(); - let sender_randomness = addition_record_to_utxo_info[&addition_record].1; - let receiver_preimage = addition_record_to_utxo_info[&addition_record].2; + let utxo = addition_record_to_utxo_info[addition_record].0.clone(); + let sender_randomness = addition_record_to_utxo_info[addition_record].1; + let receiver_preimage = addition_record_to_utxo_info[addition_record].2; info!( "Received UTXO in block {}, height {}: value = {}", new_block.hash(), diff --git a/src/tests/shared.rs b/src/tests/shared.rs index 79984c27..c8ef9705 100644 --- a/src/tests/shared.rs +++ b/src/tests/shared.rs @@ -28,7 +28,7 @@ use rand::SeedableRng; use std::path::Path; use std::path::PathBuf; use std::time::SystemTime; -use std::{collections::HashMap, env, net::SocketAddr, pin::Pin, str::FromStr, sync::Arc}; +use std::{collections::HashMap, env, net::SocketAddr, pin::Pin, str::FromStr}; use tasm_lib::twenty_first::util_types::mmr::mmr_accumulator::MmrAccumulator; use tokio::sync::{broadcast, mpsc}; use tokio_serde::{formats::SymmetricalBincode, Serializer}; @@ -63,7 +63,6 @@ use crate::models::database::BlockIndexKey; use crate::models::database::BlockIndexValue; use crate::models::database::PeerDatabases; use crate::models::peer::{HandshakeData, PeerInfo, PeerMessage, PeerStanding}; -use crate::models::shared::LatestBlockInfo; use crate::models::state::archival_state::ArchivalState; use crate::models::state::blockchain_state::{BlockchainArchivalState, BlockchainState}; use crate::models::state::light_state::LightState; @@ -72,8 +71,8 @@ use crate::models::state::networking_state::NetworkingState; use crate::models::state::wallet::address::generation_address; use crate::models::state::wallet::wallet_state::WalletState; use crate::models::state::wallet::WalletSecret; +use crate::models::state::GlobalStateLock; use crate::models::state::UtxoReceiverData; -use crate::models::state::{GlobalState, GlobalStateLock}; use crate::util_types::mutator_set::addition_record::pseudorandom_addition_record; use crate::util_types::mutator_set::addition_record::AdditionRecord; use crate::util_types::mutator_set::chunk_dictionary::pseudorandom_chunk_dictionary; diff --git a/src/util_types/mutator_set/mutator_set_accumulator.rs b/src/util_types/mutator_set/mutator_set_accumulator.rs index 48d86d10..ed655171 100644 --- a/src/util_types/mutator_set/mutator_set_accumulator.rs +++ b/src/util_types/mutator_set/mutator_set_accumulator.rs @@ -429,7 +429,7 @@ impl MutatorSetAccumulator { for mp in preserved_membership_proofs.iter_mut() { for (chunk_index, (_, chunk)) in mp.target_chunks.dictionary.iter_mut() { if mutation_data_preimage.contains_key(chunk_index) { - *chunk = mutation_data_preimage[chunk_index].0.to_owned(); + mutation_data_preimage[chunk_index].0.clone_into(chunk); } } } diff --git a/src/util_types/test_shared/mutator_set.rs b/src/util_types/test_shared/mutator_set.rs index dfd3c937..3607a58e 100644 --- a/src/util_types/test_shared/mutator_set.rs +++ b/src/util_types/test_shared/mutator_set.rs @@ -308,13 +308,9 @@ pub fn pseudorandom_merkle_root_with_authentication_paths( working_indices.dedup(); for wi in working_indices { let wi_odd = wi | 1; - if nodes.get(&wi_odd).is_none() { - nodes.insert(wi_odd, rng.gen::()); - } + nodes.entry(wi_odd).or_insert_with(|| rng.gen::()); let wi_even = wi_odd ^ 1; - if nodes.get(&wi_even).is_none() { - nodes.insert(wi_even, rng.gen::()); - } + nodes.entry(wi_even).or_insert_with(|| rng.gen::()); let hash = H::hash_pair(nodes[&wi_even], nodes[&wi_odd]); nodes.insert(wi >> 1, hash); } From b2928eb0faaaf5787b03ee3625973c3e35785f65 Mon Sep 17 00:00:00 2001 From: sword_smith Date: Tue, 7 May 2024 22:51:58 +0200 Subject: [PATCH 10/13] Fix tests after redefining behavior of `set_new_tip` After redefining the behavior of the `set_new_tip` to update global state with a new block, the function formerly known as `store_block`, we need to rearrange the order in which blocks are added in a few tests. With these changes, all tests pass, and #143 is closed. --- src/models/state/wallet/mod.rs | 13 +++++++------ src/peer_loop.rs | 15 +++++++++------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/models/state/wallet/mod.rs b/src/models/state/wallet/mod.rs index e5de44b3..9bcf4f74 100644 --- a/src/models/state/wallet/mod.rs +++ b/src/models/state/wallet/mod.rs @@ -804,10 +804,6 @@ mod wallet_tests { #[traced_test] #[tokio::test] async fn wallet_state_maintanence_multiple_inputs_outputs_test() -> Result<()> { - // An archival state is needed for how we currently add inputs to a transaction. - // So it's just used to generate test data, not in any of the functions that are - // actually tested. - let mut rng = thread_rng(); let network = Network::RegTest; let own_wallet_secret = WalletSecret::new_random(); @@ -820,10 +816,10 @@ mod wallet_tests { let premine_wallet = mock_genesis_wallet_state(WalletSecret::devnet_wallet(), network) .await .wallet_secret; - let premine_receiver_global_state_lock = + let premine_receiver_global_state = mock_genesis_global_state(network, 2, premine_wallet).await; let mut premine_receiver_global_state = - premine_receiver_global_state_lock.lock_guard_mut().await; + premine_receiver_global_state.lock_guard_mut().await; let launch = genesis_block.kernel.header.timestamp; let seven_months = Timestamp::months(7); let preminers_original_balance = premine_receiver_global_state @@ -921,6 +917,11 @@ mod wallet_tests { "Preminer must have spent 15: 12 + 1 for sent, 2 for fees" ); + own_wallet_state + .update_wallet_state_with_new_block(&previous_msa, &block_1) + .await + .unwrap(); + // Verify that update added 4 UTXOs to list of monitored transactions: // three as regular outputs, and one as coinbase UTXO monitored_utxos = get_monitored_utxos(&own_wallet_state).await; diff --git a/src/peer_loop.rs b/src/peer_loop.rs index e9b35b66..e142fa39 100644 --- a/src/peer_loop.rs +++ b/src/peer_loop.rs @@ -1601,8 +1601,9 @@ mod peer_loop_tests { #[traced_test] #[tokio::test] async fn block_request_batch_out_of_order_test() -> Result<()> { - // Scenario: Same as above, but the peer supplies their hashes in a wrong order. - // Ensure that the correct blocks are returned, in the right order. + // Scenario: A fork began at block 2, node knows two blocks of height 2 and two of height 3. + // A peer requests a batch of blocks starting from block 1, but the peer supplies their + // hashes in a wrong order. Ensure that the correct blocks are returned, in the right order. let mut rng = thread_rng(); let network = Network::Alpha; @@ -1626,9 +1627,9 @@ mod peer_loop_tests { global_state_mut.set_new_tip(block_1.clone()).await?; global_state_mut.set_new_tip(block_2_a.clone()).await?; - global_state_mut.set_new_tip(block_3_a.clone()).await?; global_state_mut.set_new_tip(block_2_b.clone()).await?; global_state_mut.set_new_tip(block_3_b.clone()).await?; + global_state_mut.set_new_tip(block_3_a.clone()).await?; drop(global_state_mut); @@ -1664,9 +1665,11 @@ mod peer_loop_tests { #[traced_test] #[tokio::test] async fn find_canonical_chain_when_multiple_blocks_at_same_height_test() -> Result<()> { - let mut rng = thread_rng(); // Scenario: A fork began at block 2, node knows two blocks of height 2 and two of height 3. - // A peer requests a block at height 2. Verify that the correct block at height 2 is returned. + // A peer requests a block at height 2. Verify that the correct block at height 2 is + // returned. + + let mut rng = thread_rng(); let network = Network::Alpha; let (_peer_broadcast_tx, from_main_rx_clone, to_main_tx, _to_main_rx1, state_lock, hsd) = get_test_genesis_setup(network, 0).await?; @@ -1688,9 +1691,9 @@ mod peer_loop_tests { global_state_mut.set_new_tip(block_1.clone()).await?; global_state_mut.set_new_tip(block_2_a.clone()).await?; - global_state_mut.set_new_tip(block_3_a.clone()).await?; global_state_mut.set_new_tip(block_2_b.clone()).await?; global_state_mut.set_new_tip(block_3_b.clone()).await?; + global_state_mut.set_new_tip(block_3_a.clone()).await?; drop(global_state_mut); From 0a540f8d7f549afe5344ecbf393fc6b09084e270 Mon Sep 17 00:00:00 2001 From: Thorkil Vaerge Date: Wed, 8 May 2024 16:12:57 +0200 Subject: [PATCH 11/13] fix(test): Fix flaky timelock test The timelock test was flaky since it often overflowed on the addition of two `BfieldElement`'s. This closes #148. --- src/models/blockchain/transaction/utxo.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/models/blockchain/transaction/utxo.rs b/src/models/blockchain/transaction/utxo.rs index 6d7ebf99..b9d7bcf2 100644 --- a/src/models/blockchain/transaction/utxo.rs +++ b/src/models/blockchain/transaction/utxo.rs @@ -307,10 +307,10 @@ mod utxo_tests { #[test] fn utxo_timelock_test() { let mut rng = thread_rng(); - let release_date = rng.gen::(); + let release_date = Timestamp(BFieldElement::new(rng.next_u64() >> 2)); let mut delta = release_date + Timestamp::seconds(1); while delta > release_date { - delta = Timestamp(BFieldElement::new(rng.next_u64() >> 1)); + delta = Timestamp(BFieldElement::new(rng.next_u64() >> 2)); } let mut utxo = Utxo::new( LockScript { From 65b283c74f42a266d7e8b678380f815474e928e4 Mon Sep 17 00:00:00 2001 From: Thorkil Vaerge Date: Fri, 10 May 2024 12:12:16 +0200 Subject: [PATCH 12/13] docs(tip-updater): Elaborate on comments in method to update tip Also changes the fn name of the internal worker to match that of the outer methods. --- src/models/state/mod.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/models/state/mod.rs b/src/models/state/mod.rs index 547db75c..bb9f58a2 100644 --- a/src/models/state/mod.rs +++ b/src/models/state/mod.rs @@ -1151,7 +1151,8 @@ impl GlobalState { } /// Update client's state with a new block. Block is assumed to be valid, also wrt. to PoW. - /// The received block will be set as the new tip, regardless of its accumulated PoW. + /// The received block will be set as the new tip, regardless of its accumulated PoW. or its + /// validity. async fn set_new_tip_internal( &mut self, new_block: Block, @@ -1159,8 +1160,7 @@ impl GlobalState { ) -> Result<()> { // note: we make this fn internal so we can log its duration and ensure it will // never be called directly by another fn, without the timings. - - async fn store_block_internal_worker( + async fn set_new_tip_internal_worker( myself: &mut GlobalState, new_block: Block, coinbase_utxo_info: Option, @@ -1194,7 +1194,10 @@ impl GlobalState { .expect("UTXO notification from miner must be accepted"); } - // Get parent of tip for mutator-set data needed for various updates + // Get parent of tip for mutator-set data needed for various updates. Parent of the + // stored block will always exist since all blocks except the genesis block have a + // parent, and the genesis block is considered code, not data, so the genesis block + // will never be changed or updated through this method. let tip_parent = myself .chain .archival_state() @@ -1202,7 +1205,7 @@ impl GlobalState { .await .expect("Parent must exist when storing a new block"); - // Sanity check + // Sanity check that must always be true for a valid block assert_eq!( tip_parent.hash(), new_block.header().prev_block_digest, @@ -1231,7 +1234,7 @@ impl GlobalState { Ok(()) } - crate::macros::duration_async_info!(store_block_internal_worker( + crate::macros::duration_async_info!(set_new_tip_internal_worker( self, new_block, coinbase_utxo_info From 0b212d1d140a1d3f9c754bdfaa80c7a52debd228 Mon Sep 17 00:00:00 2001 From: Thorkil Vaerge Date: Fri, 10 May 2024 12:24:38 +0200 Subject: [PATCH 13/13] Fix name of test-helper function after upstream changes --- src/mine_loop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mine_loop.rs b/src/mine_loop.rs index 4163a3a1..23e207f3 100644 --- a/src/mine_loop.rs +++ b/src/mine_loop.rs @@ -683,7 +683,7 @@ mod mine_loop_tests { async fn block_timestamp_represents_time_block_found() -> Result<()> { let network = Network::RegTest; let global_state_lock = - get_mock_global_state(network, 2, WalletSecret::devnet_wallet()).await; + mock_genesis_global_state(network, 2, WalletSecret::devnet_wallet()).await; let (worker_thread_tx, worker_thread_rx) = oneshot::channel::();