Skip to content

Commit 1c01db1

Browse files
committed
Add tests for miner's tenure timeout caused tenure extends
Signed-off-by: Jacinta Ferrant <[email protected]>
1 parent 02f3d01 commit 1c01db1

File tree

2 files changed

+172
-3
lines changed

2 files changed

+172
-3
lines changed

.github/workflows/bitcoin-tests.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,9 @@ jobs:
122122
- tests::signer::v0::signer_set_rollover
123123
- tests::signer::v0::signing_in_0th_tenure_of_reward_cycle
124124
- tests::signer::v0::continue_after_tenure_extend
125-
- tests::signer::v0::tenure_extend_after_idle
125+
- tests::signer::v0::tenure_extend_after_idle_signers
126+
- tests::signer::v0::tenure_extend_after_idle_miner
127+
- tests::signer::v0::tenure_extend_succeeds_after_rejected_attempt
126128
- tests::signer::v0::stx_transfers_dont_effect_idle_timeout
127129
- tests::signer::v0::idle_tenure_extend_active_mining
128130
- tests::signer::v0::multiple_miners_with_custom_chain_id

testnet/stacks-node/src/tests/signer/v0.rs

Lines changed: 169 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2580,8 +2580,8 @@ fn signers_broadcast_signed_blocks() {
25802580

25812581
#[test]
25822582
#[ignore]
2583-
/// This test verifies that a miner will produce a TenureExtend transaction after the idle timeout is reached.
2584-
fn tenure_extend_after_idle() {
2583+
/// This test verifies that a miner will produce a TenureExtend transaction after the signers' idle timeout is reached.
2584+
fn tenure_extend_after_idle_signers() {
25852585
if env::var("BITCOIND_TEST") != Ok("1".into()) {
25862586
return;
25872587
}
@@ -2629,6 +2629,173 @@ fn tenure_extend_after_idle() {
26292629
signer_test.shutdown();
26302630
}
26312631

2632+
#[test]
2633+
#[ignore]
2634+
/// This test verifies that a miner will produce a TenureExtend transaction after the miner's idle timeout
2635+
/// even if they do not see the signers' tenure extend timestamp responses.
2636+
fn tenure_extend_after_idle_miner() {
2637+
if env::var("BITCOIND_TEST") != Ok("1".into()) {
2638+
return;
2639+
}
2640+
2641+
tracing_subscriber::registry()
2642+
.with(fmt::layer())
2643+
.with(EnvFilter::from_default_env())
2644+
.init();
2645+
2646+
info!("------------------------- Test Setup -------------------------");
2647+
let num_signers = 5;
2648+
let sender_sk = Secp256k1PrivateKey::new();
2649+
let sender_addr = tests::to_addr(&sender_sk);
2650+
let send_amt = 100;
2651+
let send_fee = 180;
2652+
let _recipient = PrincipalData::from(StacksAddress::burn_address(false));
2653+
let idle_timeout = Duration::from_secs(30);
2654+
let miner_idle_timeout = idle_timeout + Duration::from_secs(10);
2655+
let mut signer_test: SignerTest<SpawnedSigner> = SignerTest::new_with_config_modifications(
2656+
num_signers,
2657+
vec![(sender_addr, send_amt + send_fee)],
2658+
|config| {
2659+
config.tenure_idle_timeout = idle_timeout;
2660+
},
2661+
|config| {
2662+
config.miner.tenure_timeout = miner_idle_timeout;
2663+
},
2664+
None,
2665+
None,
2666+
);
2667+
let _http_origin = format!("http://{}", &signer_test.running_nodes.conf.node.rpc_bind);
2668+
2669+
signer_test.boot_to_epoch_3();
2670+
2671+
info!("---- Nakamoto booted, starting test ----");
2672+
signer_test.mine_nakamoto_block(Duration::from_secs(30), true);
2673+
2674+
info!("---- Start a new tenure but ignore block signatures so no timestamps are recorded ----");
2675+
let tip_height_before = get_chain_info(&signer_test.running_nodes.conf).stacks_tip_height;
2676+
TEST_IGNORE_SIGNERS.set(true);
2677+
next_block_and(
2678+
&mut signer_test.running_nodes.btc_regtest_controller,
2679+
30,
2680+
|| {
2681+
let tip_height = get_chain_info(&signer_test.running_nodes.conf).stacks_tip_height;
2682+
Ok(tip_height > tip_height_before)
2683+
},
2684+
)
2685+
.expect("Failed to mine the tenure change block");
2686+
2687+
// Now, wait for a block with a tenure change due to the new block
2688+
wait_for(30, || {
2689+
Ok(last_block_contains_tenure_change_tx(
2690+
TenureChangeCause::BlockFound,
2691+
))
2692+
})
2693+
.expect("Timed out waiting for a block with a tenure change");
2694+
2695+
info!("---- Waiting for a tenure extend ----");
2696+
2697+
TEST_IGNORE_SIGNERS.set(false);
2698+
// Now, wait for a block with a tenure extend
2699+
wait_for(miner_idle_timeout.as_secs() + 20, || {
2700+
Ok(last_block_contains_tenure_change_tx(
2701+
TenureChangeCause::Extended,
2702+
))
2703+
})
2704+
.expect("Timed out waiting for a block with a tenure extend");
2705+
signer_test.shutdown();
2706+
}
2707+
2708+
#[test]
2709+
#[ignore]
2710+
/// This test verifies that a miner that attempts to produce a tenure extend too early will be rejected by the signers,
2711+
/// but will eventually succeed after the signers' idle timeout has passed.
2712+
fn tenure_extend_succeeds_after_rejected_attempt() {
2713+
if env::var("BITCOIND_TEST") != Ok("1".into()) {
2714+
return;
2715+
}
2716+
2717+
tracing_subscriber::registry()
2718+
.with(fmt::layer())
2719+
.with(EnvFilter::from_default_env())
2720+
.init();
2721+
2722+
info!("------------------------- Test Setup -------------------------");
2723+
let num_signers = 5;
2724+
let sender_sk = Secp256k1PrivateKey::new();
2725+
let sender_addr = tests::to_addr(&sender_sk);
2726+
let send_amt = 100;
2727+
let send_fee = 180;
2728+
let _recipient = PrincipalData::from(StacksAddress::burn_address(false));
2729+
let idle_timeout = Duration::from_secs(30);
2730+
let miner_idle_timeout = Duration::from_secs(20);
2731+
let mut signer_test: SignerTest<SpawnedSigner> = SignerTest::new_with_config_modifications(
2732+
num_signers,
2733+
vec![(sender_addr, send_amt + send_fee)],
2734+
|config| {
2735+
config.tenure_idle_timeout = idle_timeout;
2736+
},
2737+
|config| {
2738+
config.miner.tenure_timeout = miner_idle_timeout;
2739+
},
2740+
None,
2741+
None,
2742+
);
2743+
let _http_origin = format!("http://{}", &signer_test.running_nodes.conf.node.rpc_bind);
2744+
2745+
signer_test.boot_to_epoch_3();
2746+
2747+
info!("---- Nakamoto booted, starting test ----");
2748+
signer_test.mine_nakamoto_block(Duration::from_secs(30), true);
2749+
2750+
info!("---- Waiting for a rejected tenure extend ----");
2751+
// Now, wait for a block with a tenure extend proposal from the miner, but ensure it is rejected.
2752+
wait_for(30, || {
2753+
let block = test_observer::get_stackerdb_chunks()
2754+
.into_iter()
2755+
.flat_map(|chunk| chunk.modified_slots)
2756+
.find_map(|chunk| {
2757+
let message = SignerMessage::consensus_deserialize(&mut chunk.data.as_slice())
2758+
.expect("Failed to deserialize SignerMessage");
2759+
if let SignerMessage::BlockProposal(proposal) = message {
2760+
if proposal.block.get_tenure_tx_payload().unwrap().cause
2761+
== TenureChangeCause::Extended
2762+
{
2763+
return Some(proposal.block);
2764+
}
2765+
}
2766+
None
2767+
});
2768+
let Some(block) = &block else {
2769+
return Ok(false);
2770+
};
2771+
let signatures = test_observer::get_stackerdb_chunks()
2772+
.into_iter()
2773+
.flat_map(|chunk| chunk.modified_slots)
2774+
.filter_map(|chunk| {
2775+
let message = SignerMessage::consensus_deserialize(&mut chunk.data.as_slice())
2776+
.expect("Failed to deserialize SignerMessage");
2777+
if let SignerMessage::BlockResponse(BlockResponse::Rejected(rejected)) = message {
2778+
if block.header.signer_signature_hash() == rejected.signer_signature_hash {
2779+
return Some(rejected.signature);
2780+
}
2781+
}
2782+
None
2783+
})
2784+
.collect::<Vec<_>>();
2785+
Ok(signatures.len() >= num_signers * 7 / 10)
2786+
})
2787+
.expect("Test timed out while waiting for a rejected tenure extend");
2788+
2789+
info!("---- Waiting for an accepted tenure extend ----");
2790+
wait_for(idle_timeout.as_secs() + 10, || {
2791+
Ok(last_block_contains_tenure_change_tx(
2792+
TenureChangeCause::Extended,
2793+
))
2794+
})
2795+
.expect("Test timed out while waiting for an accepted tenure extend");
2796+
signer_test.shutdown();
2797+
}
2798+
26322799
#[test]
26332800
#[ignore]
26342801
/// Verify that Nakamoto blocks that don't modify the tenure's execution cost

0 commit comments

Comments
 (0)