Skip to content

Commit 0665f30

Browse files
authored
Merge branch 'develop' into fix/5502
2 parents e486b9e + b3b7117 commit 0665f30

File tree

13 files changed

+205
-59
lines changed

13 files changed

+205
-59
lines changed

.github/workflows/bitcoin-tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ jobs:
130130
- tests::signer::v0::continue_after_fast_block_no_sortition
131131
- tests::signer::v0::block_validation_response_timeout
132132
- tests::signer::v0::tenure_extend_after_bad_commit
133+
- tests::signer::v0::block_proposal_max_age_rejections
133134
- tests::nakamoto_integrations::burn_ops_integration_test
134135
- tests::nakamoto_integrations::check_block_heights
135136
- tests::nakamoto_integrations::clarity_burn_state

clarity/src/vm/contexts.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ impl EventBatch {
499499

500500
impl<'a, 'hooks> OwnedEnvironment<'a, 'hooks> {
501501
#[cfg(any(test, feature = "testing"))]
502-
pub fn new(database: ClarityDatabase<'a>, epoch: StacksEpochId) -> OwnedEnvironment<'a, '_> {
502+
pub fn new(database: ClarityDatabase<'a>, epoch: StacksEpochId) -> OwnedEnvironment<'a, 'a> {
503503
OwnedEnvironment {
504504
context: GlobalContext::new(
505505
false,
@@ -513,7 +513,7 @@ impl<'a, 'hooks> OwnedEnvironment<'a, 'hooks> {
513513
}
514514

515515
#[cfg(any(test, feature = "testing"))]
516-
pub fn new_toplevel(mut database: ClarityDatabase<'a>) -> OwnedEnvironment<'a, '_> {
516+
pub fn new_toplevel(mut database: ClarityDatabase<'a>) -> OwnedEnvironment<'a, 'a> {
517517
database.begin();
518518
let epoch = database.get_clarity_epoch_version().unwrap();
519519
let version = ClarityVersion::default_for_epoch(epoch);
@@ -540,7 +540,7 @@ impl<'a, 'hooks> OwnedEnvironment<'a, 'hooks> {
540540
mut database: ClarityDatabase<'a>,
541541
epoch: StacksEpochId,
542542
use_mainnet: bool,
543-
) -> OwnedEnvironment<'a, '_> {
543+
) -> OwnedEnvironment<'a, 'a> {
544544
use crate::vm::tests::test_only_mainnet_to_chain_id;
545545
let cost_track = LimitedCostTracker::new_max_limit(&mut database, epoch, use_mainnet)
546546
.expect("FAIL: problem instantiating cost tracking");
@@ -557,7 +557,7 @@ impl<'a, 'hooks> OwnedEnvironment<'a, 'hooks> {
557557
chain_id: u32,
558558
database: ClarityDatabase<'a>,
559559
epoch_id: StacksEpochId,
560-
) -> OwnedEnvironment<'a, '_> {
560+
) -> OwnedEnvironment<'a, 'a> {
561561
OwnedEnvironment {
562562
context: GlobalContext::new(
563563
mainnet,
@@ -576,7 +576,7 @@ impl<'a, 'hooks> OwnedEnvironment<'a, 'hooks> {
576576
database: ClarityDatabase<'a>,
577577
cost_tracker: LimitedCostTracker,
578578
epoch_id: StacksEpochId,
579-
) -> OwnedEnvironment<'a, '_> {
579+
) -> OwnedEnvironment<'a, 'a> {
580580
OwnedEnvironment {
581581
context: GlobalContext::new(mainnet, chain_id, database, cost_tracker, epoch_id),
582582
call_stack: CallStack::new(),
@@ -1546,7 +1546,7 @@ impl<'a, 'hooks> GlobalContext<'a, 'hooks> {
15461546
database: ClarityDatabase<'a>,
15471547
cost_track: LimitedCostTracker,
15481548
epoch_id: StacksEpochId,
1549-
) -> GlobalContext {
1549+
) -> GlobalContext<'a, 'hooks> {
15501550
GlobalContext {
15511551
database,
15521552
cost_track,

clarity/src/vm/database/key_value_wrapper.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ where
205205
}
206206

207207
impl<'a> RollbackWrapper<'a> {
208-
pub fn new(store: &'a mut dyn ClarityBackingStore) -> RollbackWrapper {
208+
pub fn new(store: &'a mut dyn ClarityBackingStore) -> RollbackWrapper<'a> {
209209
RollbackWrapper {
210210
store,
211211
lookup_map: HashMap::new(),
@@ -218,7 +218,7 @@ impl<'a> RollbackWrapper<'a> {
218218
pub fn from_persisted_log(
219219
store: &'a mut dyn ClarityBackingStore,
220220
log: RollbackWrapperPersistedLog,
221-
) -> RollbackWrapper {
221+
) -> RollbackWrapper<'a> {
222222
RollbackWrapper {
223223
store,
224224
lookup_map: log.lookup_map,

stacks-signer/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
99

1010
## Added
1111

12+
- Introduced the `block_proposal_max_age_secs` configuration option for signers, enabling them to automatically ignore block proposals that exceed the specified age in seconds.
13+
1214
## Changed
15+
- Improvements to the stale signer cleanup logic: deletes the prior signer if it has no remaining unprocessed blocks in its database
1316

1417
## [3.1.0.0.1.0]
1518

stacks-signer/src/chainstate.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ impl SortitionsView {
367367
tenure_extend.burn_view_consensus_hash != sortition_consensus_hash;
368368
let extend_timestamp = signer_db.calculate_tenure_extend_timestamp(
369369
self.config.tenure_idle_timeout,
370-
&block,
370+
block,
371371
false,
372372
);
373373
let epoch_time = get_epoch_time_secs();

stacks-signer/src/client/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ pub(crate) mod tests {
414414
tenure_last_block_proposal_timeout: config.tenure_last_block_proposal_timeout,
415415
block_proposal_validation_timeout: config.block_proposal_validation_timeout,
416416
tenure_idle_timeout: config.tenure_idle_timeout,
417+
block_proposal_max_age_secs: config.block_proposal_max_age_secs,
417418
}
418419
}
419420

stacks-signer/src/config.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ const BLOCK_PROPOSAL_VALIDATION_TIMEOUT_MS: u64 = 120_000;
3939
const DEFAULT_FIRST_PROPOSAL_BURN_BLOCK_TIMING_SECS: u64 = 60;
4040
const DEFAULT_TENURE_LAST_BLOCK_PROPOSAL_TIMEOUT_SECS: u64 = 30;
4141
const TENURE_IDLE_TIMEOUT_SECS: u64 = 300;
42+
const DEFAULT_BLOCK_PROPOSAL_MAX_AGE_SECS: u64 = 600;
4243

4344
#[derive(thiserror::Error, Debug)]
4445
/// An error occurred parsing the provided configuration
@@ -138,6 +139,8 @@ pub struct SignerConfig {
138139
pub block_proposal_validation_timeout: Duration,
139140
/// How much idle time must pass before allowing a tenure extend
140141
pub tenure_idle_timeout: Duration,
142+
/// The maximum age of a block proposal in seconds that will be processed by the signer
143+
pub block_proposal_max_age_secs: u64,
141144
}
142145

143146
/// The parsed configuration for the signer
@@ -176,6 +179,8 @@ pub struct GlobalConfig {
176179
pub block_proposal_validation_timeout: Duration,
177180
/// How much idle time must pass before allowing a tenure extend
178181
pub tenure_idle_timeout: Duration,
182+
/// The maximum age of a block proposal that will be processed by the signer
183+
pub block_proposal_max_age_secs: u64,
179184
}
180185

181186
/// Internal struct for loading up the config file
@@ -213,6 +218,8 @@ struct RawConfigFile {
213218
pub block_proposal_validation_timeout_ms: Option<u64>,
214219
/// How much idle time (in seconds) must pass before a tenure extend is allowed
215220
pub tenure_idle_timeout_secs: Option<u64>,
221+
/// The maximum age of a block proposal (in secs) that will be processed by the signer.
222+
pub block_proposal_max_age_secs: Option<u64>,
216223
}
217224

218225
impl RawConfigFile {
@@ -310,6 +317,10 @@ impl TryFrom<RawConfigFile> for GlobalConfig {
310317
.unwrap_or(TENURE_IDLE_TIMEOUT_SECS),
311318
);
312319

320+
let block_proposal_max_age_secs = raw_data
321+
.block_proposal_max_age_secs
322+
.unwrap_or(DEFAULT_BLOCK_PROPOSAL_MAX_AGE_SECS);
323+
313324
Ok(Self {
314325
node_host: raw_data.node_host,
315326
endpoint,
@@ -326,6 +337,7 @@ impl TryFrom<RawConfigFile> for GlobalConfig {
326337
tenure_last_block_proposal_timeout,
327338
block_proposal_validation_timeout,
328339
tenure_idle_timeout,
340+
block_proposal_max_age_secs,
329341
})
330342
}
331343
}

stacks-signer/src/runloop.rs

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ impl<Signer: SignerTrait<T>, T: StacksMessageCodec + Clone + Send + Debug> RunLo
286286
tenure_last_block_proposal_timeout: self.config.tenure_last_block_proposal_timeout,
287287
block_proposal_validation_timeout: self.config.block_proposal_validation_timeout,
288288
tenure_idle_timeout: self.config.tenure_idle_timeout,
289+
block_proposal_max_age_secs: self.config.block_proposal_max_age_secs,
289290
}))
290291
}
291292

@@ -423,23 +424,15 @@ impl<Signer: SignerTrait<T>, T: StacksMessageCodec + Clone + Send + Debug> RunLo
423424
let mut to_delete = Vec::new();
424425
for (idx, signer) in &mut self.stacks_signers {
425426
let reward_cycle = signer.reward_cycle();
426-
let next_reward_cycle = reward_cycle.wrapping_add(1);
427-
let stale = match next_reward_cycle.cmp(&current_reward_cycle) {
428-
std::cmp::Ordering::Less => true, // We are more than one reward cycle behind, so we are stale
429-
std::cmp::Ordering::Equal => {
430-
// We are the next reward cycle, so check if we were registered and have any pending blocks to process
431-
match signer {
432-
ConfiguredSigner::RegisteredSigner(signer) => {
433-
!signer.has_unprocessed_blocks()
434-
}
435-
_ => true,
436-
}
427+
if reward_cycle >= current_reward_cycle {
428+
// We are either the current or a future reward cycle, so we are not stale.
429+
continue;
430+
}
431+
if let ConfiguredSigner::RegisteredSigner(signer) = signer {
432+
if !signer.has_unprocessed_blocks() {
433+
debug!("{signer}: Signer's tenure has completed.");
434+
to_delete.push(*idx);
437435
}
438-
std::cmp::Ordering::Greater => false, // We are the current reward cycle, so we are not stale
439-
};
440-
if stale {
441-
debug!("{signer}: Signer's tenure has completed.");
442-
to_delete.push(*idx);
443436
}
444437
}
445438
for idx in to_delete {

stacks-signer/src/v0/signer.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ pub struct Signer {
9292
pub block_proposal_validation_timeout: Duration,
9393
/// The current submitted block proposal and its submission time
9494
pub submitted_block_proposal: Option<(BlockProposal, Instant)>,
95+
/// Maximum age of a block proposal in seconds before it is dropped without processing
96+
pub block_proposal_max_age_secs: u64,
9597
}
9698

9799
impl std::fmt::Display for Signer {
@@ -284,6 +286,7 @@ impl From<SignerConfig> for Signer {
284286
proposal_config,
285287
submitted_block_proposal: None,
286288
block_proposal_validation_timeout: signer_config.block_proposal_validation_timeout,
289+
block_proposal_max_age_secs: signer_config.block_proposal_max_age_secs,
287290
}
288291
}
289292
}
@@ -344,6 +347,24 @@ impl Signer {
344347
return;
345348
}
346349

350+
if block_proposal
351+
.block
352+
.header
353+
.timestamp
354+
.saturating_add(self.block_proposal_max_age_secs)
355+
< get_epoch_time_secs()
356+
{
357+
// Block is too old. Drop it with a warning. Don't even bother broadcasting to the node.
358+
warn!("{self}: Received a block proposal that is more than {} secs old. Ignoring...", self.block_proposal_max_age_secs;
359+
"signer_sighash" => %block_proposal.block.header.signer_signature_hash(),
360+
"block_id" => %block_proposal.block.block_id(),
361+
"block_height" => block_proposal.block.header.chain_length,
362+
"burn_height" => block_proposal.burn_height,
363+
"timestamp" => block_proposal.block.header.timestamp,
364+
);
365+
return;
366+
}
367+
347368
// TODO: should add a check to ignore an old burn block height if we know its outdated. Would require us to store the burn block height we last saw on the side.
348369
// the signer needs to be able to determine whether or not the block they're about to sign would conflict with an already-signed Stacks block
349370
let signer_signature_hash = block_proposal.block.header.signer_signature_hash();

stackslib/src/burnchains/bitcoin/indexer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -924,7 +924,7 @@ impl BitcoinIndexer {
924924
return Ok(());
925925
}
926926
warn!(
927-
"Header at height {} is not wihtin 2 hours of now (is at {})",
927+
"Header at height {} is not within 2 hours of now (is at {})",
928928
highest_header_height, highest_header.block_header.header.time
929929
);
930930
self.drop_headers(highest_header_height.saturating_sub(1))?;

stackslib/src/net/tests/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ impl NakamotoBootPlan {
355355
fn boot_nakamoto_peers<'a>(
356356
mut self,
357357
observer: Option<&'a TestEventObserver>,
358-
) -> (TestPeer<'a>, Vec<TestPeer>) {
358+
) -> (TestPeer<'a>, Vec<TestPeer<'a>>) {
359359
let mut peer_config = TestPeerConfig::new(&self.test_name, 0, 0);
360360
peer_config.network_id = self.network_id;
361361
peer_config.private_key = self.private_key.clone();
@@ -666,7 +666,7 @@ impl NakamotoBootPlan {
666666
self,
667667
boot_plan: Vec<NakamotoBootTenure>,
668668
observer: Option<&'a TestEventObserver>,
669-
) -> (TestPeer<'a>, Vec<TestPeer>) {
669+
) -> (TestPeer<'a>, Vec<TestPeer<'a>>) {
670670
let test_signers = self.test_signers.clone();
671671
let pox_constants = self.pox_constants.clone();
672672
let test_stackers = self.test_stackers.clone();

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,8 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
310310
}
311311

312312
/// Mine a BTC block and wait for a new Stacks block to be mined
313-
fn mine_nakamoto_block(&mut self, timeout: Duration) {
313+
/// Note: do not use nakamoto blocks mined heuristic if running a test with multiple miners
314+
fn mine_nakamoto_block(&mut self, timeout: Duration, use_nakamoto_blocks_mined: bool) {
314315
let commits_submitted = self.running_nodes.commits_submitted.clone();
315316
let mined_block_time = Instant::now();
316317
let mined_before = self.running_nodes.nakamoto_blocks_mined.get();
@@ -327,7 +328,7 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
327328
let info_after = self.get_peer_info();
328329
let blocks_mined = self.running_nodes.nakamoto_blocks_mined.get();
329330
Ok(info_after.stacks_tip_height > info_before.stacks_tip_height
330-
&& blocks_mined > mined_before)
331+
&& (!use_nakamoto_blocks_mined || blocks_mined > mined_before))
331332
})
332333
.unwrap();
333334
let mined_block_elapsed_time = mined_block_time.elapsed();

0 commit comments

Comments
 (0)