@lodestar/beacon-node 1.41.0-dev.95cf2edc4c → 1.41.0-dev.96f78af84b

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 (97) hide show
  1. package/lib/api/impl/node/utils.d.ts +1 -1
  2. package/lib/api/impl/node/utils.d.ts.map +1 -1
  3. package/lib/api/impl/node/utils.js.map +1 -1
  4. package/lib/constants/network.d.ts +2 -1
  5. package/lib/constants/network.d.ts.map +1 -1
  6. package/lib/constants/network.js +1 -0
  7. package/lib/constants/network.js.map +1 -1
  8. package/lib/db/repositories/blockArchive.d.ts.map +1 -1
  9. package/lib/db/repositories/blockArchive.js +1 -2
  10. package/lib/db/repositories/blockArchive.js.map +1 -1
  11. package/lib/execution/engine/http.d.ts +1 -0
  12. package/lib/execution/engine/http.d.ts.map +1 -1
  13. package/lib/execution/engine/http.js +3 -0
  14. package/lib/execution/engine/http.js.map +1 -1
  15. package/lib/metrics/metrics/lodestar.d.ts +3 -0
  16. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  17. package/lib/metrics/metrics/lodestar.js +5 -0
  18. package/lib/metrics/metrics/lodestar.js.map +1 -1
  19. package/lib/network/core/networkCore.d.ts +3 -3
  20. package/lib/network/core/networkCore.d.ts.map +1 -1
  21. package/lib/network/core/networkCore.js.map +1 -1
  22. package/lib/network/core/networkCoreWorkerHandler.d.ts +3 -3
  23. package/lib/network/core/networkCoreWorkerHandler.d.ts.map +1 -1
  24. package/lib/network/core/types.d.ts +2 -2
  25. package/lib/network/core/types.d.ts.map +1 -1
  26. package/lib/network/events.d.ts +2 -1
  27. package/lib/network/events.d.ts.map +1 -1
  28. package/lib/network/events.js.map +1 -1
  29. package/lib/network/gossip/encoding.d.ts +3 -3
  30. package/lib/network/gossip/encoding.d.ts.map +1 -1
  31. package/lib/network/gossip/encoding.js.map +1 -1
  32. package/lib/network/gossip/gossipsub.d.ts +13 -4
  33. package/lib/network/gossip/gossipsub.d.ts.map +1 -1
  34. package/lib/network/gossip/gossipsub.js +47 -20
  35. package/lib/network/gossip/gossipsub.js.map +1 -1
  36. package/lib/network/gossip/interface.d.ts +3 -3
  37. package/lib/network/gossip/interface.d.ts.map +1 -1
  38. package/lib/network/gossip/scoringParameters.d.ts +1 -1
  39. package/lib/network/gossip/scoringParameters.d.ts.map +1 -1
  40. package/lib/network/gossip/scoringParameters.js +1 -1
  41. package/lib/network/gossip/scoringParameters.js.map +1 -1
  42. package/lib/network/interface.d.ts +3 -3
  43. package/lib/network/interface.d.ts.map +1 -1
  44. package/lib/network/libp2p/index.d.ts +1 -1
  45. package/lib/network/libp2p/index.d.ts.map +1 -1
  46. package/lib/network/libp2p/index.js +7 -2
  47. package/lib/network/libp2p/index.js.map +1 -1
  48. package/lib/network/network.d.ts +2 -2
  49. package/lib/network/network.d.ts.map +1 -1
  50. package/lib/network/network.js.map +1 -1
  51. package/lib/network/options.d.ts.map +1 -1
  52. package/lib/network/options.js +3 -0
  53. package/lib/network/options.js.map +1 -1
  54. package/lib/network/peers/datastore.d.ts +7 -5
  55. package/lib/network/peers/datastore.d.ts.map +1 -1
  56. package/lib/network/peers/datastore.js +10 -10
  57. package/lib/network/peers/datastore.js.map +1 -1
  58. package/lib/network/peers/peerManager.d.ts +11 -0
  59. package/lib/network/peers/peerManager.d.ts.map +1 -1
  60. package/lib/network/peers/peerManager.js +148 -53
  61. package/lib/network/peers/peerManager.js.map +1 -1
  62. package/lib/network/peers/utils/prioritizePeers.d.ts +3 -3
  63. package/lib/network/peers/utils/prioritizePeers.d.ts.map +1 -1
  64. package/lib/network/processor/gossipValidatorFn.js +1 -1
  65. package/lib/network/processor/types.d.ts +1 -1
  66. package/lib/network/processor/types.d.ts.map +1 -1
  67. package/lib/network/reqresp/score.d.ts.map +1 -1
  68. package/lib/network/reqresp/score.js +0 -1
  69. package/lib/network/reqresp/score.js.map +1 -1
  70. package/lib/network/util.js +2 -2
  71. package/lib/network/util.js.map +1 -1
  72. package/package.json +38 -41
  73. package/src/api/impl/node/utils.ts +3 -3
  74. package/src/constants/network.ts +1 -0
  75. package/src/db/repositories/blockArchive.ts +1 -2
  76. package/src/execution/engine/http.ts +3 -0
  77. package/src/metrics/metrics/lodestar.ts +5 -0
  78. package/src/network/core/networkCore.ts +3 -3
  79. package/src/network/core/networkCoreWorkerHandler.ts +3 -3
  80. package/src/network/core/types.ts +2 -2
  81. package/src/network/events.ts +2 -1
  82. package/src/network/gossip/encoding.ts +3 -3
  83. package/src/network/gossip/gossipsub.ts +86 -25
  84. package/src/network/gossip/interface.ts +3 -3
  85. package/src/network/gossip/scoringParameters.ts +4 -4
  86. package/src/network/interface.ts +3 -3
  87. package/src/network/libp2p/index.ts +8 -3
  88. package/src/network/network.ts +3 -3
  89. package/src/network/options.ts +3 -0
  90. package/src/network/peers/datastore.ts +13 -10
  91. package/src/network/peers/peerManager.ts +175 -54
  92. package/src/network/peers/utils/prioritizePeers.ts +3 -3
  93. package/src/network/processor/gossipValidatorFn.ts +1 -1
  94. package/src/network/processor/types.ts +1 -1
  95. package/src/network/reqresp/score.ts +0 -1
  96. package/src/network/util.ts +2 -2
  97. package/src/util/workerEvents.ts +1 -1
@@ -8,6 +8,9 @@ type MemoryItem = {
8
8
  data: Uint8Array;
9
9
  };
10
10
 
11
+ // biome-ignore lint/suspicious/noExplicitAny: used below (copied from upstream)
12
+ type AwaitGenerator<T, TReturn = any, TNext = any> = Generator<T, TReturn, TNext> | AsyncGenerator<T, TReturn, TNext>;
13
+
11
14
  /**
12
15
  * Before libp2p 0.35, peerstore stays in memory and periodically write to db after n dirty items
13
16
  * This has a memory issue because all peer data stays in memory and loaded at startup time
@@ -93,7 +96,7 @@ export class Eth2PeerDataStore extends BaseDatastore {
93
96
  * This throws error if not found
94
97
  * see https://github.com/ipfs/js-datastore-level/blob/38f44058dd6be858e757a1c90b8edb31590ec0bc/src/index.js#L102
95
98
  */
96
- async get(key: Key): Promise<Uint8Array> {
99
+ async get(key: Key, options?: AbortOptions): Promise<Uint8Array> {
97
100
  const keyStr = key.toString();
98
101
  const memoryItem = this._memoryDatastore.get(keyStr);
99
102
  if (memoryItem) {
@@ -102,16 +105,16 @@ export class Eth2PeerDataStore extends BaseDatastore {
102
105
  }
103
106
 
104
107
  // this throws error if not found
105
- const dbValue = await this._dbDatastore.get(key);
108
+ const dbValue = await this._dbDatastore.get(key, options);
106
109
  // don't call this._memoryDatastore.set directly
107
110
  // we want to get through prune() logic with fromDb as true
108
111
  await this._put(key, dbValue, true);
109
112
  return dbValue;
110
113
  }
111
114
 
112
- async has(key: Key): Promise<boolean> {
115
+ async has(key: Key, options?: AbortOptions): Promise<boolean> {
113
116
  try {
114
- await this.get(key);
117
+ await this.get(key, options);
115
118
  } catch (err) {
116
119
  // this is the same to how js-datastore-level handles notFound error
117
120
  // https://github.com/ipfs/js-datastore-level/blob/38f44058dd6be858e757a1c90b8edb31590ec0bc/src/index.js#L121
@@ -121,26 +124,26 @@ export class Eth2PeerDataStore extends BaseDatastore {
121
124
  return true;
122
125
  }
123
126
 
124
- async delete(key: Key): Promise<void> {
127
+ async delete(key: Key, options?: AbortOptions): Promise<void> {
125
128
  this._memoryDatastore.delete(key.toString());
126
- await this._dbDatastore.delete(key);
129
+ await this._dbDatastore.delete(key, options);
127
130
  }
128
131
 
129
- async *_all(q: Query): AsyncIterable<Pair> {
132
+ async *_all(q: Query, options?: AbortOptions): AwaitGenerator<Pair> {
130
133
  for (const [key, value] of this._memoryDatastore.entries()) {
131
134
  yield {
132
135
  key: new Key(key),
133
136
  value: value.data,
134
137
  };
135
138
  }
136
- yield* this._dbDatastore.query(q);
139
+ yield* this._dbDatastore.query(q, options);
137
140
  }
138
141
 
139
- async *_allKeys(q: KeyQuery): AsyncIterable<Key> {
142
+ async *_allKeys(q: KeyQuery, options?: AbortOptions): AwaitGenerator<Key> {
140
143
  for (const key of this._memoryDatastore.keys()) {
141
144
  yield new Key(key);
142
145
  }
143
- yield* this._dbDatastore.queryKeys(q);
146
+ yield* this._dbDatastore.queryKeys(q, options);
144
147
  }
145
148
 
146
149
  private async _addDirtyItem(keyStr: string): Promise<void> {
@@ -1,4 +1,4 @@
1
- import {Connection, PeerId, PrivateKey} from "@libp2p/interface";
1
+ import {Connection, type IdentifyResult, PeerId, PrivateKey} from "@libp2p/interface";
2
2
  import {BitArray} from "@chainsafe/ssz";
3
3
  import {BeaconConfig} from "@lodestar/config";
4
4
  import {LoggerNode} from "@lodestar/logger/node";
@@ -9,6 +9,7 @@ import {prettyPrintIndices, toHex, withTimeout} from "@lodestar/utils";
9
9
  import {GOODBYE_KNOWN_CODES, GoodByeReasonCode, Libp2pEvent} from "../../constants/index.js";
10
10
  import {IClock} from "../../util/clock.js";
11
11
  import {computeColumnsForCustodyGroup, getCustodyGroups} from "../../util/dataColumns.js";
12
+ import {callInNextEventLoop} from "../../util/eventLoop.js";
12
13
  import {NetworkCoreMetrics} from "../core/metrics.js";
13
14
  import {LodestarDiscv5Opts} from "../discv5/types.js";
14
15
  import {INetworkEventBus, NetworkEvent, NetworkEventData} from "../events.js";
@@ -161,6 +162,8 @@ export class PeerManager {
161
162
 
162
163
  // A single map of connected peers with all necessary data to handle PINGs, STATUS, and metrics
163
164
  private connectedPeers: Map<PeerIdStr, PeerData>;
165
+ /** Track one in-flight identify call per peer/connection id */
166
+ private readonly identifyInProgress = new Map<PeerIdStr, string>();
164
167
 
165
168
  private opts: PeerManagerOpts;
166
169
  private intervals: NodeJS.Timeout[] = [];
@@ -192,10 +195,18 @@ export class PeerManager {
192
195
 
193
196
  this.libp2p.services.components.events.addEventListener(Libp2pEvent.connectionOpen, this.onLibp2pPeerConnect);
194
197
  this.libp2p.services.components.events.addEventListener(Libp2pEvent.connectionClose, this.onLibp2pPeerDisconnect);
198
+ this.libp2p.services.components.events.addEventListener(Libp2pEvent.peerIdentify, this.onPeerIdentify);
195
199
  this.networkEventBus.on(NetworkEvent.reqRespRequest, this.onRequest);
196
200
 
197
201
  this.lastStatus = this.statusCache.get();
198
202
 
203
+ // A connection may already be open before listeners are attached.
204
+ // Seed those peers so they are tracked in connectedPeers immediately.
205
+ this.bootstrapAlreadyOpenConnections();
206
+ // Defer status/ping to the next event loop tick so the heartbeat interval and
207
+ // event listeners are fully registered before we begin handshakes.
208
+ callInNextEventLoop(() => this.pingAndStatusTimeouts());
209
+
199
210
  // On start-up will connected to existing peers in libp2p.peerStore, same as autoDial behaviour
200
211
  this.heartbeat();
201
212
  this.intervals = [
@@ -228,6 +239,7 @@ export class PeerManager {
228
239
  Libp2pEvent.connectionClose,
229
240
  this.onLibp2pPeerDisconnect
230
241
  );
242
+ this.libp2p.services.components.events.removeEventListener(Libp2pEvent.peerIdentify, this.onPeerIdentify);
231
243
  this.networkEventBus.off(NetworkEvent.reqRespRequest, this.onRequest);
232
244
  for (const interval of this.intervals) clearInterval(interval);
233
245
  }
@@ -472,6 +484,32 @@ export class PeerManager {
472
484
  clientAgent,
473
485
  custodyColumns,
474
486
  });
487
+
488
+ // Identify peer after status proves the connection is usable.
489
+ // This is the only place we trigger identify — avoids wasted streams on
490
+ // peers that close identify right after connection open or turn out to be
491
+ // irrelevant.
492
+ if (peerData?.agentVersion === null) {
493
+ const peerIdStr = peer.toString();
494
+ const connection = getConnection(this.libp2p, peerIdStr);
495
+ if (!connection || connection.status !== "open") {
496
+ this.logger.debug("Peer has no open connection for identify", {peerId: prettyPrintPeerId(peer)});
497
+ return;
498
+ }
499
+
500
+ const identifyKey = connection.id;
501
+ if (this.identifyInProgress.get(peerIdStr) === identifyKey) {
502
+ return;
503
+ }
504
+
505
+ this.identifyInProgress.set(peerIdStr, identifyKey);
506
+ void this.identifyPeer(peerIdStr, prettyPrintPeerId(peer), connection, identifyKey).finally(() => {
507
+ // Clear only if this identify attempt is still the active one for this peer
508
+ if (this.identifyInProgress.get(peerIdStr) === identifyKey) {
509
+ this.identifyInProgress.delete(peerIdStr);
510
+ }
511
+ });
512
+ }
475
513
  }
476
514
  }
477
515
 
@@ -692,36 +730,39 @@ export class PeerManager {
692
730
  }
693
731
  }
694
732
 
695
- /**
696
- * The libp2p Upgrader has successfully upgraded a peer connection on a particular multiaddress
697
- * This event is routed through the connectionManager
698
- *
699
- * Registers a peer as connected. The `direction` parameter determines if the peer is being
700
- * dialed or connecting to us.
701
- */
702
- private onLibp2pPeerConnect = async (evt: CustomEvent<Connection>): Promise<void> => {
703
- const {direction, status, remotePeer} = evt.detail;
733
+ private bootstrapAlreadyOpenConnections(): void {
734
+ let bootstrapped = 0;
735
+
736
+ for (const {value: connections} of getConnectionsMap(this.libp2p).values()) {
737
+ for (const connection of connections) {
738
+ // trackLibp2pConnection handles deduplication via overwriteExisting: false
739
+ if (this.trackLibp2pConnection(connection, {overwriteExisting: false, triggerHandshakeNow: false})) {
740
+ bootstrapped++;
741
+ }
742
+ }
743
+ }
744
+
745
+ if (bootstrapped > 0) {
746
+ this.logger.verbose("Bootstrapped already-open libp2p peers", {bootstrapped});
747
+ }
748
+ }
749
+
750
+ private trackLibp2pConnection(
751
+ connection: Connection,
752
+ opts: {overwriteExisting: boolean; triggerHandshakeNow: boolean}
753
+ ): boolean {
754
+ const {direction, status, remotePeer} = connection;
704
755
  const remotePeerStr = remotePeer.toString();
705
756
  const remotePeerPrettyStr = prettyPrintPeerId(remotePeer);
706
- this.logger.verbose("peer connected", {peer: remotePeerPrettyStr, direction, status});
707
- // NOTE: The peerConnect event is not emitted here here, but after asserting peer relevance
708
- this.metrics?.peerConnectedEvent.inc({direction, status});
709
757
 
710
- if (evt.detail.status !== "open") {
758
+ if (status !== "open") {
711
759
  this.logger.debug("Peer disconnected before identify protocol initiated", {
712
760
  peerId: remotePeerPrettyStr,
713
- status: evt.detail.status,
761
+ status,
714
762
  });
715
- return;
763
+ return false;
716
764
  }
717
765
 
718
- // On connection:
719
- // - Outbound connections: send a STATUS and PING request
720
- // - Inbound connections: expect to be STATUS'd, schedule STATUS and PING for latter
721
- // NOTE: libp2p may emit two "peer:connect" events: One for inbound, one for outbound
722
- // If that happens, it's okay. Only the "outbound" connection triggers immediate action
723
- const now = Date.now();
724
-
725
766
  // Ethereum uses secp256k1 for node IDs, reject peers with other key types
726
767
  if (remotePeer.type !== "secp256k1") {
727
768
  this.logger.debug("Peer does not have secp256k1 key, disconnecting", {
@@ -729,52 +770,64 @@ export class PeerManager {
729
770
  type: remotePeer.type,
730
771
  });
731
772
  void this.goodbyeAndDisconnect(remotePeer, GoodByeReasonCode.IRRELEVANT_NETWORK);
732
- return;
773
+ return false;
774
+ }
775
+
776
+ if (!opts.overwriteExisting && this.connectedPeers.has(remotePeerStr)) {
777
+ return false;
733
778
  }
734
779
 
780
+ // On connection:
781
+ // - Outbound connections: send a STATUS and PING request
782
+ // - Inbound connections: expect to be STATUS'd, schedule STATUS and PING for later
783
+ // NOTE: libp2p may emit two "peer:connect" events: One for inbound, one for outbound
784
+ // If that happens, it's okay. Only the "outbound" connection triggers immediate action
785
+ const now = Date.now();
786
+ const existingPeerData = this.connectedPeers.get(remotePeerStr);
735
787
  const nodeId = computeNodeId(remotePeer);
736
788
  const peerData: PeerData = {
737
- lastReceivedMsgUnixTsMs: direction === "outbound" ? 0 : now,
789
+ // Keep existing timestamps if this peer already had another open connection.
790
+ // libp2p may emit multiple connection:open events per peer.
791
+ lastReceivedMsgUnixTsMs: existingPeerData?.lastReceivedMsgUnixTsMs ?? (direction === "outbound" ? 0 : now),
738
792
  // If inbound, request after STATUS_INBOUND_GRACE_PERIOD
739
- lastStatusUnixTsMs: direction === "outbound" ? 0 : now - STATUS_INTERVAL_MS + STATUS_INBOUND_GRACE_PERIOD,
740
- connectedUnixTsMs: now,
741
- relevantStatus: RelevantPeerStatus.Unknown,
793
+ lastStatusUnixTsMs:
794
+ existingPeerData?.lastStatusUnixTsMs ??
795
+ (direction === "outbound" ? 0 : now - STATUS_INTERVAL_MS + STATUS_INBOUND_GRACE_PERIOD),
796
+ connectedUnixTsMs: existingPeerData?.connectedUnixTsMs ?? now,
797
+ relevantStatus: existingPeerData?.relevantStatus ?? RelevantPeerStatus.Unknown,
742
798
  direction,
743
799
  nodeId,
744
800
  peerId: remotePeer,
745
- status: null,
746
- metadata: null,
747
- agentVersion: null,
748
- agentClient: null,
749
- encodingPreference: null,
801
+ status: existingPeerData?.status ?? null,
802
+ metadata: existingPeerData?.metadata ?? null,
803
+ agentVersion: existingPeerData?.agentVersion ?? null,
804
+ agentClient: existingPeerData?.agentClient ?? null,
805
+ encodingPreference: existingPeerData?.encodingPreference ?? null,
750
806
  };
751
807
  this.connectedPeers.set(remotePeerStr, peerData);
752
808
 
753
- if (direction === "outbound") {
754
- // this.pingAndStatusTimeouts();
809
+ if (direction === "outbound" && opts.triggerHandshakeNow) {
755
810
  void this.requestPing(remotePeer);
756
811
  void this.requestStatus(remotePeer, this.statusCache.get());
757
812
  }
758
813
 
759
- this.libp2p.services.identify
760
- .identify(evt.detail)
761
- .then((result) => {
762
- const agentVersion = result.agentVersion;
763
- if (agentVersion) {
764
- peerData.agentVersion = agentVersion;
765
- peerData.agentClient = getKnownClientFromAgentVersion(agentVersion);
766
- }
767
- })
768
- .catch((err) => {
769
- if (evt.detail.status !== "open") {
770
- this.logger.debug("Peer disconnected during identify protocol", {
771
- peerId: remotePeerPrettyStr,
772
- error: (err as Error).message,
773
- });
774
- } else {
775
- this.logger.debug("Error setting agentVersion for the peer", {peerId: remotePeerPrettyStr}, err);
776
- }
777
- });
814
+ return true;
815
+ }
816
+
817
+ /**
818
+ * The libp2p Upgrader has successfully upgraded a peer connection on a particular multiaddress
819
+ * This event is routed through the connectionManager
820
+ *
821
+ * Registers a peer as connected. The `direction` parameter determines if the peer is being
822
+ * dialed or connecting to us.
823
+ */
824
+ private onLibp2pPeerConnect = (evt: CustomEvent<Connection>): void => {
825
+ const {direction, status, remotePeer} = evt.detail;
826
+ this.logger.verbose("peer connected", {peer: prettyPrintPeerId(remotePeer), direction, status});
827
+ // NOTE: The peerConnect event is not emitted here here, but after asserting peer relevance
828
+ this.metrics?.peerConnectedEvent.inc({direction, status});
829
+
830
+ this.trackLibp2pConnection(evt.detail, {overwriteExisting: true, triggerHandshakeNow: true});
778
831
  };
779
832
 
780
833
  /**
@@ -784,6 +837,19 @@ export class PeerManager {
784
837
  const {direction, status, remotePeer} = evt.detail;
785
838
  const peerIdStr = remotePeer.toString();
786
839
 
840
+ const openConnections =
841
+ getConnectionsMap(this.libp2p)
842
+ .get(peerIdStr)
843
+ ?.value.filter((connection) => connection.status === "open") ?? [];
844
+ if (openConnections.length > 0) {
845
+ this.logger.debug("Ignoring peer disconnect event while another connection is still open", {
846
+ peerId: prettyPrintPeerIdStr(peerIdStr),
847
+ direction,
848
+ status,
849
+ });
850
+ return;
851
+ }
852
+
787
853
  let logMessage = "onLibp2pPeerDisconnect";
788
854
  const logContext: Record<string, string | number> = {
789
855
  peerId: prettyPrintPeerIdStr(peerIdStr),
@@ -801,6 +867,7 @@ export class PeerManager {
801
867
 
802
868
  // remove the ping and status timer for the peer
803
869
  this.connectedPeers.delete(peerIdStr);
870
+ this.identifyInProgress.delete(peerIdStr);
804
871
 
805
872
  this.logger.verbose(logMessage, logContext);
806
873
  this.networkEventBus.emit(NetworkEvent.peerDisconnected, {peer: peerIdStr});
@@ -818,6 +885,60 @@ export class PeerManager {
818
885
  }
819
886
  }
820
887
 
888
+ /**
889
+ * Consume successful identify results from libp2p events.
890
+ * This captures agentVersion from identify-push or successful inbound/outbound identify,
891
+ * even if our explicit identify request failed earlier.
892
+ */
893
+ private onPeerIdentify = (evt: CustomEvent<IdentifyResult>): void => {
894
+ const {peerId, agentVersion} = evt.detail;
895
+ if (!agentVersion) return;
896
+
897
+ const peerIdStr = peerId.toString();
898
+ const peerData = this.connectedPeers.get(peerIdStr);
899
+ if (!peerData) return;
900
+
901
+ peerData.agentVersion = agentVersion;
902
+ peerData.agentClient = getKnownClientFromAgentVersion(agentVersion);
903
+ this.identifyInProgress.delete(peerIdStr);
904
+ };
905
+
906
+ private async identifyPeer(
907
+ peerIdStr: string,
908
+ peerIdPretty: string,
909
+ connection: Connection,
910
+ identifyKey: string
911
+ ): Promise<void> {
912
+ if (this.identifyInProgress.get(peerIdStr) !== identifyKey) {
913
+ return;
914
+ }
915
+
916
+ if (connection.status !== "open") {
917
+ this.logger.debug("Peer has no open connection for identify", {peerId: peerIdPretty});
918
+ return;
919
+ }
920
+
921
+ try {
922
+ const result = await this.libp2p.services.identify.identify(connection);
923
+
924
+ // A newer identify attempt may have superseded this one (e.g. reconnect).
925
+ if (this.identifyInProgress.get(peerIdStr) !== identifyKey) {
926
+ return;
927
+ }
928
+
929
+ const agentVersion = result.agentVersion;
930
+ if (agentVersion) {
931
+ const connectedPeerData = this.connectedPeers.get(peerIdStr);
932
+ if (connectedPeerData) {
933
+ connectedPeerData.agentVersion = agentVersion;
934
+ connectedPeerData.agentClient = getKnownClientFromAgentVersion(agentVersion);
935
+ }
936
+ }
937
+ } catch (e) {
938
+ this.logger.debug("Error setting agentVersion for the peer", {peerId: peerIdPretty}, e as Error);
939
+ }
940
+ }
941
+
821
942
  private async goodbyeAndDisconnect(peer: PeerId, goodbye: GoodByeReasonCode): Promise<void> {
822
943
  const reason = GOODBYE_KNOWN_CODES[goodbye.toString()] || "";
823
944
  const peerIdStr = peer.toString();
@@ -1,4 +1,4 @@
1
- import {Direction, PeerId} from "@libp2p/interface";
1
+ import type {MessageStreamDirection, PeerId} from "@libp2p/interface";
2
2
  import {BitArray} from "@chainsafe/ssz";
3
3
  import {ChainConfig} from "@lodestar/config";
4
4
  import {ATTESTATION_SUBNET_COUNT, SYNC_COMMITTEE_SUBNET_COUNT} from "@lodestar/params";
@@ -95,7 +95,7 @@ function computeStatusScore(ours: Status, theirs: Status | null, opts: Prioritiz
95
95
 
96
96
  type PeerInfo = {
97
97
  id: PeerId;
98
- direction: Direction | null;
98
+ direction: MessageStreamDirection | null;
99
99
  statusScore: StatusScore;
100
100
  attnets: phase0.AttestationSubnets;
101
101
  syncnets: altair.SyncSubnets;
@@ -137,7 +137,7 @@ export enum ExcessPeerDisconnectReason {
137
137
  export function prioritizePeers(
138
138
  connectedPeersInfo: {
139
139
  id: PeerId;
140
- direction: Direction | null;
140
+ direction: MessageStreamDirection | null;
141
141
  status: Status | null;
142
142
  attnets: phase0.AttestationSubnets | null;
143
143
  syncnets: altair.SyncSubnets | null;
@@ -1,4 +1,4 @@
1
- import {TopicValidatorResult} from "@libp2p/interface";
1
+ import {TopicValidatorResult} from "@libp2p/gossipsub";
2
2
  import {ChainForkConfig} from "@lodestar/config";
3
3
  import {Logger} from "@lodestar/utils";
4
4
  import {AttestationError, GossipAction, GossipActionError} from "../../chain/errors/index.js";
@@ -1,4 +1,4 @@
1
- import {Message} from "@libp2p/interface";
1
+ import type {Message} from "@libp2p/gossipsub";
2
2
  import {ForkName} from "@lodestar/params";
3
3
  import {Slot, SlotOptionalRoot} from "@lodestar/types";
4
4
  import {PeerIdStr} from "../../util/peerId.js";
@@ -38,7 +38,6 @@ export function onOutgoingReqRespError(e: RequestError, method: ReqRespMethod):
38
38
  : PeerAction.LowToleranceError;
39
39
  // TODO: Detect SSZDecodeError and return PeerAction.Fatal
40
40
 
41
- case RequestErrorCode.TTFB_TIMEOUT:
42
41
  case RequestErrorCode.RESP_TIMEOUT:
43
42
  switch (method) {
44
43
  case ReqRespMethod.Ping:
@@ -23,7 +23,7 @@ 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/ChainSafe/js-libp2p-gossipsub/blob/3475242ed254f7647798ab7f36b21909f6cb61da/src/index.ts#L2009
26
+ // https://github.com/libp2p/js-libp2p/blob/f87cba928991736d9646b3e054c367f55cab315c/packages/gossipsub/src/gossipsub.ts#L2076
27
27
  export function isPublishToZeroPeersError(e: Error): boolean {
28
- return e.message.includes("PublishError.InsufficientPeers");
28
+ return e.message.includes("PublishError.NoPeersSubscribedToTopic");
29
29
  }
@@ -1,5 +1,5 @@
1
1
  import {MessagePort, Worker} from "node:worker_threads";
2
- import {Message} from "@libp2p/interface";
2
+ import type {Message} from "@libp2p/gossipsub";
3
3
  import {Thread} from "@chainsafe/threads";
4
4
  import {Logger} from "@lodestar/logger";
5
5
  import {sleep} from "@lodestar/utils";