@lodestar/beacon-node 1.41.0 → 1.42.0-dev.1d50253953

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 (225) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +35 -16
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/state/utils.d.ts +2 -2
  5. package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
  6. package/lib/api/impl/beacon/state/utils.js.map +1 -1
  7. package/lib/api/impl/validator/index.d.ts.map +1 -1
  8. package/lib/api/impl/validator/index.js +5 -1
  9. package/lib/api/impl/validator/index.js.map +1 -1
  10. package/lib/chain/archiveStore/archiveStore.d.ts +0 -1
  11. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  12. package/lib/chain/archiveStore/archiveStore.js +0 -9
  13. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  14. package/lib/chain/archiveStore/interface.d.ts +4 -4
  15. package/lib/chain/archiveStore/interface.d.ts.map +1 -1
  16. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.d.ts +4 -4
  17. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.d.ts.map +1 -1
  18. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js +4 -1
  19. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js.map +1 -1
  20. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
  21. package/lib/chain/archiveStore/utils/archiveBlocks.js +38 -0
  22. package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
  23. package/lib/chain/blocks/blockInput/types.d.ts +3 -3
  24. package/lib/chain/blocks/blockInput/types.d.ts.map +1 -1
  25. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  26. package/lib/chain/blocks/importBlock.js +29 -9
  27. package/lib/chain/blocks/importBlock.js.map +1 -1
  28. package/lib/chain/blocks/importExecutionPayload.d.ts +48 -0
  29. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -0
  30. package/lib/chain/blocks/importExecutionPayload.js +159 -0
  31. package/lib/chain/blocks/importExecutionPayload.js.map +1 -0
  32. package/lib/chain/blocks/payloadEnvelopeInput/index.d.ts +3 -0
  33. package/lib/chain/blocks/payloadEnvelopeInput/index.d.ts.map +1 -0
  34. package/lib/chain/blocks/payloadEnvelopeInput/index.js +3 -0
  35. package/lib/chain/blocks/payloadEnvelopeInput/index.js.map +1 -0
  36. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +80 -0
  37. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -0
  38. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +248 -0
  39. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -0
  40. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +29 -0
  41. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -0
  42. package/lib/chain/blocks/payloadEnvelopeInput/types.js +11 -0
  43. package/lib/chain/blocks/payloadEnvelopeInput/types.js.map +1 -0
  44. package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts +15 -0
  45. package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts.map +1 -0
  46. package/lib/chain/blocks/payloadEnvelopeProcessor.js +46 -0
  47. package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -0
  48. package/lib/chain/blocks/types.d.ts +7 -0
  49. package/lib/chain/blocks/types.d.ts.map +1 -1
  50. package/lib/chain/blocks/verifyBlocksSignatures.js +1 -1
  51. package/lib/chain/blocks/verifyBlocksSignatures.js.map +1 -1
  52. package/lib/chain/blocks/writePayloadEnvelopeInputToDb.d.ts +12 -0
  53. package/lib/chain/blocks/writePayloadEnvelopeInputToDb.d.ts.map +1 -0
  54. package/lib/chain/blocks/writePayloadEnvelopeInputToDb.js +40 -0
  55. package/lib/chain/blocks/writePayloadEnvelopeInputToDb.js.map +1 -0
  56. package/lib/chain/chain.d.ts +10 -5
  57. package/lib/chain/chain.d.ts.map +1 -1
  58. package/lib/chain/chain.js +44 -10
  59. package/lib/chain/chain.js.map +1 -1
  60. package/lib/chain/errors/executionPayloadEnvelope.d.ts +12 -2
  61. package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
  62. package/lib/chain/errors/executionPayloadEnvelope.js +3 -1
  63. package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
  64. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  65. package/lib/chain/forkChoice/index.js +0 -10
  66. package/lib/chain/forkChoice/index.js.map +1 -1
  67. package/lib/chain/interface.d.ts +8 -5
  68. package/lib/chain/interface.d.ts.map +1 -1
  69. package/lib/chain/opPools/utils.js +1 -1
  70. package/lib/chain/opPools/utils.js.map +1 -1
  71. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  72. package/lib/chain/prepareNextSlot.js +6 -2
  73. package/lib/chain/prepareNextSlot.js.map +1 -1
  74. package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
  75. package/lib/chain/produceBlock/computeNewStateRoot.js +6 -1
  76. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  77. package/lib/chain/produceBlock/produceBlockBody.js +1 -1
  78. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  79. package/lib/chain/regen/errors.d.ts +11 -1
  80. package/lib/chain/regen/errors.d.ts.map +1 -1
  81. package/lib/chain/regen/errors.js +2 -0
  82. package/lib/chain/regen/errors.js.map +1 -1
  83. package/lib/chain/regen/interface.d.ts +14 -6
  84. package/lib/chain/regen/interface.d.ts.map +1 -1
  85. package/lib/chain/regen/interface.js +2 -0
  86. package/lib/chain/regen/interface.js.map +1 -1
  87. package/lib/chain/regen/queued.d.ts +11 -6
  88. package/lib/chain/regen/queued.d.ts.map +1 -1
  89. package/lib/chain/regen/queued.js +40 -8
  90. package/lib/chain/regen/queued.js.map +1 -1
  91. package/lib/chain/regen/regen.d.ts +5 -0
  92. package/lib/chain/regen/regen.d.ts.map +1 -1
  93. package/lib/chain/regen/regen.js +33 -6
  94. package/lib/chain/regen/regen.js.map +1 -1
  95. package/lib/chain/seenCache/index.d.ts +1 -1
  96. package/lib/chain/seenCache/index.d.ts.map +1 -1
  97. package/lib/chain/seenCache/index.js +1 -1
  98. package/lib/chain/seenCache/index.js.map +1 -1
  99. package/lib/chain/seenCache/seenGossipBlockInput.js +2 -2
  100. package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
  101. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +38 -0
  102. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -0
  103. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +76 -0
  104. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -0
  105. package/lib/chain/stateCache/datastore/db.d.ts +4 -5
  106. package/lib/chain/stateCache/datastore/db.d.ts.map +1 -1
  107. package/lib/chain/stateCache/datastore/db.js +32 -10
  108. package/lib/chain/stateCache/datastore/db.js.map +1 -1
  109. package/lib/chain/stateCache/datastore/file.d.ts +1 -1
  110. package/lib/chain/stateCache/datastore/file.d.ts.map +1 -1
  111. package/lib/chain/stateCache/datastore/file.js +5 -5
  112. package/lib/chain/stateCache/datastore/file.js.map +1 -1
  113. package/lib/chain/stateCache/datastore/types.d.ts +1 -1
  114. package/lib/chain/stateCache/datastore/types.d.ts.map +1 -1
  115. package/lib/chain/stateCache/fifoBlockStateCache.d.ts +7 -4
  116. package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
  117. package/lib/chain/stateCache/fifoBlockStateCache.js +8 -3
  118. package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
  119. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +33 -14
  120. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  121. package/lib/chain/stateCache/persistentCheckpointsCache.js +217 -119
  122. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  123. package/lib/chain/stateCache/types.d.ts +15 -8
  124. package/lib/chain/stateCache/types.d.ts.map +1 -1
  125. package/lib/chain/stateCache/types.js.map +1 -1
  126. package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
  127. package/lib/chain/validation/executionPayloadEnvelope.js +30 -19
  128. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  129. package/lib/chain/validation/lightClientFinalityUpdate.js +1 -1
  130. package/lib/chain/validation/lightClientFinalityUpdate.js.map +1 -1
  131. package/lib/chain/validation/lightClientOptimisticUpdate.js +1 -1
  132. package/lib/chain/validation/lightClientOptimisticUpdate.js.map +1 -1
  133. package/lib/chain/validation/voluntaryExit.d.ts.map +1 -1
  134. package/lib/chain/validation/voluntaryExit.js +2 -2
  135. package/lib/chain/validation/voluntaryExit.js.map +1 -1
  136. package/lib/chain/validatorMonitor.d.ts +2 -1
  137. package/lib/chain/validatorMonitor.d.ts.map +1 -1
  138. package/lib/chain/validatorMonitor.js +4 -1
  139. package/lib/chain/validatorMonitor.js.map +1 -1
  140. package/lib/execution/engine/interface.d.ts +2 -2
  141. package/lib/metrics/metrics/lodestar.d.ts +28 -0
  142. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  143. package/lib/metrics/metrics/lodestar.js +74 -0
  144. package/lib/metrics/metrics/lodestar.js.map +1 -1
  145. package/lib/network/network.js +2 -2
  146. package/lib/network/network.js.map +1 -1
  147. package/lib/network/processor/extractSlotRootFns.d.ts.map +1 -1
  148. package/lib/network/processor/extractSlotRootFns.js +14 -4
  149. package/lib/network/processor/extractSlotRootFns.js.map +1 -1
  150. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  151. package/lib/network/processor/gossipHandlers.js +31 -3
  152. package/lib/network/processor/gossipHandlers.js.map +1 -1
  153. package/lib/network/reqresp/ReqRespBeaconNode.d.ts +1 -1
  154. package/lib/network/reqresp/ReqRespBeaconNode.js +1 -1
  155. package/lib/sync/backfill/backfill.d.ts +1 -1
  156. package/lib/sync/backfill/backfill.js +1 -1
  157. package/lib/sync/constants.d.ts +1 -1
  158. package/lib/sync/constants.js +1 -1
  159. package/lib/util/sszBytes.d.ts +4 -1
  160. package/lib/util/sszBytes.d.ts.map +1 -1
  161. package/lib/util/sszBytes.js +69 -12
  162. package/lib/util/sszBytes.js.map +1 -1
  163. package/package.json +15 -15
  164. package/src/api/impl/beacon/blocks/index.ts +36 -17
  165. package/src/api/impl/beacon/state/utils.ts +2 -2
  166. package/src/api/impl/validator/index.ts +7 -3
  167. package/src/chain/archiveStore/archiveStore.ts +0 -10
  168. package/src/chain/archiveStore/interface.ts +4 -4
  169. package/src/chain/archiveStore/strategies/frequencyStateArchiveStrategy.ts +8 -5
  170. package/src/chain/archiveStore/utils/archiveBlocks.ts +59 -1
  171. package/src/chain/blocks/blockInput/types.ts +3 -3
  172. package/src/chain/blocks/importBlock.ts +47 -8
  173. package/src/chain/blocks/importExecutionPayload.ts +241 -0
  174. package/src/chain/blocks/payloadEnvelopeInput/index.ts +2 -0
  175. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +336 -0
  176. package/src/chain/blocks/payloadEnvelopeInput/types.ts +33 -0
  177. package/src/chain/blocks/payloadEnvelopeProcessor.ts +61 -0
  178. package/src/chain/blocks/types.ts +8 -0
  179. package/src/chain/blocks/verifyBlocksSignatures.ts +1 -1
  180. package/src/chain/blocks/writePayloadEnvelopeInputToDb.ts +55 -0
  181. package/src/chain/chain.ts +60 -15
  182. package/src/chain/errors/executionPayloadEnvelope.ts +6 -2
  183. package/src/chain/forkChoice/index.ts +0 -10
  184. package/src/chain/interface.ts +8 -5
  185. package/src/chain/opPools/utils.ts +1 -1
  186. package/src/chain/prepareNextSlot.ts +6 -2
  187. package/src/chain/produceBlock/computeNewStateRoot.ts +6 -1
  188. package/src/chain/produceBlock/produceBlockBody.ts +1 -1
  189. package/src/chain/regen/errors.ts +6 -1
  190. package/src/chain/regen/interface.ts +14 -6
  191. package/src/chain/regen/queued.ts +48 -12
  192. package/src/chain/regen/regen.ts +37 -7
  193. package/src/chain/seenCache/index.ts +1 -1
  194. package/src/chain/seenCache/seenGossipBlockInput.ts +2 -2
  195. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +106 -0
  196. package/src/chain/stateCache/datastore/db.ts +33 -10
  197. package/src/chain/stateCache/datastore/file.ts +6 -5
  198. package/src/chain/stateCache/datastore/types.ts +3 -2
  199. package/src/chain/stateCache/fifoBlockStateCache.ts +10 -4
  200. package/src/chain/stateCache/persistentCheckpointsCache.ts +248 -139
  201. package/src/chain/stateCache/types.ts +18 -8
  202. package/src/chain/validation/executionPayloadEnvelope.ts +38 -25
  203. package/src/chain/validation/lightClientFinalityUpdate.ts +1 -1
  204. package/src/chain/validation/lightClientOptimisticUpdate.ts +1 -1
  205. package/src/chain/validation/voluntaryExit.ts +2 -1
  206. package/src/chain/validatorMonitor.ts +11 -1
  207. package/src/execution/engine/interface.ts +2 -2
  208. package/src/metrics/metrics/lodestar.ts +77 -0
  209. package/src/network/network.ts +2 -2
  210. package/src/network/processor/extractSlotRootFns.ts +18 -5
  211. package/src/network/processor/gossipHandlers.ts +37 -1
  212. package/src/network/reqresp/ReqRespBeaconNode.ts +1 -1
  213. package/src/sync/backfill/backfill.ts +1 -1
  214. package/src/sync/constants.ts +1 -1
  215. package/src/util/sszBytes.ts +90 -10
  216. package/lib/chain/archiveStore/utils/archivePayloads.d.ts +0 -7
  217. package/lib/chain/archiveStore/utils/archivePayloads.d.ts.map +0 -1
  218. package/lib/chain/archiveStore/utils/archivePayloads.js +0 -10
  219. package/lib/chain/archiveStore/utils/archivePayloads.js.map +0 -1
  220. package/lib/chain/seenCache/seenExecutionPayloadEnvelope.d.ts +0 -15
  221. package/lib/chain/seenCache/seenExecutionPayloadEnvelope.d.ts.map +0 -1
  222. package/lib/chain/seenCache/seenExecutionPayloadEnvelope.js +0 -28
  223. package/lib/chain/seenCache/seenExecutionPayloadEnvelope.js.map +0 -1
  224. package/src/chain/archiveStore/utils/archivePayloads.ts +0 -15
  225. package/src/chain/seenCache/seenExecutionPayloadEnvelope.ts +0 -34
@@ -1,6 +1,6 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
- import {IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
3
- import {SLOTS_PER_EPOCH} from "@lodestar/params";
2
+ import {IForkChoice, PayloadStatus, ProtoBlock} from "@lodestar/fork-choice";
3
+ import {ForkSeq, SLOTS_PER_EPOCH} from "@lodestar/params";
4
4
  import {
5
5
  CachedBeaconStateAllForks,
6
6
  DataAvailabilityStatus,
@@ -111,9 +111,20 @@ export class StateRegenerator implements IStateRegeneratorInternal {
111
111
  const {blockRoot} = block;
112
112
  const {checkpointStateCache} = this.modules;
113
113
  const epoch = computeEpochAtSlot(slot);
114
+
115
+ // Convert PayloadStatus to payloadPresent boolean
116
+ if (block.payloadStatus === PayloadStatus.PENDING) {
117
+ throw new RegenError({
118
+ code: RegenErrorCode.UNEXPECTED_PAYLOAD_STATUS,
119
+ blockRoot: fromHex(blockRoot),
120
+ payloadStatus: block.payloadStatus,
121
+ });
122
+ }
123
+ const payloadPresent = block.payloadStatus === PayloadStatus.FULL;
124
+
114
125
  const latestCheckpointStateCtx = allowDiskReload
115
- ? await checkpointStateCache.getOrReloadLatest(blockRoot, epoch)
116
- : checkpointStateCache.getLatest(blockRoot, epoch);
126
+ ? await checkpointStateCache.getOrReloadLatest(blockRoot, epoch, payloadPresent)
127
+ : checkpointStateCache.getLatest(blockRoot, epoch, payloadPresent);
117
128
 
118
129
  // If a checkpoint state exists with the given checkpoint root, it either is in requested epoch
119
130
  // or needs to have empty slots processed until the requested epoch
@@ -166,9 +177,19 @@ export class StateRegenerator implements IStateRegeneratorInternal {
166
177
  const lastBlockToReplay = blocksToReplay.at(-1);
167
178
  if (!lastBlockToReplay) continue;
168
179
  const epoch = computeEpochAtSlot(lastBlockToReplay.slot - 1);
180
+
181
+ // Convert PayloadStatus to payloadPresent boolean
182
+ if (b.payloadStatus === PayloadStatus.PENDING) {
183
+ throw new RegenError({
184
+ code: RegenErrorCode.INTERNAL_ERROR,
185
+ message: `Unexpected PENDING payloadStatus for ancestor block ${b.blockRoot} at slot ${b.slot}`,
186
+ });
187
+ }
188
+ const payloadPresent = b.payloadStatus === PayloadStatus.FULL;
189
+
169
190
  state = allowDiskReload
170
- ? await checkpointStateCache.getOrReloadLatest(b.blockRoot, epoch)
171
- : checkpointStateCache.getLatest(b.blockRoot, epoch);
191
+ ? await checkpointStateCache.getOrReloadLatest(b.blockRoot, epoch, payloadPresent)
192
+ : checkpointStateCache.getLatest(b.blockRoot, epoch, payloadPresent);
172
193
  if (state) {
173
194
  break;
174
195
  }
@@ -332,6 +353,11 @@ async function processSlotsByCheckpoint(
332
353
  * emitting "checkpoint" events after every epoch processed.
333
354
  *
334
355
  * Stops processing after no more full epochs can be processed.
356
+ *
357
+ * Output state variant:
358
+ * - Post-Gloas: If slots are processed, returns block state (payloadPresent=false).
359
+ * If no slots processed, returns preState as-is (preserves variant).
360
+ * - Pre-Gloas: Always payloadPresent=true (no block/payload distinction).
335
361
  */
336
362
  export async function processSlotsToNearestCheckpoint(
337
363
  modules: {
@@ -374,7 +400,11 @@ export async function processSlotsToNearestCheckpoint(
374
400
  // This may becomes the "official" checkpoint state if the 1st block of epoch is skipped
375
401
  const checkpointState = postState;
376
402
  const cp = getCheckpointFromState(checkpointState);
377
- checkpointStateCache.add(cp, checkpointState);
403
+ // processSlots() only does epoch transitions, never processes payloads
404
+ // Pre-Gloas: payloadPresent is always true (execution payload embedded in block)
405
+ // Post-Gloas: result is a block state (payloadPresent=false)
406
+ const isPayloadPresent = checkpointState.config.getForkSeq(checkpointState.slot) < ForkSeq.gloas;
407
+ checkpointStateCache.add(cp, checkpointState, isPayloadPresent);
378
408
  // consumers should not mutate state ever
379
409
  emitter?.emit(ChainEvent.checkpoint, cp, checkpointState);
380
410
 
@@ -3,5 +3,5 @@ export {SeenBlockProposers} from "./seenBlockProposers.js";
3
3
  export {SeenSyncCommitteeMessages} from "./seenCommittee.js";
4
4
  export {SeenContributionAndProof} from "./seenCommitteeContribution.js";
5
5
  export {SeenExecutionPayloadBids} from "./seenExecutionPayloadBids.js";
6
- export {SeenExecutionPayloadEnvelopes} from "./seenExecutionPayloadEnvelope.js";
7
6
  export {SeenBlockInput} from "./seenGossipBlockInput.js";
7
+ export {PayloadEnvelopeInput, SeenPayloadEnvelopeInput} from "./seenPayloadEnvelopeInput.js";
@@ -180,7 +180,7 @@ export class SeenBlockInput {
180
180
  blockInput = this.blockInputs.get(parentRootHex ?? "");
181
181
  parentRootHex = blockInput?.parentRootHex;
182
182
  }
183
- this.logger?.debug(`BlockInputCache.prune deleted ${deletedCount} cached BlockInputs`);
183
+ this.logger?.debug("BlockInputCache.prune deleted cached BlockInputs", {deletedCount});
184
184
  this.pruneToMaxSize();
185
185
  }
186
186
 
@@ -193,7 +193,7 @@ export class SeenBlockInput {
193
193
  this.evictBlockInput(blockInput);
194
194
  }
195
195
  }
196
- this.logger?.debug(`BlockInputCache.onFinalized deleted ${deletedCount} cached BlockInputs`);
196
+ this.logger?.debug("BlockInputCache.onFinalized deleted cached BlockInputs", {deletedCount});
197
197
  this.pruneToMaxSize();
198
198
  };
199
199
 
@@ -0,0 +1,106 @@
1
+ import {CheckpointWithHex} from "@lodestar/fork-choice";
2
+ import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
3
+ import {RootHex} from "@lodestar/types";
4
+ import {Logger} from "@lodestar/utils";
5
+ import {Metrics} from "../../metrics/metrics.js";
6
+ import {SerializedCache} from "../../util/serializedCache.js";
7
+ import {CreateFromBlockProps, PayloadEnvelopeInput} from "../blocks/payloadEnvelopeInput/index.js";
8
+ import {ChainEvent, ChainEventEmitter} from "../emitter.js";
9
+
10
+ export type {PayloadEnvelopeInputState} from "../blocks/payloadEnvelopeInput/index.js";
11
+ export {PayloadEnvelopeInput} from "../blocks/payloadEnvelopeInput/index.js";
12
+
13
+ export type SeenPayloadEnvelopeInputModules = {
14
+ chainEvents: ChainEventEmitter;
15
+ signal: AbortSignal;
16
+ serializedCache: SerializedCache;
17
+ metrics: Metrics | null;
18
+ logger?: Logger;
19
+ };
20
+
21
+ /**
22
+ * Cache for tracking PayloadEnvelopeInput instances, keyed by beacon block root.
23
+ *
24
+ * Created during block import when a block is processed.
25
+ * Pruned on finalization and after payload is written to DB.
26
+ */
27
+ export class SeenPayloadEnvelopeInput {
28
+ private readonly chainEvents: ChainEventEmitter;
29
+ private readonly signal: AbortSignal;
30
+ private readonly serializedCache: SerializedCache;
31
+ private readonly metrics: Metrics | null;
32
+ private readonly logger?: Logger;
33
+ private payloadInputs = new Map<RootHex, PayloadEnvelopeInput>();
34
+
35
+ constructor({chainEvents, signal, serializedCache, metrics, logger}: SeenPayloadEnvelopeInputModules) {
36
+ this.chainEvents = chainEvents;
37
+ this.signal = signal;
38
+ this.serializedCache = serializedCache;
39
+ this.metrics = metrics;
40
+ this.logger = logger;
41
+
42
+ if (metrics) {
43
+ metrics.seenCache.payloadEnvelopeInput.count.addCollect(() => {
44
+ metrics.seenCache.payloadEnvelopeInput.count.set(this.payloadInputs.size);
45
+ metrics.seenCache.payloadEnvelopeInput.serializedObjectRefs.set(
46
+ Array.from(this.payloadInputs.values()).reduce(
47
+ (count, payloadInput) => count + payloadInput.getSerializedCacheKeys().length,
48
+ 0
49
+ )
50
+ );
51
+ });
52
+ }
53
+
54
+ this.chainEvents.on(ChainEvent.forkChoiceFinalized, this.onFinalized);
55
+ this.signal.addEventListener("abort", () => {
56
+ this.chainEvents.off(ChainEvent.forkChoiceFinalized, this.onFinalized);
57
+ });
58
+ }
59
+
60
+ private onFinalized = (checkpoint: CheckpointWithHex): void => {
61
+ // Prune all entries with slot < finalized slot
62
+ const finalizedSlot = computeStartSlotAtEpoch(checkpoint.epoch);
63
+ let deletedCount = 0;
64
+ for (const [, input] of this.payloadInputs) {
65
+ if (input.slot < finalizedSlot) {
66
+ this.evictPayloadInput(input);
67
+ deletedCount++;
68
+ }
69
+ }
70
+ this.logger?.debug("SeenPayloadEnvelopeInput.onFinalized deleted cached entries", {deletedCount});
71
+ };
72
+
73
+ add(props: CreateFromBlockProps): PayloadEnvelopeInput {
74
+ if (this.payloadInputs.has(props.blockRootHex)) {
75
+ throw new Error(`PayloadEnvelopeInput already exists for block ${props.blockRootHex}`);
76
+ }
77
+ const input = PayloadEnvelopeInput.createFromBlock(props);
78
+ this.payloadInputs.set(props.blockRootHex, input);
79
+ this.metrics?.seenCache.payloadEnvelopeInput.created.inc();
80
+ return input;
81
+ }
82
+
83
+ get(blockRootHex: RootHex): PayloadEnvelopeInput | undefined {
84
+ return this.payloadInputs.get(blockRootHex);
85
+ }
86
+
87
+ has(blockRootHex: RootHex): boolean {
88
+ return this.payloadInputs.has(blockRootHex);
89
+ }
90
+
91
+ prune(blockRootHex: RootHex): void {
92
+ const payloadInput = this.payloadInputs.get(blockRootHex);
93
+ if (payloadInput) {
94
+ this.evictPayloadInput(payloadInput);
95
+ }
96
+ }
97
+
98
+ size(): number {
99
+ return this.payloadInputs.size;
100
+ }
101
+
102
+ private evictPayloadInput(payloadInput: PayloadEnvelopeInput): void {
103
+ this.serializedCache.delete(payloadInput.getSerializedCacheKeys());
104
+ this.payloadInputs.delete(payloadInput.blockRootHex);
105
+ }
106
+ }
@@ -1,6 +1,6 @@
1
1
  import {SLOTS_PER_EPOCH} from "@lodestar/params";
2
2
  import {Epoch, phase0, ssz} from "@lodestar/types";
3
- import {MapDef} from "@lodestar/utils";
3
+ import {MapDef, byteArrayEquals} from "@lodestar/utils";
4
4
  import {IBeaconDb} from "../../../db/interface.js";
5
5
  import {
6
6
  getLastProcessedSlotFromBeaconStateSerialized,
@@ -14,8 +14,8 @@ import {CPStateDatastore, DatastoreKey} from "./types.js";
14
14
  export class DbCPStateDatastore implements CPStateDatastore {
15
15
  constructor(private readonly db: IBeaconDb) {}
16
16
 
17
- async write(cpKey: phase0.Checkpoint, stateBytes: Uint8Array): Promise<DatastoreKey> {
18
- const serializedCheckpoint = checkpointToDatastoreKey(cpKey);
17
+ async write(cpKey: phase0.Checkpoint, stateBytes: Uint8Array, payloadPresent: boolean): Promise<DatastoreKey> {
18
+ const serializedCheckpoint = checkpointToDatastoreKey(cpKey, payloadPresent);
19
19
  await this.db.checkpointState.putBinary(serializedCheckpoint, stateBytes);
20
20
  return serializedCheckpoint;
21
21
  }
@@ -40,18 +40,30 @@ export class DbCPStateDatastore implements CPStateDatastore {
40
40
  }
41
41
  }
42
42
 
43
+ function extractCheckpointBytes(key: DatastoreKey): Uint8Array {
44
+ const fixedSize = ssz.phase0.Checkpoint.minSize;
45
+ return key.subarray(0, fixedSize);
46
+ }
47
+
43
48
  export function datastoreKeyToCheckpoint(key: DatastoreKey): phase0.Checkpoint {
44
- return ssz.phase0.Checkpoint.deserialize(key);
49
+ return ssz.phase0.Checkpoint.deserialize(extractCheckpointBytes(key));
50
+ }
51
+
52
+ export function checkpointToDatastoreKey(cp: phase0.Checkpoint, payloadPresent: boolean): DatastoreKey {
53
+ const cpBytes = ssz.phase0.Checkpoint.serialize(cp);
54
+ const key = new Uint8Array(cpBytes.length + 1);
55
+ key.set(cpBytes);
56
+ key[cpBytes.length] = payloadPresent ? 1 : 0;
57
+ return key;
45
58
  }
46
59
 
47
- export function checkpointToDatastoreKey(cp: phase0.Checkpoint): DatastoreKey {
48
- return ssz.phase0.Checkpoint.serialize(cp);
60
+ function isPayloadCheckpointState(key: DatastoreKey): boolean {
61
+ return key.at(-1) === 1;
49
62
  }
50
63
 
51
64
  /**
52
- * Get the latest safe checkpoint state the node can use to boot from
53
- * - it should be the checkpoint state that's unique in its epoch
54
- * - its last processed block slot should be at epoch boundary or last slot of previous epoch
65
+ * Get the latest "safe" checkpoint state the node can use to boot from
66
+ * - its last processed block slot should be at epoch boundary (CRCS) or last slot of previous epoch (PRCS)
55
67
  * - state slot should be at epoch boundary
56
68
  * - state slot should be equal to epoch * SLOTS_PER_EPOCH
57
69
  *
@@ -70,9 +82,20 @@ export async function getLatestSafeDatastoreKey(
70
82
 
71
83
  const dataStoreKeyByEpoch: Map<Epoch, DatastoreKey> = new Map();
72
84
  for (const [epoch, keys] of checkpointsByEpoch.entries()) {
73
- // only consider epochs with a single checkpoint to avoid ambiguity from forks
74
85
  if (keys.length === 1) {
86
+ // PRCS (skipped slot) or CRCS and no payloadPresent
87
+ // Pre-gloas always fall into this case
75
88
  dataStoreKeyByEpoch.set(epoch, keys[0]);
89
+ } else if (keys.length === 2) {
90
+ // CRCS without payload and CRCS with payload
91
+ // ie Two keys for the same checkpoint with different payloadPresent suffix (FULL/EMPTY)
92
+ // TODO GLOAS: Here we pick FULL key, there is a chance that payload is orphaned hence we not be able to sync
93
+ const cp0 = extractCheckpointBytes(keys[0]);
94
+ const cp1 = extractCheckpointBytes(keys[1]);
95
+ if (byteArrayEquals(cp0, cp1)) {
96
+ const fullKey = isPayloadCheckpointState(keys[0]) ? keys[0] : keys[1];
97
+ dataStoreKeyByEpoch.set(epoch, fullKey);
98
+ }
76
99
  }
77
100
  }
78
101
 
@@ -1,12 +1,13 @@
1
1
  import path from "node:path";
2
- import {phase0, ssz} from "@lodestar/types";
2
+ import {phase0} from "@lodestar/types";
3
3
  import {fromHex, toHex} from "@lodestar/utils";
4
4
  import {ensureDir, readFile, readFileNames, removeFile, writeIfNotExist} from "../../../util/file.js";
5
- import {getLatestSafeDatastoreKey} from "./db.js";
5
+ import {checkpointToDatastoreKey, getLatestSafeDatastoreKey} from "./db.js";
6
6
  import {CPStateDatastore, DatastoreKey} from "./types.js";
7
7
 
8
8
  const CHECKPOINT_STATES_FOLDER = "checkpoint_states";
9
- const CHECKPOINT_FILE_NAME_LENGTH = 82;
9
+ /** 41 bytes (40 checkpoint + 1 payloadPresent) = 82 hex chars + "0x" prefix = 84 */
10
+ const CHECKPOINT_FILE_NAME_LENGTH = 84;
10
11
 
11
12
  /**
12
13
  * Implementation of CPStateDatastore using file system, this is beneficial for debugging.
@@ -28,8 +29,8 @@ export class FileCPStateDatastore implements CPStateDatastore {
28
29
  }
29
30
  }
30
31
 
31
- async write(cpKey: phase0.Checkpoint, stateBytes: Uint8Array): Promise<DatastoreKey> {
32
- const serializedCheckpoint = ssz.phase0.Checkpoint.serialize(cpKey);
32
+ async write(cpKey: phase0.Checkpoint, stateBytes: Uint8Array, payloadPresent: boolean): Promise<DatastoreKey> {
33
+ const serializedCheckpoint = checkpointToDatastoreKey(cpKey, payloadPresent);
33
34
  const filePath = path.join(this.folderPath, toHex(serializedCheckpoint));
34
35
  await writeIfNotExist(filePath, stateBytes);
35
36
  return serializedCheckpoint;
@@ -1,11 +1,12 @@
1
1
  import {phase0} from "@lodestar/types";
2
2
 
3
- // With db implementation, persistedKey is serialized data of a checkpoint
3
+ // With db implementation, persistedKey is serialized data of a checkpoint + 1
4
+ // ie a fixed size of `ssz.phase0.Checkpoint.minSize + 1`
4
5
  export type DatastoreKey = Uint8Array;
5
6
 
6
7
  // Make this generic to support testing
7
8
  export interface CPStateDatastore {
8
- write: (cpKey: phase0.Checkpoint, stateBytes: Uint8Array) => Promise<DatastoreKey>;
9
+ write: (cpKey: phase0.Checkpoint, stateBytes: Uint8Array, payloadPresent: boolean) => Promise<DatastoreKey>;
9
10
  remove: (key: DatastoreKey) => Promise<void>;
10
11
  read: (key: DatastoreKey) => Promise<Uint8Array | null>;
11
12
  readLatestSafe: () => Promise<Uint8Array | null>;
@@ -20,6 +20,11 @@ export type FIFOBlockStateCacheOpts = {
20
20
  * clock slot
21
21
  */
22
22
  export const DEFAULT_MAX_BLOCK_STATES = 64;
23
+ /**
24
+ * For Gloas (ePBS), each block can have two states: block state and payload state.
25
+ * Double the cache size to maintain the same effective block depth.
26
+ */
27
+ export const DEFAULT_MAX_BLOCK_STATES_GLOAS = 128;
23
28
 
24
29
  /**
25
30
  * New implementation of BlockStateCache that keeps the most recent n states consistently
@@ -41,10 +46,7 @@ export const DEFAULT_MAX_BLOCK_STATES = 64;
41
46
  * The maintained key order would be: 11 -> 13 -> 12 -> 10, and state 10 will be pruned first.
42
47
  */
43
48
  export class FIFOBlockStateCache implements BlockStateCache {
44
- /**
45
- * Max number of states allowed in the cache
46
- */
47
- readonly maxStates: number;
49
+ private maxStates: number;
48
50
 
49
51
  private readonly cache: MapTracker<string, CachedBeaconStateAllForks>;
50
52
  /**
@@ -170,6 +172,10 @@ export class FIFOBlockStateCache implements BlockStateCache {
170
172
  }
171
173
  }
172
174
 
175
+ upgradeToGloas(): void {
176
+ this.maxStates = DEFAULT_MAX_BLOCK_STATES_GLOAS;
177
+ }
178
+
173
179
  /**
174
180
  * No need for this implementation
175
181
  * This is only to conform to the old api