Skip to content

Commit d0a112f

Browse files
authored
Merge branch 'develop' into chore/depend-on-clarity-backing-store-interface
2 parents 74f779e + 14d439f commit d0a112f

File tree

10 files changed

+598
-263
lines changed

10 files changed

+598
-263
lines changed

contrib/tools/block-replay.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ configure_replay_slices() {
117117

118118
## create a copy of the linked db with <number of CORES><number of RESERVED CORES>
119119
## decrement by 1 since we already have ${SLICE_DIR}0
120-
for ((i=1;i<=$( CORES - RESERVED - 1);i++)); do
120+
for ((i=1;i<=$(( CORES - RESERVED - 1));i++)); do
121121
echo "Copying ${SLICE_DIR}0 -> ${COLYELLOW}${SLICE_DIR}${i}${COLRESET}"
122122
cp -R "${SLICE_DIR}0" "${SLICE_DIR}${i}" || {
123123
echo "${COLRED}Error${COLRESET} copying ${SLICE_DIR}0 -> ${SLICE_DIR}${i}"

libsigner/src/v0/messages.rs

+382-34
Large diffs are not rendered by default.

stacks-signer/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
1717
- Increase default `block_proposal_timeout_ms` from 10 minutes to 4 hours. Until #5729 is implemented, there is no value in rejecting a late block from a miner, since a late block is better than no block at all.
1818
- Signers no longer view any block proposal by a miner in their DB as indicative of valid miner activity.
1919
- Various index improvements to the signer's database to improve performance.
20+
- Add new reject codes to the signer response for better visibility into why a block was rejected.
2021

2122
## [3.1.0.0.5.0]
2223

stacks-signer/src/chainstate.rs

+36-23
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ 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 libsigner::v0::messages::RejectReason;
2223
use slog::{slog_info, slog_warn};
2324
use stacks_common::types::chainstate::{BurnchainHeaderHash, ConsensusHash, StacksPublicKey};
2425
use stacks_common::util::get_epoch_time_secs;
@@ -40,6 +41,12 @@ pub enum SignerChainstateError {
4041
ClientError(#[from] ClientError),
4142
}
4243

44+
impl From<SignerChainstateError> for RejectReason {
45+
fn from(error: SignerChainstateError) -> Self {
46+
RejectReason::ConnectivityIssues(error.to_string())
47+
}
48+
}
49+
4350
/// Captures this signer's current view of a sortition's miner.
4451
#[derive(PartialEq, Eq, Debug)]
4552
pub enum SortitionMinerStatus {
@@ -201,7 +208,7 @@ impl SortitionsView {
201208
block: &NakamotoBlock,
202209
block_pk: &StacksPublicKey,
203210
reset_view_if_wrong_consensus_hash: bool,
204-
) -> Result<bool, SignerChainstateError> {
211+
) -> Result<(), RejectReason> {
205212
if self
206213
.cur_sortition
207214
.is_timed_out(self.config.block_proposal_timeout, signer_db)?
@@ -213,7 +220,10 @@ impl SortitionsView {
213220
"current_sortition_consensus_hash" => ?self.cur_sortition.consensus_hash,
214221
);
215222
self.cur_sortition.miner_status = SortitionMinerStatus::InvalidatedBeforeFirstBlock;
216-
} else if let Some(tip) = signer_db.get_canonical_tip()? {
223+
} else if let Some(tip) = signer_db
224+
.get_canonical_tip()
225+
.map_err(SignerChainstateError::from)?
226+
{
217227
// Check if the current sortition is aligned with the expected tenure:
218228
// - If the tip is in the current tenure, we are in the process of mining this tenure.
219229
// - If the tip is not in the current tenure, then we’re starting a new tenure,
@@ -266,7 +276,7 @@ impl SortitionsView {
266276
"current_sortition_consensus_hash" => ?self.cur_sortition.consensus_hash,
267277
"last_sortition_consensus_hash" => ?self.last_sortition.as_ref().map(|x| x.consensus_hash),
268278
);
269-
return Ok(false);
279+
return Err(RejectReason::InvalidBitvec);
270280
}
271281

272282
let block_pkh = Hash160::from_data(&block_pk.to_bytes_compressed());
@@ -293,7 +303,8 @@ impl SortitionsView {
293303
"current_sortition_consensus_hash" => ?self.cur_sortition.consensus_hash,
294304
"last_sortition_consensus_hash" => ?self.last_sortition.as_ref().map(|x| x.consensus_hash),
295305
);
296-
self.reset_view(client)?;
306+
self.reset_view(client)
307+
.map_err(SignerChainstateError::from)?;
297308
return self.check_proposal(client, signer_db, block, block_pk, false);
298309
}
299310
warn!(
@@ -303,7 +314,7 @@ impl SortitionsView {
303314
"current_sortition_consensus_hash" => ?self.cur_sortition.consensus_hash,
304315
"last_sortition_consensus_hash" => ?self.last_sortition.as_ref().map(|x| x.consensus_hash),
305316
);
306-
return Ok(false);
317+
return Err(RejectReason::SortitionViewMismatch);
307318
};
308319

309320
if proposed_by.state().miner_pkh != block_pkh {
@@ -315,7 +326,7 @@ impl SortitionsView {
315326
"proposed_block_pubkey_hash" => %block_pkh,
316327
"sortition_winner_pubkey_hash" => %proposed_by.state().miner_pkh,
317328
);
318-
return Ok(false);
329+
return Err(RejectReason::PubkeyHashMismatch);
319330
}
320331

321332
// check that this miner is the most recent sortition
@@ -327,7 +338,7 @@ impl SortitionsView {
327338
"proposed_block_consensus_hash" => %block.header.consensus_hash,
328339
"proposed_block_signer_sighash" => %block.header.signer_signature_hash(),
329340
);
330-
return Ok(false);
341+
return Err(RejectReason::InvalidMiner);
331342
}
332343
}
333344
ProposedBy::LastSortition(last_sortition) => {
@@ -343,27 +354,26 @@ impl SortitionsView {
343354
"current_sortition_miner_status" => ?self.cur_sortition.miner_status,
344355
"last_sortition" => %last_sortition.consensus_hash
345356
);
346-
return Ok(false);
357+
return Err(RejectReason::NotLatestSortitionWinner);
347358
}
348359
}
349360
};
350361

351362
if let Some(tenure_change) = block.get_tenure_change_tx_payload() {
352-
if !self.validate_tenure_change_payload(
363+
self.validate_tenure_change_payload(
353364
&proposed_by,
354365
tenure_change,
355366
block,
356367
signer_db,
357368
client,
358-
)? {
359-
return Ok(false);
360-
}
369+
)?;
361370
} else {
362371
// check if the new block confirms the last block in the current tenure
363372
let confirms_latest_in_tenure =
364-
Self::confirms_latest_block_in_same_tenure(block, signer_db)?;
373+
Self::confirms_latest_block_in_same_tenure(block, signer_db)
374+
.map_err(SignerChainstateError::from)?;
365375
if !confirms_latest_in_tenure {
366-
return Ok(false);
376+
return Err(RejectReason::InvalidParentBlock);
367377
}
368378
}
369379

@@ -389,11 +399,11 @@ impl SortitionsView {
389399
"extend_timestamp" => extend_timestamp,
390400
"epoch_time" => epoch_time,
391401
);
392-
return Ok(false);
402+
return Err(RejectReason::InvalidTenureExtend);
393403
}
394404
}
395405

396-
Ok(true)
406+
Ok(())
397407
}
398408

399409
fn check_parent_tenure_choice(
@@ -649,7 +659,7 @@ impl SortitionsView {
649659
block: &NakamotoBlock,
650660
signer_db: &mut SignerDb,
651661
client: &StacksClient,
652-
) -> Result<bool, SignerChainstateError> {
662+
) -> Result<(), RejectReason> {
653663
// Ensure that the tenure change block confirms the expected parent block
654664
let confirms_expected_parent = Self::check_tenure_change_confirms_parent(
655665
tenure_change,
@@ -658,9 +668,10 @@ impl SortitionsView {
658668
client,
659669
self.config.tenure_last_block_proposal_timeout,
660670
self.config.reorg_attempts_activity_timeout,
661-
)?;
671+
)
672+
.map_err(SignerChainstateError::from)?;
662673
if !confirms_expected_parent {
663-
return Ok(false);
674+
return Err(RejectReason::InvalidParentBlock);
664675
}
665676
// now, we have to check if the parent tenure was a valid choice.
666677
let is_valid_parent_tenure = Self::check_parent_tenure_choice(
@@ -671,21 +682,23 @@ impl SortitionsView {
671682
&self.config.first_proposal_burn_block_timing,
672683
)?;
673684
if !is_valid_parent_tenure {
674-
return Ok(false);
685+
return Err(RejectReason::ReorgNotAllowed);
675686
}
676687
let last_in_current_tenure = signer_db
677688
.get_last_globally_accepted_block(&block.header.consensus_hash)
678-
.map_err(|e| ClientError::InvalidResponse(e.to_string()))?;
689+
.map_err(|e| {
690+
SignerChainstateError::from(ClientError::InvalidResponse(e.to_string()))
691+
})?;
679692
if let Some(last_in_current_tenure) = last_in_current_tenure {
680693
warn!(
681694
"Miner block proposal contains a tenure change, but we've already signed a block in this tenure. Considering proposal invalid.";
682695
"proposed_block_consensus_hash" => %block.header.consensus_hash,
683696
"proposed_block_signer_sighash" => %block.header.signer_signature_hash(),
684697
"last_in_tenure_signer_sighash" => %last_in_current_tenure.block.header.signer_signature_hash(),
685698
);
686-
return Ok(false);
699+
return Err(RejectReason::DuplicateBlockFound);
687700
}
688-
Ok(true)
701+
Ok(())
689702
}
690703

691704
fn confirms_latest_block_in_same_tenure(

stacks-signer/src/client/stackerdb.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ mod tests {
281281
use clarity::util::hash::{MerkleTree, Sha512Trunc256Sum};
282282
use clarity::util::secp256k1::MessageSignature;
283283
use libsigner::v0::messages::{
284-
BlockRejection, BlockResponse, BlockResponseData, RejectCode, SignerMessage,
284+
BlockRejection, BlockResponse, BlockResponseData, RejectCode, RejectReason, SignerMessage,
285285
SignerMessageMetadata,
286286
};
287287
use rand::{thread_rng, RngCore};
@@ -332,7 +332,10 @@ mod tests {
332332
chain_id: thread_rng().next_u32(),
333333
signature: MessageSignature::empty(),
334334
metadata: SignerMessageMetadata::empty(),
335-
response_data: BlockResponseData::new(thread_rng().next_u64()),
335+
response_data: BlockResponseData::new(
336+
thread_rng().next_u64(),
337+
RejectReason::RejectedInPriorRound,
338+
),
336339
};
337340
let signer_message = SignerMessage::BlockResponse(BlockResponse::Rejected(block_reject));
338341
let ack = StackerDBChunkAckData {

0 commit comments

Comments
 (0)