@lodestar/beacon-node 1.50.0-dev.5866a397b6 → 1.50.0-dev.70314c9caa

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 (128) hide show
  1. package/lib/api/impl/beacon/pool/index.js +4 -4
  2. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  3. package/lib/chain/archiveStore/archiveStore.d.ts +1 -0
  4. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  5. package/lib/chain/archiveStore/archiveStore.js +6 -2
  6. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  7. package/lib/chain/archiveStore/utils/pruneHistory.d.ts +4 -1
  8. package/lib/chain/archiveStore/utils/pruneHistory.d.ts.map +1 -1
  9. package/lib/chain/archiveStore/utils/pruneHistory.js +4 -3
  10. package/lib/chain/archiveStore/utils/pruneHistory.js.map +1 -1
  11. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  12. package/lib/chain/blocks/importBlock.js +4 -0
  13. package/lib/chain/blocks/importBlock.js.map +1 -1
  14. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +2 -1
  15. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
  16. package/lib/chain/chain.js +3 -3
  17. package/lib/chain/chain.js.map +1 -1
  18. package/lib/chain/errors/gossipValidation.d.ts +3 -1
  19. package/lib/chain/errors/gossipValidation.d.ts.map +1 -1
  20. package/lib/chain/errors/gossipValidation.js +2 -0
  21. package/lib/chain/errors/gossipValidation.js.map +1 -1
  22. package/lib/chain/initState.d.ts +2 -9
  23. package/lib/chain/initState.d.ts.map +1 -1
  24. package/lib/chain/initState.js +5 -38
  25. package/lib/chain/initState.js.map +1 -1
  26. package/lib/chain/opPools/opPool.d.ts +5 -4
  27. package/lib/chain/opPools/opPool.d.ts.map +1 -1
  28. package/lib/chain/opPools/opPool.js +62 -18
  29. package/lib/chain/opPools/opPool.js.map +1 -1
  30. package/lib/chain/opPools/utils.d.ts +4 -1
  31. package/lib/chain/opPools/utils.d.ts.map +1 -1
  32. package/lib/chain/opPools/utils.js +11 -1
  33. package/lib/chain/opPools/utils.js.map +1 -1
  34. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  35. package/lib/chain/prepareNextSlot.js +5 -2
  36. package/lib/chain/prepareNextSlot.js.map +1 -1
  37. package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -1
  38. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  39. package/lib/chain/produceBlock/produceBlockBody.js +6 -2
  40. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  41. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +6 -0
  42. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  43. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +10 -0
  44. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  45. package/lib/chain/validation/attesterSlashing.d.ts +4 -4
  46. package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
  47. package/lib/chain/validation/attesterSlashing.js +3 -0
  48. package/lib/chain/validation/attesterSlashing.js.map +1 -1
  49. package/lib/chain/validation/proposerSlashing.d.ts +3 -3
  50. package/lib/chain/validation/proposerSlashing.d.ts.map +1 -1
  51. package/lib/chain/validation/proposerSlashing.js +3 -0
  52. package/lib/chain/validation/proposerSlashing.js.map +1 -1
  53. package/lib/db/beacon.d.ts.map +1 -1
  54. package/lib/db/beacon.js +2 -0
  55. package/lib/db/beacon.js.map +1 -1
  56. package/lib/db/repositories/proposerSlashing.d.ts +1 -0
  57. package/lib/db/repositories/proposerSlashing.d.ts.map +1 -1
  58. package/lib/db/repositories/proposerSlashing.js +5 -1
  59. package/lib/db/repositories/proposerSlashing.js.map +1 -1
  60. package/lib/index.d.ts +1 -1
  61. package/lib/index.d.ts.map +1 -1
  62. package/lib/index.js +1 -1
  63. package/lib/index.js.map +1 -1
  64. package/lib/metrics/metrics/beacon.d.ts +1 -1
  65. package/lib/metrics/metrics/beacon.d.ts.map +1 -1
  66. package/lib/metrics/metrics/beacon.js.map +1 -1
  67. package/lib/network/metadata.d.ts +1 -1
  68. package/lib/network/metadata.d.ts.map +1 -1
  69. package/lib/network/metadata.js +17 -17
  70. package/lib/network/metadata.js.map +1 -1
  71. package/lib/network/peers/datastore.d.ts +9 -9
  72. package/lib/network/peers/datastore.d.ts.map +1 -1
  73. package/lib/network/peers/datastore.js +36 -36
  74. package/lib/network/peers/datastore.js.map +1 -1
  75. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  76. package/lib/network/processor/gossipHandlers.js +7 -4
  77. package/lib/network/processor/gossipHandlers.js.map +1 -1
  78. package/lib/network/processor/gossipValidatorFn.d.ts +7 -3
  79. package/lib/network/processor/gossipValidatorFn.d.ts.map +1 -1
  80. package/lib/network/processor/gossipValidatorFn.js +7 -3
  81. package/lib/network/processor/gossipValidatorFn.js.map +1 -1
  82. package/lib/network/processor/index.d.ts +9 -0
  83. package/lib/network/processor/index.d.ts.map +1 -1
  84. package/lib/network/processor/index.js +77 -10
  85. package/lib/network/processor/index.js.map +1 -1
  86. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts.map +1 -1
  87. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +41 -26
  88. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
  89. package/lib/sync/range/range.d.ts.map +1 -1
  90. package/lib/sync/range/range.js +50 -18
  91. package/lib/sync/range/range.js.map +1 -1
  92. package/lib/sync/unknownBlock.d.ts +9 -5
  93. package/lib/sync/unknownBlock.d.ts.map +1 -1
  94. package/lib/sync/unknownBlock.js +88 -46
  95. package/lib/sync/unknownBlock.js.map +1 -1
  96. package/lib/util/execution.d.ts +2 -0
  97. package/lib/util/execution.d.ts.map +1 -1
  98. package/lib/util/execution.js +6 -6
  99. package/lib/util/execution.js.map +1 -1
  100. package/package.json +15 -15
  101. package/src/api/impl/beacon/pool/index.ts +4 -4
  102. package/src/chain/archiveStore/archiveStore.ts +9 -3
  103. package/src/chain/archiveStore/utils/pruneHistory.ts +6 -4
  104. package/src/chain/blocks/importBlock.ts +4 -0
  105. package/src/chain/blocks/payloadEnvelopeInput/types.ts +2 -1
  106. package/src/chain/chain.ts +3 -3
  107. package/src/chain/errors/gossipValidation.ts +2 -0
  108. package/src/chain/initState.ts +8 -60
  109. package/src/chain/opPools/opPool.ts +91 -18
  110. package/src/chain/opPools/utils.ts +31 -3
  111. package/src/chain/prepareNextSlot.ts +6 -3
  112. package/src/chain/produceBlock/produceBlockBody.ts +8 -1
  113. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +11 -0
  114. package/src/chain/validation/attesterSlashing.ts +7 -4
  115. package/src/chain/validation/proposerSlashing.ts +7 -4
  116. package/src/db/beacon.ts +2 -0
  117. package/src/db/repositories/proposerSlashing.ts +6 -1
  118. package/src/index.ts +1 -1
  119. package/src/metrics/metrics/beacon.ts +1 -1
  120. package/src/network/metadata.ts +17 -17
  121. package/src/network/peers/datastore.ts +36 -36
  122. package/src/network/processor/gossipHandlers.ts +7 -4
  123. package/src/network/processor/gossipValidatorFn.ts +13 -3
  124. package/src/network/processor/index.ts +89 -10
  125. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +42 -28
  126. package/src/sync/range/range.ts +50 -18
  127. package/src/sync/unknownBlock.ts +99 -50
  128. package/src/util/execution.ts +10 -6
@@ -15,7 +15,7 @@ import {ChainEvent, ChainEventData, IBeaconChain} from "../chain/index.js";
15
15
  import {validateGloasBlockDataColumnSidecars} from "../chain/validation/dataColumnSidecar.js";
16
16
  import {validateGossipExecutionPayloadEnvelope} from "../chain/validation/executionPayloadEnvelope.js";
17
17
  import {Metrics} from "../metrics/index.js";
18
- import {INetwork, NetworkEvent, NetworkEventData, prettyPrintPeerIdStr} from "../network/index.js";
18
+ import {INetwork, NetworkEvent, NetworkEventData, PeerAction, prettyPrintPeerIdStr} from "../network/index.js";
19
19
  import {PeerSyncMeta} from "../network/peers/peersData.js";
20
20
  import {MAX_PEERS_PER_ROOT} from "../network/processor/constants.js";
21
21
  import {ClockEvent} from "../util/clock.js";
@@ -442,6 +442,11 @@ export class BlockInputSync {
442
442
  };
443
443
 
444
444
  private addByRootHex = (rootHex: RootHex, peerIdStr?: PeerIdStr): boolean => {
445
+ if (this.knownBadBlocks.has(rootHex)) {
446
+ this.logger.debug("Ignoring known bad block root", {root: rootHex, peerIdStr: peerIdStr ?? "unknown peer"});
447
+ return false;
448
+ }
449
+
445
450
  let pendingBlock = this.pendingBlocks.get(rootHex);
446
451
  let added = false;
447
452
  if (!pendingBlock) {
@@ -477,6 +482,11 @@ export class BlockInputSync {
477
482
  };
478
483
 
479
484
  private addByBlockInput = (blockInput: IBlockInput, peerIdStr?: string): void => {
485
+ if (this.knownBadBlocks.has(blockInput.blockRootHex)) {
486
+ this.logger.debug("Ignoring known bad block", {slot: blockInput.slot, root: blockInput.blockRootHex, peerIdStr});
487
+ return;
488
+ }
489
+
480
490
  let pendingBlock = this.pendingBlocks.get(blockInput.blockRootHex);
481
491
  // if entry is missing or was added via rootHex and now we have more complete information overwrite
482
492
  // the existing information with the more complete cache entry
@@ -513,6 +523,15 @@ export class BlockInputSync {
513
523
  };
514
524
 
515
525
  private addByPayloadRootHex = (rootHex: RootHex, peerIdStr?: PeerIdStr, slot?: Slot): boolean => {
526
+ if (this.knownBadBlocks.has(rootHex)) {
527
+ this.logger.debug("Ignoring payload for known bad block root", {
528
+ slot: slot ?? "unknown",
529
+ root: rootHex,
530
+ peerIdStr,
531
+ });
532
+ return false;
533
+ }
534
+
516
535
  let pendingPayload = this.pendingPayloads.get(rootHex);
517
536
  let added = false;
518
537
  if (!pendingPayload) {
@@ -581,6 +600,15 @@ export class BlockInputSync {
581
600
  peerIdStr?: PeerIdStr,
582
601
  envelope?: gloas.SignedExecutionPayloadEnvelope
583
602
  ): void => {
603
+ if (this.knownBadBlocks.has(payloadInput.blockRootHex)) {
604
+ this.logger.debug("Ignoring payload input for known bad block root", {
605
+ slot: payloadInput.slot,
606
+ root: payloadInput.blockRootHex,
607
+ peerIdStr,
608
+ });
609
+ return;
610
+ }
611
+
584
612
  const pendingPayload = this.toPendingPayloadInput(
585
613
  payloadInput,
586
614
  this.pendingPayloads.get(payloadInput.blockRootHex),
@@ -654,6 +682,7 @@ export class BlockInputSync {
654
682
  return {kind: "ready"};
655
683
  }
656
684
 
685
+ // The parent's payload is known but does not match `parentBlockHashHex`
657
686
  if (this.chain.forkChoice.hasPayloadHexUnsafe(parentRootHex)) {
658
687
  return {kind: "invalidParentPayload", parentRootHex, parentBlockHashHex};
659
688
  }
@@ -1032,7 +1061,8 @@ export class BlockInputSync {
1032
1061
  }
1033
1062
  } else if (blockSlot <= finalizedSlot) {
1034
1063
  // the common ancestor of the downloading chain and canonical chain should be at least the finalized slot and
1035
- // we should found it through forkchoice. If not, we should penalize all peers sending us this block chain
1064
+ // we should found it through forkchoice. If not, drop the chain without penalizing peers because pending entries are
1065
+ // block retained on failed download, so finalization can pass the block while it is still pending
1036
1066
  // 0 - 1 - ... - n - finalizedSlot
1037
1067
  // \
1038
1068
  // parent 1 - parent 2 - ... - unknownParent block
@@ -1040,7 +1070,7 @@ export class BlockInputSync {
1040
1070
  ...logCtx2,
1041
1071
  finalizedSlot,
1042
1072
  });
1043
- this.removeAndDownScoreAllDescendants(block, DroppedItemReason.belowFinalized);
1073
+ this.removeAllDescendants(block, DroppedItemReason.belowFinalized);
1044
1074
  } else {
1045
1075
  this.onUnknownBlockRoot({rootHex: pending.blockInput.parentRootHex, source: BlockInputSource.byRoot});
1046
1076
  }
@@ -1172,15 +1202,18 @@ export class BlockInputSync {
1172
1202
  break;
1173
1203
 
1174
1204
  case BlockErrorCode.EXECUTION_ENGINE_INVALID:
1175
- // the peer served a bad block
1205
+ // Ban but do not downscore: optimistic-sync peers may forward and serve blocks whose payload is later
1206
+ // rejected by the execution engine. Per the spec, an INVALID execution payload SHOULD NOT cause a peer
1207
+ // to be down-scored or disconnected, only consensus-layer invalidity MAY.
1208
+ // https://github.com/ethereum/consensus-specs/blob/master/specs/bellatrix/p2p-interface.md#the-reqresp-domain
1176
1209
  this.logger.debug("Execution engine rejected block from unknown parent sync", errorData, res.err);
1177
- this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.elInvalid);
1210
+ this.removeAndBanAllDescendants(pendingBlock, DroppedItemReason.elInvalid);
1178
1211
  break;
1179
1212
 
1180
1213
  default:
1181
1214
  // Block is not correct with respect to our chain. Log error loudly
1182
1215
  this.logger.debug("Error processing block from unknown parent sync", errorData, res.err);
1183
- this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidBlock);
1216
+ this.removeAllDescendants(pendingBlock, DroppedItemReason.invalidBlock);
1184
1217
  }
1185
1218
  }
1186
1219
 
@@ -1393,20 +1426,23 @@ export class BlockInputSync {
1393
1426
  pendingPayload.status = PendingPayloadInputStatus.downloaded;
1394
1427
  break;
1395
1428
 
1429
+ // The invalid envelope is evicted from the seen cache in the cases below, otherwise the next
1430
+ // attempt would reuse it instead of fetching another envelope from peers
1396
1431
  case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
1397
1432
  this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
1433
+ this.chain.seenPayloadEnvelopeInputCache.removeInvalid(pendingPayload.payloadInput);
1398
1434
  this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.elInvalid);
1399
1435
  break;
1400
1436
 
1401
1437
  case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
1402
1438
  this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
1439
+ this.chain.seenPayloadEnvelopeInputCache.removeInvalid(pendingPayload.payloadInput);
1403
1440
  this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidEnvelope);
1404
1441
  break;
1405
1442
 
1406
1443
  case PayloadErrorCode.INVALID_SIGNATURE:
1407
- // TODO GLOAS: Decide how invalid payload inputs should eventually leave memory without
1408
- // reintroducing envelope replacement / recreation flows.
1409
1444
  this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
1445
+ this.chain.seenPayloadEnvelopeInputCache.removeInvalid(pendingPayload.payloadInput);
1410
1446
  this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidSignature);
1411
1447
  break;
1412
1448
 
@@ -1478,11 +1514,11 @@ export class BlockInputSync {
1478
1514
  }
1479
1515
 
1480
1516
  const {peerId, client: peerClient} = peerMeta;
1481
- cacheItem.peerIdStrings.add(peerId);
1482
1517
 
1483
1518
  try {
1484
1519
  if (!envelope) {
1485
1520
  envelope = await this.fetchExecutionPayloadEnvelope(peerId, blockRoot, rootHex);
1521
+ cacheItem.peerIdStrings.add(peerId);
1486
1522
  const slotWasUnknown = typeof slot !== "number";
1487
1523
  slot = envelope.message.payload.slotNumber;
1488
1524
  if (slotWasUnknown) {
@@ -1524,6 +1560,8 @@ export class BlockInputSync {
1524
1560
  const missing = pendingPayload.payloadInput.getMissingSampledColumnMeta().missing;
1525
1561
  if (missing.length > 0) {
1526
1562
  const columnSidecars = await this.fetchPayloadColumns(peerMeta, pendingPayload.payloadInput, missing);
1563
+ // toPendingPayloadInput copied cacheItem's set, so add to the object that is carried forward
1564
+ pendingPayload.peerIdStrings.add(peerId);
1527
1565
  const seenTimestampSec = Date.now() / 1000;
1528
1566
  for (const columnSidecar of columnSidecars) {
1529
1567
  if (pendingPayload.payloadInput.hasColumn(columnSidecar.index)) {
@@ -1716,8 +1754,6 @@ export class BlockInputSync {
1716
1754
  }
1717
1755
  const {peerId, client: peerClient} = peerMeta;
1718
1756
 
1719
- cacheItem.peerIdStrings.add(peerId);
1720
-
1721
1757
  try {
1722
1758
  const downloadResult = await downloadByRoot({
1723
1759
  config: this.config,
@@ -1728,6 +1764,7 @@ export class BlockInputSync {
1728
1764
  cacheItem,
1729
1765
  });
1730
1766
  cacheItem = downloadResult.result;
1767
+ cacheItem.peerIdStrings.add(peerId);
1731
1768
  if (slot === undefined) {
1732
1769
  slot = cacheItem.blockInput.slot;
1733
1770
  // we were not able to observe the time into slot when starting the fetch, do it now
@@ -1843,44 +1880,8 @@ export class BlockInputSync {
1843
1880
  throw Error(message);
1844
1881
  }
1845
1882
 
1846
- /**
1847
- * Gets all descendant blocks of `block` recursively from `pendingBlocks`.
1848
- * Assumes that if a parent block does not exist or is not processable, all descendant blocks are bad too.
1849
- * Downscore all peers that have referenced any of this bad blocks. May report peers multiple times if they have
1850
- * referenced more than one bad block.
1851
- */
1852
- private removeAndDownScoreAllDescendants(block: BlockInputSyncCacheItem, headReason: DroppedItemReason): void {
1853
- // Get all blocks that are a descendant of this one
1854
- const badPendingBlocks = this.removeAllDescendants(block, headReason, DroppedItemReason.invalidParent);
1855
- // just console log and do not penalize on pending/bad blocks for debugging
1856
- // console.log("removeAndDownscoreAllDescendants", {block});
1857
-
1858
- for (const block of badPendingBlocks) {
1859
- //
1860
- // TODO(fulu): why is this commented out here?
1861
- //
1862
- // this.knownBadBlocks.add(block.blockRootHex);
1863
- // for (const peerIdStr of block.peerIdStrings) {
1864
- // // TODO: Refactor peerRpcScores to work with peerIdStr only
1865
- // this.network.reportPeer(peerIdStr, PeerAction.LowToleranceError, "BadBlockByRoot");
1866
- // }
1867
- this.logger.debug("ignored Banning unknown block", {
1868
- slot: getBlockInputSyncCacheItemSlot(block),
1869
- root: getBlockInputSyncCacheItemRootHex(block),
1870
- peerIdStrings: Array.from(block.peerIdStrings)
1871
- .map((id) => prettyPrintPeerIdStr(id))
1872
- .join(","),
1873
- });
1874
- }
1875
-
1876
- // Prune knownBadBlocks
1877
- pruneSetToMax(this.knownBadBlocks, MAX_KNOWN_BAD_BLOCKS);
1878
- }
1879
-
1880
1883
  // Once a parent payload is invalid, every descendant waiting on that payload lineage becomes unrecoverable too.
1881
1884
  private removePendingPayloadAndDescendants(rootHex: RootHex, headReason: DroppedItemReason): void {
1882
- // Keep PayloadEnvelopeInput resident in the seen cache. importBlock() owns that object and
1883
- // later validation/finalization logic decides when it can leave memory.
1884
1885
  if (this.pendingPayloads.delete(rootHex)) {
1885
1886
  this.metrics?.blockInputSync.removedPayloads.inc({reason: headReason}, 1);
1886
1887
  }
@@ -1903,13 +1904,61 @@ export class BlockInputSync {
1903
1904
  }
1904
1905
  }
1905
1906
 
1907
+ /**
1908
+ * Same as `removeAndBanAllDescendants`, and downscores every peer that referenced or served a banned item, once per
1909
+ * peer. Only call for consensus-layer invalidity, never for an INVALID execution payload:
1910
+ * https://github.com/ethereum/consensus-specs/blob/master/specs/bellatrix/p2p-interface.md#the-reqresp-domain
1911
+ */
1912
+ private removeAndDownScoreAllDescendants(block: BlockInputSyncCacheItem, headReason: DroppedItemReason): void {
1913
+ const peersToReport = new Set<PeerIdStr>();
1914
+ for (const bannedBlock of this.removeAndBanAllDescendants(block, headReason)) {
1915
+ for (const peerIdStr of bannedBlock.peerIdStrings) {
1916
+ peersToReport.add(peerIdStr);
1917
+ }
1918
+ }
1919
+
1920
+ for (const peerIdStr of peersToReport) {
1921
+ this.network.reportPeer(peerIdStr, PeerAction.LowToleranceError, headReason);
1922
+ }
1923
+ }
1924
+
1925
+ /**
1926
+ * Removes `block` and all its pending descendants and bans their roots in `knownBadBlocks`. Only call when the chain
1927
+ * is provably bad: a banned root is never searched for again. Returns the banned items.
1928
+ */
1929
+ private removeAndBanAllDescendants(
1930
+ block: BlockInputSyncCacheItem,
1931
+ headReason: DroppedItemReason
1932
+ ): BlockInputSyncCacheItem[] {
1933
+ const bannedBlocks: BlockInputSyncCacheItem[] = [];
1934
+ for (const badBlock of this.removeAllDescendants(block, headReason, DroppedItemReason.invalidParent)) {
1935
+ const rootHex = getBlockInputSyncCacheItemRootHex(badBlock);
1936
+ // a gossip import may race a failing sync path, never ban a root fork choice has imported
1937
+ if (this.chain.forkChoice.hasBlockHex(rootHex)) {
1938
+ continue;
1939
+ }
1940
+ this.knownBadBlocks.add(rootHex);
1941
+ bannedBlocks.push(badBlock);
1942
+ this.logger.debug("Banning unknown block", {
1943
+ slot: getBlockInputSyncCacheItemSlot(badBlock),
1944
+ root: rootHex,
1945
+ reason: headReason,
1946
+ peerIdStrings: Array.from(badBlock.peerIdStrings)
1947
+ .map((id) => prettyPrintPeerIdStr(id))
1948
+ .join(","),
1949
+ });
1950
+ }
1951
+
1952
+ pruneSetToMax(this.knownBadBlocks, MAX_KNOWN_BAD_BLOCKS);
1953
+ return bannedBlocks;
1954
+ }
1955
+
1906
1956
  private removeAllDescendants(
1907
1957
  block: BlockInputSyncCacheItem,
1908
1958
  headReason: DroppedItemReason,
1909
1959
  descendantReason: DroppedItemReason = DroppedItemReason.invalidParent
1910
1960
  ): BlockInputSyncCacheItem[] {
1911
1961
  const rootHex = getBlockInputSyncCacheItemRootHex(block);
1912
- const slot = getBlockInputSyncCacheItemSlot(block);
1913
1962
  // Get all blocks that are a descendant of this one (index 0 is the head item itself)
1914
1963
  const badPendingBlocks = [block, ...getAllDescendantBlocks(rootHex, this.pendingBlocks)];
1915
1964
 
@@ -1926,8 +1975,8 @@ export class BlockInputSync {
1926
1975
  // Keep PayloadEnvelopeInput resident in the seen cache for consistency with the
1927
1976
  // importBlock()-owned lifecycle.
1928
1977
  this.logger.debug("Removing bad/unknown/incomplete BlockInputSyncCacheItem", {
1929
- slot,
1930
- blockRoot: rootHex,
1978
+ slot: getBlockInputSyncCacheItemSlot(block),
1979
+ root: rootHex,
1931
1980
  });
1932
1981
  }
1933
1982
 
@@ -259,8 +259,8 @@ export async function getDataColumnSidecarsFromExecution(
259
259
  return DataColumnEngineResult.SuccessResolved;
260
260
  }
261
261
 
262
- /** engine_getPayloadBodiesByHashV2: ELs MUST support at least 32 hashes per request. */
263
- const MAX_BODIES_REQUEST = 32;
262
+ /** engine_getPayloadBodiesByHashV2: every EL must accept requests of up to 32 hashes, larger ones may fail with -38004 */
263
+ export const MAX_BODIES_PER_REQUEST = 32;
264
264
 
265
265
  type SlotEnvelopeBytes = {slot: Slot; envelopeBytes: Uint8Array};
266
266
 
@@ -299,7 +299,7 @@ export async function* reconstructExecutionPayloadEnvelopesByRange(
299
299
 
300
300
  for await (const {key, value: bytes} of archive.binaryEntriesStream({gte: startSlot, lt: endSlot})) {
301
301
  batch.push({slot: archive.decodeKey(key), ...decodeArchivedEnvelope(bytes)});
302
- if (batch.length === MAX_BODIES_REQUEST) {
302
+ if (batch.length === MAX_BODIES_PER_REQUEST) {
303
303
  yield* reconstructBatch(executionEngine, logger, metrics, batch);
304
304
  batch = [];
305
305
  }
@@ -359,9 +359,13 @@ export async function reconstructExecutionPayloadEnvelopes(
359
359
  headerEnvelopes: gloas.SignedExecutionPayloadHeaderEnvelope[]
360
360
  ): Promise<(gloas.SignedExecutionPayloadEnvelope | RebuildMiss)[]> {
361
361
  const out: (gloas.SignedExecutionPayloadEnvelope | RebuildMiss)[] = [];
362
- for (let i = 0; i < headerEnvelopes.length; i += MAX_BODIES_REQUEST) {
362
+ for (let i = 0; i < headerEnvelopes.length; i += MAX_BODIES_PER_REQUEST) {
363
363
  out.push(
364
- ...(await reconstructEnvelopesBatch(executionEngine, metrics, headerEnvelopes.slice(i, i + MAX_BODIES_REQUEST)))
364
+ ...(await reconstructEnvelopesBatch(
365
+ executionEngine,
366
+ metrics,
367
+ headerEnvelopes.slice(i, i + MAX_BODIES_PER_REQUEST)
368
+ ))
365
369
  );
366
370
  }
367
371
  return out;
@@ -409,7 +413,7 @@ async function reconstructEnvelopesBatch(
409
413
  withdrawals: body.withdrawals,
410
414
  blockAccessList: body.blockAccessList,
411
415
  });
412
- metrics?.payloadEnvelopeReconstruction.envelopes.inc({result: "ok"});
416
+ metrics?.payloadEnvelopeReconstruction.envelopes.inc({result: "success"});
413
417
  return envelope;
414
418
  } catch (e) {
415
419
  if (