Skip to content

Commit 2233e5e

Browse files
authored
Merge pull request #5760 from stacks-network/feat/mine-txs-with-tenure-extend
feat: allow other transactions with tenure extends
2 parents 9139fb2 + 1e5b654 commit 2233e5e

File tree

5 files changed

+168
-50
lines changed

5 files changed

+168
-50
lines changed

.github/workflows/bitcoin-tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ jobs:
124124
- tests::signer::v0::signing_in_0th_tenure_of_reward_cycle
125125
- tests::signer::v0::continue_after_tenure_extend
126126
- tests::signer::v0::tenure_extend_after_idle_signers
127+
- tests::signer::v0::tenure_extend_with_other_transactions
127128
- tests::signer::v0::tenure_extend_after_idle_miner
128129
- tests::signer::v0::tenure_extend_after_failed_miner
129130
- tests::signer::v0::tenure_extend_succeeds_after_rejected_attempt

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to the versioning scheme outlined in the [README.md](README.md).
77

8+
## [Unreleased]
9+
10+
### Changed
11+
12+
- Miner will include other transactions in blocks with tenure extend transactions (#5760)
13+
814
## [3.1.0.0.4]
915

1016
### Added

stackslib/src/chainstate/stacks/miner.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2258,7 +2258,13 @@ impl StacksBlockBuilder {
22582258
// nakamoto miner tenure start heuristic:
22592259
// mine an empty block so you can start your tenure quickly!
22602260
if let Some(tx) = initial_txs.first() {
2261-
if matches!(&tx.payload, TransactionPayload::TenureChange(_)) {
2261+
if matches!(
2262+
&tx.payload,
2263+
TransactionPayload::TenureChange(TenureChangePayload {
2264+
cause: TenureChangeCause::BlockFound,
2265+
..
2266+
})
2267+
) {
22622268
info!("Nakamoto miner heuristic: during tenure change blocks, produce a fast short block to begin tenure");
22632269
return Ok((false, tx_events));
22642270
}

testnet/stacks-node/src/tests/nakamoto_integrations.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -247,9 +247,15 @@ pub fn check_nakamoto_empty_block_heuristics() {
247247
continue;
248248
}
249249
let txs = test_observer::parse_transactions(block);
250-
let has_tenure_change = txs
251-
.iter()
252-
.any(|tx| matches!(tx.payload, TransactionPayload::TenureChange(_)));
250+
let has_tenure_change = txs.iter().any(|tx| {
251+
matches!(
252+
tx.payload,
253+
TransactionPayload::TenureChange(TenureChangePayload {
254+
cause: TenureChangeCause::BlockFound,
255+
..
256+
})
257+
)
258+
});
253259
if has_tenure_change {
254260
let only_coinbase_and_tenure_change = txs.iter().all(|tx| {
255261
matches!(
@@ -7200,7 +7206,9 @@ fn continue_tenure_extend() {
72007206
let mut tenure_block_founds = vec![];
72017207
let mut transfer_tx_included = false;
72027208
let mut last_block_had_extend = false;
7203-
for block in test_observer::get_blocks() {
7209+
for pair in test_observer::get_blocks().windows(2) {
7210+
let prev_block = &pair[0];
7211+
let block = &pair[1];
72047212
let mut has_extend = false;
72057213
for tx in block["transactions"].as_array().unwrap() {
72067214
let raw_tx = tx["raw_tx"].as_str().unwrap();
@@ -7221,8 +7229,10 @@ fn continue_tenure_extend() {
72217229
tenure_extends.push(parsed);
72227230
}
72237231
TenureChangeCause::BlockFound => {
7224-
if last_block_had_extend {
7225-
panic!("Expected a Nakamoto block to happen after tenure extend block");
7232+
if last_block_had_extend
7233+
&& prev_block["transactions"].as_array().unwrap().len() <= 1
7234+
{
7235+
panic!("Expected other transactions to happen after tenure extend");
72267236
}
72277237
tenure_block_founds.push(parsed);
72287238
}

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

Lines changed: 138 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2616,6 +2616,51 @@ fn tenure_extend_after_idle_signers() {
26162616
return;
26172617
}
26182618

2619+
tracing_subscriber::registry()
2620+
.with(fmt::layer())
2621+
.with(EnvFilter::from_default_env())
2622+
.init();
2623+
2624+
info!("------------------------- Test Setup -------------------------");
2625+
let num_signers = 5;
2626+
let idle_timeout = Duration::from_secs(30);
2627+
let mut signer_test: SignerTest<SpawnedSigner> = SignerTest::new_with_config_modifications(
2628+
num_signers,
2629+
vec![],
2630+
|config| {
2631+
config.tenure_idle_timeout = idle_timeout;
2632+
},
2633+
|_| {},
2634+
None,
2635+
None,
2636+
);
2637+
2638+
signer_test.boot_to_epoch_3();
2639+
2640+
info!("---- Nakamoto booted, starting test ----");
2641+
signer_test.mine_nakamoto_block(Duration::from_secs(30), true);
2642+
2643+
info!("---- Waiting for a tenure extend ----");
2644+
2645+
// Now, wait for a block with a tenure extend
2646+
wait_for(idle_timeout.as_secs() + 10, || {
2647+
Ok(last_block_contains_tenure_change_tx(
2648+
TenureChangeCause::Extended,
2649+
))
2650+
})
2651+
.expect("Timed out waiting for a block with a tenure extend");
2652+
2653+
signer_test.shutdown();
2654+
}
2655+
2656+
#[test]
2657+
#[ignore]
2658+
/// This test verifies that a miner will include other transactions with a TenureExtend transaction.
2659+
fn tenure_extend_with_other_transactions() {
2660+
if env::var("BITCOIND_TEST") != Ok("1".into()) {
2661+
return;
2662+
}
2663+
26192664
tracing_subscriber::registry()
26202665
.with(fmt::layer())
26212666
.with(EnvFilter::from_default_env())
@@ -2627,7 +2672,7 @@ fn tenure_extend_after_idle_signers() {
26272672
let sender_addr = tests::to_addr(&sender_sk);
26282673
let send_amt = 100;
26292674
let send_fee = 180;
2630-
let _recipient = PrincipalData::from(StacksAddress::burn_address(false));
2675+
let recipient = PrincipalData::from(StacksAddress::burn_address(false));
26312676
let idle_timeout = Duration::from_secs(30);
26322677
let mut signer_test: SignerTest<SpawnedSigner> = SignerTest::new_with_config_modifications(
26332678
num_signers,
@@ -2639,20 +2684,72 @@ fn tenure_extend_after_idle_signers() {
26392684
None,
26402685
None,
26412686
);
2642-
let _http_origin = format!("http://{}", &signer_test.running_nodes.conf.node.rpc_bind);
2687+
let http_origin = format!("http://{}", &signer_test.running_nodes.conf.node.rpc_bind);
26432688

26442689
signer_test.boot_to_epoch_3();
26452690

26462691
info!("---- Nakamoto booted, starting test ----");
26472692
signer_test.mine_nakamoto_block(Duration::from_secs(30), true);
26482693

2649-
info!("---- Waiting for a tenure extend ----");
2694+
info!("Pause miner so it doesn't propose a block before the tenure extend");
2695+
TEST_MINE_STALL.set(true);
2696+
2697+
// Submit a transaction to be included with the tenure extend
2698+
let transfer_tx = make_stacks_transfer(
2699+
&sender_sk,
2700+
0,
2701+
send_fee,
2702+
signer_test.running_nodes.conf.burnchain.chain_id,
2703+
&recipient,
2704+
send_amt,
2705+
);
2706+
let _tx = submit_tx(&http_origin, &transfer_tx);
2707+
2708+
info!("---- Wait for tenure extend timeout ----");
2709+
2710+
sleep_ms(idle_timeout.as_millis() as u64 + 1000);
2711+
2712+
info!("---- Resume miner to propose a block with the tenure extend ----");
2713+
TEST_MINE_STALL.set(false);
26502714

26512715
// Now, wait for a block with a tenure extend
26522716
wait_for(idle_timeout.as_secs() + 10, || {
2653-
Ok(last_block_contains_tenure_change_tx(
2654-
TenureChangeCause::Extended,
2655-
))
2717+
let blocks = test_observer::get_blocks();
2718+
let last_block = &blocks.last().unwrap();
2719+
let transactions = last_block["transactions"].as_array().unwrap();
2720+
let (first_tx, other_txs) = transactions.split_first().unwrap();
2721+
let raw_tx = first_tx["raw_tx"].as_str().unwrap();
2722+
let tx_bytes = hex_bytes(&raw_tx[2..]).unwrap();
2723+
let parsed = StacksTransaction::consensus_deserialize(&mut &tx_bytes[..]).unwrap();
2724+
let found_tenure_extend = match &parsed.payload {
2725+
TransactionPayload::TenureChange(payload)
2726+
if payload.cause == TenureChangeCause::Extended =>
2727+
{
2728+
info!("Found tenure extend transaction: {parsed:?}");
2729+
true
2730+
}
2731+
_ => false,
2732+
};
2733+
if found_tenure_extend {
2734+
let found_transfer = other_txs.iter().any(|tx| {
2735+
let raw_tx = tx["raw_tx"].as_str().unwrap();
2736+
let tx_bytes = hex_bytes(&raw_tx[2..]).unwrap();
2737+
let parsed = StacksTransaction::consensus_deserialize(&mut &tx_bytes[..]).unwrap();
2738+
match &parsed.payload {
2739+
TransactionPayload::TokenTransfer(..) => true,
2740+
_ => false,
2741+
}
2742+
});
2743+
if found_transfer {
2744+
info!("Found transfer transaction");
2745+
Ok(true)
2746+
} else {
2747+
Err("No transfer transaction found together with the tenure extend".to_string())
2748+
}
2749+
} else {
2750+
info!("No tenure change transaction found");
2751+
Ok(false)
2752+
}
26562753
})
26572754
.expect("Timed out waiting for a block with a tenure extend");
26582755

@@ -6438,19 +6535,22 @@ fn miner_recovers_when_broadcast_block_delay_across_tenures_occurs() {
64386535
/// Mine 2 empty burn blocks (simulate fast blocks scenario)
64396536
/// Miner 2 proposes block N+1 with a TenureChangePayload
64406537
/// Signers accept and the stacks tip advances to N+1
6441-
/// Miner 2 proposes block N+2 with a TokenTransfer
6538+
/// Miner 2 proposes block N+2 with a TenureExtend
64426539
/// Signers accept and the stacks tip advances to N+2
6540+
/// Miner 2 proposes block N+3 with a TokenTransfer
6541+
/// Signers accept and the stacks tip advances to N+3
64436542
/// Mine an empty burn block
6444-
/// Miner 2 proposes block N+3 with a TenureExtend
6445-
/// Signers accept and the chain advances to N+3
6446-
/// Miner 1 wins the next tenure and proposes a block N+4 with a TenureChangePayload
6543+
/// Miner 2 proposes block N+4 with a TenureExtend
64476544
/// Signers accept and the chain advances to N+4
6545+
/// Miner 1 wins the next tenure and proposes a block N+5 with a TenureChangePayload
6546+
/// Signers accept and the chain advances to N+5
64486547
/// Asserts:
64496548
/// - Block N+1 contains the TenureChangePayload
6450-
/// - Block N+2 contains the TokenTransfer
6451-
/// - Block N+3 contains the TenureExtend
6452-
/// - Block N+4 contains the TenureChangePayload
6453-
/// - The stacks tip advances to N+4
6549+
/// - Block N+2 contains the TenureExtend
6550+
/// - Block N+3 contains the TokenTransfer
6551+
/// - Block N+4 contains the TenureExtend
6552+
/// - Block N+5 contains the TenureChangePayload
6553+
/// - The stacks tip advances to N+5
64546554
#[test]
64556555
#[ignore]
64566556
fn continue_after_fast_block_no_sortition() {
@@ -6811,7 +6911,7 @@ fn continue_after_fast_block_no_sortition() {
68116911
// Allow signers to respond to proposals again
68126912
TEST_REJECT_ALL_BLOCK_PROPOSAL.set(Vec::new());
68136913

6814-
info!("------------------------- Wait for Miner B's Block N -------------------------";
6914+
info!("------------------------- Wait for Miner B's Block N+1 -------------------------";
68156915
"blocks_processed_before_2" => %blocks_processed_before_2,
68166916
"stacks_height_before" => %stacks_height_before,
68176917
"nmb_old_blocks" => %nmb_old_blocks);
@@ -6826,7 +6926,7 @@ fn continue_after_fast_block_no_sortition() {
68266926

68276927
let blocks_mined1_val = blocks_mined1.load(Ordering::SeqCst);
68286928
let blocks_mined2_val = blocks_mined2.load(Ordering::SeqCst);
6829-
info!("Waiting for Miner B's Block N";
6929+
info!("Waiting for Miner B's Block N+1";
68306930
"blocks_mined1_val" => %blocks_mined1_val,
68316931
"blocks_mined2_val" => %blocks_mined2_val,
68326932
"stacks_height" => %stacks_height,
@@ -6841,11 +6941,11 @@ fn continue_after_fast_block_no_sortition() {
68416941
.expect("Timed out waiting for block to be mined and processed");
68426942

68436943
info!(
6844-
"------------------------- Verify Tenure Change Tx in Miner B's Block N -------------------------"
6944+
"------------------------- Verify Tenure Change Tx in Miner B's Block N+1 -------------------------"
68456945
);
68466946
verify_last_block_contains_tenure_change_tx(TenureChangeCause::BlockFound);
68476947

6848-
info!("------------------------- Wait for Miner B's Block N+1 -------------------------");
6948+
info!("------------------------- Wait for Miner B's Block N+2 -------------------------");
68496949

68506950
let nmb_old_blocks = test_observer::get_blocks().len();
68516951
let blocks_processed_before_2 = blocks_mined2.load(Ordering::SeqCst);
@@ -6855,18 +6955,7 @@ fn continue_after_fast_block_no_sortition() {
68556955
.expect("Failed to get peer info")
68566956
.stacks_tip_height;
68576957

6858-
// submit a tx so that the miner will mine an extra block
6859-
let transfer_tx = make_stacks_transfer(
6860-
&sender_sk,
6861-
sender_nonce,
6862-
send_fee,
6863-
signer_test.running_nodes.conf.burnchain.chain_id,
6864-
&recipient,
6865-
send_amt,
6866-
);
6867-
submit_tx(&http_origin, &transfer_tx);
6868-
6869-
// wait for the tenure-extend block to be processed
6958+
// wait for the transfer block to be processed
68706959
wait_for(30, || {
68716960
let stacks_height = signer_test
68726961
.stacks_client
@@ -6881,8 +6970,12 @@ fn continue_after_fast_block_no_sortition() {
68816970
})
68826971
.expect("Timed out waiting for block to be mined and processed");
68836972

6973+
info!("------------------------- Verify Miner B's Block N+2 -------------------------");
6974+
68846975
verify_last_block_contains_tenure_change_tx(TenureChangeCause::Extended);
68856976

6977+
info!("------------------------- Wait for Miner B's Block N+3 -------------------------");
6978+
68866979
let nmb_old_blocks = test_observer::get_blocks().len();
68876980
let blocks_processed_before_2 = blocks_mined2.load(Ordering::SeqCst);
68886981
let stacks_height_before = signer_test
@@ -6891,22 +6984,24 @@ fn continue_after_fast_block_no_sortition() {
68916984
.expect("Failed to get peer info")
68926985
.stacks_tip_height;
68936986

6894-
// wait for the new block with the STX transfer to be processed
6987+
// submit a tx so that the miner will mine an extra block
6988+
let transfer_tx = make_stacks_transfer(
6989+
&sender_sk,
6990+
sender_nonce,
6991+
send_fee,
6992+
signer_test.running_nodes.conf.burnchain.chain_id,
6993+
&recipient,
6994+
send_amt,
6995+
);
6996+
submit_tx(&http_origin, &transfer_tx);
6997+
6998+
// wait for the transfer block to be processed
68956999
wait_for(30, || {
68967000
let stacks_height = signer_test
68977001
.stacks_client
68987002
.get_peer_info()
68997003
.expect("Failed to get peer info")
69007004
.stacks_tip_height;
6901-
6902-
let blocks_mined1_val = blocks_mined1.load(Ordering::SeqCst);
6903-
let blocks_mined2_val = blocks_mined2.load(Ordering::SeqCst);
6904-
info!("Waiting for Miner B's Block N";
6905-
"blocks_mined1_val" => %blocks_mined1_val,
6906-
"blocks_mined2_val" => %blocks_mined2_val,
6907-
"stacks_height" => %stacks_height,
6908-
"observed_blocks" => %test_observer::get_blocks().len());
6909-
69107005
Ok(
69117006
blocks_mined2.load(Ordering::SeqCst) > blocks_processed_before_2
69127007
&& stacks_height > stacks_height_before
@@ -6915,7 +7010,7 @@ fn continue_after_fast_block_no_sortition() {
69157010
})
69167011
.expect("Timed out waiting for block to be mined and processed");
69177012

6918-
info!("------------------------- Verify Miner B's Block N+1 -------------------------");
7013+
info!("------------------------- Verify Miner B's Block N+3 -------------------------");
69197014

69207015
verify_last_block_contains_transfer_tx();
69217016

@@ -6932,7 +7027,7 @@ fn continue_after_fast_block_no_sortition() {
69327027
.unwrap();
69337028
btc_blocks_mined += 1;
69347029

6935-
info!("------------------------- Verify Miner B's Issues a Tenure Change Extend in Block N+2 -------------------------");
7030+
info!("------------------------- Verify Miner B's Issues a Tenure Change Extend in Block N+4 -------------------------");
69367031
verify_last_block_contains_tenure_change_tx(TenureChangeCause::Extended);
69377032

69387033
info!("------------------------- Unpause Miner A's Block Commits -------------------------");
@@ -6967,7 +7062,7 @@ fn continue_after_fast_block_no_sortition() {
69677062
assert!(tip.sortition);
69687063
assert_eq!(tip.miner_pk_hash.unwrap(), mining_pkh_1);
69697064

6970-
info!("------------------------- Verify Miner A's Issued a Tenure Change in Block N+4 -------------------------");
7065+
info!("------------------------- Verify Miner A's Issued a Tenure Change in Block N+5 -------------------------");
69717066
verify_last_block_contains_tenure_change_tx(TenureChangeCause::BlockFound);
69727067

69737068
info!(

0 commit comments

Comments
 (0)