@@ -519,7 +519,13 @@ impl Signer {
519
519
SignerMessage :: StateMachineUpdate ( update) => self
520
520
. handle_state_machine_update ( signer_public_key, update, received_time) ,
521
521
SignerMessage :: BlockPreCommit ( signer_signature_hash) => {
522
- todo ! ( "Unable to handle block precommit message from {signer_public_key:?}: {signer_signature_hash}" ) ;
522
+ let stacker_address =
523
+ StacksAddress :: p2pkh ( self . mainnet , signer_public_key) ;
524
+ self . handle_block_pre_commit (
525
+ stacks_client,
526
+ & stacker_address,
527
+ signer_signature_hash,
528
+ )
523
529
}
524
530
_ => { }
525
531
}
@@ -869,13 +875,9 @@ impl Signer {
869
875
870
876
/// The actual `send_block_response` implementation. Declared so that we do
871
877
/// not need to duplicate in testing.
872
- fn impl_send_block_response (
873
- & mut self ,
874
- block : Option < & NakamotoBlock > ,
875
- block_response : BlockResponse ,
876
- ) {
878
+ fn impl_send_block_response ( & mut self , block : & NakamotoBlock , block_response : BlockResponse ) {
877
879
info ! (
878
- "{self}: Broadcasting a block response to stacks node: {block_response:?}" ;
880
+ "{self}: Broadcasting block response to stacks node: {block_response:?}" ;
879
881
) ;
880
882
let accepted = matches ! ( block_response, BlockResponse :: Accepted ( ..) ) ;
881
883
match self
@@ -890,9 +892,7 @@ impl Signer {
890
892
) ;
891
893
}
892
894
crate :: monitoring:: actions:: increment_block_responses_sent ( accepted) ;
893
- if let Some ( block) = block {
894
- crate :: monitoring:: actions:: record_block_response_latency ( block) ;
895
- }
895
+ crate :: monitoring:: actions:: record_block_response_latency ( block) ;
896
896
}
897
897
Err ( e) => {
898
898
warn ! ( "{self}: Failed to send block response to stacker-db: {e:?}" , ) ;
@@ -901,11 +901,7 @@ impl Signer {
901
901
}
902
902
903
903
#[ cfg( any( test, feature = "testing" ) ) ]
904
- fn send_block_response (
905
- & mut self ,
906
- block : Option < & NakamotoBlock > ,
907
- block_response : BlockResponse ,
908
- ) {
904
+ fn send_block_response ( & mut self , block : & NakamotoBlock , block_response : BlockResponse ) {
909
905
const NUM_REPEATS : usize = 1 ;
910
906
let mut count = 0 ;
911
907
let public_keys = TEST_REPEAT_PROPOSAL_RESPONSE . get ( ) ;
@@ -923,22 +919,44 @@ impl Signer {
923
919
}
924
920
925
921
#[ cfg( not( any( test, feature = "testing" ) ) ) ]
926
- fn send_block_response (
927
- & mut self ,
928
- block : Option < & NakamotoBlock > ,
929
- block_response : BlockResponse ,
930
- ) {
922
+ fn send_block_response ( & mut self , block : & NakamotoBlock , block_response : BlockResponse ) {
931
923
self . impl_send_block_response ( block, block_response)
932
924
}
933
925
926
+ /// Send a pre block commit message to signers to indicate that we will be signing the proposed block
927
+ fn send_block_pre_commit ( & mut self , signer_signature_hash : Sha512Trunc256Sum ) {
928
+ info ! (
929
+ "{self}: Broadcasting block pre-commit to stacks node for {signer_signature_hash}" ;
930
+ ) ;
931
+ match self
932
+ . stackerdb
933
+ . send_message_with_retry ( SignerMessage :: BlockPreCommit ( signer_signature_hash) )
934
+ {
935
+ Ok ( ack) => {
936
+ if !ack. accepted {
937
+ warn ! (
938
+ "{self}: Block pre-commit not accepted by stacker-db: {:?}" ,
939
+ ack. reason
940
+ ) ;
941
+ }
942
+ crate :: monitoring:: actions:: increment_block_pre_commits_sent ( ) ;
943
+ }
944
+ Err ( e) => {
945
+ warn ! ( "{self}: Failed to send block pre-commit to stacker-db: {e:?}" , ) ;
946
+ }
947
+ }
948
+ }
949
+
934
950
/// Handle signer state update message
935
951
fn handle_state_machine_update (
936
952
& mut self ,
937
953
signer_public_key : & Secp256k1PublicKey ,
938
954
update : & StateMachineUpdate ,
939
955
received_time : & SystemTime ,
940
956
) {
941
- info ! ( "{self}: Received a new state machine update from signer {signer_public_key:?}: {update:?}" ) ;
957
+ info ! (
958
+ "{self}: Received state machine update from signer {signer_public_key:?}: {update:?}"
959
+ ) ;
942
960
let address = StacksAddress :: p2pkh ( self . mainnet , signer_public_key) ;
943
961
// Store the state machine update so we can reload it if we crash
944
962
if let Err ( e) = self . signer_db . insert_state_machine_update (
@@ -953,6 +971,96 @@ impl Signer {
953
971
. insert_update ( address, update. clone ( ) ) ;
954
972
}
955
973
974
+ /// Handle pre-commit message from another signer
975
+ fn handle_block_pre_commit (
976
+ & mut self ,
977
+ stacks_client : & StacksClient ,
978
+ stacker_address : & StacksAddress ,
979
+ block_hash : & Sha512Trunc256Sum ,
980
+ ) {
981
+ debug ! (
982
+ "{self}: Received pre-commit from signer ({stacker_address:?}) for block ({block_hash})" ,
983
+ ) ;
984
+ let Some ( mut block_info) = self . block_lookup_by_reward_cycle ( block_hash) else {
985
+ debug ! ( "{self}: Received pre-commit for a block we have not seen before. Ignoring..." ) ;
986
+ return ;
987
+ } ;
988
+ if block_info. has_reached_consensus ( ) {
989
+ debug ! (
990
+ "{self}: Received pre-commit for a block that is already marked as {}. Ignoring..." ,
991
+ block_info. state
992
+ ) ;
993
+ return ;
994
+ } ;
995
+ // Make sure the sender is part of our signing set
996
+ let is_valid_sender = self . signer_addresses . iter ( ) . any ( |addr| {
997
+ // it only matters that the address hash bytes match
998
+ stacker_address. bytes ( ) == addr. bytes ( )
999
+ } ) ;
1000
+
1001
+ if !is_valid_sender {
1002
+ debug ! ( "{self}: Received pre-commit message from an unknown sender {stacker_address:?}. Will not store." ) ;
1003
+ return ;
1004
+ }
1005
+
1006
+ if self . signer_db . has_committed ( block_hash, stacker_address) . inspect_err ( |e| warn ! ( "Failed to check if pre-commit message already considered for {stacker_address:?} for {block_hash}: {e}" ) ) . unwrap_or ( false ) {
1007
+ debug ! ( "{self}: Already considered pre-commit message from {stacker_address:?} for {block_hash}. Ignoring..." ) ;
1008
+ return ;
1009
+ }
1010
+ // commit message is from a valid sender! store it
1011
+ self . signer_db
1012
+ . add_block_pre_commit ( block_hash, stacker_address)
1013
+ . unwrap_or_else ( |_| panic ! ( "{self}: Failed to save block pre-commit" ) ) ;
1014
+
1015
+ // do we have enough pre-commits to reach consensus?
1016
+ // i.e. is the threshold reached?
1017
+ let committers = self
1018
+ . signer_db
1019
+ . get_block_pre_committers ( block_hash)
1020
+ . unwrap_or_else ( |_| panic ! ( "{self}: Failed to load block commits" ) ) ;
1021
+
1022
+ let commit_weight = self . compute_signature_signing_weight ( committers. iter ( ) ) ;
1023
+ let total_weight = self . compute_signature_total_weight ( ) ;
1024
+
1025
+ let min_weight = NakamotoBlockHeader :: compute_voting_weight_threshold ( total_weight)
1026
+ . unwrap_or_else ( |_| {
1027
+ panic ! ( "{self}: Failed to compute threshold weight for {total_weight}" )
1028
+ } ) ;
1029
+
1030
+ if min_weight > commit_weight {
1031
+ debug ! (
1032
+ "{self}: Not enough pre-committed to block {block_hash} (have {commit_weight}, need at least {min_weight}/{total_weight})"
1033
+ ) ;
1034
+ return ;
1035
+ }
1036
+
1037
+ // have enough commits, so maybe we should actually broadcast our signature...
1038
+ if block_info. valid == Some ( false ) {
1039
+ // We already marked this block as invalid. We should not do anything further as we do not change our votes on rejected blocks.
1040
+ debug ! (
1041
+ "{self}: Enough pre-committed to block {block_hash}, but we do not view the block as valid. Doing nothing."
1042
+ ) ;
1043
+ return ;
1044
+ }
1045
+ // 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.
1046
+ if let Err ( e) = block_info. mark_locally_accepted ( false ) {
1047
+ if !block_info. has_reached_consensus ( ) {
1048
+ warn ! ( "{self}: Failed to mark block as locally accepted: {e:?}" , ) ;
1049
+ }
1050
+ block_info. signed_self . get_or_insert ( get_epoch_time_secs ( ) ) ;
1051
+ }
1052
+
1053
+ self . signer_db
1054
+ . insert_block ( & block_info)
1055
+ . unwrap_or_else ( |e| self . handle_insert_block_error ( e) ) ;
1056
+ let block_response = self . create_block_acceptance ( & block_info. block ) ;
1057
+ if let Some ( accepted) = block_response. as_block_accepted ( ) {
1058
+ // have to save the signature _after_ the block info
1059
+ self . handle_block_signature ( stacks_client, accepted) ;
1060
+ }
1061
+ self . impl_send_block_response ( & block_info. block , block_response) ;
1062
+ }
1063
+
956
1064
/// Handle block proposal messages submitted to signers stackerdb
957
1065
fn handle_block_proposal (
958
1066
& mut self ,
@@ -1050,7 +1158,7 @@ impl Signer {
1050
1158
1051
1159
if let Some ( block_response) = block_response {
1052
1160
// We know proposal is invalid. Send rejection message, do not do further validation and do not store it.
1053
- self . send_block_response ( Some ( & block_info. block ) , block_response) ;
1161
+ self . send_block_response ( & block_info. block , block_response) ;
1054
1162
} else {
1055
1163
// Just in case check if the last block validation submission timed out.
1056
1164
self . check_submitted_block_proposal ( ) ;
@@ -1104,7 +1212,7 @@ impl Signer {
1104
1212
return ;
1105
1213
} ;
1106
1214
1107
- self . impl_send_block_response ( Some ( & block_info. block ) , block_response) ;
1215
+ self . impl_send_block_response ( & block_info. block , block_response) ;
1108
1216
}
1109
1217
1110
1218
/// Handle block response messages from a signer
@@ -1207,13 +1315,13 @@ impl Signer {
1207
1315
}
1208
1316
}
1209
1317
1210
- /// Handle the block validate ok response. Returns our block response if we have one
1318
+ /// Handle the block validate ok response
1211
1319
fn handle_block_validate_ok (
1212
1320
& mut self ,
1213
1321
stacks_client : & StacksClient ,
1214
1322
block_validate_ok : & BlockValidateOk ,
1215
1323
sortition_state : & mut Option < SortitionsView > ,
1216
- ) -> Option < BlockResponse > {
1324
+ ) {
1217
1325
crate :: monitoring:: actions:: increment_block_validation_responses ( true ) ;
1218
1326
let signer_signature_hash = block_validate_ok. signer_signature_hash ;
1219
1327
if self
@@ -1242,17 +1350,19 @@ impl Signer {
1242
1350
let Some ( mut block_info) = self . block_lookup_by_reward_cycle ( & signer_signature_hash) else {
1243
1351
// We have not seen this block before. Why are we getting a response for it?
1244
1352
debug ! ( "{self}: Received a block validate response for a block we have not seen before. Ignoring..." ) ;
1245
- return None ;
1353
+ return ;
1246
1354
} ;
1247
1355
if block_info. is_locally_finalized ( ) {
1248
1356
debug ! ( "{self}: Received block validation for a block that is already marked as {}. Ignoring..." , block_info. state) ;
1249
- return None ;
1357
+ return ;
1250
1358
}
1251
1359
1252
1360
if let Some ( block_response) =
1253
1361
self . check_block_against_signer_db_state ( stacks_client, & block_info. block )
1254
1362
{
1255
- let block_rejection = block_response. as_block_rejection ( ) ?;
1363
+ let Some ( block_rejection) = block_response. as_block_rejection ( ) else {
1364
+ return ;
1365
+ } ;
1256
1366
// The signer db state has changed. We no longer view this block as valid. Override the validation response.
1257
1367
if let Err ( e) = block_info. mark_locally_rejected ( ) {
1258
1368
if !block_info. has_reached_consensus ( ) {
@@ -1263,12 +1373,15 @@ impl Signer {
1263
1373
. insert_block ( & block_info)
1264
1374
. unwrap_or_else ( |e| self . handle_insert_block_error ( e) ) ;
1265
1375
self . handle_block_rejection ( block_rejection, sortition_state) ;
1266
- Some ( block_response)
1376
+ self . impl_send_block_response (
1377
+ & block_info. block ,
1378
+ BlockResponse :: Rejected ( block_rejection. clone ( ) ) ,
1379
+ ) ;
1267
1380
} else {
1268
1381
if let Err ( e) = block_info. mark_locally_accepted ( false ) {
1269
1382
if !block_info. has_reached_consensus ( ) {
1270
1383
warn ! ( "{self}: Failed to mark block as locally accepted: {e:?}" , ) ;
1271
- return None ;
1384
+ return ;
1272
1385
}
1273
1386
block_info. signed_self . get_or_insert ( get_epoch_time_secs ( ) ) ;
1274
1387
}
@@ -1282,19 +1395,19 @@ impl Signer {
1282
1395
self . signer_db
1283
1396
. insert_block ( & block_info)
1284
1397
. unwrap_or_else ( |e| self . handle_insert_block_error ( e) ) ;
1285
- let block_response = self . create_block_acceptance ( & block_info . block ) ;
1398
+ self . send_block_pre_commit ( signer_signature_hash ) ;
1286
1399
// have to save the signature _after_ the block info
1287
- self . handle_block_signature ( stacks_client , block_response . as_block_accepted ( ) ? ) ;
1288
- Some ( block_response )
1400
+ let address = self . stacks_address ;
1401
+ self . handle_block_pre_commit ( stacks_client , & address , & signer_signature_hash ) ;
1289
1402
}
1290
1403
}
1291
1404
1292
- /// Handle the block validate reject response. Returns our block response if we have one
1405
+ /// Handle the block validate reject response
1293
1406
fn handle_block_validate_reject (
1294
1407
& mut self ,
1295
1408
block_validate_reject : & BlockValidateReject ,
1296
1409
sortition_state : & mut Option < SortitionsView > ,
1297
- ) -> Option < BlockResponse > {
1410
+ ) {
1298
1411
crate :: monitoring:: actions:: increment_block_validation_responses ( false ) ;
1299
1412
let signer_signature_hash = block_validate_reject. signer_signature_hash ;
1300
1413
if self
@@ -1307,16 +1420,16 @@ impl Signer {
1307
1420
let Some ( mut block_info) = self . block_lookup_by_reward_cycle ( & signer_signature_hash) else {
1308
1421
// We have not seen this block before. Why are we getting a response for it?
1309
1422
debug ! ( "{self}: Received a block validate response for a block we have not seen before. Ignoring..." ) ;
1310
- return None ;
1423
+ return ;
1311
1424
} ;
1312
1425
if block_info. is_locally_finalized ( ) {
1313
1426
debug ! ( "{self}: Received block validation for a block that is already marked as {}. Ignoring..." , block_info. state) ;
1314
- return None ;
1427
+ return ;
1315
1428
}
1316
1429
if let Err ( e) = block_info. mark_locally_rejected ( ) {
1317
1430
if !block_info. has_reached_consensus ( ) {
1318
1431
warn ! ( "{self}: Failed to mark block as locally rejected: {e:?}" , ) ;
1319
- return None ;
1432
+ return ;
1320
1433
}
1321
1434
}
1322
1435
let block_rejection = BlockRejection :: from_validate_rejection (
@@ -1337,7 +1450,7 @@ impl Signer {
1337
1450
. insert_block ( & block_info)
1338
1451
. unwrap_or_else ( |e| self . handle_insert_block_error ( e) ) ;
1339
1452
self . handle_block_rejection ( & block_rejection, sortition_state) ;
1340
- Some ( BlockResponse :: Rejected ( block_rejection) )
1453
+ self . impl_send_block_response ( & block_info . block , BlockResponse :: Rejected ( block_rejection) ) ;
1341
1454
}
1342
1455
1343
1456
/// Handle the block validate response returned from our prior calls to submit a block for validation
@@ -1348,15 +1461,15 @@ impl Signer {
1348
1461
sortition_state : & mut Option < SortitionsView > ,
1349
1462
) {
1350
1463
info ! ( "{self}: Received a block validate response: {block_validate_response:?}" ) ;
1351
- let block_response = match block_validate_response {
1464
+ match block_validate_response {
1352
1465
BlockValidateResponse :: Ok ( block_validate_ok) => {
1353
1466
crate :: monitoring:: actions:: record_block_validation_latency (
1354
1467
block_validate_ok. validation_time_ms ,
1355
1468
) ;
1356
- self . handle_block_validate_ok ( stacks_client, block_validate_ok, sortition_state)
1469
+ self . handle_block_validate_ok ( stacks_client, block_validate_ok, sortition_state) ;
1357
1470
}
1358
1471
BlockValidateResponse :: Reject ( block_validate_reject) => {
1359
- self . handle_block_validate_reject ( block_validate_reject, sortition_state)
1472
+ self . handle_block_validate_reject ( block_validate_reject, sortition_state) ;
1360
1473
}
1361
1474
} ;
1362
1475
// Remove this block validation from the pending table
@@ -1365,15 +1478,6 @@ impl Signer {
1365
1478
. remove_pending_block_validation ( & signer_sig_hash)
1366
1479
. unwrap_or_else ( |e| warn ! ( "{self}: Failed to remove pending block validation: {e:?}" ) ) ;
1367
1480
1368
- if let Some ( response) = block_response {
1369
- let block = self
1370
- . signer_db
1371
- . block_lookup ( & signer_sig_hash)
1372
- . unwrap_or_default ( )
1373
- . map ( |info| info. block ) ;
1374
- self . impl_send_block_response ( block. as_ref ( ) , response) ;
1375
- } ;
1376
-
1377
1481
// Check if there is a pending block validation that we need to submit to the node
1378
1482
self . check_pending_block_validations ( stacks_client) ;
1379
1483
}
@@ -1464,7 +1568,7 @@ impl Signer {
1464
1568
warn ! ( "{self}: Failed to mark block as locally rejected: {e:?}" ) ;
1465
1569
}
1466
1570
} ;
1467
- self . impl_send_block_response ( Some ( & block_info. block ) , rejection) ;
1571
+ self . impl_send_block_response ( & block_info. block , rejection) ;
1468
1572
1469
1573
self . signer_db
1470
1574
. insert_block ( & block_info)
@@ -1701,6 +1805,11 @@ impl Signer {
1701
1805
return ;
1702
1806
}
1703
1807
1808
+ // If this isn't our own signature, try treating it as a pre-commit in case the caller is running an outdated version
1809
+ if signer_address != self . stacks_address {
1810
+ self . handle_block_pre_commit ( stacks_client, & signer_address, block_hash) ;
1811
+ }
1812
+
1704
1813
// do we have enough signatures to broadcast?
1705
1814
// i.e. is the threshold reached?
1706
1815
let signatures = self
0 commit comments