@lodestar/beacon-node 1.43.0-dev.aef3645690 → 1.43.0-dev.b7d69444c9

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 (227) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +1 -4
  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 +1 -4
  9. package/lib/api/impl/validator/index.js.map +1 -1
  10. package/lib/chain/GetBlobsTracker.d.ts +1 -1
  11. package/lib/chain/GetBlobsTracker.d.ts.map +1 -1
  12. package/lib/chain/GetBlobsTracker.js +1 -2
  13. package/lib/chain/GetBlobsTracker.js.map +1 -1
  14. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  15. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  16. package/lib/chain/archiveStore/interface.d.ts +4 -4
  17. package/lib/chain/archiveStore/interface.d.ts.map +1 -1
  18. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.d.ts +4 -4
  19. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.d.ts.map +1 -1
  20. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js +2 -4
  21. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js.map +1 -1
  22. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts +2 -2
  23. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
  24. package/lib/chain/archiveStore/utils/archiveBlocks.js +110 -58
  25. package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
  26. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  27. package/lib/chain/blocks/importBlock.js +27 -35
  28. package/lib/chain/blocks/importBlock.js.map +1 -1
  29. package/lib/chain/blocks/importExecutionPayload.d.ts +15 -14
  30. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  31. package/lib/chain/blocks/importExecutionPayload.js +63 -85
  32. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  33. package/lib/chain/blocks/index.d.ts.map +1 -1
  34. package/lib/chain/blocks/index.js +1 -2
  35. package/lib/chain/blocks/index.js.map +1 -1
  36. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +3 -0
  37. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
  38. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +20 -0
  39. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
  40. package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts +5 -0
  41. package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts.map +1 -1
  42. package/lib/chain/blocks/payloadEnvelopeProcessor.js +6 -4
  43. package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -1
  44. package/lib/chain/blocks/types.d.ts +15 -21
  45. package/lib/chain/blocks/types.d.ts.map +1 -1
  46. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +24 -0
  47. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -0
  48. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +76 -0
  49. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -0
  50. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts +14 -0
  51. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -0
  52. package/lib/chain/blocks/verifyPayloadsDataAvailability.js +25 -0
  53. package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -0
  54. package/lib/chain/blocks/writePayloadEnvelopeInputToDb.d.ts +1 -1
  55. package/lib/chain/blocks/writePayloadEnvelopeInputToDb.d.ts.map +1 -1
  56. package/lib/chain/blocks/writePayloadEnvelopeInputToDb.js +2 -11
  57. package/lib/chain/blocks/writePayloadEnvelopeInputToDb.js.map +1 -1
  58. package/lib/chain/chain.d.ts +5 -4
  59. package/lib/chain/chain.d.ts.map +1 -1
  60. package/lib/chain/chain.js +26 -39
  61. package/lib/chain/chain.js.map +1 -1
  62. package/lib/chain/emitter.d.ts +16 -4
  63. package/lib/chain/emitter.d.ts.map +1 -1
  64. package/lib/chain/emitter.js +5 -0
  65. package/lib/chain/emitter.js.map +1 -1
  66. package/lib/chain/errors/attestationError.d.ts +8 -1
  67. package/lib/chain/errors/attestationError.d.ts.map +1 -1
  68. package/lib/chain/errors/attestationError.js +4 -0
  69. package/lib/chain/errors/attestationError.js.map +1 -1
  70. package/lib/chain/errors/executionPayloadBid.d.ts +5 -0
  71. package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
  72. package/lib/chain/errors/executionPayloadBid.js +1 -0
  73. package/lib/chain/errors/executionPayloadBid.js.map +1 -1
  74. package/lib/chain/errors/executionPayloadEnvelope.d.ts +5 -0
  75. package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
  76. package/lib/chain/errors/executionPayloadEnvelope.js +1 -0
  77. package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
  78. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  79. package/lib/chain/forkChoice/index.js +11 -15
  80. package/lib/chain/forkChoice/index.js.map +1 -1
  81. package/lib/chain/interface.d.ts +4 -3
  82. package/lib/chain/interface.d.ts.map +1 -1
  83. package/lib/chain/interface.js.map +1 -1
  84. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  85. package/lib/chain/prepareNextSlot.js +48 -22
  86. package/lib/chain/prepareNextSlot.js.map +1 -1
  87. package/lib/chain/produceBlock/computeNewStateRoot.d.ts +3 -9
  88. package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
  89. package/lib/chain/produceBlock/computeNewStateRoot.js +5 -32
  90. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  91. package/lib/chain/produceBlock/produceBlockBody.d.ts +4 -8
  92. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  93. package/lib/chain/produceBlock/produceBlockBody.js +48 -23
  94. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  95. package/lib/chain/regen/errors.d.ts +1 -11
  96. package/lib/chain/regen/errors.d.ts.map +1 -1
  97. package/lib/chain/regen/errors.js +0 -2
  98. package/lib/chain/regen/errors.js.map +1 -1
  99. package/lib/chain/regen/interface.d.ts +6 -12
  100. package/lib/chain/regen/interface.d.ts.map +1 -1
  101. package/lib/chain/regen/queued.d.ts +6 -11
  102. package/lib/chain/regen/queued.d.ts.map +1 -1
  103. package/lib/chain/regen/queued.js +8 -40
  104. package/lib/chain/regen/queued.js.map +1 -1
  105. package/lib/chain/regen/regen.d.ts +0 -5
  106. package/lib/chain/regen/regen.d.ts.map +1 -1
  107. package/lib/chain/regen/regen.js +7 -34
  108. package/lib/chain/regen/regen.js.map +1 -1
  109. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +11 -4
  110. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  111. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +20 -18
  112. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  113. package/lib/chain/stateCache/datastore/db.d.ts +5 -4
  114. package/lib/chain/stateCache/datastore/db.d.ts.map +1 -1
  115. package/lib/chain/stateCache/datastore/db.js +10 -32
  116. package/lib/chain/stateCache/datastore/db.js.map +1 -1
  117. package/lib/chain/stateCache/datastore/file.d.ts +1 -1
  118. package/lib/chain/stateCache/datastore/file.d.ts.map +1 -1
  119. package/lib/chain/stateCache/datastore/file.js +5 -5
  120. package/lib/chain/stateCache/datastore/file.js.map +1 -1
  121. package/lib/chain/stateCache/datastore/types.d.ts +1 -1
  122. package/lib/chain/stateCache/datastore/types.d.ts.map +1 -1
  123. package/lib/chain/stateCache/fifoBlockStateCache.d.ts +1 -7
  124. package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
  125. package/lib/chain/stateCache/fifoBlockStateCache.js +0 -8
  126. package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
  127. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +13 -30
  128. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  129. package/lib/chain/stateCache/persistentCheckpointsCache.js +120 -216
  130. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  131. package/lib/chain/stateCache/types.d.ts +8 -15
  132. package/lib/chain/stateCache/types.d.ts.map +1 -1
  133. package/lib/chain/stateCache/types.js.map +1 -1
  134. package/lib/chain/validation/aggregateAndProof.js +12 -0
  135. package/lib/chain/validation/aggregateAndProof.js.map +1 -1
  136. package/lib/chain/validation/attestation.d.ts.map +1 -1
  137. package/lib/chain/validation/attestation.js +12 -0
  138. package/lib/chain/validation/attestation.js.map +1 -1
  139. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  140. package/lib/chain/validation/executionPayloadBid.js +13 -1
  141. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  142. package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
  143. package/lib/chain/validation/executionPayloadEnvelope.js +21 -11
  144. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  145. package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
  146. package/lib/chain/validation/payloadAttestationMessage.js +4 -3
  147. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  148. package/lib/db/repositories/executionPayloadEnvelopeArchive.js +1 -1
  149. package/lib/db/repositories/executionPayloadEnvelopeArchive.js.map +1 -1
  150. package/lib/execution/engine/http.d.ts.map +1 -1
  151. package/lib/execution/engine/http.js +21 -14
  152. package/lib/execution/engine/http.js.map +1 -1
  153. package/lib/execution/engine/interface.d.ts +1 -0
  154. package/lib/execution/engine/interface.d.ts.map +1 -1
  155. package/lib/execution/engine/mock.d.ts.map +1 -1
  156. package/lib/execution/engine/mock.js +6 -0
  157. package/lib/execution/engine/mock.js.map +1 -1
  158. package/lib/execution/engine/types.d.ts +20 -0
  159. package/lib/execution/engine/types.d.ts.map +1 -1
  160. package/lib/execution/engine/types.js +18 -0
  161. package/lib/execution/engine/types.js.map +1 -1
  162. package/lib/network/gossip/topic.d.ts +23 -2
  163. package/lib/network/gossip/topic.d.ts.map +1 -1
  164. package/lib/network/network.js +1 -1
  165. package/lib/network/network.js.map +1 -1
  166. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  167. package/lib/network/processor/gossipHandlers.js +22 -6
  168. package/lib/network/processor/gossipHandlers.js.map +1 -1
  169. package/lib/node/nodejs.d.ts.map +1 -1
  170. package/lib/node/nodejs.js +4 -2
  171. package/lib/node/nodejs.js.map +1 -1
  172. package/lib/util/sszBytes.d.ts.map +1 -1
  173. package/lib/util/sszBytes.js +16 -3
  174. package/lib/util/sszBytes.js.map +1 -1
  175. package/package.json +16 -16
  176. package/src/api/impl/beacon/blocks/index.ts +1 -4
  177. package/src/api/impl/beacon/state/utils.ts +2 -2
  178. package/src/api/impl/validator/index.ts +3 -6
  179. package/src/chain/GetBlobsTracker.ts +1 -2
  180. package/src/chain/archiveStore/archiveStore.ts +5 -5
  181. package/src/chain/archiveStore/interface.ts +4 -4
  182. package/src/chain/archiveStore/strategies/frequencyStateArchiveStrategy.ts +6 -8
  183. package/src/chain/archiveStore/utils/archiveBlocks.ts +153 -94
  184. package/src/chain/blocks/importBlock.ts +26 -39
  185. package/src/chain/blocks/importExecutionPayload.ts +77 -103
  186. package/src/chain/blocks/index.ts +1 -2
  187. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +27 -0
  188. package/src/chain/blocks/payloadEnvelopeProcessor.ts +6 -5
  189. package/src/chain/blocks/types.ts +15 -26
  190. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +129 -0
  191. package/src/chain/blocks/verifyPayloadsDataAvailability.ts +38 -0
  192. package/src/chain/blocks/writePayloadEnvelopeInputToDb.ts +9 -18
  193. package/src/chain/chain.ts +37 -62
  194. package/src/chain/emitter.ts +15 -3
  195. package/src/chain/errors/attestationError.ts +6 -1
  196. package/src/chain/errors/executionPayloadBid.ts +6 -0
  197. package/src/chain/errors/executionPayloadEnvelope.ts +6 -0
  198. package/src/chain/forkChoice/index.ts +8 -20
  199. package/src/chain/interface.ts +4 -2
  200. package/src/chain/prepareNextSlot.ts +62 -23
  201. package/src/chain/produceBlock/computeNewStateRoot.ts +6 -43
  202. package/src/chain/produceBlock/produceBlockBody.ts +66 -26
  203. package/src/chain/regen/errors.ts +1 -6
  204. package/src/chain/regen/interface.ts +6 -12
  205. package/src/chain/regen/queued.ts +12 -48
  206. package/src/chain/regen/regen.ts +8 -36
  207. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +22 -20
  208. package/src/chain/stateCache/datastore/db.ts +10 -33
  209. package/src/chain/stateCache/datastore/file.ts +5 -6
  210. package/src/chain/stateCache/datastore/types.ts +2 -3
  211. package/src/chain/stateCache/fifoBlockStateCache.ts +1 -10
  212. package/src/chain/stateCache/persistentCheckpointsCache.ts +139 -247
  213. package/src/chain/stateCache/types.ts +8 -14
  214. package/src/chain/validation/aggregateAndProof.ts +13 -0
  215. package/src/chain/validation/attestation.ts +13 -0
  216. package/src/chain/validation/executionPayloadBid.ts +14 -0
  217. package/src/chain/validation/executionPayloadEnvelope.ts +22 -12
  218. package/src/chain/validation/payloadAttestationMessage.ts +5 -3
  219. package/src/db/repositories/executionPayloadEnvelopeArchive.ts +1 -1
  220. package/src/execution/engine/http.ts +21 -14
  221. package/src/execution/engine/interface.ts +1 -0
  222. package/src/execution/engine/mock.ts +8 -1
  223. package/src/execution/engine/types.ts +41 -0
  224. package/src/network/network.ts +1 -1
  225. package/src/network/processor/gossipHandlers.ts +26 -10
  226. package/src/node/nodejs.ts +4 -2
  227. package/src/util/sszBytes.ts +21 -3
@@ -1,12 +1,10 @@
1
1
  import {
2
2
  DataAvailabilityStatus,
3
3
  ExecutionPayloadStatus,
4
- G2_POINT_AT_INFINITY,
5
4
  IBeaconStateView,
6
- IBeaconStateViewGloas,
7
5
  StateHashTreeRootSource,
8
6
  } from "@lodestar/state-transition";
9
- import {BeaconBlock, BlindedBeaconBlock, Gwei, Root, gloas} from "@lodestar/types";
7
+ import {BeaconBlock, BlindedBeaconBlock, Gwei, Root} from "@lodestar/types";
10
8
  import {ZERO_HASH} from "../../constants/index.js";
11
9
  import {Metrics} from "../../metrics/index.js";
12
10
 
@@ -19,11 +17,11 @@ export function computeNewStateRoot(
19
17
  metrics: Metrics | null,
20
18
  state: IBeaconStateView,
21
19
  block: BeaconBlock | BlindedBeaconBlock
22
- ): {newStateRoot: Root; proposerReward: Gwei; postBlockState: IBeaconStateView} {
20
+ ): {newStateRoot: Root; proposerReward: Gwei; postState: IBeaconStateView} {
23
21
  // Set signature to zero to re-use stateTransition() function which requires the SignedBeaconBlock type
24
22
  const blockEmptySig = {message: block, signature: ZERO_HASH};
25
23
 
26
- const postBlockState = state.stateTransition(
24
+ const postState = state.stateTransition(
27
25
  blockEmptySig,
28
26
  {
29
27
  // ExecutionPayloadStatus.valid: Assume payload valid, it has been produced by a trusted EL
@@ -42,49 +40,14 @@ export function computeNewStateRoot(
42
40
  {metrics}
43
41
  );
44
42
 
45
- const {attestations, syncAggregate, slashing} = postBlockState.proposerRewards;
43
+ const {attestations, syncAggregate, slashing} = postState.proposerRewards;
46
44
  const proposerReward = BigInt(attestations + syncAggregate + slashing);
47
45
 
48
46
  const hashTreeRootTimer = metrics?.stateHashTreeRootTime.startTimer({
49
47
  source: StateHashTreeRootSource.computeNewStateRoot,
50
48
  });
51
- const newStateRoot = postBlockState.hashTreeRoot();
49
+ const newStateRoot = postState.hashTreeRoot();
52
50
  hashTreeRootTimer?.();
53
51
 
54
- return {newStateRoot, proposerReward, postBlockState};
55
- }
56
-
57
- /**
58
- * Compute the state root after processing an execution payload envelope.
59
- * Similar to `computeNewStateRoot` but for payload envelope processing.
60
- *
61
- */
62
- export function computePayloadEnvelopeStateRoot(
63
- metrics: Metrics | null,
64
- postBlockState: IBeaconStateViewGloas,
65
- envelope: gloas.ExecutionPayloadEnvelope
66
- ): Root {
67
- const signedEnvelope: gloas.SignedExecutionPayloadEnvelope = {
68
- message: envelope,
69
- signature: G2_POINT_AT_INFINITY,
70
- };
71
-
72
- const processEnvelopeTimer = metrics?.blockPayload.executionPayloadEnvelopeProcessingTime.startTimer();
73
- const postPayloadState = postBlockState.processExecutionPayloadEnvelope(signedEnvelope, {
74
- // Signature is zero-ed (G2_POINT_AT_INFINITY), skip verification
75
- verifySignature: false,
76
- // State root is being computed here, the envelope doesn't have it yet
77
- verifyStateRoot: false,
78
- // Preserve cache in source state, since the resulting state is not added to the state cache
79
- dontTransferCache: true,
80
- });
81
- processEnvelopeTimer?.();
82
-
83
- const hashTreeRootTimer = metrics?.stateHashTreeRootTime.startTimer({
84
- source: StateHashTreeRootSource.computePayloadEnvelopeStateRoot,
85
- });
86
- const stateRoot = postPayloadState.hashTreeRoot();
87
- hashTreeRootTimer?.();
88
-
89
- return stateRoot;
52
+ return {newStateRoot, proposerReward, postState};
90
53
  }
@@ -19,7 +19,6 @@ import {
19
19
  IBeaconStateView,
20
20
  type IBeaconStateViewBellatrix,
21
21
  computeTimeAtSlot,
22
- isParentBlockFull,
23
22
  isStatePostBellatrix,
24
23
  isStatePostCapella,
25
24
  isStatePostGloas,
@@ -47,8 +46,9 @@ import {
47
46
  electra,
48
47
  fulu,
49
48
  gloas,
49
+ ssz,
50
50
  } from "@lodestar/types";
51
- import {Logger, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
51
+ import {Logger, byteArrayEquals, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
52
52
  import {ZERO_HASH_HEX} from "../../constants/index.js";
53
53
  import {numToQuantity} from "../../execution/engine/utils.js";
54
54
  import {
@@ -111,12 +111,6 @@ export type ProduceFullGloas = {
111
111
  executionRequests: electra.ExecutionRequests;
112
112
  blobsBundle: BlobsBundle<ForkPostGloas>;
113
113
  cells: fulu.Cell[][];
114
- /**
115
- * Cached payload envelope state root computed during block production.
116
- * This is the state root after running `processExecutionPayloadEnvelope` on the
117
- * post-block state, and later used to construct the `ExecutionPayloadEnvelope`.
118
- */
119
- payloadEnvelopeStateRoot: Root;
120
114
  };
121
115
  export type ProduceFullFulu = {
122
116
  type: BlockType.Full;
@@ -220,15 +214,24 @@ export async function produceBlockBody<T extends BlockType>(
220
214
  });
221
215
 
222
216
  // Get execution payload from EL
217
+ const isExtendingPayload = this.forkChoice.shouldExtendPayload(toRootHex(parentBlockRoot));
218
+ const parentBlockHash = isExtendingPayload
219
+ ? currentState.latestExecutionPayloadBid.blockHash
220
+ : currentState.latestExecutionPayloadBid.parentBlockHash;
221
+ const parentExecutionRequests = isExtendingPayload
222
+ ? await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot)
223
+ : ssz.electra.ExecutionRequests.defaultValue();
223
224
  const prepareRes = await prepareExecutionPayload(
224
225
  this,
225
226
  this.logger,
226
227
  fork,
227
228
  parentBlockRoot,
229
+ parentBlockHash,
228
230
  safeBlockHash,
229
231
  finalizedBlockHash ?? ZERO_HASH_HEX,
230
232
  currentState,
231
- feeRecipient
233
+ feeRecipient,
234
+ parentExecutionRequests
232
235
  );
233
236
 
234
237
  const {prepType, payloadId} = prepareRes;
@@ -261,8 +264,8 @@ export async function produceBlockBody<T extends BlockType>(
261
264
 
262
265
  // Create self-build execution payload bid
263
266
  const bid: gloas.ExecutionPayloadBid = {
264
- parentBlockHash: currentState.latestBlockHash,
265
- parentBlockRoot: parentBlockRoot,
267
+ parentBlockHash,
268
+ parentBlockRoot,
266
269
  blockHash: executionPayload.blockHash,
267
270
  prevRandao: currentState.getRandaoMix(currentState.epoch),
268
271
  feeRecipient: executionPayload.feeRecipient,
@@ -272,6 +275,7 @@ export async function produceBlockBody<T extends BlockType>(
272
275
  value: 0,
273
276
  executionPayment: 0,
274
277
  blobKzgCommitments: blobsBundle.commitments,
278
+ executionRequestsRoot: ssz.electra.ExecutionRequests.hashTreeRoot(executionRequests),
275
279
  };
276
280
  const signedBid: gloas.SignedExecutionPayloadBid = {
277
281
  message: bid,
@@ -283,6 +287,7 @@ export async function produceBlockBody<T extends BlockType>(
283
287
  gloasBody.signedExecutionPayloadBid = signedBid;
284
288
  // TODO GLOAS: Get payload attestations from pool for previous slot
285
289
  gloasBody.payloadAttestations = [];
290
+ gloasBody.parentExecutionRequests = parentExecutionRequests;
286
291
  blockBody = gloasBody as AssembledBodyType<T>;
287
292
 
288
293
  // Store execution payload data required to construct execution payload envelope later
@@ -340,6 +345,7 @@ export async function produceBlockBody<T extends BlockType>(
340
345
  this.logger,
341
346
  fork,
342
347
  parentBlockRoot,
348
+ currentState.latestExecutionPayloadHeader.blockHash,
343
349
  safeBlockHash,
344
350
  finalizedBlockHash ?? ZERO_HASH_HEX,
345
351
  currentState,
@@ -448,6 +454,7 @@ export async function produceBlockBody<T extends BlockType>(
448
454
  this.logger,
449
455
  fork,
450
456
  parentBlockRoot,
457
+ currentState.latestExecutionPayloadHeader.blockHash,
451
458
  safeBlockHash,
452
459
  finalizedBlockHash ?? ZERO_HASH_HEX,
453
460
  currentState,
@@ -613,17 +620,18 @@ export async function prepareExecutionPayload(
613
620
  logger: Logger,
614
621
  fork: ForkPostBellatrix,
615
622
  parentBlockRoot: Root,
623
+ parentBlockHash: Bytes32,
616
624
  safeBlockHash: RootHex,
617
625
  finalizedBlockHash: RootHex,
618
626
  state: IBeaconStateViewBellatrix,
619
- suggestedFeeRecipient: string
627
+ suggestedFeeRecipient: string,
628
+ parentExecutionRequests?: electra.ExecutionRequests
620
629
  ): Promise<{prepType: PayloadPreparationType; payloadId: PayloadId}> {
621
- const parentHash = state.latestBlockHash;
622
630
  const timestamp = computeTimeAtSlot(chain.config, state.slot, state.genesisTime);
623
631
  const prevRandao = state.getRandaoMix(state.epoch);
624
632
 
625
633
  const payloadIdCached = chain.executionEngine.payloadIdCache.get({
626
- headBlockHash: toRootHex(parentHash),
634
+ headBlockHash: toRootHex(parentBlockHash),
627
635
  finalizedBlockHash,
628
636
  timestamp: numToQuantity(timestamp),
629
637
  prevRandao: toHex(prevRandao),
@@ -652,12 +660,14 @@ export async function prepareExecutionPayload(
652
660
  prepareState: state,
653
661
  prepareSlot: state.slot,
654
662
  parentBlockRoot,
663
+ parentBlockHash,
655
664
  feeRecipient: suggestedFeeRecipient,
665
+ parentExecutionRequests,
656
666
  });
657
667
 
658
668
  payloadId = await chain.executionEngine.notifyForkchoiceUpdate(
659
669
  fork,
660
- toRootHex(parentHash),
670
+ toRootHex(parentBlockHash),
661
671
  safeBlockHash,
662
672
  finalizedBlockHash,
663
673
  attributes
@@ -709,20 +719,33 @@ export function getPayloadAttributesForSSE(
709
719
  prepareState,
710
720
  prepareSlot,
711
721
  parentBlockRoot,
722
+ parentBlockHash,
712
723
  feeRecipient,
713
- }: {prepareState: IBeaconStateViewBellatrix; prepareSlot: Slot; parentBlockRoot: Root; feeRecipient: string}
724
+ parentExecutionRequests,
725
+ }: {
726
+ prepareState: IBeaconStateViewBellatrix;
727
+ prepareSlot: Slot;
728
+ parentBlockRoot: Root;
729
+ parentBlockHash: Bytes32;
730
+ feeRecipient: string;
731
+ parentExecutionRequests?: electra.ExecutionRequests;
732
+ }
714
733
  ): SSEPayloadAttributes {
715
- const parentHash = prepareState.latestBlockHash;
716
734
  const payloadAttributes = preparePayloadAttributes(fork, chain, {
717
735
  prepareState,
718
736
  prepareSlot,
719
737
  parentBlockRoot,
738
+ parentBlockHash,
720
739
  feeRecipient,
740
+ parentExecutionRequests,
721
741
  });
722
742
 
723
743
  let parentBlockNumber: number;
724
744
  if (isForkPostGloas(fork)) {
725
- const parentBlock = chain.forkChoice.getBlockHexAndBlockHash(toRootHex(parentBlockRoot), toRootHex(parentHash));
745
+ const parentBlock = chain.forkChoice.getBlockHexAndBlockHash(
746
+ toRootHex(parentBlockRoot),
747
+ toRootHex(parentBlockHash)
748
+ );
726
749
  if (parentBlock?.executionPayloadBlockHash == null) {
727
750
  throw Error(`Parent block not found in fork choice root=${toRootHex(parentBlockRoot)}`);
728
751
  }
@@ -736,7 +759,7 @@ export function getPayloadAttributesForSSE(
736
759
  proposalSlot: prepareSlot,
737
760
  parentBlockNumber,
738
761
  parentBlockRoot,
739
- parentBlockHash: parentHash,
762
+ parentBlockHash,
740
763
  payloadAttributes,
741
764
  };
742
765
  return ssePayloadAttributes;
@@ -751,12 +774,16 @@ function preparePayloadAttributes(
751
774
  prepareState,
752
775
  prepareSlot,
753
776
  parentBlockRoot,
777
+ parentBlockHash,
754
778
  feeRecipient,
779
+ parentExecutionRequests,
755
780
  }: {
756
781
  prepareState: IBeaconStateViewBellatrix;
757
782
  prepareSlot: Slot;
758
783
  parentBlockRoot: Root;
784
+ parentBlockHash: Bytes32;
759
785
  feeRecipient: string;
786
+ parentExecutionRequests?: electra.ExecutionRequests;
760
787
  }
761
788
  ): SSEPayloadAttributes["payloadAttributes"] {
762
789
  const timestamp = computeTimeAtSlot(chain.config, prepareSlot, prepareState.genesisTime);
@@ -772,13 +799,22 @@ function preparePayloadAttributes(
772
799
  throw new Error("Expected Capella state for withdrawals");
773
800
  }
774
801
 
775
- if (isStatePostGloas(prepareState) && !isParentBlockFull(prepareState)) {
776
- // When the parent block is empty, state.payloadExpectedWithdrawals holds a batch
777
- // already deducted from CL balances but never credited on the EL (the envelope
778
- // was not delivered). The next payload must carry those same withdrawals to
779
- // restore CL/EL consistency, otherwise validators permanently lose that balance.
780
- (payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
781
- prepareState.payloadExpectedWithdrawals;
802
+ if (isStatePostGloas(prepareState)) {
803
+ const isExtendingPayload = byteArrayEquals(parentBlockHash, prepareState.latestExecutionPayloadBid.blockHash);
804
+ if (isExtendingPayload) {
805
+ if (parentExecutionRequests === undefined) {
806
+ throw new Error("parentExecutionRequests required when extending full parent");
807
+ }
808
+ (payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
809
+ prepareState.getExpectedWithdrawalsForFullParent(parentExecutionRequests);
810
+ } else {
811
+ // When the parent block is empty, state.payloadExpectedWithdrawals holds a batch
812
+ // already deducted from CL balances but never credited on the EL (the envelope
813
+ // was not delivered). The next payload must carry those same withdrawals to
814
+ // restore CL/EL consistency, otherwise validators permanently lose that balance.
815
+ (payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
816
+ prepareState.payloadExpectedWithdrawals;
817
+ }
782
818
  } else {
783
819
  // withdrawals logic is now fork aware as it changes on electra fork post capella
784
820
  (payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
@@ -790,6 +826,10 @@ function preparePayloadAttributes(
790
826
  (payloadAttributes as deneb.SSEPayloadAttributes["payloadAttributes"]).parentBeaconBlockRoot = parentBlockRoot;
791
827
  }
792
828
 
829
+ if (ForkSeq[fork] >= ForkSeq.gloas) {
830
+ (payloadAttributes as gloas.SSEPayloadAttributes["payloadAttributes"]).slotNumber = prepareSlot;
831
+ }
832
+
793
833
  return payloadAttributes;
794
834
  }
795
835
 
@@ -1,4 +1,3 @@
1
- import {PayloadStatus} from "@lodestar/fork-choice";
2
1
  import {Root, RootHex, Slot} from "@lodestar/types";
3
2
 
4
3
  export enum RegenErrorCode {
@@ -10,8 +9,6 @@ export enum RegenErrorCode {
10
9
  BLOCK_NOT_IN_DB = "REGEN_ERROR_BLOCK_NOT_IN_DB",
11
10
  STATE_TRANSITION_ERROR = "REGEN_ERROR_STATE_TRANSITION_ERROR",
12
11
  INVALID_STATE_ROOT = "REGEN_ERROR_INVALID_STATE_ROOT",
13
- UNEXPECTED_PAYLOAD_STATUS = "REGEN_ERROR_UNEXPECTED_PAYLOAD_STATUS",
14
- INTERNAL_ERROR = "REGEN_ERROR_INTERNAL_ERROR",
15
12
  }
16
13
 
17
14
  export type RegenErrorType =
@@ -22,9 +19,7 @@ export type RegenErrorType =
22
19
  | {code: RegenErrorCode.TOO_MANY_BLOCK_PROCESSED; stateRoot: RootHex | Root}
23
20
  | {code: RegenErrorCode.BLOCK_NOT_IN_DB; blockRoot: RootHex | Root}
24
21
  | {code: RegenErrorCode.STATE_TRANSITION_ERROR; error: Error}
25
- | {code: RegenErrorCode.INVALID_STATE_ROOT; slot: Slot; expected: RootHex; actual: RootHex}
26
- | {code: RegenErrorCode.UNEXPECTED_PAYLOAD_STATUS; blockRoot: RootHex | Root; payloadStatus: PayloadStatus}
27
- | {code: RegenErrorCode.INTERNAL_ERROR; message: string};
22
+ | {code: RegenErrorCode.INVALID_STATE_ROOT; slot: Slot; expected: RootHex; actual: RootHex};
28
23
 
29
24
  export class RegenError extends Error {
30
25
  type: RegenErrorType;
@@ -2,7 +2,7 @@ import {routes} from "@lodestar/api";
2
2
  import {ProtoBlock} from "@lodestar/fork-choice";
3
3
  import {IBeaconStateView} from "@lodestar/state-transition";
4
4
  import {BeaconBlock, Epoch, RootHex, Slot, phase0} from "@lodestar/types";
5
- import {CheckpointHexPayload} from "../stateCache/types.js";
5
+ import {CheckpointHex} from "../stateCache/types.js";
6
6
 
7
7
  export enum RegenCaller {
8
8
  getDuties = "getDuties",
@@ -40,21 +40,15 @@ export interface IStateRegenerator extends IStateRegeneratorInternal {
40
40
  dumpCacheSummary(): routes.lodestar.StateCacheItem[];
41
41
  getStateSync(stateRoot: RootHex): IBeaconStateView | null;
42
42
  getPreStateSync(block: BeaconBlock): IBeaconStateView | null;
43
- getCheckpointStateOrBytes(cp: CheckpointHexPayload): Promise<IBeaconStateView | Uint8Array | null>;
44
- getCheckpointStateSync(cp: CheckpointHexPayload): IBeaconStateView | null;
43
+ getCheckpointStateOrBytes(cp: CheckpointHex): Promise<IBeaconStateView | Uint8Array | null>;
44
+ getCheckpointStateSync(cp: CheckpointHex): IBeaconStateView | null;
45
45
  getClosestHeadState(head: ProtoBlock): IBeaconStateView | null;
46
46
  pruneOnCheckpoint(finalizedEpoch: Epoch, justifiedEpoch: Epoch, headStateRoot: RootHex): void;
47
47
  pruneOnFinalized(finalizedEpoch: Epoch): void;
48
- processBlockState(blockRootHex: RootHex, postState: IBeaconStateView): void;
49
- processPayloadState(payloadState: IBeaconStateView): void;
50
- /**
51
- * payloadPresent is true if this is payload state, false if block state.
52
- * payloadPresent is always true for pre-gloas.
53
- */
54
- addCheckpointState(cp: phase0.Checkpoint, item: IBeaconStateView, payloadPresent: boolean): void;
48
+ processState(blockRootHex: RootHex, postState: IBeaconStateView): void;
49
+ addCheckpointState(cp: phase0.Checkpoint, item: IBeaconStateView): void;
55
50
  updateHeadState(newHead: ProtoBlock, maybeHeadState: IBeaconStateView): void;
56
- updatePreComputedCheckpoint(rootHex: RootHex, epoch: Epoch, payloadPresent: boolean): number | null;
57
- upgradeForGloas(epoch: Epoch): void;
51
+ updatePreComputedCheckpoint(rootHex: RootHex, epoch: Epoch): number | null;
58
52
  }
59
53
 
60
54
  /**
@@ -1,11 +1,11 @@
1
1
  import {routes} from "@lodestar/api";
2
- import {IForkChoice, PayloadStatus, ProtoBlock} from "@lodestar/fork-choice";
2
+ import {IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
3
3
  import {IBeaconStateView, computeEpochAtSlot} from "@lodestar/state-transition";
4
4
  import {BeaconBlock, Epoch, RootHex, Slot, isGloasBeaconBlock, phase0} from "@lodestar/types";
5
- import {Logger, fromHex, toRootHex} from "@lodestar/utils";
5
+ import {Logger, toRootHex} from "@lodestar/utils";
6
6
  import {Metrics} from "../../metrics/index.js";
7
7
  import {JobItemQueue} from "../../util/queue/index.js";
8
- import {BlockStateCache, CheckpointHexPayload, CheckpointStateCache} from "../stateCache/types.js";
8
+ import {BlockStateCache, CheckpointHex, CheckpointStateCache} from "../stateCache/types.js";
9
9
  import {RegenError, RegenErrorCode} from "./errors.js";
10
10
  import {
11
11
  IStateRegenerator,
@@ -104,19 +104,9 @@ export class QueuedStateRegenerator implements IStateRegenerator {
104
104
  const parentEpoch = computeEpochAtSlot(parentBlock.slot);
105
105
  const blockEpoch = computeEpochAtSlot(block.slot);
106
106
 
107
- // Convert PayloadStatus to payloadPresent boolean
108
- if (parentBlock.payloadStatus === PayloadStatus.PENDING) {
109
- throw new RegenError({
110
- code: RegenErrorCode.UNEXPECTED_PAYLOAD_STATUS,
111
- blockRoot: block.parentRoot,
112
- payloadStatus: parentBlock.payloadStatus,
113
- });
114
- }
115
- const payloadPresent = parentBlock.payloadStatus === PayloadStatus.FULL;
116
-
117
107
  // Check the checkpoint cache (if the pre-state is a checkpoint state)
118
108
  if (parentEpoch < blockEpoch) {
119
- const checkpointState = this.checkpointStateCache.getLatest(parentRoot, blockEpoch, payloadPresent);
109
+ const checkpointState = this.checkpointStateCache.getLatest(parentRoot, blockEpoch);
120
110
  if (checkpointState && computeEpochAtSlot(checkpointState.slot) === blockEpoch) {
121
111
  return checkpointState;
122
112
  }
@@ -135,14 +125,14 @@ export class QueuedStateRegenerator implements IStateRegenerator {
135
125
  return null;
136
126
  }
137
127
 
138
- async getCheckpointStateOrBytes(cp: CheckpointHexPayload): Promise<IBeaconStateView | Uint8Array | null> {
128
+ async getCheckpointStateOrBytes(cp: CheckpointHex): Promise<IBeaconStateView | Uint8Array | null> {
139
129
  return this.checkpointStateCache.getStateOrBytes(cp);
140
130
  }
141
131
 
142
132
  /**
143
133
  * Get checkpoint state from cache
144
134
  */
145
- getCheckpointStateSync(cp: CheckpointHexPayload): IBeaconStateView | null {
135
+ getCheckpointStateSync(cp: CheckpointHex): IBeaconStateView | null {
146
136
  return this.checkpointStateCache.get(cp);
147
137
  }
148
138
 
@@ -150,19 +140,7 @@ export class QueuedStateRegenerator implements IStateRegenerator {
150
140
  * Get state closest to head
151
141
  */
152
142
  getClosestHeadState(head: ProtoBlock): IBeaconStateView | null {
153
- // Convert PayloadStatus to payloadPresent boolean
154
- if (head.payloadStatus === PayloadStatus.PENDING) {
155
- throw new RegenError({
156
- code: RegenErrorCode.UNEXPECTED_PAYLOAD_STATUS,
157
- blockRoot: fromHex(head.blockRoot),
158
- payloadStatus: head.payloadStatus,
159
- });
160
- }
161
- const payloadPresent = head.payloadStatus === PayloadStatus.FULL;
162
- return (
163
- this.checkpointStateCache.getLatest(head.blockRoot, Infinity, payloadPresent) ||
164
- this.blockStateCache.get(head.stateRoot)
165
- );
143
+ return this.checkpointStateCache.getLatest(head.blockRoot, Infinity) || this.blockStateCache.get(head.stateRoot);
166
144
  }
167
145
 
168
146
  pruneOnCheckpoint(finalizedEpoch: Epoch, justifiedEpoch: Epoch, headStateRoot: RootHex): void {
@@ -175,24 +153,15 @@ export class QueuedStateRegenerator implements IStateRegenerator {
175
153
  this.blockStateCache.deleteAllBeforeEpoch(finalizedEpoch);
176
154
  }
177
155
 
178
- processBlockState(blockRootHex: RootHex, postState: IBeaconStateView): void {
156
+ processState(blockRootHex: RootHex, postState: IBeaconStateView): void {
179
157
  this.blockStateCache.add(postState);
180
158
  this.checkpointStateCache.processState(blockRootHex, postState).catch((e) => {
181
159
  this.logger.debug("Error processing block state", {blockRootHex, slot: postState.slot}, e);
182
160
  });
183
161
  }
184
162
 
185
- /**
186
- * Process payload state for caching after importing execution payload.
187
- */
188
- processPayloadState(payloadState: IBeaconStateView): void {
189
- // Add payload state to block state cache (keyed by payload state root)
190
- this.blockStateCache.add(payloadState);
191
- }
192
-
193
- // TODO GLOAS: This should also be called when importing execution payload after we implement it
194
- addCheckpointState(cp: phase0.Checkpoint, item: IBeaconStateView, payloadPresent: boolean): void {
195
- this.checkpointStateCache.add(cp, item, payloadPresent);
163
+ addCheckpointState(cp: phase0.Checkpoint, item: IBeaconStateView): void {
164
+ this.checkpointStateCache.add(cp, item);
196
165
  }
197
166
 
198
167
  updateHeadState(newHead: ProtoBlock, maybeHeadState: IBeaconStateView): void {
@@ -228,13 +197,8 @@ export class QueuedStateRegenerator implements IStateRegenerator {
228
197
  }
229
198
  }
230
199
 
231
- updatePreComputedCheckpoint(rootHex: RootHex, epoch: Epoch, payloadPresent: boolean): number | null {
232
- return this.checkpointStateCache.updatePreComputedCheckpoint(rootHex, epoch, payloadPresent);
233
- }
234
-
235
- upgradeForGloas(epoch: Epoch): void {
236
- this.logger.verbose("Upgrading block state cache for Gloas fork", {epoch});
237
- this.blockStateCache.upgradeToGloas();
200
+ updatePreComputedCheckpoint(rootHex: RootHex, epoch: Epoch): number | null {
201
+ return this.checkpointStateCache.updatePreComputedCheckpoint(rootHex, epoch);
238
202
  }
239
203
 
240
204
  /**
@@ -1,6 +1,6 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
- import {IForkChoice, PayloadStatus, ProtoBlock} from "@lodestar/fork-choice";
3
- import {ForkSeq, SLOTS_PER_EPOCH} from "@lodestar/params";
2
+ import {IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
3
+ import {SLOTS_PER_EPOCH} from "@lodestar/params";
4
4
  import {
5
5
  DataAvailabilityStatus,
6
6
  ExecutionPayloadStatus,
@@ -110,19 +110,9 @@ export class StateRegenerator implements IStateRegeneratorInternal {
110
110
  const {checkpointStateCache} = this.modules;
111
111
  const epoch = computeEpochAtSlot(slot);
112
112
 
113
- // Convert PayloadStatus to payloadPresent boolean
114
- if (block.payloadStatus === PayloadStatus.PENDING) {
115
- throw new RegenError({
116
- code: RegenErrorCode.UNEXPECTED_PAYLOAD_STATUS,
117
- blockRoot: fromHex(blockRoot),
118
- payloadStatus: block.payloadStatus,
119
- });
120
- }
121
- const payloadPresent = block.payloadStatus === PayloadStatus.FULL;
122
-
123
113
  const latestCheckpointStateCtx = allowDiskReload
124
- ? await checkpointStateCache.getOrReloadLatest(blockRoot, epoch, payloadPresent)
125
- : checkpointStateCache.getLatest(blockRoot, epoch, payloadPresent);
114
+ ? await checkpointStateCache.getOrReloadLatest(blockRoot, epoch)
115
+ : checkpointStateCache.getLatest(blockRoot, epoch);
126
116
 
127
117
  // If a checkpoint state exists with the given checkpoint root, it either is in requested epoch
128
118
  // or needs to have empty slots processed until the requested epoch
@@ -176,18 +166,9 @@ export class StateRegenerator implements IStateRegeneratorInternal {
176
166
  if (!lastBlockToReplay) continue;
177
167
  const epoch = computeEpochAtSlot(lastBlockToReplay.slot - 1);
178
168
 
179
- // Convert PayloadStatus to payloadPresent boolean
180
- if (b.payloadStatus === PayloadStatus.PENDING) {
181
- throw new RegenError({
182
- code: RegenErrorCode.INTERNAL_ERROR,
183
- message: `Unexpected PENDING payloadStatus for ancestor block ${b.blockRoot} at slot ${b.slot}`,
184
- });
185
- }
186
- const payloadPresent = b.payloadStatus === PayloadStatus.FULL;
187
-
188
169
  state = allowDiskReload
189
- ? await checkpointStateCache.getOrReloadLatest(b.blockRoot, epoch, payloadPresent)
190
- : checkpointStateCache.getLatest(b.blockRoot, epoch, payloadPresent);
170
+ ? await checkpointStateCache.getOrReloadLatest(b.blockRoot, epoch)
171
+ : checkpointStateCache.getLatest(b.blockRoot, epoch);
191
172
  if (state) {
192
173
  break;
193
174
  }
@@ -351,11 +332,6 @@ async function processSlotsByCheckpoint(
351
332
  * emitting "checkpoint" events after every epoch processed.
352
333
  *
353
334
  * Stops processing after no more full epochs can be processed.
354
- *
355
- * Output state variant:
356
- * - Post-Gloas: If slots are processed, returns block state (payloadPresent=false).
357
- * If no slots processed, returns preState as-is (preserves variant).
358
- * - Pre-Gloas: Always payloadPresent=true (no block/payload distinction).
359
335
  */
360
336
  export async function processSlotsToNearestCheckpoint(
361
337
  modules: {
@@ -375,7 +351,7 @@ export async function processSlotsToNearestCheckpoint(
375
351
  const postSlot = slot;
376
352
  const preEpoch = computeEpochAtSlot(preSlot);
377
353
  let postState = preState;
378
- const {config, checkpointStateCache, emitter, metrics, logger} = modules;
354
+ const {checkpointStateCache, emitter, metrics, logger} = modules;
379
355
  let count = 0;
380
356
 
381
357
  for (
@@ -399,11 +375,7 @@ export async function processSlotsToNearestCheckpoint(
399
375
  // This may becomes the "official" checkpoint state if the 1st block of epoch is skipped
400
376
  const checkpointState = postState;
401
377
  const cp = getCheckpointFromState(checkpointState);
402
- // processSlots() only does epoch transitions, never processes payloads
403
- // Pre-Gloas: payloadPresent is always true (execution payload embedded in block)
404
- // Post-Gloas: result is a block state (payloadPresent=false)
405
- const isPayloadPresent = config.getForkSeq(checkpointState.slot) < ForkSeq.gloas;
406
- checkpointStateCache.add(cp, checkpointState, isPayloadPresent);
378
+ checkpointStateCache.add(cp, checkpointState);
407
379
  // consumers should not mutate state ever
408
380
  emitter?.emit(ChainEvent.checkpoint, cp, checkpointState);
409
381
 
@@ -1,6 +1,6 @@
1
1
  import {CheckpointWithHex} from "@lodestar/fork-choice";
2
2
  import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
3
- import {RootHex} from "@lodestar/types";
3
+ import {RootHex, Slot} from "@lodestar/types";
4
4
  import {Logger} from "@lodestar/utils";
5
5
  import {Metrics} from "../../metrics/metrics.js";
6
6
  import {SerializedCache} from "../../util/serializedCache.js";
@@ -21,8 +21,15 @@ export type SeenPayloadEnvelopeInputModules = {
21
21
  /**
22
22
  * Cache for tracking PayloadEnvelopeInput instances, keyed by beacon block root.
23
23
  *
24
- * Created during block import when a block is processed.
25
- * Pruned on finalization and after payload is written to DB.
24
+ * Created during block import when a block is processed. Two pruning paths:
25
+ * - `prepareNextSlot` calls `pruneBelow(headParentSlot)` every slot once the head we'll build
26
+ * on is known.
27
+ * - `onFinalized` calls `pruneBelow(finalizedSlot)` on every finalization for bulk cleanup.
28
+ *
29
+ * Steady state (linear chain, healthy progression): the cache holds ~2 entries — the head
30
+ * (parent for next-slot production) and its parent (proposer-boost-reorg fallback). It can
31
+ * transiently hold more during forks, range-sync bursts, or when `prepareNextSlot` skips
32
+ * ticks; subsequent ticks settle it back.
26
33
  */
27
34
  export class SeenPayloadEnvelopeInput {
28
35
  private readonly chainEvents: ChainEventEmitter;
@@ -58,16 +65,7 @@ export class SeenPayloadEnvelopeInput {
58
65
  }
59
66
 
60
67
  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});
68
+ this.pruneBelow(computeStartSlotAtEpoch(checkpoint.epoch));
71
69
  };
72
70
 
73
71
  add(props: CreateFromBlockProps): PayloadEnvelopeInput {
@@ -88,17 +86,21 @@ export class SeenPayloadEnvelopeInput {
88
86
  return this.payloadInputs.get(blockRootHex)?.hasPayloadEnvelope() ?? false;
89
87
  }
90
88
 
91
- prune(blockRootHex: RootHex): void {
92
- const payloadInput = this.payloadInputs.get(blockRootHex);
93
- if (payloadInput) {
94
- this.evictPayloadInput(payloadInput);
95
- }
96
- }
97
-
98
89
  size(): number {
99
90
  return this.payloadInputs.size;
100
91
  }
101
92
 
93
+ pruneBelow(slot: Slot): void {
94
+ let deletedCount = 0;
95
+ for (const [, input] of this.payloadInputs) {
96
+ if (input.slot < slot) {
97
+ this.evictPayloadInput(input);
98
+ deletedCount++;
99
+ }
100
+ }
101
+ this.logger?.debug("SeenPayloadEnvelopeInput.pruneBelow deleted entries", {slot, deletedCount});
102
+ }
103
+
102
104
  private evictPayloadInput(payloadInput: PayloadEnvelopeInput): void {
103
105
  this.serializedCache.delete(payloadInput.getSerializedCacheKeys());
104
106
  this.payloadInputs.delete(payloadInput.blockRootHex);