@lodestar/beacon-node 1.43.0-dev.66d2c102e3 → 1.43.0-dev.6f485b1b61

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 (195) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +13 -3
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
  5. package/lib/api/impl/beacon/pool/index.js +45 -2
  6. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  7. package/lib/api/impl/debug/index.d.ts.map +1 -1
  8. package/lib/api/impl/debug/index.js +0 -1
  9. package/lib/api/impl/debug/index.js.map +1 -1
  10. package/lib/api/impl/validator/index.d.ts.map +1 -1
  11. package/lib/api/impl/validator/index.js +68 -2
  12. package/lib/api/impl/validator/index.js.map +1 -1
  13. package/lib/chain/blocks/blockInput/blockInput.d.ts +3 -0
  14. package/lib/chain/blocks/blockInput/blockInput.d.ts.map +1 -1
  15. package/lib/chain/blocks/blockInput/blockInput.js +4 -1
  16. package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
  17. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  18. package/lib/chain/blocks/importBlock.js +16 -31
  19. package/lib/chain/blocks/importBlock.js.map +1 -1
  20. package/lib/chain/blocks/importExecutionPayload.d.ts +9 -3
  21. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  22. package/lib/chain/blocks/importExecutionPayload.js +37 -15
  23. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  24. package/lib/chain/blocks/index.d.ts.map +1 -1
  25. package/lib/chain/blocks/index.js +35 -21
  26. package/lib/chain/blocks/index.js.map +1 -1
  27. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +12 -1
  28. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
  29. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +28 -2
  30. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
  31. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +17 -0
  32. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
  33. package/lib/chain/blocks/types.d.ts +2 -1
  34. package/lib/chain/blocks/types.d.ts.map +1 -1
  35. package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
  36. package/lib/chain/blocks/utils/chainSegment.js +8 -0
  37. package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
  38. package/lib/chain/blocks/verifyBlock.d.ts +2 -1
  39. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  40. package/lib/chain/blocks/verifyBlock.js +30 -12
  41. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  42. package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts +0 -4
  43. package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts.map +1 -1
  44. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +5 -2
  45. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
  46. package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts +2 -1
  47. package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts.map +1 -1
  48. package/lib/chain/blocks/verifyBlocksSanityChecks.js +16 -7
  49. package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
  50. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +2 -2
  51. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
  52. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +10 -6
  53. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
  54. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -1
  55. package/lib/chain/blocks/verifyPayloadsDataAvailability.js +8 -3
  56. package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -1
  57. package/lib/chain/chain.d.ts.map +1 -1
  58. package/lib/chain/chain.js +25 -8
  59. package/lib/chain/chain.js.map +1 -1
  60. package/lib/chain/emitter.d.ts +0 -11
  61. package/lib/chain/emitter.d.ts.map +1 -1
  62. package/lib/chain/emitter.js +0 -4
  63. package/lib/chain/emitter.js.map +1 -1
  64. package/lib/chain/errors/proposerPreferences.d.ts +8 -1
  65. package/lib/chain/errors/proposerPreferences.d.ts.map +1 -1
  66. package/lib/chain/errors/proposerPreferences.js +1 -0
  67. package/lib/chain/errors/proposerPreferences.js.map +1 -1
  68. package/lib/chain/initState.d.ts.map +1 -1
  69. package/lib/chain/initState.js +6 -1
  70. package/lib/chain/initState.js.map +1 -1
  71. package/lib/chain/opPools/payloadAttestationPool.d.ts +3 -2
  72. package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -1
  73. package/lib/chain/opPools/payloadAttestationPool.js +26 -4
  74. package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
  75. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  76. package/lib/chain/prepareNextSlot.js +16 -18
  77. package/lib/chain/prepareNextSlot.js.map +1 -1
  78. package/lib/chain/produceBlock/produceBlockBody.d.ts +12 -3
  79. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  80. package/lib/chain/produceBlock/produceBlockBody.js +34 -22
  81. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  82. package/lib/chain/regen/queued.d.ts.map +1 -1
  83. package/lib/chain/regen/queued.js +1 -4
  84. package/lib/chain/regen/queued.js.map +1 -1
  85. package/lib/chain/regen/regen.d.ts.map +1 -1
  86. package/lib/chain/regen/regen.js +1 -4
  87. package/lib/chain/regen/regen.js.map +1 -1
  88. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +21 -11
  89. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  90. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +70 -20
  91. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  92. package/lib/chain/seenCache/seenProposerPreferences.d.ts +8 -7
  93. package/lib/chain/seenCache/seenProposerPreferences.d.ts.map +1 -1
  94. package/lib/chain/seenCache/seenProposerPreferences.js +11 -10
  95. package/lib/chain/seenCache/seenProposerPreferences.js.map +1 -1
  96. package/lib/chain/validation/executionPayloadBid.js +11 -8
  97. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  98. package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
  99. package/lib/chain/validation/proposerPreferences.js +39 -17
  100. package/lib/chain/validation/proposerPreferences.js.map +1 -1
  101. package/lib/network/gossip/topic.d.ts +2 -0
  102. package/lib/network/gossip/topic.d.ts.map +1 -1
  103. package/lib/network/interface.d.ts +1 -0
  104. package/lib/network/interface.d.ts.map +1 -1
  105. package/lib/network/network.d.ts +1 -0
  106. package/lib/network/network.d.ts.map +1 -1
  107. package/lib/network/network.js +5 -0
  108. package/lib/network/network.js.map +1 -1
  109. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  110. package/lib/network/processor/gossipHandlers.js +28 -10
  111. package/lib/network/processor/gossipHandlers.js.map +1 -1
  112. package/lib/network/processor/index.js +5 -5
  113. package/lib/network/processor/index.js.map +1 -1
  114. package/lib/node/nodejs.js +2 -2
  115. package/lib/node/nodejs.js.map +1 -1
  116. package/lib/node/notifier.js +1 -7
  117. package/lib/node/notifier.js.map +1 -1
  118. package/lib/sync/constants.d.ts +3 -1
  119. package/lib/sync/constants.d.ts.map +1 -1
  120. package/lib/sync/constants.js +3 -4
  121. package/lib/sync/constants.js.map +1 -1
  122. package/lib/sync/range/batch.d.ts +23 -3
  123. package/lib/sync/range/batch.d.ts.map +1 -1
  124. package/lib/sync/range/batch.js +191 -36
  125. package/lib/sync/range/batch.js.map +1 -1
  126. package/lib/sync/range/chain.d.ts +13 -2
  127. package/lib/sync/range/chain.d.ts.map +1 -1
  128. package/lib/sync/range/chain.js +61 -9
  129. package/lib/sync/range/chain.js.map +1 -1
  130. package/lib/sync/range/range.d.ts.map +1 -1
  131. package/lib/sync/range/range.js +14 -3
  132. package/lib/sync/range/range.js.map +1 -1
  133. package/lib/sync/sync.d.ts.map +1 -1
  134. package/lib/sync/sync.js +13 -0
  135. package/lib/sync/sync.js.map +1 -1
  136. package/lib/sync/unknownBlock.d.ts +7 -2
  137. package/lib/sync/unknownBlock.d.ts.map +1 -1
  138. package/lib/sync/unknownBlock.js +138 -57
  139. package/lib/sync/unknownBlock.js.map +1 -1
  140. package/lib/sync/utils/downloadByRange.d.ts +29 -8
  141. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  142. package/lib/sync/utils/downloadByRange.js +104 -42
  143. package/lib/sync/utils/downloadByRange.js.map +1 -1
  144. package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
  145. package/lib/sync/utils/downloadByRoot.js +10 -0
  146. package/lib/sync/utils/downloadByRoot.js.map +1 -1
  147. package/lib/util/sszBytes.d.ts.map +1 -1
  148. package/lib/util/sszBytes.js +8 -6
  149. package/lib/util/sszBytes.js.map +1 -1
  150. package/package.json +15 -15
  151. package/src/api/impl/beacon/blocks/index.ts +16 -3
  152. package/src/api/impl/beacon/pool/index.ts +83 -1
  153. package/src/api/impl/debug/index.ts +0 -1
  154. package/src/api/impl/validator/index.ts +82 -1
  155. package/src/chain/blocks/blockInput/blockInput.ts +4 -1
  156. package/src/chain/blocks/importBlock.ts +16 -50
  157. package/src/chain/blocks/importExecutionPayload.ts +51 -20
  158. package/src/chain/blocks/index.ts +32 -15
  159. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +37 -3
  160. package/src/chain/blocks/payloadEnvelopeInput/types.ts +18 -0
  161. package/src/chain/blocks/types.ts +2 -1
  162. package/src/chain/blocks/utils/chainSegment.ts +8 -0
  163. package/src/chain/blocks/verifyBlock.ts +45 -13
  164. package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +6 -4
  165. package/src/chain/blocks/verifyBlocksSanityChecks.ts +16 -6
  166. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +14 -6
  167. package/src/chain/blocks/verifyPayloadsDataAvailability.ts +7 -4
  168. package/src/chain/chain.ts +29 -7
  169. package/src/chain/emitter.ts +0 -11
  170. package/src/chain/errors/proposerPreferences.ts +9 -1
  171. package/src/chain/initState.ts +9 -1
  172. package/src/chain/opPools/payloadAttestationPool.ts +29 -8
  173. package/src/chain/prepareNextSlot.ts +21 -29
  174. package/src/chain/produceBlock/produceBlockBody.ts +45 -27
  175. package/src/chain/regen/queued.ts +2 -7
  176. package/src/chain/regen/regen.ts +2 -7
  177. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +90 -24
  178. package/src/chain/seenCache/seenProposerPreferences.ts +14 -11
  179. package/src/chain/validation/executionPayloadBid.ts +11 -8
  180. package/src/chain/validation/proposerPreferences.ts +37 -18
  181. package/src/network/interface.ts +1 -0
  182. package/src/network/network.ts +11 -0
  183. package/src/network/processor/gossipHandlers.ts +38 -11
  184. package/src/network/processor/index.ts +5 -5
  185. package/src/node/nodejs.ts +2 -2
  186. package/src/node/notifier.ts +1 -8
  187. package/src/sync/constants.ts +4 -4
  188. package/src/sync/range/batch.ts +240 -42
  189. package/src/sync/range/chain.ts +77 -10
  190. package/src/sync/range/range.ts +16 -3
  191. package/src/sync/sync.ts +13 -1
  192. package/src/sync/unknownBlock.ts +170 -60
  193. package/src/sync/utils/downloadByRange.ts +166 -44
  194. package/src/sync/utils/downloadByRoot.ts +12 -0
  195. package/src/util/sszBytes.ts +8 -6
@@ -23,6 +23,7 @@ import {
23
23
  getEffectiveBalancesFromStateBytes,
24
24
  isStatePostAltair,
25
25
  isStatePostElectra,
26
+ isStatePostGloas,
26
27
  } from "@lodestar/state-transition";
27
28
  import {
28
29
  BeaconBlock,
@@ -335,13 +336,6 @@ export class BeaconChain implements IBeaconChain {
335
336
  metrics,
336
337
  logger,
337
338
  });
338
- this.seenPayloadEnvelopeInputCache = new SeenPayloadEnvelopeInput({
339
- chainEvents: emitter,
340
- signal,
341
- serializedCache: this.serializedCache,
342
- metrics,
343
- logger,
344
- });
345
339
 
346
340
  this._earliestAvailableSlot = anchorState.slot;
347
341
 
@@ -397,6 +391,7 @@ export class BeaconChain implements IBeaconChain {
397
391
  metrics,
398
392
  logger
399
393
  );
394
+
400
395
  const regen = new QueuedStateRegenerator({
401
396
  config,
402
397
  forkChoice,
@@ -421,6 +416,33 @@ export class BeaconChain implements IBeaconChain {
421
416
  this.payloadEnvelopeProcessor = new PayloadEnvelopeProcessor(this, metrics, signal);
422
417
 
423
418
  this.forkChoice = forkChoice;
419
+
420
+ this.seenPayloadEnvelopeInputCache = new SeenPayloadEnvelopeInput({
421
+ config,
422
+ clock,
423
+ forkChoice,
424
+ chainEvents: emitter,
425
+ signal,
426
+ serializedCache: this.serializedCache,
427
+ metrics,
428
+ logger,
429
+ });
430
+
431
+ const anchorBlockSlot = anchorState.latestBlockHeader.slot;
432
+ if (isStatePostGloas(anchorState) && anchorBlockSlot > 0) {
433
+ const anchorBid = anchorState.latestExecutionPayloadBid;
434
+ this.seenPayloadEnvelopeInputCache.addFromBid({
435
+ blockRootHex: toRootHex(checkpoint.root),
436
+ slot: anchorBlockSlot,
437
+ forkName: anchorState.forkName,
438
+ proposerIndex: anchorState.latestBlockHeader.proposerIndex,
439
+ bid: anchorBid,
440
+ sampledColumns: this.custodyConfig.sampledColumns,
441
+ custodyColumns: this.custodyConfig.custodyColumns,
442
+ timeCreatedSec: Math.floor(Date.now() / 1000),
443
+ });
444
+ }
445
+
424
446
  this.clock = clock;
425
447
  this.regen = regen;
426
448
  this.bls = bls;
@@ -4,7 +4,6 @@ import {routes} from "@lodestar/api";
4
4
  import {CheckpointWithHex} from "@lodestar/fork-choice";
5
5
  import {IBeaconStateView} from "@lodestar/state-transition";
6
6
  import {DataColumnSidecar, RootHex, deneb, phase0} from "@lodestar/types";
7
- import {SignedExecutionPayloadEnvelope} from "@lodestar/types/gloas";
8
7
  import {PeerIdStr} from "../util/peerId.js";
9
8
  import {BlockInputSource, IBlockInput} from "./blocks/blockInput/types.js";
10
9
  import {PayloadEnvelopeInput} from "./blocks/payloadEnvelopeInput/payloadEnvelopeInput.js";
@@ -60,10 +59,6 @@ export enum ChainEvent {
60
59
  * Post-gloas, missing parent could be a SignedBeaconBlock and/or a SignedExecutionPayloadEnvelope
61
60
  */
62
61
  blockUnknownParent = "blockUnknownParent",
63
- /**
64
- * Trigger BlockInputSync to find a SignedBeaconBlock given a SignedExecutionPayloadEnvelop received
65
- */
66
- envelopeUnknownBlock = "envelopeUnknownBlock",
67
62
  /**
68
63
  * Trigger BlockInputSync to find a SignedBeaconBlock with specified block root.
69
64
  */
@@ -92,11 +87,6 @@ type ApiEvents = {[K in routes.events.EventType]: (data: routes.events.EventData
92
87
 
93
88
  export type ChainEventData = {
94
89
  [ChainEvent.blockUnknownParent]: {blockInput: IBlockInput; peer: PeerIdStr; source: BlockInputSource};
95
- [ChainEvent.envelopeUnknownBlock]: {
96
- envelope: SignedExecutionPayloadEnvelope;
97
- peer?: PeerIdStr;
98
- source: BlockInputSource;
99
- };
100
90
  [ChainEvent.unknownBlockRoot]: {rootHex: RootHex; peer?: PeerIdStr; source: BlockInputSource};
101
91
  [ChainEvent.incompleteBlockInput]: {blockInput: IBlockInput; peer: PeerIdStr; source: BlockInputSource};
102
92
  [ChainEvent.incompletePayloadEnvelope]: {
@@ -124,7 +114,6 @@ export type IChainEvents = ApiEvents & {
124
114
  // Sync events that are chain->chain. Initiated from network requests but do not cross the network
125
115
  // barrier so are considered ChainEvent(s).
126
116
  [ChainEvent.blockUnknownParent]: (data: ChainEventData[ChainEvent.blockUnknownParent]) => void;
127
- [ChainEvent.envelopeUnknownBlock]: (data: ChainEventData[ChainEvent.envelopeUnknownBlock]) => void;
128
117
  [ChainEvent.unknownBlockRoot]: (data: ChainEventData[ChainEvent.unknownBlockRoot]) => void;
129
118
  [ChainEvent.incompleteBlockInput]: (data: ChainEventData[ChainEvent.incompleteBlockInput]) => void;
130
119
  [ChainEvent.incompletePayloadEnvelope]: (data: ChainEventData[ChainEvent.incompletePayloadEnvelope]) => void;
@@ -1,9 +1,10 @@
1
- import {Slot, ValidatorIndex} from "@lodestar/types";
1
+ import {RootHex, Slot, ValidatorIndex} from "@lodestar/types";
2
2
  import {GossipActionError} from "./gossipValidation.js";
3
3
 
4
4
  export enum ProposerPreferencesErrorCode {
5
5
  INVALID_EPOCH = "PROPOSER_PREFERENCES_ERROR_INVALID_EPOCH",
6
6
  PROPOSAL_SLOT_PASSED = "PROPOSER_PREFERENCES_ERROR_PROPOSAL_SLOT_PASSED",
7
+ UNKNOWN_DEPENDENT_ROOT = "PROPOSER_PREFERENCES_ERROR_UNKNOWN_DEPENDENT_ROOT",
7
8
  INVALID_PROPOSER = "PROPOSER_PREFERENCES_ERROR_INVALID_PROPOSER",
8
9
  ALREADY_KNOWN = "PROPOSER_PREFERENCES_ERROR_ALREADY_KNOWN",
9
10
  INVALID_SIGNATURE = "PROPOSER_PREFERENCES_ERROR_INVALID_SIGNATURE",
@@ -20,15 +21,22 @@ export type ProposerPreferencesErrorType =
20
21
  proposalSlot: Slot;
21
22
  currentSlot: Slot;
22
23
  }
24
+ | {
25
+ code: ProposerPreferencesErrorCode.UNKNOWN_DEPENDENT_ROOT;
26
+ proposalSlot: Slot;
27
+ dependentRoot: RootHex;
28
+ }
23
29
  | {
24
30
  code: ProposerPreferencesErrorCode.INVALID_PROPOSER;
25
31
  proposalSlot: Slot;
26
32
  validatorIndex: ValidatorIndex;
33
+ dependentRoot: RootHex;
27
34
  }
28
35
  | {
29
36
  code: ProposerPreferencesErrorCode.ALREADY_KNOWN;
30
37
  proposalSlot: Slot;
31
38
  validatorIndex: ValidatorIndex;
39
+ dependentRoot: RootHex;
32
40
  }
33
41
  | {
34
42
  code: ProposerPreferencesErrorCode.INVALID_SIGNATURE;
@@ -1,7 +1,8 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
- import {ZERO_HASH} from "@lodestar/params";
2
+ import {ForkPostGloas, ForkSeq, ZERO_HASH} from "@lodestar/params";
3
3
  import {
4
4
  BeaconStateAllForks,
5
+ BeaconStateGloas,
5
6
  IBeaconStateView,
6
7
  computeEpochAtSlot,
7
8
  computeStartSlotAtEpoch,
@@ -52,6 +53,13 @@ export function createGenesisBlock(config: ChainForkConfig, genesisState: Beacon
52
53
  const genesisBlock = types.SignedBeaconBlock.defaultValue();
53
54
  const stateRoot = genesisState.hashTreeRoot();
54
55
  genesisBlock.message.stateRoot = stateRoot;
56
+
57
+ if (config.getForkSeq(GENESIS_SLOT) >= ForkSeq.gloas) {
58
+ const gloasBlock = genesisBlock as SignedBeaconBlock<ForkPostGloas>;
59
+ const gloasState = genesisState as BeaconStateGloas;
60
+ gloasBlock.message.body.signedExecutionPayloadBid.message = gloasState.latestExecutionPayloadBid.toValue();
61
+ }
62
+
55
63
  return genesisBlock;
56
64
  }
57
65
 
@@ -1,7 +1,7 @@
1
1
  import {Signature, aggregateSignatures} from "@chainsafe/blst";
2
2
  import {BitArray} from "@chainsafe/ssz";
3
3
  import {ChainForkConfig} from "@lodestar/config";
4
- import {MAX_COMMITTEES_PER_SLOT, PTC_SIZE} from "@lodestar/params";
4
+ import {MAX_COMMITTEES_PER_SLOT, MAX_PAYLOAD_ATTESTATIONS, PTC_SIZE} from "@lodestar/params";
5
5
  import {RootHex, Slot, gloas} from "@lodestar/types";
6
6
  import {MapDef, toRootHex} from "@lodestar/utils";
7
7
  import {Metrics} from "../../metrics/metrics.js";
@@ -95,13 +95,9 @@ export class PayloadAttestationPool {
95
95
 
96
96
  /**
97
97
  * Get payload attestations to be included in a block.
98
- * Pick the top `maxAttestation` number of attestations with the most votes
98
+ * Pick the top `MAX_PAYLOAD_ATTESTATIONS` aggregates with the most votes.
99
99
  */
100
- getPayloadAttestationsForBlock(
101
- beaconBlockRoot: BlockRootHex,
102
- slot: Slot,
103
- maxAttestation: number
104
- ): gloas.PayloadAttestation[] {
100
+ getPayloadAttestationsForBlock(beaconBlockRoot: BlockRootHex, slot: Slot): gloas.PayloadAttestation[] {
105
101
  const aggregateByDataRootByBlockRoot = this.aggregateByDataRootByBlockRootBySlot.get(slot);
106
102
 
107
103
  if (!aggregateByDataRootByBlockRoot) {
@@ -119,7 +115,32 @@ export class PayloadAttestationPool {
119
115
  return Array.from(aggregateByDataRoot.values())
120
116
  .slice()
121
117
  .sort((a, b) => b.aggregationBits.getTrueBitIndexes().length - a.aggregationBits.getTrueBitIndexes().length)
122
- .slice(0, maxAttestation)
118
+ .slice(0, MAX_PAYLOAD_ATTESTATIONS)
119
+ .map(fastToPayloadAttestation);
120
+ }
121
+
122
+ getAll(slot?: Slot): gloas.PayloadAttestation[] {
123
+ const aggregates: AggregateFast[] = [];
124
+
125
+ const addAggregates = (aggregateByDataRootByBlockRoot: Map<BlockRootHex, Map<DataRootHex, AggregateFast>>) => {
126
+ for (const aggregateByDataRoot of aggregateByDataRootByBlockRoot.values()) {
127
+ aggregates.push(...aggregateByDataRoot.values());
128
+ }
129
+ };
130
+
131
+ if (slot !== undefined) {
132
+ const aggregateByDataRootByBlockRoot = this.aggregateByDataRootByBlockRootBySlot.get(slot);
133
+ if (aggregateByDataRootByBlockRoot) {
134
+ addAggregates(aggregateByDataRootByBlockRoot);
135
+ }
136
+ } else {
137
+ for (const aggregateByDataRootByBlockRoot of this.aggregateByDataRootByBlockRootBySlot.values()) {
138
+ addAggregates(aggregateByDataRootByBlockRoot);
139
+ }
140
+ }
141
+
142
+ return aggregates
143
+ .sort((a, b) => b.aggregationBits.getTrueBitIndexes().length - a.aggregationBits.getTrueBitIndexes().length)
123
144
  .map(fastToPayloadAttestation);
124
145
  }
125
146
 
@@ -4,13 +4,14 @@ import {getSafeExecutionBlockHash} from "@lodestar/fork-choice";
4
4
  import {ForkPostBellatrix, ForkSeq, SLOTS_PER_EPOCH, isForkPostBellatrix} from "@lodestar/params";
5
5
  import {
6
6
  IBeaconStateView,
7
+ IBeaconStateViewBellatrix,
7
8
  StateHashTreeRootSource,
8
9
  computeEpochAtSlot,
9
10
  computeTimeAtSlot,
10
11
  isStatePostBellatrix,
11
12
  isStatePostGloas,
12
13
  } from "@lodestar/state-transition";
13
- import {Bytes32, Slot, electra} from "@lodestar/types";
14
+ import {Bytes32, Slot} from "@lodestar/types";
14
15
  import {Logger, fromHex, isErrorAborted, sleep} from "@lodestar/utils";
15
16
  import {GENESIS_SLOT, ZERO_HASH_HEX} from "../constants/constants.js";
16
17
  import {BuilderStatus} from "../execution/builder/http.js";
@@ -165,19 +166,26 @@ export class PrepareNextSlotScheduler {
165
166
  }
166
167
 
167
168
  let parentBlockHash: Bytes32;
168
- let isExtendingPayload = false;
169
+ // Apply parent payload once here as it's reused by EL prep and SSE emit below
170
+ let stateAfterParentPayload: IBeaconStateViewBellatrix = updatedPrepareState;
169
171
  if (isStatePostGloas(updatedPrepareState)) {
170
- isExtendingPayload = this.chain.forkChoice.shouldExtendPayload(updatedHead.blockRoot);
171
- parentBlockHash = isExtendingPayload
172
- ? updatedPrepareState.latestExecutionPayloadBid.blockHash
173
- : updatedPrepareState.latestExecutionPayloadBid.parentBlockHash;
172
+ if (this.chain.forkChoice.shouldExtendPayload(updatedHead.blockRoot)) {
173
+ parentBlockHash = updatedPrepareState.latestExecutionPayloadBid.blockHash;
174
+ // Skip applying parent payload unless we're proposing the next slot or have to emit payload_attributes events
175
+ if (feeRecipient !== undefined || this.chain.opts.emitPayloadAttributes === true) {
176
+ const parentExecutionRequests = await this.chain.getParentExecutionRequests(
177
+ updatedHead.slot,
178
+ updatedHead.blockRoot
179
+ );
180
+ stateAfterParentPayload = updatedPrepareState.withParentPayloadApplied(parentExecutionRequests);
181
+ }
182
+ } else {
183
+ parentBlockHash = updatedPrepareState.latestExecutionPayloadBid.parentBlockHash;
184
+ }
174
185
  } else {
175
186
  parentBlockHash = updatedPrepareState.latestExecutionPayloadHeader.blockHash;
176
187
  }
177
188
 
178
- // Reused by the SSE emit below to avoid a second DB lookup on cache miss
179
- let parentExecutionRequests: electra.ExecutionRequests | undefined;
180
-
181
189
  if (feeRecipient) {
182
190
  const preparationTime =
183
191
  computeTimeAtSlot(this.config, prepareSlot, this.chain.genesisTime) - Date.now() / 1000;
@@ -187,13 +195,6 @@ export class PrepareNextSlotScheduler {
187
195
  const finalizedBlockHash =
188
196
  this.chain.forkChoice.getFinalizedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX;
189
197
 
190
- if (isExtendingPayload) {
191
- parentExecutionRequests = await this.chain.getParentExecutionRequests(
192
- updatedHead.slot,
193
- updatedHead.blockRoot
194
- );
195
- }
196
-
197
198
  // awaiting here instead of throwing an async call because there is no other task
198
199
  // left for scheduler and this gives nice semantics to catch and log errors in the
199
200
  // try/catch wrapper here.
@@ -205,9 +206,8 @@ export class PrepareNextSlotScheduler {
205
206
  parentBlockHash,
206
207
  safeBlockHash,
207
208
  finalizedBlockHash,
208
- updatedPrepareState,
209
- feeRecipient,
210
- parentExecutionRequests
209
+ stateAfterParentPayload,
210
+ feeRecipient
211
211
  );
212
212
  this.logger.verbose("PrepareNextSlotScheduler prepared new payload", {
213
213
  prepareSlot,
@@ -222,7 +222,7 @@ export class PrepareNextSlotScheduler {
222
222
  // and head.parent (proposer-boost-reorg fallback). Anything older is evicted.
223
223
  const updatedHeadParent = this.chain.forkChoice.getBlockHexDefaultStatus(updatedHead.parentRoot);
224
224
  if (updatedHeadParent) {
225
- this.chain.seenPayloadEnvelopeInputCache.pruneBelow(updatedHeadParent.slot);
225
+ this.chain.seenPayloadEnvelopeInputCache.pruneBelowParent(updatedHeadParent);
226
226
  }
227
227
  }
228
228
 
@@ -234,20 +234,12 @@ export class PrepareNextSlotScheduler {
234
234
  (feeRecipient || this.chain.opts.emitPayloadAttributes === true) &&
235
235
  this.chain.emitter.listenerCount(routes.events.EventType.payloadAttributes)
236
236
  ) {
237
- // if we didn't fetch above (not proposing), SSE still needs it here
238
- if (!parentExecutionRequests && isExtendingPayload) {
239
- parentExecutionRequests = await this.chain.getParentExecutionRequests(
240
- updatedHead.slot,
241
- updatedHead.blockRoot
242
- );
243
- }
244
237
  const data = getPayloadAttributesForSSE(fork as ForkPostBellatrix, this.chain, {
245
- prepareState: updatedPrepareState,
238
+ prepareState: stateAfterParentPayload,
246
239
  prepareSlot,
247
240
  parentBlockRoot: fromHex(updatedHead.blockRoot),
248
241
  parentBlockHash,
249
242
  feeRecipient: feeRecipient ?? "0x0000000000000000000000000000000000000000",
250
- parentExecutionRequests,
251
243
  });
252
244
  this.chain.emitter.emit(routes.events.EventType.payloadAttributes, {data, version: fork});
253
245
  }
@@ -49,7 +49,7 @@ import {
49
49
  ssz,
50
50
  } from "@lodestar/types";
51
51
  import {Logger, byteArrayEquals, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
52
- import {ZERO_HASH, ZERO_HASH_HEX} from "../../constants/index.js";
52
+ import {ZERO_HASH_HEX} from "../../constants/index.js";
53
53
  import {numToQuantity} from "../../execution/engine/utils.js";
54
54
  import {
55
55
  IExecutionBuilder,
@@ -111,6 +111,7 @@ export type ProduceFullGloas = {
111
111
  executionRequests: electra.ExecutionRequests;
112
112
  blobsBundle: BlobsBundle<ForkPostGloas>;
113
113
  cells: fulu.Cell[][];
114
+ parentBlockRoot: Root;
114
115
  };
115
116
  export type ProduceFullFulu = {
116
117
  type: BlockType.Full;
@@ -214,19 +215,19 @@ export async function produceBlockBody<T extends BlockType>(
214
215
  });
215
216
 
216
217
  // Get execution payload from EL
218
+ let parentBlockHash: Bytes32;
219
+ let parentExecutionRequests: electra.ExecutionRequests;
220
+ // Apply parent payload once here as it's reused by EL prep and voluntary exit filtering below
221
+ let stateAfterParentPayload: IBeaconStateViewBellatrix = currentState;
217
222
  const isExtendingPayload = this.forkChoice.shouldExtendPayload(toRootHex(parentBlockRoot));
218
- let parentBlockHash = isExtendingPayload
219
- ? currentState.latestExecutionPayloadBid.blockHash
220
- : currentState.latestExecutionPayloadBid.parentBlockHash;
221
- // At gloas genesis the committed bid has no prior EL block to reference
222
- // (`bid.parentBlockHash` is zero). Fall back to `bid.blockHash` (= eth1 genesis hash) so the
223
- // FCU to the EL carries a valid head. Post-genesis bids always reference a non-zero parent.
224
- if (isStatePostGloas(currentState) && byteArrayEquals(parentBlockHash, ZERO_HASH)) {
223
+ if (isExtendingPayload) {
225
224
  parentBlockHash = currentState.latestExecutionPayloadBid.blockHash;
225
+ parentExecutionRequests = await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot);
226
+ stateAfterParentPayload = currentState.withParentPayloadApplied(parentExecutionRequests);
227
+ } else {
228
+ parentBlockHash = currentState.latestExecutionPayloadBid.parentBlockHash;
229
+ parentExecutionRequests = ssz.electra.ExecutionRequests.defaultValue();
226
230
  }
227
- const parentExecutionRequests = isExtendingPayload
228
- ? await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot)
229
- : ssz.electra.ExecutionRequests.defaultValue();
230
231
  const prepareRes = await prepareExecutionPayload(
231
232
  this,
232
233
  this.logger,
@@ -235,9 +236,8 @@ export async function produceBlockBody<T extends BlockType>(
235
236
  parentBlockHash,
236
237
  safeBlockHash,
237
238
  finalizedBlockHash ?? ZERO_HASH_HEX,
238
- currentState,
239
- feeRecipient,
240
- parentExecutionRequests
239
+ stateAfterParentPayload,
240
+ feeRecipient
241
241
  );
242
242
 
243
243
  const {prepType, payloadId} = prepareRes;
@@ -291,9 +291,19 @@ export async function produceBlockBody<T extends BlockType>(
291
291
  const commonBlockBody = await commonBlockBodyPromise;
292
292
  const gloasBody = Object.assign({}, commonBlockBody) as gloas.BeaconBlockBody;
293
293
  gloasBody.signedExecutionPayloadBid = signedBid;
294
- // TODO GLOAS: Get payload attestations from pool for previous slot
295
- gloasBody.payloadAttestations = [];
294
+ gloasBody.payloadAttestations = this.payloadAttestationPool.getPayloadAttestationsForBlock(
295
+ parentBlock.blockRoot,
296
+ blockSlot - 1
297
+ );
296
298
  gloasBody.parentExecutionRequests = parentExecutionRequests;
299
+ // Drop voluntary exits that parent_execution_requests have invalidated (e.g. a withdrawal
300
+ // request initiating an exit on the same validator). Op pool selected against the unapplied
301
+ // state, so re-validate against the post-apply state to avoid producing an invalid block.
302
+ if (isExtendingPayload && commonBlockBody.voluntaryExits.length > 0) {
303
+ gloasBody.voluntaryExits = commonBlockBody.voluntaryExits.filter((signedVoluntaryExit) =>
304
+ stateAfterParentPayload.isValidVoluntaryExit(signedVoluntaryExit, false)
305
+ );
306
+ }
297
307
  blockBody = gloasBody as AssembledBodyType<T>;
298
308
 
299
309
  // Store execution payload data required to construct execution payload envelope later
@@ -302,6 +312,7 @@ export async function produceBlockBody<T extends BlockType>(
302
312
  gloasResult.executionRequests = executionRequests;
303
313
  gloasResult.blobsBundle = blobsBundle;
304
314
  gloasResult.cells = cells;
315
+ gloasResult.parentBlockRoot = fromHex(parentBlock.blockRoot);
305
316
 
306
317
  const fetchedTime = Date.now() / 1000 - computeTimeAtSlot(this.config, blockSlot, this.genesisTime);
307
318
  this.metrics?.blockPayload.payloadFetchedTime.observe({prepType}, fetchedTime);
@@ -629,9 +640,12 @@ export async function prepareExecutionPayload(
629
640
  parentBlockHash: Bytes32,
630
641
  safeBlockHash: RootHex,
631
642
  finalizedBlockHash: RootHex,
643
+ /**
644
+ * Post-gloas, when extending a full parent, callers must apply
645
+ * parent execution payload first (see `withParentPayloadApplied`).
646
+ */
632
647
  state: IBeaconStateViewBellatrix,
633
- suggestedFeeRecipient: string,
634
- parentExecutionRequests?: electra.ExecutionRequests
648
+ suggestedFeeRecipient: string
635
649
  ): Promise<{prepType: PayloadPreparationType; payloadId: PayloadId}> {
636
650
  const timestamp = computeTimeAtSlot(chain.config, state.slot, state.genesisTime);
637
651
  const prevRandao = state.getRandaoMix(state.epoch);
@@ -668,7 +682,6 @@ export async function prepareExecutionPayload(
668
682
  parentBlockRoot,
669
683
  parentBlockHash,
670
684
  feeRecipient: suggestedFeeRecipient,
671
- parentExecutionRequests,
672
685
  });
673
686
 
674
687
  payloadId = await chain.executionEngine.notifyForkchoiceUpdate(
@@ -727,14 +740,16 @@ export function getPayloadAttributesForSSE(
727
740
  parentBlockRoot,
728
741
  parentBlockHash,
729
742
  feeRecipient,
730
- parentExecutionRequests,
731
743
  }: {
744
+ /**
745
+ * Post-gloas, when extending a full parent, callers must apply
746
+ * parent execution payload first (see `withParentPayloadApplied`).
747
+ */
732
748
  prepareState: IBeaconStateViewBellatrix;
733
749
  prepareSlot: Slot;
734
750
  parentBlockRoot: Root;
735
751
  parentBlockHash: Bytes32;
736
752
  feeRecipient: string;
737
- parentExecutionRequests?: electra.ExecutionRequests;
738
753
  }
739
754
  ): SSEPayloadAttributes {
740
755
  const payloadAttributes = preparePayloadAttributes(fork, chain, {
@@ -743,7 +758,6 @@ export function getPayloadAttributesForSSE(
743
758
  parentBlockRoot,
744
759
  parentBlockHash,
745
760
  feeRecipient,
746
- parentExecutionRequests,
747
761
  });
748
762
 
749
763
  let parentBlockNumber: number;
@@ -782,14 +796,16 @@ function preparePayloadAttributes(
782
796
  parentBlockRoot,
783
797
  parentBlockHash,
784
798
  feeRecipient,
785
- parentExecutionRequests,
786
799
  }: {
800
+ /**
801
+ * Post-gloas, when extending a full parent, callers must apply
802
+ * parent execution payload first (see `withParentPayloadApplied`).
803
+ */
787
804
  prepareState: IBeaconStateViewBellatrix;
788
805
  prepareSlot: Slot;
789
806
  parentBlockRoot: Root;
790
807
  parentBlockHash: Bytes32;
791
808
  feeRecipient: string;
792
- parentExecutionRequests?: electra.ExecutionRequests;
793
809
  }
794
810
  ): SSEPayloadAttributes["payloadAttributes"] {
795
811
  const timestamp = computeTimeAtSlot(chain.config, prepareSlot, prepareState.genesisTime);
@@ -808,11 +824,13 @@ function preparePayloadAttributes(
808
824
  if (isStatePostGloas(prepareState)) {
809
825
  const isExtendingPayload = byteArrayEquals(parentBlockHash, prepareState.latestExecutionPayloadBid.blockHash);
810
826
  if (isExtendingPayload) {
811
- if (parentExecutionRequests === undefined) {
812
- throw new Error("parentExecutionRequests required when extending full parent");
827
+ // applyParentExecutionPayload sets latestBlockHash = parentBid.blockHash, so a mismatch
828
+ // here means the caller did not apply parent payload to prepareState
829
+ if (!byteArrayEquals(prepareState.latestBlockHash, prepareState.latestExecutionPayloadBid.blockHash)) {
830
+ throw new Error("Expected state with parent execution payload applied for withdrawals");
813
831
  }
814
832
  (payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
815
- prepareState.getExpectedWithdrawalsForFullParent(parentExecutionRequests);
833
+ prepareState.getExpectedWithdrawals().expectedWithdrawals;
816
834
  } else {
817
835
  // When the parent block is empty, state.payloadExpectedWithdrawals holds a batch
818
836
  // already deducted from CL balances but never credited on the EL (the envelope
@@ -1,7 +1,7 @@
1
1
  import {routes} from "@lodestar/api";
2
2
  import {IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
3
3
  import {IBeaconStateView, computeEpochAtSlot} from "@lodestar/state-transition";
4
- import {BeaconBlock, Epoch, RootHex, Slot, isGloasBeaconBlock, phase0} from "@lodestar/types";
4
+ import {BeaconBlock, Epoch, RootHex, Slot, phase0} from "@lodestar/types";
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";
@@ -88,12 +88,7 @@ export class QueuedStateRegenerator implements IStateRegenerator {
88
88
  */
89
89
  getPreStateSync(block: BeaconBlock): IBeaconStateView | null {
90
90
  const parentRoot = toRootHex(block.parentRoot);
91
- const parentBlock = isGloasBeaconBlock(block)
92
- ? this.forkChoice.getBlockHexAndBlockHash(
93
- parentRoot,
94
- toRootHex(block.body.signedExecutionPayloadBid.message.parentBlockHash)
95
- )
96
- : this.forkChoice.getBlockHexDefaultStatus(parentRoot);
91
+ const parentBlock = this.forkChoice.getBlockHexDefaultStatus(parentRoot);
97
92
  if (!parentBlock) {
98
93
  throw new RegenError({
99
94
  code: RegenErrorCode.BLOCK_NOT_IN_FORKCHOICE,
@@ -9,7 +9,7 @@ import {
9
9
  computeEpochAtSlot,
10
10
  computeStartSlotAtEpoch,
11
11
  } from "@lodestar/state-transition";
12
- import {BeaconBlock, RootHex, SignedBeaconBlock, Slot, isGloasBeaconBlock} from "@lodestar/types";
12
+ import {BeaconBlock, RootHex, SignedBeaconBlock, Slot} from "@lodestar/types";
13
13
  import {Logger, fromHex, toRootHex} from "@lodestar/utils";
14
14
  import {IBeaconDb} from "../../db/index.js";
15
15
  import {Metrics} from "../../metrics/index.js";
@@ -57,12 +57,7 @@ export class StateRegenerator implements IStateRegeneratorInternal {
57
57
  regenCaller: RegenCaller
58
58
  ): Promise<IBeaconStateView> {
59
59
  const parentRoot = toRootHex(block.parentRoot);
60
- const parentBlock = isGloasBeaconBlock(block)
61
- ? this.modules.forkChoice.getBlockHexAndBlockHash(
62
- parentRoot,
63
- toRootHex(block.body.signedExecutionPayloadBid.message.parentBlockHash)
64
- )
65
- : this.modules.forkChoice.getBlockHexDefaultStatus(parentRoot);
60
+ const parentBlock = this.modules.forkChoice.getBlockHexDefaultStatus(parentRoot);
66
61
  if (!parentBlock) {
67
62
  throw new RegenError({
68
63
  code: RegenErrorCode.BLOCK_NOT_IN_FORKCHOICE,