2023-02-20 15:30:59 +05:30
|
|
|
import type { PeerId } from "@libp2p/interface-peer-id";
|
|
|
|
|
import type { PeerInfo } from "@libp2p/interface-peer-info";
|
2023-07-26 22:51:55 +05:30
|
|
|
import type { Peer } from "@libp2p/interface-peer-store";
|
|
|
|
|
import { CustomEvent, EventEmitter } from "@libp2p/interfaces/events";
|
|
|
|
|
import {
|
|
|
|
|
ConnectionManagerOptions,
|
|
|
|
|
EPeersByDiscoveryEvents,
|
2023-07-31 13:54:39 +05:30
|
|
|
IConnectionManager,
|
2023-07-26 22:51:55 +05:30
|
|
|
IPeersByDiscoveryEvents,
|
|
|
|
|
IRelay,
|
2023-07-31 13:54:39 +05:30
|
|
|
KeepAliveOptions,
|
2023-07-26 22:51:55 +05:30
|
|
|
PeersByDiscoveryResult,
|
|
|
|
|
} from "@waku/interfaces";
|
2023-07-25 02:17:52 +02:00
|
|
|
import { Libp2p, Tags } from "@waku/interfaces";
|
2023-02-20 15:30:59 +05:30
|
|
|
import debug from "debug";
|
|
|
|
|
|
2023-07-31 13:54:39 +05:30
|
|
|
import { KeepAliveManager } from "./keep_alive_manager.js";
|
2023-02-20 15:30:59 +05:30
|
|
|
|
|
|
|
|
const log = debug("waku:connection-manager");
|
|
|
|
|
|
|
|
|
|
export const DEFAULT_MAX_BOOTSTRAP_PEERS_ALLOWED = 1;
|
|
|
|
|
export const DEFAULT_MAX_DIAL_ATTEMPTS_FOR_PEER = 3;
|
2023-06-08 17:56:29 +05:30
|
|
|
export const DEFAULT_MAX_PARALLEL_DIALS = 3;
|
2023-02-20 15:30:59 +05:30
|
|
|
|
2023-07-31 13:54:39 +05:30
|
|
|
export class ConnectionManager
|
|
|
|
|
extends EventEmitter<IPeersByDiscoveryEvents>
|
|
|
|
|
implements IConnectionManager
|
|
|
|
|
{
|
2023-02-20 15:30:59 +05:30
|
|
|
private static instances = new Map<string, ConnectionManager>();
|
|
|
|
|
private keepAliveManager: KeepAliveManager;
|
|
|
|
|
private options: ConnectionManagerOptions;
|
2023-07-25 02:17:52 +02:00
|
|
|
private libp2p: Libp2p;
|
2023-02-20 15:30:59 +05:30
|
|
|
private dialAttemptsForPeer: Map<string, number> = new Map();
|
2023-05-11 13:35:58 +05:30
|
|
|
private dialErrorsForPeer: Map<string, any> = new Map();
|
2023-02-20 15:30:59 +05:30
|
|
|
|
2023-06-08 17:56:29 +05:30
|
|
|
private currentActiveDialCount = 0;
|
|
|
|
|
private pendingPeerDialQueue: Array<PeerId> = [];
|
|
|
|
|
|
2023-02-20 15:30:59 +05:30
|
|
|
public static create(
|
|
|
|
|
peerId: string,
|
|
|
|
|
libp2p: Libp2p,
|
|
|
|
|
keepAliveOptions: KeepAliveOptions,
|
|
|
|
|
relay?: IRelay,
|
2023-08-11 15:14:02 +02:00
|
|
|
options?: ConnectionManagerOptions,
|
2023-02-20 15:30:59 +05:30
|
|
|
): ConnectionManager {
|
|
|
|
|
let instance = ConnectionManager.instances.get(peerId);
|
|
|
|
|
if (!instance) {
|
|
|
|
|
instance = new ConnectionManager(
|
|
|
|
|
libp2p,
|
|
|
|
|
keepAliveOptions,
|
|
|
|
|
relay,
|
2023-08-11 15:14:02 +02:00
|
|
|
options,
|
2023-02-20 15:30:59 +05:30
|
|
|
);
|
|
|
|
|
ConnectionManager.instances.set(peerId, instance);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return instance;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-26 22:51:55 +05:30
|
|
|
public async getPeersByDiscovery(): Promise<PeersByDiscoveryResult> {
|
|
|
|
|
const peersDiscovered = await this.libp2p.peerStore.all();
|
|
|
|
|
const peersConnected = this.libp2p
|
|
|
|
|
.getConnections()
|
|
|
|
|
.map((conn) => conn.remotePeer);
|
|
|
|
|
|
|
|
|
|
const peersDiscoveredByBootstrap: Peer[] = [];
|
|
|
|
|
const peersDiscoveredByPeerExchange: Peer[] = [];
|
|
|
|
|
const peersConnectedByBootstrap: Peer[] = [];
|
|
|
|
|
const peersConnectedByPeerExchange: Peer[] = [];
|
|
|
|
|
|
|
|
|
|
for (const peer of peersDiscovered) {
|
|
|
|
|
const tags = await this.getTagNamesForPeer(peer.id);
|
|
|
|
|
|
|
|
|
|
if (tags.includes(Tags.BOOTSTRAP)) {
|
|
|
|
|
peersDiscoveredByBootstrap.push(peer);
|
|
|
|
|
} else if (tags.includes(Tags.PEER_EXCHANGE)) {
|
|
|
|
|
peersDiscoveredByPeerExchange.push(peer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const peerId of peersConnected) {
|
|
|
|
|
const peer = await this.libp2p.peerStore.get(peerId);
|
|
|
|
|
const tags = await this.getTagNamesForPeer(peerId);
|
|
|
|
|
|
|
|
|
|
if (tags.includes(Tags.BOOTSTRAP)) {
|
|
|
|
|
peersConnectedByBootstrap.push(peer);
|
|
|
|
|
} else if (tags.includes(Tags.PEER_EXCHANGE)) {
|
|
|
|
|
peersConnectedByPeerExchange.push(peer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
DISCOVERED: {
|
|
|
|
|
[Tags.BOOTSTRAP]: peersDiscoveredByBootstrap,
|
|
|
|
|
[Tags.PEER_EXCHANGE]: peersDiscoveredByPeerExchange,
|
|
|
|
|
},
|
|
|
|
|
CONNECTED: {
|
|
|
|
|
[Tags.BOOTSTRAP]: peersConnectedByBootstrap,
|
|
|
|
|
[Tags.PEER_EXCHANGE]: peersConnectedByPeerExchange,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-20 15:30:59 +05:30
|
|
|
private constructor(
|
2023-07-25 02:17:52 +02:00
|
|
|
libp2p: Libp2p,
|
2023-02-20 15:30:59 +05:30
|
|
|
keepAliveOptions: KeepAliveOptions,
|
|
|
|
|
relay?: IRelay,
|
2023-08-11 15:14:02 +02:00
|
|
|
options?: Partial<ConnectionManagerOptions>,
|
2023-02-20 15:30:59 +05:30
|
|
|
) {
|
2023-07-26 22:51:55 +05:30
|
|
|
super();
|
2023-07-25 02:17:52 +02:00
|
|
|
this.libp2p = libp2p;
|
2023-02-20 15:30:59 +05:30
|
|
|
this.options = {
|
|
|
|
|
maxDialAttemptsForPeer: DEFAULT_MAX_DIAL_ATTEMPTS_FOR_PEER,
|
|
|
|
|
maxBootstrapPeersAllowed: DEFAULT_MAX_BOOTSTRAP_PEERS_ALLOWED,
|
2023-06-08 17:56:29 +05:30
|
|
|
maxParallelDials: DEFAULT_MAX_PARALLEL_DIALS,
|
2023-02-20 15:30:59 +05:30
|
|
|
...options,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.keepAliveManager = new KeepAliveManager(keepAliveOptions, relay);
|
|
|
|
|
|
|
|
|
|
this.run()
|
|
|
|
|
.then(() => log(`Connection Manager is now running`))
|
|
|
|
|
.catch((error) => log(`Unexpected error while running service`, error));
|
2023-06-08 17:56:29 +05:30
|
|
|
|
|
|
|
|
// libp2p emits `peer:discovery` events during its initialization
|
|
|
|
|
// which means that before the ConnectionManager is initialized, some peers may have been discovered
|
|
|
|
|
// we will dial the peers in peerStore ONCE before we start to listen to the `peer:discovery` events within the ConnectionManager
|
2023-07-24 16:04:24 +05:30
|
|
|
this.dialPeerStorePeers().catch((error) =>
|
2023-08-11 15:14:02 +02:00
|
|
|
log(`Unexpected error while dialing peer store peers`, error),
|
2023-07-24 16:04:24 +05:30
|
|
|
);
|
2023-06-08 17:56:29 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async dialPeerStorePeers(): Promise<void> {
|
2023-07-25 02:17:52 +02:00
|
|
|
const peerInfos = await this.libp2p.peerStore.all();
|
2023-06-08 17:56:29 +05:30
|
|
|
const dialPromises = [];
|
|
|
|
|
for (const peerInfo of peerInfos) {
|
|
|
|
|
if (
|
2023-07-25 02:17:52 +02:00
|
|
|
this.libp2p.getConnections().find((c) => c.remotePeer === peerInfo.id)
|
2023-06-08 17:56:29 +05:30
|
|
|
)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
dialPromises.push(this.attemptDial(peerInfo.id));
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
await Promise.all(dialPromises);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
log(`Unexpected error while dialing peer store peers`, error);
|
|
|
|
|
}
|
2023-02-20 15:30:59 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async run(): Promise<void> {
|
|
|
|
|
// start event listeners
|
|
|
|
|
this.startPeerDiscoveryListener();
|
|
|
|
|
this.startPeerConnectionListener();
|
|
|
|
|
this.startPeerDisconnectionListener();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stop(): void {
|
|
|
|
|
this.keepAliveManager.stopAll();
|
2023-07-25 02:17:52 +02:00
|
|
|
this.libp2p.removeEventListener(
|
2023-02-20 15:30:59 +05:30
|
|
|
"peer:connect",
|
2023-08-11 15:14:02 +02:00
|
|
|
this.onEventHandlers["peer:connect"],
|
2023-02-20 15:30:59 +05:30
|
|
|
);
|
2023-07-25 02:17:52 +02:00
|
|
|
this.libp2p.removeEventListener(
|
2023-02-20 15:30:59 +05:30
|
|
|
"peer:disconnect",
|
2023-08-11 15:14:02 +02:00
|
|
|
this.onEventHandlers["peer:disconnect"],
|
2023-02-20 15:30:59 +05:30
|
|
|
);
|
2023-07-25 02:17:52 +02:00
|
|
|
this.libp2p.removeEventListener(
|
2023-02-20 15:30:59 +05:30
|
|
|
"peer:discovery",
|
2023-08-11 15:14:02 +02:00
|
|
|
this.onEventHandlers["peer:discovery"],
|
2023-02-20 15:30:59 +05:30
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async dialPeer(peerId: PeerId): Promise<void> {
|
2023-06-08 17:56:29 +05:30
|
|
|
this.currentActiveDialCount += 1;
|
2023-02-20 15:30:59 +05:30
|
|
|
let dialAttempt = 0;
|
2023-08-02 13:49:48 +05:30
|
|
|
while (dialAttempt < this.options.maxDialAttemptsForPeer) {
|
2023-02-20 15:30:59 +05:30
|
|
|
try {
|
2023-08-02 13:49:48 +05:30
|
|
|
log(`Dialing peer ${peerId.toString()} on attempt ${dialAttempt + 1}`);
|
2023-07-25 02:17:52 +02:00
|
|
|
await this.libp2p.dial(peerId);
|
2023-02-20 15:30:59 +05:30
|
|
|
|
|
|
|
|
const tags = await this.getTagNamesForPeer(peerId);
|
|
|
|
|
// add tag to connection describing discovery mechanism
|
|
|
|
|
// don't add duplicate tags
|
2023-08-02 13:49:48 +05:30
|
|
|
this.libp2p.getConnections(peerId).forEach((conn) => {
|
|
|
|
|
conn.tags = Array.from(new Set([...conn.tags, ...tags]));
|
|
|
|
|
});
|
2023-02-20 15:30:59 +05:30
|
|
|
|
|
|
|
|
this.dialAttemptsForPeer.delete(peerId.toString());
|
2023-08-02 13:49:48 +05:30
|
|
|
// Dialing succeeded, break the loop
|
|
|
|
|
break;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
if (error instanceof AggregateError) {
|
|
|
|
|
// Handle AggregateError
|
|
|
|
|
log(`Error dialing peer ${peerId.toString()} - ${error.errors}`);
|
|
|
|
|
} else {
|
|
|
|
|
// Handle generic error
|
|
|
|
|
log(
|
|
|
|
|
`Error dialing peer ${peerId.toString()} - ${
|
|
|
|
|
(error as any).message
|
2023-08-11 15:14:02 +02:00
|
|
|
}`,
|
2023-08-02 13:49:48 +05:30
|
|
|
);
|
|
|
|
|
}
|
2023-05-11 13:35:58 +05:30
|
|
|
this.dialErrorsForPeer.set(peerId.toString(), error);
|
2023-02-20 15:30:59 +05:30
|
|
|
|
2023-08-02 13:49:48 +05:30
|
|
|
dialAttempt++;
|
|
|
|
|
this.dialAttemptsForPeer.set(peerId.toString(), dialAttempt);
|
2023-02-20 15:30:59 +05:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-02 13:49:48 +05:30
|
|
|
// Always decrease the active dial count and process the dial queue
|
|
|
|
|
this.currentActiveDialCount--;
|
|
|
|
|
this.processDialQueue();
|
|
|
|
|
|
|
|
|
|
// If max dial attempts reached and dialing failed, delete the peer
|
|
|
|
|
if (dialAttempt === this.options.maxDialAttemptsForPeer) {
|
|
|
|
|
try {
|
|
|
|
|
const error = this.dialErrorsForPeer.get(peerId.toString());
|
|
|
|
|
|
|
|
|
|
let errorMessage;
|
|
|
|
|
if (error instanceof AggregateError) {
|
|
|
|
|
errorMessage = JSON.stringify(error.errors[0]);
|
|
|
|
|
} else {
|
|
|
|
|
errorMessage = error.message;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log(
|
2023-08-11 15:14:02 +02:00
|
|
|
`Deleting undialable peer ${peerId.toString()} from peer store. Error: ${errorMessage}`,
|
2023-08-02 13:49:48 +05:30
|
|
|
);
|
|
|
|
|
|
|
|
|
|
this.dialErrorsForPeer.delete(peerId.toString());
|
|
|
|
|
await this.libp2p.peerStore.delete(peerId);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
throw new Error(
|
2023-08-11 15:14:02 +02:00
|
|
|
`Error deleting undialable peer ${peerId.toString()} from peer store - ${error}`,
|
2023-08-02 13:49:48 +05:30
|
|
|
);
|
|
|
|
|
}
|
2023-06-08 17:56:29 +05:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-31 13:54:39 +05:30
|
|
|
private async dropConnection(peerId: PeerId): Promise<void> {
|
2023-06-08 17:56:29 +05:30
|
|
|
try {
|
2023-07-26 12:20:51 +05:30
|
|
|
this.keepAliveManager.stop(peerId);
|
2023-07-25 02:17:52 +02:00
|
|
|
await this.libp2p.hangUp(peerId);
|
2023-06-08 17:56:29 +05:30
|
|
|
log(`Dropped connection with peer ${peerId.toString()}`);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
log(
|
2023-08-11 15:14:02 +02:00
|
|
|
`Error dropping connection with peer ${peerId.toString()} - ${error}`,
|
2023-06-08 17:56:29 +05:30
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-24 16:04:24 +05:30
|
|
|
private processDialQueue(): void {
|
2023-06-08 17:56:29 +05:30
|
|
|
if (
|
|
|
|
|
this.pendingPeerDialQueue.length > 0 &&
|
|
|
|
|
this.currentActiveDialCount < this.options.maxParallelDials
|
|
|
|
|
) {
|
|
|
|
|
const peerId = this.pendingPeerDialQueue.shift();
|
|
|
|
|
if (!peerId) return;
|
|
|
|
|
this.attemptDial(peerId).catch((error) => {
|
|
|
|
|
log(error);
|
|
|
|
|
});
|
2023-02-20 15:30:59 +05:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private startPeerDiscoveryListener(): void {
|
2023-07-25 02:17:52 +02:00
|
|
|
this.libp2p.addEventListener(
|
2023-07-24 12:08:54 +05:30
|
|
|
"peer:discovery",
|
2023-08-11 15:14:02 +02:00
|
|
|
this.onEventHandlers["peer:discovery"],
|
2023-02-20 15:30:59 +05:30
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private startPeerConnectionListener(): void {
|
2023-07-25 02:17:52 +02:00
|
|
|
this.libp2p.addEventListener(
|
2023-02-20 15:30:59 +05:30
|
|
|
"peer:connect",
|
2023-08-11 15:14:02 +02:00
|
|
|
this.onEventHandlers["peer:connect"],
|
2023-02-20 15:30:59 +05:30
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private startPeerDisconnectionListener(): void {
|
|
|
|
|
// TODO: ensure that these following issues are updated and confirmed
|
|
|
|
|
/**
|
|
|
|
|
* NOTE: Event is not being emitted on closing nor losing a connection.
|
|
|
|
|
* @see https://github.com/libp2p/js-libp2p/issues/939
|
|
|
|
|
* @see https://github.com/status-im/js-waku/issues/252
|
|
|
|
|
*
|
|
|
|
|
* >This event will be triggered anytime we are disconnected from another peer,
|
|
|
|
|
* >regardless of the circumstances of that disconnection.
|
|
|
|
|
* >If we happen to have multiple connections to a peer,
|
|
|
|
|
* >this event will **only** be triggered when the last connection is closed.
|
|
|
|
|
* @see https://github.com/libp2p/js-libp2p/blob/bad9e8c0ff58d60a78314077720c82ae331cc55b/doc/API.md?plain=1#L2100
|
|
|
|
|
*/
|
2023-07-25 02:17:52 +02:00
|
|
|
this.libp2p.addEventListener(
|
2023-02-20 15:30:59 +05:30
|
|
|
"peer:disconnect",
|
2023-08-11 15:14:02 +02:00
|
|
|
this.onEventHandlers["peer:disconnect"],
|
2023-02-20 15:30:59 +05:30
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-08 17:56:29 +05:30
|
|
|
private async attemptDial(peerId: PeerId): Promise<void> {
|
|
|
|
|
if (this.currentActiveDialCount >= this.options.maxParallelDials) {
|
|
|
|
|
this.pendingPeerDialQueue.push(peerId);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(await this.shouldDialPeer(peerId))) return;
|
|
|
|
|
|
|
|
|
|
this.dialPeer(peerId).catch((err) => {
|
|
|
|
|
throw `Error dialing peer ${peerId.toString()} : ${err}`;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-20 15:30:59 +05:30
|
|
|
private onEventHandlers = {
|
2023-07-24 16:04:24 +05:30
|
|
|
"peer:discovery": (evt: CustomEvent<PeerInfo>): void => {
|
|
|
|
|
void (async () => {
|
|
|
|
|
const { id: peerId } = evt.detail;
|
|
|
|
|
|
2023-07-26 22:51:55 +05:30
|
|
|
const isBootstrap = (await this.getTagNamesForPeer(peerId)).includes(
|
2023-08-11 15:14:02 +02:00
|
|
|
Tags.BOOTSTRAP,
|
2023-07-26 22:51:55 +05:30
|
|
|
);
|
|
|
|
|
|
2023-08-02 13:49:48 +05:30
|
|
|
this.dispatchEvent(
|
|
|
|
|
new CustomEvent<PeerId>(
|
|
|
|
|
isBootstrap
|
|
|
|
|
? EPeersByDiscoveryEvents.PEER_DISCOVERY_BOOTSTRAP
|
|
|
|
|
: EPeersByDiscoveryEvents.PEER_DISCOVERY_PEER_EXCHANGE,
|
|
|
|
|
{
|
|
|
|
|
detail: peerId,
|
2023-08-11 15:14:02 +02:00
|
|
|
},
|
|
|
|
|
),
|
2023-08-02 13:49:48 +05:30
|
|
|
);
|
2023-07-26 22:51:55 +05:30
|
|
|
|
2023-07-24 16:04:24 +05:30
|
|
|
try {
|
|
|
|
|
await this.attemptDial(peerId);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
log(`Error dialing peer ${peerId.toString()} : ${error}`);
|
|
|
|
|
}
|
|
|
|
|
})();
|
2023-02-20 15:30:59 +05:30
|
|
|
},
|
2023-07-25 02:17:52 +02:00
|
|
|
"peer:connect": (evt: CustomEvent<PeerId>): void => {
|
2023-07-24 16:04:24 +05:30
|
|
|
void (async () => {
|
2023-07-25 02:17:52 +02:00
|
|
|
const peerId = evt.detail;
|
2023-07-24 16:04:24 +05:30
|
|
|
|
2023-07-25 02:17:52 +02:00
|
|
|
this.keepAliveManager.start(peerId, this.libp2p.services.ping);
|
2023-07-24 16:04:24 +05:30
|
|
|
|
|
|
|
|
const isBootstrap = (await this.getTagNamesForPeer(peerId)).includes(
|
2023-08-11 15:14:02 +02:00
|
|
|
Tags.BOOTSTRAP,
|
2023-07-24 16:04:24 +05:30
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (isBootstrap) {
|
2023-07-25 02:17:52 +02:00
|
|
|
const bootstrapConnections = this.libp2p
|
2023-07-24 16:04:24 +05:30
|
|
|
.getConnections()
|
|
|
|
|
.filter((conn) => conn.tags.includes(Tags.BOOTSTRAP));
|
|
|
|
|
|
|
|
|
|
// If we have too many bootstrap connections, drop one
|
|
|
|
|
if (
|
|
|
|
|
bootstrapConnections.length > this.options.maxBootstrapPeersAllowed
|
|
|
|
|
) {
|
|
|
|
|
await this.dropConnection(peerId);
|
2023-07-26 22:51:55 +05:30
|
|
|
} else {
|
|
|
|
|
this.dispatchEvent(
|
|
|
|
|
new CustomEvent<PeerId>(
|
|
|
|
|
EPeersByDiscoveryEvents.PEER_CONNECT_BOOTSTRAP,
|
|
|
|
|
{
|
|
|
|
|
detail: peerId,
|
2023-08-11 15:14:02 +02:00
|
|
|
},
|
|
|
|
|
),
|
2023-07-26 22:51:55 +05:30
|
|
|
);
|
2023-07-24 16:04:24 +05:30
|
|
|
}
|
2023-07-26 22:51:55 +05:30
|
|
|
} else {
|
|
|
|
|
this.dispatchEvent(
|
|
|
|
|
new CustomEvent<PeerId>(
|
|
|
|
|
EPeersByDiscoveryEvents.PEER_CONNECT_PEER_EXCHANGE,
|
|
|
|
|
{
|
|
|
|
|
detail: peerId,
|
2023-08-11 15:14:02 +02:00
|
|
|
},
|
|
|
|
|
),
|
2023-07-26 22:51:55 +05:30
|
|
|
);
|
2023-06-08 17:56:29 +05:30
|
|
|
}
|
2023-07-24 16:04:24 +05:30
|
|
|
})();
|
2023-02-20 15:30:59 +05:30
|
|
|
},
|
|
|
|
|
"peer:disconnect": () => {
|
2023-07-25 02:17:52 +02:00
|
|
|
return (evt: CustomEvent<PeerId>): void => {
|
|
|
|
|
this.keepAliveManager.stop(evt.detail);
|
2023-02-20 15:30:59 +05:30
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Checks if the peer is dialable based on the following conditions:
|
|
|
|
|
* 1. If the peer is a bootstrap peer, it is only dialable if the number of current bootstrap connections is less than the max allowed.
|
|
|
|
|
* 2. If the peer is not a bootstrap peer
|
|
|
|
|
*/
|
|
|
|
|
private async shouldDialPeer(peerId: PeerId): Promise<boolean> {
|
2023-07-25 02:17:52 +02:00
|
|
|
const isConnected = this.libp2p.getConnections(peerId).length > 0;
|
2023-02-20 15:30:59 +05:30
|
|
|
|
|
|
|
|
if (isConnected) return false;
|
|
|
|
|
|
2023-07-24 12:08:54 +05:30
|
|
|
const tagNames = await this.getTagNamesForPeer(peerId);
|
|
|
|
|
|
|
|
|
|
const isBootstrap = tagNames.some((tagName) => tagName === Tags.BOOTSTRAP);
|
2023-02-20 15:30:59 +05:30
|
|
|
|
|
|
|
|
if (isBootstrap) {
|
2023-07-25 02:17:52 +02:00
|
|
|
const currentBootstrapConnections = this.libp2p
|
2023-02-20 15:30:59 +05:30
|
|
|
.getConnections()
|
|
|
|
|
.filter((conn) => {
|
2023-07-24 12:08:54 +05:30
|
|
|
return conn.tags.find((name) => name === Tags.BOOTSTRAP);
|
2023-02-20 15:30:59 +05:30
|
|
|
}).length;
|
|
|
|
|
if (currentBootstrapConnections < this.options.maxBootstrapPeersAllowed)
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Fetches the tag names for a given peer
|
|
|
|
|
*/
|
|
|
|
|
private async getTagNamesForPeer(peerId: PeerId): Promise<string[]> {
|
2023-07-25 02:17:52 +02:00
|
|
|
try {
|
|
|
|
|
const peer = await this.libp2p.peerStore.get(peerId);
|
|
|
|
|
return Array.from(peer.tags.keys());
|
|
|
|
|
} catch (error) {
|
|
|
|
|
log(`Failed to get peer ${peerId}, error: ${error}`);
|
|
|
|
|
return [];
|
|
|
|
|
}
|
2023-02-20 15:30:59 +05:30
|
|
|
}
|
|
|
|
|
}
|