@lodestar/beacon-node 1.42.0-dev.8961934298 → 1.42.0-dev.89ddaaeed2

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 (149) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +11 -11
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/debug/index.js.map +1 -1
  5. package/lib/chain/ColumnReconstructionTracker.d.ts +2 -1
  6. package/lib/chain/ColumnReconstructionTracker.d.ts.map +1 -1
  7. package/lib/chain/ColumnReconstructionTracker.js +5 -5
  8. package/lib/chain/ColumnReconstructionTracker.js.map +1 -1
  9. package/lib/chain/GetBlobsTracker.d.ts +2 -1
  10. package/lib/chain/GetBlobsTracker.d.ts.map +1 -1
  11. package/lib/chain/GetBlobsTracker.js +14 -12
  12. package/lib/chain/GetBlobsTracker.js.map +1 -1
  13. package/lib/chain/blocks/blockInput/blockInput.d.ts +5 -5
  14. package/lib/chain/blocks/blockInput/blockInput.d.ts.map +1 -1
  15. package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
  16. package/lib/chain/blocks/blockInput/types.d.ts +4 -4
  17. package/lib/chain/blocks/blockInput/types.d.ts.map +1 -1
  18. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  19. package/lib/chain/blocks/importBlock.js +13 -1
  20. package/lib/chain/blocks/importBlock.js.map +1 -1
  21. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +14 -6
  22. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
  23. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +33 -2
  24. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
  25. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +2 -1
  26. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
  27. package/lib/chain/chain.d.ts +3 -2
  28. package/lib/chain/chain.d.ts.map +1 -1
  29. package/lib/chain/chain.js +55 -20
  30. package/lib/chain/chain.js.map +1 -1
  31. package/lib/chain/emitter.d.ts +29 -7
  32. package/lib/chain/emitter.d.ts.map +1 -1
  33. package/lib/chain/emitter.js +12 -3
  34. package/lib/chain/emitter.js.map +1 -1
  35. package/lib/chain/errors/dataColumnSidecarError.d.ts +31 -1
  36. package/lib/chain/errors/dataColumnSidecarError.d.ts.map +1 -1
  37. package/lib/chain/errors/dataColumnSidecarError.js +7 -0
  38. package/lib/chain/errors/dataColumnSidecarError.js.map +1 -1
  39. package/lib/chain/interface.d.ts +4 -2
  40. package/lib/chain/interface.d.ts.map +1 -1
  41. package/lib/chain/seenCache/seenGossipBlockInput.d.ts +1 -1
  42. package/lib/chain/seenCache/seenGossipBlockInput.d.ts.map +1 -1
  43. package/lib/chain/seenCache/seenGossipBlockInput.js +2 -2
  44. package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
  45. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +1 -1
  46. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  47. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +2 -2
  48. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  49. package/lib/chain/validation/dataColumnSidecar.d.ts +11 -4
  50. package/lib/chain/validation/dataColumnSidecar.d.ts.map +1 -1
  51. package/lib/chain/validation/dataColumnSidecar.js +184 -5
  52. package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
  53. package/lib/db/buckets.d.ts +2 -2
  54. package/lib/db/buckets.d.ts.map +1 -1
  55. package/lib/db/buckets.js +2 -2
  56. package/lib/db/buckets.js.map +1 -1
  57. package/lib/db/repositories/blockArchiveIndex.d.ts +2 -2
  58. package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
  59. package/lib/db/repositories/dataColumnSidecar.d.ts.map +1 -1
  60. package/lib/db/repositories/dataColumnSidecar.js +4 -2
  61. package/lib/db/repositories/dataColumnSidecar.js.map +1 -1
  62. package/lib/db/repositories/dataColumnSidecarArchive.d.ts.map +1 -1
  63. package/lib/db/repositories/dataColumnSidecarArchive.js +4 -2
  64. package/lib/db/repositories/dataColumnSidecarArchive.js.map +1 -1
  65. package/lib/metrics/metrics/lodestar.d.ts +20 -0
  66. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  67. package/lib/metrics/metrics/lodestar.js +33 -0
  68. package/lib/metrics/metrics/lodestar.js.map +1 -1
  69. package/lib/network/interface.d.ts +3 -2
  70. package/lib/network/interface.d.ts.map +1 -1
  71. package/lib/network/network.d.ts +3 -2
  72. package/lib/network/network.d.ts.map +1 -1
  73. package/lib/network/network.js +3 -0
  74. package/lib/network/network.js.map +1 -1
  75. package/lib/network/processor/extractSlotRootFns.d.ts +1 -1
  76. package/lib/network/processor/extractSlotRootFns.d.ts.map +1 -1
  77. package/lib/network/processor/extractSlotRootFns.js +25 -5
  78. package/lib/network/processor/extractSlotRootFns.js.map +1 -1
  79. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  80. package/lib/network/processor/gossipHandlers.js +242 -66
  81. package/lib/network/processor/gossipHandlers.js.map +1 -1
  82. package/lib/network/processor/index.d.ts +11 -1
  83. package/lib/network/processor/index.d.ts.map +1 -1
  84. package/lib/network/processor/index.js +232 -22
  85. package/lib/network/processor/index.js.map +1 -1
  86. package/lib/network/reqresp/types.d.ts +3 -3
  87. package/lib/network/reqresp/types.d.ts.map +1 -1
  88. package/lib/network/reqresp/types.js +9 -3
  89. package/lib/network/reqresp/types.js.map +1 -1
  90. package/lib/sync/unknownBlock.js +2 -2
  91. package/lib/sync/unknownBlock.js.map +1 -1
  92. package/lib/sync/utils/downloadByRange.d.ts +3 -3
  93. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  94. package/lib/sync/utils/downloadByRange.js +4 -2
  95. package/lib/sync/utils/downloadByRange.js.map +1 -1
  96. package/lib/sync/utils/downloadByRoot.d.ts +3 -3
  97. package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
  98. package/lib/sync/utils/downloadByRoot.js +10 -5
  99. package/lib/sync/utils/downloadByRoot.js.map +1 -1
  100. package/lib/util/blobs.d.ts +3 -3
  101. package/lib/util/blobs.d.ts.map +1 -1
  102. package/lib/util/blobs.js +21 -10
  103. package/lib/util/blobs.js.map +1 -1
  104. package/lib/util/dataColumns.d.ts +18 -11
  105. package/lib/util/dataColumns.d.ts.map +1 -1
  106. package/lib/util/dataColumns.js +51 -17
  107. package/lib/util/dataColumns.js.map +1 -1
  108. package/lib/util/execution.d.ts +6 -2
  109. package/lib/util/execution.d.ts.map +1 -1
  110. package/lib/util/execution.js +49 -25
  111. package/lib/util/execution.js.map +1 -1
  112. package/lib/util/sszBytes.d.ts +25 -1
  113. package/lib/util/sszBytes.d.ts.map +1 -1
  114. package/lib/util/sszBytes.js +189 -2
  115. package/lib/util/sszBytes.js.map +1 -1
  116. package/package.json +15 -15
  117. package/src/api/impl/beacon/blocks/index.ts +17 -14
  118. package/src/api/impl/debug/index.ts +2 -2
  119. package/src/chain/ColumnReconstructionTracker.ts +6 -5
  120. package/src/chain/GetBlobsTracker.ts +14 -12
  121. package/src/chain/blocks/blockInput/blockInput.ts +8 -8
  122. package/src/chain/blocks/blockInput/types.ts +4 -4
  123. package/src/chain/blocks/importBlock.ts +18 -1
  124. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +53 -12
  125. package/src/chain/blocks/payloadEnvelopeInput/types.ts +2 -1
  126. package/src/chain/chain.ts +63 -24
  127. package/src/chain/emitter.ts +25 -7
  128. package/src/chain/errors/dataColumnSidecarError.ts +32 -1
  129. package/src/chain/interface.ts +4 -2
  130. package/src/chain/seenCache/seenGossipBlockInput.ts +2 -2
  131. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +2 -2
  132. package/src/chain/validation/dataColumnSidecar.ts +230 -7
  133. package/src/db/buckets.ts +2 -2
  134. package/src/db/repositories/dataColumnSidecar.ts +4 -2
  135. package/src/db/repositories/dataColumnSidecarArchive.ts +4 -2
  136. package/src/metrics/metrics/lodestar.ts +34 -0
  137. package/src/network/interface.ts +3 -2
  138. package/src/network/network.ts +7 -4
  139. package/src/network/processor/extractSlotRootFns.ts +32 -6
  140. package/src/network/processor/gossipHandlers.ts +305 -79
  141. package/src/network/processor/index.ts +302 -22
  142. package/src/network/reqresp/types.ts +13 -5
  143. package/src/sync/unknownBlock.ts +3 -3
  144. package/src/sync/utils/downloadByRange.ts +9 -7
  145. package/src/sync/utils/downloadByRoot.ts +16 -12
  146. package/src/util/blobs.ts +35 -15
  147. package/src/util/dataColumns.ts +69 -25
  148. package/src/util/execution.ts +49 -30
  149. package/src/util/sszBytes.ts +245 -3
@@ -1,13 +1,16 @@
1
1
  import {routes} from "@lodestar/api";
2
2
  import {BeaconConfig, ChainForkConfig} from "@lodestar/config";
3
+ import {PayloadStatus} from "@lodestar/fork-choice";
3
4
  import {
4
5
  ForkName,
5
6
  ForkPostDeneb,
6
7
  ForkPostElectra,
8
+ ForkPostGloas,
7
9
  ForkPreElectra,
8
10
  ForkSeq,
9
11
  NUMBER_OF_COLUMNS,
10
12
  isForkPostElectra,
13
+ isForkPostGloas,
11
14
  } from "@lodestar/params";
12
15
  import {computeTimeAtSlot} from "@lodestar/state-transition";
13
16
  import {
@@ -19,6 +22,8 @@ import {
19
22
  UintNum64,
20
23
  deneb,
21
24
  fulu,
25
+ gloas,
26
+ isGloasDataColumnSidecar,
22
27
  ssz,
23
28
  sszTypesFor,
24
29
  } from "@lodestar/types";
@@ -30,7 +35,7 @@ import {
30
35
  IBlockInput,
31
36
  isBlockInputColumns,
32
37
  } from "../../chain/blocks/blockInput/index.js";
33
- import {PayloadEnvelopeInputSource} from "../../chain/blocks/payloadEnvelopeInput/index.js";
38
+ import {PayloadEnvelopeInput, PayloadEnvelopeInputSource} from "../../chain/blocks/payloadEnvelopeInput/index.js";
34
39
  import {BlobSidecarValidation} from "../../chain/blocks/types.js";
35
40
  import {ChainEvent} from "../../chain/emitter.js";
36
41
  import {
@@ -51,7 +56,10 @@ import {
51
56
  } from "../../chain/errors/index.js";
52
57
  import {IBeaconChain} from "../../chain/interface.js";
53
58
  import {validateGossipBlobSidecar} from "../../chain/validation/blobSidecar.js";
54
- import {validateGossipDataColumnSidecar} from "../../chain/validation/dataColumnSidecar.js";
59
+ import {
60
+ validateGossipFuluDataColumnSidecar,
61
+ validateGossipGloasDataColumnSidecar,
62
+ } from "../../chain/validation/dataColumnSidecar.js";
55
63
  import {validateGossipExecutionPayloadBid} from "../../chain/validation/executionPayloadBid.js";
56
64
  import {validateGossipExecutionPayloadEnvelope} from "../../chain/validation/executionPayloadEnvelope.js";
57
65
  import {
@@ -74,7 +82,7 @@ import {validateGossipPayloadAttestationMessage} from "../../chain/validation/pa
74
82
  import {OpSource} from "../../chain/validatorMonitor.js";
75
83
  import {Metrics} from "../../metrics/index.js";
76
84
  import {kzgCommitmentToVersionedHash} from "../../util/blobs.js";
77
- import {getBlobKzgCommitments} from "../../util/dataColumns.js";
85
+ import {getBlobKzgCommitments, getDataColumnSidecarSlot} from "../../util/dataColumns.js";
78
86
  import {INetworkCore} from "../core/index.js";
79
87
  import {NetworkEventBus} from "../events.js";
80
88
  import {
@@ -161,16 +169,19 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
161
169
 
162
170
  logger.debug("Received gossip block", {...logCtx});
163
171
 
164
- let blockInput: IBlockInput | undefined;
172
+ // optimistically add gossip block to the seen cache
173
+ // if validation fails, we will NOT forward this gossip block to peers
174
+ // - if PARENT_UNKNOWN error, blockInput will then be queued inside BlockInputSync. If the gossip block is really invalid, it will be pruned there
175
+ // - if other validator errors, blockInput will stay in the seen cache and will be pruned on finalization
176
+ const blockInput = chain.seenBlockInputCache.getByBlock({
177
+ block: signedBlock,
178
+ blockRootHex,
179
+ source: BlockInputSource.gossip,
180
+ seenTimestampSec,
181
+ peerIdStr,
182
+ });
165
183
  try {
166
184
  await validateGossipBlock(config, chain, signedBlock, fork);
167
- blockInput = chain.seenBlockInputCache.getByBlock({
168
- block: signedBlock,
169
- blockRootHex,
170
- source: BlockInputSource.gossip,
171
- seenTimestampSec,
172
- peerIdStr,
173
- });
174
185
  const blockInputMeta = blockInput.getLogMeta();
175
186
 
176
187
  const recvToValidation = Date.now() / 1000 - seenTimestampSec;
@@ -186,9 +197,9 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
186
197
  return blockInput;
187
198
  } catch (e) {
188
199
  if (e instanceof BlockGossipError) {
200
+ logger.debug("Gossip block has error", {slot, root: blockShortHex, code: e.type.code});
189
201
  if (e.type.code === BlockErrorCode.PARENT_UNKNOWN && blockInput) {
190
- logger.debug("Gossip block has error", {slot, root: blockShortHex, code: e.type.code});
191
- chain.emitter.emit(ChainEvent.unknownParent, {
202
+ chain.emitter.emit(ChainEvent.blockUnknownParent, {
192
203
  blockInput,
193
204
  peer: peerIdStr,
194
205
  source: BlockInputSource.gossip,
@@ -324,7 +335,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
324
335
  const blockInput = chain.seenBlockInputCache.get(blockRootHex);
325
336
  if (blockInput && isBlockInputColumns(blockInput) && blockInput.hasColumn(dataColumnSidecar.index)) {
326
337
  metrics?.peerDas.dataColumnSidecarProcessingSkip.inc();
327
- logger.debug("Already have column sidecar, skipping processing", {
338
+ logger.debug("Already have column sidecar in BlockInput, skipping processing", {
328
339
  ...blockInput.getLogMeta(),
329
340
  index: dataColumnSidecar.index,
330
341
  });
@@ -339,10 +350,11 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
339
350
  const verificationTimer = metrics?.peerDas.dataColumnSidecarGossipVerificationTime.startTimer();
340
351
 
341
352
  const delaySec = chain.clock.secFromSlot(slot, seenTimestampSec);
353
+ const secFromSlot = chain.clock.secFromSlot(slot);
342
354
  const recvToValLatency = Date.now() / 1000 - seenTimestampSec;
343
355
 
344
356
  try {
345
- await validateGossipDataColumnSidecar(chain, dataColumnSidecar, gossipSubnet, metrics);
357
+ await validateGossipFuluDataColumnSidecar(chain, dataColumnSidecar, gossipSubnet, metrics);
346
358
  const blockInput = chain.seenBlockInputCache.getByColumn({
347
359
  blockRootHex,
348
360
  columnSidecar: dataColumnSidecar,
@@ -372,6 +384,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
372
384
  currentSlot: chain.clock.currentSlot,
373
385
  peerId: peerIdStr,
374
386
  delaySec,
387
+ secFromSlot,
375
388
  gossipSubnet,
376
389
  columnIndex: dataColumnSidecar.index,
377
390
  recvToValLatency,
@@ -401,6 +414,131 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
401
414
  }
402
415
  }
403
416
 
417
+ async function validatePayloadDataColumn(
418
+ dataColumnSidecar: gloas.DataColumnSidecar,
419
+ gossipSubnet: SubnetID,
420
+ peerIdStr: string,
421
+ seenTimestampSec: number
422
+ ): Promise<PayloadEnvelopeInput> {
423
+ metrics?.peerDas.dataColumnSidecarProcessingRequests.inc();
424
+ const slot = dataColumnSidecar.slot;
425
+ const blockRootHex = toRootHex(dataColumnSidecar.beaconBlockRoot);
426
+
427
+ // check to see if payload has already been processed and PayloadEnvelopeInput has been deleted (column received via reqresp or other means)
428
+ if (chain.forkChoice.getBlockHex(blockRootHex, PayloadStatus.FULL) !== null) {
429
+ metrics?.peerDas.dataColumnSidecarProcessingSkip.inc();
430
+ logger.debug("Already processed payload for column sidecar, skipping processing", {
431
+ slot,
432
+ blockRoot: blockRootHex,
433
+ index: dataColumnSidecar.index,
434
+ });
435
+ throw new DataColumnSidecarGossipError(GossipAction.IGNORE, {
436
+ code: DataColumnSidecarErrorCode.ALREADY_KNOWN,
437
+ columnIndex: dataColumnSidecar.index,
438
+ slot,
439
+ });
440
+ }
441
+
442
+ const payloadInput = chain.seenPayloadEnvelopeInputCache.get(blockRootHex);
443
+
444
+ if (!payloadInput) {
445
+ // This should not happen for gossip because the network processor queues `data_column_sidecar`
446
+ // until block import creates the corresponding PayloadEnvelopeInput.
447
+ throw new DataColumnSidecarGossipError(GossipAction.IGNORE, {
448
+ code: DataColumnSidecarErrorCode.PAYLOAD_ENVELOPE_INPUT_MISSING,
449
+ slot,
450
+ blockRoot: blockRootHex,
451
+ });
452
+ }
453
+
454
+ // [IGNORE] The sidecar is the first sidecar for the tuple
455
+ // (sidecar.beacon_block_root, sidecar.index) with valid kzg proof.
456
+ if (payloadInput.hasColumn(dataColumnSidecar.index)) {
457
+ metrics?.peerDas.dataColumnSidecarProcessingSkip.inc();
458
+ logger.debug("Already have column sidecar in PayloadEnvelopeInput, skipping processing", {
459
+ ...payloadInput.getLogMeta(),
460
+ index: dataColumnSidecar.index,
461
+ });
462
+ throw new DataColumnSidecarGossipError(GossipAction.IGNORE, {
463
+ code: DataColumnSidecarErrorCode.ALREADY_KNOWN,
464
+ columnIndex: dataColumnSidecar.index,
465
+ slot,
466
+ });
467
+ }
468
+
469
+ const verificationTimer = metrics?.peerDas.dataColumnSidecarGossipVerificationTime.startTimer();
470
+
471
+ const delaySec = chain.clock.secFromSlot(slot, seenTimestampSec);
472
+ const secFromSlot = chain.clock.secFromSlot(slot);
473
+ const recvToValLatency = Date.now() / 1000 - seenTimestampSec;
474
+
475
+ try {
476
+ await validateGossipGloasDataColumnSidecar(chain, payloadInput, dataColumnSidecar, gossipSubnet, metrics);
477
+
478
+ const addedColumn = payloadInput.addColumn({
479
+ columnSidecar: dataColumnSidecar,
480
+ source: PayloadEnvelopeInputSource.gossip,
481
+ seenTimestampSec,
482
+ peerIdStr,
483
+ });
484
+
485
+ if (!addedColumn) {
486
+ metrics?.peerDas.dataColumnSidecarProcessingSkip.inc();
487
+ logger.debug("Already have column sidecar in PayloadEnvelopeInput, skipping processing", {
488
+ ...payloadInput.getLogMeta(),
489
+ index: dataColumnSidecar.index,
490
+ });
491
+ throw new DataColumnSidecarGossipError(GossipAction.IGNORE, {
492
+ code: DataColumnSidecarErrorCode.ALREADY_KNOWN,
493
+ columnIndex: dataColumnSidecar.index,
494
+ slot,
495
+ });
496
+ }
497
+
498
+ const recvToValidation = Date.now() / 1000 - seenTimestampSec;
499
+ const validationTime = recvToValidation - recvToValLatency;
500
+
501
+ metrics?.peerDas.dataColumnSidecarProcessingSuccesses.inc();
502
+ metrics?.gossipBlob.recvToValidation.observe(recvToValidation);
503
+ metrics?.gossipBlob.validationTime.observe(validationTime);
504
+
505
+ if (chain.emitter.listenerCount(routes.events.EventType.dataColumnSidecar)) {
506
+ chain.emitter.emit(routes.events.EventType.dataColumnSidecar, {
507
+ blockRoot: blockRootHex,
508
+ slot,
509
+ index: dataColumnSidecar.index,
510
+ });
511
+ }
512
+
513
+ logger.debug("Received gossip dataColumn", {
514
+ ...payloadInput.getLogMeta(),
515
+ currentSlot: chain.clock.currentSlot,
516
+ peerId: peerIdStr,
517
+ delaySec,
518
+ secFromSlot,
519
+ gossipSubnet,
520
+ columnIndex: dataColumnSidecar.index,
521
+ recvToValLatency,
522
+ recvToValidation,
523
+ validationTime,
524
+ });
525
+
526
+ return payloadInput;
527
+ } catch (e) {
528
+ if (e instanceof DataColumnSidecarGossipError && e.action === GossipAction.REJECT) {
529
+ chain.persistInvalidSszValue(
530
+ sszTypesFor(payloadInput.forkName as ForkPostGloas).DataColumnSidecar,
531
+ dataColumnSidecar,
532
+ `gossip_reject_slot_${slot}_index_${dataColumnSidecar.index}`
533
+ );
534
+ }
535
+
536
+ throw e;
537
+ } finally {
538
+ verificationTimer?.();
539
+ }
540
+ }
541
+
404
542
  function handleValidBeaconBlock(blockInput: IBlockInput, peerIdStr: string, seenTimestampSec: number): void {
405
543
  const signedBlock = blockInput.getBlock();
406
544
  const slot = signedBlock.message.slot;
@@ -554,78 +692,137 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
554
692
  peerIdStr,
555
693
  seenTimestampSec,
556
694
  }: GossipHandlerParamGeneric<GossipType.data_column_sidecar>) => {
695
+ const {fork} = topic.boundary;
557
696
  const {serializedData} = gossipData;
558
- // TODO GLOAS: handle gloas.DataColumnSidecar
559
- const dataColumnSidecar = sszDeserialize(topic, serializedData) as fulu.DataColumnSidecar;
560
- const dataColumnSlot = dataColumnSidecar.signedBlockHeader.message.slot;
697
+ const dataColumnSidecar = sszDeserialize(topic, serializedData);
698
+ const dataColumnSlot = getDataColumnSidecarSlot(dataColumnSidecar);
561
699
  const index = dataColumnSidecar.index;
562
-
563
- if (config.getForkSeq(dataColumnSlot) < ForkSeq.fulu) {
564
- throw new GossipActionError(GossipAction.REJECT, {code: "PRE_FULU_BLOCK"});
565
- }
566
700
  const delaySec = chain.clock.secFromSlot(dataColumnSlot, seenTimestampSec);
567
- const blockInput = await validateBeaconDataColumn(
568
- dataColumnSidecar,
569
- serializedData,
570
- topic.subnet,
571
- peerIdStr,
572
- seenTimestampSec
573
- );
574
- chain.serializedCache.set(dataColumnSidecar, serializedData);
575
- const blockInputMeta = blockInput.getLogMeta();
576
- const {receivedColumns} = blockInputMeta;
577
- // it's not helpful to track every single column received
578
- // instead of that, track 1st, 8th, 16th 32th, 64th, and 128th column
579
- switch (receivedColumns) {
580
- case 1:
581
- case config.SAMPLES_PER_SLOT:
582
- case 2 * config.SAMPLES_PER_SLOT:
583
- case NUMBER_OF_COLUMNS / 4:
584
- case NUMBER_OF_COLUMNS / 2:
585
- case NUMBER_OF_COLUMNS:
586
- metrics?.dataColumns.elapsedTimeTillReceived.observe({receivedOrder: receivedColumns}, delaySec);
587
- break;
588
- }
589
701
 
590
- if (!blockInput.hasComputedAllData()) {
591
- // immediately attempt fetch of data columns from execution engine
592
- chain.getBlobsTracker.triggerGetBlobs(blockInput);
593
- // if we've received at least half of the columns, trigger reconstruction of the rest
594
- if (blockInput.columnCount >= NUMBER_OF_COLUMNS / 2) {
595
- chain.columnReconstructionTracker.triggerColumnReconstruction(blockInput);
702
+ if (isForkPostGloas(fork)) {
703
+ if (!isGloasDataColumnSidecar(dataColumnSidecar)) {
704
+ throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
705
+ code: DataColumnSidecarErrorCode.INCORRECT_TYPE,
706
+ slot: dataColumnSlot,
707
+ columnIndex: index,
708
+ fork,
709
+ });
710
+ }
711
+
712
+ // After gloas, data columns are tracked in PayloadEnvelopeInput
713
+ const payloadInput = await validatePayloadDataColumn(
714
+ dataColumnSidecar,
715
+ topic.subnet,
716
+ peerIdStr,
717
+ seenTimestampSec
718
+ );
719
+ chain.serializedCache.set(dataColumnSidecar, serializedData);
720
+
721
+ const payloadInputMeta = payloadInput.getLogMeta();
722
+ const {receivedColumns} = payloadInputMeta;
723
+ // it's not helpful to track every single column received
724
+ // instead of that, track 1st, 8th, 16th 32th, 64th, and 128th column
725
+ switch (receivedColumns) {
726
+ case 1:
727
+ case config.SAMPLES_PER_SLOT:
728
+ case 2 * config.SAMPLES_PER_SLOT:
729
+ case NUMBER_OF_COLUMNS / 4:
730
+ case NUMBER_OF_COLUMNS / 2:
731
+ case NUMBER_OF_COLUMNS:
732
+ metrics?.dataColumns.elapsedTimeTillReceived.observe({receivedOrder: receivedColumns}, delaySec);
733
+ break;
596
734
  }
597
- }
598
735
 
599
- if (!blockInput.hasBlockAndAllData()) {
600
- const cutoffTimeMs = getCutoffTimeMs(chain, dataColumnSlot, BLOCK_AVAILABILITY_CUTOFF_MS);
601
- chain.logger.debug("Received gossip data column, waiting for full data availability", {
602
- msToWait: cutoffTimeMs,
603
- dataColumnIndex: index,
604
- ...blockInputMeta,
605
- });
606
- // do not await here to not delay gossip validation
607
- blockInput.waitForBlockAndAllData(cutoffTimeMs).catch((_e) => {
736
+ if (!payloadInput.hasComputedAllData()) {
737
+ // if we've received at least half of the columns, trigger reconstruction of the rest
738
+ if (receivedColumns >= NUMBER_OF_COLUMNS / 2) {
739
+ chain.columnReconstructionTracker.triggerColumnReconstruction(payloadInput);
740
+ }
741
+
742
+ chain.logger.debug("Received gossip data column, payload envelope input not yet complete", {
743
+ dataColumnIndex: index,
744
+ ...payloadInputMeta,
745
+ });
746
+ }
747
+
748
+ chain.processExecutionPayload(payloadInput, {validSignature: true}).catch((e) => {
608
749
  chain.logger.debug(
609
- "Waited for data after receiving gossip column. Cut-off reached so attempting to fetch remainder of BlockInput",
610
- {
611
- dataColumnIndex: index,
612
- ...blockInputMeta,
613
- }
750
+ "Error processing execution payload from gossip data column",
751
+ {slot: dataColumnSlot, root: payloadInput.blockRootHex},
752
+ e as Error
614
753
  );
615
- chain.emitter.emit(ChainEvent.incompleteBlockInput, {
616
- blockInput,
617
- peer: peerIdStr,
618
- source: BlockInputSource.gossip,
619
- });
620
754
  });
621
- }
755
+ } else {
756
+ if (config.getForkSeq(dataColumnSlot) < ForkSeq.fulu) {
757
+ throw new GossipActionError(GossipAction.REJECT, {code: "PRE_FULU_BLOCK"});
758
+ }
759
+
760
+ if (isGloasDataColumnSidecar(dataColumnSidecar)) {
761
+ throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
762
+ code: DataColumnSidecarErrorCode.INCORRECT_TYPE,
763
+ slot: dataColumnSlot,
764
+ columnIndex: index,
765
+ fork,
766
+ });
767
+ }
768
+
769
+ // Before gloas, data columns are tracked in BlockInput
770
+ const blockInput = await validateBeaconDataColumn(
771
+ dataColumnSidecar,
772
+ serializedData,
773
+ topic.subnet,
774
+ peerIdStr,
775
+ seenTimestampSec
776
+ );
777
+ chain.serializedCache.set(dataColumnSidecar, serializedData);
778
+ const blockInputMeta = blockInput.getLogMeta();
779
+ const {receivedColumns} = blockInputMeta;
780
+ // it's not helpful to track every single column received
781
+ // instead of that, track 1st, 8th, 16th 32th, 64th, and 128th column
782
+ switch (receivedColumns) {
783
+ case 1:
784
+ case config.SAMPLES_PER_SLOT:
785
+ case 2 * config.SAMPLES_PER_SLOT:
786
+ case NUMBER_OF_COLUMNS / 4:
787
+ case NUMBER_OF_COLUMNS / 2:
788
+ case NUMBER_OF_COLUMNS:
789
+ metrics?.dataColumns.elapsedTimeTillReceived.observe({receivedOrder: receivedColumns}, delaySec);
790
+ break;
791
+ }
622
792
 
623
- // TODO GLOAS: In Gloas, also add column to PayloadEnvelopeInput and notify the payload processor:
624
- // const payloadInput = chain.seenPayloadEnvelopeInput.get(blockRootHex);
625
- // if (payloadInput) {
626
- // payloadInput.addColumn({columnSidecar, source: BlockInputSource.gossip, seenTimestampSec, peerIdStr});
627
- // chain.processExecutionPayload(payloadInput, {validSignature: true});
628
- // }
793
+ if (!blockInput.hasComputedAllData()) {
794
+ // immediately attempt fetch of data columns from execution engine
795
+ chain.getBlobsTracker.triggerGetBlobs(blockInput);
796
+ // if we've received at least half of the columns, trigger reconstruction of the rest
797
+ if (blockInput.columnCount >= NUMBER_OF_COLUMNS / 2) {
798
+ chain.columnReconstructionTracker.triggerColumnReconstruction(blockInput);
799
+ }
800
+ }
801
+
802
+ if (!blockInput.hasBlockAndAllData()) {
803
+ const cutoffTimeMs = getCutoffTimeMs(chain, dataColumnSlot, BLOCK_AVAILABILITY_CUTOFF_MS);
804
+ chain.logger.debug("Received gossip data column, waiting for full data availability", {
805
+ msToWait: cutoffTimeMs,
806
+ dataColumnIndex: index,
807
+ ...blockInputMeta,
808
+ });
809
+ // do not await here to not delay gossip validation
810
+ blockInput.waitForBlockAndAllData(cutoffTimeMs).catch((_e) => {
811
+ chain.logger.debug(
812
+ "Waited for data after receiving gossip column. Cut-off reached so attempting to fetch remainder of BlockInput",
813
+ {
814
+ dataColumnIndex: index,
815
+ ...blockInputMeta,
816
+ }
817
+ );
818
+ chain.emitter.emit(ChainEvent.incompleteBlockInput, {
819
+ blockInput,
820
+ peer: peerIdStr,
821
+ source: BlockInputSource.gossip,
822
+ });
823
+ });
824
+ }
825
+ }
629
826
  },
630
827
 
631
828
  [GossipType.beacon_aggregate_and_proof]: async ({
@@ -843,8 +1040,37 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
843
1040
  const {serializedData} = gossipData;
844
1041
  const signedEnvelope = sszDeserialize(topic, serializedData);
845
1042
  const envelope = signedEnvelope.message;
846
- // TODO GLOAS: handle BLOCK_ROOT_UNKNOWN error to trigger sync
847
- await validateGossipExecutionPayloadEnvelope(chain, signedEnvelope);
1043
+
1044
+ // TODO GLOAS: consider optimistically create PayloadEnvelopeInput here similar to how we do that for beacon_block
1045
+ // so that UnknownBlockSync can handle backward sync
1046
+ // the problem now is we cannot create a PayloadEnvelopeInput without the beacon block being known, we need at least the proposer index
1047
+ // we can achieve that by looking into the EpochCache
1048
+ try {
1049
+ await validateGossipExecutionPayloadEnvelope(chain, signedEnvelope);
1050
+ } catch (e) {
1051
+ if (e instanceof ExecutionPayloadEnvelopeError) {
1052
+ const {slot, beaconBlockRoot} = signedEnvelope.message;
1053
+ logger.debug("Gossip envelope has error", {slot, root: toRootHex(beaconBlockRoot), code: e.type.code});
1054
+ if (e.type.code === ExecutionPayloadEnvelopeErrorCode.BLOCK_ROOT_UNKNOWN) {
1055
+ // TODO GLOAS: UnknownBlockSync to handle this
1056
+ chain.emitter.emit(ChainEvent.envelopeUnknownBlock, {
1057
+ envelope: signedEnvelope,
1058
+ peer: peerIdStr,
1059
+ source: BlockInputSource.gossip,
1060
+ });
1061
+ }
1062
+
1063
+ if (e.action === GossipAction.REJECT) {
1064
+ chain.persistInvalidSszValue(
1065
+ ssz.gloas.SignedExecutionPayloadEnvelope,
1066
+ signedEnvelope,
1067
+ `gossip_reject_slot_${slot}`
1068
+ );
1069
+ }
1070
+ }
1071
+
1072
+ throw e;
1073
+ }
848
1074
 
849
1075
  const slot = envelope.slot;
850
1076
  const delaySec = seenTimestampSec - computeTimeAtSlot(config, slot, chain.genesisTime);