From 37e27266f8fa8c81554077ea56a99b51a028bdae Mon Sep 17 00:00:00 2001 From: ASuciuX Date: Mon, 9 Sep 2024 18:44:12 +0300 Subject: [PATCH 1/7] added test for fast blocks when epoch 3 is deployed --- .../src/tests/nakamoto_integrations.rs | 393 +++++++++++++++++- 1 file changed, 391 insertions(+), 2 deletions(-) diff --git a/testnet/stacks-node/src/tests/nakamoto_integrations.rs b/testnet/stacks-node/src/tests/nakamoto_integrations.rs index 4839bee3be..c57b59dce6 100644 --- a/testnet/stacks-node/src/tests/nakamoto_integrations.rs +++ b/testnet/stacks-node/src/tests/nakamoto_integrations.rs @@ -100,8 +100,8 @@ use crate::operations::BurnchainOpSigner; use crate::run_loop::boot_nakamoto; use crate::tests::neon_integrations::{ call_read_only, get_account, get_account_result, get_chain_info_result, get_neighbors, - get_pox_info, next_block_and_wait, run_until_burnchain_height, submit_tx, test_observer, - wait_for_runloop, + get_pox_info, next_block_and_wait, next_block_and_wait_with_timeout, + run_until_burnchain_height, submit_tx, test_observer, wait_for_runloop, }; use crate::tests::{ get_chain_info, make_contract_publish, make_contract_publish_versioned, make_stacks_transfer, @@ -928,6 +928,158 @@ pub fn boot_to_epoch_3( info!("Bootstrapped to Epoch-3.0 boundary, Epoch2x miner should stop"); } +/// +/// * `stacker_sks` - must be a private key for sending a large `stack-stx` transaction in order +/// for pox-4 to activate +pub fn boot_to_epoch_3_flash_blocks( + naka_conf: &Config, + blocks_processed: &Arc, + stacker_sks: &[StacksPrivateKey], + signer_sks: &[StacksPrivateKey], + self_signing: &mut Option<&mut TestSigners>, + btc_regtest_controller: &mut BitcoinRegtestController, +) { + assert_eq!(stacker_sks.len(), signer_sks.len()); + + let epochs = naka_conf.burnchain.epochs.clone().unwrap(); + let epoch_3 = &epochs[StacksEpoch::find_epoch_by_id(&epochs, StacksEpochId::Epoch30).unwrap()]; + let current_height = btc_regtest_controller.get_headers_height(); + info!( + "Chain bootstrapped to bitcoin block {current_height:?}, starting Epoch 2x miner"; + "Epoch 3.0 Boundary" => (epoch_3.start_height - 1), + ); + let http_origin = format!("http://{}", &naka_conf.node.rpc_bind); + next_block_and_wait(btc_regtest_controller, &blocks_processed); + next_block_and_wait(btc_regtest_controller, &blocks_processed); + // first mined stacks block + next_block_and_wait(btc_regtest_controller, &blocks_processed); + + let start_time = Instant::now(); + loop { + if start_time.elapsed() > Duration::from_secs(20) { + panic!("Timed out waiting for the stacks height to increment") + } + let stacks_height = get_chain_info(&naka_conf).stacks_tip_height; + if stacks_height >= 1 { + break; + } + thread::sleep(Duration::from_millis(100)); + } + // stack enough to activate pox-4 + + 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(); + + for (stacker_sk, signer_sk) in stacker_sks.iter().zip(signer_sks.iter()) { + let pox_addr = PoxAddress::from_legacy( + AddressHashMode::SerializeP2PKH, + tests::to_addr(&stacker_sk).bytes, + ); + let pox_addr_tuple: clarity::vm::Value = + pox_addr.clone().as_clarity_tuple().unwrap().into(); + let signature = make_pox_4_signer_key_signature( + &pox_addr, + &signer_sk, + reward_cycle.into(), + &Pox4SignatureTopic::StackStx, + CHAIN_ID_TESTNET, + 12_u128, + u128::MAX, + 1, + ) + .unwrap() + .to_rsv(); + + let signer_pk = StacksPublicKey::from_private(signer_sk); + + let stacking_tx = tests::make_contract_call( + &stacker_sk, + 0, + 1000, + &StacksAddress::burn_address(false), + "pox-4", + "stack-stx", + &[ + clarity::vm::Value::UInt(POX_4_DEFAULT_STACKER_STX_AMT), + pox_addr_tuple.clone(), + clarity::vm::Value::UInt(block_height as u128), + clarity::vm::Value::UInt(12), + clarity::vm::Value::some(clarity::vm::Value::buff_from(signature).unwrap()) + .unwrap(), + clarity::vm::Value::buff_from(signer_pk.to_bytes_compressed()).unwrap(), + clarity::vm::Value::UInt(u128::MAX), + clarity::vm::Value::UInt(1), + ], + ); + submit_tx(&http_origin, &stacking_tx); + } + + // Update TestSigner with `signer_sks` if self-signing + if let Some(ref mut signers) = self_signing { + signers.signer_keys = signer_sks.to_vec(); + } + + let prepare_phase_start = btc_regtest_controller + .get_burnchain() + .pox_constants + .prepare_phase_start( + btc_regtest_controller.get_burnchain().first_block_height, + reward_cycle, + ); + + // Run until the prepare phase + run_until_burnchain_height( + btc_regtest_controller, + &blocks_processed, + prepare_phase_start, + &naka_conf, + ); + + // We need to vote on the aggregate public key if this test is self signing + if let Some(signers) = self_signing { + // 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.compress().data.to_vec()) + .expect("Failed to serialize aggregate public key"); + let signer_sks_unique: HashMap<_, _> = signer_sks.iter().map(|x| (x.to_hex(), x)).collect(); + let signer_set = get_stacker_set(&http_origin, reward_cycle + 1); + // Vote on the aggregate public key + for signer_sk in signer_sks_unique.values() { + let signer_index = + get_signer_index(&signer_set, &Secp256k1PublicKey::from_private(signer_sk)) + .unwrap(); + let voting_tx = tests::make_contract_call( + signer_sk, + 0, + 300, + &StacksAddress::burn_address(false), + SIGNERS_VOTING_NAME, + SIGNERS_VOTING_FUNCTION_NAME, + &[ + clarity::vm::Value::UInt(u128::try_from(signer_index).unwrap()), + aggregate_public_key.clone(), + clarity::vm::Value::UInt(0), + clarity::vm::Value::UInt(reward_cycle as u128 + 1), + ], + ); + submit_tx(&http_origin, &voting_tx); + } + } + + run_until_burnchain_height( + btc_regtest_controller, + &blocks_processed, + epoch_3.start_height - 2, + &naka_conf, + ); + + info!("Bootstrapped to Epoch-3.0 boundary, Epoch2x miner should stop"); +} + fn get_signer_index( stacker_set: &GetStackersResponse, signer_key: &Secp256k1PublicKey, @@ -1519,6 +1671,243 @@ fn simple_neon_integration() { run_loop_thread.join().unwrap(); } +#[test] +#[ignore] +/// This test spins up a nakamoto-neon node. +/// It starts in Epoch 2.0, mines with `neon_node` to Epoch 3.0, +/// having flash blocks when epoch updates and expects everything to work normally, +/// then switches to Nakamoto operation (activating pox-4 by submitting a stack-stx tx). The BootLoop +/// struct handles the epoch-2/3 tear-down and spin-up. +/// This test makes three assertions: +/// * 30 blocks are mined after 3.0 starts. This is enough to mine across 2 reward cycles +/// * A transaction submitted to the mempool in 3.0 will be mined in 3.0 +/// * The final chain tip is a nakamoto block +fn simple_neon_integration_with_flash_blocks_on_epoch_3() { + if env::var("BITCOIND_TEST") != Ok("1".into()) { + return; + } + + let (mut naka_conf, _miner_account) = naka_neon_integration_conf(None); + let prom_bind = format!("{}:{}", "127.0.0.1", 6000); + naka_conf.node.prometheus_bind = Some(prom_bind.clone()); + naka_conf.miner.wait_on_interim_blocks = Duration::from_secs(1000); + let sender_sk = Secp256k1PrivateKey::new(); + // setup sender + recipient for a test stx transfer + let sender_addr = tests::to_addr(&sender_sk); + let send_amt = 1000; + let send_fee = 100; + naka_conf.add_initial_balance( + PrincipalData::from(sender_addr.clone()).to_string(), + send_amt * 2 + send_fee, + ); + let sender_signer_sk = Secp256k1PrivateKey::new(); + let sender_signer_addr = tests::to_addr(&sender_signer_sk); + let mut signers = TestSigners::new(vec![sender_signer_sk.clone()]); + naka_conf.add_initial_balance( + PrincipalData::from(sender_signer_addr.clone()).to_string(), + 100000, + ); + let recipient = PrincipalData::from(StacksAddress::burn_address(false)); + let stacker_sk = setup_stacker(&mut naka_conf); + + test_observer::spawn(); + let observer_port = test_observer::EVENT_OBSERVER_PORT; + naka_conf.events_observers.insert(EventObserverConfig { + endpoint: format!("localhost:{observer_port}"), + events_keys: vec![EventKeyType::AnyEvent], + }); + + let mut btcd_controller = BitcoinCoreController::new(naka_conf.clone()); + btcd_controller + .start_bitcoind() + .expect("Failed starting bitcoind"); + let mut btc_regtest_controller = BitcoinRegtestController::new(naka_conf.clone(), None); + btc_regtest_controller.bootstrap_chain(201); + + let mut run_loop = boot_nakamoto::BootRunLoop::new(naka_conf.clone()).unwrap(); + let run_loop_stopper = run_loop.get_termination_switch(); + let Counters { + blocks_processed, + naka_submitted_commits: commits_submitted, + naka_proposed_blocks: proposals_submitted, + .. + } = run_loop.counters(); + + let coord_channel = run_loop.coordinator_channels(); + + let run_loop_thread = thread::spawn(move || run_loop.start(None, 0)); + wait_for_runloop(&blocks_processed); + boot_to_epoch_3_flash_blocks( + &naka_conf, + &blocks_processed, + &[stacker_sk], + &[sender_signer_sk], + &mut Some(&mut signers), + &mut btc_regtest_controller, + ); + + // mine 3 blocks which should be the ones for setting up the miner + next_block_and_wait_with_timeout(&mut btc_regtest_controller, &blocks_processed, 10); + next_block_and_wait_with_timeout(&mut btc_regtest_controller, &blocks_processed, 10); + next_block_and_wait_with_timeout(&mut btc_regtest_controller, &blocks_processed, 10); + + info!("Bootstrapped to Epoch-3.0 boundary, starting nakamoto miner"); + + let burnchain = naka_conf.get_burnchain(); + let sortdb = burnchain.open_sortition_db(true).unwrap(); + let (mut chainstate, _) = StacksChainState::open( + naka_conf.is_mainnet(), + naka_conf.burnchain.chain_id, + &naka_conf.get_chainstate_path_str(), + None, + ) + .unwrap(); + + let block_height_pre_3_0 = + NakamotoChainState::get_canonical_block_header(chainstate.db(), &sortdb) + .unwrap() + .unwrap() + .stacks_block_height; + + // query for prometheus metrics + #[cfg(feature = "monitoring_prom")] + { + let prom_http_origin = format!("http://{}", prom_bind); + let client = reqwest::blocking::Client::new(); + let res = client + .get(&prom_http_origin) + .send() + .unwrap() + .text() + .unwrap(); + let expected_result = format!("stacks_node_stacks_tip_height {block_height_pre_3_0}"); + assert!(res.contains(&expected_result)); + } + + info!("Nakamoto miner started..."); + blind_signer(&naka_conf, &signers, proposals_submitted); + + wait_for_first_naka_block_commit(60, &commits_submitted); + + // Mine 15 nakamoto tenures + for _i in 0..15 { + next_block_and_mine_commit( + &mut btc_regtest_controller, + 60, + &coord_channel, + &commits_submitted, + ) + .unwrap(); + + signer_vote_if_needed( + &btc_regtest_controller, + &naka_conf, + &[sender_signer_sk], + &signers, + ); + } + + // Submit a TX + let transfer_tx = make_stacks_transfer(&sender_sk, 0, send_fee, &recipient, send_amt); + let transfer_tx_hex = format!("0x{}", to_hex(&transfer_tx)); + + let tip = NakamotoChainState::get_canonical_block_header(chainstate.db(), &sortdb) + .unwrap() + .unwrap(); + + let mut mempool = naka_conf + .connect_mempool_db() + .expect("Database failure opening mempool"); + + mempool + .submit_raw( + &mut chainstate, + &sortdb, + &tip.consensus_hash, + &tip.anchored_header.block_hash(), + transfer_tx.clone(), + &ExecutionCost::max_value(), + &StacksEpochId::Epoch30, + ) + .unwrap(); + + // Mine 15 more nakamoto tenures + for _i in 0..15 { + next_block_and_mine_commit( + &mut btc_regtest_controller, + 60, + &coord_channel, + &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 + let tip = NakamotoChainState::get_canonical_block_header(chainstate.db(), &sortdb) + .unwrap() + .unwrap(); + info!( + "Latest tip"; + "height" => tip.stacks_block_height, + "is_nakamoto" => tip.anchored_header.as_stacks_nakamoto().is_some(), + ); + + // assert that the transfer tx was observed + let transfer_tx_included = test_observer::get_blocks() + .into_iter() + .find(|block_json| { + block_json["transactions"] + .as_array() + .unwrap() + .iter() + .find(|tx_json| tx_json["raw_tx"].as_str() == Some(&transfer_tx_hex)) + .is_some() + }) + .is_some(); + + assert!( + transfer_tx_included, + "Nakamoto node failed to include the transfer tx" + ); + + assert!(tip.anchored_header.as_stacks_nakamoto().is_some()); + assert!(tip.stacks_block_height >= block_height_pre_3_0 + 30); + + // Check that we aren't missing burn blocks + let bhh = u64::from(tip.burn_header_height); + test_observer::contains_burn_block_range(220..=bhh).unwrap(); + + // make sure prometheus returns an updated height + #[cfg(feature = "monitoring_prom")] + { + let prom_http_origin = format!("http://{}", prom_bind); + let client = reqwest::blocking::Client::new(); + let res = client + .get(&prom_http_origin) + .send() + .unwrap() + .text() + .unwrap(); + let expected_result = format!("stacks_node_stacks_tip_height {}", tip.stacks_block_height); + assert!(res.contains(&expected_result)); + } + + coord_channel + .lock() + .expect("Mutex poisoned") + .stop_chains_coordinator(); + run_loop_stopper.store(false, Ordering::SeqCst); + + run_loop_thread.join().unwrap(); +} + #[test] #[ignore] /// This test spins up a nakamoto-neon node. From f1435430325b8b9e9f394bd0946b90211191af0e Mon Sep 17 00:00:00 2001 From: ASuciuX Date: Tue, 10 Sep 2024 17:46:59 +0300 Subject: [PATCH 2/7] better naming flash blocks functions and add remove waiting for blocks --- .../src/tests/nakamoto_integrations.rs | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/testnet/stacks-node/src/tests/nakamoto_integrations.rs b/testnet/stacks-node/src/tests/nakamoto_integrations.rs index c57b59dce6..17f068f474 100644 --- a/testnet/stacks-node/src/tests/nakamoto_integrations.rs +++ b/testnet/stacks-node/src/tests/nakamoto_integrations.rs @@ -928,10 +928,13 @@ pub fn boot_to_epoch_3( info!("Bootstrapped to Epoch-3.0 boundary, Epoch2x miner should stop"); } +/// Boot the chain to just before the Epoch 3.0 boundary to allow for flash blocks +/// This function is similar to `boot_to_epoch_3`, but it stops at epoch 3 start height - 2, +/// allowing for flash blocks to occur when the epoch changes. /// -/// * `stacker_sks` - must be a private key for sending a large `stack-stx` transaction in order -/// for pox-4 to activate -pub fn boot_to_epoch_3_flash_blocks( +/// * `stacker_sks` - private keys for sending large `stack-stx` transactions to activate pox-4 +/// * `signer_sks` - corresponding signer keys for the stackers +pub fn boot_to_pre_epoch_3_boundary( naka_conf: &Config, blocks_processed: &Arc, stacker_sks: &[StacksPrivateKey], @@ -1077,7 +1080,7 @@ pub fn boot_to_epoch_3_flash_blocks( &naka_conf, ); - info!("Bootstrapped to Epoch-3.0 boundary, Epoch2x miner should stop"); + info!("Bootstrapped to one block before Epoch 3.0 boundary, Epoch 2.x miner should continue for one more block"); } fn get_signer_index( @@ -1737,7 +1740,7 @@ fn simple_neon_integration_with_flash_blocks_on_epoch_3() { let run_loop_thread = thread::spawn(move || run_loop.start(None, 0)); wait_for_runloop(&blocks_processed); - boot_to_epoch_3_flash_blocks( + boot_to_pre_epoch_3_boundary( &naka_conf, &blocks_processed, &[stacker_sk], @@ -1746,10 +1749,10 @@ fn simple_neon_integration_with_flash_blocks_on_epoch_3() { &mut btc_regtest_controller, ); - // mine 3 blocks which should be the ones for setting up the miner - next_block_and_wait_with_timeout(&mut btc_regtest_controller, &blocks_processed, 10); - next_block_and_wait_with_timeout(&mut btc_regtest_controller, &blocks_processed, 10); - next_block_and_wait_with_timeout(&mut btc_regtest_controller, &blocks_processed, 10); + // Mine 3 Bitcoin blocks quickly without waiting for Stacks blocks to be processed + for _ in 0..3 { + btc_regtest_controller.build_next_block(1); + } info!("Bootstrapped to Epoch-3.0 boundary, starting nakamoto miner"); From eb52ad90d9603c7d6556a45856543807213baeab Mon Sep 17 00:00:00 2001 From: ASuciuX Date: Thu, 12 Sep 2024 04:43:58 +0300 Subject: [PATCH 3/7] fix flash block integration test --- .../src/tests/nakamoto_integrations.rs | 80 ++++++++++--------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/testnet/stacks-node/src/tests/nakamoto_integrations.rs b/testnet/stacks-node/src/tests/nakamoto_integrations.rs index 17f068f474..6aca29b315 100644 --- a/testnet/stacks-node/src/tests/nakamoto_integrations.rs +++ b/testnet/stacks-node/src/tests/nakamoto_integrations.rs @@ -100,8 +100,8 @@ use crate::operations::BurnchainOpSigner; use crate::run_loop::boot_nakamoto; use crate::tests::neon_integrations::{ call_read_only, get_account, get_account_result, get_chain_info_result, get_neighbors, - get_pox_info, next_block_and_wait, next_block_and_wait_with_timeout, - run_until_burnchain_height, submit_tx, test_observer, wait_for_runloop, + get_pox_info, next_block_and_wait, run_until_burnchain_height, submit_tx, test_observer, + wait_for_runloop, }; use crate::tests::{ get_chain_info, make_contract_publish, make_contract_publish_versioned, make_stacks_transfer, @@ -1749,13 +1749,43 @@ fn simple_neon_integration_with_flash_blocks_on_epoch_3() { &mut btc_regtest_controller, ); - // Mine 3 Bitcoin blocks quickly without waiting for Stacks blocks to be processed - for _ in 0..3 { + let burnchain = naka_conf.get_burnchain(); + let sortdb = burnchain.open_sortition_db(true).unwrap(); + let tip = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn()).unwrap(); + let block_height_before_mining = tip.block_height; + + // Mine 3 Bitcoin blocks rapidly without waiting for Stacks blocks to be processed. + // These blocks won't be considered "mined" until the next_block_and_wait call. + for _i in 0..3 { btc_regtest_controller.build_next_block(1); + let tip = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn()).unwrap(); + + // Verify that the canonical burn chain tip hasn't advanced yet + assert_eq!( + tip.block_height, + btc_regtest_controller.get_headers_height() - 1 + ); + assert_eq!(tip.block_height, block_height_before_mining); } info!("Bootstrapped to Epoch-3.0 boundary, starting nakamoto miner"); + // Mine a new block and wait for it to be processed. + // This should update the canonical burn chain tip to include all 4 new blocks. + next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + let tip = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn()).unwrap(); + // Verify that the burn chain tip has advanced by 4 blocks + assert_eq!( + tip.block_height, + block_height_before_mining + 4, + "Burn chain tip should have advanced by 4 blocks" + ); + + assert_eq!( + tip.block_height, + btc_regtest_controller.get_headers_height() - 1 + ); + let burnchain = naka_conf.get_burnchain(); let sortdb = burnchain.open_sortition_db(true).unwrap(); let (mut chainstate, _) = StacksChainState::open( @@ -1772,21 +1802,6 @@ fn simple_neon_integration_with_flash_blocks_on_epoch_3() { .unwrap() .stacks_block_height; - // query for prometheus metrics - #[cfg(feature = "monitoring_prom")] - { - let prom_http_origin = format!("http://{}", prom_bind); - let client = reqwest::blocking::Client::new(); - let res = client - .get(&prom_http_origin) - .send() - .unwrap() - .text() - .unwrap(); - let expected_result = format!("stacks_node_stacks_tip_height {block_height_pre_3_0}"); - assert!(res.contains(&expected_result)); - } - info!("Nakamoto miner started..."); blind_signer(&naka_conf, &signers, proposals_submitted); @@ -1883,24 +1898,17 @@ fn simple_neon_integration_with_flash_blocks_on_epoch_3() { assert!(tip.anchored_header.as_stacks_nakamoto().is_some()); assert!(tip.stacks_block_height >= block_height_pre_3_0 + 30); - // Check that we aren't missing burn blocks + // Check that we have the expected burn blocks + // We expect to have blocks 220-230 and 234 onwards, with a gap for the flash blocks let bhh = u64::from(tip.burn_header_height); - test_observer::contains_burn_block_range(220..=bhh).unwrap(); - - // make sure prometheus returns an updated height - #[cfg(feature = "monitoring_prom")] - { - let prom_http_origin = format!("http://{}", prom_bind); - let client = reqwest::blocking::Client::new(); - let res = client - .get(&prom_http_origin) - .send() - .unwrap() - .text() - .unwrap(); - let expected_result = format!("stacks_node_stacks_tip_height {}", tip.stacks_block_height); - assert!(res.contains(&expected_result)); - } + test_observer::contains_burn_block_range(220..=230).unwrap(); + test_observer::contains_burn_block_range(234..=bhh).unwrap(); + // Verify that we're missing the expected flash blocks + assert!( + test_observer::contains_burn_block_range(231..=233).is_err(), + "Expected to be missing burn blocks 231-233 due to flash blocks" + ); + info!("Verified burn block ranges, including expected gap for flash blocks"); coord_channel .lock() From a3d4d42dad79437f32439698a0538055f6b6f16d Mon Sep 17 00:00:00 2001 From: ASuciuX Date: Wed, 18 Sep 2024 17:03:24 +0300 Subject: [PATCH 4/7] add integration test to run on CI --- .github/workflows/bitcoin-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/bitcoin-tests.yml b/.github/workflows/bitcoin-tests.yml index a7a483665e..04b4a51f70 100644 --- a/.github/workflows/bitcoin-tests.yml +++ b/.github/workflows/bitcoin-tests.yml @@ -77,6 +77,7 @@ jobs: - tests::epoch_25::microblocks_disabled - tests::should_succeed_handling_malformed_and_valid_txs - tests::nakamoto_integrations::simple_neon_integration + - tests::nakamoto_integrations::simple_neon_integration_with_flash_blocks_on_epoch_3 - tests::nakamoto_integrations::mine_multiple_per_tenure_integration - tests::nakamoto_integrations::block_proposal_api_endpoint - tests::nakamoto_integrations::miner_writes_proposed_block_to_stackerdb From 27410626b0b75d60030e7c550cef149456f13685 Mon Sep 17 00:00:00 2001 From: ASuciuX Date: Wed, 18 Sep 2024 22:55:15 +0300 Subject: [PATCH 5/7] update flashblocks integration test for CI --- .../src/tests/nakamoto_integrations.rs | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/testnet/stacks-node/src/tests/nakamoto_integrations.rs b/testnet/stacks-node/src/tests/nakamoto_integrations.rs index 54d82df92c..85bbf9120d 100644 --- a/testnet/stacks-node/src/tests/nakamoto_integrations.rs +++ b/testnet/stacks-node/src/tests/nakamoto_integrations.rs @@ -1676,7 +1676,6 @@ fn simple_neon_integration() { } #[test] -#[ignore] /// This test spins up a nakamoto-neon node. /// It starts in Epoch 2.0, mines with `neon_node` to Epoch 3.0, /// having flash blocks when epoch updates and expects everything to work normally, @@ -1900,15 +1899,35 @@ fn simple_neon_integration_with_flash_blocks_on_epoch_3() { assert!(tip.stacks_block_height >= block_height_pre_3_0 + 30); // Check that we have the expected burn blocks - // We expect to have blocks 220-230 and 234 onwards, with a gap for the flash blocks + // We expect to have around the blocks 220-230 and 234 onwards, with a gap of 3 blocks for the flash blocks let bhh = u64::from(tip.burn_header_height); - test_observer::contains_burn_block_range(220..=230).unwrap(); - test_observer::contains_burn_block_range(234..=bhh).unwrap(); - // Verify that we're missing the expected flash blocks - assert!( - test_observer::contains_burn_block_range(231..=233).is_err(), - "Expected to be missing burn blocks 231-233 due to flash blocks" + // Find the gap in burn blocks + let mut gap_start = 0; + let mut gap_end = 0; + for i in 220..=bhh { + if test_observer::contains_burn_block_range(i..=i).is_err() { + if gap_start == 0 { + gap_start = i; + } + gap_end = i; + } else if gap_start != 0 { + break; + } + } + + // Verify that there's a gap of exactly 3 blocks + assert_eq!( + gap_end - gap_start + 1, + 3, + "Expected a gap of exactly 3 burn blocks due to flash blocks, found gap from {} to {}", + gap_start, + gap_end ); + + // Verify blocks before and after the gap + test_observer::contains_burn_block_range(220..=(gap_start - 1)).unwrap(); + test_observer::contains_burn_block_range((gap_end + 1)..=bhh).unwrap(); + info!("Verified burn block ranges, including expected gap for flash blocks"); coord_channel From 33ff38a2d25182ff107bd3bd333c354c60e73c4b Mon Sep 17 00:00:00 2001 From: ASuciuX Date: Thu, 19 Sep 2024 00:01:11 +0300 Subject: [PATCH 6/7] add ignore to flashblocks test header --- testnet/stacks-node/src/tests/nakamoto_integrations.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/testnet/stacks-node/src/tests/nakamoto_integrations.rs b/testnet/stacks-node/src/tests/nakamoto_integrations.rs index 85bbf9120d..9b89e04856 100644 --- a/testnet/stacks-node/src/tests/nakamoto_integrations.rs +++ b/testnet/stacks-node/src/tests/nakamoto_integrations.rs @@ -1676,6 +1676,7 @@ fn simple_neon_integration() { } #[test] +#[ignore] /// This test spins up a nakamoto-neon node. /// It starts in Epoch 2.0, mines with `neon_node` to Epoch 3.0, /// having flash blocks when epoch updates and expects everything to work normally, From 56ae16867c4d7a581e7f4e4c0f7bced1df9f56e0 Mon Sep 17 00:00:00 2001 From: ASuciuX Date: Thu, 19 Sep 2024 14:31:05 +0300 Subject: [PATCH 7/7] check that epoch3 start burn block is in the missing blocks --- .../src/tests/nakamoto_integrations.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/testnet/stacks-node/src/tests/nakamoto_integrations.rs b/testnet/stacks-node/src/tests/nakamoto_integrations.rs index 9b89e04856..b50f9a459e 100644 --- a/testnet/stacks-node/src/tests/nakamoto_integrations.rs +++ b/testnet/stacks-node/src/tests/nakamoto_integrations.rs @@ -1902,6 +1902,12 @@ fn simple_neon_integration_with_flash_blocks_on_epoch_3() { // Check that we have the expected burn blocks // We expect to have around the blocks 220-230 and 234 onwards, with a gap of 3 blocks for the flash blocks let bhh = u64::from(tip.burn_header_height); + + // Get the Epoch 3.0 activation height (in terms of Bitcoin block height) + let epochs = naka_conf.burnchain.epochs.clone().unwrap(); + let epoch_3 = &epochs[StacksEpoch::find_epoch_by_id(&epochs, StacksEpochId::Epoch30).unwrap()]; + let epoch_3_start_height = epoch_3.start_height; + // Find the gap in burn blocks let mut gap_start = 0; let mut gap_end = 0; @@ -1925,11 +1931,21 @@ fn simple_neon_integration_with_flash_blocks_on_epoch_3() { gap_end ); + // Verify that the gap includes the Epoch 3.0 activation height + assert!( + gap_start <= epoch_3_start_height && epoch_3_start_height <= gap_end, + "Expected the gap ({}..={}) to include the Epoch 3.0 activation height ({})", + gap_start, + gap_end, + epoch_3_start_height + ); + // Verify blocks before and after the gap test_observer::contains_burn_block_range(220..=(gap_start - 1)).unwrap(); test_observer::contains_burn_block_range((gap_end + 1)..=bhh).unwrap(); info!("Verified burn block ranges, including expected gap for flash blocks"); + info!("Confirmed that the gap includes the Epoch 3.0 activation height (Bitcoin block height): {}", epoch_3_start_height); coord_channel .lock()