@lodestar/beacon-node 1.47.0-dev.be9d5b774d → 1.47.0-dev.beabd3a65c

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 (213) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +3 -2
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
  5. package/lib/api/impl/beacon/pool/index.js +8 -1
  6. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  7. package/lib/api/impl/beacon/state/utils.d.ts +2 -1
  8. package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
  9. package/lib/api/impl/beacon/state/utils.js.map +1 -1
  10. package/lib/api/impl/events/index.d.ts.map +1 -1
  11. package/lib/api/impl/events/index.js +2 -0
  12. package/lib/api/impl/events/index.js.map +1 -1
  13. package/lib/api/rest/base.d.ts.map +1 -1
  14. package/lib/api/rest/base.js +7 -0
  15. package/lib/api/rest/base.js.map +1 -1
  16. package/lib/chain/blocks/importExecutionPayload.d.ts +3 -2
  17. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  18. package/lib/chain/blocks/importExecutionPayload.js +11 -9
  19. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  20. package/lib/chain/blocks/index.d.ts.map +1 -1
  21. package/lib/chain/blocks/index.js +14 -6
  22. package/lib/chain/blocks/index.js.map +1 -1
  23. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +2 -1
  24. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
  25. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +6 -2
  26. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
  27. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +14 -5
  28. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
  29. package/lib/chain/blocks/payloadEnvelopeInput/types.js +5 -0
  30. package/lib/chain/blocks/payloadEnvelopeInput/types.js.map +1 -1
  31. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
  32. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +22 -12
  33. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
  34. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +2 -2
  35. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
  36. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
  37. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
  38. package/lib/chain/bls/index.d.ts +1 -1
  39. package/lib/chain/bls/index.d.ts.map +1 -1
  40. package/lib/chain/bls/interface.d.ts +5 -5
  41. package/lib/chain/bls/interface.d.ts.map +1 -1
  42. package/lib/chain/bls/multithread/index.d.ts +5 -11
  43. package/lib/chain/bls/multithread/index.d.ts.map +1 -1
  44. package/lib/chain/bls/multithread/index.js +116 -101
  45. package/lib/chain/bls/multithread/index.js.map +1 -1
  46. package/lib/chain/bls/multithread/jobItem.d.ts +5 -19
  47. package/lib/chain/bls/multithread/jobItem.d.ts.map +1 -1
  48. package/lib/chain/bls/multithread/jobItem.js +10 -65
  49. package/lib/chain/bls/multithread/jobItem.js.map +1 -1
  50. package/lib/chain/bls/multithread/types.d.ts +29 -8
  51. package/lib/chain/bls/multithread/types.d.ts.map +1 -1
  52. package/lib/chain/bls/multithread/types.js +12 -0
  53. package/lib/chain/bls/multithread/types.js.map +1 -1
  54. package/lib/chain/bls/multithread/utils.d.ts +4 -0
  55. package/lib/chain/bls/multithread/utils.d.ts.map +1 -1
  56. package/lib/chain/bls/multithread/utils.js +10 -0
  57. package/lib/chain/bls/multithread/utils.js.map +1 -1
  58. package/lib/chain/bls/multithread/worker.js +49 -23
  59. package/lib/chain/bls/multithread/worker.js.map +1 -1
  60. package/lib/chain/bls/singleThread.d.ts +4 -10
  61. package/lib/chain/bls/singleThread.d.ts.map +1 -1
  62. package/lib/chain/bls/singleThread.js +8 -45
  63. package/lib/chain/bls/singleThread.js.map +1 -1
  64. package/lib/chain/bls/utils.d.ts +4 -4
  65. package/lib/chain/bls/utils.d.ts.map +1 -1
  66. package/lib/chain/bls/utils.js +18 -19
  67. package/lib/chain/bls/utils.js.map +1 -1
  68. package/lib/chain/chain.d.ts +2 -1
  69. package/lib/chain/chain.d.ts.map +1 -1
  70. package/lib/chain/chain.js +12 -5
  71. package/lib/chain/chain.js.map +1 -1
  72. package/lib/chain/errors/blockError.d.ts +1 -0
  73. package/lib/chain/errors/blockError.d.ts.map +1 -1
  74. package/lib/chain/errors/blockError.js +6 -1
  75. package/lib/chain/errors/blockError.js.map +1 -1
  76. package/lib/chain/interface.d.ts +2 -1
  77. package/lib/chain/interface.d.ts.map +1 -1
  78. package/lib/chain/interface.js.map +1 -1
  79. package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
  80. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  81. package/lib/chain/opPools/attestationPool.d.ts +1 -1
  82. package/lib/chain/opPools/attestationPool.d.ts.map +1 -1
  83. package/lib/chain/opPools/attestationPool.js +1 -1
  84. package/lib/chain/opPools/attestationPool.js.map +1 -1
  85. package/lib/chain/opPools/payloadAttestationPool.js +3 -3
  86. package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
  87. package/lib/chain/opPools/syncCommitteeMessagePool.js +1 -1
  88. package/lib/chain/opPools/syncCommitteeMessagePool.js.map +1 -1
  89. package/lib/chain/opPools/syncContributionAndProofPool.js +1 -1
  90. package/lib/chain/opPools/syncContributionAndProofPool.js.map +1 -1
  91. package/lib/chain/opPools/utils.d.ts +1 -1
  92. package/lib/chain/opPools/utils.d.ts.map +1 -1
  93. package/lib/chain/opPools/utils.js +1 -1
  94. package/lib/chain/opPools/utils.js.map +1 -1
  95. package/lib/chain/seenCache/seenAttesters.d.ts +9 -5
  96. package/lib/chain/seenCache/seenAttesters.d.ts.map +1 -1
  97. package/lib/chain/seenCache/seenAttesters.js +25 -4
  98. package/lib/chain/seenCache/seenAttesters.js.map +1 -1
  99. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +28 -1
  100. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  101. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +121 -13
  102. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  103. package/lib/chain/validation/attestation.d.ts.map +1 -1
  104. package/lib/chain/validation/attestation.js +1 -4
  105. package/lib/chain/validation/attestation.js.map +1 -1
  106. package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
  107. package/lib/chain/validation/attesterSlashing.js +1 -1
  108. package/lib/chain/validation/attesterSlashing.js.map +1 -1
  109. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  110. package/lib/chain/validation/executionPayloadBid.js +1 -2
  111. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  112. package/lib/chain/validation/executionPayloadEnvelope.js +1 -1
  113. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  114. package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
  115. package/lib/chain/validation/payloadAttestationMessage.js +5 -14
  116. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  117. package/lib/chain/validation/proposerPreferences.js +2 -2
  118. package/lib/chain/validation/proposerPreferences.js.map +1 -1
  119. package/lib/chain/validation/proposerSlashing.js +1 -1
  120. package/lib/chain/validation/proposerSlashing.js.map +1 -1
  121. package/lib/metrics/metrics/lodestar.d.ts +45 -20
  122. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  123. package/lib/metrics/metrics/lodestar.js +87 -51
  124. package/lib/metrics/metrics/lodestar.js.map +1 -1
  125. package/lib/network/gossip/topic.d.ts +13 -0
  126. package/lib/network/gossip/topic.d.ts.map +1 -1
  127. package/lib/network/gossip/topic.js +37 -0
  128. package/lib/network/gossip/topic.js.map +1 -1
  129. package/lib/network/network.d.ts.map +1 -1
  130. package/lib/network/network.js +22 -43
  131. package/lib/network/network.js.map +1 -1
  132. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  133. package/lib/network/processor/gossipHandlers.js +5 -1
  134. package/lib/network/processor/gossipHandlers.js.map +1 -1
  135. package/lib/network/util.d.ts +0 -1
  136. package/lib/network/util.d.ts.map +1 -1
  137. package/lib/network/util.js +0 -4
  138. package/lib/network/util.js.map +1 -1
  139. package/lib/node/nodejs.d.ts +2 -1
  140. package/lib/node/nodejs.d.ts.map +1 -1
  141. package/lib/node/nodejs.js.map +1 -1
  142. package/lib/sync/range/batch.d.ts +23 -5
  143. package/lib/sync/range/batch.d.ts.map +1 -1
  144. package/lib/sync/range/batch.js +61 -22
  145. package/lib/sync/range/batch.js.map +1 -1
  146. package/lib/sync/range/chain.d.ts +2 -1
  147. package/lib/sync/range/chain.d.ts.map +1 -1
  148. package/lib/sync/range/chain.js +23 -10
  149. package/lib/sync/range/chain.js.map +1 -1
  150. package/lib/sync/unknownBlock.d.ts.map +1 -1
  151. package/lib/sync/unknownBlock.js +7 -13
  152. package/lib/sync/unknownBlock.js.map +1 -1
  153. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  154. package/lib/sync/utils/downloadByRange.js +16 -11
  155. package/lib/sync/utils/downloadByRange.js.map +1 -1
  156. package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
  157. package/lib/sync/utils/downloadByRoot.js +3 -1
  158. package/lib/sync/utils/downloadByRoot.js.map +1 -1
  159. package/package.json +35 -35
  160. package/src/api/impl/beacon/blocks/index.ts +3 -2
  161. package/src/api/impl/beacon/pool/index.ts +7 -1
  162. package/src/api/impl/beacon/state/utils.ts +2 -1
  163. package/src/api/impl/events/index.ts +3 -1
  164. package/src/api/rest/base.ts +7 -0
  165. package/src/chain/blocks/importExecutionPayload.ts +12 -11
  166. package/src/chain/blocks/index.ts +18 -6
  167. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +8 -2
  168. package/src/chain/blocks/payloadEnvelopeInput/types.ts +17 -4
  169. package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +25 -16
  170. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +1 -9
  171. package/src/chain/bls/index.ts +1 -1
  172. package/src/chain/bls/interface.ts +7 -3
  173. package/src/chain/bls/multithread/index.ts +148 -120
  174. package/src/chain/bls/multithread/jobItem.ts +12 -82
  175. package/src/chain/bls/multithread/types.ts +33 -8
  176. package/src/chain/bls/multithread/utils.ts +11 -0
  177. package/src/chain/bls/multithread/worker.ts +85 -27
  178. package/src/chain/bls/singleThread.ts +11 -55
  179. package/src/chain/bls/utils.ts +23 -24
  180. package/src/chain/chain.ts +13 -6
  181. package/src/chain/errors/blockError.ts +8 -2
  182. package/src/chain/interface.ts +2 -1
  183. package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
  184. package/src/chain/opPools/attestationPool.ts +1 -1
  185. package/src/chain/opPools/payloadAttestationPool.ts +3 -3
  186. package/src/chain/opPools/syncCommitteeMessagePool.ts +1 -1
  187. package/src/chain/opPools/syncContributionAndProofPool.ts +1 -1
  188. package/src/chain/opPools/utils.ts +1 -1
  189. package/src/chain/seenCache/seenAttesters.ts +31 -5
  190. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +143 -14
  191. package/src/chain/validation/attestation.ts +1 -7
  192. package/src/chain/validation/attesterSlashing.ts +1 -8
  193. package/src/chain/validation/executionPayloadBid.ts +1 -2
  194. package/src/chain/validation/executionPayloadEnvelope.ts +0 -1
  195. package/src/chain/validation/payloadAttestationMessage.ts +6 -17
  196. package/src/chain/validation/proposerPreferences.ts +3 -3
  197. package/src/chain/validation/proposerSlashing.ts +1 -1
  198. package/src/metrics/metrics/lodestar.ts +97 -54
  199. package/src/network/gossip/topic.ts +38 -0
  200. package/src/network/network.ts +27 -55
  201. package/src/network/processor/gossipHandlers.ts +5 -1
  202. package/src/network/util.ts +0 -5
  203. package/src/node/nodejs.ts +2 -1
  204. package/src/sync/range/batch.ts +83 -28
  205. package/src/sync/range/chain.ts +26 -11
  206. package/src/sync/unknownBlock.ts +8 -15
  207. package/src/sync/utils/downloadByRange.ts +21 -12
  208. package/src/sync/utils/downloadByRoot.ts +3 -1
  209. package/lib/chain/bls/maybeBatch.d.ts +0 -12
  210. package/lib/chain/bls/maybeBatch.d.ts.map +0 -1
  211. package/lib/chain/bls/maybeBatch.js +0 -35
  212. package/lib/chain/bls/maybeBatch.js.map +0 -1
  213. package/src/chain/bls/maybeBatch.ts +0 -45
@@ -414,3 +414,41 @@ export const gossipTopicIgnoreDuplicatePublishError: Record<GossipType, boolean>
414
414
  [GossipType.execution_payload_bid]: true,
415
415
  [GossipType.proposer_preferences]: true,
416
416
  };
417
+
418
+ /**
419
+ * Whether a publish that reached zero subscribed peers is an acceptable outcome for a topic.
420
+ *
421
+ * Publishing to a topic with no subscribed peers throws `PublishError.NoPeersSubscribedToTopic`, which
422
+ * propagates to the caller and, for messages submitted through the beacon API, is surfaced to the
423
+ * validator client. That is the right default: a message that reached nobody is a failed broadcast, and a
424
+ * VC configured with several beacon nodes can fall back to another one.
425
+ *
426
+ * Only topics that have a concrete reason to tolerate it opt out here, ie. are set to `true`, and the
427
+ * reason is named on the entry. Everything else is `false` and keeps raising. Note this is applied per
428
+ * publish, a topic set to `false` still honors the global `--network.allowPublishToZeroPeers` flag.
429
+ */
430
+ export const gossipTopicAllowPublishToZeroPeers: Record<GossipType, boolean> = {
431
+ [GossipType.beacon_block]: false,
432
+ [GossipType.blob_sidecar]: false,
433
+ // data_column_sidecar: we ensure having all topic peers via prioritizePeers(). Even with 0 peers on the
434
+ // topic the overall publish can still succeed, because a supernode rebuilds and publishes the missing
435
+ // columns for us, so track sent peers as 0 instead of raising
436
+ [GossipType.data_column_sidecar]: true,
437
+ [GossipType.beacon_aggregate_and_proof]: false,
438
+ [GossipType.beacon_attestation]: false,
439
+ [GossipType.voluntary_exit]: false,
440
+ [GossipType.proposer_slashing]: false,
441
+ [GossipType.attester_slashing]: false,
442
+ [GossipType.sync_committee_contribution_and_proof]: false,
443
+ [GossipType.sync_committee]: false,
444
+ // light_client_finality_update and light_client_optimistic_update: non-mandatory route on most of the
445
+ // network as of Oct 2022, there may be no peer subscribed to the topic at all. Reason carried over from
446
+ // the handlers in network.ts, which used to swallow the error instead
447
+ [GossipType.light_client_finality_update]: true,
448
+ [GossipType.light_client_optimistic_update]: true,
449
+ [GossipType.bls_to_execution_change]: false,
450
+ [GossipType.execution_payload]: false,
451
+ [GossipType.payload_attestation_message]: false,
452
+ [GossipType.execution_payload_bid]: false,
453
+ [GossipType.proposer_preferences]: false,
454
+ };
@@ -49,7 +49,12 @@ import {INetworkCore, NetworkCore, WorkerNetworkCore} from "./core/index.js";
49
49
  import {INetworkEventBus, NetworkEvent, NetworkEventBus, NetworkEventData} from "./events.js";
50
50
  import {getActiveForkBoundaries} from "./forks.js";
51
51
  import {GossipHandlers, GossipTopicMap, GossipType, GossipTypeMap} from "./gossip/index.js";
52
- import {getGossipSSZType, gossipTopicIgnoreDuplicatePublishError, stringifyGossipTopic} from "./gossip/topic.js";
52
+ import {
53
+ getGossipSSZType,
54
+ gossipTopicAllowPublishToZeroPeers,
55
+ gossipTopicIgnoreDuplicatePublishError,
56
+ stringifyGossipTopic,
57
+ } from "./gossip/topic.js";
53
58
  import {INetwork} from "./interface.js";
54
59
  import {NetworkOptions} from "./options.js";
55
60
  import {PeerAction, PeerScoreStats} from "./peers/index.js";
@@ -65,7 +70,7 @@ import {
65
70
  } from "./reqresp/utils/collect.js";
66
71
  import {collectSequentialBlocksInRange} from "./reqresp/utils/collectSequentialBlocksInRange.js";
67
72
  import {CommitteeSubscription} from "./subnets/index.js";
68
- import {isPublishDuplicateError, isPublishToZeroPeersError, prettyPrintPeerIdStr} from "./util.js";
73
+ import {isPublishDuplicateError, prettyPrintPeerIdStr} from "./util.js";
69
74
 
70
75
  type NetworkModules = {
71
76
  opts: NetworkOptions;
@@ -352,9 +357,7 @@ export class Network implements INetwork {
352
357
  const epoch = computeEpochAtSlot(signedBlock.message.slot);
353
358
  const boundary = this.config.getForkBoundaryAtEpoch(epoch);
354
359
 
355
- return this.publishGossip<GossipType.beacon_block>({type: GossipType.beacon_block, boundary}, signedBlock, {
356
- ignoreDuplicatePublishError: true,
357
- });
360
+ return this.publishGossip<GossipType.beacon_block>({type: GossipType.beacon_block, boundary}, signedBlock);
358
361
  }
359
362
 
360
363
  async publishBlobSidecar(blobSidecar: deneb.BlobSidecar): Promise<number> {
@@ -363,9 +366,7 @@ export class Network implements INetwork {
363
366
 
364
367
  const subnet = blobSidecar.index;
365
368
 
366
- return this.publishGossip<GossipType.blob_sidecar>({type: GossipType.blob_sidecar, boundary, subnet}, blobSidecar, {
367
- ignoreDuplicatePublishError: true,
368
- });
369
+ return this.publishGossip<GossipType.blob_sidecar>({type: GossipType.blob_sidecar, boundary, subnet}, blobSidecar);
369
370
  }
370
371
 
371
372
  async publishDataColumnSidecar(
@@ -381,14 +382,7 @@ export class Network implements INetwork {
381
382
  try {
382
383
  const sentPeers = await this.publishGossip<GossipType.data_column_sidecar>(
383
384
  {type: GossipType.data_column_sidecar, boundary, subnet},
384
- dataColumnSidecar,
385
- {
386
- // we ensure having all topic peers via prioritizePeers() function
387
- // in the worse case, if there is 0 peer on the topic, the overall publish operation could be still a success
388
- // because supernode will rebuild and publish missing data column sidecars for us
389
- // hence we want to track sent peers as 0 instead of an error
390
- allowPublishToZeroTopicPeers: true,
391
- }
385
+ dataColumnSidecar
392
386
  );
393
387
  return {sentPeers, alreadyPublished: false};
394
388
  } catch (e) {
@@ -405,8 +399,7 @@ export class Network implements INetwork {
405
399
 
406
400
  return this.publishGossip<GossipType.beacon_aggregate_and_proof>(
407
401
  {type: GossipType.beacon_aggregate_and_proof, boundary},
408
- aggregateAndProof,
409
- {ignoreDuplicatePublishError: true}
402
+ aggregateAndProof
410
403
  );
411
404
  }
412
405
 
@@ -416,8 +409,7 @@ export class Network implements INetwork {
416
409
 
417
410
  return this.publishGossip<GossipType.beacon_attestation>(
418
411
  {type: GossipType.beacon_attestation, boundary, subnet},
419
- attestation,
420
- {ignoreDuplicatePublishError: true}
412
+ attestation
421
413
  );
422
414
  }
423
415
 
@@ -425,9 +417,7 @@ export class Network implements INetwork {
425
417
  const epoch = voluntaryExit.message.epoch;
426
418
  const boundary = this.config.getForkBoundaryAtEpoch(epoch);
427
419
 
428
- return this.publishGossip<GossipType.voluntary_exit>({type: GossipType.voluntary_exit, boundary}, voluntaryExit, {
429
- ignoreDuplicatePublishError: true,
430
- });
420
+ return this.publishGossip<GossipType.voluntary_exit>({type: GossipType.voluntary_exit, boundary}, voluntaryExit);
431
421
  }
432
422
 
433
423
  async publishBlsToExecutionChange(blsToExecutionChange: capella.SignedBLSToExecutionChange): Promise<number> {
@@ -438,8 +428,7 @@ export class Network implements INetwork {
438
428
  if (fork >= ForkSeq.capella) {
439
429
  const publishPromise = this.publishGossip<GossipType.bls_to_execution_change>(
440
430
  {type: GossipType.bls_to_execution_change, boundary},
441
- blsToExecutionChange,
442
- {ignoreDuplicatePublishError: true}
431
+ blsToExecutionChange
443
432
  );
444
433
  publishChanges.push(publishPromise);
445
434
  }
@@ -457,8 +446,7 @@ export class Network implements INetwork {
457
446
 
458
447
  return this.publishGossip<GossipType.proposer_slashing>(
459
448
  {type: GossipType.proposer_slashing, boundary},
460
- proposerSlashing,
461
- {ignoreDuplicatePublishError: true}
449
+ proposerSlashing
462
450
  );
463
451
  }
464
452
 
@@ -478,10 +466,7 @@ export class Network implements INetwork {
478
466
 
479
467
  return this.publishGossip<GossipType.sync_committee>(
480
468
  {type: GossipType.sync_committee, boundary, subnet},
481
- signature,
482
- {
483
- ignoreDuplicatePublishError: true,
484
- }
469
+ signature
485
470
  );
486
471
  }
487
472
 
@@ -491,8 +476,7 @@ export class Network implements INetwork {
491
476
 
492
477
  return this.publishGossip<GossipType.sync_committee_contribution_and_proof>(
493
478
  {type: GossipType.sync_committee_contribution_and_proof, boundary},
494
- contributionAndProof,
495
- {ignoreDuplicatePublishError: true}
479
+ contributionAndProof
496
480
  );
497
481
  }
498
482
 
@@ -522,8 +506,7 @@ export class Network implements INetwork {
522
506
 
523
507
  return this.publishGossip<GossipType.execution_payload>(
524
508
  {type: GossipType.execution_payload, boundary},
525
- signedEnvelope,
526
- {ignoreDuplicatePublishError: true}
509
+ signedEnvelope
527
510
  );
528
511
  }
529
512
 
@@ -533,8 +516,7 @@ export class Network implements INetwork {
533
516
 
534
517
  return this.publishGossip<GossipType.execution_payload_bid>(
535
518
  {type: GossipType.execution_payload_bid, boundary},
536
- signedBid,
537
- {ignoreDuplicatePublishError: true}
519
+ signedBid
538
520
  );
539
521
  }
540
522
 
@@ -544,8 +526,7 @@ export class Network implements INetwork {
544
526
 
545
527
  return this.publishGossip<GossipType.payload_attestation_message>(
546
528
  {type: GossipType.payload_attestation_message, boundary},
547
- payloadAttestationMessage,
548
- {ignoreDuplicatePublishError: true}
529
+ payloadAttestationMessage
549
530
  );
550
531
  }
551
532
 
@@ -555,22 +536,21 @@ export class Network implements INetwork {
555
536
 
556
537
  return this.publishGossip<GossipType.proposer_preferences>(
557
538
  {type: GossipType.proposer_preferences, boundary},
558
- signedProposerPreferences,
559
- {ignoreDuplicatePublishError: true}
539
+ signedProposerPreferences
560
540
  );
561
541
  }
562
542
 
563
543
  private async publishGossip<K extends GossipType>(
564
544
  topic: GossipTopicMap[K],
565
- object: GossipTypeMap[K],
566
- opts?: PublishOpts | undefined
545
+ object: GossipTypeMap[K]
567
546
  ): Promise<number> {
568
547
  const topicStr = stringifyGossipTopic(this.config, topic);
569
548
  const sszType = getGossipSSZType(topic);
570
549
  const messageData = (sszType.serialize as (object: GossipTypeMap[GossipType]) => Uint8Array)(object);
571
- opts = {
572
- ...opts,
550
+ const opts: PublishOpts = {
573
551
  ignoreDuplicatePublishError: gossipTopicIgnoreDuplicatePublishError[topic.type],
552
+ // Leave undefined unless the topic opts out, so `--network.allowPublishToZeroPeers` still applies
553
+ allowPublishToZeroTopicPeers: gossipTopicAllowPublishToZeroPeers[topic.type] ? true : undefined,
574
554
  };
575
555
  const sentPeers = await this.core.publishGossip(topicStr, messageData, opts);
576
556
 
@@ -813,11 +793,7 @@ export class Network implements INetwork {
813
793
  await this.waitForSyncMessageCutoff(finalityUpdate.signatureSlot);
814
794
  await this.publishLightClientFinalityUpdate(finalityUpdate);
815
795
  } catch (e) {
816
- // Non-mandatory route on most of network as of Oct 2022. May not have found any peers on topic yet
817
- // Remove once https://github.com/ChainSafe/js-libp2p-gossipsub/issues/367
818
- if (!isPublishToZeroPeersError(e as Error)) {
819
- this.logger.debug("Error on BeaconGossipHandler.onLightclientFinalityUpdate", {}, e as Error);
820
- }
796
+ this.logger.debug("Error on BeaconGossipHandler.onLightclientFinalityUpdate", {}, e as Error);
821
797
  }
822
798
  };
823
799
 
@@ -830,11 +806,7 @@ export class Network implements INetwork {
830
806
  await this.waitForSyncMessageCutoff(optimisticUpdate.signatureSlot);
831
807
  await this.publishLightClientOptimisticUpdate(optimisticUpdate);
832
808
  } catch (e) {
833
- // Non-mandatory route on most of network as of Oct 2022. May not have found any peers on topic yet
834
- // Remove once https://github.com/ChainSafe/js-libp2p-gossipsub/issues/367
835
- if (!isPublishToZeroPeersError(e as Error)) {
836
- this.logger.debug("Error on BeaconGossipHandler.onLightclientOptimisticUpdate", {}, e as Error);
837
- }
809
+ this.logger.debug("Error on BeaconGossipHandler.onLightclientOptimisticUpdate", {}, e as Error);
838
810
  }
839
811
  };
840
812
 
@@ -196,7 +196,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
196
196
  forkName: fork,
197
197
  sampledColumns: chain.custodyConfig.sampledColumns,
198
198
  custodyColumns: chain.custodyConfig.custodyColumns,
199
- timeCreatedSec: seenTimestampSec,
199
+ seenTimestampSec,
200
+ source: PayloadEnvelopeInputSource.gossip,
200
201
  });
201
202
  }
202
203
 
@@ -640,6 +641,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
640
641
  metrics?.gossipBlock.elapsedTimeTillProcessed.observe(delaySec);
641
642
 
642
643
  if (isForkPostGloas(blockInput.forkName)) {
644
+ // we should have the payloadInput in the seen cache so no need getOrReload() here
643
645
  const payloadInput = chain.seenPayloadEnvelopeInputCache.get(blockInput.blockRootHex);
644
646
  // This payloadInput should have been created just after gossip validation
645
647
  if (!payloadInput) {
@@ -1189,6 +1191,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1189
1191
  chain.validatorMonitor?.registerExecutionPayloadEnvelope(OpSource.gossip, delaySec, signedEnvelope);
1190
1192
 
1191
1193
  const blockRootHex = toRootHex(envelope.beaconBlockRoot);
1194
+ // a gossip payload cannot be `MAX_PAYLOAD_ENVELOPE_INPUT_CACHE_SIZE` slots after the block
1195
+ // otherwise it'll get to UnknownBlockInput flow
1192
1196
  const payloadInput = chain.seenPayloadEnvelopeInputCache.get(blockRootHex);
1193
1197
 
1194
1198
  if (!payloadInput) {
@@ -23,11 +23,6 @@ export function getConnection(libp2p: Libp2p, peerIdStr: string): Connection | u
23
23
  return getConnectionsMap(libp2p).get(peerIdStr)?.value[0] ?? undefined;
24
24
  }
25
25
 
26
- // https://github.com/libp2p/js-libp2p/blob/f87cba928991736d9646b3e054c367f55cab315c/packages/gossipsub/src/gossipsub.ts#L2076
27
- export function isPublishToZeroPeersError(e: Error): boolean {
28
- return e.message.includes("PublishError.NoPeersSubscribedToTopic");
29
- }
30
-
31
26
  // https://github.com/libp2p/js-libp2p/blob/f87cba928991736d9646b3e054c367f55cab315c/packages/gossipsub/src/gossipsub.ts#L2076
32
27
  export function isPublishDuplicateError(e: Error): boolean {
33
28
  return e.message.includes("PublishError.Duplicate");
@@ -1,12 +1,13 @@
1
1
  import {setMaxListeners} from "node:events";
2
2
  import {PrivateKey} from "@libp2p/interface";
3
3
  import {Registry} from "prom-client";
4
+ import {type PubkeyCache} from "@chainsafe/lodestar-z/pubkeys";
4
5
  import {hasher} from "@chainsafe/persistent-merkle-tree";
5
6
  import {BeaconApiMethods} from "@lodestar/api/beacon/server";
6
7
  import {BeaconConfig} from "@lodestar/config";
7
8
  import type {LoggerNode} from "@lodestar/logger/node";
8
9
  import {ZERO_HASH_HEX} from "@lodestar/params";
9
- import {IBeaconStateView, PubkeyCache, isStatePostBellatrix, isStatePostGloas} from "@lodestar/state-transition";
10
+ import {IBeaconStateView, isStatePostBellatrix, isStatePostGloas} from "@lodestar/state-transition";
10
11
  import {phase0} from "@lodestar/types";
11
12
  import {sleep, toRootHex} from "@lodestar/utils";
12
13
  import {ProcessShutdownCallback} from "@lodestar/validator";
@@ -39,6 +39,7 @@ export enum BatchStatus {
39
39
  AwaitingValidation = "AwaitingValidation",
40
40
  }
41
41
 
42
+ /** Identity of one download attempt*/
42
43
  export type Attempt = {
43
44
  /** The peer that made the attempt */
44
45
  peers: PeerIdStr[];
@@ -46,6 +47,16 @@ export type Attempt = {
46
47
  hash: RootHex;
47
48
  };
48
49
 
50
+ /**
51
+ * A failed attempt that has been classified for blame.
52
+ */
53
+ export type FailedAttempt = Attempt & {
54
+ /**
55
+ * True when the failure is evidence the peers served bad data, so they may be downscored..
56
+ */
57
+ peerAttributable: boolean;
58
+ };
59
+
49
60
  type TrackedRequest = {
50
61
  /** only happen for the 1st batch in checkpoint sync */
51
62
  parentPayload: boolean;
@@ -108,10 +119,12 @@ export type BatchMetadata = {
108
119
  // Retry counters
109
120
  downloadAttempts: number;
110
121
  processingAttempts: number;
122
+ executionErrorAttempts: number;
111
123
 
112
124
  // Cumulative peer attribution for failed attempts (only present when non-empty)
113
125
  failedDownloadPeers?: string;
114
126
  failedProcessingPeers?: string;
127
+ executionErrorPeers?: string;
115
128
  };
116
129
 
117
130
  function formatRangeReq(req: {startSlot: Slot; count: number}): string {
@@ -152,10 +165,10 @@ export class Batch {
152
165
  state: BatchState = {status: BatchStatus.AwaitingDownload, blocks: [], payloadEnvelopes: null};
153
166
  /** Peers that provided good data, with column coverage for by_range requests */
154
167
  private readonly successfulDownloads = new Map<PeerIdStr, TrackedRequest>();
155
- /** The `Attempts` that have been made and failed to send us this batch. */
156
- readonly failedProcessingAttempts: Attempt[] = [];
157
- /** The `Attempts` that have been made and failed because of execution malfunction. */
158
- readonly executionErrorAttempts: Attempt[] = [];
168
+ /** The attempts that have been made and failed to send us this batch. */
169
+ readonly failedProcessingAttempts: FailedAttempt[] = [];
170
+ /** The attempts that have been made and failed because of execution malfunction. */
171
+ readonly executionErrorAttempts: FailedAttempt[] = [];
159
172
  /** The number of download retries this batch has undergone due to a failed request. */
160
173
  private readonly failedDownloadAttempts: PeerIdStr[] = [];
161
174
  private readonly config: ChainForkConfig;
@@ -419,10 +432,18 @@ export class Batch {
419
432
  }
420
433
 
421
434
  /**
422
- * Gives a list of peers from which this batch has had a failed download or processing attempt.
435
+ * Gives a list of peers from which this batch has had a failed download attempt, or a
436
+ * peer-attributable failed processing/execution attempt.
423
437
  */
424
438
  getFailedPeers(): PeerIdStr[] {
425
- return [...this.failedDownloadAttempts, ...this.failedProcessingAttempts.flatMap((a) => a.peers)];
439
+ return [...this.failedDownloadAttempts, ...this.getPeerAttributableAttempts().flatMap((a) => a.peers)];
440
+ }
441
+
442
+ /**
443
+ * Attempts whose failure is peer-attributable, across both the processing and execution error
444
+ */
445
+ getPeerAttributableAttempts(): FailedAttempt[] {
446
+ return [...this.failedProcessingAttempts, ...this.executionErrorAttempts].filter((a) => a.peerAttributable);
426
447
  }
427
448
 
428
449
  /**
@@ -457,6 +478,7 @@ export class Batch {
457
478
  getMetadata(): BatchMetadata {
458
479
  const {blocksRequest, blobsRequest, columnsRequest, envelopesRequest} = this.requests;
459
480
  const failedProcessingPeerList = this.failedProcessingAttempts.flatMap((a) => a.peers);
481
+ const executionErrorPeerList = this.executionErrorAttempts.flatMap((a) => a.peers);
460
482
  return {
461
483
  startEpoch: this.startEpoch,
462
484
  startSlot: this.startSlot,
@@ -468,12 +490,16 @@ export class Batch {
468
490
  ...(envelopesRequest && {envelopesReq: formatRangeReq(envelopesRequest)}),
469
491
  downloadAttempts: this.failedDownloadAttempts.length,
470
492
  processingAttempts: this.failedProcessingAttempts.length,
493
+ executionErrorAttempts: this.executionErrorAttempts.length,
471
494
  ...(this.failedDownloadAttempts.length > 0 && {
472
495
  failedDownloadPeers: this.failedDownloadAttempts.join(","),
473
496
  }),
474
497
  ...(failedProcessingPeerList.length > 0 && {
475
498
  failedProcessingPeers: failedProcessingPeerList.join(","),
476
499
  }),
500
+ ...(executionErrorPeerList.length > 0 && {
501
+ executionErrorPeers: executionErrorPeerList.join(","),
502
+ }),
477
503
  };
478
504
  }
479
505
 
@@ -703,11 +729,7 @@ export class Batch {
703
729
  throw new BatchError(this.wrongStatusErrorType(BatchStatus.Processing));
704
730
  }
705
731
 
706
- if (isExecutionEngineError(err)) {
707
- this.onExecutionEngineError(this.state.attempt);
708
- } else {
709
- this.onProcessingError(this.state.attempt);
710
- }
732
+ this.routeProcessingFailure(err, this.state.attempt);
711
733
  }
712
734
 
713
735
  /**
@@ -718,10 +740,17 @@ export class Batch {
718
740
  throw new BatchError(this.wrongStatusErrorType(BatchStatus.AwaitingValidation));
719
741
  }
720
742
 
721
- if (isExecutionEngineError(err)) {
722
- this.onExecutionEngineError(this.state.attempt);
743
+ this.routeProcessingFailure(err, this.state.attempt);
744
+ }
745
+
746
+ private routeProcessingFailure(err: Error, attempt: Attempt): void {
747
+ const code = err instanceof BlockError || err instanceof PayloadError ? err.type.code : null;
748
+ const failedAttempt: FailedAttempt = {...attempt, peerAttributable: isPeerAttributableFailure(code)};
749
+
750
+ if (isExecutionEngineFailure(code)) {
751
+ this.onExecutionEngineError(failedAttempt);
723
752
  } else {
724
- this.onProcessingError(this.state.attempt);
753
+ this.onProcessingError(failedAttempt);
725
754
  }
726
755
  }
727
756
 
@@ -735,7 +764,7 @@ export class Batch {
735
764
  return this.state.attempt;
736
765
  }
737
766
 
738
- private onExecutionEngineError(attempt: Attempt): void {
767
+ private onExecutionEngineError(attempt: FailedAttempt): void {
739
768
  this.executionErrorAttempts.push(attempt);
740
769
  if (this.executionErrorAttempts.length > MAX_BATCH_PROCESSING_ATTEMPTS) {
741
770
  throw new BatchError(this.errorType({code: BatchErrorCode.MAX_EXECUTION_ENGINE_ERROR_ATTEMPTS}));
@@ -746,7 +775,7 @@ export class Batch {
746
775
  this.state = {status: BatchStatus.AwaitingDownload, blocks: [], payloadEnvelopes: null};
747
776
  }
748
777
 
749
- private onProcessingError(attempt: Attempt): void {
778
+ private onProcessingError(attempt: FailedAttempt): void {
750
779
  this.failedProcessingAttempts.push(attempt);
751
780
  if (this.failedProcessingAttempts.length > MAX_BATCH_PROCESSING_ATTEMPTS) {
752
781
  throw new BatchError(this.errorType({code: BatchErrorCode.MAX_PROCESSING_ATTEMPTS}));
@@ -789,18 +818,44 @@ type BatchErrorMetadata = {
789
818
 
790
819
  export class BatchError extends LodestarError<BatchErrorType & BatchErrorMetadata> {}
791
820
 
792
- function isExecutionEngineError(err: Error): boolean {
793
- if (!(err instanceof BlockError)) {
794
- return false;
795
- }
796
-
797
- if (err.type.code === BlockErrorCode.EXECUTION_ENGINE_ERROR) {
798
- return true;
799
- }
800
-
821
+ /**
822
+ * Whether a processing failure is an execution-engine failure. Pre-gloas, all EL errors stay in
823
+ * BlockErrorCode, post-gloas, they stay in PayloadErrorCode.
824
+ */
825
+ function isExecutionEngineFailure(code: BlockErrorCode | PayloadErrorCode | null): boolean {
801
826
  return (
802
- err.type.code === BlockErrorCode.BEACON_CHAIN_ERROR &&
803
- err.type.error instanceof PayloadError &&
804
- err.type.error.type.code === PayloadErrorCode.EXECUTION_ENGINE_ERROR
827
+ code === BlockErrorCode.EXECUTION_ENGINE_INVALID ||
828
+ code === BlockErrorCode.EXECUTION_ENGINE_ERROR ||
829
+ code === PayloadErrorCode.EXECUTION_ENGINE_INVALID ||
830
+ code === PayloadErrorCode.EXECUTION_ENGINE_ERROR
805
831
  );
806
832
  }
833
+
834
+ /**
835
+ * Based on an error code, return if peer is attributable for FailedAttempt.
836
+ */
837
+ function isPeerAttributableFailure(code: BlockErrorCode | PayloadErrorCode | null): boolean {
838
+ switch (code) {
839
+ // EL returned a definitive INVALID verdict on the payload
840
+ case BlockErrorCode.EXECUTION_ENGINE_INVALID:
841
+ case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
842
+ // a block or envelope signature is invalid
843
+ case BlockErrorCode.INVALID_SIGNATURE:
844
+ case PayloadErrorCode.INVALID_SIGNATURE:
845
+ // the block's state transition produced an invalid state root, or failed per_slot/per_block processing
846
+ case BlockErrorCode.INVALID_STATE_ROOT:
847
+ case BlockErrorCode.PER_BLOCK_PROCESSING_ERROR:
848
+ // the segment this peer served is internally inconsistent (in-segment link breaks, see PR #9684)
849
+ case BlockErrorCode.NON_LINEAR_SLOTS:
850
+ case BlockErrorCode.NON_LINEAR_PARENT_ROOTS:
851
+ case BlockErrorCode.NON_LINEAR_PAYLOAD_ROOTS:
852
+ // the envelope references a mismatched block root, or fails field verification
853
+ case BlockErrorCode.ENVELOPE_BLOCK_ROOT_MISMATCH:
854
+ case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
855
+ // the peer served a block we have explicitly blacklisted
856
+ case BlockErrorCode.BLACKLISTED_BLOCK:
857
+ return true;
858
+ default:
859
+ return false;
860
+ }
861
+ }
@@ -358,9 +358,10 @@ export class SyncChain {
358
358
  // If a batch exceeds it's retry limit, maybe downscore peers.
359
359
  // shouldDownscoreOnBatchError() functions enforces that all BatchErrorCode values are covered
360
360
  if (e instanceof BatchError) {
361
- const shouldReportPeer = shouldReportPeerOnBatchError(e.type.code);
361
+ const shouldReportPeer = shouldReportPeerOnBatchError(e.type.code, this.batches.get(e.type.startEpoch));
362
362
  if (shouldReportPeer) {
363
- for (const peer of this.peerset.keys()) {
363
+ // Only the peers actually at fault are reported, never the whole peerset.
364
+ for (const peer of shouldReportPeer.peers) {
364
365
  this.reportPeer(peer, shouldReportPeer.action, shouldReportPeer.reason);
365
366
  }
366
367
  }
@@ -768,8 +769,11 @@ export class SyncChain {
768
769
 
769
770
  // The last batch attempt is right, all others are wrong. Penalize other peers
770
771
  const attemptOk = batch.validationSuccess();
771
- for (const attempt of batch.failedProcessingAttempts) {
772
+ for (const attempt of batch.getPeerAttributableAttempts()) {
772
773
  if (attempt.hash !== attemptOk.hash) {
774
+ // attempt.peers contains block/column/envelope peers.
775
+ // And if there is one bad envelope we will downscore block and column peers to
776
+ // TODO: resolve this
773
777
  for (const badAttemptPeer of attempt.peers) {
774
778
  if (attemptOk.peers.find((goodPeer) => goodPeer === badAttemptPeer)) {
775
779
  // The same peer corrected its previous attempt
@@ -830,21 +834,32 @@ export class SyncChain {
830
834
  * If peer should not be downscored, returns null.
831
835
  */
832
836
  export function shouldReportPeerOnBatchError(
833
- code: BatchErrorCode
834
- ): {action: PeerAction.LowToleranceError; reason: string} | null {
837
+ code: BatchErrorCode,
838
+ batch: Batch | undefined
839
+ ): {action: PeerAction.LowToleranceError; reason: string; peers: PeerIdStr[]} | null {
835
840
  switch (code) {
836
- // A batch could not be processed after max retry limit. It's likely that all peers
837
- // in this chain are sending invalid batches repeatedly so are either malicious or faulty.
838
- // We drop the chain and report all peers.
839
- // There are some edge cases with forks that could cause this situation, but it's unlikely.
841
+ // A batch could not be processed after max retry limit. Report
842
+ // only the peers that served a peer-attributable failed attempt.
840
843
  case BatchErrorCode.MAX_PROCESSING_ATTEMPTS:
841
- return {action: PeerAction.LowToleranceError, reason: "SyncChainMaxProcessingAttempts"};
844
+ case BatchErrorCode.MAX_EXECUTION_ENGINE_ERROR_ATTEMPTS: {
845
+ // penalize both the processing-error and execution-error peers
846
+ const attributablePeers = [
847
+ ...new Set(batch?.getPeerAttributableAttempts().flatMap((attempt) => attempt.peers) ?? []),
848
+ ];
849
+ if (attributablePeers.length === 0) {
850
+ return null;
851
+ }
852
+ const reason =
853
+ code === BatchErrorCode.MAX_PROCESSING_ATTEMPTS
854
+ ? "SyncChainMaxProcessingAttempts"
855
+ : "SyncChainMaxExecutionEngineErrorAttempts";
856
+ return {action: PeerAction.LowToleranceError, reason, peers: attributablePeers};
857
+ }
842
858
 
843
859
  // TODO: Should peers be reported for MAX_DOWNLOAD_ATTEMPTS?
844
860
  case BatchErrorCode.MAX_DOWNLOAD_ATTEMPTS:
845
861
  case BatchErrorCode.INVALID_COUNT:
846
862
  case BatchErrorCode.WRONG_STATUS:
847
- case BatchErrorCode.MAX_EXECUTION_ENGINE_ERROR_ATTEMPTS:
848
863
  return null;
849
864
  }
850
865
  }
@@ -544,15 +544,7 @@ export class BlockInputSync {
544
544
  return {kind: "invalidParentPayload", parentRootHex, parentBlockHashHex};
545
545
  }
546
546
 
547
- const parentPayloadInput = this.chain.seenPayloadEnvelopeInputCache.get(parentRootHex);
548
- if (parentPayloadInput) {
549
- if (parentPayloadInput.getBlockHashHex() === parentBlockHashHex) {
550
- return {kind: "parentPayload", rootHex: parentRootHex, slot: parentBlock.slot};
551
- }
552
-
553
- return {kind: "invalidParentPayload", parentRootHex, parentBlockHashHex};
554
- }
555
-
547
+ // Parent is in fork choice but its payload `parentBlockHashHex` isn't revealed yet
556
548
  return {kind: "parentPayload", rootHex: parentRootHex, slot: parentBlock.slot};
557
549
  }
558
550
 
@@ -894,9 +886,9 @@ export class BlockInputSync {
894
886
  this.pendingBlocks.delete(blockRootHex);
895
887
 
896
888
  if (isForkPostGloas(fork)) {
897
- const payloadInput = this.chain.seenPayloadEnvelopeInputCache.get(blockRootHex);
889
+ const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(blockRootHex);
898
890
  if (!payloadInput) {
899
- this.logger.warn("PayloadEnvelopeInput not seeded during unknown sync processReadyBlock()", logCtx);
891
+ this.logger.debug("PayloadEnvelopeInput unavailable during unknown sync processReadyBlock()", logCtx);
900
892
  } else {
901
893
  // Similar to the gossip path: immediately attempt fetch of data columns from the execution engine.
902
894
  // The bid already carries the kzg commitments, so there is no reason to wait for the payload to arrive.
@@ -969,12 +961,12 @@ export class BlockInputSync {
969
961
  */
970
962
  private async reconcilePayloadEnvelope(pendingPayload: PendingPayloadEnvelope): Promise<void> {
971
963
  const rootHex = getPayloadSyncCacheItemRootHex(pendingPayload);
964
+
972
965
  if (this.chain.forkChoice.hasPayloadHexUnsafe(rootHex)) {
973
966
  this.pendingPayloads.delete(rootHex);
974
967
  return;
975
968
  }
976
969
 
977
- const payloadInput = this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
978
970
  if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
979
971
  // Block not in fork choice yet. payloadInput may be seeded from the block body during download, so a
980
972
  // non-null payloadInput does not imply the block is imported; defer regardless and pull the block first.
@@ -990,8 +982,9 @@ export class BlockInputSync {
990
982
  return;
991
983
  }
992
984
 
985
+ const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
993
986
  if (!payloadInput) {
994
- this.logger.debug("Missing PayloadEnvelopeInput for known block while reconciling payload envelope", {
987
+ this.logger.debug("PayloadEnvelopeInput not yet reloadable for imported block, will retry", {
995
988
  root: rootHex,
996
989
  });
997
990
  return;
@@ -1187,7 +1180,7 @@ export class BlockInputSync {
1187
1180
  let slot = getPayloadSyncCacheItemSlot(cacheItem);
1188
1181
  let payloadInput = isPendingPayloadInput(cacheItem)
1189
1182
  ? cacheItem.payloadInput
1190
- : this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
1183
+ : await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
1191
1184
  let envelope = payloadInput?.hasPayloadEnvelope() ? payloadInput.getPayloadEnvelope() : undefined;
1192
1185
 
1193
1186
  let i = 0;
@@ -1220,7 +1213,7 @@ export class BlockInputSync {
1220
1213
  slot = envelope.message.payload.slotNumber;
1221
1214
  }
1222
1215
 
1223
- payloadInput ??= this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
1216
+ payloadInput ??= await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
1224
1217
  if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
1225
1218
  // Block not in fork choice yet. Validating now would throw BLOCK_ROOT_UNKNOWN, so keep the downloaded
1226
1219
  // envelope and wait for the block body; reconcilePayloadEnvelope validates once the block lands.