@@ -52,22 +52,22 @@ use crate::validator_monitor::HISTORIC_EPOCHS as VALIDATOR_MONITOR_HISTORIC_EPOC
52
52
use crate :: validator_pubkey_cache:: ValidatorPubkeyCache ;
53
53
use crate :: {
54
54
beacon_chain:: {
55
- BeaconForkChoice , BLOCK_PROCESSING_CACHE_LOCK_TIMEOUT , MAXIMUM_GOSSIP_CLOCK_DISPARITY ,
56
- VALIDATOR_PUBKEY_CACHE_LOCK_TIMEOUT ,
55
+ BeaconForkChoice , ForkChoiceError , BLOCK_PROCESSING_CACHE_LOCK_TIMEOUT ,
56
+ MAXIMUM_GOSSIP_CLOCK_DISPARITY , VALIDATOR_PUBKEY_CACHE_LOCK_TIMEOUT ,
57
57
} ,
58
58
metrics, BeaconChain , BeaconChainError , BeaconChainTypes ,
59
59
} ;
60
60
use derivative:: Derivative ;
61
61
use eth2:: types:: EventKind ;
62
62
use execution_layer:: PayloadStatus ;
63
- use fork_choice:: PayloadVerificationStatus ;
63
+ use fork_choice:: { AttestationFromBlock , PayloadVerificationStatus } ;
64
64
use parking_lot:: RwLockReadGuard ;
65
65
use proto_array:: Block as ProtoBlock ;
66
66
use safe_arith:: ArithError ;
67
67
use slog:: { debug, error, warn, Logger } ;
68
68
use slot_clock:: SlotClock ;
69
69
use ssz:: Encode ;
70
- use state_processing:: per_block_processing:: is_merge_transition_block;
70
+ use state_processing:: per_block_processing:: { errors :: IntoWithIndex , is_merge_transition_block} ;
71
71
use state_processing:: {
72
72
block_signature_verifier:: { BlockSignatureVerifier , Error as BlockSignatureVerifierError } ,
73
73
per_block_processing, per_slot_processing,
@@ -550,8 +550,22 @@ pub fn signature_verify_chain_segment<T: BeaconChainTypes>(
550
550
let pubkey_cache = get_validator_pubkey_cache ( chain) ?;
551
551
let mut signature_verifier = get_signature_verifier ( & state, & pubkey_cache, & chain. spec ) ;
552
552
553
+ let mut signature_verified_blocks = Vec :: with_capacity ( chain_segment. len ( ) ) ;
554
+
553
555
for ( block_root, block) in & chain_segment {
554
- signature_verifier. include_all_signatures ( block, Some ( * block_root) , None ) ?;
556
+ let mut consensus_context =
557
+ ConsensusContext :: new ( block. slot ( ) ) . set_current_block_root ( * block_root) ;
558
+
559
+ signature_verifier. include_all_signatures ( block, & mut consensus_context) ?;
560
+
561
+ // Save the block and its consensus context. The context will have had its proposer index
562
+ // and attesting indices filled in, which can be used to accelerate later block processing.
563
+ signature_verified_blocks. push ( SignatureVerifiedBlock {
564
+ block : block. clone ( ) ,
565
+ block_root : * block_root,
566
+ parent : None ,
567
+ consensus_context,
568
+ } ) ;
555
569
}
556
570
557
571
if signature_verifier. verify ( ) . is_err ( ) {
@@ -560,22 +574,6 @@ pub fn signature_verify_chain_segment<T: BeaconChainTypes>(
560
574
561
575
drop ( pubkey_cache) ;
562
576
563
- let mut signature_verified_blocks = chain_segment
564
- . into_iter ( )
565
- . map ( |( block_root, block) | {
566
- // Proposer index has already been verified above during signature verification.
567
- let consensus_context = ConsensusContext :: new ( block. slot ( ) )
568
- . set_current_block_root ( block_root)
569
- . set_proposer_index ( block. message ( ) . proposer_index ( ) ) ;
570
- SignatureVerifiedBlock {
571
- block,
572
- block_root,
573
- parent : None ,
574
- consensus_context,
575
- }
576
- } )
577
- . collect :: < Vec < _ > > ( ) ;
578
-
579
577
if let Some ( signature_verified_block) = signature_verified_blocks. first_mut ( ) {
580
578
signature_verified_block. parent = Some ( parent) ;
581
579
}
@@ -625,6 +623,7 @@ pub struct ExecutionPendingBlock<T: BeaconChainTypes> {
625
623
pub parent_block : SignedBeaconBlock < T :: EthSpec , BlindedPayload < T :: EthSpec > > ,
626
624
pub parent_eth1_finalization_data : Eth1FinalizationData ,
627
625
pub confirmed_state_roots : Vec < Hash256 > ,
626
+ pub consensus_context : ConsensusContext < T :: EthSpec > ,
628
627
pub payload_verification_handle : PayloadVerificationHandle < T :: EthSpec > ,
629
628
}
630
629
@@ -951,13 +950,14 @@ impl<T: BeaconChainTypes> SignatureVerifiedBlock<T> {
951
950
952
951
let mut signature_verifier = get_signature_verifier ( & state, & pubkey_cache, & chain. spec ) ;
953
952
954
- signature_verifier. include_all_signatures ( & block, Some ( block_root) , None ) ?;
953
+ let mut consensus_context =
954
+ ConsensusContext :: new ( block. slot ( ) ) . set_current_block_root ( block_root) ;
955
+
956
+ signature_verifier. include_all_signatures ( & block, & mut consensus_context) ?;
955
957
956
958
if signature_verifier. verify ( ) . is_ok ( ) {
957
959
Ok ( Self {
958
- consensus_context : ConsensusContext :: new ( block. slot ( ) )
959
- . set_current_block_root ( block_root)
960
- . set_proposer_index ( block. message ( ) . proposer_index ( ) ) ,
960
+ consensus_context,
961
961
block,
962
962
block_root,
963
963
parent : Some ( parent) ,
@@ -1002,16 +1002,16 @@ impl<T: BeaconChainTypes> SignatureVerifiedBlock<T> {
1002
1002
1003
1003
// Gossip verification has already checked the proposer index. Use it to check the RANDAO
1004
1004
// signature.
1005
- let verified_proposer_index = Some ( block . message ( ) . proposer_index ( ) ) ;
1005
+ let mut consensus_context = from . consensus_context ;
1006
1006
signature_verifier
1007
- . include_all_signatures_except_proposal ( & block, verified_proposer_index ) ?;
1007
+ . include_all_signatures_except_proposal ( & block, & mut consensus_context ) ?;
1008
1008
1009
1009
if signature_verifier. verify ( ) . is_ok ( ) {
1010
1010
Ok ( Self {
1011
1011
block,
1012
1012
block_root : from. block_root ,
1013
1013
parent : Some ( parent) ,
1014
- consensus_context : from . consensus_context ,
1014
+ consensus_context,
1015
1015
} )
1016
1016
} else {
1017
1017
Err ( BlockError :: InvalidSignature )
@@ -1138,6 +1138,79 @@ impl<T: BeaconChainTypes> ExecutionPendingBlock<T> {
1138
1138
1139
1139
check_block_relevancy ( & block, block_root, chain) ?;
1140
1140
1141
+ // Define a future that will verify the execution payload with an execution engine.
1142
+ //
1143
+ // We do this as early as possible so that later parts of this function can run in parallel
1144
+ // with the payload verification.
1145
+ let payload_notifier = PayloadNotifier :: new (
1146
+ chain. clone ( ) ,
1147
+ block. clone ( ) ,
1148
+ & parent. pre_state ,
1149
+ notify_execution_layer,
1150
+ ) ?;
1151
+ let is_valid_merge_transition_block =
1152
+ is_merge_transition_block ( & parent. pre_state , block. message ( ) . body ( ) ) ;
1153
+ let payload_verification_future = async move {
1154
+ let chain = payload_notifier. chain . clone ( ) ;
1155
+ let block = payload_notifier. block . clone ( ) ;
1156
+
1157
+ // If this block triggers the merge, check to ensure that it references valid execution
1158
+ // blocks.
1159
+ //
1160
+ // The specification defines this check inside `on_block` in the fork-choice specification,
1161
+ // however we perform the check here for two reasons:
1162
+ //
1163
+ // - There's no point in importing a block that will fail fork choice, so it's best to fail
1164
+ // early.
1165
+ // - Doing the check here means we can keep our fork-choice implementation "pure". I.e., no
1166
+ // calls to remote servers.
1167
+ if is_valid_merge_transition_block {
1168
+ validate_merge_block ( & chain, block. message ( ) , AllowOptimisticImport :: Yes ) . await ?;
1169
+ } ;
1170
+
1171
+ // The specification declares that this should be run *inside* `per_block_processing`,
1172
+ // however we run it here to keep `per_block_processing` pure (i.e., no calls to external
1173
+ // servers).
1174
+ let payload_verification_status = payload_notifier. notify_new_payload ( ) . await ?;
1175
+
1176
+ // If the payload did not validate or invalidate the block, check to see if this block is
1177
+ // valid for optimistic import.
1178
+ if payload_verification_status. is_optimistic ( ) {
1179
+ let block_hash_opt = block
1180
+ . message ( )
1181
+ . body ( )
1182
+ . execution_payload ( )
1183
+ . map ( |full_payload| full_payload. execution_payload . block_hash ) ;
1184
+
1185
+ // Ensure the block is a candidate for optimistic import.
1186
+ if !is_optimistic_candidate_block ( & chain, block. slot ( ) , block. parent_root ( ) ) . await ?
1187
+ {
1188
+ warn ! (
1189
+ chain. log,
1190
+ "Rejecting optimistic block" ;
1191
+ "block_hash" => ?block_hash_opt,
1192
+ "msg" => "the execution engine is not synced"
1193
+ ) ;
1194
+ return Err ( ExecutionPayloadError :: UnverifiedNonOptimisticCandidate . into ( ) ) ;
1195
+ }
1196
+ }
1197
+
1198
+ Ok ( PayloadVerificationOutcome {
1199
+ payload_verification_status,
1200
+ is_valid_merge_transition_block,
1201
+ } )
1202
+ } ;
1203
+ // Spawn the payload verification future as a new task, but don't wait for it to complete.
1204
+ // The `payload_verification_future` will be awaited later to ensure verification completed
1205
+ // successfully.
1206
+ let payload_verification_handle = chain
1207
+ . task_executor
1208
+ . spawn_handle (
1209
+ payload_verification_future,
1210
+ "execution_payload_verification" ,
1211
+ )
1212
+ . ok_or ( BeaconChainError :: RuntimeShutdown ) ?;
1213
+
1141
1214
/*
1142
1215
* Advance the given `parent.beacon_state` to the slot of the given `block`.
1143
1216
*/
@@ -1242,80 +1315,11 @@ impl<T: BeaconChainTypes> ExecutionPendingBlock<T> {
1242
1315
summaries. push ( summary) ;
1243
1316
}
1244
1317
}
1318
+ metrics:: stop_timer ( catchup_timer) ;
1245
1319
1246
1320
let block_slot = block. slot ( ) ;
1247
1321
let state_current_epoch = state. current_epoch ( ) ;
1248
1322
1249
- // Define a future that will verify the execution payload with an execution engine (but
1250
- // don't execute it yet).
1251
- let payload_notifier =
1252
- PayloadNotifier :: new ( chain. clone ( ) , block. clone ( ) , & state, notify_execution_layer) ?;
1253
- let is_valid_merge_transition_block =
1254
- is_merge_transition_block ( & state, block. message ( ) . body ( ) ) ;
1255
- let payload_verification_future = async move {
1256
- let chain = payload_notifier. chain . clone ( ) ;
1257
- let block = payload_notifier. block . clone ( ) ;
1258
-
1259
- // If this block triggers the merge, check to ensure that it references valid execution
1260
- // blocks.
1261
- //
1262
- // The specification defines this check inside `on_block` in the fork-choice specification,
1263
- // however we perform the check here for two reasons:
1264
- //
1265
- // - There's no point in importing a block that will fail fork choice, so it's best to fail
1266
- // early.
1267
- // - Doing the check here means we can keep our fork-choice implementation "pure". I.e., no
1268
- // calls to remote servers.
1269
- if is_valid_merge_transition_block {
1270
- validate_merge_block ( & chain, block. message ( ) , AllowOptimisticImport :: Yes ) . await ?;
1271
- } ;
1272
-
1273
- // The specification declares that this should be run *inside* `per_block_processing`,
1274
- // however we run it here to keep `per_block_processing` pure (i.e., no calls to external
1275
- // servers).
1276
- //
1277
- // It is important that this function is called *after* `per_slot_processing`, since the
1278
- // `randao` may change.
1279
- let payload_verification_status = payload_notifier. notify_new_payload ( ) . await ?;
1280
-
1281
- // If the payload did not validate or invalidate the block, check to see if this block is
1282
- // valid for optimistic import.
1283
- if payload_verification_status. is_optimistic ( ) {
1284
- let block_hash_opt = block
1285
- . message ( )
1286
- . body ( )
1287
- . execution_payload ( )
1288
- . map ( |full_payload| full_payload. execution_payload . block_hash ) ;
1289
-
1290
- // Ensure the block is a candidate for optimistic import.
1291
- if !is_optimistic_candidate_block ( & chain, block. slot ( ) , block. parent_root ( ) ) . await ?
1292
- {
1293
- warn ! (
1294
- chain. log,
1295
- "Rejecting optimistic block" ;
1296
- "block_hash" => ?block_hash_opt,
1297
- "msg" => "the execution engine is not synced"
1298
- ) ;
1299
- return Err ( ExecutionPayloadError :: UnverifiedNonOptimisticCandidate . into ( ) ) ;
1300
- }
1301
- }
1302
-
1303
- Ok ( PayloadVerificationOutcome {
1304
- payload_verification_status,
1305
- is_valid_merge_transition_block,
1306
- } )
1307
- } ;
1308
- // Spawn the payload verification future as a new task, but don't wait for it to complete.
1309
- // The `payload_verification_future` will be awaited later to ensure verification completed
1310
- // successfully.
1311
- let payload_verification_handle = chain
1312
- . task_executor
1313
- . spawn_handle (
1314
- payload_verification_future,
1315
- "execution_payload_verification" ,
1316
- )
1317
- . ok_or ( BeaconChainError :: RuntimeShutdown ) ?;
1318
-
1319
1323
// If the block is sufficiently recent, notify the validator monitor.
1320
1324
if let Some ( slot) = chain. slot_clock . now ( ) {
1321
1325
let epoch = slot. epoch ( T :: EthSpec :: slots_per_epoch ( ) ) ;
@@ -1342,8 +1346,6 @@ impl<T: BeaconChainTypes> ExecutionPendingBlock<T> {
1342
1346
}
1343
1347
}
1344
1348
1345
- metrics:: stop_timer ( catchup_timer) ;
1346
-
1347
1349
/*
1348
1350
* Build the committee caches on the state.
1349
1351
*/
@@ -1433,13 +1435,52 @@ impl<T: BeaconChainTypes> ExecutionPendingBlock<T> {
1433
1435
} ) ;
1434
1436
}
1435
1437
1438
+ /*
1439
+ * Apply the block's attestations to fork choice.
1440
+ *
1441
+ * We're running in parallel with the payload verification at this point, so this is
1442
+ * free real estate.
1443
+ */
1444
+ let current_slot = chain. slot ( ) ?;
1445
+ let mut fork_choice = chain. canonical_head . fork_choice_write_lock ( ) ;
1446
+
1447
+ // Register each attester slashing in the block with fork choice.
1448
+ for attester_slashing in block. message ( ) . body ( ) . attester_slashings ( ) {
1449
+ fork_choice. on_attester_slashing ( attester_slashing) ;
1450
+ }
1451
+
1452
+ // Register each attestation in the block with fork choice.
1453
+ for ( i, attestation) in block. message ( ) . body ( ) . attestations ( ) . iter ( ) . enumerate ( ) {
1454
+ let _fork_choice_attestation_timer =
1455
+ metrics:: start_timer ( & metrics:: FORK_CHOICE_PROCESS_ATTESTATION_TIMES ) ;
1456
+
1457
+ let indexed_attestation = consensus_context
1458
+ . get_indexed_attestation ( & state, attestation)
1459
+ . map_err ( |e| BlockError :: PerBlockProcessingError ( e. into_with_index ( i) ) ) ?;
1460
+
1461
+ match fork_choice. on_attestation (
1462
+ current_slot,
1463
+ indexed_attestation,
1464
+ AttestationFromBlock :: True ,
1465
+ & chain. spec ,
1466
+ ) {
1467
+ Ok ( ( ) ) => Ok ( ( ) ) ,
1468
+ // Ignore invalid attestations whilst importing attestations from a block. The
1469
+ // block might be very old and therefore the attestations useless to fork choice.
1470
+ Err ( ForkChoiceError :: InvalidAttestation ( _) ) => Ok ( ( ) ) ,
1471
+ Err ( e) => Err ( BlockError :: BeaconChainError ( e. into ( ) ) ) ,
1472
+ } ?;
1473
+ }
1474
+ drop ( fork_choice) ;
1475
+
1436
1476
Ok ( Self {
1437
1477
block,
1438
1478
block_root,
1439
1479
state,
1440
1480
parent_block : parent. beacon_block ,
1441
1481
parent_eth1_finalization_data,
1442
1482
confirmed_state_roots,
1483
+ consensus_context,
1443
1484
payload_verification_handle,
1444
1485
} )
1445
1486
}
0 commit comments