@lodestar/state-transition 1.35.0-dev.e18102ed8c → 1.35.0-dev.f45a2be721

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 (243) 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/processExecutionPayload.js +3 -3
  18. package/lib/block/processExecutionPayload.js.map +1 -1
  19. package/lib/block/processOperations.d.ts.map +1 -0
  20. package/lib/block/processProposerSlashing.d.ts.map +1 -0
  21. package/lib/block/processRandao.d.ts.map +1 -0
  22. package/lib/block/processSyncCommittee.d.ts.map +1 -0
  23. package/lib/block/processVoluntaryExit.d.ts.map +1 -0
  24. package/lib/block/processWithdrawalRequest.d.ts.map +1 -0
  25. package/lib/block/processWithdrawals.d.ts.map +1 -0
  26. package/lib/block/slashValidator.d.ts.map +1 -0
  27. package/lib/block/types.d.ts.map +1 -0
  28. package/lib/cache/effectiveBalanceIncrements.d.ts.map +1 -0
  29. package/lib/cache/epochCache.d.ts.map +1 -0
  30. package/lib/cache/epochTransitionCache.d.ts.map +1 -0
  31. package/lib/cache/pubkeyCache.d.ts.map +1 -0
  32. package/lib/cache/rewardCache.d.ts.map +1 -0
  33. package/lib/cache/stateCache.d.ts.map +1 -0
  34. package/lib/cache/syncCommitteeCache.d.ts.map +1 -0
  35. package/lib/cache/types.d.ts +2 -2
  36. package/lib/cache/types.d.ts.map +1 -0
  37. package/lib/constants/constants.d.ts.map +1 -0
  38. package/lib/constants/index.d.ts.map +1 -0
  39. package/lib/epoch/computeUnrealizedCheckpoints.d.ts.map +1 -0
  40. package/lib/epoch/getAttestationDeltas.d.ts.map +1 -0
  41. package/lib/epoch/getRewardsAndPenalties.d.ts.map +1 -0
  42. package/lib/epoch/index.d.ts.map +1 -0
  43. package/lib/epoch/processEffectiveBalanceUpdates.d.ts.map +1 -0
  44. package/lib/epoch/processEth1DataReset.d.ts.map +1 -0
  45. package/lib/epoch/processHistoricalRootsUpdate.d.ts.map +1 -0
  46. package/lib/epoch/processHistoricalSummariesUpdate.d.ts.map +1 -0
  47. package/lib/epoch/processInactivityUpdates.d.ts.map +1 -0
  48. package/lib/epoch/processJustificationAndFinalization.d.ts.map +1 -0
  49. package/lib/epoch/processParticipationFlagUpdates.d.ts.map +1 -0
  50. package/lib/epoch/processParticipationRecordUpdates.d.ts.map +1 -0
  51. package/lib/epoch/processPendingAttestations.d.ts.map +1 -0
  52. package/lib/epoch/processPendingConsolidations.d.ts.map +1 -0
  53. package/lib/epoch/processPendingDeposits.d.ts.map +1 -0
  54. package/lib/epoch/processProposerLookahead.d.ts.map +1 -0
  55. package/lib/epoch/processRandaoMixesReset.d.ts.map +1 -0
  56. package/lib/epoch/processRegistryUpdates.d.ts.map +1 -0
  57. package/lib/epoch/processRewardsAndPenalties.d.ts.map +1 -0
  58. package/lib/epoch/processSlashings.d.ts.map +1 -0
  59. package/lib/epoch/processSlashingsReset.d.ts.map +1 -0
  60. package/lib/epoch/processSyncCommitteeUpdates.d.ts.map +1 -0
  61. package/lib/index.d.ts.map +1 -0
  62. package/lib/metrics.d.ts.map +1 -0
  63. package/lib/signatureSets/attesterSlashings.d.ts.map +1 -0
  64. package/lib/signatureSets/blsToExecutionChange.d.ts.map +1 -0
  65. package/lib/signatureSets/index.d.ts.map +1 -0
  66. package/lib/signatureSets/indexedAttestation.d.ts.map +1 -0
  67. package/lib/signatureSets/proposer.d.ts.map +1 -0
  68. package/lib/signatureSets/proposerSlashings.d.ts.map +1 -0
  69. package/lib/signatureSets/randao.d.ts.map +1 -0
  70. package/lib/signatureSets/voluntaryExits.d.ts.map +1 -0
  71. package/lib/slot/index.d.ts.map +1 -0
  72. package/lib/slot/upgradeStateToAltair.d.ts.map +1 -0
  73. package/lib/slot/upgradeStateToBellatrix.d.ts.map +1 -0
  74. package/lib/slot/upgradeStateToCapella.d.ts.map +1 -0
  75. package/lib/slot/upgradeStateToDeneb.d.ts.map +1 -0
  76. package/lib/slot/upgradeStateToElectra.d.ts.map +1 -0
  77. package/lib/slot/upgradeStateToFulu.d.ts.map +1 -0
  78. package/lib/slot/upgradeStateToGloas.d.ts.map +1 -0
  79. package/lib/stateTransition.d.ts.map +1 -0
  80. package/lib/types.d.ts.map +1 -0
  81. package/lib/util/aggregator.d.ts.map +1 -0
  82. package/lib/util/altair.d.ts.map +1 -0
  83. package/lib/util/array.d.ts.map +1 -0
  84. package/lib/util/attestation.d.ts.map +1 -0
  85. package/lib/util/attesterStatus.d.ts.map +1 -0
  86. package/lib/util/balance.d.ts.map +1 -0
  87. package/lib/util/blindedBlock.d.ts +3 -3
  88. package/lib/util/blindedBlock.d.ts.map +1 -0
  89. package/lib/util/blindedBlock.js.map +1 -1
  90. package/lib/util/blockRoot.d.ts.map +1 -0
  91. package/lib/util/calculateCommitteeAssignments.d.ts.map +1 -0
  92. package/lib/util/capella.d.ts.map +1 -0
  93. package/lib/util/computeAnchorCheckpoint.d.ts.map +1 -0
  94. package/lib/util/deposit.d.ts.map +1 -0
  95. package/lib/util/domain.d.ts.map +1 -0
  96. package/lib/util/electra.d.ts.map +1 -0
  97. package/lib/util/epoch.d.ts.map +1 -0
  98. package/lib/util/epochShuffling.d.ts.map +1 -0
  99. package/lib/util/execution.d.ts +2 -2
  100. package/lib/util/execution.d.ts.map +1 -0
  101. package/lib/util/execution.js.map +1 -1
  102. package/lib/util/finality.d.ts.map +1 -0
  103. package/lib/util/fulu.d.ts.map +1 -0
  104. package/lib/util/genesis.d.ts.map +1 -0
  105. package/lib/util/genesis.js +0 -3
  106. package/lib/util/genesis.js.map +1 -1
  107. package/lib/util/index.d.ts.map +1 -0
  108. package/lib/util/interop.d.ts.map +1 -0
  109. package/lib/util/loadState/findModifiedInactivityScores.d.ts.map +1 -0
  110. package/lib/util/loadState/findModifiedValidators.d.ts.map +1 -0
  111. package/lib/util/loadState/index.d.ts.map +1 -0
  112. package/lib/util/loadState/loadState.d.ts.map +1 -0
  113. package/lib/util/loadState/loadValidator.d.ts.map +1 -0
  114. package/lib/util/rootCache.d.ts.map +1 -0
  115. package/lib/util/seed.d.ts.map +1 -0
  116. package/lib/util/shufflingDecisionRoot.d.ts.map +1 -0
  117. package/lib/util/signatureSets.d.ts.map +1 -0
  118. package/lib/util/signingRoot.d.ts.map +1 -0
  119. package/lib/util/slot.d.ts +0 -1
  120. package/lib/util/slot.d.ts.map +1 -0
  121. package/lib/util/slot.js +3 -7
  122. package/lib/util/slot.js.map +1 -1
  123. package/lib/util/sszBytes.d.ts.map +1 -0
  124. package/lib/util/syncCommittee.d.ts.map +1 -0
  125. package/lib/util/targetUnslashedBalance.d.ts.map +1 -0
  126. package/lib/util/validator.d.ts.map +1 -0
  127. package/lib/util/weakSubjectivity.d.ts.map +1 -0
  128. package/package.json +13 -11
  129. package/src/block/externalData.ts +26 -0
  130. package/src/block/index.ts +81 -0
  131. package/src/block/initiateValidatorExit.ts +62 -0
  132. package/src/block/isValidIndexedAttestation.ts +70 -0
  133. package/src/block/processAttestationPhase0.ts +158 -0
  134. package/src/block/processAttestations.ts +25 -0
  135. package/src/block/processAttestationsAltair.ts +184 -0
  136. package/src/block/processAttesterSlashing.ts +59 -0
  137. package/src/block/processBlobKzgCommitments.ts +21 -0
  138. package/src/block/processBlockHeader.ts +54 -0
  139. package/src/block/processBlsToExecutionChange.ts +78 -0
  140. package/src/block/processConsolidationRequest.ts +147 -0
  141. package/src/block/processDeposit.ts +166 -0
  142. package/src/block/processDepositRequest.ts +19 -0
  143. package/src/block/processEth1Data.ts +86 -0
  144. package/src/block/processExecutionPayload.ts +84 -0
  145. package/src/block/processOperations.ts +83 -0
  146. package/src/block/processProposerSlashing.ts +66 -0
  147. package/src/block/processRandao.ts +27 -0
  148. package/src/block/processSyncCommittee.ts +117 -0
  149. package/src/block/processVoluntaryExit.ts +55 -0
  150. package/src/block/processWithdrawalRequest.ts +98 -0
  151. package/src/block/processWithdrawals.ts +207 -0
  152. package/src/block/slashValidator.ts +98 -0
  153. package/src/block/types.ts +9 -0
  154. package/src/cache/effectiveBalanceIncrements.ts +39 -0
  155. package/src/cache/epochCache.ts +1213 -0
  156. package/src/cache/epochTransitionCache.ts +542 -0
  157. package/src/cache/pubkeyCache.ts +33 -0
  158. package/src/cache/rewardCache.ts +19 -0
  159. package/src/cache/stateCache.ts +268 -0
  160. package/src/cache/syncCommitteeCache.ts +96 -0
  161. package/src/cache/types.ts +18 -0
  162. package/src/constants/constants.ts +12 -0
  163. package/src/constants/index.ts +1 -0
  164. package/src/epoch/computeUnrealizedCheckpoints.ts +55 -0
  165. package/src/epoch/getAttestationDeltas.ts +169 -0
  166. package/src/epoch/getRewardsAndPenalties.ts +137 -0
  167. package/src/epoch/index.ts +202 -0
  168. package/src/epoch/processEffectiveBalanceUpdates.ts +111 -0
  169. package/src/epoch/processEth1DataReset.ts +17 -0
  170. package/src/epoch/processHistoricalRootsUpdate.ts +25 -0
  171. package/src/epoch/processHistoricalSummariesUpdate.ts +23 -0
  172. package/src/epoch/processInactivityUpdates.ts +60 -0
  173. package/src/epoch/processJustificationAndFinalization.ts +90 -0
  174. package/src/epoch/processParticipationFlagUpdates.ts +27 -0
  175. package/src/epoch/processParticipationRecordUpdates.ts +14 -0
  176. package/src/epoch/processPendingAttestations.ts +75 -0
  177. package/src/epoch/processPendingConsolidations.ts +59 -0
  178. package/src/epoch/processPendingDeposits.ts +136 -0
  179. package/src/epoch/processProposerLookahead.ts +39 -0
  180. package/src/epoch/processRandaoMixesReset.ts +18 -0
  181. package/src/epoch/processRegistryUpdates.ts +65 -0
  182. package/src/epoch/processRewardsAndPenalties.ts +58 -0
  183. package/src/epoch/processSlashings.ts +97 -0
  184. package/src/epoch/processSlashingsReset.ts +20 -0
  185. package/src/epoch/processSyncCommitteeUpdates.ts +44 -0
  186. package/src/index.ts +67 -0
  187. package/src/metrics.ts +169 -0
  188. package/src/signatureSets/attesterSlashings.ts +39 -0
  189. package/src/signatureSets/blsToExecutionChange.ts +43 -0
  190. package/src/signatureSets/index.ts +73 -0
  191. package/src/signatureSets/indexedAttestation.ts +51 -0
  192. package/src/signatureSets/proposer.ts +47 -0
  193. package/src/signatureSets/proposerSlashings.ts +41 -0
  194. package/src/signatureSets/randao.ts +31 -0
  195. package/src/signatureSets/voluntaryExits.ts +44 -0
  196. package/src/slot/index.ts +32 -0
  197. package/src/slot/upgradeStateToAltair.ts +149 -0
  198. package/src/slot/upgradeStateToBellatrix.ts +63 -0
  199. package/src/slot/upgradeStateToCapella.ts +71 -0
  200. package/src/slot/upgradeStateToDeneb.ts +40 -0
  201. package/src/slot/upgradeStateToElectra.ts +126 -0
  202. package/src/slot/upgradeStateToFulu.ts +31 -0
  203. package/src/slot/upgradeStateToGloas.ts +29 -0
  204. package/src/stateTransition.ts +305 -0
  205. package/src/types.ts +26 -0
  206. package/src/util/aggregator.ts +33 -0
  207. package/src/util/altair.ts +13 -0
  208. package/src/util/array.ts +53 -0
  209. package/src/util/attestation.ts +36 -0
  210. package/src/util/attesterStatus.ts +83 -0
  211. package/src/util/balance.ts +83 -0
  212. package/src/util/blindedBlock.ts +145 -0
  213. package/src/util/blockRoot.ts +72 -0
  214. package/src/util/calculateCommitteeAssignments.ts +43 -0
  215. package/src/util/capella.ts +8 -0
  216. package/src/util/computeAnchorCheckpoint.ts +38 -0
  217. package/src/util/deposit.ts +22 -0
  218. package/src/util/domain.ts +31 -0
  219. package/src/util/electra.ts +68 -0
  220. package/src/util/epoch.ts +135 -0
  221. package/src/util/epochShuffling.ts +185 -0
  222. package/src/util/execution.ts +179 -0
  223. package/src/util/finality.ts +17 -0
  224. package/src/util/fulu.ts +43 -0
  225. package/src/util/genesis.ts +340 -0
  226. package/src/util/index.ts +29 -0
  227. package/src/util/interop.ts +22 -0
  228. package/src/util/loadState/findModifiedInactivityScores.ts +47 -0
  229. package/src/util/loadState/findModifiedValidators.ts +46 -0
  230. package/src/util/loadState/index.ts +2 -0
  231. package/src/util/loadState/loadState.ts +225 -0
  232. package/src/util/loadState/loadValidator.ts +77 -0
  233. package/src/util/rootCache.ts +37 -0
  234. package/src/util/seed.ts +381 -0
  235. package/src/util/shufflingDecisionRoot.ts +78 -0
  236. package/src/util/signatureSets.ts +65 -0
  237. package/src/util/signingRoot.ts +13 -0
  238. package/src/util/slot.ts +22 -0
  239. package/src/util/sszBytes.ts +52 -0
  240. package/src/util/syncCommittee.ts +69 -0
  241. package/src/util/targetUnslashedBalance.ts +30 -0
  242. package/src/util/validator.ts +105 -0
  243. package/src/util/weakSubjectivity.ts +186 -0
@@ -0,0 +1,117 @@
1
+ import {byteArrayEquals} from "@chainsafe/ssz";
2
+ import {DOMAIN_SYNC_COMMITTEE, SYNC_COMMITTEE_SIZE} from "@lodestar/params";
3
+ import {altair, ssz} from "@lodestar/types";
4
+ import {G2_POINT_AT_INFINITY} from "../constants/index.js";
5
+ import {CachedBeaconStateAllForks} from "../types.js";
6
+ import {
7
+ ISignatureSet,
8
+ SignatureSetType,
9
+ computeSigningRoot,
10
+ decreaseBalance,
11
+ increaseBalance,
12
+ verifySignatureSet,
13
+ } from "../util/index.js";
14
+
15
+ export function processSyncAggregate(
16
+ state: CachedBeaconStateAllForks,
17
+ block: altair.BeaconBlock,
18
+ verifySignatures = true
19
+ ): void {
20
+ const committeeIndices = state.epochCtx.currentSyncCommitteeIndexed.validatorIndices;
21
+
22
+ // different from the spec but not sure how to get through signature verification for default/empty SyncAggregate in the spec test
23
+ if (verifySignatures) {
24
+ // This is to conform to the spec - we want the signature to be verified
25
+ const participantIndices = block.body.syncAggregate.syncCommitteeBits.intersectValues(committeeIndices);
26
+ const signatureSet = getSyncCommitteeSignatureSet(state, block, participantIndices);
27
+ // When there's no participation we consider the signature valid and just ignore i
28
+ if (signatureSet !== null && !verifySignatureSet(signatureSet)) {
29
+ throw Error("Sync committee signature invalid");
30
+ }
31
+ }
32
+
33
+ const {syncParticipantReward, syncProposerReward} = state.epochCtx;
34
+ const {syncCommitteeBits} = block.body.syncAggregate;
35
+ const proposerIndex = state.epochCtx.getBeaconProposer(state.slot);
36
+ let proposerBalance = state.balances.get(proposerIndex);
37
+
38
+ for (let i = 0; i < SYNC_COMMITTEE_SIZE; i++) {
39
+ const index = committeeIndices[i];
40
+
41
+ if (syncCommitteeBits.get(i)) {
42
+ // Positive rewards for participants
43
+ if (index === proposerIndex) {
44
+ proposerBalance += syncParticipantReward;
45
+ } else {
46
+ increaseBalance(state, index, syncParticipantReward);
47
+ }
48
+ // Proposer reward
49
+ proposerBalance += syncProposerReward;
50
+ state.proposerRewards.syncAggregate += syncProposerReward;
51
+ } else {
52
+ // Negative rewards for non participants
53
+ if (index === proposerIndex) {
54
+ proposerBalance = Math.max(0, proposerBalance - syncParticipantReward);
55
+ } else {
56
+ decreaseBalance(state, index, syncParticipantReward);
57
+ }
58
+ }
59
+ }
60
+
61
+ // Apply proposer balance
62
+ state.balances.set(proposerIndex, proposerBalance);
63
+ }
64
+
65
+ export function getSyncCommitteeSignatureSet(
66
+ state: CachedBeaconStateAllForks,
67
+ block: altair.BeaconBlock,
68
+ /** Optional parameter to prevent computing it twice */
69
+ participantIndices?: number[]
70
+ ): ISignatureSet | null {
71
+ const {epochCtx} = state;
72
+ const {syncAggregate} = block.body;
73
+ const signature = syncAggregate.syncCommitteeSignature;
74
+
75
+ // The spec uses the state to get the previous slot
76
+ // ```python
77
+ // previous_slot = max(state.slot, Slot(1)) - Slot(1)
78
+ // ```
79
+ // However we need to run the function getSyncCommitteeSignatureSet() for all the blocks in a epoch
80
+ // with the same state when verifying blocks in batch on RangeSync. Therefore we use the block.slot.
81
+ const previousSlot = Math.max(block.slot, 1) - 1;
82
+
83
+ // The spec uses the state to get the root at previousSlot
84
+ // ```python
85
+ // get_block_root_at_slot(state, previous_slot)
86
+ // ```
87
+ // However we need to run the function getSyncCommitteeSignatureSet() for all the blocks in a epoch
88
+ // with the same state when verifying blocks in batch on RangeSync.
89
+ //
90
+ // On skipped slots state block roots just copy the latest block, so using the parentRoot here is equivalent.
91
+ // So getSyncCommitteeSignatureSet() can be called with a state in any slot (with the correct shuffling)
92
+ const rootSigned = block.parentRoot;
93
+
94
+ if (!participantIndices) {
95
+ const committeeIndices = state.epochCtx.currentSyncCommitteeIndexed.validatorIndices;
96
+ participantIndices = syncAggregate.syncCommitteeBits.intersectValues(committeeIndices);
97
+ }
98
+
99
+ // When there's no participation we consider the signature valid and just ignore it
100
+ if (participantIndices.length === 0) {
101
+ // Must set signature as G2_POINT_AT_INFINITY when participating bits are empty
102
+ // https://github.com/ethereum/eth2.0-specs/blob/30f2a076377264677e27324a8c3c78c590ae5e20/specs/altair/bls.md#eth2_fast_aggregate_verify
103
+ if (byteArrayEquals(signature, G2_POINT_AT_INFINITY)) {
104
+ return null;
105
+ }
106
+ throw Error("Empty sync committee signature is not infinity");
107
+ }
108
+
109
+ const domain = state.config.getDomain(state.slot, DOMAIN_SYNC_COMMITTEE, previousSlot);
110
+
111
+ return {
112
+ type: SignatureSetType.aggregate,
113
+ pubkeys: participantIndices.map((i) => epochCtx.index2pubkey[i]),
114
+ signingRoot: computeSigningRoot(ssz.Root, rootSigned, domain),
115
+ signature,
116
+ };
117
+ }
@@ -0,0 +1,55 @@
1
+ import {FAR_FUTURE_EPOCH, ForkSeq} from "@lodestar/params";
2
+ import {phase0} from "@lodestar/types";
3
+ import {verifyVoluntaryExitSignature} from "../signatureSets/index.js";
4
+ import {CachedBeaconStateAllForks, CachedBeaconStateElectra} from "../types.js";
5
+ import {getPendingBalanceToWithdraw, isActiveValidator} from "../util/index.js";
6
+ import {initiateValidatorExit} from "./index.js";
7
+
8
+ /**
9
+ * Process a VoluntaryExit operation. Initiates the exit of a validator.
10
+ *
11
+ * PERF: Work depends on number of VoluntaryExit per block. On regular networks the average is 0 / block.
12
+ */
13
+ export function processVoluntaryExit(
14
+ fork: ForkSeq,
15
+ state: CachedBeaconStateAllForks,
16
+ signedVoluntaryExit: phase0.SignedVoluntaryExit,
17
+ verifySignature = true
18
+ ): void {
19
+ if (!isValidVoluntaryExit(fork, state, signedVoluntaryExit, verifySignature)) {
20
+ throw Error(`Invalid voluntary exit at forkSeq=${fork}`);
21
+ }
22
+
23
+ const validator = state.validators.get(signedVoluntaryExit.message.validatorIndex);
24
+ initiateValidatorExit(fork, state, validator);
25
+ }
26
+
27
+ export function isValidVoluntaryExit(
28
+ fork: ForkSeq,
29
+ state: CachedBeaconStateAllForks,
30
+ signedVoluntaryExit: phase0.SignedVoluntaryExit,
31
+ verifySignature = true
32
+ ): boolean {
33
+ const {config, epochCtx} = state;
34
+ const voluntaryExit = signedVoluntaryExit.message;
35
+ const validator = state.validators.get(voluntaryExit.validatorIndex);
36
+ const currentEpoch = epochCtx.epoch;
37
+
38
+ return (
39
+ // verify the validator is active
40
+ isActiveValidator(validator, currentEpoch) &&
41
+ // verify exit has not been initiated
42
+ validator.exitEpoch === FAR_FUTURE_EPOCH &&
43
+ // exits must specify an epoch when they become valid; they are not valid before then
44
+ currentEpoch >= voluntaryExit.epoch &&
45
+ // verify the validator had been active long enough
46
+ currentEpoch >= validator.activationEpoch + config.SHARD_COMMITTEE_PERIOD &&
47
+ (fork >= ForkSeq.electra
48
+ ? // only exit validator if it has no pending withdrawals in the queue
49
+ getPendingBalanceToWithdraw(state as CachedBeaconStateElectra, voluntaryExit.validatorIndex) === 0
50
+ : // there are no pending withdrawals in previous forks
51
+ true) &&
52
+ // verify signature
53
+ (!verifySignature || verifyVoluntaryExitSignature(state, signedVoluntaryExit))
54
+ );
55
+ }
@@ -0,0 +1,98 @@
1
+ import {
2
+ FAR_FUTURE_EPOCH,
3
+ FULL_EXIT_REQUEST_AMOUNT,
4
+ ForkSeq,
5
+ MIN_ACTIVATION_BALANCE,
6
+ PENDING_PARTIAL_WITHDRAWALS_LIMIT,
7
+ } from "@lodestar/params";
8
+ import {electra, phase0, ssz} from "@lodestar/types";
9
+ import {toHex} from "@lodestar/utils";
10
+ import {CachedBeaconStateElectra} from "../types.js";
11
+ import {hasCompoundingWithdrawalCredential, hasExecutionWithdrawalCredential} from "../util/electra.js";
12
+ import {computeExitEpochAndUpdateChurn} from "../util/epoch.js";
13
+ import {getPendingBalanceToWithdraw, isActiveValidator} from "../util/validator.js";
14
+ import {initiateValidatorExit} from "./initiateValidatorExit.js";
15
+
16
+ export function processWithdrawalRequest(
17
+ fork: ForkSeq,
18
+ state: CachedBeaconStateElectra,
19
+ withdrawalRequest: electra.WithdrawalRequest
20
+ ): void {
21
+ const amount = Number(withdrawalRequest.amount);
22
+ const {pendingPartialWithdrawals, validators, epochCtx} = state;
23
+ // no need to use unfinalized pubkey cache from 6110 as validator won't be active anyway
24
+ const {pubkey2index, config} = epochCtx;
25
+ const isFullExitRequest = amount === FULL_EXIT_REQUEST_AMOUNT;
26
+
27
+ // If partial withdrawal queue is full, only full exits are processed
28
+ if (pendingPartialWithdrawals.length >= PENDING_PARTIAL_WITHDRAWALS_LIMIT && !isFullExitRequest) {
29
+ return;
30
+ }
31
+
32
+ // bail out if validator is not in beacon state
33
+ // note that we don't need to check for 6110 unfinalized vals as they won't be eligible for withdraw/exit anyway
34
+ const validatorIndex = pubkey2index.get(withdrawalRequest.validatorPubkey);
35
+ if (validatorIndex === null) {
36
+ return;
37
+ }
38
+
39
+ const validator = validators.get(validatorIndex);
40
+ if (!isValidatorEligibleForWithdrawOrExit(validator, withdrawalRequest.sourceAddress, state)) {
41
+ return;
42
+ }
43
+
44
+ // TODO Electra: Consider caching pendingPartialWithdrawals
45
+ const pendingBalanceToWithdraw = getPendingBalanceToWithdraw(state, validatorIndex);
46
+ const validatorBalance = state.balances.get(validatorIndex);
47
+
48
+ if (isFullExitRequest) {
49
+ // only exit validator if it has no pending withdrawals in the queue
50
+ if (pendingBalanceToWithdraw === 0) {
51
+ initiateValidatorExit(fork, state, validator);
52
+ }
53
+ return;
54
+ }
55
+
56
+ // partial withdrawal request
57
+ const hasSufficientEffectiveBalance = validator.effectiveBalance >= MIN_ACTIVATION_BALANCE;
58
+ const hasExcessBalance = validatorBalance > MIN_ACTIVATION_BALANCE + pendingBalanceToWithdraw;
59
+
60
+ // Only allow partial withdrawals with compounding withdrawal credentials
61
+ if (
62
+ hasCompoundingWithdrawalCredential(validator.withdrawalCredentials) &&
63
+ hasSufficientEffectiveBalance &&
64
+ hasExcessBalance
65
+ ) {
66
+ const amountToWithdraw = BigInt(
67
+ Math.min(validatorBalance - MIN_ACTIVATION_BALANCE - pendingBalanceToWithdraw, amount)
68
+ );
69
+ const exitQueueEpoch = computeExitEpochAndUpdateChurn(state, amountToWithdraw);
70
+ const withdrawableEpoch = exitQueueEpoch + config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY;
71
+
72
+ const pendingPartialWithdrawal = ssz.electra.PendingPartialWithdrawal.toViewDU({
73
+ validatorIndex,
74
+ amount: amountToWithdraw,
75
+ withdrawableEpoch,
76
+ });
77
+ state.pendingPartialWithdrawals.push(pendingPartialWithdrawal);
78
+ }
79
+ }
80
+
81
+ function isValidatorEligibleForWithdrawOrExit(
82
+ validator: phase0.Validator,
83
+ sourceAddress: Uint8Array,
84
+ state: CachedBeaconStateElectra
85
+ ): boolean {
86
+ const {withdrawalCredentials} = validator;
87
+ const addressStr = toHex(withdrawalCredentials.subarray(12));
88
+ const sourceAddressStr = toHex(sourceAddress);
89
+ const {epoch: currentEpoch, config} = state.epochCtx;
90
+
91
+ return (
92
+ hasExecutionWithdrawalCredential(withdrawalCredentials) &&
93
+ addressStr === sourceAddressStr &&
94
+ isActiveValidator(validator, currentEpoch) &&
95
+ validator.exitEpoch === FAR_FUTURE_EPOCH &&
96
+ currentEpoch >= validator.activationEpoch + config.SHARD_COMMITTEE_PERIOD
97
+ );
98
+ }
@@ -0,0 +1,207 @@
1
+ import {byteArrayEquals} from "@chainsafe/ssz";
2
+ import {
3
+ FAR_FUTURE_EPOCH,
4
+ ForkSeq,
5
+ MAX_EFFECTIVE_BALANCE,
6
+ MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP,
7
+ MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP,
8
+ MAX_WITHDRAWALS_PER_PAYLOAD,
9
+ MIN_ACTIVATION_BALANCE,
10
+ } from "@lodestar/params";
11
+ import {ValidatorIndex, capella, ssz} from "@lodestar/types";
12
+ import {MapDef, toRootHex} from "@lodestar/utils";
13
+ import {CachedBeaconStateCapella, CachedBeaconStateElectra} from "../types.js";
14
+ import {
15
+ decreaseBalance,
16
+ getMaxEffectiveBalance,
17
+ hasEth1WithdrawalCredential,
18
+ hasExecutionWithdrawalCredential,
19
+ isCapellaPayloadHeader,
20
+ } from "../util/index.js";
21
+
22
+ export function processWithdrawals(
23
+ fork: ForkSeq,
24
+ state: CachedBeaconStateCapella | CachedBeaconStateElectra,
25
+ payload: capella.FullOrBlindedExecutionPayload
26
+ ): void {
27
+ // processedPartialWithdrawalsCount is withdrawals coming from EL since electra (EIP-7002)
28
+ const {withdrawals: expectedWithdrawals, processedPartialWithdrawalsCount} = getExpectedWithdrawals(fork, state);
29
+ const numWithdrawals = expectedWithdrawals.length;
30
+
31
+ if (isCapellaPayloadHeader(payload)) {
32
+ const expectedWithdrawalsRoot = ssz.capella.Withdrawals.hashTreeRoot(expectedWithdrawals);
33
+ const actualWithdrawalsRoot = payload.withdrawalsRoot;
34
+ if (!byteArrayEquals(expectedWithdrawalsRoot, actualWithdrawalsRoot)) {
35
+ throw Error(
36
+ `Invalid withdrawalsRoot of executionPayloadHeader, expected=${toRootHex(
37
+ expectedWithdrawalsRoot
38
+ )}, actual=${toRootHex(actualWithdrawalsRoot)}`
39
+ );
40
+ }
41
+ } else {
42
+ if (expectedWithdrawals.length !== payload.withdrawals.length) {
43
+ throw Error(`Invalid withdrawals length expected=${numWithdrawals} actual=${payload.withdrawals.length}`);
44
+ }
45
+ for (let i = 0; i < numWithdrawals; i++) {
46
+ const withdrawal = expectedWithdrawals[i];
47
+ if (!ssz.capella.Withdrawal.equals(withdrawal, payload.withdrawals[i])) {
48
+ throw Error(`Withdrawal mismatch at index=${i}`);
49
+ }
50
+ }
51
+ }
52
+
53
+ for (let i = 0; i < numWithdrawals; i++) {
54
+ const withdrawal = expectedWithdrawals[i];
55
+ decreaseBalance(state, withdrawal.validatorIndex, Number(withdrawal.amount));
56
+ }
57
+
58
+ if (fork >= ForkSeq.electra) {
59
+ const stateElectra = state as CachedBeaconStateElectra;
60
+ stateElectra.pendingPartialWithdrawals = stateElectra.pendingPartialWithdrawals.sliceFrom(
61
+ processedPartialWithdrawalsCount
62
+ );
63
+ }
64
+
65
+ // Update the nextWithdrawalIndex
66
+ const latestWithdrawal = expectedWithdrawals.at(-1);
67
+ if (latestWithdrawal) {
68
+ state.nextWithdrawalIndex = latestWithdrawal.index + 1;
69
+ }
70
+
71
+ // Update the nextWithdrawalValidatorIndex
72
+ if (latestWithdrawal && expectedWithdrawals.length === MAX_WITHDRAWALS_PER_PAYLOAD) {
73
+ // All slots filled, nextWithdrawalValidatorIndex should be validatorIndex having next turn
74
+ state.nextWithdrawalValidatorIndex = (latestWithdrawal.validatorIndex + 1) % state.validators.length;
75
+ } else {
76
+ // expected withdrawals came up short in the bound, so we move nextWithdrawalValidatorIndex to
77
+ // the next post the bound
78
+ state.nextWithdrawalValidatorIndex =
79
+ (state.nextWithdrawalValidatorIndex + MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP) % state.validators.length;
80
+ }
81
+ }
82
+
83
+ export function getExpectedWithdrawals(
84
+ fork: ForkSeq,
85
+ state: CachedBeaconStateCapella | CachedBeaconStateElectra
86
+ ): {
87
+ withdrawals: capella.Withdrawal[];
88
+ sampledValidators: number;
89
+ processedPartialWithdrawalsCount: number;
90
+ } {
91
+ if (fork < ForkSeq.capella) {
92
+ throw new Error(`getExpectedWithdrawals not supported at forkSeq=${fork} < ForkSeq.capella`);
93
+ }
94
+
95
+ const epoch = state.epochCtx.epoch;
96
+ let withdrawalIndex = state.nextWithdrawalIndex;
97
+ const {validators, balances, nextWithdrawalValidatorIndex} = state;
98
+
99
+ const withdrawals: capella.Withdrawal[] = [];
100
+ const withdrawnBalances = new MapDef<ValidatorIndex, number>(() => 0);
101
+ const isPostElectra = fork >= ForkSeq.electra;
102
+ // partialWithdrawalsCount is withdrawals coming from EL since electra (EIP-7002)
103
+ let processedPartialWithdrawalsCount = 0;
104
+
105
+ if (isPostElectra) {
106
+ const stateElectra = state as CachedBeaconStateElectra;
107
+
108
+ // MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP = 8, PENDING_PARTIAL_WITHDRAWALS_LIMIT: 134217728 so we should only call getAllReadonly() if it makes sense
109
+ // pendingPartialWithdrawals comes from EIP-7002 smart contract where it takes fee so it's more likely than not validator is in correct condition to withdraw
110
+ // also we may break early if withdrawableEpoch > epoch
111
+ const allPendingPartialWithdrawals =
112
+ stateElectra.pendingPartialWithdrawals.length <= MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP
113
+ ? stateElectra.pendingPartialWithdrawals.getAllReadonly()
114
+ : null;
115
+
116
+ // EIP-7002: Execution layer triggerable withdrawals
117
+ for (let i = 0; i < stateElectra.pendingPartialWithdrawals.length; i++) {
118
+ const withdrawal = allPendingPartialWithdrawals
119
+ ? allPendingPartialWithdrawals[i]
120
+ : stateElectra.pendingPartialWithdrawals.getReadonly(i);
121
+ if (withdrawal.withdrawableEpoch > epoch || withdrawals.length === MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP) {
122
+ break;
123
+ }
124
+
125
+ const validator = validators.getReadonly(withdrawal.validatorIndex);
126
+ const totalWithdrawn = withdrawnBalances.getOrDefault(withdrawal.validatorIndex);
127
+ const balance = state.balances.get(withdrawal.validatorIndex) - totalWithdrawn;
128
+
129
+ if (
130
+ validator.exitEpoch === FAR_FUTURE_EPOCH &&
131
+ validator.effectiveBalance >= MIN_ACTIVATION_BALANCE &&
132
+ balance > MIN_ACTIVATION_BALANCE
133
+ ) {
134
+ const balanceOverMinActivationBalance = BigInt(balance - MIN_ACTIVATION_BALANCE);
135
+ const withdrawableBalance =
136
+ balanceOverMinActivationBalance < withdrawal.amount ? balanceOverMinActivationBalance : withdrawal.amount;
137
+ withdrawals.push({
138
+ index: withdrawalIndex,
139
+ validatorIndex: withdrawal.validatorIndex,
140
+ address: validator.withdrawalCredentials.subarray(12),
141
+ amount: withdrawableBalance,
142
+ });
143
+ withdrawalIndex++;
144
+ withdrawnBalances.set(withdrawal.validatorIndex, totalWithdrawn + Number(withdrawableBalance));
145
+ }
146
+ processedPartialWithdrawalsCount++;
147
+ }
148
+ }
149
+
150
+ const bound = Math.min(validators.length, MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP);
151
+ let n = 0;
152
+ // Just run a bounded loop max iterating over all withdrawals
153
+ // however breaks out once we have MAX_WITHDRAWALS_PER_PAYLOAD
154
+ for (n = 0; n < bound; n++) {
155
+ // Get next validator in turn
156
+ const validatorIndex = (nextWithdrawalValidatorIndex + n) % validators.length;
157
+
158
+ const validator = validators.getReadonly(validatorIndex);
159
+ const withdrawnBalance = withdrawnBalances.getOrDefault(validatorIndex);
160
+ const balance = isPostElectra
161
+ ? // Deduct partially withdrawn balance already queued above
162
+ balances.get(validatorIndex) - withdrawnBalance
163
+ : balances.get(validatorIndex);
164
+ const {withdrawableEpoch, withdrawalCredentials, effectiveBalance} = validator;
165
+ const hasWithdrawableCredentials = isPostElectra
166
+ ? hasExecutionWithdrawalCredential(withdrawalCredentials)
167
+ : hasEth1WithdrawalCredential(withdrawalCredentials);
168
+ // early skip for balance = 0 as its now more likely that validator has exited/slashed with
169
+ // balance zero than not have withdrawal credentials set
170
+ if (balance === 0 || !hasWithdrawableCredentials) {
171
+ continue;
172
+ }
173
+
174
+ // capella full withdrawal
175
+ if (withdrawableEpoch <= epoch) {
176
+ withdrawals.push({
177
+ index: withdrawalIndex,
178
+ validatorIndex,
179
+ address: validator.withdrawalCredentials.subarray(12),
180
+ amount: BigInt(balance),
181
+ });
182
+ withdrawalIndex++;
183
+ withdrawnBalances.set(validatorIndex, withdrawnBalance + balance);
184
+ } else if (
185
+ effectiveBalance === (isPostElectra ? getMaxEffectiveBalance(withdrawalCredentials) : MAX_EFFECTIVE_BALANCE) &&
186
+ balance > effectiveBalance
187
+ ) {
188
+ // capella partial withdrawal
189
+ const partialAmount = balance - effectiveBalance;
190
+ withdrawals.push({
191
+ index: withdrawalIndex,
192
+ validatorIndex,
193
+ address: validator.withdrawalCredentials.subarray(12),
194
+ amount: BigInt(partialAmount),
195
+ });
196
+ withdrawalIndex++;
197
+ withdrawnBalances.set(validatorIndex, withdrawnBalance + partialAmount);
198
+ }
199
+
200
+ // Break if we have enough to pack the block
201
+ if (withdrawals.length >= MAX_WITHDRAWALS_PER_PAYLOAD) {
202
+ break;
203
+ }
204
+ }
205
+
206
+ return {withdrawals, sampledValidators: n, processedPartialWithdrawalsCount};
207
+ }
@@ -0,0 +1,98 @@
1
+ import {
2
+ EFFECTIVE_BALANCE_INCREMENT,
3
+ EPOCHS_PER_SLASHINGS_VECTOR,
4
+ ForkSeq,
5
+ MIN_SLASHING_PENALTY_QUOTIENT,
6
+ MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR,
7
+ MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX,
8
+ MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA,
9
+ PROPOSER_REWARD_QUOTIENT,
10
+ PROPOSER_WEIGHT,
11
+ TIMELY_TARGET_FLAG_INDEX,
12
+ WEIGHT_DENOMINATOR,
13
+ WHISTLEBLOWER_REWARD_QUOTIENT,
14
+ WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA,
15
+ } from "@lodestar/params";
16
+ import {ValidatorIndex} from "@lodestar/types";
17
+ import {CachedBeaconStateAllForks, CachedBeaconStateAltair} from "../types.js";
18
+ import {decreaseBalance, increaseBalance} from "../util/index.js";
19
+ import {initiateValidatorExit} from "./initiateValidatorExit.js";
20
+
21
+ /** Same to https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.5/specs/altair/beacon-chain.md#has_flag */
22
+ const TIMELY_TARGET = 1 << TIMELY_TARGET_FLAG_INDEX;
23
+
24
+ export function slashValidator(
25
+ fork: ForkSeq,
26
+ state: CachedBeaconStateAllForks,
27
+ slashedIndex: ValidatorIndex,
28
+ whistleblowerIndex?: ValidatorIndex
29
+ ): void {
30
+ const {epochCtx} = state;
31
+ const {epoch, effectiveBalanceIncrements} = epochCtx;
32
+ const validator = state.validators.get(slashedIndex);
33
+
34
+ // TODO: Bellatrix initiateValidatorExit validators.update() with the one below
35
+ initiateValidatorExit(fork, state, validator);
36
+
37
+ validator.slashed = true;
38
+ validator.withdrawableEpoch = Math.max(validator.withdrawableEpoch, epoch + EPOCHS_PER_SLASHINGS_VECTOR);
39
+
40
+ const {effectiveBalance} = validator;
41
+
42
+ // state.slashings is initially a Gwei (BigInt) vector, however since Nov 2023 it's converted to UintNum64 (number) vector in the state transition because:
43
+ // - state.slashings[nextEpoch % EPOCHS_PER_SLASHINGS_VECTOR] is reset per epoch in processSlashingsReset()
44
+ // - max slashed validators per epoch is SLOTS_PER_EPOCH * MAX_ATTESTER_SLASHINGS * MAX_VALIDATORS_PER_COMMITTEE which is 32 * 2 * 2048 = 131072 on mainnet
45
+ // - 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
46
+ // - we don't need to compute the total slashings from state.slashings, it's handled by totalSlashingsByIncrement in EpochCache
47
+ const slashingIndex = epoch % EPOCHS_PER_SLASHINGS_VECTOR;
48
+ state.slashings.set(slashingIndex, (state.slashings.get(slashingIndex) ?? 0) + effectiveBalance);
49
+ epochCtx.totalSlashingsByIncrement += effectiveBalanceIncrements[slashedIndex];
50
+
51
+ const minSlashingPenaltyQuotient =
52
+ fork === ForkSeq.phase0
53
+ ? MIN_SLASHING_PENALTY_QUOTIENT
54
+ : fork === ForkSeq.altair
55
+ ? MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR
56
+ : fork < ForkSeq.electra // no change from bellatrix to deneb
57
+ ? MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX
58
+ : MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA;
59
+ decreaseBalance(state, slashedIndex, Math.floor(effectiveBalance / minSlashingPenaltyQuotient));
60
+
61
+ // apply proposer and whistleblower rewards
62
+ const whistleblowerReward =
63
+ fork < ForkSeq.electra
64
+ ? Math.floor(effectiveBalance / WHISTLEBLOWER_REWARD_QUOTIENT)
65
+ : Math.floor(effectiveBalance / WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA);
66
+ const proposerReward =
67
+ fork === ForkSeq.phase0
68
+ ? Math.floor(whistleblowerReward / PROPOSER_REWARD_QUOTIENT)
69
+ : Math.floor((whistleblowerReward * PROPOSER_WEIGHT) / WEIGHT_DENOMINATOR);
70
+
71
+ const proposerIndex = epochCtx.getBeaconProposer(state.slot);
72
+ if (whistleblowerIndex === undefined || !Number.isSafeInteger(whistleblowerIndex)) {
73
+ // Call increaseBalance() once with `(whistleblowerReward - proposerReward) + proposerReward`
74
+ increaseBalance(state, proposerIndex, whistleblowerReward);
75
+ state.proposerRewards.slashing += whistleblowerReward;
76
+ } else {
77
+ increaseBalance(state, proposerIndex, proposerReward);
78
+ increaseBalance(state, whistleblowerIndex, whistleblowerReward - proposerReward);
79
+ state.proposerRewards.slashing += proposerReward;
80
+ }
81
+
82
+ // TODO: describe issue. Compute progressive target balances
83
+ // if a validator is slashed, lookup their participation and remove from the cumulative values
84
+ if (fork >= ForkSeq.altair) {
85
+ const {previousEpochParticipation, currentEpochParticipation} = state as CachedBeaconStateAltair;
86
+
87
+ if ((previousEpochParticipation.get(slashedIndex) & TIMELY_TARGET) === TIMELY_TARGET) {
88
+ state.epochCtx.previousTargetUnslashedBalanceIncrements -= Math.floor(
89
+ effectiveBalance / EFFECTIVE_BALANCE_INCREMENT
90
+ );
91
+ }
92
+ if ((currentEpochParticipation.get(slashedIndex) & TIMELY_TARGET) === TIMELY_TARGET) {
93
+ state.epochCtx.currentTargetUnslashedBalanceIncrements -= Math.floor(
94
+ effectiveBalance / EFFECTIVE_BALANCE_INCREMENT
95
+ );
96
+ }
97
+ }
98
+ }
@@ -0,0 +1,9 @@
1
+ export interface ProcessBlockOpts {
2
+ verifySignatures?: boolean;
3
+ }
4
+
5
+ export enum ProposerRewardType {
6
+ attestation = "attestation",
7
+ syncAggregate = "sync_aggregate",
8
+ slashing = "slashing",
9
+ }
@@ -0,0 +1,39 @@
1
+ import {EFFECTIVE_BALANCE_INCREMENT} from "@lodestar/params";
2
+ import {BeaconStateAllForks} from "../types.js";
3
+
4
+ /**
5
+ * Alias to allow easier refactoring.
6
+ */
7
+ export type EffectiveBalanceIncrements = Uint16Array;
8
+
9
+ /** Helper to prevent re-writting tests downstream if we change Uint16Array to number[] */
10
+ export function getEffectiveBalanceIncrementsZeroed(len: number): EffectiveBalanceIncrements {
11
+ return new Uint16Array(len);
12
+ }
13
+
14
+ /**
15
+ * effectiveBalanceIncrements length will always be equal or greater than validatorCount. The
16
+ * getEffectiveBalanceIncrementsByteLen() modulo is used to reduce the frequency at which its Uint16Array is recreated.
17
+ * if effectiveBalanceIncrements has length greater than validatorCount it's not a problem since those values would
18
+ * never be accessed.
19
+ */
20
+ export function getEffectiveBalanceIncrementsWithLen(validatorCount: number): EffectiveBalanceIncrements {
21
+ // TODO: Research what's the best number to minimize both memory cost and copy costs
22
+ const byteLen = 1024 * Math.ceil(validatorCount / 1024);
23
+
24
+ return new Uint16Array(byteLen);
25
+ }
26
+
27
+ /**
28
+ * Shows how EffectiveBalanceIncrements is meant to be populated.
29
+ * In practice this function should not be used, since it more efficient to loop the validators array once and do
30
+ * more tasks than only populating effectiveBalanceIncrements
31
+ */
32
+ export function getEffectiveBalanceIncrements(state: BeaconStateAllForks): EffectiveBalanceIncrements {
33
+ const validatorsArr = state.validators.getAllReadonlyValues();
34
+ const effectiveBalanceIncrements = new Uint16Array(validatorsArr.length);
35
+ for (let i = 0; i < validatorsArr.length; i++) {
36
+ effectiveBalanceIncrements[i] = Math.floor(validatorsArr[i].effectiveBalance / EFFECTIVE_BALANCE_INCREMENT);
37
+ }
38
+ return effectiveBalanceIncrements;
39
+ }