Skip to content

Commit 7e70420

Browse files
committed
Merge branch 'develop' of https://github.com/stacks-network/stacks-core into fix/fast-block-test
2 parents 6ec21f5 + a055de5 commit 7e70420

File tree

8 files changed

+229
-56
lines changed

8 files changed

+229
-56
lines changed

.github/workflows/bitcoin-tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ jobs:
123123
- tests::signer::v0::signing_in_0th_tenure_of_reward_cycle
124124
- tests::signer::v0::continue_after_tenure_extend
125125
- tests::signer::v0::multiple_miners_with_custom_chain_id
126+
- tests::signer::v0::block_commit_delay
126127
- tests::signer::v0::continue_after_fast_block_no_sortition
127128
- tests::nakamoto_integrations::burn_ops_integration_test
128129
- tests::nakamoto_integrations::check_block_heights

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
1010
### Changed
1111
- Add index for StacksBlockId to nakamoto block headers table (improves node performance)
1212
- Remove the panic for reporting DB deadlocks (just error and continue waiting)
13+
- Add `block_commit_delay_ms` to the config file to control the time to wait after seeing a new burn block, before submitting a block commit, to allow time for the first Nakamoto block of the new tenure to be mined, allowing this miner to avoid the need to RBF the block commit.
1314

1415
## [3.0.0.0.1]
1516

docs/mining.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,26 @@ nakamoto_attempt_time_ms = 20000
1919
[burnchain]
2020
# Maximum amount (in sats) of "burn commitment" to broadcast for the next block's leader election
2121
burn_fee_cap = 20000
22-
# Amount (in sats) per byte - Used to calculate the transaction fees
23-
satoshis_per_byte = 25
24-
# Amount of sats to add when RBF'ing bitcoin tx (default: 5)
22+
# Amount in sats per byte used to calculate the Bitcoin transaction fee (default: 50)
23+
satoshis_per_byte = 50
24+
# Amount of sats per byte to add when RBF'ing a Bitcoin tx (default: 5)
2525
rbf_fee_increment = 5
26-
# Maximum percentage to RBF bitcoin tx (default: 150% of satsv/B)
26+
# Maximum percentage of satoshis_per_byte to allow in RBF fee (default: 150)
2727
max_rbf = 150
2828
```
2929

30+
NOTE: Ensuring that your miner can successfully use RBF (Replace-by-Fee) is
31+
critical for reliable block production. If a miner fails to replace an outdated
32+
block commit with a higher-fee transaction, it risks committing to an incorrect
33+
tenure. This would prevent the miner from producing valid blocks during its
34+
tenure, as it would be building on an invalid chain tip, causing the signers to
35+
reject its blocks.
36+
37+
To avoid this, configure satoshis_per_byte, rbf_fee_increment, and max_rbf to
38+
allow for at least three fee increments within the max_rbf limit. This helps
39+
ensure that your miner can adjust its fees sufficiently to stay on the canonical
40+
chain.
41+
3042
You can verify that your node is operating as a miner by checking its log output
3143
to verify that it was able to find its Bitcoin UTXOs:
3244

testnet/stacks-node/src/config.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ const INV_REWARD_CYCLES_TESTNET: u64 = 6;
8989
const DEFAULT_MIN_TIME_BETWEEN_BLOCKS_MS: u64 = 1_000;
9090
const DEFAULT_FIRST_REJECTION_PAUSE_MS: u64 = 5_000;
9191
const DEFAULT_SUBSEQUENT_REJECTION_PAUSE_MS: u64 = 10_000;
92+
const DEFAULT_BLOCK_COMMIT_DELAY_MS: u64 = 20_000;
9293

9394
#[derive(Clone, Deserialize, Default, Debug)]
9495
#[serde(deny_unknown_fields)]
@@ -2167,6 +2168,8 @@ pub struct MinerConfig {
21672168
pub first_rejection_pause_ms: u64,
21682169
/// Time in milliseconds to pause after receiving subsequent threshold rejections, before proposing a new block.
21692170
pub subsequent_rejection_pause_ms: u64,
2171+
/// Duration to wait for a Nakamoto block after seeing a burnchain block before submitting a block commit.
2172+
pub block_commit_delay: Duration,
21702173
}
21712174

21722175
impl Default for MinerConfig {
@@ -2199,6 +2202,7 @@ impl Default for MinerConfig {
21992202
min_time_between_blocks_ms: DEFAULT_MIN_TIME_BETWEEN_BLOCKS_MS,
22002203
first_rejection_pause_ms: DEFAULT_FIRST_REJECTION_PAUSE_MS,
22012204
subsequent_rejection_pause_ms: DEFAULT_SUBSEQUENT_REJECTION_PAUSE_MS,
2205+
block_commit_delay: Duration::from_millis(DEFAULT_BLOCK_COMMIT_DELAY_MS),
22022206
}
22032207
}
22042208
}
@@ -2568,6 +2572,7 @@ pub struct MinerConfigFile {
25682572
pub min_time_between_blocks_ms: Option<u64>,
25692573
pub first_rejection_pause_ms: Option<u64>,
25702574
pub subsequent_rejection_pause_ms: Option<u64>,
2575+
pub block_commit_delay_ms: Option<u64>,
25712576
}
25722577

25732578
impl MinerConfigFile {
@@ -2683,6 +2688,7 @@ impl MinerConfigFile {
26832688
}).unwrap_or(miner_default_config.min_time_between_blocks_ms),
26842689
first_rejection_pause_ms: self.first_rejection_pause_ms.unwrap_or(miner_default_config.first_rejection_pause_ms),
26852690
subsequent_rejection_pause_ms: self.subsequent_rejection_pause_ms.unwrap_or(miner_default_config.subsequent_rejection_pause_ms),
2691+
block_commit_delay: self.block_commit_delay_ms.map(Duration::from_millis).unwrap_or(miner_default_config.block_commit_delay),
26862692
})
26872693
}
26882694
}

testnet/stacks-node/src/nakamoto_node/relayer.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,8 @@ pub struct RelayerThread {
236236
/// Information about the last-sent block commit, and the relayer's view of the chain at the
237237
/// time it was sent.
238238
last_committed: Option<LastCommit>,
239+
/// Timeout for waiting for the first block in a tenure before submitting a block commit
240+
new_tenure_timeout: Option<Instant>,
239241
}
240242

241243
impl RelayerThread {
@@ -293,6 +295,7 @@ impl RelayerThread {
293295
is_miner,
294296
next_initiative: Instant::now() + Duration::from_millis(next_initiative_delay),
295297
last_committed: None,
298+
new_tenure_timeout: None,
296299
}
297300
}
298301

@@ -1191,6 +1194,32 @@ impl RelayerThread {
11911194
return None;
11921195
}
11931196

1197+
if !highest_tenure_changed {
1198+
debug!("Relayer: burnchain view changed, but highest tenure did not");
1199+
// The burnchain view changed, but the highest tenure did not, so
1200+
// wait a bit for the first block in the new tenure to arrive. This
1201+
// is to avoid submitting a block commit that will be immediately
1202+
// RBFed when the first block arrives.
1203+
if let Some(new_tenure_timeout) = self.new_tenure_timeout {
1204+
debug!(
1205+
"Relayer: {}s elapsed since burn block arrival",
1206+
new_tenure_timeout.elapsed().as_secs(),
1207+
);
1208+
if new_tenure_timeout.elapsed() < self.config.miner.block_commit_delay {
1209+
return None;
1210+
}
1211+
} else {
1212+
info!(
1213+
"Relayer: starting new tenure timeout for {}s",
1214+
self.config.miner.block_commit_delay.as_secs()
1215+
);
1216+
let timeout = Instant::now() + self.config.miner.block_commit_delay;
1217+
self.new_tenure_timeout = Some(Instant::now());
1218+
self.next_initiative = timeout;
1219+
return None;
1220+
}
1221+
}
1222+
11941223
// burnchain view or highest-tenure view changed, so we need to send (or RBF) a commit
11951224
Some(RelayerDirective::IssueBlockCommit(
11961225
stacks_tip_ch,

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3721,7 +3721,7 @@ fn follower_bootup_across_multiple_cycles() {
37213721

37223722
let (mut naka_conf, _miner_account) = naka_neon_integration_conf(None);
37233723
naka_conf.miner.wait_on_interim_blocks = Duration::from_secs(1);
3724-
naka_conf.node.pox_sync_sample_secs = 30;
3724+
naka_conf.node.pox_sync_sample_secs = 180;
37253725
naka_conf.burnchain.max_rbf = 10_000_000;
37263726

37273727
let sender_sk = Secp256k1PrivateKey::new();
@@ -4866,6 +4866,7 @@ fn forked_tenure_is_ignored() {
48664866

48674867
let (mut naka_conf, _miner_account) = naka_neon_integration_conf(None);
48684868
naka_conf.miner.wait_on_interim_blocks = Duration::from_secs(10);
4869+
naka_conf.miner.block_commit_delay = Duration::from_secs(0);
48694870
let sender_sk = Secp256k1PrivateKey::new();
48704871
// setup sender + recipient for a test stx transfer
48714872
let sender_addr = tests::to_addr(&sender_sk);

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,7 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
557557
signer_signature_hash: &Sha512Trunc256Sum,
558558
expected_signers: &[StacksPublicKey],
559559
) -> Result<(), String> {
560-
// Make sure that ALL signers accepted the block proposal
560+
// Make sure that at least 70% of signers accepted the block proposal
561561
wait_for(timeout_secs, || {
562562
let signatures = test_observer::get_stackerdb_chunks()
563563
.into_iter()
@@ -585,7 +585,7 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
585585
}
586586
})
587587
.collect::<HashSet<_>>();
588-
Ok(signatures.len() == expected_signers.len())
588+
Ok(signatures.len() > expected_signers.len() * 7 / 10)
589589
})
590590
}
591591

0 commit comments

Comments
 (0)