@lodestar/beacon-node 1.48.0-dev.a427d3dbb5 → 1.48.0-dev.af8bc602aa

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 (72) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +4 -4
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/chain/archiveStore/historicalState/metrics.d.ts.map +1 -1
  5. package/lib/chain/archiveStore/historicalState/metrics.js +1 -12
  6. package/lib/chain/archiveStore/historicalState/metrics.js.map +1 -1
  7. package/lib/chain/blocks/index.d.ts.map +1 -1
  8. package/lib/chain/blocks/index.js +2 -3
  9. package/lib/chain/blocks/index.js.map +1 -1
  10. package/lib/chain/chain.d.ts +2 -2
  11. package/lib/chain/chain.d.ts.map +1 -1
  12. package/lib/chain/chain.js +11 -13
  13. package/lib/chain/chain.js.map +1 -1
  14. package/lib/chain/errors/blockError.d.ts +7 -19
  15. package/lib/chain/errors/blockError.d.ts.map +1 -1
  16. package/lib/chain/errors/blockError.js +2 -2
  17. package/lib/chain/errors/blockError.js.map +1 -1
  18. package/lib/chain/errors/dataColumnSidecarError.d.ts +2 -15
  19. package/lib/chain/errors/dataColumnSidecarError.d.ts.map +1 -1
  20. package/lib/chain/errors/dataColumnSidecarError.js.map +1 -1
  21. package/lib/chain/errors/executionPayloadEnvelope.d.ts +11 -10
  22. package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
  23. package/lib/chain/errors/executionPayloadEnvelope.js +2 -0
  24. package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
  25. package/lib/chain/interface.d.ts +2 -2
  26. package/lib/chain/interface.d.ts.map +1 -1
  27. package/lib/chain/prepareNextSlot.js +13 -13
  28. package/lib/chain/prepareNextSlot.js.map +1 -1
  29. package/lib/chain/validation/block.d.ts.map +1 -1
  30. package/lib/chain/validation/block.js +54 -35
  31. package/lib/chain/validation/block.js.map +1 -1
  32. package/lib/chain/validation/dataColumnSidecar.d.ts.map +1 -1
  33. package/lib/chain/validation/dataColumnSidecar.js +13 -38
  34. package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
  35. package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
  36. package/lib/chain/validation/executionPayloadEnvelope.js +29 -10
  37. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  38. package/lib/network/gossip/encoding.d.ts.map +1 -1
  39. package/lib/network/gossip/encoding.js +6 -0
  40. package/lib/network/gossip/encoding.js.map +1 -1
  41. package/lib/network/gossip/topic.d.ts +1 -2
  42. package/lib/network/gossip/topic.d.ts.map +1 -1
  43. package/lib/network/gossip/topic.js +20 -4
  44. package/lib/network/gossip/topic.js.map +1 -1
  45. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  46. package/lib/network/processor/gossipHandlers.js +7 -6
  47. package/lib/network/processor/gossipHandlers.js.map +1 -1
  48. package/lib/network/processor/index.d.ts +3 -49
  49. package/lib/network/processor/index.d.ts.map +1 -1
  50. package/lib/network/processor/index.js +98 -232
  51. package/lib/network/processor/index.js.map +1 -1
  52. package/lib/network/reqresp/protocols.d.ts.map +1 -1
  53. package/lib/network/reqresp/protocols.js +22 -6
  54. package/lib/network/reqresp/protocols.js.map +1 -1
  55. package/package.json +15 -15
  56. package/src/api/impl/beacon/blocks/index.ts +4 -8
  57. package/src/chain/archiveStore/historicalState/metrics.ts +1 -14
  58. package/src/chain/blocks/index.ts +2 -13
  59. package/src/chain/chain.ts +11 -22
  60. package/src/chain/errors/blockError.ts +10 -22
  61. package/src/chain/errors/dataColumnSidecarError.ts +8 -24
  62. package/src/chain/errors/executionPayloadEnvelope.ts +11 -14
  63. package/src/chain/interface.ts +2 -2
  64. package/src/chain/prepareNextSlot.ts +16 -16
  65. package/src/chain/validation/block.ts +70 -35
  66. package/src/chain/validation/dataColumnSidecar.ts +14 -47
  67. package/src/chain/validation/executionPayloadEnvelope.ts +37 -10
  68. package/src/network/gossip/encoding.ts +6 -0
  69. package/src/network/gossip/topic.ts +25 -3
  70. package/src/network/processor/gossipHandlers.ts +11 -11
  71. package/src/network/processor/index.ts +148 -263
  72. package/src/network/reqresp/protocols.ts +24 -7
@@ -12,6 +12,8 @@ export enum ExecutionPayloadEnvelopeErrorCode {
12
12
  BUILDER_INDEX_MISMATCH = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_BUILDER_INDEX_MISMATCH",
13
13
  BLOCK_HASH_MISMATCH = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_BLOCK_HASH_MISMATCH",
14
14
  EXECUTION_REQUESTS_ROOT_MISMATCH = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_EXECUTION_REQUESTS_ROOT_MISMATCH",
15
+ EXECUTION_REQUESTS_COUNT_EXCEEDED = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_EXECUTION_REQUESTS_COUNT_EXCEEDED",
16
+ WITHDRAWALS_COUNT_EXCEEDED = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_WITHDRAWALS_COUNT_EXCEEDED",
15
17
  INVALID_SIGNATURE = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_INVALID_SIGNATURE",
16
18
  PAYLOAD_ENVELOPE_INPUT_MISSING = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_PAYLOAD_ENVELOPE_INPUT_MISSING",
17
19
  }
@@ -26,35 +28,30 @@ export type ExecutionPayloadEnvelopeErrorType =
26
28
  slot: Slot;
27
29
  }
28
30
  | {code: ExecutionPayloadEnvelopeErrorCode.INVALID_BLOCK; blockRoot: RootHex}
29
- | {
30
- code: ExecutionPayloadEnvelopeErrorCode.SLOT_MISMATCH;
31
- slot: Slot;
32
- root: RootHex;
33
- envelopeSlot: Slot;
34
- blockSlot: Slot;
35
- }
31
+ | {code: ExecutionPayloadEnvelopeErrorCode.SLOT_MISMATCH; envelopeSlot: Slot; blockSlot: Slot}
36
32
  | {
37
33
  code: ExecutionPayloadEnvelopeErrorCode.BUILDER_INDEX_MISMATCH;
38
- slot: Slot;
39
- root: RootHex;
40
34
  envelopeBuilderIndex: BuilderIndex;
41
35
  bidBuilderIndex: BuilderIndex | null;
42
36
  }
43
37
  | {
44
38
  code: ExecutionPayloadEnvelopeErrorCode.BLOCK_HASH_MISMATCH;
45
- slot: Slot;
46
- root: RootHex;
47
39
  envelopeBlockHash: RootHex;
48
40
  bidBlockHash: RootHex | null;
49
41
  }
50
42
  | {
51
43
  code: ExecutionPayloadEnvelopeErrorCode.EXECUTION_REQUESTS_ROOT_MISMATCH;
52
- slot: Slot;
53
- root: RootHex;
54
44
  envelopeRequestsRoot: RootHex;
55
45
  bidRequestsRoot: RootHex;
56
46
  }
57
- | {code: ExecutionPayloadEnvelopeErrorCode.INVALID_SIGNATURE; slot: Slot; root: RootHex}
47
+ | {
48
+ code: ExecutionPayloadEnvelopeErrorCode.EXECUTION_REQUESTS_COUNT_EXCEEDED;
49
+ name: string;
50
+ count: number;
51
+ limit: number;
52
+ }
53
+ | {code: ExecutionPayloadEnvelopeErrorCode.WITHDRAWALS_COUNT_EXCEEDED; count: number; limit: number}
54
+ | {code: ExecutionPayloadEnvelopeErrorCode.INVALID_SIGNATURE}
58
55
  | {code: ExecutionPayloadEnvelopeErrorCode.PAYLOAD_ENVELOPE_INPUT_MISSING; blockRoot: RootHex};
59
56
 
60
57
  export class ExecutionPayloadEnvelopeError extends GossipActionError<ExecutionPayloadEnvelopeErrorType> {}
@@ -290,8 +290,8 @@ export interface IBeaconChain {
290
290
  postState: IBeaconStateView,
291
291
  block: SignedBeaconBlock
292
292
  ): Promise<void>;
293
- persistInvalidSszValue<T>(type: Type<T>, sszObject: T, suffix?: string, rootHex?: RootHex): void;
294
- persistInvalidSszBytes(type: string, sszBytes: Uint8Array, rootHex: RootHex, suffix?: string): void;
293
+ persistInvalidSszValue<T>(type: Type<T>, sszObject: T | Uint8Array, suffix?: string): void;
294
+ persistInvalidSszBytes(type: string, sszBytes: Uint8Array, suffix?: string): void;
295
295
  regenStateForAttestationVerification(
296
296
  attEpoch: Epoch,
297
297
  shufflingDependentRoot: RootHex,
@@ -243,22 +243,6 @@ export class PrepareNextSlotScheduler {
243
243
  parentBlockHash = preparedState.latestExecutionPayloadHeader.blockHash;
244
244
  }
245
245
 
246
- // If emitPayloadAttributes is true emit a SSE payloadAttributes event for
247
- // every slot. Without the flag, only emit the event if we are proposing in the next slot.
248
- if (
249
- (feeRecipient || this.chain.opts.emitPayloadAttributes === true) &&
250
- this.chain.emitter.listenerCount(routes.events.EventType.payloadAttributes)
251
- ) {
252
- const data = getPayloadAttributesForSSE(fork as ForkPostBellatrix, this.chain, {
253
- prepareState: stateAfterParentPayload,
254
- prepareSlot,
255
- parentBlockRoot: fromHex(updatedHead.blockRoot),
256
- parentBlockHash,
257
- feeRecipient: feeRecipient ?? "0x0000000000000000000000000000000000000000",
258
- });
259
- this.chain.emitter.emit(routes.events.EventType.payloadAttributes, {data, version: fork});
260
- }
261
-
262
246
  if (feeRecipient) {
263
247
  const preparationTime =
264
248
  computeTimeAtSlot(this.config, prepareSlot, this.chain.genesisTime) - Date.now() / 1000;
@@ -289,6 +273,22 @@ export class PrepareNextSlotScheduler {
289
273
  }
290
274
 
291
275
  this.computeStateHashTreeRoot(preparedState, isEpochTransition);
276
+
277
+ // If emitPayloadAttributes is true emit a SSE payloadAttributes event for
278
+ // every slot. Without the flag, only emit the event if we are proposing in the next slot.
279
+ if (
280
+ (feeRecipient || this.chain.opts.emitPayloadAttributes === true) &&
281
+ this.chain.emitter.listenerCount(routes.events.EventType.payloadAttributes)
282
+ ) {
283
+ const data = getPayloadAttributesForSSE(fork as ForkPostBellatrix, this.chain, {
284
+ prepareState: stateAfterParentPayload,
285
+ prepareSlot,
286
+ parentBlockRoot: fromHex(updatedHead.blockRoot),
287
+ parentBlockHash,
288
+ feeRecipient: feeRecipient ?? "0x0000000000000000000000000000000000000000",
289
+ });
290
+ this.chain.emitter.emit(routes.events.EventType.payloadAttributes, {data, version: fork});
291
+ }
292
292
  } else {
293
293
  // Pre-bellatrix only reaches here at an epoch transition to precompute the next epoch state
294
294
  const preparedState = await this.chain.regen.getBlockSlotState(
@@ -1,6 +1,22 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
2
  import {ExecutionStatus} from "@lodestar/fork-choice";
3
- import {ForkName, MIN_SEED_LOOKAHEAD, isForkPostBellatrix, isForkPostDeneb, isForkPostGloas} from "@lodestar/params";
3
+ import {
4
+ ForkName,
5
+ MAX_ATTESTATIONS_ELECTRA,
6
+ MAX_ATTESTER_SLASHINGS_ELECTRA,
7
+ MAX_BLS_TO_EXECUTION_CHANGES,
8
+ MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD,
9
+ MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD,
10
+ MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD,
11
+ MAX_PAYLOAD_ATTESTATIONS,
12
+ MAX_PROPOSER_SLASHINGS,
13
+ MAX_VOLUNTARY_EXITS,
14
+ MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD,
15
+ MIN_SEED_LOOKAHEAD,
16
+ isForkPostBellatrix,
17
+ isForkPostDeneb,
18
+ isForkPostGloas,
19
+ } from "@lodestar/params";
4
20
  import {
5
21
  computeEpochAtSlot,
6
22
  computeStartSlotAtEpoch,
@@ -134,9 +150,8 @@ export async function validateGossipBlock(
134
150
  if (parentBlock.slot >= blockSlot) {
135
151
  throw new BlockGossipError(GossipAction.REJECT, {
136
152
  code: BlockErrorCode.NOT_LATER_THAN_PARENT,
137
- slot: blockSlot,
138
- root: blockRoot,
139
153
  parentSlot: parentBlock.slot,
154
+ slot: blockSlot,
140
155
  });
141
156
  }
142
157
 
@@ -151,8 +166,6 @@ export async function validateGossipBlock(
151
166
  if (blobKzgCommitmentsLen > maxBlobsPerBlock) {
152
167
  throw new BlockGossipError(GossipAction.REJECT, {
153
168
  code: BlockErrorCode.TOO_MANY_KZG_COMMITMENTS,
154
- slot: blockSlot,
155
- root: blockRoot,
156
169
  blobKzgCommitmentsLen,
157
170
  commitmentLimit: maxBlobsPerBlock,
158
171
  });
@@ -160,8 +173,7 @@ export async function validateGossipBlock(
160
173
  }
161
174
 
162
175
  if (isForkPostGloas(fork)) {
163
- const body = (block as gloas.BeaconBlock).body;
164
- const bid = body.signedExecutionPayloadBid.message;
176
+ const bid = (block as gloas.BeaconBlock).body.signedExecutionPayloadBid.message;
165
177
 
166
178
  // [REJECT] The length of KZG commitments is less than or equal to the limitation defined in Consensus Layer
167
179
  // -- i.e. validate that len(bid.blob_kzg_commitments) <= max_blobs_per_block
@@ -170,8 +182,6 @@ export async function validateGossipBlock(
170
182
  if (blobKzgCommitmentsLen > maxBlobsPerBlock) {
171
183
  throw new BlockGossipError(GossipAction.REJECT, {
172
184
  code: BlockErrorCode.TOO_MANY_KZG_COMMITMENTS,
173
- slot: blockSlot,
174
- root: blockRoot,
175
185
  blobKzgCommitmentsLen,
176
186
  commitmentLimit: maxBlobsPerBlock,
177
187
  });
@@ -181,21 +191,59 @@ export async function validateGossipBlock(
181
191
  if (!byteArrayEquals(bid.parentBlockRoot, block.parentRoot)) {
182
192
  throw new BlockGossipError(GossipAction.REJECT, {
183
193
  code: BlockErrorCode.BID_PARENT_ROOT_MISMATCH,
184
- slot: blockSlot,
185
- root: blockRoot,
186
194
  bidParentRoot: toRootHex(bid.parentBlockRoot),
187
195
  blockParentRoot: parentRoot,
188
196
  });
189
197
  }
190
198
 
191
- // [REJECT] The block contains no deposits.
192
- if (body.deposits.length !== 0) {
193
- throw new BlockGossipError(GossipAction.REJECT, {
194
- code: BlockErrorCode.NON_ZERO_DEPOSITS,
195
- slot: blockSlot,
196
- root: blockRoot,
197
- count: body.deposits.length,
198
- });
199
+ // [REJECT] The counts of `block.body.parent_execution_requests` are within
200
+ // their respective limits -- i.e. validate that
201
+ // `len(block.body.parent_execution_requests.withdrawals) <= MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD`,
202
+ // `len(block.body.parent_execution_requests.consolidations) <= MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD`,
203
+ // `len(block.body.parent_execution_requests.builder_deposits) <= MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD`,
204
+ // and
205
+ // `len(block.body.parent_execution_requests.builder_exits) <= MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD`.
206
+ // [REJECT] The counts of the block body operations are within their respective
207
+ // limits -- i.e. validate that
208
+ // `len(block.body.proposer_slashings) <= MAX_PROPOSER_SLASHINGS`,
209
+ // `len(block.body.attester_slashings) <= MAX_ATTESTER_SLASHINGS_ELECTRA`,
210
+ // `len(block.body.attestations) <= MAX_ATTESTATIONS_ELECTRA`,
211
+ // `len(block.body.deposits) == 0`,
212
+ // `len(block.body.voluntary_exits) <= MAX_VOLUNTARY_EXITS`,
213
+ // `len(block.body.bls_to_execution_changes) <= MAX_BLS_TO_EXECUTION_CHANGES`,
214
+ // and `len(block.body.payload_attestations) <= MAX_PAYLOAD_ATTESTATIONS`.
215
+ const body = (block as gloas.BeaconBlock).body;
216
+ const requests = body.parentExecutionRequests;
217
+ const countLimits: [string, number, number][] = [
218
+ ["parentExecutionRequests.withdrawals", requests.withdrawals.length, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD],
219
+ [
220
+ "parentExecutionRequests.consolidations",
221
+ requests.consolidations.length,
222
+ MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD,
223
+ ],
224
+ [
225
+ "parentExecutionRequests.builderDeposits",
226
+ requests.builderDeposits.length,
227
+ MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD,
228
+ ],
229
+ ["parentExecutionRequests.builderExits", requests.builderExits.length, MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD],
230
+ ["proposerSlashings", body.proposerSlashings.length, MAX_PROPOSER_SLASHINGS],
231
+ ["attesterSlashings", body.attesterSlashings.length, MAX_ATTESTER_SLASHINGS_ELECTRA],
232
+ ["attestations", body.attestations.length, MAX_ATTESTATIONS_ELECTRA],
233
+ ["deposits", body.deposits.length, 0],
234
+ ["voluntaryExits", body.voluntaryExits.length, MAX_VOLUNTARY_EXITS],
235
+ ["blsToExecutionChanges", body.blsToExecutionChanges.length, MAX_BLS_TO_EXECUTION_CHANGES],
236
+ ["payloadAttestations", body.payloadAttestations.length, MAX_PAYLOAD_ATTESTATIONS],
237
+ ];
238
+ for (const [name, count, limit] of countLimits) {
239
+ if (count > limit) {
240
+ throw new BlockGossipError(GossipAction.REJECT, {
241
+ code: BlockErrorCode.TOO_MANY_BLOCK_OPERATIONS,
242
+ name,
243
+ count,
244
+ limit,
245
+ });
246
+ }
199
247
  }
200
248
 
201
249
  // TODO GLOAS: [REJECT] The block's execution payload parent (defined by bid.parent_block_hash) passes all validation
@@ -258,8 +306,6 @@ export async function validateGossipBlock(
258
306
  if (executionPayload.timestamp !== computeTimeAtSlot(config, blockSlot, chain.genesisTime)) {
259
307
  throw new BlockGossipError(GossipAction.REJECT, {
260
308
  code: BlockErrorCode.INCORRECT_TIMESTAMP,
261
- slot: blockSlot,
262
- root: blockRoot,
263
309
  timestamp: executionPayload.timestamp,
264
310
  expectedTimestamp,
265
311
  });
@@ -269,12 +315,7 @@ export async function validateGossipBlock(
269
315
 
270
316
  // [REJECT] The proposer index is a valid validator index
271
317
  if (proposerIndex >= state.validatorCount) {
272
- throw new BlockGossipError(GossipAction.REJECT, {
273
- code: BlockErrorCode.UNKNOWN_PROPOSER,
274
- slot: blockSlot,
275
- root: blockRoot,
276
- proposerIndex,
277
- });
318
+ throw new BlockGossipError(GossipAction.REJECT, {code: BlockErrorCode.UNKNOWN_PROPOSER, proposerIndex});
278
319
  }
279
320
 
280
321
  // [REJECT] The proposer signature, signed_beacon_block.signature, is valid with respect to the proposer_index pubkey.
@@ -286,12 +327,7 @@ export async function validateGossipBlock(
286
327
  // shuffling, the block MAY be queued for later processing while proposers for the block's branch are calculated --
287
328
  // in such a case do not REJECT, instead IGNORE this message.
288
329
  if (state.getBeaconProposer(blockSlot) !== proposerIndex) {
289
- throw new BlockGossipError(GossipAction.REJECT, {
290
- code: BlockErrorCode.INCORRECT_PROPOSER,
291
- slot: blockSlot,
292
- root: blockRoot,
293
- proposerIndex,
294
- });
330
+ throw new BlockGossipError(GossipAction.REJECT, {code: BlockErrorCode.INCORRECT_PROPOSER, proposerIndex});
295
331
  }
296
332
 
297
333
  // Simple implementation of a pending block queue. Keeping the block here recycles the queue logic, and keeps the
@@ -336,8 +372,7 @@ export async function verifyBlockProposerSignature(
336
372
  if (!(await chain.bls.verifySignatureSets([signatureSet], {verifyOnMainThread: opts.verifyOnMainThread ?? true}))) {
337
373
  throw new BlockGossipError(GossipAction.REJECT, {
338
374
  code: BlockErrorCode.PROPOSAL_SIGNATURE_INVALID,
339
- slot: blockSlot,
340
- root: blockRoot,
375
+ blockSlot,
341
376
  });
342
377
  }
343
378
 
@@ -11,7 +11,7 @@ import {
11
11
  computeStartSlotAtEpoch,
12
12
  getBlockHeaderProposerSignatureSetByHeaderSlot,
13
13
  } from "@lodestar/state-transition";
14
- import {DataColumnSidecar, Root, RootHex, Slot, SubnetID, ValidatorIndex, fulu, gloas, ssz} from "@lodestar/types";
14
+ import {DataColumnSidecar, Root, Slot, SubnetID, ValidatorIndex, fulu, gloas, ssz} from "@lodestar/types";
15
15
  import {byteArrayEquals, toRootHex, verifyMerkleBranch} from "@lodestar/utils";
16
16
  import {BeaconMetrics} from "../../metrics/metrics/beacon.js";
17
17
  import {Metrics} from "../../metrics/metrics.js";
@@ -39,14 +39,12 @@ export async function validateGossipFuluDataColumnSidecar(
39
39
  const blockRootHex = toRootHex(ssz.phase0.BeaconBlockHeader.hashTreeRoot(blockHeader));
40
40
 
41
41
  // 1) [REJECT] The sidecar is valid as verified by verify_data_column_sidecar
42
- verifyFuluDataColumnSidecar(chain.config, dataColumnSidecar, blockRootHex);
42
+ verifyFuluDataColumnSidecar(chain.config, dataColumnSidecar);
43
43
 
44
44
  // 2) [REJECT] The sidecar is for the correct subnet -- i.e. compute_subnet_for_data_column_sidecar(sidecar.index) == subnet_id
45
45
  if (computeSubnetForDataColumnSidecar(chain.config, dataColumnSidecar) !== gossipSubnet) {
46
46
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
47
47
  code: DataColumnSidecarErrorCode.INVALID_SUBNET,
48
- slot: blockHeader.slot,
49
- root: blockRootHex,
50
48
  columnIndex: dataColumnSidecar.index,
51
49
  gossipSubnet: gossipSubnet,
52
50
  });
@@ -102,9 +100,8 @@ export async function validateGossipFuluDataColumnSidecar(
102
100
  if (parentBlock.slot >= blockHeader.slot) {
103
101
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
104
102
  code: DataColumnSidecarErrorCode.NOT_LATER_THAN_PARENT,
105
- slot: blockHeader.slot,
106
- root: blockRootHex,
107
103
  parentSlot: parentBlock.slot,
104
+ slot: blockHeader.slot,
108
105
  });
109
106
  }
110
107
 
@@ -143,8 +140,6 @@ export async function validateGossipFuluDataColumnSidecar(
143
140
  if (proposerIndex !== expectedProposerIndex) {
144
141
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
145
142
  code: DataColumnSidecarErrorCode.INCORRECT_PROPOSER,
146
- slot: blockHeader.slot,
147
- root: blockRootHex,
148
143
  actualProposerIndex: proposerIndex,
149
144
  expectedProposerIndex,
150
145
  });
@@ -166,9 +161,9 @@ export async function validateGossipFuluDataColumnSidecar(
166
161
  ) {
167
162
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
168
163
  code: DataColumnSidecarErrorCode.PROPOSAL_SIGNATURE_INVALID,
169
- slot: blockHeader.slot,
170
- root: blockRootHex,
164
+ blockRoot: blockRootHex,
171
165
  index: dataColumnSidecar.index,
166
+ slot: blockHeader.slot,
172
167
  });
173
168
  }
174
169
 
@@ -190,8 +185,7 @@ export async function validateGossipFuluDataColumnSidecar(
190
185
  if (!valid) {
191
186
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
192
187
  code: DataColumnSidecarErrorCode.INCLUSION_PROOF_INVALID,
193
- slot: blockHeader.slot,
194
- root: blockRootHex,
188
+ slot: dataColumnSidecar.signedBlockHeader.message.slot,
195
189
  columnIndex: dataColumnSidecar.index,
196
190
  });
197
191
  }
@@ -210,7 +204,6 @@ export async function validateGossipFuluDataColumnSidecar(
210
204
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
211
205
  code: DataColumnSidecarErrorCode.INVALID_KZG_PROOF,
212
206
  slot: blockHeader.slot,
213
- root: blockRootHex,
214
207
  columnIndex: dataColumnSidecar.index,
215
208
  });
216
209
  } finally {
@@ -247,7 +240,6 @@ export async function validateGossipGloasDataColumnSidecar(
247
240
  if (block.slot !== dataColumnSidecar.slot) {
248
241
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
249
242
  code: DataColumnSidecarErrorCode.INCORRECT_SIDECAR_SLOT,
250
- root: blockRootHex,
251
243
  columnIndex: dataColumnSidecar.index,
252
244
  expected: block.slot,
253
245
  actual: dataColumnSidecar.slot,
@@ -256,14 +248,12 @@ export async function validateGossipGloasDataColumnSidecar(
256
248
 
257
249
  // [REJECT] The sidecar must pass verify_data_column_sidecar against the block commitments
258
250
  const kzgCommitments = payloadInput.getBlobKzgCommitments();
259
- verifyGloasDataColumnSidecar(dataColumnSidecar, kzgCommitments, blockRootHex);
251
+ verifyGloasDataColumnSidecar(dataColumnSidecar, kzgCommitments);
260
252
 
261
253
  // [REJECT] The sidecar must be on the correct subnet
262
254
  if (computeSubnetForDataColumnSidecar(chain.config, dataColumnSidecar) !== gossipSubnet) {
263
255
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
264
256
  code: DataColumnSidecarErrorCode.INVALID_SUBNET,
265
- slot: dataColumnSidecar.slot,
266
- root: blockRootHex,
267
257
  columnIndex: dataColumnSidecar.index,
268
258
  gossipSubnet,
269
259
  });
@@ -282,7 +272,6 @@ export async function validateGossipGloasDataColumnSidecar(
282
272
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
283
273
  code: DataColumnSidecarErrorCode.INVALID_KZG_PROOF,
284
274
  slot: dataColumnSidecar.slot,
285
- root: blockRootHex,
286
275
  columnIndex: dataColumnSidecar.index,
287
276
  });
288
277
  } finally {
@@ -294,17 +283,11 @@ export async function validateGossipGloasDataColumnSidecar(
294
283
  * SPEC FUNCTION
295
284
  * https://github.com/ethereum/consensus-specs/blob/v1.6.0-alpha.4/specs/fulu/p2p-interface.md#verify_data_column_sidecar
296
285
  */
297
- function verifyFuluDataColumnSidecar(
298
- config: ChainForkConfig,
299
- dataColumnSidecar: fulu.DataColumnSidecar,
300
- blockRootHex: RootHex
301
- ): void {
302
- const slot = dataColumnSidecar.signedBlockHeader.message.slot;
286
+ function verifyFuluDataColumnSidecar(config: ChainForkConfig, dataColumnSidecar: fulu.DataColumnSidecar): void {
303
287
  if (dataColumnSidecar.index >= NUMBER_OF_COLUMNS) {
304
288
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
305
289
  code: DataColumnSidecarErrorCode.INVALID_INDEX,
306
- slot,
307
- root: blockRootHex,
290
+ slot: dataColumnSidecar.signedBlockHeader.message.slot,
308
291
  columnIndex: dataColumnSidecar.index,
309
292
  });
310
293
  }
@@ -312,20 +295,18 @@ function verifyFuluDataColumnSidecar(
312
295
  if (dataColumnSidecar.kzgCommitments.length === 0) {
313
296
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
314
297
  code: DataColumnSidecarErrorCode.NO_COMMITMENTS,
315
- slot,
316
- root: blockRootHex,
298
+ slot: dataColumnSidecar.signedBlockHeader.message.slot,
317
299
  columnIndex: dataColumnSidecar.index,
318
300
  });
319
301
  }
320
302
 
321
- const epoch = computeEpochAtSlot(slot);
303
+ const epoch = computeEpochAtSlot(dataColumnSidecar.signedBlockHeader.message.slot);
322
304
  const maxBlobsPerBlock = config.getMaxBlobsPerBlock(epoch);
323
305
 
324
306
  if (dataColumnSidecar.kzgCommitments.length > maxBlobsPerBlock) {
325
307
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
326
308
  code: DataColumnSidecarErrorCode.TOO_MANY_KZG_COMMITMENTS,
327
- slot,
328
- root: blockRootHex,
309
+ slot: dataColumnSidecar.signedBlockHeader.message.slot,
329
310
  columnIndex: dataColumnSidecar.index,
330
311
  count: dataColumnSidecar.kzgCommitments.length,
331
312
  limit: maxBlobsPerBlock,
@@ -338,8 +319,6 @@ function verifyFuluDataColumnSidecar(
338
319
  ) {
339
320
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
340
321
  code: DataColumnSidecarErrorCode.MISMATCHED_LENGTHS,
341
- slot,
342
- root: blockRootHex,
343
322
  columnLength: dataColumnSidecar.column.length,
344
323
  commitmentsLength: dataColumnSidecar.kzgCommitments.length,
345
324
  proofsLength: dataColumnSidecar.kzgProofs.length,
@@ -351,17 +330,12 @@ function verifyFuluDataColumnSidecar(
351
330
  * SPEC FUNCTION
352
331
  * https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.3/specs/gloas/p2p-interface.md#modified-verify_data_column_sidecar
353
332
  */
354
- function verifyGloasDataColumnSidecar(
355
- dataColumnSidecar: gloas.DataColumnSidecar,
356
- kzgCommitments: Uint8Array[],
357
- blockRootHex: RootHex
358
- ): void {
333
+ function verifyGloasDataColumnSidecar(dataColumnSidecar: gloas.DataColumnSidecar, kzgCommitments: Uint8Array[]): void {
359
334
  const slot = getDataColumnSidecarSlot(dataColumnSidecar);
360
335
  if (dataColumnSidecar.index >= NUMBER_OF_COLUMNS) {
361
336
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
362
337
  code: DataColumnSidecarErrorCode.INVALID_INDEX,
363
338
  slot,
364
- root: blockRootHex,
365
339
  columnIndex: dataColumnSidecar.index,
366
340
  });
367
341
  }
@@ -370,7 +344,6 @@ function verifyGloasDataColumnSidecar(
370
344
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
371
345
  code: DataColumnSidecarErrorCode.NO_COMMITMENTS,
372
346
  slot,
373
- root: blockRootHex,
374
347
  columnIndex: dataColumnSidecar.index,
375
348
  });
376
349
  }
@@ -381,8 +354,6 @@ function verifyGloasDataColumnSidecar(
381
354
  ) {
382
355
  throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
383
356
  code: DataColumnSidecarErrorCode.MISMATCHED_LENGTHS,
384
- slot,
385
- root: blockRootHex,
386
357
  columnLength: dataColumnSidecar.column.length,
387
358
  commitmentsLength: kzgCommitments.length,
388
359
  proofsLength: dataColumnSidecar.kzgProofs.length,
@@ -494,8 +465,8 @@ export async function validateFuluBlockDataColumnSidecars(
494
465
  ) {
495
466
  throw new DataColumnSidecarValidationError({
496
467
  code: DataColumnSidecarErrorCode.PROPOSAL_SIGNATURE_INVALID,
468
+ blockRoot: rootHex,
497
469
  slot: blockSlot,
498
- root: rootHex,
499
470
  index: dataColumnSidecars[0].index,
500
471
  });
501
472
  }
@@ -528,7 +499,6 @@ export async function validateFuluBlockDataColumnSidecars(
528
499
  {
529
500
  code: DataColumnSidecarErrorCode.INVALID_INDEX,
530
501
  slot: blockSlot,
531
- root: toRootHex(blockRoot),
532
502
  columnIndex: columnSidecar.index,
533
503
  },
534
504
  "DataColumnSidecar has invalid index"
@@ -573,7 +543,6 @@ export async function validateFuluBlockDataColumnSidecars(
573
543
  {
574
544
  code: DataColumnSidecarErrorCode.INCLUSION_PROOF_INVALID,
575
545
  slot: blockSlot,
576
- root: toRootHex(blockRoot),
577
546
  columnIndex: columnSidecar.index,
578
547
  },
579
548
  "DataColumnSidecar has invalid inclusion proof"
@@ -654,7 +623,6 @@ export async function validateGloasBlockDataColumnSidecars(
654
623
  if (columnSidecar.slot !== blockSlot) {
655
624
  throw new DataColumnSidecarValidationError({
656
625
  code: DataColumnSidecarErrorCode.INCORRECT_SIDECAR_SLOT,
657
- root: toRootHex(blockRoot),
658
626
  columnIndex: columnSidecar.index,
659
627
  expected: blockSlot,
660
628
  actual: columnSidecar.slot,
@@ -676,7 +644,6 @@ export async function validateGloasBlockDataColumnSidecars(
676
644
  {
677
645
  code: DataColumnSidecarErrorCode.INVALID_INDEX,
678
646
  slot: blockSlot,
679
- root: toRootHex(blockRoot),
680
647
  columnIndex: columnSidecar.index,
681
648
  },
682
649
  "DataColumnSidecar has invalid index"
@@ -1,4 +1,11 @@
1
1
  import {PayloadStatus} from "@lodestar/fork-choice";
2
+ import {
3
+ MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD,
4
+ MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD,
5
+ MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD,
6
+ MAX_WITHDRAWALS_PER_PAYLOAD,
7
+ MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD,
8
+ } from "@lodestar/params";
2
9
  import {
3
10
  computeStartSlotAtEpoch,
4
11
  getExecutionPayloadEnvelopeSignatureSet,
@@ -82,8 +89,6 @@ async function validateExecutionPayloadEnvelope(
82
89
  if (block.slot !== payload.slotNumber) {
83
90
  throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
84
91
  code: ExecutionPayloadEnvelopeErrorCode.SLOT_MISMATCH,
85
- slot: payload.slotNumber,
86
- root: blockRootHex,
87
92
  envelopeSlot: payload.slotNumber,
88
93
  blockSlot: block.slot,
89
94
  });
@@ -93,8 +98,6 @@ async function validateExecutionPayloadEnvelope(
93
98
  if (envelope.builderIndex !== payloadInput.getBuilderIndex()) {
94
99
  throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
95
100
  code: ExecutionPayloadEnvelopeErrorCode.BUILDER_INDEX_MISMATCH,
96
- slot: payload.slotNumber,
97
- root: blockRootHex,
98
101
  envelopeBuilderIndex: envelope.builderIndex,
99
102
  bidBuilderIndex: payloadInput.getBuilderIndex(),
100
103
  });
@@ -104,8 +107,6 @@ async function validateExecutionPayloadEnvelope(
104
107
  if (toRootHex(payload.blockHash) !== payloadInput.getBlockHashHex()) {
105
108
  throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
106
109
  code: ExecutionPayloadEnvelopeErrorCode.BLOCK_HASH_MISMATCH,
107
- slot: payload.slotNumber,
108
- root: blockRootHex,
109
110
  envelopeBlockHash: toRootHex(payload.blockHash),
110
111
  bidBlockHash: payloadInput.getBlockHashHex(),
111
112
  });
@@ -116,13 +117,41 @@ async function validateExecutionPayloadEnvelope(
116
117
  if (!byteArrayEquals(requestsRoot, payloadInput.getBid().executionRequestsRoot)) {
117
118
  throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
118
119
  code: ExecutionPayloadEnvelopeErrorCode.EXECUTION_REQUESTS_ROOT_MISMATCH,
119
- slot: payload.slotNumber,
120
- root: blockRootHex,
121
120
  envelopeRequestsRoot: toRootHex(requestsRoot),
122
121
  bidRequestsRoot: toRootHex(payloadInput.getBid().executionRequestsRoot),
123
122
  });
124
123
  }
125
124
 
125
+ // [REJECT] The counts of `execution_requests` are within their respective limits.
126
+ // New in Gloas:EIP7688 — progressive lists are unbounded at the type level, so bounds
127
+ // are enforced here in gossip validation.
128
+ const {executionRequests} = envelope;
129
+ const requestCountLimits: [string, number, number][] = [
130
+ ["withdrawals", executionRequests.withdrawals.length, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD],
131
+ ["consolidations", executionRequests.consolidations.length, MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD],
132
+ ["builderDeposits", executionRequests.builderDeposits.length, MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD],
133
+ ["builderExits", executionRequests.builderExits.length, MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD],
134
+ ];
135
+ for (const [name, count, limit] of requestCountLimits) {
136
+ if (count > limit) {
137
+ throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
138
+ code: ExecutionPayloadEnvelopeErrorCode.EXECUTION_REQUESTS_COUNT_EXCEEDED,
139
+ name,
140
+ count,
141
+ limit,
142
+ });
143
+ }
144
+ }
145
+
146
+ // [REJECT] The number of withdrawals is within the limit.
147
+ if (payload.withdrawals.length > MAX_WITHDRAWALS_PER_PAYLOAD) {
148
+ throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
149
+ code: ExecutionPayloadEnvelopeErrorCode.WITHDRAWALS_COUNT_EXCEEDED,
150
+ count: payload.withdrawals.length,
151
+ limit: MAX_WITHDRAWALS_PER_PAYLOAD,
152
+ });
153
+ }
154
+
126
155
  // Get the block state to verify the builder's signature.
127
156
  const blockState = await chain.regen
128
157
  .getState(block.stateRoot, RegenCaller.validateGossipPayloadEnvelope)
@@ -149,8 +178,6 @@ async function validateExecutionPayloadEnvelope(
149
178
  if (!(await chain.bls.verifySignatureSets([signatureSet], {verifyOnMainThread: true}))) {
150
179
  throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
151
180
  code: ExecutionPayloadEnvelopeErrorCode.INVALID_SIGNATURE,
152
- slot: payload.slotNumber,
153
- root: blockRootHex,
154
181
  });
155
182
  }
156
183
  }
@@ -123,6 +123,9 @@ export class DataTransformSnappy implements DataTransform {
123
123
  if (uncompressedDataLength > maxSize) {
124
124
  throw Error(`ssz_snappy decoded data length ${uncompressedDataLength} > ${maxSize}`);
125
125
  }
126
+ if (uncompressedDataLength > sszType.maxSize) {
127
+ throw Error(`ssz_snappy decoded data length ${uncompressedDataLength} > ${sszType.maxSize}`);
128
+ }
126
129
 
127
130
  // Only after sanity length checks, we can decompress the data
128
131
  // Using Buffer.alloc() instead of Buffer.allocUnsafe() to mitigate high GC pressure observed in some environments
@@ -143,6 +146,9 @@ export class DataTransformSnappy implements DataTransform {
143
146
  if (data.length > maxSize) {
144
147
  throw Error(`ssz_snappy encoded data length ${data.length} > ${maxSize}`);
145
148
  }
149
+ if (data.length > sszType.maxSize) {
150
+ throw Error(`ssz_snappy encoded data length ${data.length} > ${sszType.maxSize}`);
151
+ }
146
152
 
147
153
  // Using Buffer.alloc() instead of Buffer.allocUnsafe() to mitigate high GC pressure observed in some environments
148
154
  const compressedData = Buffer.alloc(snappyWasm.max_compress_len(data.length));