Skip to content

Commit 2a0217a

Browse files
authored
Merge pull request #5549 from stacks-network/feat/ignore-old-proposals-signer
[signer] Add block_proposal_max_age_secs signer configuration to drop old proposals without further processing
2 parents b108d09 + 016a63f commit 2a0217a

File tree

9 files changed

+185
-32
lines changed

9 files changed

+185
-32
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

stacks-signer/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ 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
1315

1416
## [3.1.0.0.1.0]

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: 1 addition & 0 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

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();

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)