@lodestar/state-transition 1.35.0-dev.f80d2d52da → 1.35.0-dev.fcf8d024ea
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/block/externalData.d.ts.map +1 -0
- package/lib/block/index.d.ts +2 -2
- package/lib/block/index.d.ts.map +1 -0
- package/lib/block/index.js +2 -2
- package/lib/block/index.js.map +1 -1
- package/lib/block/initiateValidatorExit.d.ts.map +1 -0
- package/lib/block/isValidIndexedAttestation.d.ts.map +1 -0
- package/lib/block/processAttestationPhase0.d.ts.map +1 -0
- package/lib/block/processAttestationPhase0.js +1 -2
- package/lib/block/processAttestationPhase0.js.map +1 -1
- package/lib/block/processAttestations.d.ts.map +1 -0
- package/lib/block/processAttestationsAltair.d.ts +1 -1
- package/lib/block/processAttestationsAltair.d.ts.map +1 -0
- package/lib/block/processAttestationsAltair.js +1 -1
- package/lib/block/processAttestationsAltair.js.map +1 -1
- package/lib/block/processAttesterSlashing.d.ts.map +1 -0
- package/lib/block/processAttesterSlashing.js.map +1 -1
- package/lib/block/processBlobKzgCommitments.d.ts.map +1 -0
- package/lib/block/processBlockHeader.d.ts.map +1 -0
- package/lib/block/processBlsToExecutionChange.d.ts.map +1 -0
- package/lib/block/processBlsToExecutionChange.js.map +1 -1
- package/lib/block/processConsolidationRequest.d.ts.map +1 -0
- package/lib/block/processConsolidationRequest.js.map +1 -1
- package/lib/block/processDeposit.d.ts +2 -2
- package/lib/block/processDeposit.d.ts.map +1 -0
- package/lib/block/processDeposit.js +1 -1
- package/lib/block/processDeposit.js.map +1 -1
- package/lib/block/processDepositRequest.d.ts.map +1 -0
- package/lib/block/processDepositRequest.js.map +1 -1
- package/lib/block/processEth1Data.d.ts.map +1 -0
- package/lib/block/processExecutionPayload.d.ts.map +1 -0
- package/lib/block/processOperations.d.ts.map +1 -0
- package/lib/block/processOperations.js.map +1 -1
- package/lib/block/processProposerSlashing.d.ts.map +1 -0
- package/lib/block/processRandao.d.ts.map +1 -0
- package/lib/block/processSyncCommittee.d.ts.map +1 -0
- package/lib/block/processSyncCommittee.js +1 -2
- package/lib/block/processSyncCommittee.js.map +1 -1
- package/lib/block/processVoluntaryExit.d.ts.map +1 -0
- package/lib/block/processWithdrawalRequest.d.ts.map +1 -0
- package/lib/block/processWithdrawalRequest.js.map +1 -1
- package/lib/block/processWithdrawals.d.ts.map +1 -0
- package/lib/block/processWithdrawals.js.map +1 -1
- package/lib/block/slashValidator.d.ts.map +1 -0
- package/lib/block/slashValidator.js.map +1 -1
- package/lib/block/types.d.ts.map +1 -0
- package/lib/cache/effectiveBalanceIncrements.d.ts.map +1 -0
- package/lib/cache/epochCache.d.ts.map +1 -0
- package/lib/cache/epochCache.js +130 -0
- package/lib/cache/epochCache.js.map +1 -1
- package/lib/cache/epochTransitionCache.d.ts.map +1 -0
- package/lib/cache/epochTransitionCache.js.map +1 -1
- package/lib/cache/pubkeyCache.d.ts.map +1 -0
- package/lib/cache/rewardCache.d.ts.map +1 -0
- package/lib/cache/stateCache.d.ts.map +1 -0
- package/lib/cache/syncCommitteeCache.d.ts.map +1 -0
- package/lib/cache/types.d.ts +2 -2
- package/lib/cache/types.d.ts.map +1 -0
- package/lib/constants/constants.d.ts.map +1 -0
- package/lib/constants/index.d.ts.map +1 -0
- package/lib/epoch/computeUnrealizedCheckpoints.d.ts.map +1 -0
- package/lib/epoch/getAttestationDeltas.d.ts.map +1 -0
- package/lib/epoch/getRewardsAndPenalties.d.ts.map +1 -0
- package/lib/epoch/index.d.ts.map +1 -0
- package/lib/epoch/index.js.map +1 -1
- package/lib/epoch/processEffectiveBalanceUpdates.d.ts.map +1 -0
- package/lib/epoch/processEth1DataReset.d.ts.map +1 -0
- package/lib/epoch/processHistoricalRootsUpdate.d.ts.map +1 -0
- package/lib/epoch/processHistoricalSummariesUpdate.d.ts.map +1 -0
- package/lib/epoch/processInactivityUpdates.d.ts.map +1 -0
- package/lib/epoch/processJustificationAndFinalization.d.ts.map +1 -0
- package/lib/epoch/processParticipationFlagUpdates.d.ts.map +1 -0
- package/lib/epoch/processParticipationRecordUpdates.d.ts.map +1 -0
- package/lib/epoch/processPendingAttestations.d.ts.map +1 -0
- package/lib/epoch/processPendingConsolidations.d.ts.map +1 -0
- package/lib/epoch/processPendingDeposits.d.ts.map +1 -0
- package/lib/epoch/processProposerLookahead.d.ts.map +1 -0
- package/lib/epoch/processRandaoMixesReset.d.ts.map +1 -0
- package/lib/epoch/processRegistryUpdates.d.ts.map +1 -0
- package/lib/epoch/processRewardsAndPenalties.d.ts.map +1 -0
- package/lib/epoch/processSlashings.d.ts.map +1 -0
- package/lib/epoch/processSlashings.js.map +1 -1
- package/lib/epoch/processSlashingsReset.d.ts.map +1 -0
- package/lib/epoch/processSyncCommitteeUpdates.d.ts.map +1 -0
- package/lib/index.d.ts +17 -17
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +16 -16
- package/lib/index.js.map +1 -1
- package/lib/metrics.d.ts.map +1 -0
- package/lib/signatureSets/attesterSlashings.d.ts.map +1 -0
- package/lib/signatureSets/blsToExecutionChange.d.ts.map +1 -0
- package/lib/signatureSets/blsToExecutionChange.js.map +1 -1
- package/lib/signatureSets/index.d.ts +1 -1
- package/lib/signatureSets/index.d.ts.map +1 -0
- package/lib/signatureSets/index.js +1 -1
- package/lib/signatureSets/index.js.map +1 -1
- package/lib/signatureSets/indexedAttestation.d.ts.map +1 -0
- package/lib/signatureSets/proposer.d.ts.map +1 -0
- package/lib/signatureSets/proposerSlashings.d.ts.map +1 -0
- package/lib/signatureSets/randao.d.ts.map +1 -0
- package/lib/signatureSets/voluntaryExits.d.ts.map +1 -0
- package/lib/slot/index.d.ts.map +1 -0
- package/lib/slot/upgradeStateToAltair.d.ts.map +1 -0
- package/lib/slot/upgradeStateToBellatrix.d.ts.map +1 -0
- package/lib/slot/upgradeStateToCapella.d.ts.map +1 -0
- package/lib/slot/upgradeStateToDeneb.d.ts +1 -2
- package/lib/slot/upgradeStateToDeneb.d.ts.map +1 -0
- package/lib/slot/upgradeStateToDeneb.js.map +1 -1
- package/lib/slot/upgradeStateToElectra.d.ts.map +1 -0
- package/lib/slot/upgradeStateToFulu.d.ts.map +1 -0
- package/lib/slot/upgradeStateToGloas.d.ts.map +1 -0
- package/lib/slot/upgradeStateToGloas.js +2 -2
- package/lib/slot/upgradeStateToGloas.js.map +1 -1
- package/lib/stateTransition.d.ts.map +1 -0
- package/lib/types.d.ts +2 -2
- package/lib/types.d.ts.map +1 -0
- package/lib/util/aggregator.d.ts.map +1 -0
- package/lib/util/altair.d.ts.map +1 -0
- package/lib/util/array.d.ts.map +1 -0
- package/lib/util/attestation.d.ts.map +1 -0
- package/lib/util/attesterStatus.d.ts.map +1 -0
- package/lib/util/balance.d.ts.map +1 -0
- package/lib/util/blindedBlock.d.ts.map +1 -0
- package/lib/util/blindedBlock.js.map +1 -1
- package/lib/util/blockRoot.d.ts.map +1 -0
- package/lib/util/calculateCommitteeAssignments.d.ts.map +1 -0
- package/lib/util/capella.d.ts.map +1 -0
- package/lib/util/computeAnchorCheckpoint.d.ts.map +1 -0
- package/lib/util/deposit.d.ts.map +1 -0
- package/lib/util/domain.d.ts.map +1 -0
- package/lib/util/electra.d.ts.map +1 -0
- package/lib/util/epoch.d.ts.map +1 -0
- package/lib/util/epochShuffling.d.ts.map +1 -0
- package/lib/util/execution.d.ts.map +1 -0
- package/lib/util/execution.js.map +1 -1
- package/lib/util/finality.d.ts.map +1 -0
- package/lib/util/fulu.d.ts.map +1 -0
- package/lib/util/genesis.d.ts.map +1 -0
- package/lib/util/genesis.js +3 -6
- package/lib/util/genesis.js.map +1 -1
- package/lib/util/index.d.ts +5 -5
- package/lib/util/index.d.ts.map +1 -0
- package/lib/util/index.js +5 -5
- package/lib/util/index.js.map +1 -1
- package/lib/util/interop.d.ts.map +1 -0
- package/lib/util/interop.js +1 -1
- package/lib/util/interop.js.map +1 -1
- package/lib/util/loadState/findModifiedInactivityScores.d.ts.map +1 -0
- package/lib/util/loadState/findModifiedValidators.d.ts.map +1 -0
- package/lib/util/loadState/index.d.ts.map +1 -0
- package/lib/util/loadState/loadState.d.ts.map +1 -0
- package/lib/util/loadState/loadValidator.d.ts.map +1 -0
- package/lib/util/rootCache.d.ts.map +1 -0
- package/lib/util/rootCache.js +5 -2
- package/lib/util/rootCache.js.map +1 -1
- package/lib/util/seed.d.ts.map +1 -0
- package/lib/util/seed.js +1 -2
- package/lib/util/seed.js.map +1 -1
- package/lib/util/shufflingDecisionRoot.d.ts.map +1 -0
- package/lib/util/signatureSets.d.ts.map +1 -0
- package/lib/util/signingRoot.d.ts.map +1 -0
- package/lib/util/slot.d.ts +0 -1
- package/lib/util/slot.d.ts.map +1 -0
- package/lib/util/slot.js +1 -5
- package/lib/util/slot.js.map +1 -1
- package/lib/util/sszBytes.d.ts.map +1 -0
- package/lib/util/syncCommittee.d.ts.map +1 -0
- package/lib/util/targetUnslashedBalance.d.ts.map +1 -0
- package/lib/util/validator.d.ts.map +1 -0
- package/lib/util/weakSubjectivity.d.ts.map +1 -0
- package/lib/util/weakSubjectivity.js.map +1 -1
- package/package.json +13 -11
- package/src/block/externalData.ts +26 -0
- package/src/block/index.ts +81 -0
- package/src/block/initiateValidatorExit.ts +62 -0
- package/src/block/isValidIndexedAttestation.ts +70 -0
- package/src/block/processAttestationPhase0.ts +158 -0
- package/src/block/processAttestations.ts +25 -0
- package/src/block/processAttestationsAltair.ts +184 -0
- package/src/block/processAttesterSlashing.ts +59 -0
- package/src/block/processBlobKzgCommitments.ts +21 -0
- package/src/block/processBlockHeader.ts +54 -0
- package/src/block/processBlsToExecutionChange.ts +78 -0
- package/src/block/processConsolidationRequest.ts +147 -0
- package/src/block/processDeposit.ts +166 -0
- package/src/block/processDepositRequest.ts +19 -0
- package/src/block/processEth1Data.ts +86 -0
- package/src/block/processExecutionPayload.ts +84 -0
- package/src/block/processOperations.ts +83 -0
- package/src/block/processProposerSlashing.ts +66 -0
- package/src/block/processRandao.ts +27 -0
- package/src/block/processSyncCommittee.ts +117 -0
- package/src/block/processVoluntaryExit.ts +55 -0
- package/src/block/processWithdrawalRequest.ts +98 -0
- package/src/block/processWithdrawals.ts +207 -0
- package/src/block/slashValidator.ts +98 -0
- package/src/block/types.ts +9 -0
- package/src/cache/effectiveBalanceIncrements.ts +39 -0
- package/src/cache/epochCache.ts +1213 -0
- package/src/cache/epochTransitionCache.ts +542 -0
- package/src/cache/pubkeyCache.ts +33 -0
- package/src/cache/rewardCache.ts +19 -0
- package/src/cache/stateCache.ts +268 -0
- package/src/cache/syncCommitteeCache.ts +96 -0
- package/src/cache/types.ts +18 -0
- package/src/constants/constants.ts +12 -0
- package/src/constants/index.ts +1 -0
- package/src/epoch/computeUnrealizedCheckpoints.ts +55 -0
- package/src/epoch/getAttestationDeltas.ts +169 -0
- package/src/epoch/getRewardsAndPenalties.ts +137 -0
- package/src/epoch/index.ts +202 -0
- package/src/epoch/processEffectiveBalanceUpdates.ts +111 -0
- package/src/epoch/processEth1DataReset.ts +17 -0
- package/src/epoch/processHistoricalRootsUpdate.ts +25 -0
- package/src/epoch/processHistoricalSummariesUpdate.ts +23 -0
- package/src/epoch/processInactivityUpdates.ts +60 -0
- package/src/epoch/processJustificationAndFinalization.ts +90 -0
- package/src/epoch/processParticipationFlagUpdates.ts +27 -0
- package/src/epoch/processParticipationRecordUpdates.ts +14 -0
- package/src/epoch/processPendingAttestations.ts +75 -0
- package/src/epoch/processPendingConsolidations.ts +59 -0
- package/src/epoch/processPendingDeposits.ts +136 -0
- package/src/epoch/processProposerLookahead.ts +39 -0
- package/src/epoch/processRandaoMixesReset.ts +18 -0
- package/src/epoch/processRegistryUpdates.ts +65 -0
- package/src/epoch/processRewardsAndPenalties.ts +58 -0
- package/src/epoch/processSlashings.ts +97 -0
- package/src/epoch/processSlashingsReset.ts +20 -0
- package/src/epoch/processSyncCommitteeUpdates.ts +44 -0
- package/src/index.ts +67 -0
- package/src/metrics.ts +169 -0
- package/src/signatureSets/attesterSlashings.ts +39 -0
- package/src/signatureSets/blsToExecutionChange.ts +43 -0
- package/src/signatureSets/index.ts +73 -0
- package/src/signatureSets/indexedAttestation.ts +51 -0
- package/src/signatureSets/proposer.ts +47 -0
- package/src/signatureSets/proposerSlashings.ts +41 -0
- package/src/signatureSets/randao.ts +31 -0
- package/src/signatureSets/voluntaryExits.ts +44 -0
- package/src/slot/index.ts +32 -0
- package/src/slot/upgradeStateToAltair.ts +149 -0
- package/src/slot/upgradeStateToBellatrix.ts +63 -0
- package/src/slot/upgradeStateToCapella.ts +71 -0
- package/src/slot/upgradeStateToDeneb.ts +40 -0
- package/src/slot/upgradeStateToElectra.ts +126 -0
- package/src/slot/upgradeStateToFulu.ts +31 -0
- package/src/slot/upgradeStateToGloas.ts +29 -0
- package/src/stateTransition.ts +305 -0
- package/src/types.ts +26 -0
- package/src/util/aggregator.ts +33 -0
- package/src/util/altair.ts +13 -0
- package/src/util/array.ts +53 -0
- package/src/util/attestation.ts +36 -0
- package/src/util/attesterStatus.ts +83 -0
- package/src/util/balance.ts +83 -0
- package/src/util/blindedBlock.ts +144 -0
- package/src/util/blockRoot.ts +72 -0
- package/src/util/calculateCommitteeAssignments.ts +43 -0
- package/src/util/capella.ts +8 -0
- package/src/util/computeAnchorCheckpoint.ts +38 -0
- package/src/util/deposit.ts +22 -0
- package/src/util/domain.ts +31 -0
- package/src/util/electra.ts +68 -0
- package/src/util/epoch.ts +135 -0
- package/src/util/epochShuffling.ts +185 -0
- package/src/util/execution.ts +177 -0
- package/src/util/finality.ts +17 -0
- package/src/util/fulu.ts +43 -0
- package/src/util/genesis.ts +340 -0
- package/src/util/index.ts +29 -0
- package/src/util/interop.ts +22 -0
- package/src/util/loadState/findModifiedInactivityScores.ts +47 -0
- package/src/util/loadState/findModifiedValidators.ts +46 -0
- package/src/util/loadState/index.ts +2 -0
- package/src/util/loadState/loadState.ts +225 -0
- package/src/util/loadState/loadValidator.ts +77 -0
- package/src/util/rootCache.ts +37 -0
- package/src/util/seed.ts +381 -0
- package/src/util/shufflingDecisionRoot.ts +78 -0
- package/src/util/signatureSets.ts +65 -0
- package/src/util/signingRoot.ts +13 -0
- package/src/util/slot.ts +22 -0
- package/src/util/sszBytes.ts +52 -0
- package/src/util/syncCommittee.ts +69 -0
- package/src/util/targetUnslashedBalance.ts +30 -0
- package/src/util/validator.ts +105 -0
- package/src/util/weakSubjectivity.ts +186 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import {PublicKey} from "@chainsafe/blst";
|
|
2
|
+
import {BeaconConfig} from "@lodestar/config";
|
|
3
|
+
import {loadState} from "../util/loadState/loadState.js";
|
|
4
|
+
import {EpochCache, EpochCacheImmutableData, EpochCacheOpts} from "./epochCache.js";
|
|
5
|
+
import {RewardCache, createEmptyRewardCache} from "./rewardCache.js";
|
|
6
|
+
import {
|
|
7
|
+
BeaconStateAllForks,
|
|
8
|
+
BeaconStateAltair,
|
|
9
|
+
BeaconStateBellatrix,
|
|
10
|
+
BeaconStateCapella,
|
|
11
|
+
BeaconStateDeneb,
|
|
12
|
+
BeaconStateElectra,
|
|
13
|
+
BeaconStateExecutions,
|
|
14
|
+
BeaconStateFulu,
|
|
15
|
+
BeaconStateGloas,
|
|
16
|
+
BeaconStatePhase0,
|
|
17
|
+
} from "./types.js";
|
|
18
|
+
|
|
19
|
+
export type BeaconStateCache = {
|
|
20
|
+
config: BeaconConfig;
|
|
21
|
+
epochCtx: EpochCache;
|
|
22
|
+
/** Count of clones created from this BeaconStateCache instance. readonly to prevent accidental usage downstream */
|
|
23
|
+
readonly clonedCount: number;
|
|
24
|
+
readonly clonedCountWithTransferCache: number;
|
|
25
|
+
readonly createdWithTransferCache: boolean;
|
|
26
|
+
proposerRewards: RewardCache;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
type Mutable<T> = {
|
|
30
|
+
-readonly [P in keyof T]: T[P];
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
type BeaconStateCacheMutable = Mutable<BeaconStateCache>;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* `BeaconState` with various caches
|
|
37
|
+
*
|
|
38
|
+
* Currently contains the following:
|
|
39
|
+
* - The full list of network params, ssz types, and fork schedule
|
|
40
|
+
* - The ssz type for the state
|
|
41
|
+
* - The full merkle tree representation of the state
|
|
42
|
+
* - A cache of shufflings, committees, proposers, expanded pubkeys
|
|
43
|
+
* - A flat copy of validators (for fast access/iteration)
|
|
44
|
+
*
|
|
45
|
+
* ### BeaconState data representation tradeoffs:
|
|
46
|
+
*
|
|
47
|
+
* Requirements of a BeaconState:
|
|
48
|
+
* - Block processing and epoch processing be performant to not block the node. This functions requires to iterate over
|
|
49
|
+
* very large arrays fast, while doing random mutations or big mutations. After them the state must be hashed.
|
|
50
|
+
* Processing times: (ideal / current / maximum)
|
|
51
|
+
* - block processing: 20ms / 200ms / 500ms
|
|
52
|
+
* - epoch processing: 200ms / 2s / 4s
|
|
53
|
+
*
|
|
54
|
+
* - BeaconState must be memory efficient. Data should only be represented once in a succint manner. Uint8Arrays are
|
|
55
|
+
* expensive, native types are not.
|
|
56
|
+
* - BeaconState must be hashed efficiently. Data must be merkelized before hashing so the conversion to merkelized
|
|
57
|
+
* must be fast or be done already. It must must persist a hashing cache that should be structurally shared between
|
|
58
|
+
* states for memory efficiency.
|
|
59
|
+
* - BeaconState raw data changes sparsingly, so it should be structurally shared between states for memory efficiency
|
|
60
|
+
*
|
|
61
|
+
* Summary of goals:
|
|
62
|
+
* - Structurally share data + hashing cache
|
|
63
|
+
* - Very fast read and iteration over large arrays
|
|
64
|
+
* - Fast bulk writes and somewhat fast single writes
|
|
65
|
+
* - Fast merkelization of data for hashing
|
|
66
|
+
*
|
|
67
|
+
* #### state.validators
|
|
68
|
+
*
|
|
69
|
+
* 91% of all memory merkelized is state.validators. In normal network conditions state.validators changes rarely.
|
|
70
|
+
* However for epoch processing the entire array must be iterated and read. So we need fast reads and slow writes.
|
|
71
|
+
* Tradeoffs to achieve that:
|
|
72
|
+
* - Represent leaf data with native JS types (deserialized form)
|
|
73
|
+
* - Use a single Tree for structurally sharing leaf data + hashing cache
|
|
74
|
+
* - Keep only the root cached on leaf nodes
|
|
75
|
+
* - Micro-optimizations (TODO):
|
|
76
|
+
* - Keep also the root of the node above pubkey and withdrawal creds. Will never change
|
|
77
|
+
* - Keep pubkey + withdrawal creds in the same Uint8Array
|
|
78
|
+
* - Have a global pubkey + withdrawal creds Uint8Array global cache, like with the index2pubkey cache
|
|
79
|
+
*
|
|
80
|
+
* ------------------
|
|
81
|
+
*
|
|
82
|
+
* _Previous JSDocs for `BeaconStateContext`_
|
|
83
|
+
*
|
|
84
|
+
* Cache useful data associated to a specific state.
|
|
85
|
+
* Optimize processing speed of block processing + gossip validation while having a low memory cost.
|
|
86
|
+
*
|
|
87
|
+
* Previously BeaconStateContext included:
|
|
88
|
+
* ```ts
|
|
89
|
+
* validators: CachedValidatorList & T["validators"];
|
|
90
|
+
* balances: CachedBalanceList & T["balances"];
|
|
91
|
+
* inactivityScores: CachedInactivityScoreList & Number64[];
|
|
92
|
+
* ```
|
|
93
|
+
*
|
|
94
|
+
* Those caches where removed since they are no strictly necessary to make the epoch transition faster,
|
|
95
|
+
* but have a high memory cost. Note that all data was duplicated between the Tree and MutableVector.
|
|
96
|
+
* 1. TreeView, for efficient hashing
|
|
97
|
+
* 2. MutableVector (persistent-ts) with StructBacked validator objects for fast accessing and iteration
|
|
98
|
+
*
|
|
99
|
+
* ### validators
|
|
100
|
+
* state.validators is the heaviest data structure in the state. As TreeView, the leafs account for 91% with
|
|
101
|
+
* 200_000 validators. It requires ~ 2_000_000 Uint8Array instances with total memory of ~ 400MB.
|
|
102
|
+
* However its contents don't change very often. Validators only change when;
|
|
103
|
+
* - they first deposit
|
|
104
|
+
* - they dip from 32 effective balance to 31 (pretty much only when inactive for very long, or slashed)
|
|
105
|
+
* - they activate (once)
|
|
106
|
+
* - they exit (once)
|
|
107
|
+
* - they get slashed (max once)
|
|
108
|
+
*
|
|
109
|
+
* ### balances
|
|
110
|
+
* The balances array completely changes at the epoch boundary, where almost all the validator balances
|
|
111
|
+
* are updated. However it may have tiny changes during block processing if:
|
|
112
|
+
* - On a valid deposit
|
|
113
|
+
* - Validator gets slashed
|
|
114
|
+
* - On altair, the block proposer. Optimized to only happen once per block
|
|
115
|
+
*
|
|
116
|
+
* ### epochParticipation
|
|
117
|
+
* epochParticipation changes continuously through the epoch for each partipation bit of each valid attestation in the state.
|
|
118
|
+
* The entire structure is dropped after two epochs.
|
|
119
|
+
*
|
|
120
|
+
* ### inactivityScores
|
|
121
|
+
* inactivityScores can be changed only:
|
|
122
|
+
* - At the epoch transition. It only changes when a validator is offline. So it may change a bit but not
|
|
123
|
+
* a lot on normal network conditions.
|
|
124
|
+
* - During block processing, when a validator joins a new 0 entry is pushed
|
|
125
|
+
*
|
|
126
|
+
* RESULT: Don't keep a duplicated structure around always. During block processing just push to the tree. During
|
|
127
|
+
* epoch processing some temporary flat structures are computed but dropped after processing the epoch.
|
|
128
|
+
*/
|
|
129
|
+
export type CachedBeaconState<T extends BeaconStateAllForks> = T & BeaconStateCache;
|
|
130
|
+
|
|
131
|
+
export type CachedBeaconStatePhase0 = CachedBeaconState<BeaconStatePhase0>;
|
|
132
|
+
export type CachedBeaconStateAltair = CachedBeaconState<BeaconStateAltair>;
|
|
133
|
+
export type CachedBeaconStateBellatrix = CachedBeaconState<BeaconStateBellatrix>;
|
|
134
|
+
export type CachedBeaconStateCapella = CachedBeaconState<BeaconStateCapella>;
|
|
135
|
+
export type CachedBeaconStateDeneb = CachedBeaconState<BeaconStateDeneb>;
|
|
136
|
+
export type CachedBeaconStateElectra = CachedBeaconState<BeaconStateElectra>;
|
|
137
|
+
export type CachedBeaconStateFulu = CachedBeaconState<BeaconStateFulu>;
|
|
138
|
+
export type CachedBeaconStateGloas = CachedBeaconState<BeaconStateGloas>;
|
|
139
|
+
|
|
140
|
+
export type CachedBeaconStateAllForks = CachedBeaconState<BeaconStateAllForks>;
|
|
141
|
+
export type CachedBeaconStateExecutions = CachedBeaconState<BeaconStateExecutions>;
|
|
142
|
+
/**
|
|
143
|
+
* Create CachedBeaconState computing a new EpochCache instance
|
|
144
|
+
* TODO ELECTRA: rename this to createFinalizedCachedBeaconState() as it's intended for finalized state only
|
|
145
|
+
*/
|
|
146
|
+
export function createCachedBeaconState<T extends BeaconStateAllForks>(
|
|
147
|
+
state: T,
|
|
148
|
+
immutableData: EpochCacheImmutableData,
|
|
149
|
+
opts?: EpochCacheOpts
|
|
150
|
+
): T & BeaconStateCache {
|
|
151
|
+
const epochCache = EpochCache.createFromState(state, immutableData, opts);
|
|
152
|
+
const cachedState = getCachedBeaconState(state, {
|
|
153
|
+
config: immutableData.config,
|
|
154
|
+
epochCtx: epochCache,
|
|
155
|
+
clonedCount: 0,
|
|
156
|
+
clonedCountWithTransferCache: 0,
|
|
157
|
+
createdWithTransferCache: false,
|
|
158
|
+
proposerRewards: createEmptyRewardCache(),
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
return cachedState;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Create a CachedBeaconState given a cached seed state and state bytes
|
|
166
|
+
* This guarantees that the returned state shares the same tree with the seed state
|
|
167
|
+
* Check loadState() api for more details
|
|
168
|
+
* // TODO: rename to loadUnfinalizedCachedBeaconState() due to ELECTRA
|
|
169
|
+
*/
|
|
170
|
+
export function loadCachedBeaconState<T extends BeaconStateAllForks & BeaconStateCache>(
|
|
171
|
+
cachedSeedState: T,
|
|
172
|
+
stateBytes: Uint8Array,
|
|
173
|
+
opts?: EpochCacheOpts,
|
|
174
|
+
seedValidatorsBytes?: Uint8Array
|
|
175
|
+
): T {
|
|
176
|
+
const {state: migratedState, modifiedValidators} = loadState(
|
|
177
|
+
cachedSeedState.config,
|
|
178
|
+
cachedSeedState,
|
|
179
|
+
stateBytes,
|
|
180
|
+
seedValidatorsBytes
|
|
181
|
+
);
|
|
182
|
+
const {pubkey2index, index2pubkey, shufflingCache} = cachedSeedState.epochCtx;
|
|
183
|
+
// Get the validators sub tree once for all the loop
|
|
184
|
+
const validators = migratedState.validators;
|
|
185
|
+
for (const validatorIndex of modifiedValidators) {
|
|
186
|
+
const validator = validators.getReadonly(validatorIndex);
|
|
187
|
+
const pubkey = validator.pubkey;
|
|
188
|
+
pubkey2index.set(pubkey, validatorIndex);
|
|
189
|
+
index2pubkey[validatorIndex] = PublicKey.fromBytes(pubkey);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return createCachedBeaconState(
|
|
193
|
+
migratedState,
|
|
194
|
+
{
|
|
195
|
+
config: cachedSeedState.config,
|
|
196
|
+
pubkey2index,
|
|
197
|
+
index2pubkey,
|
|
198
|
+
shufflingCache,
|
|
199
|
+
},
|
|
200
|
+
{...(opts ?? {}), ...{skipSyncPubkeys: true}}
|
|
201
|
+
) as T;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Attach an already computed BeaconStateCache to a BeaconState object
|
|
206
|
+
*/
|
|
207
|
+
export function getCachedBeaconState<T extends BeaconStateAllForks>(
|
|
208
|
+
state: T,
|
|
209
|
+
cache: BeaconStateCache
|
|
210
|
+
): T & BeaconStateCache {
|
|
211
|
+
const cachedState = state as T & BeaconStateCache;
|
|
212
|
+
cachedState.config = cache.config;
|
|
213
|
+
cachedState.epochCtx = cache.epochCtx;
|
|
214
|
+
(cachedState as BeaconStateCacheMutable).clonedCount = cache.clonedCount;
|
|
215
|
+
(cachedState as BeaconStateCacheMutable).clonedCountWithTransferCache = cache.clonedCountWithTransferCache;
|
|
216
|
+
(cachedState as BeaconStateCacheMutable).createdWithTransferCache = cache.createdWithTransferCache;
|
|
217
|
+
cachedState.proposerRewards = cache.proposerRewards;
|
|
218
|
+
|
|
219
|
+
// Overwrite .clone function to preserve cache
|
|
220
|
+
// TreeViewDU.clone() creates a new object that does not have the attached cache
|
|
221
|
+
const viewDUClone = cachedState.clone.bind(cachedState);
|
|
222
|
+
|
|
223
|
+
function clone(this: T & BeaconStateCache, dontTransferCache?: boolean): T & BeaconStateCache {
|
|
224
|
+
const viewDUCloned = viewDUClone(dontTransferCache);
|
|
225
|
+
|
|
226
|
+
// Override `readonly` attribute in single place where `.clonedCount` is incremented
|
|
227
|
+
(this as BeaconStateCacheMutable).clonedCount++;
|
|
228
|
+
|
|
229
|
+
if (!dontTransferCache) {
|
|
230
|
+
(this as BeaconStateCacheMutable).clonedCountWithTransferCache++;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return getCachedBeaconState(viewDUCloned, {
|
|
234
|
+
config: this.config,
|
|
235
|
+
epochCtx: this.epochCtx.clone(),
|
|
236
|
+
clonedCount: 0,
|
|
237
|
+
clonedCountWithTransferCache: 0,
|
|
238
|
+
createdWithTransferCache: !dontTransferCache,
|
|
239
|
+
proposerRewards: createEmptyRewardCache(), // this sets the rewards to 0 while cloning new state
|
|
240
|
+
}) as T & BeaconStateCache;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
cachedState.clone = clone as typeof viewDUClone;
|
|
244
|
+
|
|
245
|
+
return cachedState;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Typeguard to check if a state contains a BeaconStateCache
|
|
250
|
+
*/
|
|
251
|
+
export function isCachedBeaconState<T extends BeaconStateAllForks>(
|
|
252
|
+
state: T | (T & BeaconStateCache)
|
|
253
|
+
): state is T & BeaconStateCache {
|
|
254
|
+
return (state as T & BeaconStateCache).epochCtx !== undefined;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Given a CachedBeaconState, check if validators array internal cache is populated.
|
|
258
|
+
// This cache is populated during epoch transition, and should be preserved for performance.
|
|
259
|
+
// If the cache is missing too often, means that our clone strategy is not working well.
|
|
260
|
+
export function isStateValidatorsNodesPopulated(state: CachedBeaconStateAllForks): boolean {
|
|
261
|
+
// biome-ignore lint/complexity/useLiteralKeys: It is a private attribute
|
|
262
|
+
return state.validators["nodesPopulated"] === true;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export function isStateBalancesNodesPopulated(state: CachedBeaconStateAllForks): boolean {
|
|
266
|
+
// biome-ignore lint/complexity/useLiteralKeys: It is a private attribute
|
|
267
|
+
return state.balances["nodesPopulated"] === true;
|
|
268
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import {PubkeyIndexMap} from "@chainsafe/pubkey-index-map";
|
|
2
|
+
import {CompositeViewDU} from "@chainsafe/ssz";
|
|
3
|
+
import {ValidatorIndex, ssz} from "@lodestar/types";
|
|
4
|
+
import {toPubkeyHex} from "@lodestar/utils";
|
|
5
|
+
|
|
6
|
+
type ValidatorSyncCommitteeIndexMap = Map<ValidatorIndex, number[]>;
|
|
7
|
+
|
|
8
|
+
export type SyncCommitteeCache = {
|
|
9
|
+
/**
|
|
10
|
+
* Update freq: every ~ 27h.
|
|
11
|
+
* Memory cost: 512 Number integers.
|
|
12
|
+
*/
|
|
13
|
+
validatorIndices: Uint32Array;
|
|
14
|
+
/**
|
|
15
|
+
* Update freq: every ~ 27h.
|
|
16
|
+
* Memory cost: Map of Number -> Number with 512 entries.
|
|
17
|
+
* Note: it stores the position indices in sync committee for each sync committee validator
|
|
18
|
+
*/
|
|
19
|
+
validatorIndexMap: ValidatorSyncCommitteeIndexMap;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** Placeholder object for pre-altair fork */
|
|
23
|
+
export class SyncCommitteeCacheEmpty implements SyncCommitteeCache {
|
|
24
|
+
get validatorIndices(): Uint32Array {
|
|
25
|
+
throw Error("Empty SyncCommitteeCache");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get validatorIndexMap(): ValidatorSyncCommitteeIndexMap {
|
|
29
|
+
throw Error("Empty SyncCommitteeCache");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function getSyncCommitteeCache(validatorIndices: Uint32Array): SyncCommitteeCache {
|
|
34
|
+
return {
|
|
35
|
+
validatorIndices,
|
|
36
|
+
validatorIndexMap: computeValidatorSyncCommitteeIndexMap(validatorIndices),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function computeSyncCommitteeCache(
|
|
41
|
+
syncCommittee: CompositeViewDU<typeof ssz.altair.SyncCommittee>,
|
|
42
|
+
pubkey2index: PubkeyIndexMap
|
|
43
|
+
): SyncCommitteeCache {
|
|
44
|
+
const validatorIndices = computeSyncCommitteeValidatorIndices(syncCommittee, pubkey2index);
|
|
45
|
+
const validatorIndexMap = computeValidatorSyncCommitteeIndexMap(validatorIndices);
|
|
46
|
+
return {
|
|
47
|
+
validatorIndices,
|
|
48
|
+
validatorIndexMap,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Compute all position index in sync committee for all validatorIndexes in `syncCommitteeIndexes`.
|
|
54
|
+
* Helps reduce work necessary to verify a validatorIndex belongs in a sync committee and which.
|
|
55
|
+
* This is similar to compute_subnets_for_sync_committee in https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.5/specs/altair/validator.md
|
|
56
|
+
*/
|
|
57
|
+
export function computeValidatorSyncCommitteeIndexMap(
|
|
58
|
+
validatorIndices: ArrayLike<ValidatorIndex>
|
|
59
|
+
): ValidatorSyncCommitteeIndexMap {
|
|
60
|
+
const map = new Map<ValidatorIndex, number[]>();
|
|
61
|
+
|
|
62
|
+
for (let i = 0, len = validatorIndices.length; i < len; i++) {
|
|
63
|
+
const validatorIndex = validatorIndices[i];
|
|
64
|
+
let indexes = map.get(validatorIndex);
|
|
65
|
+
if (!indexes) {
|
|
66
|
+
indexes = [];
|
|
67
|
+
map.set(validatorIndex, indexes);
|
|
68
|
+
}
|
|
69
|
+
if (!indexes.includes(i)) {
|
|
70
|
+
indexes.push(i);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return map;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Extract validator indices from current and next sync committee
|
|
79
|
+
*/
|
|
80
|
+
function computeSyncCommitteeValidatorIndices(
|
|
81
|
+
syncCommittee: CompositeViewDU<typeof ssz.altair.SyncCommittee>,
|
|
82
|
+
pubkey2index: PubkeyIndexMap
|
|
83
|
+
): Uint32Array {
|
|
84
|
+
const pubkeys = syncCommittee.pubkeys.getAllReadonly();
|
|
85
|
+
const validatorIndices = new Uint32Array(pubkeys.length);
|
|
86
|
+
for (const [i, pubkey] of pubkeys.entries()) {
|
|
87
|
+
const validatorIndex = pubkey2index.get(pubkey);
|
|
88
|
+
if (validatorIndex === null) {
|
|
89
|
+
throw Error(`SyncCommittee pubkey is unknown ${toPubkeyHex(pubkey)}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
validatorIndices[i] = validatorIndex;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return validatorIndices;
|
|
96
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {CompositeViewDU} from "@chainsafe/ssz";
|
|
2
|
+
import {ForkAll, ForkName, ForkPostBellatrix, ForkPreGloas} from "@lodestar/params";
|
|
3
|
+
import {Epoch, RootHex, SSZTypesFor} from "@lodestar/types";
|
|
4
|
+
import {EpochShuffling} from "../util/epochShuffling.js";
|
|
5
|
+
|
|
6
|
+
export type BeaconStatePhase0 = CompositeViewDU<SSZTypesFor<ForkName.phase0, "BeaconState">>;
|
|
7
|
+
export type BeaconStateAltair = CompositeViewDU<SSZTypesFor<ForkName.altair, "BeaconState">>;
|
|
8
|
+
export type BeaconStateBellatrix = CompositeViewDU<SSZTypesFor<ForkName.bellatrix, "BeaconState">>;
|
|
9
|
+
export type BeaconStateCapella = CompositeViewDU<SSZTypesFor<ForkName.capella, "BeaconState">>;
|
|
10
|
+
export type BeaconStateDeneb = CompositeViewDU<SSZTypesFor<ForkName.deneb, "BeaconState">>;
|
|
11
|
+
export type BeaconStateElectra = CompositeViewDU<SSZTypesFor<ForkName.electra, "BeaconState">>;
|
|
12
|
+
export type BeaconStateFulu = CompositeViewDU<SSZTypesFor<ForkName.fulu, "BeaconState">>;
|
|
13
|
+
export type BeaconStateGloas = CompositeViewDU<SSZTypesFor<ForkName.gloas, "BeaconState">>;
|
|
14
|
+
|
|
15
|
+
export type BeaconStateAllForks = CompositeViewDU<SSZTypesFor<ForkAll, "BeaconState">>;
|
|
16
|
+
export type BeaconStateExecutions = CompositeViewDU<SSZTypesFor<ForkPostBellatrix & ForkPreGloas, "BeaconState">>;
|
|
17
|
+
|
|
18
|
+
export type ShufflingGetter = (shufflingEpoch: Epoch, dependentRoot: RootHex) => EpochShuffling | null;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const ZERO_HASH = new Uint8Array(32).fill(0);
|
|
2
|
+
export const EMPTY_SIGNATURE = new Uint8Array(96).fill(0);
|
|
3
|
+
export const SECONDS_PER_DAY = 86400;
|
|
4
|
+
export const BASE_REWARDS_PER_EPOCH = 4;
|
|
5
|
+
export const G2_POINT_AT_INFINITY = new Uint8Array(
|
|
6
|
+
Buffer.from(
|
|
7
|
+
"c000000000000000000000000000000000000000000000000000000000000000" +
|
|
8
|
+
"0000000000000000000000000000000000000000000000000000000000000000" +
|
|
9
|
+
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
10
|
+
"hex"
|
|
11
|
+
)
|
|
12
|
+
);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./constants.js";
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {ForkSeq, GENESIS_EPOCH} from "@lodestar/params";
|
|
2
|
+
import {phase0} from "@lodestar/types";
|
|
3
|
+
import {beforeProcessEpoch} from "../cache/epochTransitionCache.js";
|
|
4
|
+
import {CachedBeaconStateAllForks} from "../types.js";
|
|
5
|
+
import {
|
|
6
|
+
processJustificationAndFinalization,
|
|
7
|
+
weighJustificationAndFinalization,
|
|
8
|
+
} from "./processJustificationAndFinalization.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Compute on-the-fly justified / finalized checkpoints.
|
|
12
|
+
* - For phase0, we need to create the cache through beforeProcessEpoch
|
|
13
|
+
* - For other forks, use the progressive balances inside EpochCache
|
|
14
|
+
*/
|
|
15
|
+
export function computeUnrealizedCheckpoints(state: CachedBeaconStateAllForks): {
|
|
16
|
+
justifiedCheckpoint: phase0.Checkpoint;
|
|
17
|
+
finalizedCheckpoint: phase0.Checkpoint;
|
|
18
|
+
} {
|
|
19
|
+
let stateRealizedCheckpoints: CachedBeaconStateAllForks;
|
|
20
|
+
|
|
21
|
+
// For phase0, we need to create the cache through beforeProcessEpoch
|
|
22
|
+
if (state.config.getForkSeq(state.slot) === ForkSeq.phase0) {
|
|
23
|
+
// Clone state to mutate below true = do not transfer cache
|
|
24
|
+
stateRealizedCheckpoints = state.clone(true);
|
|
25
|
+
const epochTransitionCache = beforeProcessEpoch(stateRealizedCheckpoints);
|
|
26
|
+
processJustificationAndFinalization(stateRealizedCheckpoints, epochTransitionCache);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// For other forks, use the progressive balances inside EpochCache
|
|
30
|
+
else {
|
|
31
|
+
// same logic to processJustificationAndFinalization
|
|
32
|
+
if (state.epochCtx.epoch <= GENESIS_EPOCH + 1) {
|
|
33
|
+
stateRealizedCheckpoints = state;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Clone state and use progressive balances
|
|
37
|
+
else {
|
|
38
|
+
// Clone state to mutate below true = do not transfer cache
|
|
39
|
+
stateRealizedCheckpoints = state.clone(true);
|
|
40
|
+
|
|
41
|
+
weighJustificationAndFinalization(
|
|
42
|
+
stateRealizedCheckpoints,
|
|
43
|
+
state.epochCtx.totalActiveBalanceIncrements,
|
|
44
|
+
// minimum of total progressive unslashed balance should be 1
|
|
45
|
+
Math.max(state.epochCtx.previousTargetUnslashedBalanceIncrements, 1),
|
|
46
|
+
Math.max(state.epochCtx.currentTargetUnslashedBalanceIncrements, 1)
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
justifiedCheckpoint: stateRealizedCheckpoints.currentJustifiedCheckpoint,
|
|
53
|
+
finalizedCheckpoint: stateRealizedCheckpoints.finalizedCheckpoint,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BASE_REWARD_FACTOR,
|
|
3
|
+
EFFECTIVE_BALANCE_INCREMENT,
|
|
4
|
+
INACTIVITY_PENALTY_QUOTIENT,
|
|
5
|
+
MIN_EPOCHS_TO_INACTIVITY_PENALTY,
|
|
6
|
+
PROPOSER_REWARD_QUOTIENT,
|
|
7
|
+
} from "@lodestar/params";
|
|
8
|
+
import {bigIntSqrt, bnToNum} from "@lodestar/utils";
|
|
9
|
+
import {BASE_REWARDS_PER_EPOCH as BASE_REWARDS_PER_EPOCH_CONST} from "../constants/index.js";
|
|
10
|
+
import {CachedBeaconStatePhase0, EpochTransitionCache} from "../types.js";
|
|
11
|
+
import {hasMarkers} from "../util/attesterStatus.js";
|
|
12
|
+
import {newZeroedArray} from "../util/index.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Redefine constants in attesterStatus to improve performance
|
|
16
|
+
*/
|
|
17
|
+
const FLAG_PREV_SOURCE_ATTESTER = 1 << 0;
|
|
18
|
+
const FLAG_PREV_TARGET_ATTESTER = 1 << 1;
|
|
19
|
+
const FLAG_PREV_HEAD_ATTESTER = 1 << 2;
|
|
20
|
+
const FLAG_UNSLASHED = 1 << 6;
|
|
21
|
+
const FLAG_ELIGIBLE_ATTESTER = 1 << 7;
|
|
22
|
+
|
|
23
|
+
const FLAG_PREV_SOURCE_ATTESTER_OR_UNSLASHED = FLAG_PREV_SOURCE_ATTESTER | FLAG_UNSLASHED;
|
|
24
|
+
const FLAG_PREV_TARGET_ATTESTER_OR_UNSLASHED = FLAG_PREV_TARGET_ATTESTER | FLAG_UNSLASHED;
|
|
25
|
+
const FLAG_PREV_HEAD_ATTESTER_OR_UNSLASHED = FLAG_PREV_HEAD_ATTESTER | FLAG_UNSLASHED;
|
|
26
|
+
|
|
27
|
+
type RewardPenaltyItem = {
|
|
28
|
+
baseReward: number;
|
|
29
|
+
proposerReward: number;
|
|
30
|
+
maxAttesterReward: number;
|
|
31
|
+
sourceCheckpointReward: number;
|
|
32
|
+
targetCheckpointReward: number;
|
|
33
|
+
headReward: number;
|
|
34
|
+
basePenalty: number;
|
|
35
|
+
finalityDelayPenalty: number;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Return attestation reward/penalty deltas for each validator.
|
|
40
|
+
*
|
|
41
|
+
* - On normal mainnet conditions
|
|
42
|
+
* - prevSourceAttester: 98%
|
|
43
|
+
* - prevTargetAttester: 96%
|
|
44
|
+
* - prevHeadAttester: 93%
|
|
45
|
+
* - currSourceAttester: 95%
|
|
46
|
+
* - currTargetAttester: 93%
|
|
47
|
+
* - currHeadAttester: 91%
|
|
48
|
+
* - unslashed: 100%
|
|
49
|
+
* - eligibleAttester: 98%
|
|
50
|
+
*/
|
|
51
|
+
export function getAttestationDeltas(
|
|
52
|
+
state: CachedBeaconStatePhase0,
|
|
53
|
+
cache: EpochTransitionCache
|
|
54
|
+
): [number[], number[]] {
|
|
55
|
+
const {flags, proposerIndices, inclusionDelays} = cache;
|
|
56
|
+
const validatorCount = flags.length;
|
|
57
|
+
const rewards = newZeroedArray(validatorCount);
|
|
58
|
+
const penalties = newZeroedArray(validatorCount);
|
|
59
|
+
|
|
60
|
+
// no need this as we make sure it in EpochTransitionCache
|
|
61
|
+
// let totalBalance = bigIntMax(epochTransitionCache.totalActiveStake, increment);
|
|
62
|
+
const totalBalance = cache.totalActiveStakeByIncrement;
|
|
63
|
+
const totalBalanceInGwei = BigInt(totalBalance) * BigInt(EFFECTIVE_BALANCE_INCREMENT);
|
|
64
|
+
|
|
65
|
+
// increment is factored out from balance totals to avoid overflow
|
|
66
|
+
const prevEpochSourceStakeByIncrement = cache.prevEpochUnslashedStake.sourceStakeByIncrement;
|
|
67
|
+
const prevEpochTargetStakeByIncrement = cache.prevEpochUnslashedStake.targetStakeByIncrement;
|
|
68
|
+
const prevEpochHeadStakeByIncrement = cache.prevEpochUnslashedStake.headStakeByIncrement;
|
|
69
|
+
|
|
70
|
+
// sqrt first, before factoring out the increment for later usage
|
|
71
|
+
const balanceSqRoot = bnToNum(bigIntSqrt(totalBalanceInGwei));
|
|
72
|
+
const finalityDelay = cache.prevEpoch - state.finalizedCheckpoint.epoch;
|
|
73
|
+
|
|
74
|
+
const BASE_REWARDS_PER_EPOCH = BASE_REWARDS_PER_EPOCH_CONST;
|
|
75
|
+
const proposerRewardQuotient = PROPOSER_REWARD_QUOTIENT;
|
|
76
|
+
const isInInactivityLeak = finalityDelay > MIN_EPOCHS_TO_INACTIVITY_PENALTY;
|
|
77
|
+
|
|
78
|
+
// effectiveBalance is multiple of EFFECTIVE_BALANCE_INCREMENT and less than MAX_EFFECTIVE_BALANCE
|
|
79
|
+
// so there are limited values of them like 32, 31, 30
|
|
80
|
+
const rewardPnaltyItemCache = new Map<number, RewardPenaltyItem>();
|
|
81
|
+
const {effectiveBalanceIncrements} = state.epochCtx;
|
|
82
|
+
for (let i = 0; i < flags.length; i++) {
|
|
83
|
+
const flag = flags[i];
|
|
84
|
+
const effectiveBalanceIncrement = effectiveBalanceIncrements[i];
|
|
85
|
+
const effectiveBalance = effectiveBalanceIncrement * EFFECTIVE_BALANCE_INCREMENT;
|
|
86
|
+
|
|
87
|
+
let rewardItem = rewardPnaltyItemCache.get(effectiveBalanceIncrement);
|
|
88
|
+
if (!rewardItem) {
|
|
89
|
+
const baseReward = Math.floor(
|
|
90
|
+
Math.floor((effectiveBalance * BASE_REWARD_FACTOR) / balanceSqRoot) / BASE_REWARDS_PER_EPOCH
|
|
91
|
+
);
|
|
92
|
+
const proposerReward = Math.floor(baseReward / proposerRewardQuotient);
|
|
93
|
+
rewardItem = {
|
|
94
|
+
baseReward,
|
|
95
|
+
proposerReward,
|
|
96
|
+
maxAttesterReward: baseReward - proposerReward,
|
|
97
|
+
sourceCheckpointReward: isInInactivityLeak
|
|
98
|
+
? baseReward
|
|
99
|
+
: Math.floor((baseReward * prevEpochSourceStakeByIncrement) / totalBalance),
|
|
100
|
+
targetCheckpointReward: isInInactivityLeak
|
|
101
|
+
? baseReward
|
|
102
|
+
: Math.floor((baseReward * prevEpochTargetStakeByIncrement) / totalBalance),
|
|
103
|
+
headReward: isInInactivityLeak
|
|
104
|
+
? baseReward
|
|
105
|
+
: Math.floor((baseReward * prevEpochHeadStakeByIncrement) / totalBalance),
|
|
106
|
+
basePenalty: baseReward * BASE_REWARDS_PER_EPOCH_CONST - proposerReward,
|
|
107
|
+
finalityDelayPenalty: Math.floor((effectiveBalance * finalityDelay) / INACTIVITY_PENALTY_QUOTIENT),
|
|
108
|
+
};
|
|
109
|
+
rewardPnaltyItemCache.set(effectiveBalanceIncrement, rewardItem);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const {
|
|
113
|
+
baseReward,
|
|
114
|
+
proposerReward,
|
|
115
|
+
maxAttesterReward,
|
|
116
|
+
sourceCheckpointReward,
|
|
117
|
+
targetCheckpointReward,
|
|
118
|
+
headReward,
|
|
119
|
+
basePenalty,
|
|
120
|
+
finalityDelayPenalty,
|
|
121
|
+
} = rewardItem;
|
|
122
|
+
|
|
123
|
+
// inclusion speed bonus
|
|
124
|
+
if (hasMarkers(flag, FLAG_PREV_SOURCE_ATTESTER_OR_UNSLASHED)) {
|
|
125
|
+
rewards[proposerIndices[i]] += proposerReward;
|
|
126
|
+
rewards[i] += Math.floor(maxAttesterReward / inclusionDelays[i]);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (hasMarkers(flag, FLAG_ELIGIBLE_ATTESTER)) {
|
|
130
|
+
// expected FFG source
|
|
131
|
+
if (hasMarkers(flag, FLAG_PREV_SOURCE_ATTESTER_OR_UNSLASHED)) {
|
|
132
|
+
// justification-participation reward
|
|
133
|
+
rewards[i] += sourceCheckpointReward;
|
|
134
|
+
} else {
|
|
135
|
+
// justification-non-participation R-penalty
|
|
136
|
+
penalties[i] += baseReward;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// expected FFG target
|
|
140
|
+
if (hasMarkers(flag, FLAG_PREV_TARGET_ATTESTER_OR_UNSLASHED)) {
|
|
141
|
+
// boundary-attestation reward
|
|
142
|
+
rewards[i] += targetCheckpointReward;
|
|
143
|
+
} else {
|
|
144
|
+
// boundary-attestation-non-participation R-penalty
|
|
145
|
+
penalties[i] += baseReward;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// expected head
|
|
149
|
+
if (hasMarkers(flag, FLAG_PREV_HEAD_ATTESTER_OR_UNSLASHED)) {
|
|
150
|
+
// canonical-participation reward
|
|
151
|
+
rewards[i] += headReward;
|
|
152
|
+
} else {
|
|
153
|
+
// non-canonical-participation R-penalty
|
|
154
|
+
penalties[i] += baseReward;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// take away max rewards if we're not finalizing
|
|
158
|
+
if (isInInactivityLeak) {
|
|
159
|
+
penalties[i] += basePenalty;
|
|
160
|
+
|
|
161
|
+
if (!hasMarkers(flag, FLAG_PREV_TARGET_ATTESTER_OR_UNSLASHED)) {
|
|
162
|
+
penalties[i] += finalityDelayPenalty;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return [rewards, penalties];
|
|
169
|
+
}
|