Skip to content

Commit 0ea23b0

Browse files
committed
fix: allow signing of a competing proposal after tenure_last_block_proposal_timeout
1 parent ccb8746 commit 0ea23b0

2 files changed

Lines changed: 27 additions & 6 deletions

File tree

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
Accept a replacement tenure-start block when the previous proposal in the tenure was only pre-committed (never signed), instead of rejecting it with `DuplicateBlockFound`. This prevents a stall that occasionally shows up on mainnet.
2-
Refuse to sign a block at the pre-commit threshold if a different block at the same or higher height in the tenure has already been signed.
2+
Refuse to sign a block at the pre-commit threshold if a different block at the same or higher height in the tenure has been signed within `tenure_last_block_proposal_timeout`. After that timeout, the conflicting signed block is considered to have failed to reach consensus and the replacement may be signed.

stacks-signer/src/v0/signer.rs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,8 +1158,8 @@ impl Signer {
11581158
}
11591159
// A pre-commit may be superseded by a competing proposal at the same height (e.g. a
11601160
// re-proposed tenure-start block after the first failed to reach consensus), but a
1161-
// signature must never be. Refuse to sign if we have already signed a different block
1162-
// at this height or above in this tenure.
1161+
// signature must not be while it is still fresh. Refuse to sign if we have recently
1162+
// signed a different block at this height or above in this tenure.
11631163
let last_signed = match self
11641164
.signer_db
11651165
.get_last_signed_block(&block_info.block.header.consensus_hash)
@@ -1174,14 +1174,35 @@ impl Signer {
11741174
if last_signed.block.header.chain_length >= block_info.block.header.chain_length
11751175
&& last_signed.block.header.signer_signature_hash() != block_hash
11761176
{
1177-
warn!(
1178-
"{self}: Reached the pre-commit threshold for a block, but we have already signed a different block at the same or higher height in this tenure. Refusing to sign.";
1177+
// Only refuse while our signature on the conflicting block is fresh. If
1178+
// `tenure_last_block_proposal_timeout` has passed without that block reaching
1179+
// consensus, proposal evaluation already allows blocks that do not confirm it,
1180+
// so allow signing the replacement too -- otherwise the tenure stalls until the
1181+
// next sortition.
1182+
let signature_is_fresh = last_signed.signed_self.is_some_and(|signed_time| {
1183+
signed_time.saturating_add(
1184+
self.proposal_config
1185+
.tenure_last_block_proposal_timeout
1186+
.as_secs(),
1187+
) > get_epoch_time_secs()
1188+
});
1189+
if signature_is_fresh {
1190+
warn!(
1191+
"{self}: Reached the pre-commit threshold for a block, but we have already signed a different block at the same or higher height in this tenure. Refusing to sign.";
1192+
"signer_signature_hash" => %block_hash,
1193+
"block_height" => block_info.block.header.chain_length,
1194+
"signed_signer_signature_hash" => %last_signed.block.header.signer_signature_hash(),
1195+
"signed_block_height" => last_signed.block.header.chain_length,
1196+
);
1197+
return;
1198+
}
1199+
info!(
1200+
"{self}: Reached the pre-commit threshold for a block that conflicts with a previously signed block, but that block has timed out without reaching consensus. Signing the replacement.";
11791201
"signer_signature_hash" => %block_hash,
11801202
"block_height" => block_info.block.header.chain_length,
11811203
"signed_signer_signature_hash" => %last_signed.block.header.signer_signature_hash(),
11821204
"signed_block_height" => last_signed.block.header.chain_length,
11831205
);
1184-
return;
11851206
}
11861207
}
11871208
// It is only considered globally accepted IFF we receive a new block event confirming it OR see the chain tip of the node advance to it.

0 commit comments

Comments
 (0)