Skip to content

Commit ca7a793

Browse files
authored
Merge branch 'develop' into chore/document-burnchain-config
2 parents 07a2e32 + 50e304a commit ca7a793

File tree

9 files changed

+199
-11
lines changed

9 files changed

+199
-11
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ jobs:
9494
needs:
9595
- rustfmt
9696
- check-release
97+
secrets: inherit
9798
uses: ./.github/workflows/github-release.yml
9899
with:
99100
node_tag: ${{ needs.check-release.outputs.node_tag }}

.github/workflows/core-build-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
- name: Build the binaries
2525
id: build
2626
run: |
27-
cargo build
27+
cargo build --bin stacks-inspect
2828
- name: Dump constants JSON
2929
id: consts-dump
3030
run: cargo run --bin stacks-inspect -- dump-consts | tee out.json

.github/workflows/github-release.yml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ concurrency:
3838
run-name: ${{ inputs.node_tag || inputs.signer_tag }}
3939

4040
jobs:
41-
## This job's sole purpose is trigger a secondary approval outside of the matrix jobs below.
41+
## This job's sole purpose is trigger a secondary approval outside of the matrix jobs below.
4242
## - If this job isn't approved to run, then the subsequent jobs will also not run - for this reason, we always exit 0
43-
## - `andon-cord` requires the repo environment "Build Release", which will trigger a secondary approval step before running this workflow.
43+
## - `andon-cord` requires the repo environment "Build Release", which will trigger a secondary approval step before running this workflow.
4444
andon-cord:
4545
if: |
4646
inputs.node_tag != '' ||
@@ -65,6 +65,9 @@ jobs:
6565
runs-on: ubuntu-latest
6666
needs:
6767
- andon-cord
68+
permissions:
69+
id-token: write
70+
attestations: write
6871
strategy:
6972
## Run a maximum of 10 builds concurrently, using the matrix defined in inputs.arch
7073
max-parallel: 10
@@ -135,6 +138,10 @@ jobs:
135138
inputs.signer_tag != ''
136139
name: Docker Image (Binary)
137140
runs-on: ubuntu-latest
141+
environment: "Push to Docker"
142+
permissions:
143+
id-token: write
144+
attestations: write
138145
needs:
139146
- andon-cord
140147
- build-binaries
@@ -164,7 +171,6 @@ jobs:
164171
## Create the downstream PR for the release branch to master,develop
165172
create-pr:
166173
if: |
167-
!contains(github.ref, '-rc') &&
168174
(
169175
inputs.node_tag != '' ||
170176
inputs.signer_tag != ''

.github/workflows/image-build-source.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ jobs:
2323
runs-on: ubuntu-latest
2424
## Requires the repo environment "Push to Docker", which will trigger a secondary approval step before running this workflow.
2525
environment: "Push to Docker"
26+
permissions:
27+
id-token: write
28+
attestations: write
2629
steps:
2730
## Setup Docker for the builds
2831
- name: Docker setup
@@ -68,3 +71,22 @@ jobs:
6871
GIT_COMMIT=${{ env.GITHUB_SHA_SHORT }}
6972
TARGET_CPU=x86-64-v3
7073
push: ${{ env.DOCKER_PUSH }}
74+
75+
## Generate docker image attestation(s)
76+
- name: Generate artifact attestation (${{ github.event.repository.name }})
77+
id: attest_primary
78+
uses: actions/attest-build-provenance@c074443f1aee8d4aeeae555aebba3282517141b2 # v2.2.3
79+
with:
80+
subject-name: |
81+
index.docker.io/${{env.docker-org}}/${{ github.event.repository.name }}
82+
subject-digest: ${{ steps.docker_build.outputs.digest }}
83+
push-to-registry: true
84+
85+
- name: Generate artifact attestation (stacks-blockchain)
86+
id: attest_secondary
87+
uses: actions/attest-build-provenance@c074443f1aee8d4aeeae555aebba3282517141b2 # v2.2.3
88+
with:
89+
subject-name: |
90+
index.docker.io/${{env.docker-org}}/stacks-blockchain
91+
subject-digest: ${{ steps.docker_build.outputs.digest }}
92+
push-to-registry: true

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 {
@@ -2440,6 +2443,9 @@ pub struct MinerConfig {
24402443
/// 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
24412444
/// within the same second as its parent, it will be rejected by the signers.
24422445
pub min_time_between_blocks_ms: u64,
2446+
/// The amount of time that the miner should sleep in between attempts to
2447+
/// mine a block when the mempool is empty
2448+
pub empty_mempool_sleep_time: Duration,
24432449
/// Time in milliseconds to pause after receiving the first threshold rejection, before proposing a new block.
24442450
pub first_rejection_pause_ms: u64,
24452451
/// Time in milliseconds to pause after receiving subsequent threshold rejections, before proposing a new block.
@@ -2492,6 +2498,7 @@ impl Default for MinerConfig {
24922498
max_reorg_depth: 3,
24932499
pre_nakamoto_mock_signing: false, // Should only default true if mining key is set
24942500
min_time_between_blocks_ms: DEFAULT_MIN_TIME_BETWEEN_BLOCKS_MS,
2501+
empty_mempool_sleep_time: Duration::from_millis(DEFAULT_EMPTY_MEMPOOL_SLEEP_MS),
24952502
first_rejection_pause_ms: DEFAULT_FIRST_REJECTION_PAUSE_MS,
24962503
subsequent_rejection_pause_ms: DEFAULT_SUBSEQUENT_REJECTION_PAUSE_MS,
24972504
block_commit_delay: Duration::from_millis(DEFAULT_BLOCK_COMMIT_DELAY_MS),
@@ -2907,6 +2914,7 @@ pub struct MinerConfigFile {
29072914
pub max_reorg_depth: Option<u64>,
29082915
pub pre_nakamoto_mock_signing: Option<bool>,
29092916
pub min_time_between_blocks_ms: Option<u64>,
2917+
pub empty_mempool_sleep_ms: Option<u64>,
29102918
pub first_rejection_pause_ms: Option<u64>,
29112919
pub subsequent_rejection_pause_ms: Option<u64>,
29122920
pub block_commit_delay_ms: Option<u64>,
@@ -3063,6 +3071,7 @@ impl MinerConfigFile {
30633071
} else {
30643072
ms
30653073
}).unwrap_or(miner_default_config.min_time_between_blocks_ms),
3074+
empty_mempool_sleep_time: self.empty_mempool_sleep_ms.map(Duration::from_millis).unwrap_or(miner_default_config.empty_mempool_sleep_time),
30663075
first_rejection_pause_ms: self.first_rejection_pause_ms.unwrap_or(miner_default_config.first_rejection_pause_ms),
30673076
subsequent_rejection_pause_ms: self.subsequent_rejection_pause_ms.unwrap_or(miner_default_config.subsequent_rejection_pause_ms),
30683077
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: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,8 +568,38 @@ 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+
// Pause the miner to wait for transactions to arrive
578+
let now = Instant::now();
579+
while now.elapsed() < self.config.miner.empty_mempool_sleep_time {
580+
if self.abort_flag.load(Ordering::SeqCst) {
581+
info!("Miner interrupted while mining in order to shut down");
582+
self.globals
583+
.raise_initiative(format!("MiningFailure: aborted by node"));
584+
return Err(ChainstateError::MinerAborted.into());
585+
}
586+
587+
// Check if the burnchain tip has changed
588+
let Ok(sort_db) = SortitionDB::open(
589+
&self.config.get_burn_db_file_path(),
590+
false,
591+
self.burnchain.pox_constants.clone(),
592+
) else {
593+
error!("Failed to open sortition DB. Will try mining again.");
594+
continue;
595+
};
596+
if self.check_burn_tip_changed(&sort_db).is_err() {
597+
return Err(NakamotoNodeError::BurnchainTipChanged);
598+
}
599+
600+
thread::sleep(Duration::from_millis(ABORT_TRY_AGAIN_MS));
601+
}
602+
573603
break None;
574604
}
575605
Err(e) => {
@@ -680,6 +710,13 @@ impl BlockMinerThread {
680710

681711
thread::sleep(Duration::from_millis(ABORT_TRY_AGAIN_MS));
682712

713+
if self.abort_flag.load(Ordering::SeqCst) {
714+
info!("Miner interrupted while mining in order to shut down");
715+
self.globals
716+
.raise_initiative(format!("MiningFailure: aborted by node"));
717+
return Err(ChainstateError::MinerAborted.into());
718+
}
719+
683720
// Check if the burnchain tip has changed
684721
let Ok(sort_db) = SortitionDB::open(
685722
&self.config.get_burn_db_file_path(),

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

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9819,7 +9819,11 @@ fn skip_mining_long_tx() {
98199819

98209820
// Sleep for longer than the miner's attempt time, so that the miner will
98219821
// mark this tx as long-running and skip it in the next attempt
9822-
sleep_ms(naka_conf.miner.nakamoto_attempt_time_ms + 1000);
9822+
sleep_ms(
9823+
naka_conf.miner.nakamoto_attempt_time_ms
9824+
+ naka_conf.miner.empty_mempool_sleep_time.as_millis() as u64
9825+
+ 1000,
9826+
);
98239827

98249828
TEST_TX_STALL.set(false);
98259829

@@ -12339,3 +12343,114 @@ fn handle_considered_txs_foreign_key_failure() {
1233912343

1234012344
run_loop_thread.join().unwrap();
1234112345
}
12346+
12347+
#[test]
12348+
#[ignore]
12349+
fn empty_mempool_sleep_ms() {
12350+
if env::var("BITCOIND_TEST") != Ok("1".into()) {
12351+
return;
12352+
}
12353+
12354+
let (mut conf, _miner_account) = naka_neon_integration_conf(None);
12355+
let password = "12345".to_string();
12356+
conf.connection_options.auth_token = Some(password.clone());
12357+
let stacker_sk = setup_stacker(&mut conf);
12358+
let signer_sk = Secp256k1PrivateKey::random();
12359+
let signer_addr = tests::to_addr(&signer_sk);
12360+
let sender_sk = Secp256k1PrivateKey::random();
12361+
// setup sender + recipient for a stx transfer
12362+
let sender_addr = tests::to_addr(&sender_sk);
12363+
let send_amt = 100;
12364+
let send_fee = 180;
12365+
conf.add_initial_balance(
12366+
PrincipalData::from(sender_addr).to_string(),
12367+
send_amt + send_fee,
12368+
);
12369+
conf.add_initial_balance(PrincipalData::from(signer_addr).to_string(), 100000);
12370+
12371+
// Set the empty mempool sleep time to something long enough that we can
12372+
// see the effect in the test.
12373+
conf.miner.empty_mempool_sleep_time = Duration::from_secs(30);
12374+
12375+
let mut btcd_controller = BitcoinCoreController::new(conf.clone());
12376+
btcd_controller
12377+
.start_bitcoind()
12378+
.expect("Failed starting bitcoind");
12379+
let mut btc_regtest_controller = BitcoinRegtestController::new(conf.clone(), None);
12380+
btc_regtest_controller.bootstrap_chain(201);
12381+
12382+
let mut run_loop = boot_nakamoto::BootRunLoop::new(conf.clone()).unwrap();
12383+
let run_loop_stopper = run_loop.get_termination_switch();
12384+
let Counters {
12385+
blocks_processed,
12386+
naka_submitted_commits: commits_submitted,
12387+
naka_proposed_blocks,
12388+
..
12389+
} = run_loop.counters();
12390+
let counters = run_loop.counters();
12391+
12392+
let coord_channel = run_loop.coordinator_channels();
12393+
let http_origin = format!("http://{}", &conf.node.rpc_bind);
12394+
12395+
let run_loop_thread = thread::spawn(move || run_loop.start(None, 0));
12396+
let mut signers = TestSigners::new(vec![signer_sk]);
12397+
wait_for_runloop(&blocks_processed);
12398+
boot_to_epoch_3(
12399+
&conf,
12400+
&blocks_processed,
12401+
&[stacker_sk],
12402+
&[signer_sk],
12403+
&mut Some(&mut signers),
12404+
&mut btc_regtest_controller,
12405+
);
12406+
12407+
info!("------------------------- Reached Epoch 3.0 -------------------------");
12408+
12409+
blind_signer(&conf, &signers, &counters);
12410+
12411+
wait_for_first_naka_block_commit(60, &commits_submitted);
12412+
12413+
next_block_and_wait(&mut btc_regtest_controller, &blocks_processed);
12414+
12415+
// Sleep for 5 seconds to ensure that the miner tries to mine and sees an
12416+
// empty mempool.
12417+
thread::sleep(Duration::from_secs(5));
12418+
12419+
info!("------------------------- Submit a transaction -------------------------");
12420+
let proposals_before = naka_proposed_blocks.load(Ordering::SeqCst);
12421+
12422+
let transfer_tx = make_stacks_transfer(
12423+
&sender_sk,
12424+
0,
12425+
send_fee,
12426+
conf.burnchain.chain_id,
12427+
&signer_addr.into(),
12428+
send_amt,
12429+
);
12430+
submit_tx(&http_origin, &transfer_tx);
12431+
12432+
// The miner should have slept for 30 seconds after seeing an empty mempool
12433+
// before trying to mine again. Let's check that there was at least 10s
12434+
// before the next block proposal.
12435+
wait_for(10, || {
12436+
let proposals_after = naka_proposed_blocks.load(Ordering::SeqCst);
12437+
Ok(proposals_after > proposals_before)
12438+
})
12439+
.expect_err("Expected to wait for 30 seconds before mining a block");
12440+
12441+
// Wait for the transaction to be mined
12442+
wait_for(60, || {
12443+
let account = get_account(&http_origin, &sender_addr);
12444+
Ok(account.nonce == 1)
12445+
})
12446+
.expect("Timed out waiting for transaction to be mined after delay");
12447+
12448+
coord_channel
12449+
.lock()
12450+
.expect("Mutex poisoned")
12451+
.stop_chains_coordinator();
12452+
12453+
run_loop_stopper.store(false, Ordering::SeqCst);
12454+
12455+
run_loop_thread.join().unwrap();
12456+
}

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9360,10 +9360,7 @@ fn injected_signatures_are_ignored_across_boundaries() {
93609360
}
93619361

93629362
info!("---- Manually mine a single burn block to force the signers to update ----");
9363-
next_block_and_wait(
9364-
&mut signer_test.running_nodes.btc_regtest_controller,
9365-
&signer_test.running_nodes.counters.blocks_processed,
9366-
);
9363+
signer_test.mine_nakamoto_block(Duration::from_secs(60), true);
93679364

93689365
signer_test.wait_for_registered_both_reward_cycles();
93699366

0 commit comments

Comments
 (0)