@lodestar/state-transition 1.46.0-dev.1cd9a671ea → 1.46.0-dev.1dde9abaa6

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 (292) hide show
  1. package/lib/block/externalData.js +2 -4
  2. package/lib/block/externalData.js.map +1 -1
  3. package/lib/block/index.d.ts.map +1 -1
  4. package/lib/block/index.js +3 -2
  5. package/lib/block/index.js.map +1 -1
  6. package/lib/block/initiateValidatorExit.js.map +1 -1
  7. package/lib/block/isValidIndexedAttestation.js.map +1 -1
  8. package/lib/block/isValidIndexedPayloadAttestation.js.map +1 -1
  9. package/lib/block/processAttestationPhase0.js.map +1 -1
  10. package/lib/block/processAttestations.d.ts +2 -2
  11. package/lib/block/processAttestations.d.ts.map +1 -1
  12. package/lib/block/processAttestations.js +2 -2
  13. package/lib/block/processAttestations.js.map +1 -1
  14. package/lib/block/processAttestationsAltair.d.ts +3 -3
  15. package/lib/block/processAttestationsAltair.d.ts.map +1 -1
  16. package/lib/block/processAttestationsAltair.js +7 -4
  17. package/lib/block/processAttestationsAltair.js.map +1 -1
  18. package/lib/block/processAttesterSlashing.js.map +1 -1
  19. package/lib/block/processBlobKzgCommitments.js.map +1 -1
  20. package/lib/block/processBlockHeader.js.map +1 -1
  21. package/lib/block/processBlsToExecutionChange.js.map +1 -1
  22. package/lib/block/processBuilderDepositRequest.js.map +1 -1
  23. package/lib/block/processBuilderExitRequest.js.map +1 -1
  24. package/lib/block/processConsolidationRequest.js.map +1 -1
  25. package/lib/block/processDeposit.d.ts +15 -0
  26. package/lib/block/processDeposit.d.ts.map +1 -1
  27. package/lib/block/processDeposit.js +73 -1
  28. package/lib/block/processDeposit.js.map +1 -1
  29. package/lib/block/processDepositRequest.js.map +1 -1
  30. package/lib/block/processEth1Data.js.map +1 -1
  31. package/lib/block/processExecutionPayload.js.map +1 -1
  32. package/lib/block/processExecutionPayloadBid.d.ts +3 -3
  33. package/lib/block/processExecutionPayloadBid.d.ts.map +1 -1
  34. package/lib/block/processExecutionPayloadBid.js +10 -3
  35. package/lib/block/processExecutionPayloadBid.js.map +1 -1
  36. package/lib/block/processOperations.d.ts +2 -2
  37. package/lib/block/processOperations.d.ts.map +1 -1
  38. package/lib/block/processOperations.js +2 -2
  39. package/lib/block/processOperations.js.map +1 -1
  40. package/lib/block/processParentExecutionPayload.js.map +1 -1
  41. package/lib/block/processPayloadAttestation.js.map +1 -1
  42. package/lib/block/processProposerSlashing.js.map +1 -1
  43. package/lib/block/processRandao.js.map +1 -1
  44. package/lib/block/processSyncCommittee.js.map +1 -1
  45. package/lib/block/processVoluntaryExit.js +1 -2
  46. package/lib/block/processVoluntaryExit.js.map +1 -1
  47. package/lib/block/processWithdrawalRequest.d.ts.map +1 -1
  48. package/lib/block/processWithdrawalRequest.js +2 -2
  49. package/lib/block/processWithdrawalRequest.js.map +1 -1
  50. package/lib/block/processWithdrawals.js.map +1 -1
  51. package/lib/block/slashValidator.js.map +1 -1
  52. package/lib/block/types.js +1 -2
  53. package/lib/block/types.js.map +1 -1
  54. package/lib/cache/builderDepositSignatureCache.d.ts +48 -0
  55. package/lib/cache/builderDepositSignatureCache.d.ts.map +1 -0
  56. package/lib/cache/builderDepositSignatureCache.js +70 -0
  57. package/lib/cache/builderDepositSignatureCache.js.map +1 -0
  58. package/lib/cache/effectiveBalanceIncrements.js.map +1 -1
  59. package/lib/cache/epochCache.d.ts +9 -0
  60. package/lib/cache/epochCache.d.ts.map +1 -1
  61. package/lib/cache/epochCache.js +32 -21
  62. package/lib/cache/epochCache.js.map +1 -1
  63. package/lib/cache/epochTransitionCache.js.map +1 -1
  64. package/lib/cache/pubkeyCache.js.map +1 -1
  65. package/lib/cache/rewardCache.js.map +1 -1
  66. package/lib/cache/stateCache.d.ts +2 -1
  67. package/lib/cache/stateCache.d.ts.map +1 -1
  68. package/lib/cache/stateCache.js +3 -2
  69. package/lib/cache/stateCache.js.map +1 -1
  70. package/lib/cache/syncCommitteeCache.js.map +1 -1
  71. package/lib/cache/types.d.ts +1 -0
  72. package/lib/cache/types.d.ts.map +1 -1
  73. package/lib/epoch/computeUnrealizedCheckpoints.js.map +1 -1
  74. package/lib/epoch/getAttestationDeltas.js.map +1 -1
  75. package/lib/epoch/getRewardsAndPenalties.js.map +1 -1
  76. package/lib/epoch/index.js +1 -2
  77. package/lib/epoch/index.js.map +1 -1
  78. package/lib/epoch/processBuilderPendingPayments.js.map +1 -1
  79. package/lib/epoch/processEffectiveBalanceUpdates.js.map +1 -1
  80. package/lib/epoch/processEth1DataReset.js.map +1 -1
  81. package/lib/epoch/processHistoricalRootsUpdate.js.map +1 -1
  82. package/lib/epoch/processHistoricalSummariesUpdate.js.map +1 -1
  83. package/lib/epoch/processInactivityUpdates.js.map +1 -1
  84. package/lib/epoch/processJustificationAndFinalization.js.map +1 -1
  85. package/lib/epoch/processParticipationFlagUpdates.js.map +1 -1
  86. package/lib/epoch/processParticipationRecordUpdates.js.map +1 -1
  87. package/lib/epoch/processPendingAttestations.js.map +1 -1
  88. package/lib/epoch/processPendingConsolidations.js.map +1 -1
  89. package/lib/epoch/processPendingDeposits.js.map +1 -1
  90. package/lib/epoch/processProposerLookahead.js.map +1 -1
  91. package/lib/epoch/processPtcWindow.js.map +1 -1
  92. package/lib/epoch/processRandaoMixesReset.js.map +1 -1
  93. package/lib/epoch/processRegistryUpdates.js.map +1 -1
  94. package/lib/epoch/processRewardsAndPenalties.js.map +1 -1
  95. package/lib/epoch/processSlashings.js.map +1 -1
  96. package/lib/epoch/processSlashingsReset.js.map +1 -1
  97. package/lib/epoch/processSyncCommitteeUpdates.js.map +1 -1
  98. package/lib/index.d.ts +2 -2
  99. package/lib/index.d.ts.map +1 -1
  100. package/lib/index.js +1 -1
  101. package/lib/index.js.map +1 -1
  102. package/lib/lightClient/proofs.js.map +1 -1
  103. package/lib/lightClient/spec/index.js.map +1 -1
  104. package/lib/lightClient/spec/isBetterUpdate.js.map +1 -1
  105. package/lib/lightClient/spec/processLightClientUpdate.js.map +1 -1
  106. package/lib/lightClient/spec/store.js.map +1 -1
  107. package/lib/lightClient/spec/utils.d.ts +0 -2
  108. package/lib/lightClient/spec/utils.d.ts.map +1 -1
  109. package/lib/lightClient/spec/utils.js +8 -2
  110. package/lib/lightClient/spec/utils.js.map +1 -1
  111. package/lib/lightClient/spec/validateLightClientBootstrap.js.map +1 -1
  112. package/lib/lightClient/spec/validateLightClientUpdate.js.map +1 -1
  113. package/lib/metrics.d.ts.map +1 -1
  114. package/lib/metrics.js +3 -2
  115. package/lib/metrics.js.map +1 -1
  116. package/lib/rewards/attestationsRewards.js.map +1 -1
  117. package/lib/rewards/blockRewards.d.ts.map +1 -1
  118. package/lib/rewards/blockRewards.js +7 -4
  119. package/lib/rewards/blockRewards.js.map +1 -1
  120. package/lib/rewards/syncCommitteeRewards.js.map +1 -1
  121. package/lib/signatureSets/attesterSlashings.js.map +1 -1
  122. package/lib/signatureSets/blsToExecutionChange.js.map +1 -1
  123. package/lib/signatureSets/executionPayloadBid.d.ts +2 -2
  124. package/lib/signatureSets/executionPayloadBid.d.ts.map +1 -1
  125. package/lib/signatureSets/executionPayloadBid.js +7 -5
  126. package/lib/signatureSets/executionPayloadBid.js.map +1 -1
  127. package/lib/signatureSets/executionPayloadEnvelope.js.map +1 -1
  128. package/lib/signatureSets/index.js.map +1 -1
  129. package/lib/signatureSets/indexedAttestation.js.map +1 -1
  130. package/lib/signatureSets/indexedPayloadAttestation.js.map +1 -1
  131. package/lib/signatureSets/proposer.js.map +1 -1
  132. package/lib/signatureSets/proposerPreferences.js.map +1 -1
  133. package/lib/signatureSets/proposerSlashings.js.map +1 -1
  134. package/lib/signatureSets/randao.js.map +1 -1
  135. package/lib/signatureSets/voluntaryExits.js.map +1 -1
  136. package/lib/slot/index.d.ts +1 -0
  137. package/lib/slot/index.d.ts.map +1 -1
  138. package/lib/slot/index.js +1 -0
  139. package/lib/slot/index.js.map +1 -1
  140. package/lib/slot/upgradeStateToAltair.js +1 -1
  141. package/lib/slot/upgradeStateToAltair.js.map +1 -1
  142. package/lib/slot/upgradeStateToBellatrix.js.map +1 -1
  143. package/lib/slot/upgradeStateToCapella.js.map +1 -1
  144. package/lib/slot/upgradeStateToDeneb.js.map +1 -1
  145. package/lib/slot/upgradeStateToElectra.js.map +1 -1
  146. package/lib/slot/upgradeStateToFulu.js.map +1 -1
  147. package/lib/slot/upgradeStateToGloas.js +14 -8
  148. package/lib/slot/upgradeStateToGloas.js.map +1 -1
  149. package/lib/slot/upgradeStateToHeze.d.ts +7 -0
  150. package/lib/slot/upgradeStateToHeze.d.ts.map +1 -0
  151. package/lib/slot/upgradeStateToHeze.js +83 -0
  152. package/lib/slot/upgradeStateToHeze.js.map +1 -0
  153. package/lib/stateTransition.d.ts.map +1 -1
  154. package/lib/stateTransition.js +6 -5
  155. package/lib/stateTransition.js.map +1 -1
  156. package/lib/stateView/beaconStateView.d.ts +3 -0
  157. package/lib/stateView/beaconStateView.d.ts.map +1 -1
  158. package/lib/stateView/beaconStateView.js +7 -0
  159. package/lib/stateView/beaconStateView.js.map +1 -1
  160. package/lib/stateView/interface.d.ts +16 -3
  161. package/lib/stateView/interface.d.ts.map +1 -1
  162. package/lib/stateView/interface.js +4 -1
  163. package/lib/stateView/interface.js.map +1 -1
  164. package/lib/stateView/nativeBeaconStateView.d.ts +3 -0
  165. package/lib/stateView/nativeBeaconStateView.d.ts.map +1 -1
  166. package/lib/stateView/nativeBeaconStateView.js +6 -0
  167. package/lib/stateView/nativeBeaconStateView.js.map +1 -1
  168. package/lib/stateView/stateViewFactory.js.map +1 -1
  169. package/lib/testUtils/infura.js.map +1 -1
  170. package/lib/testUtils/interop.js.map +1 -1
  171. package/lib/testUtils/params.d.ts.map +1 -1
  172. package/lib/testUtils/state.js +1 -1
  173. package/lib/testUtils/state.js.map +1 -1
  174. package/lib/testUtils/testFileCache.js.map +1 -1
  175. package/lib/testUtils/util.d.ts +17 -2
  176. package/lib/testUtils/util.d.ts.map +1 -1
  177. package/lib/testUtils/util.js +57 -4
  178. package/lib/testUtils/util.js.map +1 -1
  179. package/lib/types.d.ts +2 -2
  180. package/lib/types.d.ts.map +1 -1
  181. package/lib/util/aggregator.js.map +1 -1
  182. package/lib/util/altair.js.map +1 -1
  183. package/lib/util/array.js.map +1 -1
  184. package/lib/util/attestation.js.map +1 -1
  185. package/lib/util/attesterStatus.js.map +1 -1
  186. package/lib/util/balance.js.map +1 -1
  187. package/lib/util/blindedBlock.js.map +1 -1
  188. package/lib/util/blockRoot.js.map +1 -1
  189. package/lib/util/capella.js.map +1 -1
  190. package/lib/util/clock.d.ts +61 -0
  191. package/lib/util/clock.d.ts.map +1 -0
  192. package/lib/util/clock.js +141 -0
  193. package/lib/util/clock.js.map +1 -0
  194. package/lib/util/computeAnchorCheckpoint.js.map +1 -1
  195. package/lib/util/deposit.d.ts.map +1 -1
  196. package/lib/util/deposit.js +10 -8
  197. package/lib/util/deposit.js.map +1 -1
  198. package/lib/util/domain.js.map +1 -1
  199. package/lib/util/electra.js.map +1 -1
  200. package/lib/util/epoch.js.map +1 -1
  201. package/lib/util/epochShuffling.js.map +1 -1
  202. package/lib/util/execution.js.map +1 -1
  203. package/lib/util/finality.js.map +1 -1
  204. package/lib/util/fulu.js.map +1 -1
  205. package/lib/util/genesis.d.ts.map +1 -1
  206. package/lib/util/genesis.js +7 -2
  207. package/lib/util/genesis.js.map +1 -1
  208. package/lib/util/gloas.d.ts +2 -1
  209. package/lib/util/gloas.d.ts.map +1 -1
  210. package/lib/util/gloas.js +7 -4
  211. package/lib/util/gloas.js.map +1 -1
  212. package/lib/util/index.d.ts +2 -0
  213. package/lib/util/index.d.ts.map +1 -1
  214. package/lib/util/index.js +2 -0
  215. package/lib/util/index.js.map +1 -1
  216. package/lib/util/interop.js.map +1 -1
  217. package/lib/util/loadState/findModifiedInactivityScores.js.map +1 -1
  218. package/lib/util/loadState/findModifiedValidators.js.map +1 -1
  219. package/lib/util/loadState/loadState.d.ts.map +1 -1
  220. package/lib/util/loadState/loadState.js +36 -5
  221. package/lib/util/loadState/loadState.js.map +1 -1
  222. package/lib/util/loadState/loadValidator.js.map +1 -1
  223. package/lib/util/pendingDepositsLookup.d.ts +7 -10
  224. package/lib/util/pendingDepositsLookup.d.ts.map +1 -1
  225. package/lib/util/pendingDepositsLookup.js +10 -19
  226. package/lib/util/pendingDepositsLookup.js.map +1 -1
  227. package/lib/util/preVerifyBuilderDeposits.d.ts +52 -0
  228. package/lib/util/preVerifyBuilderDeposits.d.ts.map +1 -0
  229. package/lib/util/preVerifyBuilderDeposits.js +124 -0
  230. package/lib/util/preVerifyBuilderDeposits.js.map +1 -0
  231. package/lib/util/rootCache.js.map +1 -1
  232. package/lib/util/seed.js.map +1 -1
  233. package/lib/util/shuffling.js +1 -2
  234. package/lib/util/shuffling.js.map +1 -1
  235. package/lib/util/signatureSets.js +1 -2
  236. package/lib/util/signatureSets.js.map +1 -1
  237. package/lib/util/signingRoot.js.map +1 -1
  238. package/lib/util/slot.js.map +1 -1
  239. package/lib/util/ssz.d.ts +5 -0
  240. package/lib/util/ssz.d.ts.map +1 -1
  241. package/lib/util/ssz.js +8 -1
  242. package/lib/util/ssz.js.map +1 -1
  243. package/lib/util/sszBytes.js.map +1 -1
  244. package/lib/util/syncCommittee.js.map +1 -1
  245. package/lib/util/targetUnslashedBalance.js.map +1 -1
  246. package/lib/util/validator.d.ts +5 -0
  247. package/lib/util/validator.d.ts.map +1 -1
  248. package/lib/util/validator.js +9 -0
  249. package/lib/util/validator.js.map +1 -1
  250. package/lib/util/weakSubjectivity.d.ts +1 -0
  251. package/lib/util/weakSubjectivity.d.ts.map +1 -1
  252. package/lib/util/weakSubjectivity.js +20 -8
  253. package/lib/util/weakSubjectivity.js.map +1 -1
  254. package/package.json +15 -15
  255. package/src/block/index.ts +4 -3
  256. package/src/block/processAttestations.ts +10 -2
  257. package/src/block/processAttestationsAltair.ts +10 -4
  258. package/src/block/processDeposit.ts +75 -1
  259. package/src/block/processExecutionPayloadBid.ts +22 -9
  260. package/src/block/processOperations.ts +3 -2
  261. package/src/block/processWithdrawalRequest.ts +6 -2
  262. package/src/cache/builderDepositSignatureCache.ts +81 -0
  263. package/src/cache/epochCache.ts +33 -22
  264. package/src/cache/stateCache.ts +5 -2
  265. package/src/cache/types.ts +1 -0
  266. package/src/index.ts +4 -0
  267. package/src/lightClient/spec/utils.ts +9 -2
  268. package/src/metrics.ts +3 -2
  269. package/src/rewards/blockRewards.ts +20 -5
  270. package/src/signatureSets/executionPayloadBid.ts +7 -9
  271. package/src/slot/index.ts +1 -0
  272. package/src/slot/upgradeStateToAltair.ts +1 -0
  273. package/src/slot/upgradeStateToGloas.ts +18 -11
  274. package/src/slot/upgradeStateToHeze.ts +91 -0
  275. package/src/stateTransition.ts +5 -0
  276. package/src/stateView/beaconStateView.ts +13 -0
  277. package/src/stateView/interface.ts +21 -2
  278. package/src/stateView/nativeBeaconStateView.ts +9 -0
  279. package/src/testUtils/state.ts +1 -1
  280. package/src/testUtils/util.ts +75 -5
  281. package/src/types.ts +2 -0
  282. package/src/util/clock.ts +183 -0
  283. package/src/util/deposit.ts +11 -9
  284. package/src/util/genesis.ts +8 -2
  285. package/src/util/gloas.ts +9 -5
  286. package/src/util/index.ts +2 -0
  287. package/src/util/loadState/loadState.ts +50 -11
  288. package/src/util/pendingDepositsLookup.ts +13 -24
  289. package/src/util/preVerifyBuilderDeposits.ts +153 -0
  290. package/src/util/ssz.ts +9 -1
  291. package/src/util/validator.ts +17 -0
  292. package/src/util/weakSubjectivity.ts +61 -22
@@ -0,0 +1,183 @@
1
+ import {ChainForkConfig} from "@lodestar/config";
2
+ import {GENESIS_SLOT, SLOTS_PER_EPOCH} from "@lodestar/params";
3
+ import {Epoch, Slot, TimeSeconds} from "@lodestar/types";
4
+ import {ErrorAborted, Logger, isErrorAborted, sleep} from "@lodestar/utils";
5
+ import {computeEpochAtSlot} from "./epoch.js";
6
+ import {computeTimeAtSlot, getCurrentSlot} from "./slot.js";
7
+
8
+ type RunEveryFn = (slot: Slot, signal: AbortSignal) => Promise<void>;
9
+
10
+ export type ClockOptions = {
11
+ skipSlots?: boolean;
12
+ };
13
+
14
+ export interface IClock {
15
+ readonly genesisTime: number;
16
+ readonly secondsPerSlot: number;
17
+
18
+ readonly currentEpoch: number;
19
+
20
+ start(signal: AbortSignal): void;
21
+ runEverySlot(fn: (slot: Slot, signal: AbortSignal) => Promise<void>): void;
22
+ runEveryEpoch(fn: (epoch: Epoch, signal: AbortSignal) => Promise<void>): void;
23
+ msToSlot(slot: Slot): number;
24
+ msFromSlot(slot: Slot): number;
25
+ secFromSlot(slot: Slot): number;
26
+ getCurrentSlot(): Slot;
27
+ getCurrentEpoch(): Epoch;
28
+ }
29
+
30
+ export enum TimeItem {
31
+ Slot,
32
+ Epoch,
33
+ }
34
+
35
+ export class Clock implements IClock {
36
+ readonly genesisTime: number;
37
+ readonly secondsPerSlot: number;
38
+ private readonly config: ChainForkConfig;
39
+ private readonly logger: Logger;
40
+ private readonly fns: {timeItem: TimeItem; fn: RunEveryFn}[] = [];
41
+
42
+ constructor(
43
+ config: ChainForkConfig,
44
+ logger: Logger,
45
+ private readonly opts: {genesisTime: number} & ClockOptions
46
+ ) {
47
+ this.genesisTime = opts.genesisTime;
48
+ this.secondsPerSlot = config.SLOT_DURATION_MS / 1000;
49
+ this.config = config;
50
+ this.logger = logger;
51
+ }
52
+
53
+ get currentEpoch(): Epoch {
54
+ return computeEpochAtSlot(getCurrentSlot(this.config, this.genesisTime));
55
+ }
56
+
57
+ start(signal: AbortSignal): void {
58
+ for (const {timeItem, fn} of this.fns) {
59
+ this.runAtMostEvery(timeItem, signal, fn).catch((e: Error) => {
60
+ if (!isErrorAborted(e)) {
61
+ this.logger.error("runAtMostEvery", {}, e);
62
+ }
63
+ });
64
+ }
65
+ }
66
+
67
+ getCurrentSlot(): Slot {
68
+ return getCurrentSlot(this.config, this.genesisTime);
69
+ }
70
+
71
+ getCurrentEpoch(): Epoch {
72
+ return computeEpochAtSlot(getCurrentSlot(this.config, this.genesisTime));
73
+ }
74
+
75
+ runEverySlot(fn: RunEveryFn): void {
76
+ this.fns.push({timeItem: TimeItem.Slot, fn});
77
+ }
78
+
79
+ runEveryEpoch(fn: RunEveryFn): void {
80
+ this.fns.push({timeItem: TimeItem.Epoch, fn});
81
+ }
82
+
83
+ /** Milliseconds from now to a specific slot */
84
+ msToSlot(slot: Slot): number {
85
+ return computeTimeAtSlot(this.config, slot, this.genesisTime) * 1000 - Date.now();
86
+ }
87
+
88
+ /** Milliseconds elapsed from a specific slot to now */
89
+ msFromSlot(slot: Slot): number {
90
+ return Date.now() - computeTimeAtSlot(this.config, slot, this.genesisTime) * 1000;
91
+ }
92
+
93
+ /** Seconds elapsed from a specific slot to now */
94
+ secFromSlot(slot: Slot): number {
95
+ return Date.now() / 1000 - computeTimeAtSlot(this.config, slot, this.genesisTime);
96
+ }
97
+
98
+ /**
99
+ * If a task happens to take more than one slot to run, we might skip a slot. This is unfortunate,
100
+ * however the alternative is to *always* process every slot, which has the chance of creating a
101
+ * theoretically unlimited backlog of tasks. It was a conscious decision to choose to drop tasks
102
+ * on an overloaded/latent system rather than overload it even more.
103
+ */
104
+ private async runAtMostEvery(timeItem: TimeItem, signal: AbortSignal, fn: RunEveryFn): Promise<void> {
105
+ // Run immediately first
106
+ let slot = getCurrentSlot(this.config, this.genesisTime);
107
+ let slotOrEpoch = timeItem === TimeItem.Slot ? slot : computeEpochAtSlot(slot);
108
+ while (!signal.aborted) {
109
+ // Must catch fn() to ensure `sleep()` is awaited both for resolve and reject
110
+ const task = fn(slotOrEpoch, signal).catch((e: Error) => {
111
+ if (!isErrorAborted(e)) this.logger.error("Error on runEvery fn", {}, e);
112
+ });
113
+
114
+ if (timeItem !== TimeItem.Slot || this.opts.skipSlots !== false) {
115
+ // await response to only continue with next task if current task finished within slot
116
+ await task;
117
+ }
118
+
119
+ try {
120
+ await sleep(this.timeUntilNext(timeItem), signal);
121
+ // calling getCurrentSlot here may not be correct when we're close to the next slot
122
+ // it's safe to call getCurrentSlotAround after we sleep
123
+ const nextSlot = getCurrentSlotAround(this.config, this.genesisTime);
124
+
125
+ if (timeItem === TimeItem.Slot) {
126
+ if (nextSlot > slot + 1) {
127
+ // It's not very likely that we skip more than one slot as HTTP timeout is set
128
+ // to SLOT_DURATION_MS so we will fail task before skipping another slot.
129
+ this.logger.warn("Skipped slot due to task taking more than one slot to run", {
130
+ skippedSlot: slot + 1,
131
+ });
132
+ }
133
+ slotOrEpoch = nextSlot;
134
+ } else {
135
+ slotOrEpoch = computeEpochAtSlot(nextSlot);
136
+ }
137
+ slot = nextSlot;
138
+ } catch (e) {
139
+ if (e instanceof ErrorAborted) {
140
+ return;
141
+ }
142
+ throw e;
143
+ }
144
+ }
145
+ }
146
+
147
+ private timeUntilNext(timeItem: TimeItem): number {
148
+ const milliSecondsPerSlot = this.config.SLOT_DURATION_MS;
149
+ const msFromGenesis = Date.now() - this.genesisTime * 1000;
150
+
151
+ if (timeItem === TimeItem.Slot) {
152
+ if (msFromGenesis >= 0) {
153
+ return milliSecondsPerSlot - (msFromGenesis % milliSecondsPerSlot);
154
+ }
155
+ return Math.abs(msFromGenesis) % milliSecondsPerSlot;
156
+ }
157
+ const milliSecondsPerEpoch = SLOTS_PER_EPOCH * milliSecondsPerSlot;
158
+ if (msFromGenesis >= 0) {
159
+ return milliSecondsPerEpoch - (msFromGenesis % milliSecondsPerEpoch);
160
+ }
161
+ return Math.abs(msFromGenesis) % milliSecondsPerEpoch;
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Same to the spec but we use Math.round instead of Math.floor.
167
+ */
168
+ export function getCurrentSlotAround(config: ChainForkConfig, genesisTime: TimeSeconds): Slot {
169
+ const diffInSeconds = Date.now() / 1000 - genesisTime;
170
+ const slotsSinceGenesis = Math.round((diffInSeconds * 1000) / config.SLOT_DURATION_MS);
171
+ return GENESIS_SLOT + slotsSinceGenesis;
172
+ }
173
+
174
+ // function useEventStream() {
175
+ // this.stream = this.events.getEventStream([BeaconEventType.BLOCK, BeaconEventType.HEAD, BeaconEventType.CHAIN_REORG]);
176
+ // pipeToEmitter(this.stream, this).catch((e: Error) => {
177
+ // this.logger.error("Error on stream pipe", {}, e);
178
+ // });
179
+
180
+ // // On stop
181
+ // this.stream.stop();
182
+ // this.stream = null;
183
+ // }
@@ -4,19 +4,21 @@ import {CachedBeaconStateAllForks, CachedBeaconStateElectra} from "../types.js";
4
4
 
5
5
  export function getEth1DepositCount(state: CachedBeaconStateAllForks, eth1Data?: phase0.Eth1Data): UintNum64 {
6
6
  const eth1DataToUse = eth1Data ?? state.eth1Data;
7
+ // Proposer can set any value, use in bigint until the result is bounded by MAX_DEPOSITS
8
+ const depositCount = eth1DataToUse.depositCount;
9
+ const eth1DepositIndex = BigInt(state.eth1DepositIndex);
10
+ const maxDeposits = BigInt(MAX_DEPOSITS);
7
11
  if (state.config.getForkSeq(state.slot) >= ForkSeq.electra) {
8
12
  const electraState = state as CachedBeaconStateElectra;
9
- // eth1DataIndexLimit = min(UintNum64, UintBn64) can be safely casted as UintNum64
10
- // since the result lies within upper and lower bound of UintNum64
11
- const eth1DataIndexLimit: UintNum64 =
12
- eth1DataToUse.depositCount < electraState.depositRequestsStartIndex
13
- ? eth1DataToUse.depositCount
14
- : Number(electraState.depositRequestsStartIndex);
13
+ const eth1DataIndexLimit =
14
+ depositCount < electraState.depositRequestsStartIndex ? depositCount : electraState.depositRequestsStartIndex;
15
15
 
16
- if (state.eth1DepositIndex < eth1DataIndexLimit) {
17
- return Math.min(MAX_DEPOSITS, eth1DataIndexLimit - state.eth1DepositIndex);
16
+ if (eth1DepositIndex < eth1DataIndexLimit) {
17
+ const available = eth1DataIndexLimit - eth1DepositIndex;
18
+ return Number(available < maxDeposits ? available : maxDeposits);
18
19
  }
19
20
  return 0;
20
21
  }
21
- return Math.min(MAX_DEPOSITS, eth1DataToUse.depositCount - state.eth1DepositIndex);
22
+ const available = depositCount - eth1DepositIndex;
23
+ return Number(available < maxDeposits ? available : maxDeposits);
22
24
  }
@@ -139,7 +139,7 @@ export function applyDeposits(
139
139
  const fullDepositDataRootArr = fullDepositDataRootList ? fullDepositDataRootList.getAllReadonlyValues() : null;
140
140
 
141
141
  if (fullDepositDataRootArr) {
142
- const depositCount = state.eth1Data.depositCount;
142
+ const depositCount = Number(state.eth1Data.depositCount);
143
143
  for (let index = 0; index < depositCount; index++) {
144
144
  depositDataRootList.push(fullDepositDataRootArr[index]);
145
145
  }
@@ -160,7 +160,7 @@ export function applyDeposits(
160
160
  );
161
161
  }
162
162
 
163
- state.eth1Data.depositCount += 1;
163
+ state.eth1Data.depositCount += 1n;
164
164
 
165
165
  const fork = config.getForkSeq(GENESIS_SLOT);
166
166
  processDeposit(fork, state, deposit);
@@ -334,6 +334,12 @@ export function initializeBeaconStateFromEth1(
334
334
  stateGloas.fork.currentVersion = config.GLOAS_FORK_VERSION;
335
335
  }
336
336
 
337
+ if (fork >= ForkSeq.heze) {
338
+ const stateHeze = state as CompositeViewDU<typeof ssz.heze.BeaconState>;
339
+ stateHeze.fork.previousVersion = config.HEZE_FORK_VERSION;
340
+ stateHeze.fork.currentVersion = config.HEZE_FORK_VERSION;
341
+ }
342
+
337
343
  state.commit();
338
344
 
339
345
  return state;
package/src/util/gloas.ts CHANGED
@@ -87,15 +87,19 @@ export function isActiveBuilder(builder: gloas.Builder, finalizedEpoch: Epoch):
87
87
  * From https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md
88
88
  */
89
89
  export function getExpectedGasLimit(parentGasLimit: number, targetGasLimit: number): number {
90
- const maxGasLimitDifference = Math.max(Math.floor(parentGasLimit / 1024), 1) - 1;
90
+ return Number(getExpectedGasLimitBigint(BigInt(parentGasLimit), BigInt(targetGasLimit)));
91
+ }
92
+
93
+ export function getExpectedGasLimitBigint(parentGasLimit: bigint, targetGasLimit: bigint): bigint {
94
+ const maxGasLimitDifference = (parentGasLimit / 1024n > 1n ? parentGasLimit / 1024n : 1n) - 1n;
91
95
 
92
96
  if (targetGasLimit > parentGasLimit) {
93
97
  const gasDiff = targetGasLimit - parentGasLimit;
94
- return parentGasLimit + Math.min(gasDiff, maxGasLimitDifference);
98
+ return parentGasLimit + (gasDiff < maxGasLimitDifference ? gasDiff : maxGasLimitDifference);
95
99
  }
96
100
 
97
101
  const gasDiff = parentGasLimit - targetGasLimit;
98
- return parentGasLimit - Math.min(gasDiff, maxGasLimitDifference);
102
+ return parentGasLimit - (gasDiff < maxGasLimitDifference ? gasDiff : maxGasLimitDifference);
99
103
  }
100
104
 
101
105
  /**
@@ -104,8 +108,8 @@ export function getExpectedGasLimit(parentGasLimit: number, targetGasLimit: numb
104
108
  * adjustment step of the parent, otherwise it must hit the clamped boundary.
105
109
  * Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.8/specs/gloas/builder.md#new-is_gas_limit_target_compatible
106
110
  */
107
- export function isGasLimitTargetCompatible(parentGasLimit: number, gasLimit: number, targetGasLimit: number): boolean {
108
- return gasLimit === getExpectedGasLimit(parentGasLimit, targetGasLimit);
111
+ export function isGasLimitTargetCompatible(parentGasLimit: bigint, gasLimit: bigint, targetGasLimit: bigint): boolean {
112
+ return gasLimit === getExpectedGasLimitBigint(parentGasLimit, targetGasLimit);
109
113
  }
110
114
 
111
115
  /**
package/src/util/index.ts CHANGED
@@ -6,6 +6,7 @@ export * from "./balance.js";
6
6
  export * from "./blindedBlock.js";
7
7
  export * from "./blockRoot.js";
8
8
  export * from "./capella.js";
9
+ export * from "./clock.js";
9
10
  export * from "./computeAnchorCheckpoint.js";
10
11
  export * from "./deposit.js";
11
12
  export * from "./domain.js";
@@ -19,6 +20,7 @@ export * from "./gloas.js";
19
20
  export * from "./interop.js";
20
21
  export * from "./loadState/index.js";
21
22
  export * from "./pendingDepositsLookup.js";
23
+ export * from "./preVerifyBuilderDeposits.js";
22
24
  export * from "./rootCache.js";
23
25
  export * from "./seed.js";
24
26
  export * from "./shuffling.js";
@@ -25,10 +25,29 @@ export function loadState(
25
25
  ): MigrateStateOutput {
26
26
  // casting only to make typescript happy
27
27
  const stateType = getStateTypeFromBytes(config, stateBytes) as typeof ssz.capella.BeaconState;
28
+ const fork = getForkFromStateBytes(config, stateBytes);
29
+ const seedFork = config.getForkSeq(seedState.slot);
30
+
28
31
  const dataView = new DataView(stateBytes.buffer, stateBytes.byteOffset, stateBytes.byteLength);
29
32
  const fieldRanges = stateType.getFieldRanges(dataView, 0, stateBytes.length);
30
33
  const allFields = Object.keys(stateType.fields);
31
34
  const validatorsFieldIndex = allFields.indexOf("validators");
35
+ const validatorsRange = fieldRanges[validatorsFieldIndex];
36
+ const newValidatorsBytes = stateBytes.subarray(validatorsRange.start, validatorsRange.end);
37
+
38
+ // EIP-7688 replaces List with ProgressiveList for validators and inactivityScores at gloas,
39
+ // changing the merkle tree shape. Seed nodes cannot be reused when one state is pre-gloas
40
+ // and the other post-gloas.
41
+ const crossesGloasFork =
42
+ (fork >= ForkSeq.gloas && seedFork < ForkSeq.gloas) || (fork < ForkSeq.gloas && seedFork >= ForkSeq.gloas);
43
+ if (crossesGloasFork) {
44
+ const migratedState = stateType.deserializeToViewDU(stateBytes) as BeaconStateAllForks;
45
+ // modified validators must still be reported so that the pubkey cache is refreshed for
46
+ // any index that differs from the seed state, which may not be an ancestor of this state
47
+ const modifiedValidators = findModifiedAndAppendedValidators(seedState, newValidatorsBytes, seedValidatorsBytes);
48
+ return {state: migratedState, modifiedValidators};
49
+ }
50
+
32
51
  // start with default view has the same performance to start with seed state
33
52
  // and it is not fork dependent
34
53
  const migratedState = deserializeContainerIgnoreFields(
@@ -39,19 +58,10 @@ export function loadState(
39
58
  ) as BeaconStateAllForks;
40
59
 
41
60
  // validators are rarely changed
42
- const validatorsRange = fieldRanges[validatorsFieldIndex];
43
- const modifiedValidators = loadValidators(
44
- migratedState,
45
- seedState,
46
- stateBytes.subarray(validatorsRange.start, validatorsRange.end),
47
- seedValidatorsBytes
48
- );
61
+ const modifiedValidators = loadValidators(migratedState, seedState, newValidatorsBytes, seedValidatorsBytes);
49
62
 
50
63
  // inactivityScores are rarely changed
51
64
  // this saves ~500ms of hashTreeRoot() time of state
52
- const fork = getForkFromStateBytes(config, stateBytes);
53
- const seedFork = config.getForkSeq(seedState.slot);
54
-
55
65
  if (fork >= ForkSeq.altair && seedFork >= ForkSeq.altair) {
56
66
  const inactivityScoresIndex = allFields.indexOf("inactivityScores");
57
67
  const inactivityScoresRange = fieldRanges[inactivityScoresIndex];
@@ -141,7 +151,9 @@ function loadInactivityScores(
141
151
  }
142
152
  } else {
143
153
  if (newValidator - 1 < 0) {
144
- migratedState.inactivityScores = ssz.altair.InactivityScores.defaultViewDU();
154
+ // use the state's own field type, the list shape differs between altair (List) and gloas (ProgressiveList)
155
+ const inactivityScoresType = (migratedState.type as typeof ssz.altair.BeaconState).fields.inactivityScores;
156
+ migratedState.inactivityScores = inactivityScoresType.defaultViewDU();
145
157
  } else {
146
158
  migratedState.inactivityScores = migratedState.inactivityScores.sliceTo(newValidator - 1);
147
159
  }
@@ -177,6 +189,33 @@ function loadInactivityScores(
177
189
  * @param migratedState state to be migrated, the validators are loaded to this state
178
190
  * @returns modified validator indices
179
191
  */
192
+ /**
193
+ * Find indices of validators whose serialized bytes differ from the seed state, plus indices
194
+ * appended past the seed state's validator count. Unlike loadValidators() this only diffs
195
+ * bytes and does not share the seed state's tree.
196
+ */
197
+ function findModifiedAndAppendedValidators(
198
+ seedState: BeaconStateAllForks,
199
+ newValidatorsBytes: Uint8Array,
200
+ seedStateValidatorsBytes?: Uint8Array
201
+ ): number[] {
202
+ const seedValidatorCount = seedState.validators.length;
203
+ const newValidatorCount = Math.floor(newValidatorsBytes.length / VALIDATOR_BYTES_SIZE);
204
+ const minValidatorCount = Math.min(seedValidatorCount, newValidatorCount);
205
+ const seedValidatorsBytes = seedStateValidatorsBytes ?? seedState.validators.serialize();
206
+ const modifiedValidators: number[] = [];
207
+ findModifiedValidators(
208
+ seedValidatorsBytes.subarray(0, minValidatorCount * VALIDATOR_BYTES_SIZE),
209
+ newValidatorsBytes.subarray(0, minValidatorCount * VALIDATOR_BYTES_SIZE),
210
+ modifiedValidators
211
+ );
212
+
213
+ for (let validatorIndex = seedValidatorCount; validatorIndex < newValidatorCount; validatorIndex++) {
214
+ modifiedValidators.push(validatorIndex);
215
+ }
216
+ return modifiedValidators;
217
+ }
218
+
180
219
  function loadValidators(
181
220
  migratedState: BeaconStateAllForks,
182
221
  seedState: BeaconStateAllForks,
@@ -1,8 +1,7 @@
1
1
  import {BeaconConfig} from "@lodestar/config";
2
2
  import {PubkeyHex, electra} from "@lodestar/types";
3
- import {toPubkeyHex} from "@lodestar/utils";
4
3
  import {isValidDepositSignature} from "../block/processDeposit.js";
5
- import {CachedBeaconStateGloas} from "../types.js";
4
+ import {BuilderDepositSignatureCache} from "../cache/builderDepositSignatureCache.js";
6
5
 
7
6
  type PendingDepositsValidation = {
8
7
  hasValidSignature: boolean;
@@ -34,28 +33,14 @@ export class PendingDepositsLookup {
34
33
  }
35
34
 
36
35
  /**
37
- * Build a pubkey -> pending-deposits lookup from `state.pendingDeposits`.
38
- * No BLS work is done here; signature verification happens lazily in `hasPendingValidator`.
36
+ * Append a pending deposit to the represented sequence, keyed by its (precomputed) `pubkeyHex`.
39
37
  */
40
- static build(state: CachedBeaconStateGloas): PendingDepositsLookup {
41
- const lookup = PendingDepositsLookup.buildEmpty();
42
- for (const pendingDeposit of state.pendingDeposits.getAllReadonly()) {
43
- lookup.add(pendingDeposit);
44
- }
45
- return lookup;
46
- }
47
-
48
- /**
49
- * Append a pending deposit to the represented sequence.
50
- * Pass `pubkeyHex` if the caller has already computed it.
51
- */
52
- add(pendingDeposit: electra.PendingDeposit, pubkeyHex?: PubkeyHex): void {
53
- const key = pubkeyHex ?? toPubkeyHex(pendingDeposit.pubkey);
54
- const existing = this.depositsByPubkey.get(key);
38
+ add(pendingDeposit: electra.PendingDeposit, pubkeyHex: PubkeyHex): void {
39
+ const existing = this.depositsByPubkey.get(pubkeyHex);
55
40
  if (existing) {
56
41
  existing.push(pendingDeposit);
57
42
  } else {
58
- this.depositsByPubkey.set(key, [pendingDeposit]);
43
+ this.depositsByPubkey.set(pubkeyHex, [pendingDeposit]);
59
44
  }
60
45
  }
61
46
 
@@ -63,8 +48,11 @@ export class PendingDepositsLookup {
63
48
  * Returns true if any pending deposit for `pubkeyHex` has a valid BLS deposit signature.
64
49
  * Memoizes the result in `validationCache` so repeated checks for the same pubkey
65
50
  * within a block only verify deposits that have not already been checked.
51
+ *
52
+ * Most of the time it should hit the BuilderDepositSignatureCache so we don't have to validate
53
+ * signatures here.
66
54
  */
67
- hasPendingValidator(config: BeaconConfig, pubkeyHex: PubkeyHex): boolean {
55
+ hasPendingValidator(config: BeaconConfig, pubkeyHex: PubkeyHex, cache: BuilderDepositSignatureCache): boolean {
68
56
  const validation = this.validationCache.get(pubkeyHex);
69
57
  if (validation?.hasValidSignature === true) {
70
58
  return true;
@@ -85,15 +73,16 @@ export class PendingDepositsLookup {
85
73
 
86
74
  for (let i = startIndex; i < deposits.length; i++) {
87
75
  const deposit = deposits[i];
88
- if (
76
+ const isValid =
77
+ cache.getSignatureValidity(deposit) ??
89
78
  isValidDepositSignature(
90
79
  config,
91
80
  deposit.pubkey,
92
81
  deposit.withdrawalCredentials,
93
82
  deposit.amount,
94
83
  deposit.signature
95
- )
96
- ) {
84
+ );
85
+ if (isValid) {
97
86
  this.validationCache.set(pubkeyHex, {hasValidSignature: true, validatedCount: i + 1});
98
87
  return true;
99
88
  }
@@ -0,0 +1,153 @@
1
+ import {electra} from "@lodestar/types";
2
+ import {toPubkeyHex} from "@lodestar/utils";
3
+ import {verifyDepositSignatures} from "../block/processDeposit.js";
4
+ import {CachedBeaconStateFulu} from "../types.js";
5
+ import {isBuilderWithdrawalCredential} from "./gloas.js";
6
+
7
+ /** Verify queued deposit signatures in batches of this size. Also the time-box granularity: the
8
+ * deadline is checked once per batch, so overrun is bounded to a single batch. Exported for tests. */
9
+ export const BUILDER_DEPOSIT_BATCH_SIZE = 32;
10
+
11
+ /**
12
+ * Per-tick cap on builder deposits the prepareForNextSlot scanner will verify.
13
+ * ~2.7s for 10_000 BLS verifications on a typical server — fits within the slot budget while the
14
+ * pre-window (see GLOAS_PREVERIFY_WINDOW_EPOCHS) covers up to ~620k deposits over its duration.
15
+ */
16
+ export const MAX_BUILDER_DEPOSITS_PER_SLOT = 10_000;
17
+
18
+ /**
19
+ * Number of epochs before GLOAS_FORK_EPOCH during which the prepareForNextSlot scanner pre-verifies
20
+ * builder-deposit signatures. Two: deposits arriving this late cannot collude with validator
21
+ * deposits — validator-vs-builder routing is decided at the fork boundary from what is already in
22
+ * pendingDeposits, so anything submitted inside this window is unambiguous.
23
+ */
24
+ export const GLOAS_PREVERIFY_WINDOW_EPOCHS = 2;
25
+
26
+ /**
27
+ * Summary of a single `preVerifyBuilderDepositsPreGloas` call.
28
+ */
29
+ export type PreVerifyBuilderDepositsResult = {
30
+ /** Builder-prefix deposit signatures verified valid in this call (cached true). */
31
+ validBuilderSignaturesCount: number;
32
+ /** Builder-prefix deposit signatures verified invalid in this call (cached false, dropped at fork). */
33
+ invalidBuilderSignaturesCount: number;
34
+ /** Validator (builder-pubkey) deposit signatures verified valid in this call. */
35
+ validValidatorSignaturesCount: number;
36
+ /** Validator (builder-pubkey) deposit signatures verified invalid in this call (abuse signal). */
37
+ invalidValidatorSignaturesCount: number;
38
+ /** Pending deposits examined this call (all creds; < pendingDepositsCount ⇒ per-tick cap hit). */
39
+ scannedPendingDeposits: number;
40
+ /** Cumulative deposits verified & cached so far this window (cache size, pass + fail). */
41
+ totalCachedDeposits: number;
42
+ /** Distinct builder pubkeys tracked so far this window. */
43
+ totalBuilderPubkeys: number;
44
+ /** Current `state.pendingDeposits.length` — the full backlog. */
45
+ pendingDepositsCount: number;
46
+ };
47
+
48
+ /**
49
+ * Scanner driven by `prepareForNextSlot` over the `GLOAS_PREVERIFY_WINDOW_EPOCHS` epochs before
50
+ * GLOAS_FORK_EPOCH. Walks `state.pendingDeposits` (as struct values via getAllReadonlyValues) and
51
+ * queues, for signature verification, the deposits the fork transition will check on its hot path:
52
+ * - builder-prefix deposits (for onboarding); records each builder pubkey on the cache, and
53
+ * - validator deposits sharing a builder pubkey (the set `hasPendingValidator` walks at the fork),
54
+ * but only until one is found valid — `hasPendingValidator` stops at the first valid signature, so
55
+ * the cache marks the pubkey and the scanner skips that pubkey's remaining validator deposits.
56
+ *
57
+ * Bounded by two knobs: at most `maxBuilderDeposits` new deposits are queued per call, and the chunk
58
+ * verification is **time-boxed** to `maxDurationMs`.
59
+ */
60
+ export function preVerifyBuilderDepositsPreGloas(
61
+ state: CachedBeaconStateFulu,
62
+ maxBuilderDeposits: number,
63
+ maxDurationMs: number
64
+ ): PreVerifyBuilderDepositsResult {
65
+ const cache = state.epochCtx.builderDepositSignatureCache;
66
+
67
+ // Collect uncached deposits to verify as struct values. Full rescan each tick: iteration is
68
+ // O(pendingDeposits) but runs in prepareNextSlot spare time, and isVerified() is an O(1) skip for
69
+ // already-cached deposits. For a ContainerNodeStructType, getAllReadonlyValues() returns each
70
+ // node.value by reference — the stable cache key.
71
+ const queue: electra.PendingDeposit[] = [];
72
+ let scannedPendingDeposits = 0;
73
+ for (const deposit of state.pendingDeposits.getAllReadonlyValues()) {
74
+ if (queue.length >= maxBuilderDeposits) break; // per-tick cap; next tick resumes via isVerified()
75
+ scannedPendingDeposits++;
76
+ if (cache.isVerified(deposit)) continue; // already verified in an earlier tick
77
+
78
+ if (isBuilderWithdrawalCredential(deposit.withdrawalCredentials)) {
79
+ // Record the builder pubkey so validator deposits sharing it get pre-verified — this or a
80
+ // later tick, since those validator deposits precede the builder deposit in the queue. A cached
81
+ // builder deposit already added its pubkey, so it's safe to skip via the `continue` above.
82
+ cache.addBuilderPubkey(toPubkeyHex(deposit.pubkey));
83
+ queue.push(deposit);
84
+ } else {
85
+ // Validator deposit — pre-verify only if it shares a builder pubkey (the set hasPendingValidator
86
+ // walks at the fork) AND we haven't already cached a valid validator deposit for that pubkey.
87
+ // this is the same logic to hasPendingValidator.
88
+ const pubkeyHex = toPubkeyHex(deposit.pubkey);
89
+ if (cache.isBuilderPubkey(pubkeyHex) && !cache.hasValidValidatorDeposit(pubkeyHex)) {
90
+ queue.push(deposit);
91
+ }
92
+ }
93
+ }
94
+
95
+ const pendingDepositsCount = state.pendingDeposits.length;
96
+ if (queue.length === 0) {
97
+ return {
98
+ validBuilderSignaturesCount: 0,
99
+ invalidBuilderSignaturesCount: 0,
100
+ validValidatorSignaturesCount: 0,
101
+ invalidValidatorSignaturesCount: 0,
102
+ scannedPendingDeposits,
103
+ totalCachedDeposits: cache.cachedDepositCount,
104
+ totalBuilderPubkeys: cache.builderPubkeyCount,
105
+ pendingDepositsCount,
106
+ };
107
+ }
108
+
109
+ // Verify in chunks; chunking caps the fallback blast radius so one bad signature only forces 32
110
+ // individual re-verifications. Key each result by the deposit value object. Counts are tracked
111
+ // incrementally (not queue.length - verified) so they stay correct when we time-box out early.
112
+ const deadline = Date.now() + maxDurationMs;
113
+ let validBuilderSignaturesCount = 0;
114
+ let invalidBuilderSignaturesCount = 0;
115
+ let validValidatorSignaturesCount = 0;
116
+ let invalidValidatorSignaturesCount = 0;
117
+ for (let chunkStart = 0; chunkStart < queue.length; chunkStart += BUILDER_DEPOSIT_BATCH_SIZE) {
118
+ const end = Math.min(chunkStart + BUILDER_DEPOSIT_BATCH_SIZE, queue.length);
119
+ const chunk = queue.slice(chunkStart, end);
120
+ const chunkResults = verifyDepositSignatures(state.epochCtx.config, chunk);
121
+ for (let j = 0; j < chunk.length; j++) {
122
+ cache.setSignatureValidity(chunk[j], chunkResults[j]);
123
+ const isBuilder = isBuilderWithdrawalCredential(chunk[j].withdrawalCredentials);
124
+ if (chunkResults[j]) {
125
+ if (isBuilder) {
126
+ validBuilderSignaturesCount++;
127
+ } else {
128
+ validValidatorSignaturesCount++;
129
+ // A valid validator deposit satisfies hasPendingValidator() for this pubkey; stop
130
+ // pre-verifying its remaining validator deposits on later ticks.
131
+ cache.setValidValidatorDeposit(toPubkeyHex(chunk[j].pubkey));
132
+ }
133
+ } else if (isBuilder) {
134
+ invalidBuilderSignaturesCount++;
135
+ } else {
136
+ invalidValidatorSignaturesCount++;
137
+ }
138
+ }
139
+ // Time-box: keep everything verified so far (already cached) and stop
140
+ if (Date.now() >= deadline) break;
141
+ }
142
+
143
+ return {
144
+ validBuilderSignaturesCount,
145
+ invalidBuilderSignaturesCount,
146
+ validValidatorSignaturesCount,
147
+ invalidValidatorSignaturesCount,
148
+ scannedPendingDeposits,
149
+ totalCachedDeposits: cache.cachedDepositCount,
150
+ totalBuilderPubkeys: cache.builderPubkeyCount,
151
+ pendingDepositsCount,
152
+ };
153
+ }
package/src/util/ssz.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import {BranchNode, LeafNode, Node, zeroNode} from "@chainsafe/persistent-merkle-tree";
2
2
  import {progressiveSubtreeFillToContents} from "@chainsafe/ssz";
3
3
 
4
- // TODO: move these utils to @chainsafe/ssz (progressive.ts, next to progressiveSubtreeFillToContents)
4
+ // TODO: move these utils to @chainsafe/ssz, see https://github.com/ChainSafe/ssz/issues/542
5
5
 
6
6
  /** Root node (chunks + length mix-in) of a zero-filled ProgressiveListBasicType of `length` items */
7
7
  export function zeroProgressiveListBasicRootNode(itemsPerChunk: number, length: number): Node {
@@ -15,6 +15,14 @@ export function zeroProgressiveListBasicRootNode(itemsPerChunk: number, length:
15
15
  return new BranchNode(zeroProgressiveNode(numSubtrees), LeafNode.fromUint32(length));
16
16
  }
17
17
 
18
+ /**
19
+ * Check if an array-type ViewDU (ListBasic, ListComposite or their progressive equivalents) has its
20
+ * internal nodes cache populated. The flag is a private attribute maintained by all of these classes.
21
+ */
22
+ export function isViewDUNodesPopulated(view: unknown): boolean {
23
+ return (view as {nodesPopulated?: boolean}).nodesPopulated === true;
24
+ }
25
+
18
26
  /**
19
27
  * Root node of a progressive list from its chunk/element nodes + length mix-in.
20
28
  * `nodes` are packed 32-byte chunk leaves for basic lists, or element root nodes for composite lists.
@@ -128,6 +128,23 @@ export function getConsolidationChurnLimit(fork: ForkSeq, epochCtx: EpochCache):
128
128
  return getBalanceChurnLimitFromCache(epochCtx) - getActivationExitChurnLimit(epochCtx);
129
129
  }
130
130
 
131
+ // NOTE: duplicates the quotient/min choices in get{Exit,Activation,Consolidation}ChurnLimit.
132
+ export function getGloasChurnLimits(
133
+ config: ChainForkConfig,
134
+ totalActiveBalanceIncrements: number
135
+ ): {exit: number; activation: number; consolidation: number} {
136
+ const exit = getBalanceChurnLimit(
137
+ totalActiveBalanceIncrements,
138
+ config.CHURN_LIMIT_QUOTIENT_GLOAS,
139
+ config.MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA
140
+ );
141
+ return {
142
+ exit,
143
+ activation: Math.min(config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT_GLOAS, exit),
144
+ consolidation: getBalanceChurnLimit(totalActiveBalanceIncrements, config.CONSOLIDATION_CHURN_LIMIT_QUOTIENT, 0),
145
+ };
146
+ }
147
+
131
148
  export function getMaxEffectiveBalance(withdrawalCredentials: Uint8Array): number {
132
149
  // Compounding withdrawal credential only available since Electra
133
150
  if (hasCompoundingWithdrawalCredential(withdrawalCredentials)) {