@@ -589,60 +589,80 @@ impl Signer {
589
589
}
590
590
}
591
591
592
- /// WARNING: Do NOT call this function PRIOR to check_proposal or block_proposal validation succeeds.
592
+ /// WARNING: This is an incomplete check. Do NOT call this function PRIOR to check_proposal or block_proposal validation succeeds.
593
593
///
594
594
/// Re-verify a block's chain length against the last signed block within signerdb.
595
595
/// This is required in case a block has been approved since the initial checks of the block validation endpoint.
596
596
fn check_block_against_signer_db_state (
597
- & self ,
597
+ & mut self ,
598
+ stacks_client : & StacksClient ,
598
599
proposed_block : & NakamotoBlock ,
599
600
) -> Option < BlockResponse > {
600
601
let signer_signature_hash = proposed_block. header . signer_signature_hash ( ) ;
601
602
let proposed_block_consensus_hash = proposed_block. header . consensus_hash ;
603
+ // If this is a tenure change block, ensure that it confirms the correct number of blocks from the parent tenure.
604
+ if let Some ( tenure_change) = proposed_block. get_tenure_change_tx_payload ( ) {
605
+ // Ensure that the tenure change block confirms the expected parent block
606
+ match SortitionsView :: check_tenure_change_confirms_parent (
607
+ tenure_change,
608
+ proposed_block,
609
+ & mut self . signer_db ,
610
+ stacks_client,
611
+ self . proposal_config . tenure_last_block_proposal_timeout ,
612
+ ) {
613
+ Ok ( true ) => { }
614
+ Ok ( false ) => {
615
+ return Some (
616
+ self . create_block_rejection (
617
+ RejectCode :: SortitionViewMismatch ,
618
+ proposed_block,
619
+ ) ,
620
+ )
621
+ }
622
+ Err ( e) => {
623
+ warn ! ( "{self}: Error checking block proposal: {e}" ;
624
+ "signer_sighash" => %signer_signature_hash,
625
+ "block_id" => %proposed_block. block_id( )
626
+ ) ;
627
+ return Some (
628
+ self . create_block_rejection ( RejectCode :: ConnectivityIssues , proposed_block) ,
629
+ ) ;
630
+ }
631
+ }
632
+ }
602
633
603
- match self . signer_db . get_signer_last_accepted_block ( ) {
634
+ // Ensure that the block is the last block in the chain of its current tenure.
635
+ match self
636
+ . signer_db
637
+ . get_last_accepted_block ( & proposed_block_consensus_hash)
638
+ {
604
639
Ok ( Some ( last_block_info) ) => {
605
640
if proposed_block. header . chain_length <= last_block_info. block . header . chain_length {
606
- // We do not allow reorgs at any time within the same consensus hash OR of globally accepted blocks
607
- let non_reorgable_block = last_block_info. block . header . consensus_hash
608
- == proposed_block_consensus_hash
609
- || last_block_info. state == BlockState :: GloballyAccepted ;
610
- // Is the reorg timeout requirement exceeded?
611
- let reorg_timeout_exceeded = last_block_info
612
- . signed_self
613
- . map ( |signed_over_time| {
614
- signed_over_time. saturating_add (
615
- self . proposal_config
616
- . tenure_last_block_proposal_timeout
617
- . as_secs ( ) ,
618
- ) <= get_epoch_time_secs ( )
619
- } )
620
- . unwrap_or ( false ) ;
621
- if non_reorgable_block || !reorg_timeout_exceeded {
622
- warn ! (
623
- "Miner's block proposal does not confirm as many blocks as we expect" ;
624
- "proposed_block_consensus_hash" => %proposed_block_consensus_hash,
625
- "proposed_block_signer_sighash" => %signer_signature_hash,
626
- "proposed_chain_length" => proposed_block. header. chain_length,
627
- "expected_at_least" => last_block_info. block. header. chain_length + 1 ,
628
- ) ;
629
- return Some ( self . create_block_rejection (
630
- RejectCode :: SortitionViewMismatch ,
631
- proposed_block,
632
- ) ) ;
633
- }
641
+ warn ! (
642
+ "Miner's block proposal does not confirm as many blocks as we expect" ;
643
+ "proposed_block_consensus_hash" => %proposed_block_consensus_hash,
644
+ "proposed_block_signer_sighash" => %signer_signature_hash,
645
+ "proposed_chain_length" => proposed_block. header. chain_length,
646
+ "expected_at_least" => last_block_info. block. header. chain_length + 1 ,
647
+ ) ;
648
+ return Some ( self . create_block_rejection (
649
+ RejectCode :: SortitionViewMismatch ,
650
+ proposed_block,
651
+ ) ) ;
634
652
}
635
- None
636
653
}
637
- Ok ( _) => None ,
654
+ Ok ( _) => { }
638
655
Err ( e) => {
639
656
warn ! ( "{self}: Failed to check block against signer db: {e}" ;
640
657
"signer_sighash" => %signer_signature_hash,
641
658
"block_id" => %proposed_block. block_id( )
642
659
) ;
643
- Some ( self . create_block_rejection ( RejectCode :: ConnectivityIssues , proposed_block) )
660
+ return Some (
661
+ self . create_block_rejection ( RejectCode :: ConnectivityIssues , proposed_block) ,
662
+ ) ;
644
663
}
645
664
}
665
+ None
646
666
}
647
667
648
668
/// Handle the block validate ok response. Returns our block response if we have one
@@ -674,7 +694,9 @@ impl Signer {
674
694
return None ;
675
695
}
676
696
677
- if let Some ( block_response) = self . check_block_against_signer_db_state ( & block_info. block ) {
697
+ if let Some ( block_response) =
698
+ self . check_block_against_signer_db_state ( stacks_client, & block_info. block )
699
+ {
678
700
// The signer db state has changed. We no longer view this block as valid. Override the validation response.
679
701
if let Err ( e) = block_info. mark_locally_rejected ( ) {
680
702
if !block_info. has_reached_consensus ( ) {
0 commit comments