@@ -19,7 +19,7 @@ use std::time::{Duration, Instant};
19
19
20
20
use blockstack_lib:: chainstate:: nakamoto:: { NakamotoBlock , NakamotoBlockHeader } ;
21
21
use blockstack_lib:: net:: api:: postblock_proposal:: {
22
- BlockValidateOk , BlockValidateReject , BlockValidateResponse ,
22
+ BlockValidateOk , BlockValidateReject , BlockValidateResponse , TOO_MANY_REQUESTS_STATUS ,
23
23
} ;
24
24
use clarity:: types:: chainstate:: StacksPrivateKey ;
25
25
use clarity:: types:: { PrivateKey , StacksEpochId } ;
@@ -33,11 +33,12 @@ use libsigner::{BlockProposal, SignerEvent};
33
33
use slog:: { slog_debug, slog_error, slog_info, slog_warn} ;
34
34
use stacks_common:: types:: chainstate:: StacksAddress ;
35
35
use stacks_common:: util:: get_epoch_time_secs;
36
+ use stacks_common:: util:: hash:: Sha512Trunc256Sum ;
36
37
use stacks_common:: util:: secp256k1:: MessageSignature ;
37
38
use stacks_common:: { debug, error, info, warn} ;
38
39
39
40
use crate :: chainstate:: { ProposalEvalConfig , SortitionsView } ;
40
- use crate :: client:: { SignerSlotID , StackerDB , StacksClient } ;
41
+ use crate :: client:: { ClientError , SignerSlotID , StackerDB , StacksClient } ;
41
42
use crate :: config:: SignerConfig ;
42
43
use crate :: runloop:: SignerResult ;
43
44
use crate :: signerdb:: { BlockInfo , BlockState , SignerDb } ;
@@ -90,7 +91,7 @@ pub struct Signer {
90
91
/// marking a submitted block as invalid
91
92
pub block_proposal_validation_timeout : Duration ,
92
93
/// The current submitted block proposal and its submission time
93
- pub submitted_block_proposal : Option < ( BlockProposal , Instant ) > ,
94
+ pub submitted_block_proposal : Option < ( Sha512Trunc256Sum , Instant ) > ,
94
95
}
95
96
96
97
impl std:: fmt:: Display for Signer {
@@ -476,15 +477,8 @@ impl Signer {
476
477
"block_height" => block_proposal. block. header. chain_length,
477
478
"burn_height" => block_proposal. burn_height,
478
479
) ;
479
- match stacks_client. submit_block_for_validation ( block_info. block . clone ( ) ) {
480
- Ok ( _) => {
481
- self . submitted_block_proposal =
482
- Some ( ( block_proposal. clone ( ) , Instant :: now ( ) ) ) ;
483
- }
484
- Err ( e) => {
485
- warn ! ( "{self}: Failed to submit block for validation: {e:?}" ) ;
486
- }
487
- } ;
480
+
481
+ self . submit_block_for_validation ( stacks_client, block_proposal. block . clone ( ) ) ;
488
482
} else {
489
483
// Still store the block but log we can't submit it for validation. We may receive enough signatures/rejections
490
484
// from other signers to push the proposed block into a global rejection/acceptance regardless of our participation.
@@ -509,12 +503,44 @@ impl Signer {
509
503
match block_response {
510
504
BlockResponse :: Accepted ( accepted) => {
511
505
self . handle_block_signature ( stacks_client, accepted) ;
506
+ accepted. signer_signature_hash
512
507
}
513
508
BlockResponse :: Rejected ( block_rejection) => {
514
509
self . handle_block_rejection ( block_rejection) ;
510
+ block_rejection. signer_signature_hash
515
511
}
512
+ } ;
513
+
514
+ // Remove this block validation from the pending table
515
+ let signer_sig_hash = block_response. signer_signature_hash ( ) ;
516
+ self . signer_db
517
+ . remove_pending_block_validation ( & signer_sig_hash)
518
+ . unwrap_or_else ( |e| warn ! ( "{self}: Failed to remove pending block validation: {e:?}" ) ) ;
519
+
520
+ match self . signer_db . get_pending_block_validation ( ) {
521
+ Ok ( Some ( signer_sig_hash) ) => {
522
+ info ! ( "{self}: Found a pending block validation: {signer_sig_hash:?}" ) ;
523
+ match self
524
+ . signer_db
525
+ . block_lookup ( self . reward_cycle , & signer_sig_hash)
526
+ {
527
+ Ok ( Some ( block_info) ) => {
528
+ self . submit_block_for_validation ( stacks_client, block_info. block ) ;
529
+ }
530
+ Ok ( None ) => {
531
+ // This should never happen
532
+ error ! (
533
+ "{self}: Pending block validation not found in DB: {signer_sig_hash:?}"
534
+ ) ;
535
+ }
536
+ Err ( e) => error ! ( "{self}: Failed to get block info: {e:?}" ) ,
537
+ }
538
+ }
539
+ Ok ( None ) => { }
540
+ Err ( e) => warn ! ( "{self}: Failed to get pending block validation: {e:?}" ) ,
516
541
}
517
542
}
543
+
518
544
/// Handle the block validate ok response. Returns our block response if we have one
519
545
fn handle_block_validate_ok (
520
546
& mut self ,
@@ -525,10 +551,7 @@ impl Signer {
525
551
let signer_signature_hash = block_validate_ok. signer_signature_hash ;
526
552
if self
527
553
. submitted_block_proposal
528
- . as_ref ( )
529
- . map ( |( proposal, _) | {
530
- proposal. block . header . signer_signature_hash ( ) == signer_signature_hash
531
- } )
554
+ . map ( |( proposal_hash, _) | proposal_hash == signer_signature_hash)
532
555
. unwrap_or ( false )
533
556
{
534
557
self . submitted_block_proposal = None ;
@@ -584,10 +607,7 @@ impl Signer {
584
607
let signer_signature_hash = block_validate_reject. signer_signature_hash ;
585
608
if self
586
609
. submitted_block_proposal
587
- . as_ref ( )
588
- . map ( |( proposal, _) | {
589
- proposal. block . header . signer_signature_hash ( ) == signer_signature_hash
590
- } )
610
+ . map ( |( proposal_hash, _) | proposal_hash == signer_signature_hash)
591
611
. unwrap_or ( false )
592
612
{
593
613
self . submitted_block_proposal = None ;
@@ -670,20 +690,21 @@ impl Signer {
670
690
/// Check the current tracked submitted block proposal to see if it has timed out.
671
691
/// Broadcasts a rejection and marks the block locally rejected if it has.
672
692
fn check_submitted_block_proposal ( & mut self ) {
673
- let Some ( ( block_proposal, block_submission) ) = self . submitted_block_proposal . take ( ) else {
693
+ let Some ( ( proposal_signer_sighash, block_submission) ) =
694
+ self . submitted_block_proposal . take ( )
695
+ else {
674
696
// Nothing to check.
675
697
return ;
676
698
} ;
677
699
if block_submission. elapsed ( ) < self . block_proposal_validation_timeout {
678
700
// Not expired yet. Put it back!
679
- self . submitted_block_proposal = Some ( ( block_proposal , block_submission) ) ;
701
+ self . submitted_block_proposal = Some ( ( proposal_signer_sighash , block_submission) ) ;
680
702
return ;
681
703
}
682
- let signature_sighash = block_proposal. block . header . signer_signature_hash ( ) ;
683
704
// For mutability reasons, we need to take the block_info out of the map and add it back after processing
684
705
let mut block_info = match self
685
706
. signer_db
686
- . block_lookup ( self . reward_cycle , & signature_sighash )
707
+ . block_lookup ( self . reward_cycle , & proposal_signer_sighash )
687
708
{
688
709
Ok ( Some ( block_info) ) => {
689
710
if block_info. state == BlockState :: GloballyRejected
@@ -698,8 +719,7 @@ impl Signer {
698
719
// This is weird. If this is reached, its probably an error in code logic or the db was flushed.
699
720
// Why are we tracking a block submission for a block we have never seen / stored before.
700
721
error ! ( "{self}: tracking an unknown block validation submission." ;
701
- "signer_sighash" => %signature_sighash,
702
- "block_id" => %block_proposal. block. block_id( ) ,
722
+ "signer_sighash" => %proposal_signer_sighash,
703
723
) ;
704
724
return ;
705
725
}
@@ -712,11 +732,10 @@ impl Signer {
712
732
// Reject it so we aren't holding up the network because of our inaction.
713
733
warn ! (
714
734
"{self}: Failed to receive block validation response within {} ms. Rejecting block." , self . block_proposal_validation_timeout. as_millis( ) ;
715
- "signer_sighash" => %signature_sighash,
716
- "block_id" => %block_proposal. block. block_id( ) ,
735
+ "signer_sighash" => %proposal_signer_sighash,
717
736
) ;
718
737
let rejection = BlockResponse :: rejected (
719
- block_proposal . block . header . signer_signature_hash ( ) ,
738
+ proposal_signer_sighash ,
720
739
RejectCode :: ConnectivityIssues ,
721
740
& self . private_key ,
722
741
self . mainnet ,
@@ -851,7 +870,7 @@ impl Signer {
851
870
if self
852
871
. submitted_block_proposal
853
872
. as_ref ( )
854
- . map ( |( proposal , _) | & proposal . block . header . signer_signature_hash ( ) == block_hash)
873
+ . map ( |( proposal_signer_sighash , _) | proposal_signer_sighash == block_hash)
855
874
. unwrap_or ( false )
856
875
{
857
876
// Consensus reached! No longer bother tracking its validation submission to the node as we are too late to participate in the decision anyway.
@@ -1002,7 +1021,7 @@ impl Signer {
1002
1021
if self
1003
1022
. submitted_block_proposal
1004
1023
. as_ref ( )
1005
- . map ( |( proposal , _) | & proposal . block . header . signer_signature_hash ( ) == block_hash)
1024
+ . map ( |( proposal_hash , _) | proposal_hash == block_hash)
1006
1025
. unwrap_or ( false )
1007
1026
{
1008
1027
// Consensus reached! No longer bother tracking its validation submission to the node as we are too late to participate in the decision anyway.
@@ -1046,6 +1065,36 @@ impl Signer {
1046
1065
}
1047
1066
}
1048
1067
1068
+ /// Submit a block for validation, and mark it as pending if the node
1069
+ fn submit_block_for_validation ( & mut self , stacks_client : & StacksClient , block : NakamotoBlock ) {
1070
+ let signer_signature_hash = block. header . signer_signature_hash ( ) ;
1071
+ match stacks_client. submit_block_for_validation ( block. clone ( ) ) {
1072
+ Ok ( _) => {
1073
+ self . submitted_block_proposal = Some ( ( signer_signature_hash, Instant :: now ( ) ) ) ;
1074
+ }
1075
+ Err ( ClientError :: RequestFailure ( status) ) => {
1076
+ if status. as_u16 ( ) == TOO_MANY_REQUESTS_STATUS {
1077
+ info ! ( "{self}: Received 429 from stacks node. Inserting pending block validation..." ;
1078
+ "signer_signature_hash" => %signer_signature_hash,
1079
+ ) ;
1080
+ self . signer_db
1081
+ . insert_pending_block_validation (
1082
+ & signer_signature_hash,
1083
+ get_epoch_time_secs ( ) ,
1084
+ )
1085
+ . unwrap_or_else ( |e| {
1086
+ warn ! ( "{self}: Failed to insert pending block validation: {e:?}" )
1087
+ } ) ;
1088
+ } else {
1089
+ warn ! ( "{self}: Received non-429 status from stacks node: {status}" ) ;
1090
+ }
1091
+ }
1092
+ Err ( e) => {
1093
+ warn ! ( "{self}: Failed to submit block for validation: {e:?}" ) ;
1094
+ }
1095
+ }
1096
+ }
1097
+
1049
1098
#[ cfg( any( test, feature = "testing" ) ) ]
1050
1099
fn test_skip_block_broadcast ( & self , block : & NakamotoBlock ) -> bool {
1051
1100
if * TEST_SKIP_BLOCK_BROADCAST . lock ( ) . unwrap ( ) == Some ( true ) {
0 commit comments