@lodestar/beacon-node 1.48.0-dev.84285dbabf → 1.48.0-dev.84db2dcfe4

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 (102) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +0 -7
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
  5. package/lib/api/impl/beacon/state/utils.js +14 -0
  6. package/lib/api/impl/beacon/state/utils.js.map +1 -1
  7. package/lib/api/impl/config/index.d.ts.map +1 -1
  8. package/lib/api/impl/config/index.js +4 -1
  9. package/lib/api/impl/config/index.js.map +1 -1
  10. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  11. package/lib/chain/archiveStore/archiveStore.js +4 -1
  12. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  13. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts +9 -1
  14. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
  15. package/lib/chain/archiveStore/utils/archiveBlocks.js +37 -4
  16. package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
  17. package/lib/chain/blocks/importBlock.d.ts +5 -1
  18. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  19. package/lib/chain/blocks/importBlock.js +12 -5
  20. package/lib/chain/blocks/importBlock.js.map +1 -1
  21. package/lib/chain/blocks/index.d.ts +8 -4
  22. package/lib/chain/blocks/index.d.ts.map +1 -1
  23. package/lib/chain/blocks/index.js +80 -15
  24. package/lib/chain/blocks/index.js.map +1 -1
  25. package/lib/chain/blocks/types.d.ts +8 -0
  26. package/lib/chain/blocks/types.d.ts.map +1 -1
  27. package/lib/chain/blocks/types.js.map +1 -1
  28. package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
  29. package/lib/chain/blocks/utils/chainSegment.js +16 -10
  30. package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
  31. package/lib/chain/chain.d.ts +2 -2
  32. package/lib/chain/chain.d.ts.map +1 -1
  33. package/lib/chain/chain.js +2 -2
  34. package/lib/chain/chain.js.map +1 -1
  35. package/lib/chain/errors/executionPayloadBid.d.ts +4 -0
  36. package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
  37. package/lib/chain/errors/executionPayloadBid.js +1 -0
  38. package/lib/chain/errors/executionPayloadBid.js.map +1 -1
  39. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  40. package/lib/chain/forkChoice/index.js +2 -0
  41. package/lib/chain/forkChoice/index.js.map +1 -1
  42. package/lib/chain/interface.d.ts +2 -2
  43. package/lib/chain/interface.d.ts.map +1 -1
  44. package/lib/chain/lightClient/index.d.ts +3 -3
  45. package/lib/chain/lightClient/index.d.ts.map +1 -1
  46. package/lib/chain/lightClient/index.js +21 -23
  47. package/lib/chain/lightClient/index.js.map +1 -1
  48. package/lib/chain/lightClient/proofs.d.ts +2 -1
  49. package/lib/chain/lightClient/proofs.d.ts.map +1 -1
  50. package/lib/chain/lightClient/proofs.js +5 -1
  51. package/lib/chain/lightClient/proofs.js.map +1 -1
  52. package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
  53. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  54. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  55. package/lib/chain/validation/executionPayloadBid.js +56 -40
  56. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  57. package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
  58. package/lib/chain/validation/payloadAttestationMessage.js +5 -6
  59. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  60. package/lib/execution/builder/validateBid.d.ts.map +1 -1
  61. package/lib/execution/builder/validateBid.js +8 -5
  62. package/lib/execution/builder/validateBid.js.map +1 -1
  63. package/lib/metrics/metrics/lodestar.d.ts +4 -3
  64. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  65. package/lib/metrics/metrics/lodestar.js +5 -5
  66. package/lib/metrics/metrics/lodestar.js.map +1 -1
  67. package/lib/network/network.js +2 -2
  68. package/lib/network/network.js.map +1 -1
  69. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  70. package/lib/network/processor/gossipHandlers.js +382 -278
  71. package/lib/network/processor/gossipHandlers.js.map +1 -1
  72. package/lib/network/processor/index.d.ts.map +1 -1
  73. package/lib/network/processor/index.js +11 -16
  74. package/lib/network/processor/index.js.map +1 -1
  75. package/lib/sync/range/range.d.ts.map +1 -1
  76. package/lib/sync/range/range.js +24 -1
  77. package/lib/sync/range/range.js.map +1 -1
  78. package/package.json +14 -14
  79. package/src/api/impl/beacon/blocks/index.ts +0 -10
  80. package/src/api/impl/beacon/state/utils.ts +15 -0
  81. package/src/api/impl/config/index.ts +4 -1
  82. package/src/chain/archiveStore/archiveStore.ts +6 -0
  83. package/src/chain/archiveStore/utils/archiveBlocks.ts +39 -3
  84. package/src/chain/blocks/importBlock.ts +25 -7
  85. package/src/chain/blocks/index.ts +107 -21
  86. package/src/chain/blocks/types.ts +9 -0
  87. package/src/chain/blocks/utils/chainSegment.ts +16 -11
  88. package/src/chain/chain.ts +4 -4
  89. package/src/chain/errors/executionPayloadBid.ts +2 -0
  90. package/src/chain/forkChoice/index.ts +2 -0
  91. package/src/chain/interface.ts +2 -2
  92. package/src/chain/lightClient/index.ts +32 -36
  93. package/src/chain/lightClient/proofs.ts +10 -0
  94. package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
  95. package/src/chain/validation/executionPayloadBid.ts +62 -44
  96. package/src/chain/validation/payloadAttestationMessage.ts +8 -9
  97. package/src/execution/builder/validateBid.ts +10 -6
  98. package/src/metrics/metrics/lodestar.ts +6 -5
  99. package/src/network/network.ts +2 -2
  100. package/src/network/processor/gossipHandlers.ts +489 -344
  101. package/src/network/processor/index.ts +11 -17
  102. package/src/sync/range/range.ts +23 -1
@@ -5,6 +5,7 @@ import {
5
5
  ForkName,
6
6
  ForkPostAltair,
7
7
  ForkPostBellatrix,
8
+ ForkPostGloas,
8
9
  ForkPreGloas,
9
10
  ForkSeq,
10
11
  MIN_SYNC_COMMITTEE_PARTICIPANTS,
@@ -40,7 +41,6 @@ import {
40
41
  Slot,
41
42
  SyncPeriod,
42
43
  altair,
43
- electra,
44
44
  phase0,
45
45
  ssz,
46
46
  } from "@lodestar/types";
@@ -52,7 +52,12 @@ import {Metrics} from "../../metrics/index.js";
52
52
  import {IClock} from "../../util/clock.js";
53
53
  import {ChainEventEmitter} from "../emitter.js";
54
54
  import {LightClientServerError, LightClientServerErrorCode} from "../errors/lightClientError.js";
55
- import {getBlockBodyExecutionHeaderProof, getCurrentSyncCommitteeBranch, getNextSyncCommitteeBranch} from "./proofs.js";
55
+ import {
56
+ getBlockBodyExecutionBlockHashProof,
57
+ getBlockBodyExecutionHeaderProof,
58
+ getCurrentSyncCommitteeBranch,
59
+ getNextSyncCommitteeBranch,
60
+ } from "./proofs.js";
56
61
 
57
62
  export type LightClientServerOpts = {
58
63
  disableLightClientServerOnImportBlockHead?: boolean;
@@ -61,6 +66,7 @@ export type LightClientServerOpts = {
61
66
 
62
67
  type DependentRootHex = RootHex;
63
68
  type BlockRooHex = RootHex;
69
+ type LightClientZeroFinality = Pick<LightClientUpdate, "finalityBranch" | "finalizedHeader">;
64
70
 
65
71
  export type SyncAttestedData = {
66
72
  attestedHeader: LightClientHeader;
@@ -199,6 +205,7 @@ export class LightClientServer {
199
205
  private readonly clock: IClock;
200
206
  private readonly signal: AbortSignal;
201
207
  private readonly knownSyncCommittee = new MapDef<SyncPeriod, Set<DependentRootHex>>(() => new Set());
208
+ private readonly zeroFinalityByFork = new Map<ForkName, LightClientZeroFinality>();
202
209
  private storedCurrentSyncCommittee = false;
203
210
 
204
211
  /**
@@ -208,10 +215,6 @@ export class LightClientServer {
208
215
  private checkpointHeaders = new Map<BlockRooHex, LightClientHeader>();
209
216
  private latestHeadUpdate: LightClientOptimisticUpdate | null = null;
210
217
 
211
- private readonly zero: Pick<
212
- altair.LightClientUpdate | electra.LightClientUpdate,
213
- "finalityBranch" | "finalizedHeader"
214
- >;
215
218
  private finalized: LightClientFinalityUpdate | null = null;
216
219
 
217
220
  constructor(
@@ -227,14 +230,6 @@ export class LightClientServer {
227
230
  this.logger = logger;
228
231
  this.signal = signal;
229
232
 
230
- this.zero = {
231
- // Assign the highest pre-Gloas light-client header because post-Gloas light-client updates are skipped for now.
232
- finalizedHeader: ssz.electra.LightClientHeader.defaultValue(),
233
- // Electra finalityBranch has fixed length of 5 whereas altair has 4. The fifth element will be ignored
234
- // when serializing as altair LightClientUpdate
235
- finalityBranch: ssz.electra.LightClientUpdate.fields.finalityBranch.defaultValue(),
236
- };
237
-
238
233
  if (metrics) {
239
234
  metrics.lightclientServer.highestSlot.addCollect(() => {
240
235
  if (this.latestHeadUpdate) {
@@ -270,14 +265,6 @@ export class LightClientServer {
270
265
  return;
271
266
  }
272
267
 
273
- // TODO GLOAS: Light client updates for gloas are not yet updated in the spec.
274
- // The block body no longer contains execution payload, so `blockToLightClientHeader`
275
- // cannot construct a header from a gloas block. Skip all light client processing
276
- // for post-gloas blocks, revisit once there is a spec for it.
277
- if (this.config.getForkSeq(block.slot) >= ForkSeq.gloas) {
278
- return;
279
- }
280
-
281
268
  // What is the syncAggregate signing?
282
269
  // From the state-transition
283
270
  // ```
@@ -695,9 +682,16 @@ export class LightClientServer {
695
682
  }
696
683
  } else {
697
684
  isFinalized = false;
698
- finalityBranch = this.zero.finalityBranch;
699
- // No need to upgrade finalizedHeader because its anyway set to zero of highest fork
700
- finalizedHeader = this.zero.finalizedHeader;
685
+ let zeroFinality = this.zeroFinalityByFork.get(attestedFork);
686
+ if (!zeroFinality) {
687
+ const {finalityBranch, finalizedHeader} = this.config
688
+ .getPostAltairForkTypes(attestedHeader.beacon.slot)
689
+ .LightClientUpdate.defaultValue();
690
+ zeroFinality = {finalityBranch, finalizedHeader};
691
+ this.zeroFinalityByFork.set(attestedFork, zeroFinality);
692
+ }
693
+ finalityBranch = zeroFinality.finalityBranch;
694
+ finalizedHeader = zeroFinality.finalizedHeader;
701
695
  }
702
696
 
703
697
  const newUpdate = {
@@ -769,22 +763,24 @@ export function sumBits(bits: BitArray): number {
769
763
  return bits.getTrueBitIndexes().length;
770
764
  }
771
765
 
772
- // TODO GLOAS: Pending light-client spec but this function probably won't be used
773
- // in Gloas. So we can assume any types here are pre-gloas
774
- export function blockToLightClientHeader(
775
- fork: ForkName,
776
- block: BeaconBlock<ForkPostAltair & ForkPreGloas>
777
- ): LightClientHeader {
778
- const blockSlot = block.slot;
766
+ export function blockToLightClientHeader(fork: ForkName, block: BeaconBlock<ForkPostAltair>): LightClientHeader {
779
767
  const beacon: phase0.BeaconBlockHeader = {
780
- slot: blockSlot,
768
+ slot: block.slot,
781
769
  proposerIndex: block.proposerIndex,
782
770
  parentRoot: block.parentRoot,
783
771
  stateRoot: block.stateRoot,
784
- bodyRoot: (ssz[fork].BeaconBlockBody as SSZTypesFor<ForkPostAltair & ForkPreGloas, "BeaconBlockBody">).hashTreeRoot(
785
- block.body
786
- ),
772
+ bodyRoot: (ssz[fork].BeaconBlockBody as SSZTypesFor<ForkPostAltair, "BeaconBlockBody">).hashTreeRoot(block.body),
787
773
  };
774
+
775
+ if (isForkPostGloas(fork)) {
776
+ const blockBody = block.body as BeaconBlockBody<ForkPostGloas>;
777
+ return {
778
+ beacon,
779
+ executionBlockHash: blockBody.signedExecutionPayloadBid.message.parentBlockHash,
780
+ executionBranch: getBlockBodyExecutionBlockHashProof(fork, blockBody),
781
+ };
782
+ }
783
+
788
784
  if (ForkSeq[fork] >= ForkSeq.capella) {
789
785
  const blockBody = block.body as BeaconBlockBody<ForkPostBellatrix & ForkPreGloas>;
790
786
  const execution = executionPayloadToPayloadHeader(ForkSeq[fork], blockBody.executionPayload);
@@ -2,11 +2,13 @@ import {Tree} from "@chainsafe/persistent-merkle-tree";
2
2
  import {
3
3
  BLOCK_BODY_EXECUTION_PAYLOAD_GINDEX,
4
4
  CURRENT_SYNC_COMMITTEE_GINDEX_GLOAS,
5
+ EXECUTION_BLOCK_HASH_GINDEX_GLOAS,
5
6
  FINALIZED_ROOT_GINDEX,
6
7
  FINALIZED_ROOT_GINDEX_ELECTRA,
7
8
  FINALIZED_ROOT_GINDEX_GLOAS,
8
9
  ForkName,
9
10
  ForkPostBellatrix,
11
+ ForkPostGloas,
10
12
  NEXT_SYNC_COMMITTEE_GINDEX_GLOAS,
11
13
  isForkPostElectra,
12
14
  isForkPostGloas,
@@ -116,3 +118,11 @@ export function getBlockBodyExecutionHeaderProof(
116
118
  const bodyView = (ssz[fork].BeaconBlockBody as SSZTypesFor<ForkPostBellatrix, "BeaconBlockBody">).toView(body);
117
119
  return new Tree(bodyView.node).getSingleProof(BigInt(BLOCK_BODY_EXECUTION_PAYLOAD_GINDEX));
118
120
  }
121
+
122
+ export function getBlockBodyExecutionBlockHashProof(
123
+ fork: ForkPostGloas,
124
+ body: BeaconBlockBody<ForkPostGloas>
125
+ ): Uint8Array[] {
126
+ const bodyView = (ssz[fork].BeaconBlockBody as SSZTypesFor<ForkPostGloas, "BeaconBlockBody">).toView(body);
127
+ return new Tree(bodyView.node).getSingleProof(BigInt(EXECUTION_BLOCK_HASH_GINDEX_GLOAS));
128
+ }
@@ -363,7 +363,7 @@ export class AggregatedAttestationPool {
363
363
  stateEpoch,
364
364
  rootCache,
365
365
  gloasState?.executionPayloadAvailability ?? null,
366
- gloasState?.latestExecutionPayloadBid.slot ?? null
366
+ gloasState?.latestBlockHeader.slot ?? null
367
367
  );
368
368
 
369
369
  const weight =
@@ -104,6 +104,37 @@ export async function validateApiExecutionPayloadBid(
104
104
  const bid = signedExecutionPayloadBid.message;
105
105
  const bidParentBlockRoot = toRootHex(bid.parentBlockRoot);
106
106
 
107
+ // [REJECT] `bid.execution_payment` is zero.
108
+ if (bid.executionPayment !== 0n) {
109
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
110
+ code: ExecutionPayloadBidErrorCode.NON_ZERO_EXECUTION_PAYMENT,
111
+ builderIndex: bid.builderIndex,
112
+ executionPayment: bid.executionPayment,
113
+ });
114
+ }
115
+
116
+ // [REJECT] The bid's block hash is not equal to its parent block hash -- i.e.
117
+ // `bid.block_hash != bid.parent_block_hash`.
118
+ if (byteArrayEquals(bid.blockHash, bid.parentBlockHash)) {
119
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
120
+ code: ExecutionPayloadBidErrorCode.BLOCK_HASH_EQUALS_PARENT_BLOCK_HASH,
121
+ blockHash: toRootHex(bid.blockHash),
122
+ });
123
+ }
124
+
125
+ // [REJECT] The length of KZG commitments is less than or equal to the limitation defined in the
126
+ // consensus layer -- i.e. validate that
127
+ // `len(bid.blob_kzg_commitments) <= get_blob_parameters(compute_epoch_at_slot(bid.slot)).max_blobs_per_block`.
128
+ const blobKzgCommitmentsLen = bid.blobKzgCommitments.length;
129
+ const maxBlobsPerBlock = chain.config.getMaxBlobsPerBlock(computeEpochAtSlot(bid.slot));
130
+ if (blobKzgCommitmentsLen > maxBlobsPerBlock) {
131
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
132
+ code: ExecutionPayloadBidErrorCode.TOO_MANY_KZG_COMMITMENTS,
133
+ blobKzgCommitmentsLen,
134
+ commitmentLimit: maxBlobsPerBlock,
135
+ });
136
+ }
137
+
107
138
  // [IGNORE] `bid.slot` is the current slot, or the next slot (`bid.slot - 1` is current), allowing for `MAXIMUM_GOSSIP_CLOCK_DISPARITY`.
108
139
  if (
109
140
  !chain.clock.isCurrentSlotGivenGossipDisparity(bid.slot) &&
@@ -138,28 +169,6 @@ export async function validateApiExecutionPayloadBid(
138
169
  });
139
170
  }
140
171
 
141
- // [REJECT] `bid.execution_payment` is zero.
142
- if (bid.executionPayment !== 0n) {
143
- throw new ExecutionPayloadBidError(GossipAction.REJECT, {
144
- code: ExecutionPayloadBidErrorCode.NON_ZERO_EXECUTION_PAYMENT,
145
- builderIndex: bid.builderIndex,
146
- executionPayment: bid.executionPayment,
147
- });
148
- }
149
-
150
- // [REJECT] The length of KZG commitments is less than or equal to the limitation defined in the
151
- // consensus layer -- i.e. validate that
152
- // `len(bid.blob_kzg_commitments) <= get_blob_parameters(compute_epoch_at_slot(bid.slot)).max_blobs_per_block`.
153
- const blobKzgCommitmentsLen = bid.blobKzgCommitments.length;
154
- const maxBlobsPerBlock = chain.config.getMaxBlobsPerBlock(computeEpochAtSlot(bid.slot));
155
- if (blobKzgCommitmentsLen > maxBlobsPerBlock) {
156
- throw new ExecutionPayloadBidError(GossipAction.REJECT, {
157
- code: ExecutionPayloadBidErrorCode.TOO_MANY_KZG_COMMITMENTS,
158
- blobKzgCommitmentsLen,
159
- commitmentLimit: maxBlobsPerBlock,
160
- });
161
- }
162
-
163
172
  const state = await chain.regen
164
173
  .getBlockSlotState(parentBlock, bid.slot, {dontTransferCache: true}, RegenCaller.validateApiExecutionPayloadBid)
165
174
  .catch(() => {
@@ -257,6 +266,37 @@ async function validateExecutionPayloadBid(
257
266
  const bidParentBlockRoot = toRootHex(bid.parentBlockRoot);
258
267
  const bidParentBlockHash = toRootHex(bid.parentBlockHash);
259
268
 
269
+ // [REJECT] `bid.execution_payment` is zero.
270
+ if (bid.executionPayment !== 0n) {
271
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
272
+ code: ExecutionPayloadBidErrorCode.NON_ZERO_EXECUTION_PAYMENT,
273
+ builderIndex: bid.builderIndex,
274
+ executionPayment: bid.executionPayment,
275
+ });
276
+ }
277
+
278
+ // [REJECT] The bid's block hash is not equal to its parent block hash -- i.e.
279
+ // `bid.block_hash != bid.parent_block_hash`.
280
+ if (byteArrayEquals(bid.blockHash, bid.parentBlockHash)) {
281
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
282
+ code: ExecutionPayloadBidErrorCode.BLOCK_HASH_EQUALS_PARENT_BLOCK_HASH,
283
+ blockHash: toRootHex(bid.blockHash),
284
+ });
285
+ }
286
+
287
+ // [REJECT] The length of KZG commitments is less than or equal to the limitation defined in the
288
+ // consensus layer -- i.e. validate that
289
+ // `len(bid.blob_kzg_commitments) <= get_blob_parameters(compute_epoch_at_slot(bid.slot)).max_blobs_per_block`.
290
+ const blobKzgCommitmentsLen = bid.blobKzgCommitments.length;
291
+ const maxBlobsPerBlock = chain.config.getMaxBlobsPerBlock(computeEpochAtSlot(bid.slot));
292
+ if (blobKzgCommitmentsLen > maxBlobsPerBlock) {
293
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
294
+ code: ExecutionPayloadBidErrorCode.TOO_MANY_KZG_COMMITMENTS,
295
+ blobKzgCommitmentsLen,
296
+ commitmentLimit: maxBlobsPerBlock,
297
+ });
298
+ }
299
+
260
300
  // [IGNORE] `bid.slot` is the current slot, or the next slot (`bid.slot - 1` is current), allowing for `MAXIMUM_GOSSIP_CLOCK_DISPARITY`.
261
301
  if (
262
302
  !chain.clock.isCurrentSlotGivenGossipDisparity(bid.slot) &&
@@ -402,15 +442,6 @@ async function validateExecutionPayloadBid(
402
442
  });
403
443
  }
404
444
 
405
- // [REJECT] `bid.execution_payment` is zero.
406
- if (bid.executionPayment !== 0n) {
407
- throw new ExecutionPayloadBidError(GossipAction.REJECT, {
408
- code: ExecutionPayloadBidErrorCode.NON_ZERO_EXECUTION_PAYMENT,
409
- builderIndex: bid.builderIndex,
410
- executionPayment: bid.executionPayment,
411
- });
412
- }
413
-
414
445
  // [IGNORE] `bid.fee_recipient == proposer_preferences.fee_recipient`.
415
446
  if (!byteArrayEquals(bid.feeRecipient, proposerPreferences.message.feeRecipient)) {
416
447
  throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
@@ -451,19 +482,6 @@ async function validateExecutionPayloadBid(
451
482
  });
452
483
  }
453
484
 
454
- // [REJECT] The length of KZG commitments is less than or equal to the limitation defined in the
455
- // consensus layer -- i.e. validate that
456
- // `len(bid.blob_kzg_commitments) <= get_blob_parameters(compute_epoch_at_slot(bid.slot)).max_blobs_per_block`.
457
- const blobKzgCommitmentsLen = bid.blobKzgCommitments.length;
458
- const maxBlobsPerBlock = chain.config.getMaxBlobsPerBlock(computeEpochAtSlot(bid.slot));
459
- if (blobKzgCommitmentsLen > maxBlobsPerBlock) {
460
- throw new ExecutionPayloadBidError(GossipAction.REJECT, {
461
- code: ExecutionPayloadBidErrorCode.TOO_MANY_KZG_COMMITMENTS,
462
- blobKzgCommitmentsLen,
463
- commitmentLimit: maxBlobsPerBlock,
464
- });
465
- }
466
-
467
485
  // [IGNORE] this bid is the highest value bid seen for the tuple
468
486
  // `(bid.slot, bid.parent_block_hash, bid.parent_block_root)`.
469
487
  // As a DoS prevention measure, the bid must also exceed the current highest bid by a minimum
@@ -7,7 +7,6 @@ import {RootHex, gloas, ssz} from "@lodestar/types";
7
7
  import {toRootHex} from "@lodestar/utils";
8
8
  import {GossipAction, PayloadAttestationError, PayloadAttestationErrorCode} from "../errors/index.js";
9
9
  import {IBeaconChain} from "../index.js";
10
- import {RegenCaller} from "../regen/index.js";
11
10
 
12
11
  export type PayloadAttestationValidationResult = {
13
12
  attDataRootHex: RootHex;
@@ -82,15 +81,15 @@ async function validatePayloadAttestationMessage(
82
81
  // TODO GLOAS: implement this. Technically if we cannot get proto block from fork choice,
83
82
  // it is possible that the block didn't pass the validation
84
83
 
85
- // Use the referenced block's branch state for the PTC committee check
86
- const state = await chain.regen
87
- .getBlockSlotState(block, data.slot, {dontTransferCache: true}, RegenCaller.validateGossipPayloadAttestationMessage)
88
- .catch(() => {
89
- throw new PayloadAttestationError(GossipAction.IGNORE, {
90
- code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT,
91
- blockRoot: toRootHex(data.beaconBlockRoot),
92
- });
84
+ // block.slot === data.slot is enforced above, so use the block's post-state directly to avoid
85
+ // getting through regen queue
86
+ const state = chain.regen.getStateSync(block.stateRoot);
87
+ if (state == null) {
88
+ throw new PayloadAttestationError(GossipAction.IGNORE, {
89
+ code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT,
90
+ blockRoot: toRootHex(data.beaconBlockRoot),
93
91
  });
92
+ }
94
93
 
95
94
  if (!isStatePostGloas(state)) {
96
95
  throw new Error(`Expected gloas+ state for payload attestation validation, got fork=${state.forkName}`);
@@ -56,6 +56,16 @@ export async function validateBuilderApiExecutionPayloadBid(
56
56
  );
57
57
  }
58
58
 
59
+ if (byteArrayEquals(bid.blockHash, bid.parentBlockHash)) {
60
+ throw Error(`Bid block hash ${toRootHex(bid.blockHash)} must not equal its parent block hash`);
61
+ }
62
+
63
+ const blobKzgCommitmentsLen = bid.blobKzgCommitments.length;
64
+ const maxBlobsPerBlock = chain.config.getMaxBlobsPerBlock(computeEpochAtSlot(bid.slot));
65
+ if (blobKzgCommitmentsLen > maxBlobsPerBlock) {
66
+ throw Error(`Bid has too many KZG commitments len=${blobKzgCommitmentsLen} limit=${maxBlobsPerBlock}`);
67
+ }
68
+
59
69
  const totalPayment = getBuilderBidTotalGwei(bid, entry.maxExecutionPayment);
60
70
  if (totalPayment < entry.minBid) {
61
71
  throw Error(
@@ -100,12 +110,6 @@ export async function validateBuilderApiExecutionPayloadBid(
100
110
  );
101
111
  }
102
112
 
103
- const blobKzgCommitmentsLen = bid.blobKzgCommitments.length;
104
- const maxBlobsPerBlock = chain.config.getMaxBlobsPerBlock(computeEpochAtSlot(bid.slot));
105
- if (blobKzgCommitmentsLen > maxBlobsPerBlock) {
106
- throw Error(`Bid has too many KZG commitments len=${blobKzgCommitmentsLen} limit=${maxBlobsPerBlock}`);
107
- }
108
-
109
113
  // The coverage check only applies to the staked collateral payment, a pure execution
110
114
  // layer payment bid has nothing to cover on-chain
111
115
  if (bid.value > 0 && !state.canBuilderCoverBid(bid.builderIndex, bid.value)) {
@@ -2,6 +2,7 @@
2
2
  import {NotReorgedReason} from "@lodestar/fork-choice";
3
3
  import {ArchiveStoreTask} from "../../chain/archiveStore/archiveStore.js";
4
4
  import {FrequencyStateArchiveStep} from "../../chain/archiveStore/strategies/frequencyStateArchiveStrategy.js";
5
+ import type {LateCanonicalBlockReason} from "../../chain/archiveStore/utils/archiveBlocks.js";
5
6
  import {BlockInputSource} from "../../chain/blocks/blockInput/index.js";
6
7
  import {PayloadErrorCode} from "../../chain/blocks/importExecutionPayload.js";
7
8
  import {
@@ -1047,6 +1048,11 @@ export function createLodestarMetrics(
1047
1048
  name: "lodestar_import_block_set_head_after_cutoff_total",
1048
1049
  help: "Total times an imported block is set as head after ATTESTATION_DUE_BPS of the slot",
1049
1050
  }),
1051
+ lateCanonicalBlock: register.counter<{reason: LateCanonicalBlockReason}>({
1052
+ name: "lodestar_import_block_late_canonical_total",
1053
+ help: "Total finalized-canonical blocks this node imported after the attestation cutoff; reason distinguishes this node's processing lag (slow_import) from late reception (late_receive)",
1054
+ labelNames: ["reason"],
1055
+ }),
1050
1056
  bySource: register.gauge<{source: BlockInputSource}>({
1051
1057
  name: "lodestar_import_block_by_source_total",
1052
1058
  help: "Total number of imported blocks by source",
@@ -1352,11 +1358,6 @@ export function createLodestarMetrics(
1352
1358
  help: "Total number of InsertOutcome as a result of adding an execution payload bid from gossip to the pool",
1353
1359
  labelNames: ["insertOutcome"],
1354
1360
  }),
1355
- apiInsertOutcome: register.counter<{insertOutcome: InsertOutcome}>({
1356
- name: "lodestar_oppool_execution_payload_bid_pool_api_insert_outcome_total",
1357
- help: "Total number of InsertOutcome as a result of adding an execution payload bid from api to the pool",
1358
- labelNames: ["insertOutcome"],
1359
- }),
1360
1361
  apiValidationTime: register.histogram({
1361
1362
  name: "lodestar_api_execution_payload_bid_validation_time_seconds",
1362
1363
  help: "Time elapsed for signed execution payload bid validation - api path",
@@ -481,7 +481,7 @@ export class Network implements INetwork {
481
481
  }
482
482
 
483
483
  async publishLightClientFinalityUpdate(update: LightClientFinalityUpdate): Promise<number> {
484
- const epoch = computeEpochAtSlot(update.signatureSlot);
484
+ const epoch = computeEpochAtSlot(update.attestedHeader.beacon.slot);
485
485
  const boundary = this.config.getForkBoundaryAtEpoch(epoch);
486
486
 
487
487
  return this.publishGossip<GossipType.light_client_finality_update>(
@@ -491,7 +491,7 @@ export class Network implements INetwork {
491
491
  }
492
492
 
493
493
  async publishLightClientOptimisticUpdate(update: LightClientOptimisticUpdate): Promise<number> {
494
- const epoch = computeEpochAtSlot(update.signatureSlot);
494
+ const epoch = computeEpochAtSlot(update.attestedHeader.beacon.slot);
495
495
  const boundary = this.config.getForkBoundaryAtEpoch(epoch);
496
496
 
497
497
  return this.publishGossip<GossipType.light_client_optimistic_update>(