@lodestar/state-transition 1.35.0-dev.83de5b8dea → 1.35.0-dev.8689cc3545

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.
Files changed (231) hide show
  1. package/lib/block/externalData.d.ts.map +1 -0
  2. package/lib/block/index.d.ts.map +1 -0
  3. package/lib/block/initiateValidatorExit.d.ts.map +1 -0
  4. package/lib/block/isValidIndexedAttestation.d.ts.map +1 -0
  5. package/lib/block/processAttestationPhase0.d.ts.map +1 -0
  6. package/lib/block/processAttestations.d.ts.map +1 -0
  7. package/lib/block/processAttestationsAltair.d.ts.map +1 -0
  8. package/lib/block/processAttesterSlashing.d.ts.map +1 -0
  9. package/lib/block/processBlobKzgCommitments.d.ts.map +1 -0
  10. package/lib/block/processBlockHeader.d.ts.map +1 -0
  11. package/lib/block/processBlsToExecutionChange.d.ts.map +1 -0
  12. package/lib/block/processConsolidationRequest.d.ts.map +1 -0
  13. package/lib/block/processDeposit.d.ts.map +1 -0
  14. package/lib/block/processDepositRequest.d.ts.map +1 -0
  15. package/lib/block/processEth1Data.d.ts.map +1 -0
  16. package/lib/block/processExecutionPayload.d.ts.map +1 -0
  17. package/lib/block/processOperations.d.ts.map +1 -0
  18. package/lib/block/processProposerSlashing.d.ts.map +1 -0
  19. package/lib/block/processRandao.d.ts.map +1 -0
  20. package/lib/block/processSyncCommittee.d.ts.map +1 -0
  21. package/lib/block/processVoluntaryExit.d.ts.map +1 -0
  22. package/lib/block/processWithdrawalRequest.d.ts.map +1 -0
  23. package/lib/block/processWithdrawals.d.ts.map +1 -0
  24. package/lib/block/slashValidator.d.ts.map +1 -0
  25. package/lib/block/types.d.ts.map +1 -0
  26. package/lib/cache/effectiveBalanceIncrements.d.ts.map +1 -0
  27. package/lib/cache/epochCache.d.ts.map +1 -0
  28. package/lib/cache/epochTransitionCache.d.ts.map +1 -0
  29. package/lib/cache/pubkeyCache.d.ts.map +1 -0
  30. package/lib/cache/rewardCache.d.ts.map +1 -0
  31. package/lib/cache/stateCache.d.ts.map +1 -0
  32. package/lib/cache/syncCommitteeCache.d.ts.map +1 -0
  33. package/lib/cache/types.d.ts.map +1 -0
  34. package/lib/constants/constants.d.ts.map +1 -0
  35. package/lib/constants/index.d.ts.map +1 -0
  36. package/lib/epoch/computeUnrealizedCheckpoints.d.ts.map +1 -0
  37. package/lib/epoch/getAttestationDeltas.d.ts.map +1 -0
  38. package/lib/epoch/getRewardsAndPenalties.d.ts.map +1 -0
  39. package/lib/epoch/index.d.ts.map +1 -0
  40. package/lib/epoch/processEffectiveBalanceUpdates.d.ts.map +1 -0
  41. package/lib/epoch/processEth1DataReset.d.ts.map +1 -0
  42. package/lib/epoch/processHistoricalRootsUpdate.d.ts.map +1 -0
  43. package/lib/epoch/processHistoricalSummariesUpdate.d.ts.map +1 -0
  44. package/lib/epoch/processInactivityUpdates.d.ts.map +1 -0
  45. package/lib/epoch/processJustificationAndFinalization.d.ts.map +1 -0
  46. package/lib/epoch/processParticipationFlagUpdates.d.ts.map +1 -0
  47. package/lib/epoch/processParticipationRecordUpdates.d.ts.map +1 -0
  48. package/lib/epoch/processPendingAttestations.d.ts.map +1 -0
  49. package/lib/epoch/processPendingConsolidations.d.ts.map +1 -0
  50. package/lib/epoch/processPendingDeposits.d.ts.map +1 -0
  51. package/lib/epoch/processProposerLookahead.d.ts.map +1 -0
  52. package/lib/epoch/processRandaoMixesReset.d.ts.map +1 -0
  53. package/lib/epoch/processRegistryUpdates.d.ts.map +1 -0
  54. package/lib/epoch/processRewardsAndPenalties.d.ts.map +1 -0
  55. package/lib/epoch/processSlashings.d.ts.map +1 -0
  56. package/lib/epoch/processSlashingsReset.d.ts.map +1 -0
  57. package/lib/epoch/processSyncCommitteeUpdates.d.ts.map +1 -0
  58. package/lib/index.d.ts.map +1 -0
  59. package/lib/metrics.d.ts.map +1 -0
  60. package/lib/signatureSets/attesterSlashings.d.ts.map +1 -0
  61. package/lib/signatureSets/blsToExecutionChange.d.ts.map +1 -0
  62. package/lib/signatureSets/index.d.ts.map +1 -0
  63. package/lib/signatureSets/indexedAttestation.d.ts.map +1 -0
  64. package/lib/signatureSets/proposer.d.ts.map +1 -0
  65. package/lib/signatureSets/proposerSlashings.d.ts.map +1 -0
  66. package/lib/signatureSets/randao.d.ts.map +1 -0
  67. package/lib/signatureSets/voluntaryExits.d.ts.map +1 -0
  68. package/lib/slot/index.d.ts.map +1 -0
  69. package/lib/slot/upgradeStateToAltair.d.ts.map +1 -0
  70. package/lib/slot/upgradeStateToBellatrix.d.ts.map +1 -0
  71. package/lib/slot/upgradeStateToCapella.d.ts.map +1 -0
  72. package/lib/slot/upgradeStateToDeneb.d.ts.map +1 -0
  73. package/lib/slot/upgradeStateToElectra.d.ts.map +1 -0
  74. package/lib/slot/upgradeStateToFulu.d.ts.map +1 -0
  75. package/lib/slot/upgradeStateToGloas.d.ts.map +1 -0
  76. package/lib/stateTransition.d.ts.map +1 -0
  77. package/lib/types.d.ts.map +1 -0
  78. package/lib/util/aggregator.d.ts.map +1 -0
  79. package/lib/util/altair.d.ts.map +1 -0
  80. package/lib/util/array.d.ts.map +1 -0
  81. package/lib/util/attestation.d.ts.map +1 -0
  82. package/lib/util/attesterStatus.d.ts.map +1 -0
  83. package/lib/util/balance.d.ts.map +1 -0
  84. package/lib/util/blindedBlock.d.ts.map +1 -0
  85. package/lib/util/blockRoot.d.ts.map +1 -0
  86. package/lib/util/calculateCommitteeAssignments.d.ts.map +1 -0
  87. package/lib/util/capella.d.ts.map +1 -0
  88. package/lib/util/computeAnchorCheckpoint.d.ts.map +1 -0
  89. package/lib/util/deposit.d.ts.map +1 -0
  90. package/lib/util/domain.d.ts.map +1 -0
  91. package/lib/util/electra.d.ts.map +1 -0
  92. package/lib/util/epoch.d.ts.map +1 -0
  93. package/lib/util/epochShuffling.d.ts.map +1 -0
  94. package/lib/util/execution.d.ts.map +1 -0
  95. package/lib/util/finality.d.ts.map +1 -0
  96. package/lib/util/fulu.d.ts.map +1 -0
  97. package/lib/util/genesis.d.ts.map +1 -0
  98. package/lib/util/index.d.ts.map +1 -0
  99. package/lib/util/interop.d.ts.map +1 -0
  100. package/lib/util/loadState/findModifiedInactivityScores.d.ts.map +1 -0
  101. package/lib/util/loadState/findModifiedValidators.d.ts.map +1 -0
  102. package/lib/util/loadState/index.d.ts.map +1 -0
  103. package/lib/util/loadState/loadState.d.ts.map +1 -0
  104. package/lib/util/loadState/loadValidator.d.ts.map +1 -0
  105. package/lib/util/rootCache.d.ts.map +1 -0
  106. package/lib/util/seed.d.ts.map +1 -0
  107. package/lib/util/shufflingDecisionRoot.d.ts.map +1 -0
  108. package/lib/util/signatureSets.d.ts.map +1 -0
  109. package/lib/util/signingRoot.d.ts.map +1 -0
  110. package/lib/util/slot.d.ts.map +1 -0
  111. package/lib/util/sszBytes.d.ts.map +1 -0
  112. package/lib/util/syncCommittee.d.ts.map +1 -0
  113. package/lib/util/targetUnslashedBalance.d.ts.map +1 -0
  114. package/lib/util/validator.d.ts.map +1 -0
  115. package/lib/util/weakSubjectivity.d.ts.map +1 -0
  116. package/package.json +9 -11
  117. package/src/block/externalData.ts +26 -0
  118. package/src/block/index.ts +81 -0
  119. package/src/block/initiateValidatorExit.ts +62 -0
  120. package/src/block/isValidIndexedAttestation.ts +70 -0
  121. package/src/block/processAttestationPhase0.ts +158 -0
  122. package/src/block/processAttestations.ts +25 -0
  123. package/src/block/processAttestationsAltair.ts +184 -0
  124. package/src/block/processAttesterSlashing.ts +59 -0
  125. package/src/block/processBlobKzgCommitments.ts +21 -0
  126. package/src/block/processBlockHeader.ts +54 -0
  127. package/src/block/processBlsToExecutionChange.ts +78 -0
  128. package/src/block/processConsolidationRequest.ts +147 -0
  129. package/src/block/processDeposit.ts +166 -0
  130. package/src/block/processDepositRequest.ts +19 -0
  131. package/src/block/processEth1Data.ts +86 -0
  132. package/src/block/processExecutionPayload.ts +84 -0
  133. package/src/block/processOperations.ts +83 -0
  134. package/src/block/processProposerSlashing.ts +66 -0
  135. package/src/block/processRandao.ts +27 -0
  136. package/src/block/processSyncCommittee.ts +117 -0
  137. package/src/block/processVoluntaryExit.ts +55 -0
  138. package/src/block/processWithdrawalRequest.ts +98 -0
  139. package/src/block/processWithdrawals.ts +207 -0
  140. package/src/block/slashValidator.ts +98 -0
  141. package/src/block/types.ts +9 -0
  142. package/src/cache/effectiveBalanceIncrements.ts +39 -0
  143. package/src/cache/epochCache.ts +1213 -0
  144. package/src/cache/epochTransitionCache.ts +542 -0
  145. package/src/cache/pubkeyCache.ts +33 -0
  146. package/src/cache/rewardCache.ts +19 -0
  147. package/src/cache/stateCache.ts +268 -0
  148. package/src/cache/syncCommitteeCache.ts +96 -0
  149. package/src/cache/types.ts +18 -0
  150. package/src/constants/constants.ts +12 -0
  151. package/src/constants/index.ts +1 -0
  152. package/src/epoch/computeUnrealizedCheckpoints.ts +55 -0
  153. package/src/epoch/getAttestationDeltas.ts +169 -0
  154. package/src/epoch/getRewardsAndPenalties.ts +137 -0
  155. package/src/epoch/index.ts +202 -0
  156. package/src/epoch/processEffectiveBalanceUpdates.ts +111 -0
  157. package/src/epoch/processEth1DataReset.ts +17 -0
  158. package/src/epoch/processHistoricalRootsUpdate.ts +25 -0
  159. package/src/epoch/processHistoricalSummariesUpdate.ts +23 -0
  160. package/src/epoch/processInactivityUpdates.ts +60 -0
  161. package/src/epoch/processJustificationAndFinalization.ts +90 -0
  162. package/src/epoch/processParticipationFlagUpdates.ts +27 -0
  163. package/src/epoch/processParticipationRecordUpdates.ts +14 -0
  164. package/src/epoch/processPendingAttestations.ts +75 -0
  165. package/src/epoch/processPendingConsolidations.ts +59 -0
  166. package/src/epoch/processPendingDeposits.ts +136 -0
  167. package/src/epoch/processProposerLookahead.ts +39 -0
  168. package/src/epoch/processRandaoMixesReset.ts +18 -0
  169. package/src/epoch/processRegistryUpdates.ts +65 -0
  170. package/src/epoch/processRewardsAndPenalties.ts +58 -0
  171. package/src/epoch/processSlashings.ts +97 -0
  172. package/src/epoch/processSlashingsReset.ts +20 -0
  173. package/src/epoch/processSyncCommitteeUpdates.ts +44 -0
  174. package/src/index.ts +67 -0
  175. package/src/metrics.ts +169 -0
  176. package/src/signatureSets/attesterSlashings.ts +39 -0
  177. package/src/signatureSets/blsToExecutionChange.ts +43 -0
  178. package/src/signatureSets/index.ts +73 -0
  179. package/src/signatureSets/indexedAttestation.ts +51 -0
  180. package/src/signatureSets/proposer.ts +47 -0
  181. package/src/signatureSets/proposerSlashings.ts +41 -0
  182. package/src/signatureSets/randao.ts +31 -0
  183. package/src/signatureSets/voluntaryExits.ts +44 -0
  184. package/src/slot/index.ts +32 -0
  185. package/src/slot/upgradeStateToAltair.ts +149 -0
  186. package/src/slot/upgradeStateToBellatrix.ts +63 -0
  187. package/src/slot/upgradeStateToCapella.ts +71 -0
  188. package/src/slot/upgradeStateToDeneb.ts +40 -0
  189. package/src/slot/upgradeStateToElectra.ts +126 -0
  190. package/src/slot/upgradeStateToFulu.ts +31 -0
  191. package/src/slot/upgradeStateToGloas.ts +29 -0
  192. package/src/stateTransition.ts +305 -0
  193. package/src/types.ts +26 -0
  194. package/src/util/aggregator.ts +33 -0
  195. package/src/util/altair.ts +13 -0
  196. package/src/util/array.ts +53 -0
  197. package/src/util/attestation.ts +36 -0
  198. package/src/util/attesterStatus.ts +83 -0
  199. package/src/util/balance.ts +83 -0
  200. package/src/util/blindedBlock.ts +144 -0
  201. package/src/util/blockRoot.ts +72 -0
  202. package/src/util/calculateCommitteeAssignments.ts +43 -0
  203. package/src/util/capella.ts +8 -0
  204. package/src/util/computeAnchorCheckpoint.ts +38 -0
  205. package/src/util/deposit.ts +22 -0
  206. package/src/util/domain.ts +31 -0
  207. package/src/util/electra.ts +68 -0
  208. package/src/util/epoch.ts +135 -0
  209. package/src/util/epochShuffling.ts +185 -0
  210. package/src/util/execution.ts +177 -0
  211. package/src/util/finality.ts +17 -0
  212. package/src/util/fulu.ts +43 -0
  213. package/src/util/genesis.ts +343 -0
  214. package/src/util/index.ts +29 -0
  215. package/src/util/interop.ts +22 -0
  216. package/src/util/loadState/findModifiedInactivityScores.ts +47 -0
  217. package/src/util/loadState/findModifiedValidators.ts +46 -0
  218. package/src/util/loadState/index.ts +2 -0
  219. package/src/util/loadState/loadState.ts +225 -0
  220. package/src/util/loadState/loadValidator.ts +77 -0
  221. package/src/util/rootCache.ts +37 -0
  222. package/src/util/seed.ts +381 -0
  223. package/src/util/shufflingDecisionRoot.ts +78 -0
  224. package/src/util/signatureSets.ts +65 -0
  225. package/src/util/signingRoot.ts +13 -0
  226. package/src/util/slot.ts +27 -0
  227. package/src/util/sszBytes.ts +52 -0
  228. package/src/util/syncCommittee.ts +69 -0
  229. package/src/util/targetUnslashedBalance.ts +30 -0
  230. package/src/util/validator.ts +105 -0
  231. package/src/util/weakSubjectivity.ts +186 -0
@@ -0,0 +1,137 @@
1
+ import {
2
+ EFFECTIVE_BALANCE_INCREMENT,
3
+ ForkSeq,
4
+ INACTIVITY_PENALTY_QUOTIENT_ALTAIR,
5
+ INACTIVITY_PENALTY_QUOTIENT_BELLATRIX,
6
+ PARTICIPATION_FLAG_WEIGHTS,
7
+ TIMELY_HEAD_FLAG_INDEX,
8
+ TIMELY_SOURCE_FLAG_INDEX,
9
+ TIMELY_TARGET_FLAG_INDEX,
10
+ WEIGHT_DENOMINATOR,
11
+ } from "@lodestar/params";
12
+ import {CachedBeaconStateAltair, EpochTransitionCache} from "../types.js";
13
+ import {
14
+ FLAG_ELIGIBLE_ATTESTER,
15
+ FLAG_PREV_HEAD_ATTESTER_UNSLASHED,
16
+ FLAG_PREV_SOURCE_ATTESTER_UNSLASHED,
17
+ FLAG_PREV_TARGET_ATTESTER_UNSLASHED,
18
+ hasMarkers,
19
+ } from "../util/attesterStatus.js";
20
+ import {isInInactivityLeak} from "../util/index.js";
21
+
22
+ type RewardPenaltyItem = {
23
+ baseReward: number;
24
+ timelySourceReward: number;
25
+ timelySourcePenalty: number;
26
+ timelyTargetReward: number;
27
+ timelyTargetPenalty: number;
28
+ timelyHeadReward: number;
29
+ };
30
+
31
+ /**
32
+ * This data is reused and never gc.
33
+ */
34
+ const rewards = new Array<number>();
35
+ const penalties = new Array<number>();
36
+ /**
37
+ * An aggregate of getFlagIndexDeltas and getInactivityPenaltyDeltas that loop through process.flags 1 time instead of 4.
38
+ *
39
+ * - On normal mainnet conditions
40
+ * - prevSourceAttester: 98%
41
+ * - prevTargetAttester: 96%
42
+ * - prevHeadAttester: 93%
43
+ * - currSourceAttester: 95%
44
+ * - currTargetAttester: 93%
45
+ * - currHeadAttester: 91%
46
+ * - unslashed: 100%
47
+ * - eligibleAttester: 98%
48
+ */
49
+ export function getRewardsAndPenaltiesAltair(
50
+ state: CachedBeaconStateAltair,
51
+ cache: EpochTransitionCache
52
+ ): [number[], number[]] {
53
+ // TODO: Is there a cheaper way to measure length that going to `state.validators`?
54
+ const validatorCount = state.validators.length;
55
+ const activeIncrements = cache.totalActiveStakeByIncrement;
56
+ rewards.length = validatorCount;
57
+ rewards.fill(0);
58
+ penalties.length = validatorCount;
59
+ penalties.fill(0);
60
+
61
+ const isInInactivityLeakBn = isInInactivityLeak(state);
62
+ // effectiveBalance is multiple of EFFECTIVE_BALANCE_INCREMENT and less than MAX_EFFECTIVE_BALANCE
63
+ // so there are limited values of them like 32, 31, 30
64
+ const rewardPenaltyItemCache = new Map<number, RewardPenaltyItem>();
65
+ const {config, epochCtx} = state;
66
+ const fork = config.getForkSeq(state.slot);
67
+
68
+ const inactivityPenalityMultiplier =
69
+ fork === ForkSeq.altair ? INACTIVITY_PENALTY_QUOTIENT_ALTAIR : INACTIVITY_PENALTY_QUOTIENT_BELLATRIX;
70
+ const penaltyDenominator = config.INACTIVITY_SCORE_BIAS * inactivityPenalityMultiplier;
71
+
72
+ const {flags} = cache;
73
+ for (let i = 0; i < flags.length; i++) {
74
+ const flag = flags[i];
75
+ if (!hasMarkers(flag, FLAG_ELIGIBLE_ATTESTER)) {
76
+ continue;
77
+ }
78
+
79
+ const effectiveBalanceIncrement = epochCtx.effectiveBalanceIncrements[i];
80
+
81
+ let rewardPenaltyItem = rewardPenaltyItemCache.get(effectiveBalanceIncrement);
82
+ if (!rewardPenaltyItem) {
83
+ const baseReward = effectiveBalanceIncrement * cache.baseRewardPerIncrement;
84
+ const tsWeigh = PARTICIPATION_FLAG_WEIGHTS[TIMELY_SOURCE_FLAG_INDEX];
85
+ const ttWeigh = PARTICIPATION_FLAG_WEIGHTS[TIMELY_TARGET_FLAG_INDEX];
86
+ const thWeigh = PARTICIPATION_FLAG_WEIGHTS[TIMELY_HEAD_FLAG_INDEX];
87
+ const tsUnslashedParticipatingIncrements = cache.prevEpochUnslashedStake.sourceStakeByIncrement;
88
+ const ttUnslashedParticipatingIncrements = cache.prevEpochUnslashedStake.targetStakeByIncrement;
89
+ const thUnslashedParticipatingIncrements = cache.prevEpochUnslashedStake.headStakeByIncrement;
90
+ const tsRewardNumerator = baseReward * tsWeigh * tsUnslashedParticipatingIncrements;
91
+ const ttRewardNumerator = baseReward * ttWeigh * ttUnslashedParticipatingIncrements;
92
+ const thRewardNumerator = baseReward * thWeigh * thUnslashedParticipatingIncrements;
93
+ rewardPenaltyItem = {
94
+ baseReward: baseReward,
95
+ timelySourceReward: Math.floor(tsRewardNumerator / (activeIncrements * WEIGHT_DENOMINATOR)),
96
+ timelyTargetReward: Math.floor(ttRewardNumerator / (activeIncrements * WEIGHT_DENOMINATOR)),
97
+ timelyHeadReward: Math.floor(thRewardNumerator / (activeIncrements * WEIGHT_DENOMINATOR)),
98
+ timelySourcePenalty: Math.floor((baseReward * tsWeigh) / WEIGHT_DENOMINATOR),
99
+ timelyTargetPenalty: Math.floor((baseReward * ttWeigh) / WEIGHT_DENOMINATOR),
100
+ };
101
+ rewardPenaltyItemCache.set(effectiveBalanceIncrement, rewardPenaltyItem);
102
+ }
103
+
104
+ const {timelySourceReward, timelySourcePenalty, timelyTargetReward, timelyTargetPenalty, timelyHeadReward} =
105
+ rewardPenaltyItem;
106
+
107
+ // same logic to getFlagIndexDeltas
108
+ if (hasMarkers(flag, FLAG_PREV_SOURCE_ATTESTER_UNSLASHED)) {
109
+ if (!isInInactivityLeakBn) {
110
+ rewards[i] += timelySourceReward;
111
+ }
112
+ } else {
113
+ penalties[i] += timelySourcePenalty;
114
+ }
115
+
116
+ if (hasMarkers(flag, FLAG_PREV_TARGET_ATTESTER_UNSLASHED)) {
117
+ if (!isInInactivityLeakBn) {
118
+ rewards[i] += timelyTargetReward;
119
+ }
120
+ } else {
121
+ penalties[i] += timelyTargetPenalty;
122
+ }
123
+
124
+ if (hasMarkers(flag, FLAG_PREV_HEAD_ATTESTER_UNSLASHED) && !isInInactivityLeakBn) {
125
+ rewards[i] += timelyHeadReward;
126
+ }
127
+
128
+ // Same logic to getInactivityPenaltyDeltas
129
+ // TODO: if we have limited value in inactivityScores we can provide a cache too
130
+ if (!hasMarkers(flag, FLAG_PREV_TARGET_ATTESTER_UNSLASHED)) {
131
+ const penaltyNumerator = effectiveBalanceIncrement * EFFECTIVE_BALANCE_INCREMENT * state.inactivityScores.get(i);
132
+ penalties[i] += Math.floor(penaltyNumerator / penaltyDenominator);
133
+ }
134
+ }
135
+
136
+ return [rewards, penalties];
137
+ }
@@ -0,0 +1,202 @@
1
+ import {
2
+ ForkSeq,
3
+ MAX_ATTESTER_SLASHINGS,
4
+ MAX_EFFECTIVE_BALANCE,
5
+ MAX_VALIDATORS_PER_COMMITTEE,
6
+ SLOTS_PER_EPOCH,
7
+ } from "@lodestar/params";
8
+ import {BeaconStateTransitionMetrics} from "../metrics.js";
9
+ import {
10
+ CachedBeaconStateAllForks,
11
+ CachedBeaconStateAltair,
12
+ CachedBeaconStateCapella,
13
+ CachedBeaconStateElectra,
14
+ CachedBeaconStateFulu,
15
+ CachedBeaconStatePhase0,
16
+ EpochTransitionCache,
17
+ } from "../types.js";
18
+ import {processEffectiveBalanceUpdates} from "./processEffectiveBalanceUpdates.js";
19
+ import {processEth1DataReset} from "./processEth1DataReset.js";
20
+ import {processHistoricalRootsUpdate} from "./processHistoricalRootsUpdate.js";
21
+ import {processHistoricalSummariesUpdate} from "./processHistoricalSummariesUpdate.js";
22
+ import {processInactivityUpdates} from "./processInactivityUpdates.js";
23
+ import {processJustificationAndFinalization} from "./processJustificationAndFinalization.js";
24
+ import {processParticipationFlagUpdates} from "./processParticipationFlagUpdates.js";
25
+ import {processParticipationRecordUpdates} from "./processParticipationRecordUpdates.js";
26
+ import {processPendingConsolidations} from "./processPendingConsolidations.js";
27
+ import {processPendingDeposits} from "./processPendingDeposits.js";
28
+ import {processProposerLookahead} from "./processProposerLookahead.js";
29
+ import {processRandaoMixesReset} from "./processRandaoMixesReset.js";
30
+ import {processRegistryUpdates} from "./processRegistryUpdates.js";
31
+ import {processRewardsAndPenalties} from "./processRewardsAndPenalties.js";
32
+ import {processSlashings} from "./processSlashings.js";
33
+ import {processSlashingsReset} from "./processSlashingsReset.js";
34
+ import {processSyncCommitteeUpdates} from "./processSyncCommitteeUpdates.js";
35
+
36
+ // For spec tests
37
+ export {getRewardsAndPenalties} from "./processRewardsAndPenalties.js";
38
+ export {
39
+ processJustificationAndFinalization,
40
+ processInactivityUpdates,
41
+ processRewardsAndPenalties,
42
+ processRegistryUpdates,
43
+ processSlashings,
44
+ processEth1DataReset,
45
+ processEffectiveBalanceUpdates,
46
+ processSlashingsReset,
47
+ processRandaoMixesReset,
48
+ processHistoricalRootsUpdate,
49
+ processParticipationRecordUpdates,
50
+ processParticipationFlagUpdates,
51
+ processSyncCommitteeUpdates,
52
+ processHistoricalSummariesUpdate,
53
+ processPendingDeposits,
54
+ processPendingConsolidations,
55
+ processProposerLookahead,
56
+ };
57
+
58
+ export {computeUnrealizedCheckpoints} from "./computeUnrealizedCheckpoints.js";
59
+
60
+ const maxValidatorsPerStateSlashing = SLOTS_PER_EPOCH * MAX_ATTESTER_SLASHINGS * MAX_VALIDATORS_PER_COMMITTEE;
61
+ const maxSafeValidators = Math.floor(Number.MAX_SAFE_INTEGER / MAX_EFFECTIVE_BALANCE);
62
+
63
+ /**
64
+ * Epoch transition steps tracked in metrics
65
+ */
66
+ export enum EpochTransitionStep {
67
+ beforeProcessEpoch = "beforeProcessEpoch",
68
+ afterProcessEpoch = "afterProcessEpoch",
69
+ finalProcessEpoch = "finalProcessEpoch",
70
+ processJustificationAndFinalization = "processJustificationAndFinalization",
71
+ processInactivityUpdates = "processInactivityUpdates",
72
+ processRegistryUpdates = "processRegistryUpdates",
73
+ processSlashings = "processSlashings",
74
+ processRewardsAndPenalties = "processRewardsAndPenalties",
75
+ processEffectiveBalanceUpdates = "processEffectiveBalanceUpdates",
76
+ processParticipationFlagUpdates = "processParticipationFlagUpdates",
77
+ processSyncCommitteeUpdates = "processSyncCommitteeUpdates",
78
+ processPendingDeposits = "processPendingDeposits",
79
+ processPendingConsolidations = "processPendingConsolidations",
80
+ processProposerLookahead = "processProposerLookahead",
81
+ }
82
+
83
+ export function processEpoch(
84
+ fork: ForkSeq,
85
+ state: CachedBeaconStateAllForks,
86
+ cache: EpochTransitionCache,
87
+ metrics?: BeaconStateTransitionMetrics | null
88
+ ): void {
89
+ // state.slashings is initially a Gwei (BigInt) vector, however since Nov 2023 it's converted to UintNum64 (number) vector in the state transition because:
90
+ // - state.slashings[nextEpoch % EPOCHS_PER_SLASHINGS_VECTOR] is reset per epoch in processSlashingsReset()
91
+ // - max slashed validators per epoch is SLOTS_PER_EPOCH * MAX_ATTESTER_SLASHINGS * MAX_VALIDATORS_PER_COMMITTEE which is 32 * 2 * 2048 = 131072 on mainnet
92
+ // - with that and 32_000_000_000 MAX_EFFECTIVE_BALANCE or 2048_000_000_000 MAX_EFFECTIVE_BALANCE_ELECTRA, it still fits in a number given that Math.floor(Number.MAX_SAFE_INTEGER / 32_000_000_000) = 281474
93
+ if (maxValidatorsPerStateSlashing > maxSafeValidators) {
94
+ throw new Error("Lodestar does not support this network, parameters don't fit number value inside state.slashings");
95
+ }
96
+
97
+ {
98
+ const timer = metrics?.epochTransitionStepTime.startTimer({
99
+ step: EpochTransitionStep.processJustificationAndFinalization,
100
+ });
101
+ processJustificationAndFinalization(state, cache);
102
+ timer?.();
103
+ }
104
+
105
+ if (fork >= ForkSeq.altair) {
106
+ const timer = metrics?.epochTransitionStepTime.startTimer({step: EpochTransitionStep.processInactivityUpdates});
107
+ processInactivityUpdates(state as CachedBeaconStateAltair, cache);
108
+ timer?.();
109
+ }
110
+
111
+ // processRewardsAndPenalties() is 2nd step in the specs, we optimize to do it
112
+ // after processSlashings() to update balances only once
113
+ // processRewardsAndPenalties(state, cache);
114
+ {
115
+ metrics?.validatorsInActivationQueue.set(cache.indicesEligibleForActivationQueue.length);
116
+ metrics?.validatorsInExitQueue.set(cache.indicesToEject.length);
117
+ const timer = metrics?.epochTransitionStepTime.startTimer({step: EpochTransitionStep.processRegistryUpdates});
118
+ processRegistryUpdates(fork, state, cache);
119
+ timer?.();
120
+ }
121
+
122
+ // accumulate slashing penalties and only update balances once in processRewardsAndPenalties()
123
+ let slashingPenalties: number[];
124
+ {
125
+ const timer = metrics?.epochTransitionStepTime.startTimer({step: EpochTransitionStep.processSlashings});
126
+ slashingPenalties = processSlashings(state, cache, false);
127
+ timer?.();
128
+ }
129
+
130
+ {
131
+ const timer = metrics?.epochTransitionStepTime.startTimer({step: EpochTransitionStep.processRewardsAndPenalties});
132
+ processRewardsAndPenalties(state, cache, slashingPenalties);
133
+ timer?.();
134
+ }
135
+
136
+ processEth1DataReset(state, cache);
137
+
138
+ if (fork >= ForkSeq.electra) {
139
+ const stateElectra = state as CachedBeaconStateElectra;
140
+ {
141
+ const timer = metrics?.epochTransitionStepTime.startTimer({
142
+ step: EpochTransitionStep.processPendingDeposits,
143
+ });
144
+ processPendingDeposits(stateElectra, cache);
145
+ timer?.();
146
+ }
147
+
148
+ {
149
+ const timer = metrics?.epochTransitionStepTime.startTimer({
150
+ step: EpochTransitionStep.processPendingConsolidations,
151
+ });
152
+ processPendingConsolidations(stateElectra, cache);
153
+ timer?.();
154
+ }
155
+ }
156
+
157
+ {
158
+ const timer = metrics?.epochTransitionStepTime.startTimer({
159
+ step: EpochTransitionStep.processEffectiveBalanceUpdates,
160
+ });
161
+ const numUpdate = processEffectiveBalanceUpdates(fork, state, cache);
162
+ timer?.();
163
+ metrics?.numEffectiveBalanceUpdates.set(numUpdate);
164
+ }
165
+
166
+ processSlashingsReset(state, cache);
167
+ processRandaoMixesReset(state, cache);
168
+
169
+ if (fork >= ForkSeq.capella) {
170
+ processHistoricalSummariesUpdate(state as CachedBeaconStateCapella, cache);
171
+ } else {
172
+ processHistoricalRootsUpdate(state, cache);
173
+ }
174
+
175
+ if (fork === ForkSeq.phase0) {
176
+ processParticipationRecordUpdates(state as CachedBeaconStatePhase0);
177
+ } else {
178
+ {
179
+ const timer = metrics?.epochTransitionStepTime.startTimer({
180
+ step: EpochTransitionStep.processParticipationFlagUpdates,
181
+ });
182
+ processParticipationFlagUpdates(state as CachedBeaconStateAltair);
183
+ timer?.();
184
+ }
185
+
186
+ {
187
+ const timer = metrics?.epochTransitionStepTime.startTimer({
188
+ step: EpochTransitionStep.processSyncCommitteeUpdates,
189
+ });
190
+ processSyncCommitteeUpdates(fork, state as CachedBeaconStateAltair);
191
+ timer?.();
192
+ }
193
+ }
194
+
195
+ if (fork >= ForkSeq.fulu) {
196
+ const timer = metrics?.epochTransitionStepTime.startTimer({
197
+ step: EpochTransitionStep.processProposerLookahead,
198
+ });
199
+ processProposerLookahead(fork, state as CachedBeaconStateFulu, cache);
200
+ timer?.();
201
+ }
202
+ }
@@ -0,0 +1,111 @@
1
+ import {
2
+ EFFECTIVE_BALANCE_INCREMENT,
3
+ ForkSeq,
4
+ HYSTERESIS_DOWNWARD_MULTIPLIER,
5
+ HYSTERESIS_QUOTIENT,
6
+ HYSTERESIS_UPWARD_MULTIPLIER,
7
+ MAX_EFFECTIVE_BALANCE,
8
+ MAX_EFFECTIVE_BALANCE_ELECTRA,
9
+ MIN_ACTIVATION_BALANCE,
10
+ TIMELY_TARGET_FLAG_INDEX,
11
+ } from "@lodestar/params";
12
+ import {BeaconStateAltair, CachedBeaconStateAllForks, EpochTransitionCache} from "../types.js";
13
+
14
+ /** Same to https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.5/specs/altair/beacon-chain.md#has_flag */
15
+ const TIMELY_TARGET = 1 << TIMELY_TARGET_FLAG_INDEX;
16
+ /**
17
+ * Update effective balances if validator.balance has changed enough
18
+ *
19
+ * PERF: Cost 'proportional' to $VALIDATOR_COUNT, to iterate over all balances. Then cost is proportional to the amount
20
+ * of validators whose effectiveBalance changed. Worst case is a massive network leak or a big slashing event which
21
+ * causes a large amount of the network to decrease their balance simultaneously.
22
+ *
23
+ * - On normal mainnet conditions 0 validators change their effective balance
24
+ * - In case of big innactivity event a medium portion of validators may have their effectiveBalance updated
25
+ *
26
+ * Return number of validators updated
27
+ */
28
+ export function processEffectiveBalanceUpdates(
29
+ fork: ForkSeq,
30
+ state: CachedBeaconStateAllForks,
31
+ cache: EpochTransitionCache
32
+ ): number {
33
+ const HYSTERESIS_INCREMENT = EFFECTIVE_BALANCE_INCREMENT / HYSTERESIS_QUOTIENT;
34
+ const DOWNWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_DOWNWARD_MULTIPLIER;
35
+ const UPWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_UPWARD_MULTIPLIER;
36
+ const {validators, epochCtx} = state;
37
+ const {effectiveBalanceIncrements} = epochCtx;
38
+ const forkSeq = epochCtx.config.getForkSeq(state.slot);
39
+ let nextEpochTotalActiveBalanceByIncrement = 0;
40
+
41
+ // update effective balances with hysteresis
42
+
43
+ // epochTransitionCache.balances is initialized in processRewardsAndPenalties()
44
+ // and updated in processPendingDeposits() and processPendingConsolidations()
45
+ // so it's recycled here for performance.
46
+ const balances = cache.balances ?? state.balances.getAll();
47
+ const isCompoundingValidatorArr = cache.isCompoundingValidatorArr;
48
+
49
+ let numUpdate = 0;
50
+ for (let i = 0, len = balances.length; i < len; i++) {
51
+ const balance = balances[i];
52
+
53
+ // PERF: It's faster to access to get() every single element (4ms) than to convert to regular array then loop (9ms)
54
+ let effectiveBalanceIncrement = effectiveBalanceIncrements[i];
55
+ let effectiveBalance = effectiveBalanceIncrement * EFFECTIVE_BALANCE_INCREMENT;
56
+
57
+ let effectiveBalanceLimit: number;
58
+ if (fork < ForkSeq.electra) {
59
+ effectiveBalanceLimit = MAX_EFFECTIVE_BALANCE;
60
+ } else {
61
+ // from electra, effectiveBalanceLimit is per validator
62
+ effectiveBalanceLimit = isCompoundingValidatorArr[i] ? MAX_EFFECTIVE_BALANCE_ELECTRA : MIN_ACTIVATION_BALANCE;
63
+ }
64
+
65
+ if (
66
+ // Too big
67
+ effectiveBalance > balance + DOWNWARD_THRESHOLD ||
68
+ // Too small. Check effectiveBalance < MAX_EFFECTIVE_BALANCE to prevent unnecessary updates
69
+ (effectiveBalance < effectiveBalanceLimit && effectiveBalance + UPWARD_THRESHOLD < balance)
70
+ ) {
71
+ // Update the state tree
72
+ // Should happen rarely, so it's fine to update the tree
73
+ const validator = validators.get(i);
74
+
75
+ effectiveBalance = Math.min(balance - (balance % EFFECTIVE_BALANCE_INCREMENT), effectiveBalanceLimit);
76
+ validator.effectiveBalance = effectiveBalance;
77
+ // Also update the fast cached version
78
+ const newEffectiveBalanceIncrement = Math.floor(effectiveBalance / EFFECTIVE_BALANCE_INCREMENT);
79
+
80
+ // TODO: describe issue. Compute progressive target balances
81
+ // Must update target balances for consistency, see comments below
82
+ if (forkSeq >= ForkSeq.altair) {
83
+ const deltaEffectiveBalanceIncrement = newEffectiveBalanceIncrement - effectiveBalanceIncrement;
84
+ const {previousEpochParticipation, currentEpochParticipation} = state as BeaconStateAltair;
85
+
86
+ if (!validator.slashed && (previousEpochParticipation.get(i) & TIMELY_TARGET) === TIMELY_TARGET) {
87
+ epochCtx.previousTargetUnslashedBalanceIncrements += deltaEffectiveBalanceIncrement;
88
+ }
89
+
90
+ // currentTargetUnslashedBalanceIncrements is transfered to previousTargetUnslashedBalanceIncrements in afterEpochTransitionCache
91
+ // at epoch transition of next epoch (in EpochTransitionCache), prevTargetUnslStake is calculated based on newEffectiveBalanceIncrement
92
+ if (!validator.slashed && (currentEpochParticipation.get(i) & TIMELY_TARGET) === TIMELY_TARGET) {
93
+ epochCtx.currentTargetUnslashedBalanceIncrements += deltaEffectiveBalanceIncrement;
94
+ }
95
+ }
96
+
97
+ effectiveBalanceIncrement = newEffectiveBalanceIncrement;
98
+ effectiveBalanceIncrements[i] = effectiveBalanceIncrement;
99
+ numUpdate++;
100
+ }
101
+
102
+ // TODO: Do this in afterEpochTransitionCache, looping a Uint8Array should be very cheap
103
+ if (cache.isActiveNextEpoch[i]) {
104
+ // We track nextEpochTotalActiveBalanceByIncrement as ETH to fit total network balance in a JS number (53 bits)
105
+ nextEpochTotalActiveBalanceByIncrement += effectiveBalanceIncrement;
106
+ }
107
+ }
108
+
109
+ cache.nextEpochTotalActiveBalanceByIncrement = nextEpochTotalActiveBalanceByIncrement;
110
+ return numUpdate;
111
+ }
@@ -0,0 +1,17 @@
1
+ import {EPOCHS_PER_ETH1_VOTING_PERIOD} from "@lodestar/params";
2
+ import {ssz} from "@lodestar/types";
3
+ import {CachedBeaconStateAllForks, EpochTransitionCache} from "../types.js";
4
+
5
+ /**
6
+ * Reset eth1DataVotes tree every `EPOCHS_PER_ETH1_VOTING_PERIOD`.
7
+ *
8
+ * PERF: Almost no (constant) cost
9
+ */
10
+ export function processEth1DataReset(state: CachedBeaconStateAllForks, cache: EpochTransitionCache): void {
11
+ const nextEpoch = cache.currentEpoch + 1;
12
+
13
+ // reset eth1 data votes
14
+ if (nextEpoch % EPOCHS_PER_ETH1_VOTING_PERIOD === 0) {
15
+ state.eth1DataVotes = ssz.phase0.Eth1DataVotes.defaultViewDU();
16
+ }
17
+ }
@@ -0,0 +1,25 @@
1
+ import {SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
2
+ import {ssz} from "@lodestar/types";
3
+ import {intDiv} from "@lodestar/utils";
4
+ import {CachedBeaconStateAllForks, EpochTransitionCache} from "../types.js";
5
+
6
+ /**
7
+ * Persist blockRoots and stateRoots to historicalRoots.
8
+ *
9
+ * PERF: Very low (constant) cost. Most of the HistoricalBatch should already be hashed.
10
+ */
11
+ export function processHistoricalRootsUpdate(state: CachedBeaconStateAllForks, cache: EpochTransitionCache): void {
12
+ const nextEpoch = cache.currentEpoch + 1;
13
+
14
+ // set historical root accumulator
15
+ if (nextEpoch % intDiv(SLOTS_PER_HISTORICAL_ROOT, SLOTS_PER_EPOCH) === 0) {
16
+ state.historicalRoots.push(
17
+ // HistoricalBatchRoots = Non-spec'ed helper type to allow efficient hashing in epoch transition.
18
+ // This type is like a 'Header' of HistoricalBatch where its fields are hashed.
19
+ ssz.phase0.HistoricalBatchRoots.hashTreeRoot({
20
+ blockRoots: state.blockRoots.hashTreeRoot(),
21
+ stateRoots: state.stateRoots.hashTreeRoot(),
22
+ })
23
+ );
24
+ }
25
+ }
@@ -0,0 +1,23 @@
1
+ import {SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
2
+ import {ssz} from "@lodestar/types";
3
+ import {intDiv} from "@lodestar/utils";
4
+ import {CachedBeaconStateCapella, EpochTransitionCache} from "../types.js";
5
+
6
+ /**
7
+ * Persist blockRoots and stateRoots to historicalSummaries.
8
+ *
9
+ * PERF: Very low (constant) cost. Most of the HistoricalSummaries should already be hashed.
10
+ */
11
+ export function processHistoricalSummariesUpdate(state: CachedBeaconStateCapella, cache: EpochTransitionCache): void {
12
+ const nextEpoch = cache.currentEpoch + 1;
13
+
14
+ // set historical root accumulator
15
+ if (nextEpoch % intDiv(SLOTS_PER_HISTORICAL_ROOT, SLOTS_PER_EPOCH) === 0) {
16
+ state.historicalSummaries.push(
17
+ ssz.capella.HistoricalSummary.toViewDU({
18
+ blockSummaryRoot: state.blockRoots.hashTreeRoot(),
19
+ stateSummaryRoot: state.stateRoots.hashTreeRoot(),
20
+ })
21
+ );
22
+ }
23
+ }
@@ -0,0 +1,60 @@
1
+ import {GENESIS_EPOCH} from "@lodestar/params";
2
+ import {CachedBeaconStateAltair, EpochTransitionCache} from "../types.js";
3
+ import * as attesterStatusUtil from "../util/attesterStatus.js";
4
+ import {isInInactivityLeak} from "../util/index.js";
5
+
6
+ /**
7
+ * This data is reused and never gc.
8
+ */
9
+ const inactivityScoresArr = new Array<number>();
10
+
11
+ /**
12
+ * Mutates `inactivityScores` from pre-calculated validator flags.
13
+ *
14
+ * PERF: Cost = iterate over an array of size $VALIDATOR_COUNT + 'proportional' to how many validtors are inactive or
15
+ * have been inactive in the past, i.e. that require an update to their inactivityScore. Worst case = all validators
16
+ * need to update their non-zero `inactivityScore`.
17
+ *
18
+ * - On normal mainnet conditions
19
+ * - prevTargetAttester: 96%
20
+ * - unslashed: 100%
21
+ * - eligibleAttester: 98%
22
+ *
23
+ * TODO: Compute from altair testnet inactivityScores updates on average
24
+ */
25
+ export function processInactivityUpdates(state: CachedBeaconStateAltair, cache: EpochTransitionCache): void {
26
+ if (state.epochCtx.epoch === GENESIS_EPOCH) {
27
+ return;
28
+ }
29
+
30
+ const {config, inactivityScores} = state;
31
+ const {INACTIVITY_SCORE_BIAS, INACTIVITY_SCORE_RECOVERY_RATE} = config;
32
+ const {flags} = cache;
33
+ const inActivityLeak = isInInactivityLeak(state);
34
+
35
+ // this avoids importing FLAG_ELIGIBLE_ATTESTER inside the for loop, check the compiled code
36
+ const {FLAG_PREV_TARGET_ATTESTER_UNSLASHED, FLAG_ELIGIBLE_ATTESTER, hasMarkers} = attesterStatusUtil;
37
+
38
+ inactivityScoresArr.length = state.validators.length;
39
+ inactivityScores.getAll(inactivityScoresArr);
40
+
41
+ for (let i = 0; i < flags.length; i++) {
42
+ const flag = flags[i];
43
+ if (hasMarkers(flag, FLAG_ELIGIBLE_ATTESTER)) {
44
+ let inactivityScore = inactivityScoresArr[i];
45
+
46
+ const prevInactivityScore = inactivityScore;
47
+ if (hasMarkers(flag, FLAG_PREV_TARGET_ATTESTER_UNSLASHED)) {
48
+ inactivityScore -= Math.min(1, inactivityScore);
49
+ } else {
50
+ inactivityScore += INACTIVITY_SCORE_BIAS;
51
+ }
52
+ if (!inActivityLeak) {
53
+ inactivityScore -= Math.min(INACTIVITY_SCORE_RECOVERY_RATE, inactivityScore);
54
+ }
55
+ if (inactivityScore !== prevInactivityScore) {
56
+ inactivityScores.set(i, inactivityScore);
57
+ }
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,90 @@
1
+ import {BitArray} from "@chainsafe/ssz";
2
+ import {GENESIS_EPOCH} from "@lodestar/params";
3
+ import {ssz} from "@lodestar/types";
4
+ import {CachedBeaconStateAllForks, EpochTransitionCache} from "../types.js";
5
+ import {computeEpochAtSlot, getBlockRoot} from "../util/index.js";
6
+
7
+ /**
8
+ * Update justified and finalized checkpoints depending on network participation.
9
+ *
10
+ * PERF: Very low (constant) cost. Persist small objects to the tree.
11
+ */
12
+ export function processJustificationAndFinalization(
13
+ state: CachedBeaconStateAllForks,
14
+ cache: EpochTransitionCache
15
+ ): void {
16
+ // Initial FFG checkpoint values have a `0x00` stub for `root`.
17
+ // Skip FFG updates in the first two epochs to avoid corner cases that might result in modifying this stub.
18
+ if (cache.currentEpoch <= GENESIS_EPOCH + 1) {
19
+ return;
20
+ }
21
+ weighJustificationAndFinalization(
22
+ state,
23
+ cache.totalActiveStakeByIncrement,
24
+ cache.prevEpochUnslashedStake.targetStakeByIncrement,
25
+ cache.currEpochUnslashedTargetStakeByIncrement
26
+ );
27
+ }
28
+
29
+ /**
30
+ * Updates `state` checkpoints based on previous and current target balance
31
+ */
32
+ export function weighJustificationAndFinalization(
33
+ state: CachedBeaconStateAllForks,
34
+ totalActiveBalance: number,
35
+ previousEpochTargetBalance: number,
36
+ currentEpochTargetBalance: number
37
+ ): void {
38
+ const currentEpoch = computeEpochAtSlot(state.slot);
39
+ const previousEpoch = currentEpoch - 1;
40
+
41
+ const oldPreviousJustifiedCheckpoint = state.previousJustifiedCheckpoint;
42
+ const oldCurrentJustifiedCheckpoint = state.currentJustifiedCheckpoint;
43
+
44
+ // Process justifications
45
+ state.previousJustifiedCheckpoint = state.currentJustifiedCheckpoint;
46
+ const bits = state.justificationBits.toBoolArray();
47
+
48
+ // Rotate bits
49
+ for (let i = bits.length - 1; i >= 1; i--) {
50
+ bits[i] = bits[i - 1];
51
+ }
52
+ bits[0] = false;
53
+
54
+ if (previousEpochTargetBalance * 3 >= totalActiveBalance * 2) {
55
+ state.currentJustifiedCheckpoint = ssz.phase0.Checkpoint.toViewDU({
56
+ epoch: previousEpoch,
57
+ root: getBlockRoot(state, previousEpoch),
58
+ });
59
+ bits[1] = true;
60
+ }
61
+ if (currentEpochTargetBalance * 3 >= totalActiveBalance * 2) {
62
+ state.currentJustifiedCheckpoint = ssz.phase0.Checkpoint.toViewDU({
63
+ epoch: currentEpoch,
64
+ root: getBlockRoot(state, currentEpoch),
65
+ });
66
+ bits[0] = true;
67
+ }
68
+
69
+ state.justificationBits = ssz.phase0.JustificationBits.toViewDU(BitArray.fromBoolArray(bits));
70
+
71
+ // TODO: Consider rendering bits as array of boolean for faster repeated access here
72
+
73
+ // Process finalizations
74
+ // The 2nd/3rd/4th most recent epochs are all justified, the 2nd using the 4th as source
75
+ if (bits[1] && bits[2] && bits[3] && oldPreviousJustifiedCheckpoint.epoch + 3 === currentEpoch) {
76
+ state.finalizedCheckpoint = oldPreviousJustifiedCheckpoint;
77
+ }
78
+ // The 2nd/3rd most recent epochs are both justified, the 2nd using the 3rd as source
79
+ if (bits[1] && bits[2] && oldPreviousJustifiedCheckpoint.epoch + 2 === currentEpoch) {
80
+ state.finalizedCheckpoint = oldPreviousJustifiedCheckpoint;
81
+ }
82
+ // The 1st/2nd/3rd most recent epochs are all justified, the 1st using the 3rd as source
83
+ if (bits[0] && bits[1] && bits[2] && oldCurrentJustifiedCheckpoint.epoch + 2 === currentEpoch) {
84
+ state.finalizedCheckpoint = oldCurrentJustifiedCheckpoint;
85
+ }
86
+ // The 1st/2nd most recent epochs are both justified, the 1st using the 2nd as source
87
+ if (bits[0] && bits[1] && oldCurrentJustifiedCheckpoint.epoch + 1 === currentEpoch) {
88
+ state.finalizedCheckpoint = oldCurrentJustifiedCheckpoint;
89
+ }
90
+ }