Skip to content

Commit 5f7590b

Browse files
authored
Merge branch 'develop' into fix/relayer-drain-channel
2 parents 5fdaaf0 + 2456645 commit 5f7590b

File tree

15 files changed

+730
-43
lines changed

15 files changed

+730
-43
lines changed

.github/workflows/bitcoin-tests.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ jobs:
9999
- tests::signer::v0::forked_tenure_okay
100100
- tests::signer::v0::forked_tenure_invalid
101101
- tests::signer::v0::empty_sortition
102+
- tests::signer::v0::empty_sortition_before_approval
103+
- tests::signer::v0::empty_sortition_before_proposal
102104
- tests::signer::v0::bitcoind_forking_test
103105
- tests::signer::v0::multiple_miners
104106
- tests::signer::v0::mock_sign_epoch_25
@@ -112,6 +114,7 @@ jobs:
112114
- tests::signer::v0::locally_accepted_blocks_overriden_by_global_rejection
113115
- tests::signer::v0::locally_rejected_blocks_overriden_by_global_acceptance
114116
- tests::signer::v0::reorg_locally_accepted_blocks_across_tenures_succeeds
117+
- tests::signer::v0::reorg_locally_accepted_blocks_across_tenures_fails
115118
- tests::signer::v0::miner_recovers_when_broadcast_block_delay_across_tenures_occurs
116119
- tests::signer::v0::multiple_miners_with_nakamoto_blocks
117120
- tests::signer::v0::partial_tenure_fork
@@ -133,6 +136,7 @@ jobs:
133136
- tests::nakamoto_integrations::utxo_check_on_startup_panic
134137
- tests::nakamoto_integrations::utxo_check_on_startup_recover
135138
- tests::nakamoto_integrations::v3_signer_api_endpoint
139+
- tests::nakamoto_integrations::signer_chainstate
136140
# TODO: enable these once v1 signer is supported by a new nakamoto epoch
137141
# - tests::signer::v1::dkg
138142
# - tests::signer::v1::sign_request_rejected

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,21 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
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)
1313

14+
## [3.0.0.0.1]
15+
16+
### Changed
17+
- Add index for StacksBlockId to nakamoto block headers table (improves node performance)
18+
- Remove the panic for reporting DB deadlocks (just error and continue waiting)
19+
- Various test fixes for CI (5353, 5368, 5372, 5371, 5380, 5378, 5387, 5396, 5390, 5394)
20+
- Various log fixes:
21+
- don't say proceeding to mine blocks if not a miner
22+
- misc. warns downgraded to debugs
23+
- 5391: Update default block proposal timeout to 10 minutes
24+
- 5406: After block rejection, miner pauses
25+
- Docs fixes
26+
- Fix signer docs link
27+
- Specify burn block in clarity docs
28+
1429
## [3.0.0.0.0]
1530

1631
### Added

stacks-signer/CHANGELOG.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,21 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
1111

1212
### Changed
1313

14-
## [3.0.0.0.0]
14+
## [3.0.0.0.1.0]
15+
16+
### Changed
17+
18+
- Change block rejection message to generic block response
19+
20+
## [3.0.0.0.0.1]
21+
22+
### Added
23+
24+
### Changed
25+
- Update block proposal timeout default to 10 minutes (#5391)
26+
- Updated documentation link in output (#5363)
27+
28+
## [3.0.0.0.0.0]
1529

1630
### Added
1731

stacks-signer/src/chainstate.rs

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ use blockstack_lib::chainstate::nakamoto::NakamotoBlock;
1919
use blockstack_lib::chainstate::stacks::TenureChangePayload;
2020
use blockstack_lib::net::api::getsortition::SortitionInfo;
2121
use blockstack_lib::util_lib::db::Error as DBError;
22-
use clarity::types::chainstate::BurnchainHeaderHash;
2322
use slog::{slog_info, slog_warn};
24-
use stacks_common::types::chainstate::{ConsensusHash, StacksPublicKey};
23+
use stacks_common::types::chainstate::{BurnchainHeaderHash, ConsensusHash, StacksPublicKey};
24+
use stacks_common::util::get_epoch_time_secs;
2525
use stacks_common::util::hash::Hash160;
2626
use stacks_common::{info, warn};
2727

2828
use crate::client::{ClientError, CurrentAndLastSortition, StacksClient};
2929
use crate::config::SignerConfig;
30-
use crate::signerdb::{BlockState, SignerDb};
30+
use crate::signerdb::{BlockInfo, BlockState, SignerDb};
3131

3232
#[derive(thiserror::Error, Debug)]
3333
/// Error type for the signer chainstate module
@@ -119,13 +119,17 @@ pub struct ProposalEvalConfig {
119119
pub first_proposal_burn_block_timing: Duration,
120120
/// Time between processing a sortition and proposing a block before the block is considered invalid
121121
pub block_proposal_timeout: Duration,
122+
/// Time to wait for the last block of a tenure to be globally accepted or rejected before considering
123+
/// a new miner's block at the same height as valid.
124+
pub tenure_last_block_proposal_timeout: Duration,
122125
}
123126

124127
impl From<&SignerConfig> for ProposalEvalConfig {
125128
fn from(value: &SignerConfig) -> Self {
126129
Self {
127130
first_proposal_burn_block_timing: value.first_proposal_burn_block_timing,
128131
block_proposal_timeout: value.block_proposal_timeout,
132+
tenure_last_block_proposal_timeout: value.tenure_last_block_proposal_timeout,
129133
}
130134
}
131135
}
@@ -460,7 +464,36 @@ impl SortitionsView {
460464
Ok(true)
461465
}
462466

463-
/// Check if the tenure change block confirms the expected parent block (i.e., the last globally accepted block in the parent tenure)
467+
/// Get the last block from the given tenure
468+
/// Returns the last locally accepted block if it is not timed out, otherwise it will return the last globally accepted block.
469+
fn get_tenure_last_block_info(
470+
consensus_hash: &ConsensusHash,
471+
signer_db: &SignerDb,
472+
tenure_last_block_proposal_timeout: Duration,
473+
) -> Result<Option<BlockInfo>, ClientError> {
474+
// Get the last known block in the previous tenure
475+
let last_locally_accepted_block = signer_db
476+
.get_last_accepted_block(consensus_hash)
477+
.map_err(|e| ClientError::InvalidResponse(e.to_string()))?;
478+
479+
if let Some(local_info) = last_locally_accepted_block {
480+
if let Some(signed_over_time) = local_info.signed_self {
481+
if signed_over_time + tenure_last_block_proposal_timeout.as_secs()
482+
> get_epoch_time_secs()
483+
{
484+
// The last locally accepted block is not timed out, return it
485+
return Ok(Some(local_info));
486+
}
487+
}
488+
}
489+
// The last locally accepted block is timed out, get the last globally accepted block
490+
signer_db
491+
.get_last_globally_accepted_block(consensus_hash)
492+
.map_err(|e| ClientError::InvalidResponse(e.to_string()))
493+
}
494+
495+
/// Check if the tenure change block confirms the expected parent block
496+
/// (i.e., the last locally accepted block in the parent tenure, or if that block is timed out, the last globally accepted block in the parent tenure)
464497
/// It checks the local DB first, and if the block is not present in the local DB, it asks the
465498
/// Stacks node for the highest processed block header in the given tenure (and then caches it
466499
/// in the DB).
@@ -473,24 +506,27 @@ impl SortitionsView {
473506
reward_cycle: u64,
474507
signer_db: &mut SignerDb,
475508
client: &StacksClient,
509+
tenure_last_block_proposal_timeout: Duration,
476510
) -> Result<bool, ClientError> {
477-
// If the tenure change block confirms the expected parent block, it should confirm at least one more block than the last globally accepted block in the parent tenure.
478-
let last_globally_accepted_block = signer_db
479-
.get_last_globally_accepted_block(&tenure_change.prev_tenure_consensus_hash)
480-
.map_err(|e| ClientError::InvalidResponse(e.to_string()))?;
511+
// If the tenure change block confirms the expected parent block, it should confirm at least one more block than the last accepted block in the parent tenure.
512+
let last_block_info = Self::get_tenure_last_block_info(
513+
&tenure_change.prev_tenure_consensus_hash,
514+
signer_db,
515+
tenure_last_block_proposal_timeout,
516+
)?;
481517

482-
if let Some(global_info) = last_globally_accepted_block {
518+
if let Some(info) = last_block_info {
483519
// N.B. this block might not be the last globally accepted block across the network;
484520
// it's just the highest one in this tenure that we know about. If this given block is
485521
// no higher than it, then it's definitely no higher than the last globally accepted
486522
// block across the network, so we can do an early rejection here.
487-
if block.header.chain_length <= global_info.block.header.chain_length {
523+
if block.header.chain_length <= info.block.header.chain_length {
488524
warn!(
489525
"Miner's block proposal does not confirm as many blocks as we expect";
490526
"proposed_block_consensus_hash" => %block.header.consensus_hash,
491527
"proposed_block_signer_sighash" => %block.header.signer_signature_hash(),
492528
"proposed_chain_length" => block.header.chain_length,
493-
"expected_at_least" => global_info.block.header.chain_length + 1,
529+
"expected_at_least" => info.block.header.chain_length + 1,
494530
);
495531
return Ok(false);
496532
}
@@ -558,6 +594,7 @@ impl SortitionsView {
558594
reward_cycle,
559595
signer_db,
560596
client,
597+
self.config.tenure_last_block_proposal_timeout,
561598
)?;
562599
if !confirms_expected_parent {
563600
return Ok(false);
@@ -573,15 +610,15 @@ impl SortitionsView {
573610
if !is_valid_parent_tenure {
574611
return Ok(false);
575612
}
576-
let last_in_tenure = signer_db
613+
let last_in_current_tenure = signer_db
577614
.get_last_globally_accepted_block(&block.header.consensus_hash)
578615
.map_err(|e| ClientError::InvalidResponse(e.to_string()))?;
579-
if let Some(last_in_tenure) = last_in_tenure {
616+
if let Some(last_in_current_tenure) = last_in_current_tenure {
580617
warn!(
581618
"Miner block proposal contains a tenure change, but we've already signed a block in this tenure. Considering proposal invalid.";
582619
"proposed_block_consensus_hash" => %block.header.consensus_hash,
583620
"proposed_block_signer_sighash" => %block.header.signer_signature_hash(),
584-
"last_in_tenure_signer_sighash" => %last_in_tenure.block.header.signer_signature_hash(),
621+
"last_in_tenure_signer_sighash" => %last_in_current_tenure.block.header.signer_signature_hash(),
585622
);
586623
return Ok(false);
587624
}

stacks-signer/src/client/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ pub(crate) mod tests {
411411
db_path: config.db_path.clone(),
412412
first_proposal_burn_block_timing: config.first_proposal_burn_block_timing,
413413
block_proposal_timeout: config.block_proposal_timeout,
414+
tenure_last_block_proposal_timeout: config.tenure_last_block_proposal_timeout,
414415
}
415416
}
416417

stacks-signer/src/config.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ use crate::client::SignerSlotID;
3636
const EVENT_TIMEOUT_MS: u64 = 5000;
3737
const BLOCK_PROPOSAL_TIMEOUT_MS: u64 = 600_000;
3838
const DEFAULT_FIRST_PROPOSAL_BURN_BLOCK_TIMING_SECS: u64 = 60;
39+
const DEFAULT_TENURE_LAST_BLOCK_PROPOSAL_TIMEOUT_SECS: u64 = 30;
3940

4041
#[derive(thiserror::Error, Debug)]
4142
/// An error occurred parsing the provided configuration
@@ -128,6 +129,9 @@ pub struct SignerConfig {
128129
pub first_proposal_burn_block_timing: Duration,
129130
/// How much time to wait for a miner to propose a block following a sortition
130131
pub block_proposal_timeout: Duration,
132+
/// Time to wait for the last block of a tenure to be globally accepted or rejected
133+
/// before considering a new miner's block at the same height as potentially valid.
134+
pub tenure_last_block_proposal_timeout: Duration,
131135
}
132136

133137
/// The parsed configuration for the signer
@@ -158,6 +162,9 @@ pub struct GlobalConfig {
158162
pub block_proposal_timeout: Duration,
159163
/// An optional custom Chain ID
160164
pub chain_id: Option<u32>,
165+
/// Time to wait for the last block of a tenure to be globally accepted or rejected
166+
/// before considering a new miner's block at the same height as potentially valid.
167+
pub tenure_last_block_proposal_timeout: Duration,
161168
}
162169

163170
/// Internal struct for loading up the config file
@@ -180,13 +187,16 @@ struct RawConfigFile {
180187
pub db_path: String,
181188
/// Metrics endpoint
182189
pub metrics_endpoint: Option<String>,
183-
/// How much time must pass between the first block proposal in a tenure and the next bitcoin block
190+
/// How much time must pass in seconds between the first block proposal in a tenure and the next bitcoin block
184191
/// before a subsequent miner isn't allowed to reorg the tenure
185192
pub first_proposal_burn_block_timing_secs: Option<u64>,
186193
/// How much time to wait for a miner to propose a block following a sortition in milliseconds
187194
pub block_proposal_timeout_ms: Option<u64>,
188195
/// An optional custom Chain ID
189196
pub chain_id: Option<u32>,
197+
/// Time in seconds to wait for the last block of a tenure to be globally accepted or rejected
198+
/// before considering a new miner's block at the same height as potentially valid.
199+
pub tenure_last_block_proposal_timeout_secs: Option<u64>,
190200
}
191201

192202
impl RawConfigFile {
@@ -266,6 +276,12 @@ impl TryFrom<RawConfigFile> for GlobalConfig {
266276
.unwrap_or(BLOCK_PROPOSAL_TIMEOUT_MS),
267277
);
268278

279+
let tenure_last_block_proposal_timeout = Duration::from_secs(
280+
raw_data
281+
.tenure_last_block_proposal_timeout_secs
282+
.unwrap_or(DEFAULT_TENURE_LAST_BLOCK_PROPOSAL_TIMEOUT_SECS),
283+
);
284+
269285
Ok(Self {
270286
node_host: raw_data.node_host,
271287
endpoint,
@@ -279,6 +295,7 @@ impl TryFrom<RawConfigFile> for GlobalConfig {
279295
first_proposal_burn_block_timing,
280296
block_proposal_timeout,
281297
chain_id: raw_data.chain_id,
298+
tenure_last_block_proposal_timeout,
282299
})
283300
}
284301
}

stacks-signer/src/runloop.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ impl<Signer: SignerTrait<T>, T: StacksMessageCodec + Clone + Send + Debug> RunLo
283283
mainnet: self.config.network.is_mainnet(),
284284
db_path: self.config.db_path.clone(),
285285
block_proposal_timeout: self.config.block_proposal_timeout,
286+
tenure_last_block_proposal_timeout: self.config.tenure_last_block_proposal_timeout,
286287
}))
287288
}
288289

stacks-signer/src/signerdb.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ use blockstack_lib::util_lib::db::{
2424
Error as DBError,
2525
};
2626
use clarity::types::chainstate::{BurnchainHeaderHash, StacksAddress};
27-
use clarity::util::get_epoch_time_secs;
2827
use libsigner::BlockProposal;
2928
use rusqlite::{
3029
params, Connection, Error as SqliteError, OpenFlags, OptionalExtension, Transaction,
@@ -33,6 +32,7 @@ use serde::{Deserialize, Serialize};
3332
use slog::{slog_debug, slog_error};
3433
use stacks_common::codec::{read_next, write_next, Error as CodecError, StacksMessageCodec};
3534
use stacks_common::types::chainstate::ConsensusHash;
35+
use stacks_common::util::get_epoch_time_secs;
3636
use stacks_common::util::hash::Sha512Trunc256Sum;
3737
use stacks_common::util::secp256k1::MessageSignature;
3838
use stacks_common::{debug, define_u8_enum, error};

stacks-signer/src/tests/chainstate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ fn setup_test_environment(
8989
config: ProposalEvalConfig {
9090
first_proposal_burn_block_timing: Duration::from_secs(30),
9191
block_proposal_timeout: Duration::from_secs(5),
92+
tenure_last_block_proposal_timeout: Duration::from_secs(30),
9293
},
9394
};
9495

0 commit comments

Comments
 (0)