@lodestar/beacon-node 1.43.0-dev.ade910fc78 → 1.43.0-dev.ca1fc40294

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 (177) 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 +23 -31
  28. package/lib/chain/blocks/importBlock.js.map +1 -1
  29. package/lib/chain/blocks/importExecutionPayload.d.ts +1 -1
  30. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  31. package/lib/chain/blocks/importExecutionPayload.js +19 -26
  32. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  33. package/lib/chain/blocks/index.js +1 -1
  34. package/lib/chain/blocks/index.js.map +1 -1
  35. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +3 -0
  36. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
  37. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +20 -0
  38. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
  39. package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts +5 -0
  40. package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts.map +1 -1
  41. package/lib/chain/blocks/payloadEnvelopeProcessor.js +6 -4
  42. package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -1
  43. package/lib/chain/blocks/types.d.ts +1 -1
  44. package/lib/chain/blocks/types.d.ts.map +1 -1
  45. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts +14 -0
  46. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -0
  47. package/lib/chain/blocks/verifyPayloadsDataAvailability.js +25 -0
  48. package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -0
  49. package/lib/chain/chain.d.ts +3 -3
  50. package/lib/chain/chain.d.ts.map +1 -1
  51. package/lib/chain/chain.js +16 -32
  52. package/lib/chain/chain.js.map +1 -1
  53. package/lib/chain/emitter.d.ts +16 -4
  54. package/lib/chain/emitter.d.ts.map +1 -1
  55. package/lib/chain/emitter.js +5 -0
  56. package/lib/chain/emitter.js.map +1 -1
  57. package/lib/chain/errors/attestationError.d.ts +8 -1
  58. package/lib/chain/errors/attestationError.d.ts.map +1 -1
  59. package/lib/chain/errors/attestationError.js +4 -0
  60. package/lib/chain/errors/attestationError.js.map +1 -1
  61. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  62. package/lib/chain/forkChoice/index.js +9 -13
  63. package/lib/chain/forkChoice/index.js.map +1 -1
  64. package/lib/chain/interface.d.ts +2 -2
  65. package/lib/chain/interface.d.ts.map +1 -1
  66. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  67. package/lib/chain/prepareNextSlot.js +22 -16
  68. package/lib/chain/prepareNextSlot.js.map +1 -1
  69. package/lib/chain/produceBlock/computeNewStateRoot.d.ts +3 -9
  70. package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
  71. package/lib/chain/produceBlock/computeNewStateRoot.js +5 -32
  72. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  73. package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -8
  74. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  75. package/lib/chain/produceBlock/produceBlockBody.js +29 -19
  76. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  77. package/lib/chain/regen/errors.d.ts +1 -11
  78. package/lib/chain/regen/errors.d.ts.map +1 -1
  79. package/lib/chain/regen/errors.js +0 -2
  80. package/lib/chain/regen/errors.js.map +1 -1
  81. package/lib/chain/regen/interface.d.ts +6 -11
  82. package/lib/chain/regen/interface.d.ts.map +1 -1
  83. package/lib/chain/regen/queued.d.ts +6 -10
  84. package/lib/chain/regen/queued.d.ts.map +1 -1
  85. package/lib/chain/regen/queued.js +3 -10
  86. package/lib/chain/regen/queued.js.map +1 -1
  87. package/lib/chain/regen/regen.d.ts +0 -5
  88. package/lib/chain/regen/regen.d.ts.map +1 -1
  89. package/lib/chain/regen/regen.js +0 -8
  90. package/lib/chain/regen/regen.js.map +1 -1
  91. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +1 -7
  92. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  93. package/lib/chain/stateCache/persistentCheckpointsCache.js +4 -9
  94. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  95. package/lib/chain/stateCache/types.d.ts +0 -6
  96. package/lib/chain/stateCache/types.d.ts.map +1 -1
  97. package/lib/chain/stateCache/types.js.map +1 -1
  98. package/lib/chain/validation/aggregateAndProof.js +12 -0
  99. package/lib/chain/validation/aggregateAndProof.js.map +1 -1
  100. package/lib/chain/validation/attestation.d.ts.map +1 -1
  101. package/lib/chain/validation/attestation.js +12 -0
  102. package/lib/chain/validation/attestation.js.map +1 -1
  103. package/lib/chain/validation/executionPayloadEnvelope.js +10 -10
  104. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  105. package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
  106. package/lib/chain/validation/payloadAttestationMessage.js +4 -3
  107. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  108. package/lib/db/repositories/executionPayloadEnvelopeArchive.js +1 -1
  109. package/lib/db/repositories/executionPayloadEnvelopeArchive.js.map +1 -1
  110. package/lib/execution/engine/http.d.ts.map +1 -1
  111. package/lib/execution/engine/http.js +21 -14
  112. package/lib/execution/engine/http.js.map +1 -1
  113. package/lib/execution/engine/interface.d.ts +1 -0
  114. package/lib/execution/engine/interface.d.ts.map +1 -1
  115. package/lib/execution/engine/mock.d.ts.map +1 -1
  116. package/lib/execution/engine/mock.js +6 -0
  117. package/lib/execution/engine/mock.js.map +1 -1
  118. package/lib/execution/engine/types.d.ts +20 -0
  119. package/lib/execution/engine/types.d.ts.map +1 -1
  120. package/lib/execution/engine/types.js +18 -0
  121. package/lib/execution/engine/types.js.map +1 -1
  122. package/lib/network/gossip/topic.d.ts +3 -2
  123. package/lib/network/gossip/topic.d.ts.map +1 -1
  124. package/lib/network/network.js +1 -1
  125. package/lib/network/network.js.map +1 -1
  126. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  127. package/lib/network/processor/gossipHandlers.js +22 -6
  128. package/lib/network/processor/gossipHandlers.js.map +1 -1
  129. package/lib/node/nodejs.d.ts.map +1 -1
  130. package/lib/node/nodejs.js +4 -2
  131. package/lib/node/nodejs.js.map +1 -1
  132. package/lib/util/sszBytes.d.ts.map +1 -1
  133. package/lib/util/sszBytes.js +16 -3
  134. package/lib/util/sszBytes.js.map +1 -1
  135. package/package.json +16 -16
  136. package/src/api/impl/beacon/blocks/index.ts +1 -4
  137. package/src/api/impl/beacon/state/utils.ts +2 -2
  138. package/src/api/impl/validator/index.ts +3 -6
  139. package/src/chain/GetBlobsTracker.ts +1 -2
  140. package/src/chain/archiveStore/archiveStore.ts +5 -5
  141. package/src/chain/archiveStore/interface.ts +4 -4
  142. package/src/chain/archiveStore/strategies/frequencyStateArchiveStrategy.ts +6 -8
  143. package/src/chain/archiveStore/utils/archiveBlocks.ts +153 -94
  144. package/src/chain/blocks/importBlock.ts +22 -35
  145. package/src/chain/blocks/importExecutionPayload.ts +20 -26
  146. package/src/chain/blocks/index.ts +1 -1
  147. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +27 -0
  148. package/src/chain/blocks/payloadEnvelopeProcessor.ts +6 -5
  149. package/src/chain/blocks/types.ts +1 -1
  150. package/src/chain/blocks/verifyPayloadsDataAvailability.ts +38 -0
  151. package/src/chain/chain.ts +23 -48
  152. package/src/chain/emitter.ts +15 -3
  153. package/src/chain/errors/attestationError.ts +6 -1
  154. package/src/chain/forkChoice/index.ts +6 -18
  155. package/src/chain/interface.ts +2 -2
  156. package/src/chain/prepareNextSlot.ts +25 -16
  157. package/src/chain/produceBlock/computeNewStateRoot.ts +6 -43
  158. package/src/chain/produceBlock/produceBlockBody.ts +40 -20
  159. package/src/chain/regen/errors.ts +1 -6
  160. package/src/chain/regen/interface.ts +6 -11
  161. package/src/chain/regen/queued.ts +6 -14
  162. package/src/chain/regen/regen.ts +0 -8
  163. package/src/chain/stateCache/persistentCheckpointsCache.ts +5 -15
  164. package/src/chain/stateCache/types.ts +0 -3
  165. package/src/chain/validation/aggregateAndProof.ts +13 -0
  166. package/src/chain/validation/attestation.ts +13 -0
  167. package/src/chain/validation/executionPayloadEnvelope.ts +10 -10
  168. package/src/chain/validation/payloadAttestationMessage.ts +5 -3
  169. package/src/db/repositories/executionPayloadEnvelopeArchive.ts +1 -1
  170. package/src/execution/engine/http.ts +21 -14
  171. package/src/execution/engine/interface.ts +1 -0
  172. package/src/execution/engine/mock.ts +8 -1
  173. package/src/execution/engine/types.ts +41 -0
  174. package/src/network/network.ts +1 -1
  175. package/src/network/processor/gossipHandlers.ts +26 -10
  176. package/src/node/nodejs.ts +4 -2
  177. package/src/util/sszBytes.ts +21 -3
@@ -1,6 +1,6 @@
1
1
  import {routes} from "@lodestar/api";
2
2
  import {ChainForkConfig} from "@lodestar/config";
3
- import {PayloadStatus, getSafeExecutionBlockHash} from "@lodestar/fork-choice";
3
+ import {getSafeExecutionBlockHash} from "@lodestar/fork-choice";
4
4
  import {ForkPostBellatrix, ForkSeq, SLOTS_PER_EPOCH, isForkPostBellatrix} from "@lodestar/params";
5
5
  import {
6
6
  IBeaconStateView,
@@ -8,8 +8,9 @@ import {
8
8
  computeEpochAtSlot,
9
9
  computeTimeAtSlot,
10
10
  isStatePostBellatrix,
11
+ isStatePostGloas,
11
12
  } from "@lodestar/state-transition";
12
- import {Slot} from "@lodestar/types";
13
+ import {Bytes32, Slot} from "@lodestar/types";
13
14
  import {Logger, fromHex, isErrorAborted, sleep} from "@lodestar/utils";
14
15
  import {GENESIS_SLOT, ZERO_HASH_HEX} from "../constants/constants.js";
15
16
  import {BuilderStatus} from "../execution/builder/http.js";
@@ -81,6 +82,8 @@ export class PrepareNextSlotScheduler {
81
82
  // calling updateHead() here before we produce a block to reduce reorg possibility
82
83
  const headBlock = this.chain.recomputeForkChoiceHead(ForkchoiceCaller.prepareNextSlot);
83
84
  const {slot: headSlot, blockRoot: headRoot} = headBlock;
85
+ // may be updated below if we predict a proposer-boost-reorg
86
+ let updatedHeadRoot = headRoot;
84
87
 
85
88
  // PS: previously this was comparing slots, but that gave no leway on the skipped
86
89
  // slots on epoch bounday. Making it more fluid.
@@ -123,7 +126,6 @@ export class PrepareNextSlotScheduler {
123
126
  const proposerIndex = prepareState.getBeaconProposer(prepareSlot);
124
127
  const feeRecipient = this.chain.beaconProposerCache.get(proposerIndex);
125
128
  let updatedPrepareState = prepareState;
126
- let updatedHeadRoot = headRoot;
127
129
 
128
130
  if (feeRecipient) {
129
131
  // If we are proposing next slot, we need to predict if we can proposer-boost-reorg or not
@@ -156,25 +158,39 @@ export class PrepareNextSlotScheduler {
156
158
  this.logger.error("Builder disabled as the check status api failed", {prepareSlot}, e as Error);
157
159
  });
158
160
  }
161
+ }
162
+
163
+ if (!isStatePostBellatrix(updatedPrepareState)) {
164
+ throw new Error("Expected Bellatrix state for payload attributes");
165
+ }
166
+
167
+ let parentBlockHash: Bytes32;
168
+ if (isStatePostGloas(updatedPrepareState)) {
169
+ parentBlockHash = this.chain.forkChoice.shouldExtendPayload(updatedHeadRoot)
170
+ ? updatedPrepareState.latestExecutionPayloadBid.blockHash
171
+ : updatedPrepareState.latestExecutionPayloadBid.parentBlockHash;
172
+ } else {
173
+ parentBlockHash = updatedPrepareState.latestExecutionPayloadHeader.blockHash;
174
+ }
159
175
 
176
+ if (feeRecipient) {
160
177
  const preparationTime =
161
178
  computeTimeAtSlot(this.config, prepareSlot, this.chain.genesisTime) - Date.now() / 1000;
162
179
  this.metrics?.blockPayload.payloadAdvancePrepTime.observe(preparationTime);
163
- if (!isStatePostBellatrix(updatedPrepareState)) {
164
- throw new Error("Expected Bellatrix state for payload preparation");
165
- }
166
180
 
167
181
  const safeBlockHash = getSafeExecutionBlockHash(this.chain.forkChoice);
168
182
  const finalizedBlockHash =
169
183
  this.chain.forkChoice.getFinalizedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX;
184
+
170
185
  // awaiting here instead of throwing an async call because there is no other task
171
- // left for scheduler and this gives nice sematics to catch and log errors in the
186
+ // left for scheduler and this gives nice semantics to catch and log errors in the
172
187
  // try/catch wrapper here.
173
188
  await prepareExecutionPayload(
174
189
  this.chain,
175
190
  this.logger,
176
191
  fork as ForkPostBellatrix, // State is of execution type
177
192
  fromHex(updatedHeadRoot),
193
+ parentBlockHash,
178
194
  safeBlockHash,
179
195
  finalizedBlockHash,
180
196
  updatedPrepareState,
@@ -187,10 +203,6 @@ export class PrepareNextSlotScheduler {
187
203
  });
188
204
  }
189
205
 
190
- if (!isStatePostBellatrix(updatedPrepareState)) {
191
- throw new Error("Expected Bellatrix state for payload attributes");
192
- }
193
-
194
206
  this.computeStateHashTreeRoot(updatedPrepareState, isEpochTransition);
195
207
 
196
208
  // If emitPayloadAttributes is true emit a SSE payloadAttributes event
@@ -202,6 +214,7 @@ export class PrepareNextSlotScheduler {
202
214
  prepareState: updatedPrepareState,
203
215
  prepareSlot,
204
216
  parentBlockRoot: fromHex(headRoot),
217
+ parentBlockHash,
205
218
  // The likely consumers of this API are builders and will anyway ignore the
206
219
  // feeRecipient, so just pass zero hash for now till a real use case arises
207
220
  feeRecipient: "0x0000000000000000000000000000000000000000000000000000000000000000",
@@ -217,11 +230,7 @@ export class PrepareNextSlotScheduler {
217
230
  // + if next slot is a skipped slot, it'd help getting target checkpoint state faster to validate attestations
218
231
  if (isEpochTransition) {
219
232
  this.metrics?.precomputeNextEpochTransition.count.inc({result: "success"}, 1);
220
- // Determine payloadPresent from head block's payload status
221
- // Pre-Gloas: payloadStatus is always FULL → payloadPresent = true
222
- // Post-Gloas: FULL → true, EMPTY → false, PENDING → false (conservative, treat as block state)
223
- const payloadPresent = headBlock.payloadStatus === PayloadStatus.FULL;
224
- const previousHits = this.chain.regen.updatePreComputedCheckpoint(headRoot, nextEpoch, payloadPresent);
233
+ const previousHits = this.chain.regen.updatePreComputedCheckpoint(headRoot, nextEpoch);
225
234
  if (previousHits === 0) {
226
235
  this.metrics?.precomputeNextEpochTransition.waste.inc();
227
236
  }
@@ -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,11 +214,15 @@ export async function produceBlockBody<T extends BlockType>(
220
214
  });
221
215
 
222
216
  // Get execution payload from EL
217
+ const parentBlockHash = this.forkChoice.shouldExtendPayload(toRootHex(parentBlockRoot))
218
+ ? currentState.latestExecutionPayloadBid.blockHash
219
+ : currentState.latestExecutionPayloadBid.parentBlockHash;
223
220
  const prepareRes = await prepareExecutionPayload(
224
221
  this,
225
222
  this.logger,
226
223
  fork,
227
224
  parentBlockRoot,
225
+ parentBlockHash,
228
226
  safeBlockHash,
229
227
  finalizedBlockHash ?? ZERO_HASH_HEX,
230
228
  currentState,
@@ -261,8 +259,8 @@ export async function produceBlockBody<T extends BlockType>(
261
259
 
262
260
  // Create self-build execution payload bid
263
261
  const bid: gloas.ExecutionPayloadBid = {
264
- parentBlockHash: currentState.latestBlockHash,
265
- parentBlockRoot: parentBlockRoot,
262
+ parentBlockHash,
263
+ parentBlockRoot,
266
264
  blockHash: executionPayload.blockHash,
267
265
  prevRandao: currentState.getRandaoMix(currentState.epoch),
268
266
  feeRecipient: executionPayload.feeRecipient,
@@ -272,6 +270,7 @@ export async function produceBlockBody<T extends BlockType>(
272
270
  value: 0,
273
271
  executionPayment: 0,
274
272
  blobKzgCommitments: blobsBundle.commitments,
273
+ executionRequestsRoot: ssz.electra.ExecutionRequests.hashTreeRoot(executionRequests),
275
274
  };
276
275
  const signedBid: gloas.SignedExecutionPayloadBid = {
277
276
  message: bid,
@@ -340,6 +339,7 @@ export async function produceBlockBody<T extends BlockType>(
340
339
  this.logger,
341
340
  fork,
342
341
  parentBlockRoot,
342
+ currentState.latestExecutionPayloadHeader.blockHash,
343
343
  safeBlockHash,
344
344
  finalizedBlockHash ?? ZERO_HASH_HEX,
345
345
  currentState,
@@ -448,6 +448,7 @@ export async function produceBlockBody<T extends BlockType>(
448
448
  this.logger,
449
449
  fork,
450
450
  parentBlockRoot,
451
+ currentState.latestExecutionPayloadHeader.blockHash,
451
452
  safeBlockHash,
452
453
  finalizedBlockHash ?? ZERO_HASH_HEX,
453
454
  currentState,
@@ -613,17 +614,17 @@ export async function prepareExecutionPayload(
613
614
  logger: Logger,
614
615
  fork: ForkPostBellatrix,
615
616
  parentBlockRoot: Root,
617
+ parentBlockHash: Bytes32,
616
618
  safeBlockHash: RootHex,
617
619
  finalizedBlockHash: RootHex,
618
620
  state: IBeaconStateViewBellatrix,
619
621
  suggestedFeeRecipient: string
620
622
  ): Promise<{prepType: PayloadPreparationType; payloadId: PayloadId}> {
621
- const parentHash = state.latestBlockHash;
622
623
  const timestamp = computeTimeAtSlot(chain.config, state.slot, state.genesisTime);
623
624
  const prevRandao = state.getRandaoMix(state.epoch);
624
625
 
625
626
  const payloadIdCached = chain.executionEngine.payloadIdCache.get({
626
- headBlockHash: toRootHex(parentHash),
627
+ headBlockHash: toRootHex(parentBlockHash),
627
628
  finalizedBlockHash,
628
629
  timestamp: numToQuantity(timestamp),
629
630
  prevRandao: toHex(prevRandao),
@@ -652,12 +653,13 @@ export async function prepareExecutionPayload(
652
653
  prepareState: state,
653
654
  prepareSlot: state.slot,
654
655
  parentBlockRoot,
656
+ parentBlockHash,
655
657
  feeRecipient: suggestedFeeRecipient,
656
658
  });
657
659
 
658
660
  payloadId = await chain.executionEngine.notifyForkchoiceUpdate(
659
661
  fork,
660
- toRootHex(parentHash),
662
+ toRootHex(parentBlockHash),
661
663
  safeBlockHash,
662
664
  finalizedBlockHash,
663
665
  attributes
@@ -709,20 +711,30 @@ export function getPayloadAttributesForSSE(
709
711
  prepareState,
710
712
  prepareSlot,
711
713
  parentBlockRoot,
714
+ parentBlockHash,
712
715
  feeRecipient,
713
- }: {prepareState: IBeaconStateViewBellatrix; prepareSlot: Slot; parentBlockRoot: Root; feeRecipient: string}
716
+ }: {
717
+ prepareState: IBeaconStateViewBellatrix;
718
+ prepareSlot: Slot;
719
+ parentBlockRoot: Root;
720
+ parentBlockHash: Bytes32;
721
+ feeRecipient: string;
722
+ }
714
723
  ): SSEPayloadAttributes {
715
- const parentHash = prepareState.latestBlockHash;
716
724
  const payloadAttributes = preparePayloadAttributes(fork, chain, {
717
725
  prepareState,
718
726
  prepareSlot,
719
727
  parentBlockRoot,
728
+ parentBlockHash,
720
729
  feeRecipient,
721
730
  });
722
731
 
723
732
  let parentBlockNumber: number;
724
733
  if (isForkPostGloas(fork)) {
725
- const parentBlock = chain.forkChoice.getBlockHexAndBlockHash(toRootHex(parentBlockRoot), toRootHex(parentHash));
734
+ const parentBlock = chain.forkChoice.getBlockHexAndBlockHash(
735
+ toRootHex(parentBlockRoot),
736
+ toRootHex(parentBlockHash)
737
+ );
726
738
  if (parentBlock?.executionPayloadBlockHash == null) {
727
739
  throw Error(`Parent block not found in fork choice root=${toRootHex(parentBlockRoot)}`);
728
740
  }
@@ -736,7 +748,7 @@ export function getPayloadAttributesForSSE(
736
748
  proposalSlot: prepareSlot,
737
749
  parentBlockNumber,
738
750
  parentBlockRoot,
739
- parentBlockHash: parentHash,
751
+ parentBlockHash,
740
752
  payloadAttributes,
741
753
  };
742
754
  return ssePayloadAttributes;
@@ -751,11 +763,13 @@ function preparePayloadAttributes(
751
763
  prepareState,
752
764
  prepareSlot,
753
765
  parentBlockRoot,
766
+ parentBlockHash,
754
767
  feeRecipient,
755
768
  }: {
756
769
  prepareState: IBeaconStateViewBellatrix;
757
770
  prepareSlot: Slot;
758
771
  parentBlockRoot: Root;
772
+ parentBlockHash: Bytes32;
759
773
  feeRecipient: string;
760
774
  }
761
775
  ): SSEPayloadAttributes["payloadAttributes"] {
@@ -772,13 +786,15 @@ function preparePayloadAttributes(
772
786
  throw new Error("Expected Capella state for withdrawals");
773
787
  }
774
788
 
775
- if (isStatePostGloas(prepareState) && !isParentBlockFull(prepareState)) {
789
+ if (isStatePostGloas(prepareState)) {
790
+ const isExtendingPayload = byteArrayEquals(parentBlockHash, prepareState.latestExecutionPayloadBid.blockHash);
776
791
  // When the parent block is empty, state.payloadExpectedWithdrawals holds a batch
777
792
  // already deducted from CL balances but never credited on the EL (the envelope
778
793
  // was not delivered). The next payload must carry those same withdrawals to
779
794
  // restore CL/EL consistency, otherwise validators permanently lose that balance.
780
- (payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
781
- prepareState.payloadExpectedWithdrawals;
795
+ (payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals = isExtendingPayload
796
+ ? prepareState.getExpectedWithdrawals().expectedWithdrawals
797
+ : prepareState.payloadExpectedWithdrawals;
782
798
  } else {
783
799
  // withdrawals logic is now fork aware as it changes on electra fork post capella
784
800
  (payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
@@ -790,6 +806,10 @@ function preparePayloadAttributes(
790
806
  (payloadAttributes as deneb.SSEPayloadAttributes["payloadAttributes"]).parentBeaconBlockRoot = parentBlockRoot;
791
807
  }
792
808
 
809
+ if (ForkSeq[fork] >= ForkSeq.gloas) {
810
+ (payloadAttributes as gloas.SSEPayloadAttributes["payloadAttributes"]).slotNumber = prepareSlot;
811
+ }
812
+
793
813
  return payloadAttributes;
794
814
  }
795
815
 
@@ -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,20 +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;
51
+ updatePreComputedCheckpoint(rootHex: RootHex, epoch: Epoch): number | null;
57
52
  }
58
53
 
59
54
  /**
@@ -5,7 +5,7 @@ import {BeaconBlock, Epoch, RootHex, Slot, isGloasBeaconBlock, phase0} from "@lo
5
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,
@@ -125,14 +125,14 @@ export class QueuedStateRegenerator implements IStateRegenerator {
125
125
  return null;
126
126
  }
127
127
 
128
- async getCheckpointStateOrBytes(cp: CheckpointHexPayload): Promise<IBeaconStateView | Uint8Array | null> {
128
+ async getCheckpointStateOrBytes(cp: CheckpointHex): Promise<IBeaconStateView | Uint8Array | null> {
129
129
  return this.checkpointStateCache.getStateOrBytes(cp);
130
130
  }
131
131
 
132
132
  /**
133
133
  * Get checkpoint state from cache
134
134
  */
135
- getCheckpointStateSync(cp: CheckpointHexPayload): IBeaconStateView | null {
135
+ getCheckpointStateSync(cp: CheckpointHex): IBeaconStateView | null {
136
136
  return this.checkpointStateCache.get(cp);
137
137
  }
138
138
 
@@ -153,22 +153,14 @@ export class QueuedStateRegenerator implements IStateRegenerator {
153
153
  this.blockStateCache.deleteAllBeforeEpoch(finalizedEpoch);
154
154
  }
155
155
 
156
- processBlockState(blockRootHex: RootHex, postState: IBeaconStateView): void {
156
+ processState(blockRootHex: RootHex, postState: IBeaconStateView): void {
157
157
  this.blockStateCache.add(postState);
158
158
  this.checkpointStateCache.processState(blockRootHex, postState).catch((e) => {
159
159
  this.logger.debug("Error processing block state", {blockRootHex, slot: postState.slot}, e);
160
160
  });
161
161
  }
162
162
 
163
- /**
164
- * Process payload state for caching after importing execution payload.
165
- */
166
- processPayloadState(payloadState: IBeaconStateView): void {
167
- // Add payload state to block state cache (keyed by payload state root)
168
- this.blockStateCache.add(payloadState);
169
- }
170
-
171
- addCheckpointState(cp: phase0.Checkpoint, item: IBeaconStateView, _payloadPresent: boolean): void {
163
+ addCheckpointState(cp: phase0.Checkpoint, item: IBeaconStateView): void {
172
164
  this.checkpointStateCache.add(cp, item);
173
165
  }
174
166
 
@@ -205,7 +197,7 @@ export class QueuedStateRegenerator implements IStateRegenerator {
205
197
  }
206
198
  }
207
199
 
208
- updatePreComputedCheckpoint(rootHex: RootHex, epoch: Epoch, _payloadPresent: boolean): number | null {
200
+ updatePreComputedCheckpoint(rootHex: RootHex, epoch: Epoch): number | null {
209
201
  return this.checkpointStateCache.updatePreComputedCheckpoint(rootHex, epoch);
210
202
  }
211
203
 
@@ -332,11 +332,6 @@ async function processSlotsByCheckpoint(
332
332
  * emitting "checkpoint" events after every epoch processed.
333
333
  *
334
334
  * Stops processing after no more full epochs can be processed.
335
- *
336
- * Output state variant:
337
- * - Post-Gloas: If slots are processed, returns block state (payloadPresent=false).
338
- * If no slots processed, returns preState as-is (preserves variant).
339
- * - Pre-Gloas: Always payloadPresent=true (no block/payload distinction).
340
335
  */
341
336
  export async function processSlotsToNearestCheckpoint(
342
337
  modules: {
@@ -380,9 +375,6 @@ export async function processSlotsToNearestCheckpoint(
380
375
  // This may becomes the "official" checkpoint state if the 1st block of epoch is skipped
381
376
  const checkpointState = postState;
382
377
  const cp = getCheckpointFromState(checkpointState);
383
- // processSlots() only does epoch transitions, never processes payloads
384
- // Pre-Gloas: payloadPresent is always true (execution payload embedded in block)
385
- // Post-Gloas: result is a block state (payloadPresent=false)
386
378
  checkpointStateCache.add(cp, checkpointState);
387
379
  // consumers should not mutate state ever
388
380
  emitter?.emit(ChainEvent.checkpoint, cp, checkpointState);
@@ -9,7 +9,7 @@ import {IClock} from "../../util/clock.js";
9
9
  import {serializeState} from "../serializeState.js";
10
10
  import {CPStateDatastore, DatastoreKey} from "./datastore/index.js";
11
11
  import {MapTracker} from "./mapMetrics.js";
12
- import {BlockStateCache, CacheItemType, CheckpointHex, CheckpointHexPayload, CheckpointStateCache} from "./types.js";
12
+ import {BlockStateCache, CacheItemType, CheckpointHex, CheckpointStateCache} from "./types.js";
13
13
 
14
14
  export type PersistentCheckpointStateCacheOpts = {
15
15
  /** Keep max n state epochs in memory, persist the rest to disk */
@@ -226,7 +226,10 @@ export class PersistentCheckpointStateCache implements CheckpointStateCache {
226
226
  }
227
227
  sszTimer?.();
228
228
  const timer = this.metrics?.cpStateCache.stateReloadDuration.startTimer();
229
- const newCachedState = seedState.loadOtherState(stateBytes, validatorsBytes);
229
+ // preload validators and balances for faster state transition
230
+ const newCachedState = seedState.loadOtherState(stateBytes, validatorsBytes, {
231
+ preloadValidatorsAndBalances: true,
232
+ });
230
233
  // hashTreeRoot() calls the commit() inside
231
234
  // there is no modification inside the state, it's just that we want to compute and cache all roots
232
235
  const stateRoot = toRootHex(newCachedState.hashTreeRoot());
@@ -843,19 +846,6 @@ export function toCheckpointHex(checkpoint: phase0.Checkpoint): CheckpointHex {
843
846
  };
844
847
  }
845
848
 
846
- /** TODO GLOAS: remove after rolling back regen dual-state changes */
847
- export function fcCheckpointToHexPayload(checkpoint: {
848
- epoch: Epoch;
849
- rootHex: RootHex;
850
- payloadStatus?: number;
851
- }): CheckpointHexPayload {
852
- return {
853
- epoch: checkpoint.epoch,
854
- rootHex: checkpoint.rootHex,
855
- payloadPresent: true,
856
- };
857
- }
858
-
859
849
  export function toCheckpointKey(cp: CheckpointHex): string {
860
850
  return `${cp.rootHex}:${cp.epoch}`;
861
851
  }
@@ -4,9 +4,6 @@ import {Epoch, RootHex, phase0} from "@lodestar/types";
4
4
 
5
5
  export type CheckpointHex = {epoch: Epoch; rootHex: RootHex};
6
6
 
7
- /** TODO GLOAS: payloadPresent is ignored — remove after rolling back regen dual-state changes */
8
- export type CheckpointHexPayload = {epoch: Epoch; rootHex: RootHex; payloadPresent: boolean};
9
-
10
7
  /**
11
8
  * Lodestar currently keeps two state caches around.
12
9
  *
@@ -90,6 +90,19 @@ async function validateAggregateAndProof(
90
90
  });
91
91
  }
92
92
 
93
+ // [REJECT] If `aggregate.data.index == 1` (payload present for a past
94
+ // block), the execution payload for `block` passes validation.
95
+ // [IGNORE] When `aggregate.data.index == 1` (payload present for a past block),
96
+ // the corresponding execution payload for `block` has been seen (a client MAY queue
97
+ // attestations for processing once the payload is retrieved and SHOULD request the
98
+ // payload envelope via `ExecutionPayloadEnvelopesByRoot`).
99
+ if (block !== null && attData.index === 1 && !chain.seenPayloadEnvelope(toRootHex(attData.beaconBlockRoot))) {
100
+ throw new AttestationError(GossipAction.IGNORE, {
101
+ code: AttestationErrorCode.EXECUTION_PAYLOAD_NOT_SEEN,
102
+ beaconBlockRoot: toRootHex(attData.beaconBlockRoot),
103
+ });
104
+ }
105
+
93
106
  // [REJECT] len(committee_indices) == 1, where committee_indices = get_committee_indices(aggregate)
94
107
  committeeIndex = (aggregate as electra.Attestation).committeeBits.getSingleTrueBit();
95
108
  if (committeeIndex === null) {
@@ -315,6 +315,19 @@ async function validateAttestationNoSignatureCheck(
315
315
  code: AttestationErrorCode.PREMATURELY_INDICATED_PAYLOAD_PRESENT,
316
316
  });
317
317
  }
318
+
319
+ // [REJECT] If `attestation.data.index == 1` (payload present for a past
320
+ // block), the execution payload for `block` passes validation.
321
+ // [IGNORE] When `attestation.data.index == 1` (payload present for a past block),
322
+ // the corresponding execution payload for `block` has been seen (a client MAY queue
323
+ // attestations for processing once the payload is retrieved and SHOULD request the
324
+ // payload envelope via `ExecutionPayloadEnvelopesByRoot`).
325
+ if (block !== null && attData.index === 1 && !chain.seenPayloadEnvelope(toRootHex(attData.beaconBlockRoot))) {
326
+ throw new AttestationError(GossipAction.IGNORE, {
327
+ code: AttestationErrorCode.EXECUTION_PAYLOAD_NOT_SEEN,
328
+ beaconBlockRoot: toRootHex(attData.beaconBlockRoot),
329
+ });
330
+ }
318
331
  } else {
319
332
  // [REJECT] attestation.data.index == 0
320
333
  if (attData.index !== 0) {