@lodestar/beacon-node 1.51.0-dev.e21949cfcf → 1.51.0-dev.e2bf489dd3

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 (100) hide show
  1. package/lib/chain/GetBlobsTracker.d.ts +2 -2
  2. package/lib/chain/GetBlobsTracker.d.ts.map +1 -1
  3. package/lib/chain/GetBlobsTracker.js.map +1 -1
  4. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  5. package/lib/chain/blocks/importBlock.js +17 -47
  6. package/lib/chain/blocks/importBlock.js.map +1 -1
  7. package/lib/chain/blocks/importExecutionPayload.d.ts +4 -0
  8. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  9. package/lib/chain/blocks/importExecutionPayload.js +24 -3
  10. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  11. package/lib/chain/blocks/index.d.ts.map +1 -1
  12. package/lib/chain/blocks/index.js +2 -2
  13. package/lib/chain/blocks/index.js.map +1 -1
  14. package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts +1 -1
  15. package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts.map +1 -1
  16. package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts.map +1 -1
  17. package/lib/chain/blocks/verifyBlocksSanityChecks.js +4 -0
  18. package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
  19. package/lib/chain/chain.d.ts +7 -1
  20. package/lib/chain/chain.d.ts.map +1 -1
  21. package/lib/chain/chain.js +53 -3
  22. package/lib/chain/chain.js.map +1 -1
  23. package/lib/chain/forkChoice/index.d.ts +11 -1
  24. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  25. package/lib/chain/forkChoice/index.js +11 -0
  26. package/lib/chain/forkChoice/index.js.map +1 -1
  27. package/lib/chain/forkChoiceExecutionEngine.d.ts +53 -0
  28. package/lib/chain/forkChoiceExecutionEngine.d.ts.map +1 -0
  29. package/lib/chain/forkChoiceExecutionEngine.js +121 -0
  30. package/lib/chain/forkChoiceExecutionEngine.js.map +1 -0
  31. package/lib/chain/interface.d.ts +3 -2
  32. package/lib/chain/interface.d.ts.map +1 -1
  33. package/lib/chain/interface.js.map +1 -1
  34. package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -2
  35. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  36. package/lib/chain/produceBlock/produceBlockBody.js +1 -1
  37. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  38. package/lib/chain/regen/interface.d.ts +1 -1
  39. package/lib/chain/regen/interface.d.ts.map +1 -1
  40. package/lib/chain/regen/queued.d.ts +1 -1
  41. package/lib/chain/regen/queued.d.ts.map +1 -1
  42. package/lib/chain/regen/queued.js +5 -3
  43. package/lib/chain/regen/queued.js.map +1 -1
  44. package/lib/execution/engine/http.d.ts +2 -2
  45. package/lib/execution/engine/http.d.ts.map +1 -1
  46. package/lib/execution/engine/http.js +5 -4
  47. package/lib/execution/engine/http.js.map +1 -1
  48. package/lib/execution/engine/interface.d.ts +16 -2
  49. package/lib/execution/engine/interface.d.ts.map +1 -1
  50. package/lib/execution/engine/jsonRpcTransport.d.ts +2 -2
  51. package/lib/execution/engine/jsonRpcTransport.d.ts.map +1 -1
  52. package/lib/execution/engine/jsonRpcTransport.js +1 -1
  53. package/lib/execution/engine/jsonRpcTransport.js.map +1 -1
  54. package/lib/execution/engine/restTransport.d.ts +2 -2
  55. package/lib/execution/engine/restTransport.d.ts.map +1 -1
  56. package/lib/execution/engine/restTransport.js.map +1 -1
  57. package/lib/execution/engine/transport.d.ts +1 -12
  58. package/lib/execution/engine/transport.d.ts.map +1 -1
  59. package/lib/index.d.ts +1 -1
  60. package/lib/index.d.ts.map +1 -1
  61. package/lib/index.js +1 -1
  62. package/lib/index.js.map +1 -1
  63. package/lib/network/network.d.ts +2 -1
  64. package/lib/network/network.d.ts.map +1 -1
  65. package/lib/network/network.js +42 -17
  66. package/lib/network/network.js.map +1 -1
  67. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  68. package/lib/network/processor/gossipHandlers.js +3 -0
  69. package/lib/network/processor/gossipHandlers.js.map +1 -1
  70. package/lib/network/processor/index.d.ts +6 -0
  71. package/lib/network/processor/index.d.ts.map +1 -1
  72. package/lib/network/processor/index.js +29 -3
  73. package/lib/network/processor/index.js.map +1 -1
  74. package/lib/util/execution.d.ts +4 -4
  75. package/lib/util/execution.d.ts.map +1 -1
  76. package/lib/util/execution.js.map +1 -1
  77. package/package.json +16 -16
  78. package/src/chain/GetBlobsTracker.ts +2 -2
  79. package/src/chain/blocks/importBlock.ts +35 -67
  80. package/src/chain/blocks/importExecutionPayload.ts +42 -13
  81. package/src/chain/blocks/index.ts +5 -2
  82. package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +1 -1
  83. package/src/chain/blocks/verifyBlocksSanityChecks.ts +4 -1
  84. package/src/chain/chain.ts +62 -3
  85. package/src/chain/forkChoice/index.ts +11 -0
  86. package/src/chain/forkChoiceExecutionEngine.ts +197 -0
  87. package/src/chain/interface.ts +3 -2
  88. package/src/chain/produceBlock/produceBlockBody.ts +4 -2
  89. package/src/chain/regen/interface.ts +1 -1
  90. package/src/chain/regen/queued.ts +6 -4
  91. package/src/execution/engine/http.ts +12 -13
  92. package/src/execution/engine/interface.ts +18 -2
  93. package/src/execution/engine/jsonRpcTransport.ts +10 -2
  94. package/src/execution/engine/restTransport.ts +3 -1
  95. package/src/execution/engine/transport.ts +8 -14
  96. package/src/index.ts +1 -1
  97. package/src/network/network.ts +40 -21
  98. package/src/network/processor/gossipHandlers.ts +3 -0
  99. package/src/network/processor/index.ts +25 -3
  100. package/src/util/execution.ts +6 -6
@@ -30,7 +30,7 @@ import {
30
30
  isGloasDataColumnSidecar,
31
31
  phase0,
32
32
  } from "@lodestar/types";
33
- import {prettyPrintIndices, sleep} from "@lodestar/utils";
33
+ import {defer, prettyPrintIndices, sleep} from "@lodestar/utils";
34
34
  import {BlockInputSource} from "../chain/blocks/blockInput/types.js";
35
35
  import {ChainEvent, IBeaconChain} from "../chain/index.js";
36
36
  import {computeSubnetForDataColumnSidecar} from "../chain/validation/dataColumnSidecar.js";
@@ -128,6 +128,7 @@ export class Network implements INetwork {
128
128
 
129
129
  private subscribedToCoreTopics = false;
130
130
  private connectedPeersSyncMeta = new Map<PeerIdStr, Omit<PeerSyncMeta, "peerId">>();
131
+ private closePromise: Promise<void> | undefined;
131
132
 
132
133
  constructor(modules: NetworkModules) {
133
134
  this.peerId = peerIdFromPrivateKey(modules.privateKey);
@@ -145,12 +146,8 @@ export class Network implements INetwork {
145
146
  this.events.on(NetworkEvent.peerConnected, this.onPeerConnected);
146
147
  this.events.on(NetworkEvent.peerDisconnected, this.onPeerDisconnected);
147
148
  this.chain.emitter.on(routes.events.EventType.head, this.onHead);
148
- this.chain.emitter.on(routes.events.EventType.lightClientFinalityUpdate, ({data}) =>
149
- this.onLightClientFinalityUpdate(data)
150
- );
151
- this.chain.emitter.on(routes.events.EventType.lightClientOptimisticUpdate, ({data}) =>
152
- this.onLightClientOptimisticUpdate(data)
153
- );
149
+ this.chain.emitter.on(routes.events.EventType.lightClientFinalityUpdate, this.onLightClientFinalityUpdate);
150
+ this.chain.emitter.on(routes.events.EventType.lightClientOptimisticUpdate, this.onLightClientOptimisticUpdate);
154
151
  this.chain.emitter.on(ChainEvent.updateTargetCustodyGroupCount, this.onTargetGroupCountUpdated);
155
152
  this.chain.emitter.on(ChainEvent.publishDataColumns, this.onPublishDataColumns);
156
153
  this.chain.emitter.on(ChainEvent.publishBlobSidecars, this.onPublishBlobSidecars);
@@ -240,9 +237,16 @@ export class Network implements INetwork {
240
237
  return this.controller.signal.aborted;
241
238
  }
242
239
 
243
- /** Destroy this instance. Can only be called once. */
244
- async close(): Promise<void> {
245
- if (this.closed) return;
240
+ close(): Promise<void> {
241
+ if (this.closePromise !== undefined) return this.closePromise;
242
+ const completion = defer<void>();
243
+ this.closePromise = completion.promise;
244
+ void this.closeInternal().then(completion.resolve, completion.reject);
245
+ return this.closePromise;
246
+ }
247
+
248
+ private async closeInternal(): Promise<void> {
249
+ this.controller.abort();
246
250
 
247
251
  this.events.off(NetworkEvent.peerConnected, this.onPeerConnected);
248
252
  this.events.off(NetworkEvent.peerDisconnected, this.onPeerDisconnected);
@@ -254,10 +258,12 @@ export class Network implements INetwork {
254
258
  this.chain.emitter.off(ChainEvent.publishBlobSidecars, this.onPublishBlobSidecars);
255
259
  this.chain.emitter.off(ChainEvent.publishProposerSlashing, this.onPublishProposerSlashing);
256
260
  this.chain.emitter.off(ChainEvent.updateStatus, this.onUpdateStatus);
257
- await this.core.close();
258
-
259
- // Used only for sleep() statements
260
- this.controller.abort();
261
+ this.connectedPeersSyncMeta.clear();
262
+ this.subscribedToCoreTopics = false;
263
+ const results = await Promise.allSettled([this.networkProcessor.stop(), this.core.close()]);
264
+ for (const result of results) {
265
+ if (result.status === "rejected") throw result.reason;
266
+ }
261
267
  this.logger.debug("network core closed");
262
268
  }
263
269
 
@@ -333,6 +339,7 @@ export class Network implements INetwork {
333
339
  async subscribeGossipCoreTopics(): Promise<void> {
334
340
  if (!this.subscribedToCoreTopics) {
335
341
  await this.core.subscribeGossipCoreTopics();
342
+ if (this.closed) return;
336
343
  // Only mark subscribedToCoreTopics if worker resolved this call
337
344
  this.subscribedToCoreTopics = true;
338
345
  }
@@ -803,26 +810,32 @@ export class Network implements INetwork {
803
810
  return this.core.writeDiscv5HeapSnapshot(prefix, dirpath);
804
811
  }
805
812
 
806
- private onLightClientFinalityUpdate = async (finalityUpdate: LightClientFinalityUpdate): Promise<void> => {
813
+ private onLightClientFinalityUpdate = async ({
814
+ data: finalityUpdate,
815
+ }: routes.events.EventData[routes.events.EventType.lightClientFinalityUpdate]): Promise<void> => {
807
816
  // TODO: Review is OK to remove if (this.hasAttachedSyncCommitteeMember())
808
817
 
809
818
  try {
810
819
  // messages SHOULD be broadcast after SYNC_MESSAGE_DUE_BPS of slot has transpired
811
820
  // https://github.com/ethereum/consensus-specs/blob/v1.6.1/specs/altair/light-client/p2p-interface.md#sync-committee
812
821
  await this.waitForSyncMessageCutoff(finalityUpdate.signatureSlot);
822
+ if (this.closed) return;
813
823
  await this.publishLightClientFinalityUpdate(finalityUpdate);
814
824
  } catch (e) {
815
825
  this.logger.debug("Error on BeaconGossipHandler.onLightclientFinalityUpdate", {}, e as Error);
816
826
  }
817
827
  };
818
828
 
819
- private onLightClientOptimisticUpdate = async (optimisticUpdate: LightClientOptimisticUpdate): Promise<void> => {
829
+ private onLightClientOptimisticUpdate = async ({
830
+ data: optimisticUpdate,
831
+ }: routes.events.EventData[routes.events.EventType.lightClientOptimisticUpdate]): Promise<void> => {
820
832
  // TODO: Review is OK to remove if (this.hasAttachedSyncCommitteeMember())
821
833
 
822
834
  try {
823
835
  // messages SHOULD be broadcast after SYNC_MESSAGE_DUE_BPS of slot has transpired
824
836
  // https://github.com/ethereum/consensus-specs/blob/v1.6.1/specs/altair/light-client/p2p-interface.md#sync-committee
825
837
  await this.waitForSyncMessageCutoff(optimisticUpdate.signatureSlot);
838
+ if (this.closed) return;
826
839
  await this.publishLightClientOptimisticUpdate(optimisticUpdate);
827
840
  } catch (e) {
828
841
  this.logger.debug("Error on BeaconGossipHandler.onLightclientOptimisticUpdate", {}, e as Error);
@@ -859,10 +872,12 @@ export class Network implements INetwork {
859
872
  this.connectedPeersSyncMeta.delete(data.peer);
860
873
  };
861
874
 
862
- private onTargetGroupCountUpdated = (count: number): void => {
863
- this.core.setTargetGroupCount(count).catch((e) => {
864
- this.logger.debug("Error setting target custody group count", {count}, e as Error);
865
- });
875
+ private onTargetGroupCountUpdated = async (count: number): Promise<void> => {
876
+ try {
877
+ await this.core.setTargetGroupCount(count);
878
+ } catch (error) {
879
+ this.logger.error("Error updating network custody group count", {count}, error as Error);
880
+ }
866
881
  };
867
882
 
868
883
  private onPublishDataColumns = async (sidecars: DataColumnSidecar[]): Promise<void> => {
@@ -886,6 +901,10 @@ export class Network implements INetwork {
886
901
  };
887
902
 
888
903
  private onUpdateStatus = async (): Promise<void> => {
889
- await this.core.updateStatus(this.chain.getStatus());
904
+ try {
905
+ await this.core.updateStatus(this.chain.getStatus());
906
+ } catch (error) {
907
+ this.logger.error("Error updating network status", {}, error as Error);
908
+ }
890
909
  };
891
910
  }
@@ -1377,6 +1377,9 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1377
1377
  // Errors might indicate an issue with our node or the connected EL client
1378
1378
  logLevel = LogLevel.error;
1379
1379
  break;
1380
+ case PayloadErrorCode.BLOCK_EXECUTION_INVALID:
1381
+ logLevel = LogLevel.warn;
1382
+ break;
1380
1383
  // INVALID_SIGNATURE only happens for an envelope added by another source
1381
1384
  case PayloadErrorCode.INVALID_SIGNATURE:
1382
1385
  case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
@@ -244,6 +244,12 @@ type SearchUnknownRootTarget =
244
244
  * Such that enough work is processed to fill either one of the queue.
245
245
  */
246
246
  export class NetworkProcessor {
247
+ private stopped = false;
248
+ /**
249
+ * Replaced by dropAllJobs(). Loops that yield while re-queueing awaiting messages compare it after each yield
250
+ * and stop, so no message is queued after a drop.
251
+ */
252
+ private jobGeneration = Symbol();
247
253
  private readonly chain: IBeaconChain;
248
254
  private readonly core: INetworkCore;
249
255
  private readonly events: NetworkEventBus;
@@ -335,16 +341,27 @@ export class NetworkProcessor {
335
341
  }
336
342
 
337
343
  async stop(): Promise<void> {
344
+ if (this.stopped) return;
345
+ this.stopped = true;
338
346
  this.events.off(NetworkEvent.pendingGossipsubMessage, this.onPendingGossipsubMessage);
339
347
  this.chain.emitter.off(routes.events.EventType.block, this.onBlockProcessed);
340
348
  this.chain.emitter.off(routes.events.EventType.executionPayload, this.onPayloadEnvelopeProcessed);
341
- this.chain.emitter.off(ClockEvent.slot, this.onClockSlot);
349
+ this.chain.clock.off(ClockEvent.slot, this.onClockSlot);
350
+ this.dropAllJobs();
342
351
  }
343
352
 
344
353
  dropAllJobs(): void {
354
+ this.jobGeneration = Symbol();
345
355
  for (const topic of executeGossipWorkOrder) {
346
356
  this.gossipQueues[topic].clear();
347
357
  }
358
+ this.awaitingMessagesByBlockRoot.clear();
359
+ this.awaitingBlockMessageCount = 0;
360
+ this.awaitingMessagesByPayloadBlockRoot.clear();
361
+ this.awaitingPayloadMessageCount = 0;
362
+ this.searchedRootsBySlot.clear();
363
+ this.bufferedRootsBySlot.clear();
364
+ this.fatalPeers.clear();
348
365
  }
349
366
 
350
367
  dumpGossipQueue(topic: GossipType): PendingGossipsubMessage[] {
@@ -364,7 +381,7 @@ export class NetworkProcessor {
364
381
  * prefer them as by-root download candidates.
365
382
  */
366
383
  searchUnknownBlock({slot, root}: SlotRootHex, source: BlockInputSource, peer?: PeerIdStr): void {
367
- if (this.chain.seenBlock(root)) {
384
+ if (this.stopped || this.chain.seenBlock(root)) {
368
385
  return;
369
386
  }
370
387
  const entry = this.searchedRootsBySlot.getOrDefault(slot).getOrDefault(root);
@@ -398,7 +415,7 @@ export class NetworkProcessor {
398
415
  peer?: PeerIdStr,
399
416
  slotIsPayloadSlot = false
400
417
  ): void {
401
- if (this.chain.seenPayloadEnvelope(root)) {
418
+ if (this.stopped || this.chain.seenPayloadEnvelope(root)) {
402
419
  return;
403
420
  }
404
421
  const entry = this.searchedRootsBySlot.getOrDefault(slot).getOrDefault(root);
@@ -801,6 +818,7 @@ export class NetworkProcessor {
801
818
  this.awaitingBlockMessageCount -= total;
802
819
  }
803
820
 
821
+ const generation = this.jobGeneration;
804
822
  const nowSec = Date.now() / 1000;
805
823
  let count = 0;
806
824
  // TODO: we can group attestations to process in batches but since we have the SeenAttestationDatas
@@ -819,6 +837,7 @@ export class NetworkProcessor {
819
837
  if (count === MAX_AWAITING_GOSSIP_OBJECTS_PER_TICK) {
820
838
  count = 0;
821
839
  await sleep(AWAITING_GOSSIP_OBJECTS_YIELD_EVERY_MS);
840
+ if (generation !== this.jobGeneration) return;
822
841
  }
823
842
  }
824
843
  }
@@ -843,6 +862,7 @@ export class NetworkProcessor {
843
862
  this.awaitingPayloadMessageCount -= total;
844
863
  }
845
864
 
865
+ const generation = this.jobGeneration;
846
866
  const nowSec = Date.now() / 1000;
847
867
  let count = 0;
848
868
  for (const messages of messagesByTopic.values()) {
@@ -858,6 +878,7 @@ export class NetworkProcessor {
858
878
  if (count === MAX_AWAITING_GOSSIP_OBJECTS_PER_TICK) {
859
879
  count = 0;
860
880
  await sleep(AWAITING_GOSSIP_OBJECTS_YIELD_EVERY_MS);
881
+ if (generation !== this.jobGeneration) return;
861
882
  }
862
883
  }
863
884
  }
@@ -937,6 +958,7 @@ export class NetworkProcessor {
937
958
  };
938
959
 
939
960
  private executeWork(): void {
961
+ if (this.stopped) return;
940
962
  // TODO: Maybe de-bounce by timing the last time executeWork was run
941
963
 
942
964
  this.metrics?.networkProcessor.executeWorkCalls.inc();
@@ -42,7 +42,7 @@ export enum DataColumnEngineResult {
42
42
 
43
43
  export async function getBlobSidecarsFromExecution(
44
44
  config: ChainForkConfig,
45
- executionEngine: IExecutionEngine,
45
+ executionEngine: Pick<IExecutionEngine, "getBlobs">,
46
46
  metrics: Metrics | null,
47
47
  emitter: ChainEventEmitter,
48
48
  blockInput: IBlockInput
@@ -140,7 +140,7 @@ export async function getBlobSidecarsFromExecution(
140
140
  */
141
141
  export async function getDataColumnSidecarsFromExecution(
142
142
  config: ChainForkConfig,
143
- executionEngine: IExecutionEngine,
143
+ executionEngine: Pick<IExecutionEngine, "getBlobs">,
144
144
  emitter: ChainEventEmitter,
145
145
  input: IBlockInput | PayloadEnvelopeInput,
146
146
  metrics: Metrics | null,
@@ -289,7 +289,7 @@ export type RebuildMiss = {slot: Slot; reason: "unavailable" | "mismatch"; error
289
289
  */
290
290
  export async function* reconstructExecutionPayloadEnvelopesByRange(
291
291
  db: IBeaconDb,
292
- executionEngine: IExecutionEngine,
292
+ executionEngine: Pick<IExecutionEngine, "getPayloadBodiesByHashV2">,
293
293
  logger: Logger,
294
294
  metrics: Metrics | null,
295
295
  startSlot: Slot,
@@ -315,7 +315,7 @@ export async function* reconstructExecutionPayloadEnvelopesByRange(
315
315
  * RANGE_UNSERVABLE for it
316
316
  */
317
317
  async function* reconstructBatch(
318
- executionEngine: IExecutionEngine,
318
+ executionEngine: Pick<IExecutionEngine, "getPayloadBodiesByHashV2">,
319
319
  logger: Logger,
320
320
  metrics: Metrics | null,
321
321
  batch: RangeEntry[]
@@ -355,7 +355,7 @@ async function* reconstructBatch(
355
355
  * on its path. Throws ENGINE_UNAVAILABLE only.
356
356
  */
357
357
  export async function reconstructExecutionPayloadEnvelopes(
358
- executionEngine: IExecutionEngine,
358
+ executionEngine: Pick<IExecutionEngine, "getPayloadBodiesByHashV2">,
359
359
  metrics: Metrics | null,
360
360
  headerEnvelopes: gloas.SignedExecutionPayloadHeaderEnvelope[]
361
361
  ): Promise<(gloas.SignedExecutionPayloadEnvelope | RebuildMiss)[]> {
@@ -382,7 +382,7 @@ export function isRebuildMiss(result: gloas.SignedExecutionPayloadEnvelope | Reb
382
382
  * incremented once, in this function.
383
383
  */
384
384
  async function reconstructEnvelopesBatch(
385
- executionEngine: IExecutionEngine,
385
+ executionEngine: Pick<IExecutionEngine, "getPayloadBodiesByHashV2">,
386
386
  metrics: Metrics | null,
387
387
  headerEnvelopes: gloas.SignedExecutionPayloadHeaderEnvelope[]
388
388
  ): Promise<(gloas.SignedExecutionPayloadEnvelope | RebuildMiss)[]> {