Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Review importBlock order of operations #4976

Merged
merged 1 commit into from
Jan 5, 2023

Conversation

dapplion
Copy link
Contributor

@dapplion dapplion commented Jan 5, 2023

Motivation

We noted with @g11tech that the import order of importBlock is not safe since it can import a block into the fork-choice without it being available in the DB. That can cause the node to get stuck if it ever needs to regen, and other potential undefined behavior.

Description

ImportBlock order of operations must guarantee that BeaconNode does not end in an unknown state:

  1. Persist block to hot DB (pre-emptively)
    • Done before importing block to fork-choice to guarantee that blocks in the fork-choice lwaysare persisted
      in the DB. Otherwise the beacon node may end up in an unrecoverable state. If a block is persisted in the hot
      db but is unknown by the fork-choice, then it will just use some extra disk space. On restart is will be
      pruned regardless.
    • Note that doing a disk write first introduces a small delay before setting the head. An improvement where disk
      write happens latter requires the ability to roll back a fork-choice head change if disk write fails
  2. Import block to fork-choice
  3. Import attestations to fork-choice
  4. Import attester slashings to fork-choice
  5. Compute head. If new head, immediately stateCache.setHeadState()
  6. Queue notifyForkchoiceUpdate to engine api
  7. Add post state to stateCache

@dapplion dapplion requested a review from a team as a code owner January 5, 2023 08:32
@@ -309,7 +309,7 @@ export class ForkChoice implements IForkChoice {
blockDelaySec: number,
currentSlot: Slot,
executionStatus: MaybeValidExecutionStatus
): void {
): ProtoBlock {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make onBlock return the blockSummary to prevent having to consider in the forkchoice that the new block is not found in the fork-choice

import {REPROCESS_MIN_TIME_TO_NEXT_SLOT_SEC} from "../reprocess.js";
import {RegenCaller} from "../regen/interface.js";
import type {BeaconChain} from "../chain.js";
import {BlockInputType, FullyVerifiedBlock, ImportBlockOpts, AttestationImportOpt} from "./types.js";
import {PendingEvents} from "./utils/pendingEvents.js";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it may be safe from a logic perspective, but we may want to keep this to avoid a future footgun?

Now we will be adding additional work, running all event handlers, interspersed with importBlock, which we want to finish ASAP.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's safe because none of those events trigger side effects, unlike they did before. They are solely consumed by API eventsream clients

@dapplion dapplion force-pushed the dapplion/importblock-order branch from fb549ae to 84b4cdb Compare January 5, 2023 08:41
@github-actions
Copy link
Contributor

github-actions bot commented Jan 5, 2023

Performance Report

✔️ no performance regression detected

Full benchmark results
Benchmark suite Current: 6898ca6 Previous: 65ce97f Ratio
getPubkeys - index2pubkey - req 1000 vs - 250000 vc 947.55 us/op 845.27 us/op 1.12
getPubkeys - validatorsArr - req 1000 vs - 250000 vc 59.794 us/op 58.752 us/op 1.02
BLS verify - blst-native 2.1752 ms/op 2.1752 ms/op 1.00
BLS verifyMultipleSignatures 3 - blst-native 4.4906 ms/op 4.4884 ms/op 1.00
BLS verifyMultipleSignatures 8 - blst-native 9.7165 ms/op 9.7019 ms/op 1.00
BLS verifyMultipleSignatures 32 - blst-native 35.338 ms/op 35.530 ms/op 0.99
BLS aggregatePubkeys 32 - blst-native 46.851 us/op 47.154 us/op 0.99
BLS aggregatePubkeys 128 - blst-native 184.00 us/op 183.70 us/op 1.00
getAttestationsForBlock 76.762 ms/op 73.374 ms/op 1.05
isKnown best case - 1 super set check 466.00 ns/op 435.00 ns/op 1.07
isKnown normal case - 2 super set checks 452.00 ns/op 427.00 ns/op 1.06
isKnown worse case - 16 super set checks 451.00 ns/op 428.00 ns/op 1.05
CheckpointStateCache - add get delete 8.6180 us/op 8.0240 us/op 1.07
validate gossip signedAggregateAndProof - struct 5.0178 ms/op 5.0247 ms/op 1.00
validate gossip attestation - struct 2.3565 ms/op 2.3613 ms/op 1.00
pickEth1Vote - no votes 2.1525 ms/op 2.1910 ms/op 0.98
pickEth1Vote - max votes 15.716 ms/op 14.769 ms/op 1.06
pickEth1Vote - Eth1Data hashTreeRoot value x2048 13.124 ms/op 12.594 ms/op 1.04
pickEth1Vote - Eth1Data hashTreeRoot tree x2048 20.509 ms/op 19.440 ms/op 1.06
pickEth1Vote - Eth1Data fastSerialize value x2048 1.0689 ms/op 1.0427 ms/op 1.03
pickEth1Vote - Eth1Data fastSerialize tree x2048 9.3166 ms/op 9.1463 ms/op 1.02
bytes32 toHexString 830.00 ns/op 819.00 ns/op 1.01
bytes32 Buffer.toString(hex) 558.00 ns/op 524.00 ns/op 1.06
bytes32 Buffer.toString(hex) from Uint8Array 999.00 ns/op 982.00 ns/op 1.02
bytes32 Buffer.toString(hex) + 0x 570.00 ns/op 522.00 ns/op 1.09
Object access 1 prop 0.27400 ns/op 0.26200 ns/op 1.05
Map access 1 prop 0.25400 ns/op 0.25400 ns/op 1.00
Object get x1000 10.548 ns/op 10.570 ns/op 1.00
Map get x1000 0.91900 ns/op 0.90200 ns/op 1.02
Object set x1000 72.272 ns/op 66.507 ns/op 1.09
Map set x1000 43.308 ns/op 40.034 ns/op 1.08
Return object 10000 times 0.43510 ns/op 0.43170 ns/op 1.01
Throw Error 10000 times 6.5331 us/op 6.6913 us/op 0.98
fastMsgIdFn sha256 / 200 bytes 4.7730 us/op 4.8570 us/op 0.98
fastMsgIdFn h32 xxhash / 200 bytes 468.00 ns/op 446.00 ns/op 1.05
fastMsgIdFn h64 xxhash / 200 bytes 721.00 ns/op 590.00 ns/op 1.22
fastMsgIdFn sha256 / 1000 bytes 15.679 us/op 15.595 us/op 1.01
fastMsgIdFn h32 xxhash / 1000 bytes 637.00 ns/op 617.00 ns/op 1.03
fastMsgIdFn h64 xxhash / 1000 bytes 799.00 ns/op 699.00 ns/op 1.14
fastMsgIdFn sha256 / 10000 bytes 137.25 us/op 136.96 us/op 1.00
fastMsgIdFn h32 xxhash / 10000 bytes 2.5550 us/op 2.5030 us/op 1.02
fastMsgIdFn h64 xxhash / 10000 bytes 1.7940 us/op 1.6970 us/op 1.06
enrSubnets - fastDeserialize 64 bits 2.2160 us/op 2.1200 us/op 1.05
enrSubnets - ssz BitVector 64 bits 852.00 ns/op 772.00 ns/op 1.10
enrSubnets - fastDeserialize 4 bits 288.00 ns/op 272.00 ns/op 1.06
enrSubnets - ssz BitVector 4 bits 863.00 ns/op 778.00 ns/op 1.11
prioritizePeers score -10:0 att 32-0.1 sync 2-0 146.33 us/op 138.15 us/op 1.06
prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 177.39 us/op 168.50 us/op 1.05
prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 253.48 us/op 242.90 us/op 1.04
prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 441.32 us/op 425.26 us/op 1.04
prioritizePeers score 0:0 att 64-1 sync 4-1 503.40 us/op 470.95 us/op 1.07
array of 16000 items push then shift 51.616 us/op 51.592 us/op 1.00
LinkedList of 16000 items push then shift 12.381 ns/op 12.047 ns/op 1.03
array of 16000 items push then pop 188.81 ns/op 181.54 ns/op 1.04
LinkedList of 16000 items push then pop 11.936 ns/op 11.781 ns/op 1.01
array of 24000 items push then shift 77.384 us/op 77.346 us/op 1.00
LinkedList of 24000 items push then shift 12.533 ns/op 12.130 ns/op 1.03
array of 24000 items push then pop 191.01 ns/op 188.28 ns/op 1.01
LinkedList of 24000 items push then pop 12.058 ns/op 11.907 ns/op 1.01
intersect bitArray bitLen 8 21.697 ns/op 21.655 ns/op 1.00
intersect array and set length 8 122.32 ns/op 119.41 ns/op 1.02
intersect bitArray bitLen 128 71.171 ns/op 71.039 ns/op 1.00
intersect array and set length 128 1.6494 us/op 1.6085 us/op 1.03
Buffer.concat 32 items 4.5110 us/op 4.4730 us/op 1.01
Uint8Array.set 32 items 3.6180 us/op 3.6330 us/op 1.00
pass gossip attestations to forkchoice per slot 3.7490 ms/op 3.7574 ms/op 1.00
computeDeltas 4.2612 ms/op 4.1828 ms/op 1.02
computeProposerBoostScoreFromBalances 2.5522 ms/op 2.4365 ms/op 1.05
altair processAttestation - 250000 vs - 7PWei normalcase 3.3966 ms/op 3.2394 ms/op 1.05
altair processAttestation - 250000 vs - 7PWei worstcase 5.6425 ms/op 5.5650 ms/op 1.01
altair processAttestation - setStatus - 1/6 committees join 176.97 us/op 182.95 us/op 0.97
altair processAttestation - setStatus - 1/3 committees join 354.79 us/op 362.08 us/op 0.98
altair processAttestation - setStatus - 1/2 committees join 514.83 us/op 522.02 us/op 0.99
altair processAttestation - setStatus - 2/3 committees join 679.40 us/op 685.49 us/op 0.99
altair processAttestation - setStatus - 4/5 committees join 938.59 us/op 947.98 us/op 0.99
altair processAttestation - setStatus - 100% committees join 1.1198 ms/op 1.1451 ms/op 0.98
altair processBlock - 250000 vs - 7PWei normalcase 23.050 ms/op 22.422 ms/op 1.03
altair processBlock - 250000 vs - 7PWei normalcase hashState 38.148 ms/op 36.929 ms/op 1.03
altair processBlock - 250000 vs - 7PWei worstcase 74.206 ms/op 75.989 ms/op 0.98
altair processBlock - 250000 vs - 7PWei worstcase hashState 112.33 ms/op 98.524 ms/op 1.14
phase0 processBlock - 250000 vs - 7PWei normalcase 3.2240 ms/op 3.1337 ms/op 1.03
phase0 processBlock - 250000 vs - 7PWei worstcase 51.196 ms/op 51.341 ms/op 1.00
altair processEth1Data - 250000 vs - 7PWei normalcase 631.33 us/op 599.98 us/op 1.05
vc - 250000 eb 1 eth1 1 we 0 wn 0 - smpl 15 7.5170 us/op 16.456 us/op 0.46
vc - 250000 eb 0.95 eth1 0.1 we 0.05 wn 0 - smpl 219 23.924 us/op 48.252 us/op 0.50
vc - 250000 eb 0.95 eth1 0.3 we 0.05 wn 0 - smpl 42 9.9430 us/op 20.523 us/op 0.48
vc - 250000 eb 0.95 eth1 0.7 we 0.05 wn 0 - smpl 18 7.7690 us/op 15.945 us/op 0.49
vc - 250000 eb 0.1 eth1 0.1 we 0 wn 0 - smpl 1020 95.066 us/op 202.46 us/op 0.47
vc - 250000 eb 0.03 eth1 0.03 we 0 wn 0 - smpl 11777 874.70 us/op 1.0967 ms/op 0.80
vc - 250000 eb 0.01 eth1 0.01 we 0 wn 0 - smpl 16384 1.1729 ms/op 1.4275 ms/op 0.82
vc - 250000 eb 0 eth1 0 we 0 wn 0 - smpl 16384 1.1815 ms/op 1.3674 ms/op 0.86
vc - 250000 eb 0 eth1 0 we 0 wn 0 nocache - smpl 16384 3.9757 ms/op 4.1587 ms/op 0.96
vc - 250000 eb 0 eth1 1 we 0 wn 0 - smpl 16384 2.7493 ms/op 2.5738 ms/op 1.07
vc - 250000 eb 0 eth1 1 we 0 wn 0 nocache - smpl 16384 6.8373 ms/op 7.0436 ms/op 0.97
Tree 40 250000 create 584.60 ms/op 548.54 ms/op 1.07
Tree 40 250000 get(125000) 228.36 ns/op 226.07 ns/op 1.01
Tree 40 250000 set(125000) 1.9738 us/op 1.7728 us/op 1.11
Tree 40 250000 toArray() 26.386 ms/op 25.632 ms/op 1.03
Tree 40 250000 iterate all - toArray() + loop 26.438 ms/op 24.728 ms/op 1.07
Tree 40 250000 iterate all - get(i) 107.16 ms/op 104.16 ms/op 1.03
MutableVector 250000 create 14.539 ms/op 13.071 ms/op 1.11
MutableVector 250000 get(125000) 10.593 ns/op 10.660 ns/op 0.99
MutableVector 250000 set(125000) 501.39 ns/op 479.40 ns/op 1.05
MutableVector 250000 toArray() 5.6554 ms/op 5.3482 ms/op 1.06
MutableVector 250000 iterate all - toArray() + loop 5.7812 ms/op 5.6773 ms/op 1.02
MutableVector 250000 iterate all - get(i) 2.6956 ms/op 2.7988 ms/op 0.96
Array 250000 create 5.5640 ms/op 5.3222 ms/op 1.05
Array 250000 clone - spread 2.4770 ms/op 2.4410 ms/op 1.01
Array 250000 get(125000) 1.1510 ns/op 1.1470 ns/op 1.00
Array 250000 set(125000) 1.1590 ns/op 1.1490 ns/op 1.01
Array 250000 iterate all - loop 150.97 us/op 151.94 us/op 0.99
effectiveBalanceIncrements clone Uint8Array 300000 41.120 us/op 37.301 us/op 1.10
effectiveBalanceIncrements clone MutableVector 300000 781.00 ns/op 721.00 ns/op 1.08
effectiveBalanceIncrements rw all Uint8Array 300000 243.90 us/op 243.85 us/op 1.00
effectiveBalanceIncrements rw all MutableVector 300000 158.58 ms/op 155.72 ms/op 1.02
phase0 afterProcessEpoch - 250000 vs - 7PWei 205.50 ms/op 200.35 ms/op 1.03
phase0 beforeProcessEpoch - 250000 vs - 7PWei 57.361 ms/op 56.632 ms/op 1.01
altair processEpoch - mainnet_e81889 549.02 ms/op 528.75 ms/op 1.04
mainnet_e81889 - altair beforeProcessEpoch 75.957 ms/op 117.98 ms/op 0.64
mainnet_e81889 - altair processJustificationAndFinalization 26.021 us/op 16.470 us/op 1.58
mainnet_e81889 - altair processInactivityUpdates 8.8369 ms/op 8.4344 ms/op 1.05
mainnet_e81889 - altair processRewardsAndPenalties 119.55 ms/op 75.793 ms/op 1.58
mainnet_e81889 - altair processRegistryUpdates 2.9370 us/op 4.5970 us/op 0.64
mainnet_e81889 - altair processSlashings 718.00 ns/op 914.00 ns/op 0.79
mainnet_e81889 - altair processEth1DataReset 684.00 ns/op 642.00 ns/op 1.07
mainnet_e81889 - altair processEffectiveBalanceUpdates 2.2628 ms/op 1.9807 ms/op 1.14
mainnet_e81889 - altair processSlashingsReset 4.4500 us/op 3.9500 us/op 1.13
mainnet_e81889 - altair processRandaoMixesReset 4.6120 us/op 4.9350 us/op 0.93
mainnet_e81889 - altair processHistoricalRootsUpdate 669.00 ns/op 919.00 ns/op 0.73
mainnet_e81889 - altair processParticipationFlagUpdates 2.7380 us/op 3.1380 us/op 0.87
mainnet_e81889 - altair processSyncCommitteeUpdates 1.2050 us/op 1.0310 us/op 1.17
mainnet_e81889 - altair afterProcessEpoch 217.23 ms/op 217.86 ms/op 1.00
phase0 processEpoch - mainnet_e58758 606.44 ms/op 597.74 ms/op 1.01
mainnet_e58758 - phase0 beforeProcessEpoch 227.03 ms/op 208.66 ms/op 1.09
mainnet_e58758 - phase0 processJustificationAndFinalization 21.462 us/op 17.015 us/op 1.26
mainnet_e58758 - phase0 processRewardsAndPenalties 132.73 ms/op 122.53 ms/op 1.08
mainnet_e58758 - phase0 processRegistryUpdates 8.5440 us/op 7.5340 us/op 1.13
mainnet_e58758 - phase0 processSlashings 707.00 ns/op 479.00 ns/op 1.48
mainnet_e58758 - phase0 processEth1DataReset 631.00 ns/op 534.00 ns/op 1.18
mainnet_e58758 - phase0 processEffectiveBalanceUpdates 1.9434 ms/op 1.7519 ms/op 1.11
mainnet_e58758 - phase0 processSlashingsReset 4.7440 us/op 3.2670 us/op 1.45
mainnet_e58758 - phase0 processRandaoMixesReset 5.1140 us/op 3.9920 us/op 1.28
mainnet_e58758 - phase0 processHistoricalRootsUpdate 657.00 ns/op 559.00 ns/op 1.18
mainnet_e58758 - phase0 processParticipationRecordUpdates 3.7740 us/op 3.5360 us/op 1.07
mainnet_e58758 - phase0 afterProcessEpoch 161.90 ms/op 162.29 ms/op 1.00
phase0 processEffectiveBalanceUpdates - 250000 normalcase 1.9361 ms/op 1.9661 ms/op 0.98
phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 2.3832 ms/op 2.3972 ms/op 0.99
altair processInactivityUpdates - 250000 normalcase 47.381 ms/op 50.035 ms/op 0.95
altair processInactivityUpdates - 250000 worstcase 50.388 ms/op 53.479 ms/op 0.94
phase0 processRegistryUpdates - 250000 normalcase 6.7010 us/op 6.9200 us/op 0.97
phase0 processRegistryUpdates - 250000 badcase_full_deposits 377.81 us/op 374.26 us/op 1.01
phase0 processRegistryUpdates - 250000 worstcase 0.5 224.11 ms/op 226.32 ms/op 0.99
altair processRewardsAndPenalties - 250000 normalcase 128.07 ms/op 131.67 ms/op 0.97
altair processRewardsAndPenalties - 250000 worstcase 131.95 ms/op 131.89 ms/op 1.00
phase0 getAttestationDeltas - 250000 normalcase 11.346 ms/op 10.985 ms/op 1.03
phase0 getAttestationDeltas - 250000 worstcase 11.622 ms/op 11.239 ms/op 1.03
phase0 processSlashings - 250000 worstcase 5.9347 ms/op 5.1436 ms/op 1.15
altair processSyncCommitteeUpdates - 250000 289.22 ms/op 296.04 ms/op 0.98
BeaconState.hashTreeRoot - No change 447.00 ns/op 493.00 ns/op 0.91
BeaconState.hashTreeRoot - 1 full validator 73.116 us/op 69.452 us/op 1.05
BeaconState.hashTreeRoot - 32 full validator 816.11 us/op 802.38 us/op 1.02
BeaconState.hashTreeRoot - 512 full validator 8.2139 ms/op 8.4747 ms/op 0.97
BeaconState.hashTreeRoot - 1 validator.effectiveBalance 90.340 us/op 86.621 us/op 1.04
BeaconState.hashTreeRoot - 32 validator.effectiveBalance 1.4137 ms/op 1.4219 ms/op 0.99
BeaconState.hashTreeRoot - 512 validator.effectiveBalance 16.775 ms/op 16.361 ms/op 1.03
BeaconState.hashTreeRoot - 1 balances 76.458 us/op 62.998 us/op 1.21
BeaconState.hashTreeRoot - 32 balances 727.74 us/op 714.97 us/op 1.02
BeaconState.hashTreeRoot - 512 balances 6.1654 ms/op 7.0490 ms/op 0.87
BeaconState.hashTreeRoot - 250000 balances 98.478 ms/op 108.09 ms/op 0.91
aggregationBits - 2048 els - zipIndexesInBitList 23.621 us/op 20.862 us/op 1.13
regular array get 100000 times 60.497 us/op 60.527 us/op 1.00
wrappedArray get 100000 times 60.507 us/op 60.477 us/op 1.00
arrayWithProxy get 100000 times 28.413 ms/op 29.243 ms/op 0.97
ssz.Root.equals 976.00 ns/op 1.0190 us/op 0.96
byteArrayEquals 977.00 ns/op 883.00 ns/op 1.11
shuffle list - 16384 els 11.396 ms/op 11.563 ms/op 0.99
shuffle list - 250000 els 168.09 ms/op 168.10 ms/op 1.00
processSlot - 1 slots 12.846 us/op 12.383 us/op 1.04
processSlot - 32 slots 1.9084 ms/op 1.8833 ms/op 1.01
getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei 393.33 us/op 325.36 us/op 1.21
getCommitteeAssignments - req 1 vs - 250000 vc 5.4378 ms/op 5.4409 ms/op 1.00
getCommitteeAssignments - req 100 vs - 250000 vc 8.0180 ms/op 7.9549 ms/op 1.01
getCommitteeAssignments - req 1000 vs - 250000 vc 8.5416 ms/op 8.4907 ms/op 1.01
RootCache.getBlockRootAtSlot - 250000 vs - 7PWei 8.0800 ns/op 7.5500 ns/op 1.07
state getBlockRootAtSlot - 250000 vs - 7PWei 1.1393 us/op 1.0071 us/op 1.13
computeProposers - vc 250000 16.738 ms/op 17.223 ms/op 0.97
computeEpochShuffling - vc 250000 170.37 ms/op 170.02 ms/op 1.00
getNextSyncCommittee - vc 250000 278.50 ms/op 290.14 ms/op 0.96

by benchmarkbot/action

@twoeths
Copy link
Contributor

twoeths commented Jan 5, 2023

@dapplion what happened in #4700 is head block existed as head but post state was not in the cache due to an error in the middle, should we add post state to state cache before adding block to forkchoice?

@dapplion
Copy link
Contributor Author

dapplion commented Jan 5, 2023

@dapplion what happened in #4700 is head block existed as head but post state was not in the cache due to an error in the middle, should we add post state to state cache before adding block to forkchoice?

The only await points are the DB writes, so all steps after 1. are executed in sequence. It's not possible for anything else to do a request to the cache, so adding to the fork-choice and adding to the cache will happen "atomically" if nothing throws.

If there are not awaits it's more of a question of what is allowed to throw before what.

Regarding the head state keep in mind there's a possibility the head state is not available, so it triggers regen and will be null for a while.

@dapplion dapplion force-pushed the dapplion/importblock-order branch from 84b4cdb to a565018 Compare January 5, 2023 11:13
@dapplion dapplion closed this Jan 5, 2023
@dapplion dapplion reopened this Jan 5, 2023
@dapplion
Copy link
Contributor Author

dapplion commented Jan 5, 2023

Closing + re-opening to trigger CI, somehow it got stuck

@wemeetagain wemeetagain merged commit 75b399e into unstable Jan 5, 2023
@wemeetagain wemeetagain deleted the dapplion/importblock-order branch January 5, 2023 18:17
Copy link
Contributor

@g11tech g11tech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good! reviewing post merge 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants