@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,381 @@
1
+ import {digest} from "@chainsafe/as-sha256";
2
+ import {
3
+ computeProposerIndex as nativeComputeProposerIndex,
4
+ computeSyncCommitteeIndices as nativeComputeSyncCommitteeIndices,
5
+ } from "@chainsafe/swap-or-not-shuffle";
6
+ import {
7
+ DOMAIN_BEACON_PROPOSER,
8
+ DOMAIN_SYNC_COMMITTEE,
9
+ EFFECTIVE_BALANCE_INCREMENT,
10
+ EPOCHS_PER_HISTORICAL_VECTOR,
11
+ ForkSeq,
12
+ MAX_EFFECTIVE_BALANCE,
13
+ MAX_EFFECTIVE_BALANCE_ELECTRA,
14
+ MIN_SEED_LOOKAHEAD,
15
+ SHUFFLE_ROUND_COUNT,
16
+ SLOTS_PER_EPOCH,
17
+ SYNC_COMMITTEE_SIZE,
18
+ } from "@lodestar/params";
19
+ import {Bytes32, DomainType, Epoch, ValidatorIndex} from "@lodestar/types";
20
+ import {assert, bytesToBigInt, bytesToInt, intToBytes} from "@lodestar/utils";
21
+ import {EffectiveBalanceIncrements} from "../cache/effectiveBalanceIncrements.js";
22
+ import {BeaconStateAllForks, CachedBeaconStateAllForks} from "../types.js";
23
+ import {computeEpochAtSlot, computeStartSlotAtEpoch} from "./epoch.js";
24
+
25
+ /**
26
+ * Compute proposer indices for an epoch
27
+ */
28
+ export function computeProposers(
29
+ fork: ForkSeq,
30
+ epochSeed: Uint8Array,
31
+ shuffling: {epoch: Epoch; activeIndices: Uint32Array},
32
+ effectiveBalanceIncrements: EffectiveBalanceIncrements
33
+ ): number[] {
34
+ const startSlot = computeStartSlotAtEpoch(shuffling.epoch);
35
+ const proposers = [];
36
+ for (let slot = startSlot; slot < startSlot + SLOTS_PER_EPOCH; slot++) {
37
+ proposers.push(
38
+ computeProposerIndex(
39
+ fork,
40
+ effectiveBalanceIncrements,
41
+ shuffling.activeIndices,
42
+ // TODO: if we use hashTree, we can precompute the roots for the next n loops
43
+ digest(Buffer.concat([epochSeed, intToBytes(slot, 8)]))
44
+ )
45
+ );
46
+ }
47
+ return proposers;
48
+ }
49
+
50
+ /**
51
+ * Return from ``indices`` a random index sampled by effective balance.
52
+ * This is just to make sure lodestar follows the spec, this is not for production.
53
+ *
54
+ * SLOW CODE - 🐢
55
+ */
56
+ export function naiveComputeProposerIndex(
57
+ fork: ForkSeq,
58
+ effectiveBalanceIncrements: EffectiveBalanceIncrements,
59
+ indices: ArrayLike<ValidatorIndex>,
60
+ seed: Uint8Array
61
+ ): ValidatorIndex {
62
+ if (indices.length === 0) {
63
+ throw Error("Validator indices must not be empty");
64
+ }
65
+
66
+ if (fork >= ForkSeq.electra) {
67
+ const MAX_RANDOM_VALUE = 2 ** 16 - 1;
68
+ const MAX_EFFECTIVE_BALANCE_INCREMENT = MAX_EFFECTIVE_BALANCE_ELECTRA / EFFECTIVE_BALANCE_INCREMENT;
69
+
70
+ let i = 0;
71
+ while (true) {
72
+ const candidateIndex = indices[computeShuffledIndex(i % indices.length, indices.length, seed)];
73
+ const randomBytes = digest(Buffer.concat([seed, intToBytes(Math.floor(i / 16), 8, "le")]));
74
+ const offset = (i % 16) * 2;
75
+ const randomValue = bytesToInt(randomBytes.subarray(offset, offset + 2));
76
+
77
+ const effectiveBalanceIncrement = effectiveBalanceIncrements[candidateIndex];
78
+ if (effectiveBalanceIncrement * MAX_RANDOM_VALUE >= MAX_EFFECTIVE_BALANCE_INCREMENT * randomValue) {
79
+ return candidateIndex;
80
+ }
81
+
82
+ i += 1;
83
+ }
84
+ } else {
85
+ const MAX_RANDOM_BYTE = 2 ** 8 - 1;
86
+ const MAX_EFFECTIVE_BALANCE_INCREMENT = MAX_EFFECTIVE_BALANCE / EFFECTIVE_BALANCE_INCREMENT;
87
+
88
+ let i = 0;
89
+ while (true) {
90
+ const candidateIndex = indices[computeShuffledIndex(i % indices.length, indices.length, seed)];
91
+ const randomByte = digest(Buffer.concat([seed, intToBytes(Math.floor(i / 32), 8, "le")]))[i % 32];
92
+
93
+ const effectiveBalanceIncrement = effectiveBalanceIncrements[candidateIndex];
94
+ if (effectiveBalanceIncrement * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE_INCREMENT * randomByte) {
95
+ return candidateIndex;
96
+ }
97
+
98
+ i += 1;
99
+ }
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Optimized version of `naiveComputeProposerIndex`.
105
+ * It shows > 3x speedup according to the perf test.
106
+ */
107
+ export function computeProposerIndex(
108
+ fork: ForkSeq,
109
+ effectiveBalanceIncrements: EffectiveBalanceIncrements,
110
+ indices: Uint32Array,
111
+ seed: Uint8Array
112
+ ): ValidatorIndex {
113
+ if (indices.length === 0) {
114
+ throw Error("Validator indices must not be empty");
115
+ }
116
+
117
+ let maxEffectiveBalance: number;
118
+ let randByteCount: number;
119
+ if (fork >= ForkSeq.electra) {
120
+ maxEffectiveBalance = MAX_EFFECTIVE_BALANCE_ELECTRA;
121
+ randByteCount = 2;
122
+ } else {
123
+ maxEffectiveBalance = MAX_EFFECTIVE_BALANCE;
124
+ randByteCount = 1;
125
+ }
126
+
127
+ return nativeComputeProposerIndex(
128
+ seed,
129
+ indices,
130
+ effectiveBalanceIncrements,
131
+ randByteCount,
132
+ maxEffectiveBalance,
133
+ EFFECTIVE_BALANCE_INCREMENT,
134
+ SHUFFLE_ROUND_COUNT
135
+ );
136
+ }
137
+
138
+ /**
139
+ * Return the proposer indices for the given `epoch`.
140
+ * A more generic version of `computeProposers`
141
+ */
142
+ export function computeProposerIndices(
143
+ fork: ForkSeq,
144
+ state: CachedBeaconStateAllForks,
145
+ shuffling: {activeIndices: Uint32Array},
146
+ epoch: Epoch
147
+ ): ValidatorIndex[] {
148
+ const startSlot = computeStartSlotAtEpoch(epoch);
149
+ const proposers = [];
150
+ const epochSeed = getSeed(state, epoch, DOMAIN_BEACON_PROPOSER);
151
+
152
+ for (let slot = startSlot; slot < startSlot + SLOTS_PER_EPOCH; slot++) {
153
+ proposers.push(
154
+ computeProposerIndex(
155
+ fork,
156
+ state.epochCtx.effectiveBalanceIncrements,
157
+ shuffling.activeIndices,
158
+ digest(Buffer.concat([epochSeed, intToBytes(slot, 8)]))
159
+ )
160
+ );
161
+ }
162
+ return proposers;
163
+ }
164
+
165
+ /**
166
+ * Naive version, this is not supposed to be used in production.
167
+ * See `computeProposerIndex` for the optimized version.
168
+ *
169
+ * Return the sync committee indices for a given state and epoch.
170
+ * Aligns `epoch` to `baseEpoch` so the result is the same with any `epoch` within a sync period.
171
+ * Note: This function should only be called at sync committee period boundaries, as
172
+ * ``get_sync_committee_indices`` is not stable within a given period.
173
+ *
174
+ * SLOW CODE - 🐢
175
+ */
176
+ export function naiveGetNextSyncCommitteeIndices(
177
+ fork: ForkSeq,
178
+ state: BeaconStateAllForks,
179
+ activeValidatorIndices: ArrayLike<ValidatorIndex>,
180
+ effectiveBalanceIncrements: EffectiveBalanceIncrements
181
+ ): ValidatorIndex[] {
182
+ const syncCommitteeValidatorIndices = [];
183
+
184
+ if (fork >= ForkSeq.electra) {
185
+ const MAX_RANDOM_VALUE = 2 ** 16 - 1;
186
+ const MAX_EFFECTIVE_BALANCE_INCREMENT = MAX_EFFECTIVE_BALANCE_ELECTRA / EFFECTIVE_BALANCE_INCREMENT;
187
+
188
+ const epoch = computeEpochAtSlot(state.slot) + 1;
189
+ const activeValidatorCount = activeValidatorIndices.length;
190
+ const seed = getSeed(state, epoch, DOMAIN_SYNC_COMMITTEE);
191
+
192
+ let i = 0;
193
+ while (syncCommitteeValidatorIndices.length < SYNC_COMMITTEE_SIZE) {
194
+ const shuffledIndex = computeShuffledIndex(i % activeValidatorCount, activeValidatorCount, seed);
195
+ const candidateIndex = activeValidatorIndices[shuffledIndex];
196
+ const randomBytes = digest(Buffer.concat([seed, intToBytes(Math.floor(i / 16), 8, "le")]));
197
+ const offset = (i % 16) * 2;
198
+ const randomValue = bytesToInt(randomBytes.subarray(offset, offset + 2));
199
+
200
+ const effectiveBalanceIncrement = effectiveBalanceIncrements[candidateIndex];
201
+ if (effectiveBalanceIncrement * MAX_RANDOM_VALUE >= MAX_EFFECTIVE_BALANCE_INCREMENT * randomValue) {
202
+ syncCommitteeValidatorIndices.push(candidateIndex);
203
+ }
204
+
205
+ i += 1;
206
+ }
207
+ } else {
208
+ const MAX_RANDOM_BYTE = 2 ** 8 - 1;
209
+ const MAX_EFFECTIVE_BALANCE_INCREMENT = MAX_EFFECTIVE_BALANCE / EFFECTIVE_BALANCE_INCREMENT;
210
+
211
+ const epoch = computeEpochAtSlot(state.slot) + 1;
212
+ const activeValidatorCount = activeValidatorIndices.length;
213
+ const seed = getSeed(state, epoch, DOMAIN_SYNC_COMMITTEE);
214
+
215
+ let i = 0;
216
+ while (syncCommitteeValidatorIndices.length < SYNC_COMMITTEE_SIZE) {
217
+ const shuffledIndex = computeShuffledIndex(i % activeValidatorCount, activeValidatorCount, seed);
218
+ const candidateIndex = activeValidatorIndices[shuffledIndex];
219
+ const randomByte = digest(Buffer.concat([seed, intToBytes(Math.floor(i / 32), 8, "le")]))[i % 32];
220
+
221
+ const effectiveBalanceIncrement = effectiveBalanceIncrements[candidateIndex];
222
+ if (effectiveBalanceIncrement * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE_INCREMENT * randomByte) {
223
+ syncCommitteeValidatorIndices.push(candidateIndex);
224
+ }
225
+
226
+ i += 1;
227
+ }
228
+ }
229
+
230
+ return syncCommitteeValidatorIndices;
231
+ }
232
+
233
+ /**
234
+ * Optmized version of `naiveGetNextSyncCommitteeIndices`.
235
+ *
236
+ * In the worse case scenario, this could be >1000x speedup according to the perf test.
237
+ */
238
+ export function getNextSyncCommitteeIndices(
239
+ fork: ForkSeq,
240
+ state: BeaconStateAllForks,
241
+ activeValidatorIndices: Uint32Array,
242
+ effectiveBalanceIncrements: EffectiveBalanceIncrements
243
+ ): Uint32Array {
244
+ let maxEffectiveBalance: number;
245
+ let randByteCount: number;
246
+
247
+ if (fork >= ForkSeq.electra) {
248
+ maxEffectiveBalance = MAX_EFFECTIVE_BALANCE_ELECTRA;
249
+ randByteCount = 2;
250
+ } else {
251
+ maxEffectiveBalance = MAX_EFFECTIVE_BALANCE;
252
+ randByteCount = 1;
253
+ }
254
+
255
+ const epoch = computeEpochAtSlot(state.slot) + 1;
256
+ const seed = getSeed(state, epoch, DOMAIN_SYNC_COMMITTEE);
257
+ return nativeComputeSyncCommitteeIndices(
258
+ seed,
259
+ activeValidatorIndices,
260
+ effectiveBalanceIncrements,
261
+ randByteCount,
262
+ SYNC_COMMITTEE_SIZE,
263
+ maxEffectiveBalance,
264
+ EFFECTIVE_BALANCE_INCREMENT,
265
+ SHUFFLE_ROUND_COUNT
266
+ );
267
+ }
268
+
269
+ /**
270
+ * Return the shuffled validator index corresponding to ``seed`` (and ``index_count``).
271
+ *
272
+ * Swap or not
273
+ * https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf
274
+ *
275
+ * See the 'generalized domain' algorithm on page 3.
276
+ * This is the naive implementation just to make sure lodestar follows the spec, this is not for production.
277
+ * The optimized version is in `getComputeShuffledIndexFn`.
278
+ */
279
+ export function computeShuffledIndex(index: number, indexCount: number, seed: Bytes32): number {
280
+ let permuted = index;
281
+ assert.lt(index, indexCount, "indexCount must be less than index");
282
+ assert.lte(indexCount, 2 ** 40, "indexCount too big");
283
+ const _seed = seed;
284
+ for (let i = 0; i < SHUFFLE_ROUND_COUNT; i++) {
285
+ const pivot = Number(
286
+ bytesToBigInt(digest(Buffer.concat([_seed, intToBytes(i, 1)])).slice(0, 8)) % BigInt(indexCount)
287
+ );
288
+ const flip = (pivot + indexCount - permuted) % indexCount;
289
+ const position = Math.max(permuted, flip);
290
+ const source = digest(Buffer.concat([_seed, intToBytes(i, 1), intToBytes(Math.floor(position / 256), 4)]));
291
+ const byte = source[Math.floor((position % 256) / 8)];
292
+ const bit = (byte >> (position % 8)) % 2;
293
+ permuted = bit ? flip : permuted;
294
+ }
295
+ return permuted;
296
+ }
297
+
298
+ type ComputeShuffledIndexFn = (index: number) => number;
299
+
300
+ /**
301
+ * An optimized version of `computeShuffledIndex`, this is for production.
302
+ */
303
+ export function getComputeShuffledIndexFn(indexCount: number, seed: Bytes32): ComputeShuffledIndexFn {
304
+ // there are possibly SHUFFLE_ROUND_COUNT (90 for mainnet) values for this cache
305
+ // this cache will always hit after the 1st call
306
+ const pivotByIndex: Map<number, number> = new Map();
307
+ // given 2M active validators, there are 2 M / 256 = 8k possible positionDiv
308
+ // it means there are at most 8k different sources for each round
309
+ const sourceByPositionDivByIndex: Map<number, Map<number, Uint8Array>> = new Map();
310
+ // 32 bytes seed + 1 byte i
311
+ const pivotBuffer = Buffer.alloc(32 + 1);
312
+ pivotBuffer.set(seed, 0);
313
+ // 32 bytes seed + 1 byte i + 4 bytes positionDiv
314
+ const sourceBuffer = Buffer.alloc(32 + 1 + 4);
315
+ sourceBuffer.set(seed, 0);
316
+
317
+ return (index): number => {
318
+ assert.lt(index, indexCount, "indexCount must be less than index");
319
+ assert.lte(indexCount, 2 ** 40, "indexCount too big");
320
+ let permuted = index;
321
+ // const _seed = seed;
322
+ for (let i = 0; i < SHUFFLE_ROUND_COUNT; i++) {
323
+ // optimized version of the below naive code
324
+ // const pivot = Number(
325
+ // bytesToBigInt(digest(Buffer.concat([_seed, intToBytes(i, 1)])).slice(0, 8)) % BigInt(indexCount)
326
+ // );
327
+
328
+ let pivot = pivotByIndex.get(i);
329
+ if (pivot == null) {
330
+ // naive version always creates a new buffer, we can reuse the buffer
331
+ // pivot = Number(
332
+ // bytesToBigInt(digest(Buffer.concat([_seed, intToBytes(i, 1)])).slice(0, 8)) % BigInt(indexCount)
333
+ // );
334
+ pivotBuffer[32] = i % 256;
335
+ pivot = Number(bytesToBigInt(digest(pivotBuffer).subarray(0, 8)) % BigInt(indexCount));
336
+ pivotByIndex.set(i, pivot);
337
+ }
338
+
339
+ const flip = (pivot + indexCount - permuted) % indexCount;
340
+ const position = Math.max(permuted, flip);
341
+
342
+ // optimized version of the below naive code
343
+ // const source = digest(Buffer.concat([_seed, intToBytes(i, 1), intToBytes(Math.floor(position / 256), 4)]));
344
+ let sourceByPositionDiv = sourceByPositionDivByIndex.get(i);
345
+ if (sourceByPositionDiv == null) {
346
+ sourceByPositionDiv = new Map<number, Uint8Array>();
347
+ sourceByPositionDivByIndex.set(i, sourceByPositionDiv);
348
+ }
349
+ const positionDiv256 = Math.floor(position / 256);
350
+ let source = sourceByPositionDiv.get(positionDiv256);
351
+ if (source == null) {
352
+ // naive version always creates a new buffer, we can reuse the buffer
353
+ // don't want to go through intToBytes() to avoid BigInt
354
+ sourceBuffer[32] = i % 256;
355
+ sourceBuffer.writeUint32LE(positionDiv256, 33);
356
+ source = digest(sourceBuffer);
357
+ sourceByPositionDiv.set(positionDiv256, source);
358
+ }
359
+ const byte = source[Math.floor((position % 256) / 8)];
360
+ const bit = (byte >> (position % 8)) % 2;
361
+ permuted = bit ? flip : permuted;
362
+ }
363
+ return permuted;
364
+ };
365
+ }
366
+
367
+ /**
368
+ * Return the randao mix at a recent [[epoch]].
369
+ */
370
+ export function getRandaoMix(state: BeaconStateAllForks, epoch: Epoch): Bytes32 {
371
+ return state.randaoMixes.get(epoch % EPOCHS_PER_HISTORICAL_VECTOR);
372
+ }
373
+
374
+ /**
375
+ * Return the seed at [[epoch]].
376
+ */
377
+ export function getSeed(state: BeaconStateAllForks, epoch: Epoch, domainType: DomainType): Uint8Array {
378
+ const mix = getRandaoMix(state, epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD - 1);
379
+
380
+ return digest(Buffer.concat([domainType as Buffer, intToBytes(epoch, 8), mix]));
381
+ }
@@ -0,0 +1,78 @@
1
+ import {Epoch, Root, Slot} from "@lodestar/types";
2
+ import {CachedBeaconStateAllForks} from "../types.js";
3
+ import {getBlockRootAtSlot} from "./blockRoot.js";
4
+ import {computeStartSlotAtEpoch} from "./epoch.js";
5
+
6
+ /**
7
+ * Returns the block root which decided the proposer shuffling for the current epoch. This root
8
+ * can be used to key this proposer shuffling.
9
+ *
10
+ * Returns `null` on the one-off scenario where the genesis block decides its own shuffling.
11
+ * It should be set to the latest block applied to this `state` or the genesis block root.
12
+ */
13
+ export function proposerShufflingDecisionRoot(state: CachedBeaconStateAllForks): Root | null {
14
+ const decisionSlot = proposerShufflingDecisionSlot(state);
15
+ if (state.slot === decisionSlot) {
16
+ return null;
17
+ }
18
+ return getBlockRootAtSlot(state, decisionSlot);
19
+ }
20
+
21
+ /**
22
+ * Returns the slot at which the proposer shuffling was decided. The block root at this slot
23
+ * can be used to key the proposer shuffling for the current epoch.
24
+ */
25
+ function proposerShufflingDecisionSlot(state: CachedBeaconStateAllForks): Slot {
26
+ const startSlot = computeStartSlotAtEpoch(state.epochCtx.epoch);
27
+ return Math.max(startSlot - 1, 0);
28
+ }
29
+
30
+ /**
31
+ * Returns the block root which decided the attester shuffling for the given `requestedEpoch`.
32
+ * This root can be used to key that attester shuffling.
33
+ *
34
+ * Returns `null` on the one-off scenario where the genesis block decides its own shuffling.
35
+ * It should be set to the latest block applied to this `state` or the genesis block root.
36
+ */
37
+ export function attesterShufflingDecisionRoot(state: CachedBeaconStateAllForks, requestedEpoch: Epoch): Root | null {
38
+ const decisionSlot = attesterShufflingDecisionSlot(state, requestedEpoch);
39
+ if (state.slot === decisionSlot) {
40
+ return null;
41
+ }
42
+ return getBlockRootAtSlot(state, decisionSlot);
43
+ }
44
+
45
+ /**
46
+ * Returns the slot at which the proposer shuffling was decided. The block root at this slot
47
+ * can be used to key the proposer shuffling for the current epoch.
48
+ */
49
+ function attesterShufflingDecisionSlot(state: CachedBeaconStateAllForks, requestedEpoch: Epoch): Slot {
50
+ const epoch = attesterShufflingDecisionEpoch(state, requestedEpoch);
51
+ const slot = computeStartSlotAtEpoch(epoch);
52
+ return Math.max(slot - 1, 0);
53
+ }
54
+
55
+ /**
56
+ * Returns the epoch at which the attester shuffling was decided.
57
+ *
58
+ * Spec ref: https://github.com/ethereum/beacon-APIs/blob/v2.1.0/apis/validator/duties/attester.yaml#L15
59
+ *
60
+ * Throws an error when:
61
+ * - `EpochTooLow` when `requestedEpoch` is more than 1 prior to `currentEpoch`.
62
+ * - `EpochTooHigh` when `requestedEpoch` is more than 1 after `currentEpoch`.
63
+ */
64
+ function attesterShufflingDecisionEpoch(state: CachedBeaconStateAllForks, requestedEpoch: Epoch): Epoch {
65
+ const currentEpoch = state.epochCtx.epoch;
66
+
67
+ // Next
68
+ if (requestedEpoch === currentEpoch + 1) return currentEpoch;
69
+ // Current
70
+ if (requestedEpoch === currentEpoch) return Math.max(currentEpoch - 1, 0);
71
+ // Previous
72
+ if (requestedEpoch === currentEpoch - 1) return Math.max(currentEpoch - 2, 0);
73
+
74
+ if (requestedEpoch < currentEpoch) {
75
+ throw Error(`EpochTooLow: current ${currentEpoch} requested ${requestedEpoch}`);
76
+ }
77
+ throw Error(`EpochTooHigh: current ${currentEpoch} requested ${requestedEpoch}`);
78
+ }
@@ -0,0 +1,65 @@
1
+ import {PublicKey, Signature, fastAggregateVerify, verify} from "@chainsafe/blst";
2
+ import {Root} from "@lodestar/types";
3
+
4
+ export enum SignatureSetType {
5
+ single = "single",
6
+ aggregate = "aggregate",
7
+ }
8
+
9
+ export type SingleSignatureSet = {
10
+ type: SignatureSetType.single;
11
+ pubkey: PublicKey;
12
+ signingRoot: Root;
13
+ signature: Uint8Array;
14
+ };
15
+
16
+ export type AggregatedSignatureSet = {
17
+ type: SignatureSetType.aggregate;
18
+ pubkeys: PublicKey[];
19
+ signingRoot: Root;
20
+ signature: Uint8Array;
21
+ };
22
+
23
+ export type ISignatureSet = SingleSignatureSet | AggregatedSignatureSet;
24
+
25
+ export function verifySignatureSet(signatureSet: ISignatureSet): boolean {
26
+ // All signatures are not trusted and must be group checked (p2.subgroup_check)
27
+ const signature = Signature.fromBytes(signatureSet.signature, true);
28
+
29
+ switch (signatureSet.type) {
30
+ case SignatureSetType.single:
31
+ return verify(signatureSet.signingRoot, signatureSet.pubkey, signature);
32
+
33
+ case SignatureSetType.aggregate:
34
+ return fastAggregateVerify(signatureSet.signingRoot, signatureSet.pubkeys, signature);
35
+
36
+ default:
37
+ throw Error("Unknown signature set type");
38
+ }
39
+ }
40
+
41
+ export function createSingleSignatureSetFromComponents(
42
+ pubkey: PublicKey,
43
+ signingRoot: Root,
44
+ signature: Uint8Array
45
+ ): SingleSignatureSet {
46
+ return {
47
+ type: SignatureSetType.single,
48
+ pubkey,
49
+ signingRoot,
50
+ signature,
51
+ };
52
+ }
53
+
54
+ export function createAggregateSignatureSetFromComponents(
55
+ pubkeys: PublicKey[],
56
+ signingRoot: Root,
57
+ signature: Uint8Array
58
+ ): AggregatedSignatureSet {
59
+ return {
60
+ type: SignatureSetType.aggregate,
61
+ pubkeys,
62
+ signingRoot,
63
+ signature,
64
+ };
65
+ }
@@ -0,0 +1,13 @@
1
+ import {Type} from "@chainsafe/ssz";
2
+ import {Domain, phase0, ssz} from "@lodestar/types";
3
+
4
+ /**
5
+ * Return the signing root of an object by calculating the root of the object-domain tree.
6
+ */
7
+ export function computeSigningRoot<T>(type: Type<T>, sszObject: T, domain: Domain): Uint8Array {
8
+ const domainWrappedObject: phase0.SigningData = {
9
+ objectRoot: type.hashTreeRoot(sszObject),
10
+ domain,
11
+ };
12
+ return ssz.phase0.SigningData.hashTreeRoot(domainWrappedObject);
13
+ }
@@ -0,0 +1,22 @@
1
+ import {ChainConfig} from "@lodestar/config";
2
+ import {GENESIS_SLOT} from "@lodestar/params";
3
+ import {Epoch, Slot, TimeSeconds} from "@lodestar/types";
4
+ import {computeEpochAtSlot, computeStartSlotAtEpoch} from "./epoch.js";
5
+
6
+ export function getSlotsSinceGenesis(config: ChainConfig, genesisTime: TimeSeconds): Slot {
7
+ const diffInSeconds = Date.now() / 1000 - genesisTime;
8
+ return Math.floor(diffInSeconds / (config.SLOT_DURATION_MS / 1000));
9
+ }
10
+
11
+ export function getCurrentSlot(config: ChainConfig, genesisTime: TimeSeconds): Slot {
12
+ return GENESIS_SLOT + getSlotsSinceGenesis(config, genesisTime);
13
+ }
14
+
15
+ export function computeSlotsSinceEpochStart(slot: Slot, epoch?: Epoch): Slot {
16
+ const computeEpoch = epoch ?? computeEpochAtSlot(slot);
17
+ return slot - computeStartSlotAtEpoch(computeEpoch);
18
+ }
19
+
20
+ export function computeTimeAtSlot(config: ChainConfig, slot: Slot, genesisTime: TimeSeconds): TimeSeconds {
21
+ return genesisTime + slot * (config.SLOT_DURATION_MS / 1000);
22
+ }
@@ -0,0 +1,52 @@
1
+ import {ChainForkConfig} from "@lodestar/config";
2
+ import {ForkAll, ForkSeq} from "@lodestar/params";
3
+ import {SSZTypesFor, Slot} from "@lodestar/types";
4
+ import {bytesToInt} from "@lodestar/utils";
5
+
6
+ /**
7
+ * Slot uint64
8
+ */
9
+ const SLOT_BYTE_COUNT = 8;
10
+
11
+ /**
12
+ * 48 + 32 + 8 + 1 + 8 + 8 + 8 + 8 = 121
13
+ * ```
14
+ * class Validator(Container):
15
+ pubkey: BLSPubkey [fixed - 48 bytes]
16
+ withdrawal_credentials: Bytes32 [fixed - 32 bytes]
17
+ effective_balance: Gwei [fixed - 8 bytes]
18
+ slashed: boolean [fixed - 1 byte]
19
+ # Status epochs
20
+ activation_eligibility_epoch: Epoch [fixed - 8 bytes]
21
+ activation_epoch: Epoch [fixed - 8 bytes]
22
+ exit_epoch: Epoch [fixed - 8 bytes]
23
+ withdrawable_epoch: Epoch [fixed - 8 bytes]
24
+ ```
25
+ */
26
+ export const VALIDATOR_BYTES_SIZE = 121;
27
+
28
+ /**
29
+ * 8 + 32 = 40
30
+ * ```
31
+ * class BeaconState(Container):
32
+ * genesis_time: uint64 [fixed - 8 bytes]
33
+ * genesis_validators_root: Root [fixed - 32 bytes]
34
+ * slot: Slot [fixed - 8 bytes]
35
+ * ...
36
+ * ```
37
+ */
38
+ const SLOT_BYTES_POSITION_IN_STATE = 40;
39
+
40
+ export function getForkFromStateBytes(config: ChainForkConfig, bytes: Uint8Array): ForkSeq {
41
+ const slot = bytesToInt(bytes.subarray(SLOT_BYTES_POSITION_IN_STATE, SLOT_BYTES_POSITION_IN_STATE + SLOT_BYTE_COUNT));
42
+ return config.getForkSeq(slot);
43
+ }
44
+
45
+ export function getStateTypeFromBytes(config: ChainForkConfig, bytes: Uint8Array): SSZTypesFor<ForkAll, "BeaconState"> {
46
+ const slot = getStateSlotFromBytes(bytes);
47
+ return config.getForkTypes(slot).BeaconState;
48
+ }
49
+
50
+ export function getStateSlotFromBytes(bytes: Uint8Array): Slot {
51
+ return bytesToInt(bytes.subarray(SLOT_BYTES_POSITION_IN_STATE, SLOT_BYTES_POSITION_IN_STATE + SLOT_BYTE_COUNT));
52
+ }
@@ -0,0 +1,69 @@
1
+ import {aggregateSerializedPublicKeys} from "@chainsafe/blst";
2
+ import {
3
+ BASE_REWARD_FACTOR,
4
+ EFFECTIVE_BALANCE_INCREMENT,
5
+ ForkSeq,
6
+ SLOTS_PER_EPOCH,
7
+ SYNC_COMMITTEE_SIZE,
8
+ SYNC_REWARD_WEIGHT,
9
+ WEIGHT_DENOMINATOR,
10
+ } from "@lodestar/params";
11
+ import {altair} from "@lodestar/types";
12
+ import {bigIntSqrt} from "@lodestar/utils";
13
+ import {EffectiveBalanceIncrements} from "../cache/effectiveBalanceIncrements.js";
14
+ import {BeaconStateAllForks} from "../types.js";
15
+ import {getNextSyncCommitteeIndices} from "./seed.js";
16
+
17
+ /**
18
+ * Return the sync committee for a given state and epoch.
19
+ *
20
+ * SLOW CODE - 🐢
21
+ */
22
+ export function getNextSyncCommittee(
23
+ fork: ForkSeq,
24
+ state: BeaconStateAllForks,
25
+ activeValidatorIndices: Uint32Array,
26
+ effectiveBalanceIncrements: EffectiveBalanceIncrements
27
+ ): {indices: Uint32Array; syncCommittee: altair.SyncCommittee} {
28
+ const indices = getNextSyncCommitteeIndices(fork, state, activeValidatorIndices, effectiveBalanceIncrements);
29
+
30
+ // Using the index2pubkey cache is slower because it needs the serialized pubkey.
31
+ const pubkeys = [];
32
+ for (const index of indices) {
33
+ pubkeys.push(state.validators.getReadonly(index).pubkey);
34
+ }
35
+
36
+ return {
37
+ indices,
38
+ syncCommittee: {
39
+ pubkeys,
40
+ aggregatePubkey: aggregateSerializedPublicKeys(pubkeys).toBytes(),
41
+ },
42
+ };
43
+ }
44
+
45
+ /**
46
+ * Same logic in https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.5/specs/altair/beacon-chain.md#sync-committee-processing
47
+ */
48
+ export function computeSyncParticipantReward(totalActiveBalanceIncrements: number): number {
49
+ const totalActiveBalance = BigInt(totalActiveBalanceIncrements) * BigInt(EFFECTIVE_BALANCE_INCREMENT);
50
+ const baseRewardPerIncrement = Math.floor(
51
+ (EFFECTIVE_BALANCE_INCREMENT * BASE_REWARD_FACTOR) / Number(bigIntSqrt(totalActiveBalance))
52
+ );
53
+ const totalBaseRewards = baseRewardPerIncrement * totalActiveBalanceIncrements;
54
+ const maxParticipantRewards = Math.floor(
55
+ Math.floor((totalBaseRewards * SYNC_REWARD_WEIGHT) / WEIGHT_DENOMINATOR) / SLOTS_PER_EPOCH
56
+ );
57
+ return Math.floor(maxParticipantRewards / SYNC_COMMITTEE_SIZE);
58
+ }
59
+
60
+ /**
61
+ * Before we manage bigIntSqrt(totalActiveStake) as BigInt and return BigInt.
62
+ * bigIntSqrt(totalActiveStake) should fit a number (2 ** 53 -1 max)
63
+ **/
64
+ export function computeBaseRewardPerIncrement(totalActiveStakeByIncrement: number): number {
65
+ return Math.floor(
66
+ (EFFECTIVE_BALANCE_INCREMENT * BASE_REWARD_FACTOR) /
67
+ Number(bigIntSqrt(BigInt(totalActiveStakeByIncrement) * BigInt(EFFECTIVE_BALANCE_INCREMENT)))
68
+ );
69
+ }