diff --git a/stacks-signer/src/client/mod.rs b/stacks-signer/src/client/mod.rs index 5e957a2166..3fe9f09354 100644 --- a/stacks-signer/src/client/mod.rs +++ b/stacks-signer/src/client/mod.rs @@ -89,9 +89,6 @@ pub enum ClientError { /// Invalid response from the stacks node #[error("Invalid response from the stacks node: {0}")] InvalidResponse(String), - /// A successful sortition has not occurred yet - #[error("The Stacks chain has not processed any successful sortitions yet")] - NoSortitionOnChain, /// A successful sortition's info response should be parseable into a SortitionState #[error("A successful sortition's info response should be parseable into a SortitionState")] UnexpectedSortitionInfo, diff --git a/stacks-signer/src/client/stacks_client.rs b/stacks-signer/src/client/stacks_client.rs index f415896e86..89a3eda2d5 100644 --- a/stacks-signer/src/client/stacks_client.rs +++ b/stacks-signer/src/client/stacks_client.rs @@ -30,7 +30,7 @@ use blockstack_lib::net::api::get_tenures_fork_info::{ use blockstack_lib::net::api::getaccount::AccountEntryResponse; use blockstack_lib::net::api::getpoxinfo::RPCPoxInfoData; use blockstack_lib::net::api::getsortition::{SortitionInfo, RPC_SORTITION_INFO_PATH}; -use blockstack_lib::net::api::getstackers::{GetStackersErrors, GetStackersResponse}; +use blockstack_lib::net::api::getstackers::GetStackersResponse; use blockstack_lib::net::api::postblock::StacksBlockAcceptedData; use blockstack_lib::net::api::postblock_proposal::NakamotoBlockProposal; use blockstack_lib::net::api::postblock_v3; @@ -78,7 +78,6 @@ pub struct StacksClient { #[derive(Deserialize)] struct GetStackersErrorResp { - err_type: String, err_msg: String, } @@ -483,14 +482,11 @@ impl StacksClient { warn!("Failed to parse the GetStackers error response: {e}"); backoff::Error::permanent(e.into()) })?; - if error_data.err_type == GetStackersErrors::NOT_AVAILABLE_ERR_TYPE { - Err(backoff::Error::permanent(ClientError::NoSortitionOnChain)) - } else { - warn!("Got error response ({status}): {}", error_data.err_msg); - Err(backoff::Error::permanent(ClientError::RequestFailure( - status, - ))) - } + + warn!("Got error response ({status}): {}", error_data.err_msg); + Err(backoff::Error::permanent(ClientError::RequestFailure( + status, + ))) }; let stackers_response = retry_with_exponential_backoff::<_, ClientError, GetStackersResponse>(send_request)?; diff --git a/stacks-signer/src/v0/signer.rs b/stacks-signer/src/v0/signer.rs index d6eaa37af8..3b6a2a9180 100644 --- a/stacks-signer/src/v0/signer.rs +++ b/stacks-signer/src/v0/signer.rs @@ -530,7 +530,16 @@ impl Signer { .signer_db .block_lookup(self.reward_cycle, &signer_signature_hash) { - Ok(Some(block_info)) => block_info, + Ok(Some(block_info)) => { + if block_info.state == BlockState::GloballyRejected + || block_info.state == BlockState::GloballyAccepted + { + debug!("{self}: Received block validation for a block that is already marked as {}. Ignoring...", block_info.state); + return None; + } else { + block_info + } + } Ok(None) => { // We have not seen this block before. Why are we getting a response for it? debug!("{self}: Received a block validate response for a block we have not seen before. Ignoring..."); diff --git a/testnet/stacks-node/src/tests/epoch_25.rs b/testnet/stacks-node/src/tests/epoch_25.rs index 42369b800a..2b2a9a640f 100644 --- a/testnet/stacks-node/src/tests/epoch_25.rs +++ b/testnet/stacks-node/src/tests/epoch_25.rs @@ -211,11 +211,12 @@ fn microblocks_disabled() { ); assert_eq!(account.nonce, 1); - info!( - "Microblocks assembled: {}", - test_observer::get_microblocks().len() + let microblocks_assembled = test_observer::get_microblocks().len(); + info!("Microblocks assembled: {microblocks_assembled}",); + assert!( + microblocks_assembled > 0, + "There should be at least 1 microblock assembled" ); - assert_eq!(test_observer::get_microblocks().len(), 1); let miner_nonce_before_microblock_assembly = get_account(&http_origin, &miner_account).nonce; @@ -244,8 +245,8 @@ fn microblocks_disabled() { ); assert_eq!(account.nonce, 1); - // but we should have assembled and announced at least 1 to the observer - assert!(test_observer::get_microblocks().len() >= 2); + // but we should have assembled and announced at least 1 more block to the observer + assert!(test_observer::get_microblocks().len() > microblocks_assembled); info!( "Microblocks assembled: {}", test_observer::get_microblocks().len() diff --git a/testnet/stacks-node/src/tests/nakamoto_integrations.rs b/testnet/stacks-node/src/tests/nakamoto_integrations.rs index 1f5d4491cc..0be76c5362 100644 --- a/testnet/stacks-node/src/tests/nakamoto_integrations.rs +++ b/testnet/stacks-node/src/tests/nakamoto_integrations.rs @@ -1155,69 +1155,6 @@ pub fn is_key_set_for_cycle( Ok(key.is_some()) } -fn signer_vote_if_needed( - btc_regtest_controller: &BitcoinRegtestController, - naka_conf: &Config, - signer_sks: &[StacksPrivateKey], // TODO: Is there some way to get this from the TestSigners? - signers: &TestSigners, -) { - // When we reach the next prepare phase, submit new voting transactions - let block_height = btc_regtest_controller.get_headers_height(); - let reward_cycle = btc_regtest_controller - .get_burnchain() - .block_height_to_reward_cycle(block_height) - .unwrap(); - let prepare_phase_start = btc_regtest_controller - .get_burnchain() - .pox_constants - .prepare_phase_start( - btc_regtest_controller.get_burnchain().first_block_height, - reward_cycle, - ); - - if block_height >= prepare_phase_start { - // If the key is already set, do nothing. - if is_key_set_for_cycle( - reward_cycle + 1, - naka_conf.is_mainnet(), - &naka_conf.node.rpc_bind, - ) - .unwrap_or(false) - { - return; - } - - // If we are self-signing, then we need to vote on the aggregate public key - let http_origin = format!("http://{}", &naka_conf.node.rpc_bind); - - // Get the aggregate key - let aggregate_key = signers.clone().generate_aggregate_key(reward_cycle + 1); - let aggregate_public_key = clarity::vm::Value::buff_from(aggregate_key) - .expect("Failed to serialize aggregate public key"); - - for (i, signer_sk) in signer_sks.iter().enumerate() { - let signer_nonce = get_account(&http_origin, &to_addr(signer_sk)).nonce; - - // Vote on the aggregate public key - let voting_tx = tests::make_contract_call( - &signer_sk, - signer_nonce, - 300, - &StacksAddress::burn_address(false), - SIGNERS_VOTING_NAME, - "vote-for-aggregate-public-key", - &[ - clarity::vm::Value::UInt(i as u128), - aggregate_public_key.clone(), - clarity::vm::Value::UInt(0), - clarity::vm::Value::UInt(reward_cycle as u128 + 1), - ], - ); - submit_tx(&http_origin, &voting_tx); - } - } -} - pub fn setup_epoch_3_reward_set( naka_conf: &Config, blocks_processed: &Arc, @@ -1553,13 +1490,6 @@ fn simple_neon_integration() { &commits_submitted, ) .unwrap(); - - signer_vote_if_needed( - &btc_regtest_controller, - &naka_conf, - &[sender_signer_sk], - &signers, - ); } // Submit a TX @@ -1595,13 +1525,6 @@ fn simple_neon_integration() { &commits_submitted, ) .unwrap(); - - signer_vote_if_needed( - &btc_regtest_controller, - &naka_conf, - &[sender_signer_sk], - &signers, - ); } // load the chain tip, and assert that it is a nakamoto block and at least 30 blocks have advanced in epoch 3 @@ -1805,13 +1728,6 @@ fn simple_neon_integration_with_flash_blocks_on_epoch_3() { &commits_submitted, ) .unwrap(); - - signer_vote_if_needed( - &btc_regtest_controller, - &naka_conf, - &[sender_signer_sk], - &signers, - ); } // Submit a TX @@ -1847,13 +1763,6 @@ fn simple_neon_integration_with_flash_blocks_on_epoch_3() { &commits_submitted, ) .unwrap(); - - signer_vote_if_needed( - &btc_regtest_controller, - &naka_conf, - &[sender_signer_sk], - &signers, - ); } // load the chain tip, and assert that it is a nakamoto block and at least 30 blocks have advanced in epoch 3 @@ -2144,6 +2053,7 @@ fn multiple_miners() { let node_2_p2p = 51025; let http_origin = format!("http://{}", &naka_conf.node.rpc_bind); naka_conf.miner.wait_on_interim_blocks = Duration::from_secs(1); + naka_conf.node.pox_sync_sample_secs = 5; let sender_sk = Secp256k1PrivateKey::new(); let sender_signer_sk = Secp256k1PrivateKey::new(); let sender_signer_addr = tests::to_addr(&sender_signer_sk); @@ -2579,13 +2489,6 @@ fn correct_burn_outs() { &naka_conf, ); - signer_vote_if_needed( - &btc_regtest_controller, - &naka_conf, - &[sender_signer_sk], - &signers, - ); - run_until_burnchain_height( &mut btc_regtest_controller, &blocks_processed, @@ -2645,13 +2548,6 @@ fn correct_burn_outs() { tip_sn.block_height > prior_tip, "The new burnchain tip must have been processed" ); - - signer_vote_if_needed( - &btc_regtest_controller, - &naka_conf, - &[sender_signer_sk], - &signers, - ); } coord_channel @@ -4751,13 +4647,6 @@ fn forked_tenure_is_ignored() { }) .unwrap(); - signer_vote_if_needed( - &btc_regtest_controller, - &naka_conf, - &[sender_signer_sk], - &signers, - ); - info!("Commit op is submitted; unpause Tenure B's block"); // Unpause the broadcast of Tenure B's block, do not submit commits, and do not allow blocks to @@ -6198,13 +6087,6 @@ fn signer_chainstate() { make_stacks_transfer(&sender_sk, sender_nonce, send_fee, &recipient, send_amt); submit_tx(&http_origin, &transfer_tx); - signer_vote_if_needed( - &btc_regtest_controller, - &naka_conf, - &[sender_signer_sk], - &signers, - ); - let timer = Instant::now(); while proposals_submitted.load(Ordering::SeqCst) <= before { thread::sleep(Duration::from_millis(5)); @@ -6681,13 +6563,6 @@ fn continue_tenure_extend() { ) .unwrap(); - signer_vote_if_needed( - &btc_regtest_controller, - &naka_conf, - &[sender_signer_sk], - &signers, - ); - wait_for(5, || { let blocks_processed = coord_channel .lock() @@ -6707,13 +6582,6 @@ fn continue_tenure_extend() { next_block_and(&mut btc_regtest_controller, 60, || Ok(true)).unwrap(); - signer_vote_if_needed( - &btc_regtest_controller, - &naka_conf, - &[sender_signer_sk], - &signers, - ); - wait_for(5, || { let blocks_processed = coord_channel .lock() @@ -6755,13 +6623,6 @@ fn continue_tenure_extend() { next_block_and_process_new_stacks_block(&mut btc_regtest_controller, 60, &coord_channel) .unwrap(); - signer_vote_if_needed( - &btc_regtest_controller, - &naka_conf, - &[sender_signer_sk], - &signers, - ); - wait_for(5, || { let blocks_processed = coord_channel .lock() @@ -6778,13 +6639,6 @@ fn continue_tenure_extend() { next_block_and(&mut btc_regtest_controller, 60, || Ok(true)).unwrap(); - signer_vote_if_needed( - &btc_regtest_controller, - &naka_conf, - &[sender_signer_sk], - &signers, - ); - wait_for(5, || { let blocks_processed = coord_channel .lock() @@ -6810,13 +6664,6 @@ fn continue_tenure_extend() { }) .unwrap(); - signer_vote_if_needed( - &btc_regtest_controller, - &naka_conf, - &[sender_signer_sk], - &signers, - ); - wait_for(5, || { let blocks_processed = coord_channel .lock() diff --git a/testnet/stacks-node/src/tests/signer/mod.rs b/testnet/stacks-node/src/tests/signer/mod.rs index 4248e72145..5961298f2e 100644 --- a/testnet/stacks-node/src/tests/signer/mod.rs +++ b/testnet/stacks-node/src/tests/signer/mod.rs @@ -61,7 +61,6 @@ use stacks_signer::{Signer, SpawnedSigner}; use super::nakamoto_integrations::wait_for; use crate::config::{Config as NeonConfig, EventKeyType, EventObserverConfig, InitialBalance}; -use crate::event_dispatcher::MinedNakamotoBlockEvent; use crate::neon::{Counters, TestFlag}; use crate::run_loop::boot_nakamoto; use crate::tests::bitcoin_regtest::BitcoinCoreController; @@ -312,9 +311,10 @@ impl + Send + 'static, T: SignerEventTrait + 'static> SignerTest MinedNakamotoBlockEvent { + fn mine_nakamoto_block(&mut self, timeout: Duration) { let commits_submitted = self.running_nodes.commits_submitted.clone(); let mined_block_time = Instant::now(); + let info_before = self.stacks_client.get_peer_info().unwrap(); next_block_and_mine_commit( &mut self.running_nodes.btc_regtest_controller, timeout.as_secs(), @@ -323,20 +323,16 @@ impl + Send + 'static, T: SignerEventTrait + 'static> SignerTest info_before.stacks_tip_height) + }) + .unwrap(); let mined_block_elapsed_time = mined_block_time.elapsed(); info!( "Nakamoto block mine time elapsed: {:?}", mined_block_elapsed_time ); - test_observer::get_mined_nakamoto_blocks().pop().unwrap() } fn mine_block_wait_on_processing( @@ -621,7 +617,7 @@ impl + Send + 'static, T: SignerEventTrait + 'static> SignerTest + Send + 'static, T: SignerEventTrait + 'static> SignerTest None, } }) - .collect::>(); + .collect::>(); Ok(block_rejections.len() == expected_signers.len()) }) } diff --git a/testnet/stacks-node/src/tests/signer/v0.rs b/testnet/stacks-node/src/tests/signer/v0.rs index 0bcabcc658..c76d988105 100644 --- a/testnet/stacks-node/src/tests/signer/v0.rs +++ b/testnet/stacks-node/src/tests/signer/v0.rs @@ -1257,8 +1257,9 @@ fn bitcoind_forking_test() { let pre_epoch_3_nonce = get_account(&http_origin, &miner_address).nonce; let pre_fork_tenures = 10; - for _i in 0..pre_fork_tenures { - let _mined_block = signer_test.mine_nakamoto_block(Duration::from_secs(30)); + for i in 0..pre_fork_tenures { + info!("Mining pre-fork tenure {} of {pre_fork_tenures}", i + 1); + signer_test.mine_nakamoto_block(Duration::from_secs(30)); } let pre_fork_1_nonce = get_account(&http_origin, &miner_address).nonce; @@ -1328,7 +1329,8 @@ fn bitcoind_forking_test() { assert_eq!(post_fork_1_nonce, pre_fork_1_nonce - 1 * 2); - for _i in 0..5 { + for i in 0..5 { + info!("Mining post-fork tenure {} of 5", i + 1); signer_test.mine_nakamoto_block(Duration::from_secs(30)); } @@ -1441,8 +1443,9 @@ fn multiple_miners() { let node_2_rpc = 51026; let node_2_p2p = 51025; - let node_1_rpc_bind = format!("127.0.0.1:{}", node_1_rpc); - let node_2_rpc_bind = format!("127.0.0.1:{}", node_2_rpc); + let localhost = "127.0.0.1"; + let node_1_rpc_bind = format!("{localhost}:{node_1_rpc}"); + let node_2_rpc_bind = format!("{localhost}:{node_2_rpc}"); let mut node_2_listeners = Vec::new(); // partition the signer set so that ~half are listening and using node 1 for RPC and events, @@ -1460,11 +1463,12 @@ fn multiple_miners() { signer_config.node_host = node_host.to_string(); }, |config| { - let localhost = "127.0.0.1"; - config.node.rpc_bind = format!("{}:{}", localhost, node_1_rpc); - config.node.p2p_bind = format!("{}:{}", localhost, node_1_p2p); - config.node.data_url = format!("http://{}:{}", localhost, node_1_rpc); - config.node.p2p_address = format!("{}:{}", localhost, node_1_p2p); + config.node.rpc_bind = format!("{localhost}:{node_1_rpc}"); + config.node.p2p_bind = format!("{localhost}:{node_1_p2p}"); + config.node.data_url = format!("http://{localhost}:{node_1_rpc}"); + config.node.p2p_address = format!("{localhost}:{node_1_p2p}"); + config.miner.wait_on_interim_blocks = Duration::from_secs(5); + config.node.pox_sync_sample_secs = 5; config.node.seed = btc_miner_1_seed.clone(); config.node.local_peer_seed = btc_miner_1_seed.clone(); @@ -1491,11 +1495,10 @@ fn multiple_miners() { ); let conf = signer_test.running_nodes.conf.clone(); let mut conf_node_2 = conf.clone(); - let localhost = "127.0.0.1"; - conf_node_2.node.rpc_bind = format!("{}:{}", localhost, node_2_rpc); - conf_node_2.node.p2p_bind = format!("{}:{}", localhost, node_2_p2p); - conf_node_2.node.data_url = format!("http://{}:{}", localhost, node_2_rpc); - conf_node_2.node.p2p_address = format!("{}:{}", localhost, node_2_p2p); + conf_node_2.node.rpc_bind = format!("{localhost}:{node_2_rpc}"); + conf_node_2.node.p2p_bind = format!("{localhost}:{node_2_p2p}"); + conf_node_2.node.data_url = format!("http://{localhost}:{node_2_rpc}"); + conf_node_2.node.p2p_address = format!("{localhost}:{node_2_p2p}"); conf_node_2.node.seed = btc_miner_2_seed.clone(); conf_node_2.burnchain.local_mining_public_key = Some(btc_miner_2_pk.to_hex()); conf_node_2.node.local_peer_seed = btc_miner_2_seed.clone(); @@ -1517,17 +1520,30 @@ fn multiple_miners() { ); let mut run_loop_2 = boot_nakamoto::BootRunLoop::new(conf_node_2.clone()).unwrap(); + let run_loop_stopper_2 = run_loop_2.get_termination_switch(); let rl2_coord_channels = run_loop_2.coordinator_channels(); let Counters { naka_submitted_commits: rl2_commits, .. } = run_loop_2.counters(); - let _run_loop_2_thread = thread::Builder::new() + let run_loop_2_thread = thread::Builder::new() .name("run_loop_2".into()) .spawn(move || run_loop_2.start(None, 0)) .unwrap(); signer_test.boot_to_epoch_3(); + + wait_for(120, || { + let Some(node_1_info) = get_chain_info_opt(&conf) else { + return Ok(false); + }; + let Some(node_2_info) = get_chain_info_opt(&conf_node_2) else { + return Ok(false); + }; + Ok(node_1_info.stacks_tip_height == node_2_info.stacks_tip_height) + }) + .expect("Timed out waiting for boostrapped node to catch up to the miner"); + let pre_nakamoto_peer_1_height = get_chain_info(&conf).stacks_tip_height; info!("------------------------- Reached Epoch 3.0 -------------------------"); @@ -1624,6 +1640,12 @@ fn multiple_miners() { u64::try_from(miner_1_tenures + miner_2_tenures).unwrap() ); + rl2_coord_channels + .lock() + .expect("Mutex poisoned") + .stop_chains_coordinator(); + run_loop_stopper_2.store(false, Ordering::SeqCst); + run_loop_2_thread.join().unwrap(); signer_test.shutdown(); } @@ -1799,6 +1821,18 @@ fn miner_forking() { .unwrap(); signer_test.boot_to_epoch_3(); + + wait_for(120, || { + let Some(node_1_info) = get_chain_info_opt(&conf) else { + return Ok(false); + }; + let Some(node_2_info) = get_chain_info_opt(&conf_node_2) else { + return Ok(false); + }; + Ok(node_1_info.stacks_tip_height == node_2_info.stacks_tip_height) + }) + .expect("Timed out waiting for boostrapped node to catch up to the miner"); + let pre_nakamoto_peer_1_height = get_chain_info(&conf).stacks_tip_height; naka_skip_commit_op.0.lock().unwrap().replace(false); @@ -3003,7 +3037,8 @@ fn signer_set_rollover() { let transfer_tx = make_stacks_transfer(&sender_sk, sender_nonce, send_fee, &recipient, send_amt); submit_tx(&http_origin, &transfer_tx); - let mined_block = signer_test.mine_nakamoto_block(short_timeout); + signer_test.mine_nakamoto_block(short_timeout); + let mined_block = test_observer::get_mined_nakamoto_blocks().pop().unwrap(); let block_sighash = mined_block.signer_signature_hash; let signer_signatures = mined_block.signer_signature; @@ -3117,7 +3152,8 @@ fn signer_set_rollover() { let transfer_tx = make_stacks_transfer(&sender_sk, sender_nonce, send_fee, &recipient, send_amt); submit_tx(&http_origin, &transfer_tx); - let mined_block = signer_test.mine_nakamoto_block(short_timeout); + signer_test.mine_nakamoto_block(short_timeout); + let mined_block = test_observer::get_mined_nakamoto_blocks().pop().unwrap(); info!("---- Verifying that the new signers signed the block -----"); let signer_signatures = mined_block.signer_signature; @@ -3367,8 +3403,9 @@ fn multiple_miners_with_nakamoto_blocks() { let node_2_rpc = 51026; let node_2_p2p = 51025; - let node_1_rpc_bind = format!("127.0.0.1:{}", node_1_rpc); - let node_2_rpc_bind = format!("127.0.0.1:{}", node_2_rpc); + let localhost = "127.0.0.1"; + let node_1_rpc_bind = format!("{localhost}:{node_1_rpc}"); + let node_2_rpc_bind = format!("{localhost}:{node_2_rpc}"); let mut node_2_listeners = Vec::new(); // partition the signer set so that ~half are listening and using node 1 for RPC and events, @@ -3388,11 +3425,12 @@ fn multiple_miners_with_nakamoto_blocks() { signer_config.node_host = node_host.to_string(); }, |config| { - let localhost = "127.0.0.1"; - config.node.rpc_bind = format!("{}:{}", localhost, node_1_rpc); - config.node.p2p_bind = format!("{}:{}", localhost, node_1_p2p); - config.node.data_url = format!("http://{}:{}", localhost, node_1_rpc); - config.node.p2p_address = format!("{}:{}", localhost, node_1_p2p); + config.node.rpc_bind = format!("{localhost}:{node_1_rpc}"); + config.node.p2p_bind = format!("{localhost}:{node_1_p2p}"); + config.node.data_url = format!("http://{localhost}:{node_1_rpc}"); + config.node.p2p_address = format!("{localhost}:{node_1_p2p}"); + config.miner.wait_on_interim_blocks = Duration::from_secs(5); + config.node.pox_sync_sample_secs = 5; config.node.seed = btc_miner_1_seed.clone(); config.node.local_peer_seed = btc_miner_1_seed.clone(); @@ -3421,11 +3459,10 @@ fn multiple_miners_with_nakamoto_blocks() { let conf = signer_test.running_nodes.conf.clone(); let mut conf_node_2 = conf.clone(); - let localhost = "127.0.0.1"; - conf_node_2.node.rpc_bind = format!("{}:{}", localhost, node_2_rpc); - conf_node_2.node.p2p_bind = format!("{}:{}", localhost, node_2_p2p); - conf_node_2.node.data_url = format!("http://{}:{}", localhost, node_2_rpc); - conf_node_2.node.p2p_address = format!("{}:{}", localhost, node_2_p2p); + conf_node_2.node.rpc_bind = format!("{localhost}:{node_2_rpc}"); + conf_node_2.node.p2p_bind = format!("{localhost}:{node_2_p2p}"); + conf_node_2.node.data_url = format!("http://{localhost}:{node_2_rpc}"); + conf_node_2.node.p2p_address = format!("{localhost}:{node_2_p2p}"); conf_node_2.node.seed = btc_miner_2_seed.clone(); conf_node_2.burnchain.local_mining_public_key = Some(btc_miner_2_pk.to_hex()); conf_node_2.node.local_peer_seed = btc_miner_2_seed.clone(); @@ -3449,18 +3486,31 @@ fn multiple_miners_with_nakamoto_blocks() { let http_origin = format!("http://{}", &conf.node.rpc_bind); let mut run_loop_2 = boot_nakamoto::BootRunLoop::new(conf_node_2.clone()).unwrap(); + let run_loop_stopper_2 = run_loop_2.get_termination_switch(); let rl2_coord_channels = run_loop_2.coordinator_channels(); let Counters { naka_submitted_commits: rl2_commits, naka_mined_blocks: blocks_mined2, .. } = run_loop_2.counters(); - let _run_loop_2_thread = thread::Builder::new() + let run_loop_2_thread = thread::Builder::new() .name("run_loop_2".into()) .spawn(move || run_loop_2.start(None, 0)) .unwrap(); signer_test.boot_to_epoch_3(); + + wait_for(120, || { + let Some(node_1_info) = get_chain_info_opt(&conf) else { + return Ok(false); + }; + let Some(node_2_info) = get_chain_info_opt(&conf_node_2) else { + return Ok(false); + }; + Ok(node_1_info.stacks_tip_height == node_2_info.stacks_tip_height) + }) + .expect("Timed out waiting for follower to catch up to the miner"); + let pre_nakamoto_peer_1_height = get_chain_info(&conf).stacks_tip_height; info!("------------------------- Reached Epoch 3.0 -------------------------"); @@ -3582,7 +3632,12 @@ fn multiple_miners_with_nakamoto_blocks() { btc_blocks_mined, u64::try_from(miner_1_tenures + miner_2_tenures).unwrap() ); - + rl2_coord_channels + .lock() + .expect("Mutex poisoned") + .stop_chains_coordinator(); + run_loop_stopper_2.store(false, Ordering::SeqCst); + run_loop_2_thread.join().unwrap(); signer_test.shutdown(); } @@ -3621,6 +3676,7 @@ fn partial_tenure_fork() { let node_1_rpc_bind = format!("127.0.0.1:{}", node_1_rpc); + let localhost = "127.0.0.1"; // All signers are listening to node 1 let mut signer_test: SignerTest = SignerTest::new_with_config_modifications( num_signers, @@ -3632,11 +3688,12 @@ fn partial_tenure_fork() { signer_config.node_host = node_1_rpc_bind.clone(); }, |config| { - let localhost = "127.0.0.1"; - config.node.rpc_bind = format!("{}:{}", localhost, node_1_rpc); - config.node.p2p_bind = format!("{}:{}", localhost, node_1_p2p); - config.node.data_url = format!("http://{}:{}", localhost, node_1_rpc); - config.node.p2p_address = format!("{}:{}", localhost, node_1_p2p); + config.node.rpc_bind = format!("{localhost}:{node_1_rpc}"); + config.node.p2p_bind = format!("{localhost}:{node_1_p2p}"); + config.node.data_url = format!("http://{localhost}:{node_1_rpc}"); + config.node.p2p_address = format!("{localhost}:{node_1_p2p}"); + config.miner.wait_on_interim_blocks = Duration::from_secs(5); + config.node.pox_sync_sample_secs = 5; config.node.seed = btc_miner_1_seed.clone(); config.node.local_peer_seed = btc_miner_1_seed.clone(); @@ -3666,11 +3723,10 @@ fn partial_tenure_fork() { let conf = signer_test.running_nodes.conf.clone(); let mut conf_node_2 = conf.clone(); - let localhost = "127.0.0.1"; - conf_node_2.node.rpc_bind = format!("{}:{}", localhost, node_2_rpc); - conf_node_2.node.p2p_bind = format!("{}:{}", localhost, node_2_p2p); - conf_node_2.node.data_url = format!("http://{}:{}", localhost, node_2_rpc); - conf_node_2.node.p2p_address = format!("{}:{}", localhost, node_2_p2p); + conf_node_2.node.rpc_bind = format!("{localhost}:{node_2_rpc}"); + conf_node_2.node.p2p_bind = format!("{localhost}:{node_2_p2p}"); + conf_node_2.node.data_url = format!("http://{localhost}:{node_2_rpc}"); + conf_node_2.node.p2p_address = format!("{localhost}:{node_2_p2p}"); conf_node_2.node.seed = btc_miner_2_seed.clone(); conf_node_2.burnchain.local_mining_public_key = Some(btc_miner_2_pk.to_hex()); conf_node_2.node.local_peer_seed = btc_miner_2_seed.clone(); @@ -3692,6 +3748,8 @@ fn partial_tenure_fork() { let http_origin = format!("http://{}", &conf.node.rpc_bind); let mut run_loop_2 = boot_nakamoto::BootRunLoop::new(conf_node_2.clone()).unwrap(); + let rl2_coord_channels = run_loop_2.coordinator_channels(); + let run_loop_stopper_2 = run_loop_2.get_termination_switch(); let Counters { naka_mined_blocks: blocks_mined2, naka_proposed_blocks: blocks_proposed2, @@ -3699,14 +3757,14 @@ fn partial_tenure_fork() { } = run_loop_2.counters(); signer_test.boot_to_epoch_3(); - let _run_loop_2_thread = thread::Builder::new() + let run_loop_2_thread = thread::Builder::new() .name("run_loop_2".into()) .spawn(move || run_loop_2.start(None, 0)) .unwrap(); let pre_nakamoto_peer_1_height = get_chain_info(&conf).stacks_tip_height; - wait_for(120, || { + wait_for(200, || { let Some(node_1_info) = get_chain_info_opt(&conf) else { return Ok(false); }; @@ -3964,7 +4022,12 @@ fn partial_tenure_fork() { .unwrap() .unwrap(); assert_eq!(tip.stacks_block_height, ignore_block - 1); - + rl2_coord_channels + .lock() + .expect("Mutex poisoned") + .stop_chains_coordinator(); + run_loop_stopper_2.store(false, Ordering::SeqCst); + run_loop_2_thread.join().unwrap(); signer_test.shutdown(); } @@ -4000,7 +4063,7 @@ fn locally_accepted_blocks_overriden_by_global_rejection() { let sender_addr = tests::to_addr(&sender_sk); let send_amt = 100; let send_fee = 180; - let nmb_txs = 2; + let nmb_txs = 3; let recipient = PrincipalData::from(StacksAddress::burn_address(false)); let short_timeout_secs = 20; let mut signer_test: SignerTest = SignerTest::new( @@ -4056,7 +4119,11 @@ fn locally_accepted_blocks_overriden_by_global_rejection() { info!("------------------------- Attempt to Mine Nakamoto Block N+1 -------------------------"); // Make half of the signers reject the block proposal by the miner to ensure its marked globally rejected - let rejecting_signers: Vec<_> = all_signers.iter().cloned().take(num_signers / 2).collect(); + let rejecting_signers: Vec<_> = all_signers + .iter() + .cloned() + .take(num_signers / 2 + num_signers % 2) + .collect(); TEST_REJECT_ALL_BLOCK_PROPOSAL .lock() .unwrap() @@ -4065,6 +4132,7 @@ fn locally_accepted_blocks_overriden_by_global_rejection() { let transfer_tx = make_stacks_transfer(&sender_sk, sender_nonce, send_fee, &recipient, send_amt); let tx = submit_tx(&http_origin, &transfer_tx); + sender_nonce += 1; info!("Submitted tx {tx} to mine block N+1"); let blocks_before = mined_blocks.load(Ordering::SeqCst); @@ -4088,6 +4156,12 @@ fn locally_accepted_blocks_overriden_by_global_rejection() { .lock() .unwrap() .replace(Vec::new()); + + let transfer_tx = + make_stacks_transfer(&sender_sk, sender_nonce, send_fee, &recipient, send_amt); + let tx = submit_tx(&http_origin, &transfer_tx); + info!("Submitted tx {tx} to mine block N+1'"); + wait_for(short_timeout_secs, || { Ok(mined_blocks.load(Ordering::SeqCst) > blocks_before && signer_test