@@ -28,7 +28,7 @@ use clarity::util::hash::{MerkleHashFunc, Sha512Trunc256Sum};
28
28
use clarity:: util:: secp256k1:: Secp256k1PublicKey ;
29
29
use libsigner:: v0:: messages:: {
30
30
BlockAccepted , BlockRejection , BlockResponse , MessageSlotID , MockProposal , MockSignature ,
31
- RejectReason , SignerMessage ,
31
+ RejectReason , RejectReasonPrefix , SignerMessage ,
32
32
} ;
33
33
use libsigner:: { BlockProposal , SignerEvent } ;
34
34
use slog:: { slog_debug, slog_error, slog_info, slog_warn} ;
@@ -37,7 +37,7 @@ use stacks_common::util::get_epoch_time_secs;
37
37
use stacks_common:: util:: secp256k1:: MessageSignature ;
38
38
use stacks_common:: { debug, error, info, warn} ;
39
39
40
- use crate :: chainstate:: { ProposalEvalConfig , SortitionsView } ;
40
+ use crate :: chainstate:: { ProposalEvalConfig , SortitionMinerStatus , SortitionsView } ;
41
41
use crate :: client:: { ClientError , SignerSlotID , StackerDB , StacksClient } ;
42
42
use crate :: config:: { SignerConfig , SignerConfigMode } ;
43
43
use crate :: runloop:: SignerResult ;
@@ -165,7 +165,11 @@ impl SignerTrait<SignerMessage> for Signer {
165
165
match event {
166
166
SignerEvent :: BlockValidationResponse ( block_validate_response) => {
167
167
debug ! ( "{self}: Received a block proposal result from the stacks node..." ) ;
168
- self . handle_block_validate_response ( stacks_client, block_validate_response)
168
+ self . handle_block_validate_response (
169
+ stacks_client,
170
+ block_validate_response,
171
+ sortition_state,
172
+ )
169
173
}
170
174
SignerEvent :: SignerMessages ( _signer_set, messages) => {
171
175
debug ! (
@@ -177,7 +181,7 @@ impl SignerTrait<SignerMessage> for Signer {
177
181
let SignerMessage :: BlockResponse ( block_response) = message else {
178
182
continue ;
179
183
} ;
180
- self . handle_block_response ( stacks_client, block_response) ;
184
+ self . handle_block_response ( stacks_client, block_response, sortition_state ) ;
181
185
}
182
186
}
183
187
SignerEvent :: MinerMessages ( messages, miner_pubkey) => {
@@ -618,13 +622,14 @@ impl Signer {
618
622
& mut self ,
619
623
stacks_client : & StacksClient ,
620
624
block_response : & BlockResponse ,
625
+ sortition_state : & mut Option < SortitionsView > ,
621
626
) {
622
627
match block_response {
623
628
BlockResponse :: Accepted ( accepted) => {
624
629
self . handle_block_signature ( stacks_client, accepted) ;
625
630
}
626
631
BlockResponse :: Rejected ( block_rejection) => {
627
- self . handle_block_rejection ( block_rejection) ;
632
+ self . handle_block_rejection ( block_rejection, sortition_state ) ;
628
633
}
629
634
} ;
630
635
}
@@ -793,6 +798,7 @@ impl Signer {
793
798
fn handle_block_validate_reject (
794
799
& mut self ,
795
800
block_validate_reject : & BlockValidateReject ,
801
+ sortition_state : & mut Option < SortitionsView > ,
796
802
) -> Option < BlockResponse > {
797
803
crate :: monitoring:: actions:: increment_block_validation_responses ( false ) ;
798
804
let signer_signature_hash = block_validate_reject. signer_signature_hash ;
@@ -833,7 +839,7 @@ impl Signer {
833
839
self . signer_db
834
840
. insert_block ( & block_info)
835
841
. unwrap_or_else ( |e| self . handle_insert_block_error ( e) ) ;
836
- self . handle_block_rejection ( & block_rejection) ;
842
+ self . handle_block_rejection ( & block_rejection, sortition_state ) ;
837
843
Some ( BlockResponse :: Rejected ( block_rejection) )
838
844
}
839
845
@@ -842,6 +848,7 @@ impl Signer {
842
848
& mut self ,
843
849
stacks_client : & StacksClient ,
844
850
block_validate_response : & BlockValidateResponse ,
851
+ sortition_state : & mut Option < SortitionsView > ,
845
852
) {
846
853
info ! ( "{self}: Received a block validate response: {block_validate_response:?}" ) ;
847
854
let block_response = match block_validate_response {
@@ -852,7 +859,7 @@ impl Signer {
852
859
self . handle_block_validate_ok ( stacks_client, block_validate_ok)
853
860
}
854
861
BlockValidateResponse :: Reject ( block_validate_reject) => {
855
- self . handle_block_validate_reject ( block_validate_reject)
862
+ self . handle_block_validate_reject ( block_validate_reject, sortition_state )
856
863
}
857
864
} ;
858
865
// Remove this block validation from the pending table
@@ -994,6 +1001,21 @@ impl Signer {
994
1001
} )
995
1002
}
996
1003
1004
+ /// Compute the rejection weight for the given reject code, given a list of signatures
1005
+ fn compute_reject_code_signing_weight < ' a > (
1006
+ & self ,
1007
+ addrs : impl Iterator < Item = & ' a ( StacksAddress , RejectReasonPrefix ) > ,
1008
+ reject_code : RejectReasonPrefix ,
1009
+ ) -> u32 {
1010
+ addrs. filter ( |( _, code) | * code == reject_code) . fold (
1011
+ 0u32 ,
1012
+ |signing_weight, ( stacker_address, _) | {
1013
+ let stacker_weight = self . signer_weights . get ( stacker_address) . unwrap_or ( & 0 ) ;
1014
+ signing_weight. saturating_add ( * stacker_weight)
1015
+ } ,
1016
+ )
1017
+ }
1018
+
997
1019
/// Compute the total signing weight
998
1020
fn compute_signature_total_weight ( & self ) -> u32 {
999
1021
self . signer_weights
@@ -1002,7 +1024,11 @@ impl Signer {
1002
1024
}
1003
1025
1004
1026
/// Handle an observed rejection from another signer
1005
- fn handle_block_rejection ( & mut self , rejection : & BlockRejection ) {
1027
+ fn handle_block_rejection (
1028
+ & mut self ,
1029
+ rejection : & BlockRejection ,
1030
+ sortition_state : & mut Option < SortitionsView > ,
1031
+ ) {
1006
1032
debug ! ( "{self}: Received a block-reject signature: {rejection:?}" ) ;
1007
1033
1008
1034
let block_hash = & rejection. signer_signature_hash ;
@@ -1045,10 +1071,11 @@ impl Signer {
1045
1071
}
1046
1072
1047
1073
// signature is valid! store it
1048
- if let Err ( e) = self
1049
- . signer_db
1050
- . add_block_rejection_signer_addr ( block_hash, & signer_address)
1051
- {
1074
+ if let Err ( e) = self . signer_db . add_block_rejection_signer_addr (
1075
+ block_hash,
1076
+ & signer_address,
1077
+ & rejection. response_data . reject_reason ,
1078
+ ) {
1052
1079
warn ! ( "{self}: Failed to save block rejection signature: {e:?}" , ) ;
1053
1080
}
1054
1081
@@ -1061,7 +1088,8 @@ impl Signer {
1061
1088
return ;
1062
1089
}
1063
1090
} ;
1064
- let total_reject_weight = self . compute_signature_signing_weight ( rejection_addrs. iter ( ) ) ;
1091
+ let total_reject_weight =
1092
+ self . compute_signature_signing_weight ( rejection_addrs. iter ( ) . map ( |( addr, _) | addr) ) ;
1065
1093
let total_weight = self . compute_signature_total_weight ( ) ;
1066
1094
1067
1095
let min_weight = NakamotoBlockHeader :: compute_voting_weight_threshold ( total_weight)
@@ -1089,6 +1117,24 @@ impl Signer {
1089
1117
// Consensus reached! No longer bother tracking its validation submission to the node as we are too late to participate in the decision anyway.
1090
1118
self . submitted_block_proposal = None ;
1091
1119
}
1120
+
1121
+ // If 30% of the signers have rejected the block due to an invalid
1122
+ // reorg, mark the miner as invalid.
1123
+ let total_reorg_reject_weight = self . compute_reject_code_signing_weight (
1124
+ rejection_addrs. iter ( ) ,
1125
+ RejectReasonPrefix :: ReorgNotAllowed ,
1126
+ ) ;
1127
+ if total_reorg_reject_weight. saturating_add ( min_weight) > total_weight {
1128
+ // Mark the miner as invalid
1129
+ if let Some ( sortition_state) = sortition_state {
1130
+ let ch = block_info. block . header . consensus_hash ;
1131
+ if sortition_state. cur_sortition . consensus_hash == ch {
1132
+ info ! ( "{self}: Marking miner as invalid for attempted reorg" ) ;
1133
+ sortition_state. cur_sortition . miner_status =
1134
+ SortitionMinerStatus :: InvalidatedBeforeFirstBlock ;
1135
+ }
1136
+ }
1137
+ }
1092
1138
}
1093
1139
1094
1140
/// Handle an observed signature from another signer
0 commit comments