@@ -13549,3 +13549,217 @@ fn verify_mempool_caches() {
13549
13549
13550
13550
signer_test.shutdown();
13551
13551
}
13552
+
13553
+ #[test]
13554
+ #[ignore]
13555
+ /// This test checks the behavior of `burn-block-height` within a normal
13556
+ /// Nakamoto block and within a tenure-extend block.
13557
+ fn burn_block_height_behavior() {
13558
+ if env::var("BITCOIND_TEST") != Ok("1".into()) {
13559
+ return;
13560
+ }
13561
+
13562
+ tracing_subscriber::registry()
13563
+ .with(fmt::layer())
13564
+ .with(EnvFilter::from_default_env())
13565
+ .init();
13566
+
13567
+ info!("------------------------- Test Setup -------------------------");
13568
+ let num_signers = 5;
13569
+ let sender_sk = Secp256k1PrivateKey::random();
13570
+ let sender_addr = tests::to_addr(&sender_sk);
13571
+ let send_amt = 100;
13572
+ let send_fee = 180;
13573
+ let deployer_sk = Secp256k1PrivateKey::random();
13574
+ let deployer_addr = tests::to_addr(&deployer_sk);
13575
+ let tx_fee = 10000;
13576
+ let deploy_fee = 200000;
13577
+ let block_proposal_timeout = Duration::from_secs(20);
13578
+ let mut signer_test: SignerTest<SpawnedSigner> = SignerTest::new_with_config_modifications(
13579
+ num_signers,
13580
+ vec![
13581
+ (sender_addr, send_amt + send_fee),
13582
+ (deployer_addr, deploy_fee + tx_fee * 3),
13583
+ ],
13584
+ |config| {
13585
+ // make the duration long enough that the miner will be marked as malicious
13586
+ config.block_proposal_timeout = block_proposal_timeout;
13587
+ },
13588
+ |_| {},
13589
+ None,
13590
+ None,
13591
+ );
13592
+ let http_origin = format!("http://{}", &signer_test.running_nodes.conf.node.rpc_bind);
13593
+
13594
+ signer_test.boot_to_epoch_3();
13595
+
13596
+ let Counters {
13597
+ naka_skip_commit_op: skip_commit_op,
13598
+ ..
13599
+ } = signer_test.running_nodes.counters.clone();
13600
+
13601
+ info!("------------------------- Test Mine Regular Tenure A -------------------------");
13602
+
13603
+ let contract_src = "(define-public (foo) (ok burn-block-height))";
13604
+
13605
+ // First, lets deploy the contract
13606
+ let mut deployer_nonce = 0;
13607
+ let contract_tx = make_contract_publish(
13608
+ &deployer_sk,
13609
+ deployer_nonce,
13610
+ deploy_fee,
13611
+ signer_test.running_nodes.conf.burnchain.chain_id,
13612
+ "foo",
13613
+ &contract_src,
13614
+ );
13615
+ submit_tx(&http_origin, &contract_tx);
13616
+ deployer_nonce += 1;
13617
+
13618
+ // Wait for this transaction to be mined in a block
13619
+ info!("----- Submitted deploy txs, waiting for block -----");
13620
+ wait_for(60, || {
13621
+ Ok(get_account(&http_origin, &deployer_addr).nonce == deployer_nonce)
13622
+ })
13623
+ .unwrap();
13624
+
13625
+ // Stall block commits, so the next block will have no sortition
13626
+ skip_commit_op.set(true);
13627
+
13628
+ // Mine a regular tenure
13629
+ let info_before = signer_test.get_peer_info();
13630
+ next_block_and(
13631
+ &mut signer_test.running_nodes.btc_regtest_controller,
13632
+ 60,
13633
+ || {
13634
+ let chain_info = get_chain_info(&signer_test.running_nodes.conf);
13635
+ Ok(chain_info.stacks_tip_height > info_before.stacks_tip_height)
13636
+ },
13637
+ )
13638
+ .expect("Timed out waiting for block");
13639
+
13640
+ let info = get_chain_info(&signer_test.running_nodes.conf);
13641
+ let stacks_height_before = info.stacks_tip_height;
13642
+ let burn_height_before = info.burn_block_height;
13643
+
13644
+ info!("------------------------- submit contract call 1 -------------------------");
13645
+ let call_tx = make_contract_call(
13646
+ &deployer_sk,
13647
+ deployer_nonce,
13648
+ tx_fee,
13649
+ signer_test.running_nodes.conf.burnchain.chain_id,
13650
+ &deployer_addr,
13651
+ "foo",
13652
+ "foo",
13653
+ &[],
13654
+ );
13655
+ let txid = submit_tx(&http_origin, &call_tx);
13656
+ deployer_nonce += 1;
13657
+
13658
+ info!("------------------------- wait for the call tx to be mined -------------------------");
13659
+ // Wait for the call tx to be mined in a new Nakamoto block
13660
+ wait_for(60, || {
13661
+ test_observer::get_mined_nakamoto_blocks()
13662
+ .last()
13663
+ .and_then(|block| {
13664
+ block.tx_events.iter().find_map(|tx| match tx {
13665
+ TransactionEvent::Success(tx) if tx.txid.to_string() == txid => {
13666
+ let result = tx
13667
+ .result
13668
+ .clone()
13669
+ .expect_result_ok()
13670
+ .ok()?
13671
+ .expect_u128()
13672
+ .ok()?;
13673
+ Some(result)
13674
+ }
13675
+ _ => None,
13676
+ })
13677
+ })
13678
+ .map_or(Ok(false), |result_height| {
13679
+ assert_eq!(result_height, burn_height_before as u128);
13680
+ Ok(true)
13681
+ })
13682
+ })
13683
+ .expect("Timed out waiting for call tx to be mined");
13684
+
13685
+ info!("------------------------- Wait for the block to be processed -------------------------");
13686
+ // Wait for the block to be processed
13687
+ wait_for(60, || {
13688
+ let info = get_chain_info(&signer_test.running_nodes.conf);
13689
+ Ok(info.stacks_tip_height > stacks_height_before)
13690
+ })
13691
+ .expect("Timed out waiting for block to be processed");
13692
+
13693
+ // Stall mining, so that the next call will get included in the tenure extend block
13694
+ TEST_MINE_STALL.set(true);
13695
+
13696
+ // Wait to ensure the miner reaches the stalled state
13697
+ // This is necessary because it's possible that the miner will mine the
13698
+ // following transaction before reaching the stall state, causing the test
13699
+ // to be flaky.
13700
+ sleep_ms(5000);
13701
+
13702
+ info!("------------------------- submit contract call 2 -------------------------");
13703
+ let call_tx = make_contract_call(
13704
+ &deployer_sk,
13705
+ deployer_nonce,
13706
+ tx_fee,
13707
+ signer_test.running_nodes.conf.burnchain.chain_id,
13708
+ &deployer_addr,
13709
+ "foo",
13710
+ "foo",
13711
+ &[],
13712
+ );
13713
+ let txid = submit_tx(&http_origin, &call_tx);
13714
+
13715
+ info!(
13716
+ "------------------------- mine bitcoin block with no sortition -------------------------"
13717
+ );
13718
+ let info = get_chain_info(&signer_test.running_nodes.conf);
13719
+ let stacks_height_before = info.stacks_tip_height;
13720
+ let burn_height_before = info.burn_block_height;
13721
+
13722
+ signer_test
13723
+ .running_nodes
13724
+ .btc_regtest_controller
13725
+ .build_next_block(1);
13726
+
13727
+ wait_for(60, || {
13728
+ let info = get_chain_info(&signer_test.running_nodes.conf);
13729
+ Ok(info.burn_block_height == burn_height_before + 1)
13730
+ })
13731
+ .expect("Failed to advance chain tip");
13732
+
13733
+ info!("------------------------- wait for tenure change block -------------------------");
13734
+
13735
+ // Resume mining and wait for the next block to be mined
13736
+ TEST_MINE_STALL.set(false);
13737
+ wait_for_tenure_change_tx(60, TenureChangeCause::Extended, stacks_height_before + 1)
13738
+ .expect("Timed out waiting for tenure extend");
13739
+
13740
+ let blocks = test_observer::get_mined_nakamoto_blocks();
13741
+ let last_block = blocks.last().unwrap();
13742
+ let txs = &last_block.tx_events;
13743
+ assert_eq!(txs.len(), 2, "Expected 2 txs in the tenure extend block");
13744
+ let _tenure_extend_tx = txs.first().unwrap();
13745
+ let call_tx = txs.last().unwrap();
13746
+ match call_tx {
13747
+ TransactionEvent::Success(tx) => {
13748
+ if tx.txid.to_string() == txid {
13749
+ let result_height = tx
13750
+ .result
13751
+ .clone()
13752
+ .expect_result_ok()
13753
+ .unwrap()
13754
+ .expect_u128()
13755
+ .unwrap();
13756
+ assert_eq!(result_height, burn_height_before as u128 + 1);
13757
+ }
13758
+ }
13759
+ _ => {}
13760
+ }
13761
+
13762
+ info!("------------------------- shutdown -------------------------");
13763
+
13764
+ signer_test.shutdown();
13765
+ }
0 commit comments