Skip to content

Commit 7143f7b

Browse files
authored
Merge branch 'develop' into feat/replay-signer-validation
2 parents bb7e57b + 88a946a commit 7143f7b

File tree

11 files changed

+325
-75
lines changed

11 files changed

+325
-75
lines changed

.github/workflows/clippy.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ name: Clippy Checks
77
# Only run when:
88
# - PRs are (re)opened against develop branch
99
on:
10+
merge_group:
11+
types:
12+
- checks_requested
1013
pull_request:
1114
branches:
1215
- develop
@@ -34,4 +37,4 @@ jobs:
3437
components: clippy
3538
- name: Clippy
3639
id: clippy
37-
run: cargo clippy-stacks
40+
run: cargo clippy-stacks

.github/workflows/github-release.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,11 @@ jobs:
164164
## Create the downstream PR for the release branch to master,develop
165165
create-pr:
166166
if: |
167-
inputs.node_tag != '' ||
168-
inputs.signer_tag != ''
167+
!contains(github.ref, '-rc') &&
168+
(
169+
inputs.node_tag != '' ||
170+
inputs.signer_tag != ''
171+
)
169172
name: Create Downstream PR (${{ github.ref_name }})
170173
runs-on: ubuntu-latest
171174
needs:

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ 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.
2324

2425
## [3.1.0.0.7]
2526

stacks-common/src/util/macros.rs

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,32 @@ macro_rules! iterable_enum {
4040
/// and EnumType.get_name() for free.
4141
#[macro_export]
4242
macro_rules! define_named_enum {
43-
($Name:ident { $($Variant:ident($VarName:literal),)* }) =>
44-
{
43+
(
44+
$(#[$enum_meta:meta])*
45+
$Name:ident {
46+
$(
47+
$(#[$variant_meta:meta])*
48+
$Variant:ident($VarName:literal),
49+
)*
50+
}
51+
) => {
52+
$(#[$enum_meta])*
4553
#[derive(::serde::Serialize, ::serde::Deserialize, Debug, Hash, PartialEq, Eq, Copy, Clone)]
4654
pub enum $Name {
47-
$($Variant),*,
55+
$(
56+
$(#[$variant_meta])*
57+
$Variant,
58+
)*
4859
}
60+
4961
impl $Name {
62+
/// All variants of the enum.
5063
pub const ALL: &[$Name] = &[$($Name::$Variant),*];
64+
65+
/// All names corresponding to the enum variants.
5166
pub const ALL_NAMES: &[&str] = &[$($VarName),*];
5267

68+
/// Looks up a variant by its name string.
5369
pub fn lookup_by_name(name: &str) -> Option<Self> {
5470
match name {
5571
$(
@@ -59,6 +75,7 @@ macro_rules! define_named_enum {
5975
}
6076
}
6177

78+
/// Gets the name of the enum variant as a `String`.
6279
pub fn get_name(&self) -> String {
6380
match self {
6481
$(
@@ -67,6 +84,7 @@ macro_rules! define_named_enum {
6784
}
6885
}
6986

87+
/// Gets the name of the enum variant as a static string slice.
7088
pub fn get_name_str(&self) -> &'static str {
7189
match self {
7290
$(
@@ -75,12 +93,13 @@ macro_rules! define_named_enum {
7593
}
7694
}
7795
}
96+
7897
impl ::std::fmt::Display for $Name {
7998
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
8099
write!(f, "{}", self.get_name_str())
81100
}
82101
}
83-
}
102+
};
84103
}
85104

86105
/// Define a "named" enum, i.e., each variant corresponds
@@ -739,3 +758,46 @@ macro_rules! function_name {
739758
stdext::function_name!()
740759
};
741760
}
761+
762+
#[cfg(test)]
763+
mod tests {
764+
#[test]
765+
fn test_macro_define_named_enum_without_docs() {
766+
define_named_enum!(
767+
MyEnum {
768+
Variant1("variant1"),
769+
Variant2("variant2"),
770+
});
771+
772+
assert_eq!("variant1", MyEnum::Variant1.get_name());
773+
assert_eq!("variant2", MyEnum::Variant2.get_name());
774+
775+
assert_eq!("variant1", MyEnum::Variant1.get_name_str());
776+
assert_eq!("variant2", MyEnum::Variant2.get_name_str());
777+
778+
assert_eq!(Some(MyEnum::Variant1), MyEnum::lookup_by_name("variant1"));
779+
assert_eq!(Some(MyEnum::Variant2), MyEnum::lookup_by_name("variant2"));
780+
assert_eq!(None, MyEnum::lookup_by_name("inexistent"));
781+
}
782+
#[test]
783+
fn test_macro_define_named_enum_with_docs() {
784+
define_named_enum!(
785+
/// MyEnum doc
786+
MyEnum {
787+
/// Variant1 doc
788+
Variant1("variant1"),
789+
/// Variant2 doc
790+
Variant2("variant2"),
791+
});
792+
793+
assert_eq!("variant1", MyEnum::Variant1.get_name());
794+
assert_eq!("variant2", MyEnum::Variant2.get_name());
795+
796+
assert_eq!("variant1", MyEnum::Variant1.get_name_str());
797+
assert_eq!("variant2", MyEnum::Variant2.get_name_str());
798+
799+
assert_eq!(Some(MyEnum::Variant1), MyEnum::lookup_by_name("variant1"));
800+
assert_eq!(Some(MyEnum::Variant2), MyEnum::lookup_by_name("variant2"));
801+
assert_eq!(None, MyEnum::lookup_by_name("inexistent"));
802+
}
803+
}

stacks-signer/src/signerdb.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1264,7 +1264,7 @@ impl SignerDb {
12641264
// Plus (ms + 999)/1000 to round up to the nearest second
12651265
let tenure_extend_timestamp = tenure_start_time
12661266
.saturating_add(tenure_idle_timeout_secs)
1267-
.saturating_add(tenure_process_time_ms.saturating_add(999) / 1000);
1267+
.saturating_add(tenure_process_time_ms.div_ceil(1000));
12681268
debug!("Calculated tenure extend timestamp";
12691269
"tenure_extend_timestamp" => tenure_extend_timestamp,
12701270
"tenure_start_time" => tenure_start_time,

stackslib/src/config/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2138,7 +2138,8 @@ pub struct MinerConfig {
21382138
pub unprocessed_block_deadline_secs: u64,
21392139
pub mining_key: Option<Secp256k1PrivateKey>,
21402140
/// Amount of time while mining in nakamoto to wait in between mining interim blocks
2141-
pub wait_on_interim_blocks: Duration,
2141+
/// DEPRECATED: use `min_time_between_blocks_ms` instead
2142+
pub wait_on_interim_blocks: Option<Duration>,
21422143
/// minimum number of transactions that must be in a block if we're going to replace a pending
21432144
/// block-commit with a new block-commit
21442145
pub min_tx_count: u64,
@@ -2209,7 +2210,7 @@ impl Default for MinerConfig {
22092210
candidate_retry_cache_size: 1024 * 1024,
22102211
unprocessed_block_deadline_secs: 30,
22112212
mining_key: None,
2212-
wait_on_interim_blocks: Duration::from_millis(2_500),
2213+
wait_on_interim_blocks: None,
22132214
min_tx_count: 0,
22142215
only_increase_tx_count: false,
22152216
unconfirmed_commits_helper: None,
@@ -2733,8 +2734,7 @@ impl MinerConfigFile {
27332734
.transpose()?,
27342735
wait_on_interim_blocks: self
27352736
.wait_on_interim_blocks_ms
2736-
.map(Duration::from_millis)
2737-
.unwrap_or(miner_default_config.wait_on_interim_blocks),
2737+
.map(Duration::from_millis),
27382738
min_tx_count: self
27392739
.min_tx_count
27402740
.unwrap_or(miner_default_config.min_tx_count),
@@ -2789,7 +2789,7 @@ impl MinerConfigFile {
27892789
pre_nakamoto_mock_signing: self
27902790
.pre_nakamoto_mock_signing
27912791
.unwrap_or(pre_nakamoto_mock_signing), // Should only default true if mining key is set
2792-
min_time_between_blocks_ms: self.min_time_between_blocks_ms.map(|ms| if ms < DEFAULT_MIN_TIME_BETWEEN_BLOCKS_MS {
2792+
min_time_between_blocks_ms: self.min_time_between_blocks_ms.map(|ms| if ms < DEFAULT_MIN_TIME_BETWEEN_BLOCKS_MS {
27932793
warn!("miner.min_time_between_blocks_ms is less than the minimum allowed value of {DEFAULT_MIN_TIME_BETWEEN_BLOCKS_MS} ms. Using the default value instead.");
27942794
DEFAULT_MIN_TIME_BETWEEN_BLOCKS_MS
27952795
} else {

stackslib/src/net/httpcore.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ pub const STACKS_REQUEST_ID: &str = "X-Request-Id";
7070
/// from non-Stacks nodes (like Gaia hubs, CDNs, vanilla HTTP servers, and so on).
7171
pub const HTTP_REQUEST_ID_RESERVED: u32 = 0;
7272

73+
/// The interval at which to send heartbeat logs
74+
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(60);
75+
7376
/// All representations of the `tip=` query parameter value
7477
#[derive(Debug, Clone, PartialEq)]
7578
pub enum TipRequest {
@@ -1823,8 +1826,8 @@ pub fn send_http_request(
18231826
stream.set_nodelay(true)?;
18241827

18251828
let start = Instant::now();
1826-
1827-
debug!("send_request: Sending request"; "request" => %request.request_path());
1829+
let request_path = request.request_path();
1830+
debug!("send_request: Sending request"; "request" => request_path);
18281831

18291832
// Some explanation of what's going on here is in order.
18301833
//
@@ -1882,6 +1885,7 @@ pub fn send_http_request(
18821885
.map_err(|e| handle_net_error(e, "Failed to serialize request body"))?;
18831886

18841887
debug!("send_request(sending data)");
1888+
let mut last_heartbeat_time = start; // Initialize heartbeat timer for sending loop
18851889
loop {
18861890
let flushed = request_handle
18871891
.try_flush()
@@ -1900,18 +1904,26 @@ pub fn send_http_request(
19001904
break;
19011905
}
19021906

1903-
if Instant::now().saturating_duration_since(start) > timeout {
1907+
if start.elapsed() >= timeout {
19041908
return Err(io::Error::new(
19051909
io::ErrorKind::WouldBlock,
1906-
"Timed out while receiving request",
1910+
"Timed out while sending request",
19071911
));
19081912
}
1913+
if last_heartbeat_time.elapsed() >= HEARTBEAT_INTERVAL {
1914+
info!(
1915+
"send_request(sending data): heartbeat - still sending request to {} path='{}' (elapsed: {:?})",
1916+
addr, request_path, start.elapsed()
1917+
);
1918+
last_heartbeat_time = Instant::now();
1919+
}
19091920
}
19101921

19111922
// Step 4: pull bytes from the socket back into the handle, and see if the connection decoded
19121923
// and dispatched any new messages to the request handle. If so, then extract the message and
19131924
// check that it's a well-formed HTTP response.
19141925
debug!("send_request(receiving data)");
1926+
last_heartbeat_time = Instant::now();
19151927
let response = loop {
19161928
// get back the reply
19171929
debug!("send_request(receiving data): try to receive data");
@@ -1944,12 +1956,19 @@ pub fn send_http_request(
19441956
};
19451957
request_handle = rh;
19461958

1947-
if Instant::now().saturating_duration_since(start) > timeout {
1959+
if start.elapsed() >= timeout {
19481960
return Err(io::Error::new(
19491961
io::ErrorKind::WouldBlock,
1950-
"Timed out while receiving request",
1962+
"Timed out while receiving response",
19511963
));
19521964
}
1965+
if last_heartbeat_time.elapsed() >= HEARTBEAT_INTERVAL {
1966+
info!(
1967+
"send_request(receiving data): heartbeat - still receiving response from {} path='{}' (elapsed: {:?})",
1968+
addr, request_path, start.elapsed()
1969+
);
1970+
last_heartbeat_time = Instant::now();
1971+
}
19531972
};
19541973

19551974
// Step 5: decode the HTTP message and return it if it's not an error.

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

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,9 @@ impl BlockMinerThread {
643643
"block_height" => new_block.header.chain_length,
644644
"consensus_hash" => %new_block.header.consensus_hash,
645645
);
646+
647+
// We successfully mined, so the mempool caches are valid.
648+
self.reset_nonce_cache = false;
646649
}
647650

648651
// update mined-block counters and mined-tenure counters
@@ -660,20 +663,35 @@ impl BlockMinerThread {
660663
self.mined_blocks += 1;
661664
}
662665

663-
let Ok(sort_db) = SortitionDB::open(
664-
&self.config.get_burn_db_file_path(),
665-
true,
666-
self.burnchain.pox_constants.clone(),
667-
) else {
668-
error!("Failed to open sortition DB. Will try mining again.");
669-
return Ok(());
670-
};
666+
if let Some(last_block_mined) = &self.last_block_mined {
667+
// Wait until the last block mined has been processed
668+
loop {
669+
let (_, processed, _, _) = chain_state
670+
.nakamoto_blocks_db()
671+
.get_block_processed_and_signed_weight(
672+
&last_block_mined.header.consensus_hash,
673+
&last_block_mined.header.block_hash(),
674+
)?
675+
.ok_or_else(|| NakamotoNodeError::UnexpectedChainState)?;
676+
677+
if processed {
678+
break;
679+
}
680+
681+
thread::sleep(Duration::from_millis(ABORT_TRY_AGAIN_MS));
671682

672-
let wait_start = Instant::now();
673-
while wait_start.elapsed() < self.config.miner.wait_on_interim_blocks {
674-
thread::sleep(Duration::from_millis(ABORT_TRY_AGAIN_MS));
675-
if self.check_burn_tip_changed(&sort_db).is_err() {
676-
return Err(NakamotoNodeError::BurnchainTipChanged);
683+
// Check if the burnchain tip has changed
684+
let Ok(sort_db) = SortitionDB::open(
685+
&self.config.get_burn_db_file_path(),
686+
false,
687+
self.burnchain.pox_constants.clone(),
688+
) else {
689+
error!("Failed to open sortition DB. Will try mining again.");
690+
return Ok(());
691+
};
692+
if self.check_burn_tip_changed(&sort_db).is_err() {
693+
return Err(NakamotoNodeError::BurnchainTipChanged);
694+
}
677695
}
678696
}
679697

0 commit comments

Comments
 (0)