Skip to content

Commit de3f7c1

Browse files
committed
feat: add empty_mempool_sleep_ms
This defines a time that the miner should sleep after attempting to mine and finding an empty mempool.
1 parent 88a946a commit de3f7c1

File tree

4 files changed

+128
-2
lines changed

4 files changed

+128
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
2020
- When a miner times out waiting for signatures, it will re-propose the same block instead of building a new block ([#5877](https://github.com/stacks-network/stacks-core/pull/5877))
2121
- Improve tenure downloader trace verbosity applying proper logging level depending on the tenure state ("debug" if unconfirmed, "info" otherwise) ([#5871](https://github.com/stacks-network/stacks-core/issues/5871))
2222
- Remove warning log about missing UTXOs when a node is configured as `miner` with `mock_mining` mode enabled ([#5841](https://github.com/stacks-network/stacks-core/issues/5841))
23-
- Deprecated the `wait_on_interim_blocks` option in the miner config file. This option is no longer needed, as the miner will always wait for interim blocks to be processed before mining a new block. To wait extra time in between blocks, use the `min_time_between_blocks_ms` option instead.
23+
- Deprecated the `wait_on_interim_blocks` option in the miner config file. This option is no longer needed, as the miner will always wait for interim blocks to be processed before mining a new block. To wait extra time in between blocks, use the `min_time_between_blocks_ms` option instead. ([#5979](https://github.com/stacks-network/stacks-core/pull/5979))
24+
- Added `empty_mempool_sleep_ms` to the miner config file to control the time to wait in between mining attempts when the mempool is empty. If not set, the default sleep time is 2.5s. ([#5997](https://github.com/stacks-network/stacks-core/pull/5997))
2425

2526
## [3.1.0.0.7]
2627

stackslib/src/config/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ const DEFAULT_TENURE_TIMEOUT_SECS: u64 = 180;
123123
/// Default percentage of block budget that must be used before attempting a
124124
/// time-based tenure extend
125125
const DEFAULT_TENURE_EXTEND_COST_THRESHOLD: u64 = 50;
126+
/// Default number of milliseconds that the miner should sleep between mining
127+
/// attempts when the mempool is empty.
128+
const DEFAULT_EMPTY_MEMPOOL_SLEEP_MS: u64 = 2_500;
126129

127130
static HELIUM_DEFAULT_CONNECTION_OPTIONS: LazyLock<ConnectionOptions> =
128131
LazyLock::new(|| ConnectionOptions {
@@ -2172,6 +2175,9 @@ pub struct MinerConfig {
21722175
/// The minimum time to wait between mining blocks in milliseconds. The value must be greater than or equal to 1000 ms because if a block is mined
21732176
/// within the same second as its parent, it will be rejected by the signers.
21742177
pub min_time_between_blocks_ms: u64,
2178+
/// The amount of time that the miner should sleep in between attempts to
2179+
/// mine a block when the mempool is empty
2180+
pub empty_mempool_sleep_time: Duration,
21752181
/// Time in milliseconds to pause after receiving the first threshold rejection, before proposing a new block.
21762182
pub first_rejection_pause_ms: u64,
21772183
/// Time in milliseconds to pause after receiving subsequent threshold rejections, before proposing a new block.
@@ -2224,6 +2230,7 @@ impl Default for MinerConfig {
22242230
max_reorg_depth: 3,
22252231
pre_nakamoto_mock_signing: false, // Should only default true if mining key is set
22262232
min_time_between_blocks_ms: DEFAULT_MIN_TIME_BETWEEN_BLOCKS_MS,
2233+
empty_mempool_sleep_time: Duration::from_millis(DEFAULT_EMPTY_MEMPOOL_SLEEP_MS),
22272234
first_rejection_pause_ms: DEFAULT_FIRST_REJECTION_PAUSE_MS,
22282235
subsequent_rejection_pause_ms: DEFAULT_SUBSEQUENT_REJECTION_PAUSE_MS,
22292236
block_commit_delay: Duration::from_millis(DEFAULT_BLOCK_COMMIT_DELAY_MS),
@@ -2639,6 +2646,7 @@ pub struct MinerConfigFile {
26392646
pub max_reorg_depth: Option<u64>,
26402647
pub pre_nakamoto_mock_signing: Option<bool>,
26412648
pub min_time_between_blocks_ms: Option<u64>,
2649+
pub empty_mempool_sleep_ms: Option<u64>,
26422650
pub first_rejection_pause_ms: Option<u64>,
26432651
pub subsequent_rejection_pause_ms: Option<u64>,
26442652
pub block_commit_delay_ms: Option<u64>,
@@ -2795,6 +2803,7 @@ impl MinerConfigFile {
27952803
} else {
27962804
ms
27972805
}).unwrap_or(miner_default_config.min_time_between_blocks_ms),
2806+
empty_mempool_sleep_time: self.empty_mempool_sleep_ms.map(Duration::from_millis).unwrap_or(miner_default_config.empty_mempool_sleep_time),
27982807
first_rejection_pause_ms: self.first_rejection_pause_ms.unwrap_or(miner_default_config.first_rejection_pause_ms),
27992808
subsequent_rejection_pause_ms: self.subsequent_rejection_pause_ms.unwrap_or(miner_default_config.subsequent_rejection_pause_ms),
28002809
block_commit_delay: self.block_commit_delay_ms.map(Duration::from_millis).unwrap_or(miner_default_config.block_commit_delay),

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,8 +568,13 @@ impl BlockMinerThread {
568568
continue;
569569
}
570570
Err(NakamotoNodeError::MiningFailure(ChainstateError::NoTransactionsToMine)) => {
571-
debug!("Miner did not find any transactions to mine");
571+
debug!(
572+
"Miner did not find any transactions to mine, sleeping for {:?}",
573+
self.config.miner.empty_mempool_sleep_time
574+
);
572575
self.reset_nonce_cache = false;
576+
577+
thread::sleep(self.config.miner.empty_mempool_sleep_time);
573578
break None;
574579
}
575580
Err(e) => {

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

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12183,3 +12183,114 @@ fn v3_transaction_api_endpoint() {
1218312183

1218412184
run_loop_thread.join().unwrap();
1218512185
}
12186+
12187+
#[test]
12188+
#[ignore]
12189+
fn empty_mempool_sleep_ms() {
12190+
if env::var("BITCOIND_TEST") != Ok("1".into()) {
12191+
return;
12192+
}
12193+
12194+
let (mut conf, _miner_account) = naka_neon_integration_conf(None);
12195+
let password = "12345".to_string();
12196+
conf.connection_options.auth_token = Some(password.clone());
12197+
let stacker_sk = setup_stacker(&mut conf);
12198+
let signer_sk = Secp256k1PrivateKey::random();
12199+
let signer_addr = tests::to_addr(&signer_sk);
12200+
let sender_sk = Secp256k1PrivateKey::random();
12201+
// setup sender + recipient for a stx transfer
12202+
let sender_addr = tests::to_addr(&sender_sk);
12203+
let send_amt = 100;
12204+
let send_fee = 180;
12205+
conf.add_initial_balance(
12206+
PrincipalData::from(sender_addr).to_string(),
12207+
send_amt + send_fee,
12208+
);
12209+
conf.add_initial_balance(PrincipalData::from(signer_addr).to_string(), 100000);
12210+
12211+
// Set the empty mempool sleep time to something long enough that we can
12212+
// see the effect in the test.
12213+
conf.miner.empty_mempool_sleep_time = Duration::from_secs(30);
12214+
12215+
let mut btcd_controller = BitcoinCoreController::new(conf.clone());
12216+
btcd_controller
12217+
.start_bitcoind()
12218+
.expect("Failed starting bitcoind");
12219+
let mut btc_regtest_controller = BitcoinRegtestController::new(conf.clone(), None);
12220+
btc_regtest_controller.bootstrap_chain(201);
12221+
12222+
let mut run_loop = boot_nakamoto::BootRunLoop::new(conf.clone()).unwrap();
12223+
let run_loop_stopper = run_loop.get_termination_switch();
12224+
let Counters {
12225+
blocks_processed,
12226+
naka_submitted_commits: commits_submitted,
12227+
naka_proposed_blocks,
12228+
..
12229+
} = run_loop.counters();
12230+
let counters = run_loop.counters();
12231+
12232+
let coord_channel = run_loop.coordinator_channels();
12233+
let http_origin = format!("http://{}", &conf.node.rpc_bind);
12234+
12235+
let run_loop_thread = thread::spawn(move || run_loop.start(None, 0));
12236+
let mut signers = TestSigners::new(vec![signer_sk]);
12237+
wait_for_runloop(&blocks_processed);
12238+
boot_to_epoch_3(
12239+
&conf,
12240+
&blocks_processed,
12241+
&[stacker_sk],
12242+
&[signer_sk],
12243+
&mut Some(&mut signers),
12244+
&mut btc_regtest_controller,
12245+
);
12246+
12247+
info!("------------------------- Reached Epoch 3.0 -------------------------");
12248+
12249+
blind_signer(&conf, &signers, &counters);
12250+
12251+
wait_for_first_naka_block_commit(60, &commits_submitted);
12252+
12253+
next_block_and_wait(&mut btc_regtest_controller, &blocks_processed);
12254+
12255+
// Sleep for 5 seconds to ensure that the miner tries to mine and sees an
12256+
// empty mempool.
12257+
thread::sleep(Duration::from_secs(5));
12258+
12259+
info!("------------------------- Submit a transaction -------------------------");
12260+
let proposals_before = naka_proposed_blocks.load(Ordering::SeqCst);
12261+
12262+
let transfer_tx = make_stacks_transfer(
12263+
&sender_sk,
12264+
0,
12265+
send_fee,
12266+
conf.burnchain.chain_id,
12267+
&signer_addr.into(),
12268+
send_amt,
12269+
);
12270+
submit_tx(&http_origin, &transfer_tx);
12271+
12272+
// The miner should have slept for 30 seconds after seeing an empty mempool
12273+
// before trying to mine again. Let's check that there was at least 10s
12274+
// before the next block proposal.
12275+
wait_for(10, || {
12276+
let proposals_after = naka_proposed_blocks.load(Ordering::SeqCst);
12277+
Ok(proposals_after > proposals_before)
12278+
})
12279+
.expect_err("Expected to wait for 30 seconds before mining a block");
12280+
12281+
// Wait for the transaction to be mined
12282+
wait_for(60, || {
12283+
let account = get_account(&http_origin, &sender_addr);
12284+
Ok(account.nonce == 1)
12285+
})
12286+
.expect("Timed out waiting for transaction to be mined after delay");
12287+
12288+
coord_channel
12289+
.lock()
12290+
.expect("Mutex poisoned")
12291+
.stop_chains_coordinator();
12292+
12293+
run_loop_stopper.store(false, Ordering::SeqCst);
12294+
12295+
run_loop_thread.join().unwrap();
12296+
}

0 commit comments

Comments
 (0)