@lodestar/beacon-node 1.51.0-dev.a736f94f66 → 1.51.0-dev.ab919f3073

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/lib/api/impl/api.js +1 -1
  2. package/lib/api/impl/api.js.map +1 -1
  3. package/lib/api/impl/beacon/blocks/index.d.ts +2 -1
  4. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  5. package/lib/api/impl/beacon/blocks/index.js +12 -7
  6. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  7. package/lib/api/impl/beacon/index.d.ts +2 -1
  8. package/lib/api/impl/beacon/index.d.ts.map +1 -1
  9. package/lib/api/impl/beacon/index.js +2 -2
  10. package/lib/api/impl/beacon/index.js.map +1 -1
  11. package/lib/api/options.d.ts +2 -0
  12. package/lib/api/options.d.ts.map +1 -1
  13. package/lib/api/options.js +1 -0
  14. package/lib/api/options.js.map +1 -1
  15. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
  16. package/lib/chain/archiveStore/utils/archiveBlocks.js +54 -96
  17. package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
  18. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  19. package/lib/chain/blocks/importBlock.js +6 -5
  20. package/lib/chain/blocks/importBlock.js.map +1 -1
  21. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  22. package/lib/chain/blocks/importExecutionPayload.js +2 -1
  23. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  24. package/lib/chain/chain.js +4 -4
  25. package/lib/chain/chain.js.map +1 -1
  26. package/lib/chain/opPools/inclusionListStore.d.ts.map +1 -1
  27. package/lib/chain/opPools/inclusionListStore.js +2 -2
  28. package/lib/chain/opPools/inclusionListStore.js.map +1 -1
  29. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  30. package/lib/chain/produceBlock/produceBlockBody.js +0 -1
  31. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  32. package/lib/chain/regen/interface.d.ts +0 -1
  33. package/lib/chain/regen/interface.d.ts.map +1 -1
  34. package/lib/chain/regen/interface.js +0 -1
  35. package/lib/chain/regen/interface.js.map +1 -1
  36. package/lib/chain/validation/executionPayloadBid.d.ts +0 -15
  37. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  38. package/lib/chain/validation/executionPayloadBid.js +0 -153
  39. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  40. package/lib/chain/validation/inclusionList.d.ts.map +1 -1
  41. package/lib/chain/validation/inclusionList.js +11 -2
  42. package/lib/chain/validation/inclusionList.js.map +1 -1
  43. package/lib/db/beacon.d.ts.map +1 -1
  44. package/lib/db/beacon.js +2 -0
  45. package/lib/db/beacon.js.map +1 -1
  46. package/lib/execution/engine/blobVersionedHashes.d.ts +1 -1
  47. package/lib/execution/engine/blobVersionedHashes.d.ts.map +1 -1
  48. package/lib/execution/engine/blobVersionedHashes.js.map +1 -1
  49. package/lib/execution/engine/http.d.ts.map +1 -1
  50. package/lib/execution/engine/http.js +0 -1
  51. package/lib/execution/engine/http.js.map +1 -1
  52. package/lib/execution/engine/mock.d.ts.map +1 -1
  53. package/lib/execution/engine/mock.js +6 -4
  54. package/lib/execution/engine/mock.js.map +1 -1
  55. package/lib/execution/engine/payloadIdCache.d.ts +1 -2
  56. package/lib/execution/engine/payloadIdCache.d.ts.map +1 -1
  57. package/lib/execution/engine/payloadIdCache.js +3 -3
  58. package/lib/execution/engine/payloadIdCache.js.map +1 -1
  59. package/lib/execution/engine/sszTypes.d.ts +38 -38
  60. package/lib/execution/engine/types.d.ts +1 -1
  61. package/lib/execution/engine/types.d.ts.map +1 -1
  62. package/lib/execution/engine/types.js +7 -6
  63. package/lib/execution/engine/types.js.map +1 -1
  64. package/lib/index.d.ts +1 -0
  65. package/lib/index.d.ts.map +1 -1
  66. package/lib/index.js +2 -0
  67. package/lib/index.js.map +1 -1
  68. package/lib/metrics/metrics/lodestar.d.ts +3 -0
  69. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  70. package/lib/metrics/metrics/lodestar.js +5 -0
  71. package/lib/metrics/metrics/lodestar.js.map +1 -1
  72. package/lib/network/gossip/topic.d.ts +6 -6
  73. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  74. package/lib/network/processor/gossipHandlers.js +27 -18
  75. package/lib/network/processor/gossipHandlers.js.map +1 -1
  76. package/lib/sync/unknownBlock.d.ts.map +1 -1
  77. package/lib/sync/unknownBlock.js +42 -18
  78. package/lib/sync/unknownBlock.js.map +1 -1
  79. package/package.json +30 -30
  80. package/src/api/impl/api.ts +1 -1
  81. package/src/api/impl/beacon/blocks/index.ts +22 -17
  82. package/src/api/impl/beacon/index.ts +3 -1
  83. package/src/api/options.ts +3 -0
  84. package/src/chain/archiveStore/utils/archiveBlocks.ts +64 -126
  85. package/src/chain/blocks/importBlock.ts +8 -7
  86. package/src/chain/blocks/importExecutionPayload.ts +2 -1
  87. package/src/chain/chain.ts +4 -4
  88. package/src/chain/opPools/inclusionListStore.ts +3 -3
  89. package/src/chain/produceBlock/produceBlockBody.ts +0 -1
  90. package/src/chain/regen/interface.ts +0 -1
  91. package/src/chain/validation/executionPayloadBid.ts +0 -180
  92. package/src/chain/validation/inclusionList.ts +11 -2
  93. package/src/db/beacon.ts +5 -0
  94. package/src/execution/engine/blobVersionedHashes.ts +1 -1
  95. package/src/execution/engine/http.ts +0 -1
  96. package/src/execution/engine/mock.ts +6 -4
  97. package/src/execution/engine/payloadIdCache.ts +6 -7
  98. package/src/execution/engine/types.ts +8 -8
  99. package/src/index.ts +2 -0
  100. package/src/metrics/metrics/lodestar.ts +5 -0
  101. package/src/network/processor/gossipHandlers.ts +35 -20
  102. package/src/sync/unknownBlock.ts +47 -28
@@ -19,10 +19,9 @@ export type WithdrawalV1 = {
19
19
  amount: QUANTITY;
20
20
  };
21
21
 
22
- type FcuAttributes = {headBlockHash: DATA; finalizedBlockHash: DATA} & Omit<
23
- PayloadAttributesRpc,
24
- "withdrawals" | "inclusionListTransactions"
25
- > & {inclusionListTransactions?: bellatrix.Transactions};
22
+ type FcuAttributes = {headBlockHash: DATA} & Omit<PayloadAttributesRpc, "withdrawals" | "inclusionListTransactions"> & {
23
+ inclusionListTransactions?: bellatrix.Transactions;
24
+ };
26
25
 
27
26
  export class PayloadIdCache {
28
27
  private readonly payloadIdByFcuAttributes = new Map<
@@ -30,8 +29,8 @@ export class PayloadIdCache {
30
29
  {payloadId: PayloadId; fullKey: string; inclusionListTransactions?: bellatrix.Transactions}
31
30
  >();
32
31
 
33
- getFullKey({headBlockHash, finalizedBlockHash, timestamp, prevRandao, suggestedFeeRecipient}: FcuAttributes): string {
34
- return `${headBlockHash}-${finalizedBlockHash}-${timestamp}-${prevRandao}-${suggestedFeeRecipient}`;
32
+ getFullKey({headBlockHash, timestamp, prevRandao, suggestedFeeRecipient}: FcuAttributes): string {
33
+ return `${headBlockHash}-${timestamp}-${prevRandao}-${suggestedFeeRecipient}`;
35
34
  }
36
35
  getKey({timestamp}: Pick<FcuAttributes, "timestamp">): string {
37
36
  return timestamp;
@@ -69,5 +68,5 @@ function isSameInclusionList(a?: bellatrix.Transactions, b?: bellatrix.Transacti
69
68
  if (a === undefined || b === undefined) {
70
69
  return a === b;
71
70
  }
72
- return a.length === b.length && a.every((transaction, i) => byteArrayEquals(transaction, b[i]));
71
+ return byteArrayEquals(a.bytes, b.bytes);
73
72
  }
@@ -1,4 +1,4 @@
1
- import {Type} from "@chainsafe/ssz";
1
+ import {SerializedList, Type} from "@chainsafe/ssz";
2
2
  import {
3
3
  BUILDER_DEPOSIT_REQUEST_TYPE,
4
4
  BUILDER_EXIT_REQUEST_TYPE,
@@ -181,7 +181,7 @@ export type ExecutionPayloadBodyRpc = {
181
181
  };
182
182
 
183
183
  export type ExecutionPayloadBody = {
184
- transactions: bellatrix.Transaction[];
184
+ transactions: bellatrix.Transactions;
185
185
  withdrawals: capella.Withdrawals | null;
186
186
  };
187
187
 
@@ -316,7 +316,7 @@ export function serializeExecutionPayload(fork: ForkName, data: ExecutionPayload
316
316
  extraData: bytesToData(data.extraData),
317
317
  baseFeePerGas: numToQuantity(data.baseFeePerGas),
318
318
  blockHash: bytesToData(data.blockHash),
319
- transactions: data.transactions.map((tran) => bytesToData(tran)),
319
+ transactions: Array.from(data.transactions, (tran) => bytesToData(tran)),
320
320
  };
321
321
 
322
322
  // Capella adds withdrawals to the ExecutionPayload
@@ -400,7 +400,7 @@ export function parseExecutionPayload(
400
400
  extraData: dataToBytes(data.extraData, null),
401
401
  baseFeePerGas: quantityToBigint(data.baseFeePerGas),
402
402
  blockHash: dataToBytes(data.blockHash, 32),
403
- transactions: data.transactions.map((tran) => dataToBytes(tran, null)),
403
+ transactions: SerializedList.fromArray(data.transactions.map((tran) => dataToBytes(tran, null))),
404
404
  };
405
405
 
406
406
  if (ForkSeq[fork] >= ForkSeq.capella) {
@@ -474,11 +474,11 @@ export function serializeBeaconBlockRoot(data: Root): DATA {
474
474
  }
475
475
 
476
476
  export function serializeInclusionList(data: bellatrix.Transactions): InclusionListRpc {
477
- return data.map((transaction) => bytesToData(transaction));
477
+ return Array.from(data, (transaction) => bytesToData(transaction));
478
478
  }
479
479
 
480
480
  export function deserializeInclusionList(data: InclusionListRpc): bellatrix.Transactions {
481
- return data.map((transaction) => dataToBytes(transaction, null));
481
+ return SerializedList.fromArray(data.map((transaction) => dataToBytes(transaction, null)));
482
482
  }
483
483
 
484
484
  export function deserializePayloadAttributes(data: PayloadAttributesRpc): PayloadAttributes {
@@ -670,7 +670,7 @@ export function deserializeExecutionRequests(fork: ForkName, serialized: Executi
670
670
  export function deserializeExecutionPayloadBody(data: ExecutionPayloadBodyRpc | null): ExecutionPayloadBody | null {
671
671
  return data
672
672
  ? {
673
- transactions: data.transactions.map((tran) => dataToBytes(tran, null)),
673
+ transactions: SerializedList.fromArray(data.transactions.map((tran) => dataToBytes(tran, null))),
674
674
  withdrawals: data.withdrawals ? data.withdrawals.map(deserializeWithdrawal) : null,
675
675
  }
676
676
  : null;
@@ -693,7 +693,7 @@ export function serializeExecutionPayloadBodyV2(data: ExecutionPayloadBodyV2 | n
693
693
  export function serializeExecutionPayloadBody(data: ExecutionPayloadBody | null): ExecutionPayloadBodyRpc | null {
694
694
  return data
695
695
  ? {
696
- transactions: data.transactions.map((tran) => bytesToData(tran)),
696
+ transactions: Array.from(data.transactions, (tran) => bytesToData(tran)),
697
697
  withdrawals: data.withdrawals ? data.withdrawals.map(serializeWithdrawal) : null,
698
698
  }
699
699
  : null;
package/src/index.ts CHANGED
@@ -6,6 +6,8 @@ export {persistAnchorState} from "./chain/index.js";
6
6
  export {DbCPStateDatastore} from "./chain/stateCache/datastore/db.js";
7
7
  export {FileCPStateDatastore} from "./chain/stateCache/datastore/file.js";
8
8
  export {BeaconDb, type IBeaconDb} from "./db/index.js";
9
+ // Export execution engine to make it usable by builder
10
+ export {initializeExecutionEngine} from "./execution/index.js";
9
11
  // Export metrics utilities to de-duplicate validator metrics
10
12
  export {
11
13
  type HttpMetricsServer,
@@ -1474,6 +1474,11 @@ export function createLodestarMetrics(
1474
1474
  help: "Total number of InsertOutcome as a result of adding an execution payload bid from gossip to the pool",
1475
1475
  labelNames: ["insertOutcome"],
1476
1476
  }),
1477
+ apiInsertOutcome: register.counter<{insertOutcome: InsertOutcome}>({
1478
+ name: "lodestar_oppool_execution_payload_bid_pool_api_insert_outcome_total",
1479
+ help: "Total number of InsertOutcome as a result of adding an execution payload bid from api to the pool",
1480
+ labelNames: ["insertOutcome"],
1481
+ }),
1477
1482
  apiValidationTime: register.histogram({
1478
1483
  name: "lodestar_api_execution_payload_bid_validation_time_seconds",
1479
1484
  help: "Time elapsed for signed execution payload bid validation - api path",
@@ -24,6 +24,7 @@ import {
24
24
  deneb,
25
25
  fulu,
26
26
  gloas,
27
+ isGloasBeaconBlock,
27
28
  isGloasDataColumnSidecar,
28
29
  ssz,
29
30
  sszTypesFor,
@@ -150,6 +151,31 @@ export function getGossipHandlers(modules: ValidatorFnsModules, options: GossipH
150
151
  */
151
152
  function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHandlerOpts): SequentialGossipHandlers {
152
153
  const {chain, config, metrics, logger, core} = modules;
154
+ const incompletePayloadWaits = new WeakSet<PayloadEnvelopeInput>();
155
+
156
+ function scheduleIncompletePayloadEnvelope(payloadInput: PayloadEnvelopeInput, peer: string): void {
157
+ if (payloadInput.isComplete() || incompletePayloadWaits.has(payloadInput)) {
158
+ return;
159
+ }
160
+ incompletePayloadWaits.add(payloadInput);
161
+
162
+ const fork = config.getForkName(payloadInput.slot);
163
+ const cutoffTimeMs = getCutoffTimeMs(chain, payloadInput.slot, config.getPayloadDueMs(fork));
164
+ payloadInput.waitForEnvelopeAndAllData(cutoffTimeMs).catch(() => {
165
+ if (
166
+ payloadInput.isComplete() ||
167
+ chain.seenPayloadEnvelopeInputCache.get(payloadInput.blockRootHex) !== payloadInput
168
+ ) {
169
+ return;
170
+ }
171
+ chain.logger.debug("Payload envelope incomplete at payload deadline", payloadInput.getLogMeta());
172
+ chain.emitter.emit(ChainEvent.incompletePayloadEnvelope, {
173
+ payloadInput,
174
+ peer,
175
+ source: BlockInputSource.gossip,
176
+ });
177
+ });
178
+ }
153
179
 
154
180
  /**
155
181
  * Add a gossip block to the seen caches. The proposer signature of the block MUST be verified.
@@ -764,7 +790,12 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
764
790
  // roots per proposer and slot, which bounds full imports over gossip to one alternate
765
791
  chain.seenBlockProposers.hasBlockRoot(signedBlock.message.slot, e.type.proposerIndex, e.type.root) &&
766
792
  // A sibling with an unknown parent cannot be imported, sync fetches it if its branch becomes relevant
767
- chain.forkChoice.getBlockHexDefaultStatus(toRootHex(signedBlock.message.parentRoot)) !== null
793
+ chain.forkChoice.getBlockHexDefaultStatus(toRootHex(signedBlock.message.parentRoot)) !== null &&
794
+ (!isGloasBeaconBlock(signedBlock.message) ||
795
+ chain.forkChoice.getBlockHexAndBlockHash(
796
+ toRootHex(signedBlock.message.parentRoot),
797
+ toRootHex(signedBlock.message.body.signedExecutionPayloadBid.message.parentBlockHash)
798
+ ) !== null)
768
799
  ) {
769
800
  const blockInput = cacheSignatureVerifiedBlock(
770
801
  signedBlock,
@@ -902,25 +933,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
902
933
  // envelope arrival (gossip or API). An in-flight importExecutionPayload is awaiting
903
934
  // payloadInput.waitForAllData(); addColumn above will resolve it once hasAllData flips.
904
935
 
905
- if (!payloadInput.isComplete()) {
906
- const cutoffTimeMs = getCutoffTimeMs(chain, dataColumnSlot, BLOCK_AVAILABILITY_CUTOFF_MS);
907
- // do not await here to not delay gossip validation
908
- payloadInput.waitForEnvelopeAndAllData(cutoffTimeMs).catch((_e) => {
909
- chain.logger.debug(
910
- "Waited for envelope and data after receiving gossip column. Cut-off reached so emitting incompletePayloadEnvelope",
911
- {
912
- dataColumnIndex: index,
913
- ...payloadInputMeta,
914
- }
915
- );
916
- // TODO GLOAS: UnknownBlockSync to handle this event
917
- chain.emitter.emit(ChainEvent.incompletePayloadEnvelope, {
918
- payloadInput,
919
- peer: peerIdStr,
920
- source: BlockInputSource.gossip,
921
- });
922
- });
923
- }
936
+ scheduleIncompletePayloadEnvelope(payloadInput, peerIdStr);
924
937
  } catch (e) {
925
938
  logger.debug(
926
939
  "Error handling gossip data column",
@@ -1330,6 +1343,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1330
1343
  peerIdStr,
1331
1344
  });
1332
1345
 
1346
+ scheduleIncompletePayloadEnvelope(payloadInput, peerIdStr);
1347
+
1333
1348
  chain.emitter.emit(routes.events.EventType.executionPayloadGossip, {
1334
1349
  slot,
1335
1350
  builderIndex: envelope.builderIndex,
@@ -300,10 +300,19 @@ export class BlockInputSync {
300
300
 
301
301
  private onIncompletePayloadEnvelope = (data: ChainEventData[ChainEvent.incompletePayloadEnvelope]): void => {
302
302
  try {
303
- this.addByPayloadInput(data.payloadInput, data.peer);
304
- this.triggerUnknownBlockSearch();
305
- this.metrics?.blockInputSync.requests.inc({type: PendingBlockType.INCOMPLETE_PAYLOAD_ENVELOPE});
306
- this.metrics?.blockInputSync.payloadSource.inc({source: data.source});
303
+ const isNewRoot = this.addByPayloadInput(data.payloadInput, data.peer);
304
+ const pendingPayload = this.pendingPayloads.get(data.payloadInput.blockRootHex);
305
+ if (
306
+ pendingPayload &&
307
+ pendingPayload.status !== PendingPayloadInputStatus.fetching &&
308
+ pendingPayload.status !== PendingPayloadInputStatus.processing
309
+ ) {
310
+ this.triggerUnknownBlockSearch();
311
+ }
312
+ if (isNewRoot) {
313
+ this.metrics?.blockInputSync.requests.inc({type: PendingBlockType.INCOMPLETE_PAYLOAD_ENVELOPE});
314
+ this.metrics?.blockInputSync.payloadSource.inc({source: data.source});
315
+ }
307
316
  } catch (e) {
308
317
  this.logger.debug("Error handling incompletePayloadEnvelope event", {}, e as Error);
309
318
  }
@@ -599,43 +608,44 @@ export class BlockInputSync {
599
608
  return this.chain.seenPayloadEnvelopeInputCache.get(rootHex)?.slot;
600
609
  }
601
610
 
602
- private addByPayloadInput = (
603
- payloadInput: PayloadEnvelopeInput,
604
- peerIdStr?: PeerIdStr,
605
- envelope?: gloas.SignedExecutionPayloadEnvelope
606
- ): void => {
611
+ private addByPayloadInput = (payloadInput: PayloadEnvelopeInput, peerIdStr?: PeerIdStr): boolean => {
607
612
  if (this.knownBadBlocks.has(payloadInput.blockRootHex)) {
608
613
  this.logger.debug("Ignoring payload input for known bad block root", {
609
614
  slot: payloadInput.slot,
610
615
  root: payloadInput.blockRootHex,
611
616
  peerIdStr,
612
617
  });
613
- return;
618
+ return false;
614
619
  }
615
620
 
616
- const pendingPayload = this.toPendingPayloadInput(
617
- payloadInput,
618
- this.pendingPayloads.get(payloadInput.blockRootHex),
619
- envelope
620
- );
621
+ let pendingPayload = this.pendingPayloads.get(payloadInput.blockRootHex);
622
+ const isNewRoot = pendingPayload === undefined;
623
+ // Envelope-only entries must go through reconciliation to validate their speculative envelope.
624
+ if (
625
+ !pendingPayload ||
626
+ pendingPayload.status === PendingPayloadInputStatus.pending ||
627
+ pendingPayload.status === PendingPayloadInputStatus.downloaded
628
+ ) {
629
+ pendingPayload = this.toPendingPayloadInput(payloadInput, pendingPayload);
630
+ this.pendingPayloads.set(payloadInput.blockRootHex, pendingPayload);
631
+
632
+ this.logger.verbose("Added payloadInput to BlockInputSync.pendingPayloads", {
633
+ slot: payloadInput.slot,
634
+ root: payloadInput.blockRootHex,
635
+ delaySec: this.chain.clock.secFromSlot(payloadInput.slot),
636
+ });
637
+ }
621
638
 
622
639
  if (peerIdStr && pendingPayload.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
623
640
  pendingPayload.peerIdStrings.add(peerIdStr);
624
641
  }
625
642
 
626
- this.pendingPayloads.set(payloadInput.blockRootHex, pendingPayload);
627
-
628
- this.logger.verbose("Added payloadInput to BlockInputSync.pendingPayloads", {
629
- slot: payloadInput.slot,
630
- root: payloadInput.blockRootHex,
631
- delaySec: this.chain.clock.secFromSlot(payloadInput.slot),
632
- });
633
-
634
643
  const prunedItemCount = pruneSetToMax(this.pendingPayloads, this.maxPendingBlocks);
635
644
  if (prunedItemCount > 0) {
636
645
  this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.capacity}, prunedItemCount);
637
646
  this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingPayloads`);
638
647
  }
648
+ return isNewRoot;
639
649
  };
640
650
 
641
651
  private onPeerConnected = (data: NetworkEventData[NetworkEvent.peerConnected]): void => {
@@ -1267,6 +1277,9 @@ export class BlockInputSync {
1267
1277
  }
1268
1278
 
1269
1279
  const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
1280
+ if (this.pendingPayloads.get(rootHex) !== pendingPayload) {
1281
+ return;
1282
+ }
1270
1283
  if (!payloadInput) {
1271
1284
  this.logger.debug("PayloadEnvelopeInput not yet reloadable for imported block, will retry", {
1272
1285
  root: rootHex,
@@ -1278,6 +1291,9 @@ export class BlockInputSync {
1278
1291
  const validationResult = await wrapError(
1279
1292
  validateGossipExecutionPayloadEnvelope(this.chain, pendingPayload.envelope)
1280
1293
  );
1294
+ if (this.pendingPayloads.get(rootHex) !== pendingPayload) {
1295
+ return;
1296
+ }
1281
1297
  if (validationResult.err) {
1282
1298
  this.logger.debug(
1283
1299
  "Pending payload envelope failed validation after block import, refetching by root",
@@ -1345,14 +1361,13 @@ export class BlockInputSync {
1345
1361
  payload.status = PendingPayloadInputStatus.fetching;
1346
1362
 
1347
1363
  const res = await wrapError(this.fetchPayloadInput(payload));
1364
+ if (this.pendingPayloads.get(rootHex) !== payload) {
1365
+ this.logger.verbose("Dropping downloaded payload, entry changed during fetch", logCtx);
1366
+ return;
1367
+ }
1348
1368
  if (!res.err) {
1349
1369
  const pendingPayload = res.result;
1350
1370
  const resultRootHex = getPayloadSyncCacheItemRootHex(pendingPayload);
1351
- // finalization may have deleted this entry while we were awaiting the network, do not resurrect it via the set below
1352
- if (!this.pendingPayloads.has(resultRootHex)) {
1353
- this.logger.verbose("Dropping downloaded payload, entry pruned during fetch", logCtx);
1354
- return;
1355
- }
1356
1371
  this.pendingPayloads.set(resultRootHex, pendingPayload);
1357
1372
  const result = isPendingPayloadEnvelope(pendingPayload)
1358
1373
  ? DownloadResult.WaitingForBlock
@@ -1423,12 +1438,16 @@ export class BlockInputSync {
1423
1438
  this.logger.debug("Processing downloaded payload", logCtx);
1424
1439
 
1425
1440
  const res = await wrapError(this.chain.processExecutionPayload(pendingPayload.payloadInput));
1441
+ // The executionPayload event emitted during import usually removes the entry before this point
1426
1442
  if (!res.err) {
1427
1443
  this.logger.debug("Processed payload from unknown sync", logCtx);
1428
1444
  this.pendingPayloads.delete(rootHex);
1429
1445
  this.triggerUnknownBlockSearch();
1430
1446
  return;
1431
1447
  }
1448
+ if (this.pendingPayloads.get(rootHex) !== pendingPayload) {
1449
+ return;
1450
+ }
1432
1451
 
1433
1452
  if (res.err instanceof PayloadError) {
1434
1453
  switch (res.err.type.code) {