From e7fd919de1051026d0d811157df8ccac16c1d625 Mon Sep 17 00:00:00 2001 From: sword_smith Date: Wed, 16 Oct 2024 19:13:52 +0200 Subject: [PATCH] GlobalState: add test where same block is stored twice Verify that nothing seemingly breaks if the same block is stored as tip twice. This could, theoretically, happen with some weird race conditions. We want to check that nothing breaks if that happens. A better solution to allowing it, though, would maybe be if the `GlobalState` simply ignored the block the 2nd time that it sees it. --- src/main_loop.rs | 9 ++++++++ src/models/state/mod.rs | 48 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/main_loop.rs b/src/main_loop.rs index 9dc90025..dbe7c334 100644 --- a/src/main_loop.rs +++ b/src/main_loop.rs @@ -431,6 +431,15 @@ impl MainLoopHandler { new_block.kernel.header.timestamp.standard_format() ); + // Potential race condition here. + // What if last block is new and canonical, but first + // block is already known then we'll store the same block + // twice. That should be OK though, as the appropriate + // database entries are simply overwritten with the new + // block info. See the + // [GlobalState::test::setting_same_tip_twice_is_allowed] + // test for a test of this phenomenon. + global_state_mut.set_new_tip(new_block).await?; } } diff --git a/src/models/state/mod.rs b/src/models/state/mod.rs index 9cb703b3..a9018a20 100644 --- a/src/models/state/mod.rs +++ b/src/models/state/mod.rs @@ -2297,6 +2297,54 @@ mod global_state_tests { .is_valid(&genesis_block, now)); } + #[traced_test] + #[tokio::test] + async fn setting_same_tip_twice_is_allowed() { + let mut rng = thread_rng(); + let network = Network::Main; + let mut global_state_lock = + 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; + let address = global_state + .wallet_state + .wallet_secret + .nth_generation_spending_key(0) + .to_address(); + + let (block_1, _cb_utxo, _cb_output_randomness) = + make_mock_block(&genesis_block, None, address, rng.gen()); + + global_state.set_new_tip(block_1.clone()).await.unwrap(); + global_state.set_new_tip(block_1.clone()).await.unwrap(); + + assert!(global_state + .chain + .light_state() + .is_valid(&genesis_block, now)); + assert_eq!( + block_1.hash(), + global_state + .chain + .archival_state() + .archival_mutator_set + .get_sync_label() + .await + ); + assert_eq!( + block_1.hash(), + global_state + .chain + .archival_state() + .get_block(block_1.hash()) + .await + .unwrap() + .unwrap() + .hash() + ); + } + /// tests that pertain to restoring a wallet from seed-phrase /// and comparing onchain vs offchain notification methods. mod restore_wallet {