2024-06-19 01:52:16 -04:00
|
|
|
import type { Peer, PeerId } from "@libp2p/interface";
|
2024-10-11 03:17:12 +05:30
|
|
|
import { ConnectionManager } from "@waku/core";
|
2024-06-19 01:52:16 -04:00
|
|
|
import { BaseProtocol } from "@waku/core/lib/base_protocol";
|
2024-10-11 03:17:12 +05:30
|
|
|
import { IBaseProtocolSDK, ProtocolUseOptions } from "@waku/interfaces";
|
|
|
|
|
import { Logger } from "@waku/utils";
|
|
|
|
|
|
|
|
|
|
import { PeerManager } from "./peer_manager.js";
|
2024-03-11 18:50:34 +05:30
|
|
|
|
|
|
|
|
interface Options {
|
|
|
|
|
numPeersToUse?: number;
|
2024-06-19 01:52:16 -04:00
|
|
|
maintainPeersInterval?: number;
|
2024-03-11 18:50:34 +05:30
|
|
|
}
|
|
|
|
|
|
2024-10-11 03:17:12 +05:30
|
|
|
const DEFAULT_NUM_PEERS_TO_USE = 2;
|
2024-06-19 01:52:16 -04:00
|
|
|
const DEFAULT_MAINTAIN_PEERS_INTERVAL = 30_000;
|
2024-03-11 18:50:34 +05:30
|
|
|
|
|
|
|
|
export class BaseProtocolSDK implements IBaseProtocolSDK {
|
2024-10-11 03:17:12 +05:30
|
|
|
private peerManager: PeerManager;
|
2024-06-19 01:52:16 -04:00
|
|
|
public readonly numPeersToUse: number;
|
|
|
|
|
private maintainPeersIntervalId: ReturnType<
|
|
|
|
|
typeof window.setInterval
|
|
|
|
|
> | null = null;
|
2024-07-19 15:58:17 +05:30
|
|
|
private log: Logger;
|
2024-03-11 18:50:34 +05:30
|
|
|
|
2024-07-19 15:58:17 +05:30
|
|
|
public constructor(
|
2024-06-19 01:52:16 -04:00
|
|
|
protected core: BaseProtocol,
|
2024-09-13 14:57:29 +05:30
|
|
|
protected connectionManager: ConnectionManager,
|
2024-06-19 01:52:16 -04:00
|
|
|
options: Options
|
|
|
|
|
) {
|
|
|
|
|
this.log = new Logger(`sdk:${core.multicodec}`);
|
2024-07-27 18:27:54 +05:30
|
|
|
|
2024-10-11 03:17:12 +05:30
|
|
|
this.peerManager = new PeerManager(connectionManager, core, this.log);
|
2024-07-27 18:27:54 +05:30
|
|
|
|
2024-06-19 01:52:16 -04:00
|
|
|
this.numPeersToUse = options?.numPeersToUse ?? DEFAULT_NUM_PEERS_TO_USE;
|
|
|
|
|
const maintainPeersInterval =
|
|
|
|
|
options?.maintainPeersInterval ?? DEFAULT_MAINTAIN_PEERS_INTERVAL;
|
|
|
|
|
|
2024-10-11 03:17:12 +05:30
|
|
|
this.log.info(
|
|
|
|
|
`Initializing BaseProtocolSDK with numPeersToUse: ${this.numPeersToUse}, maintainPeersInterval: ${maintainPeersInterval}ms`
|
|
|
|
|
);
|
2024-06-19 01:52:16 -04:00
|
|
|
void this.startMaintainPeersInterval(maintainPeersInterval);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-19 15:58:17 +05:30
|
|
|
public get connectedPeers(): Peer[] {
|
2024-10-11 03:17:12 +05:30
|
|
|
return this.peerManager.getPeers();
|
2024-06-19 01:52:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Disconnects from a peer and tries to find a new one to replace it.
|
|
|
|
|
* @param peerToDisconnect The peer to disconnect from.
|
2024-07-10 15:34:16 +05:30
|
|
|
* @returns The new peer that was found and connected to.
|
2024-06-19 01:52:16 -04:00
|
|
|
*/
|
2024-10-11 03:17:12 +05:30
|
|
|
public async renewPeer(peerToDisconnect: PeerId): Promise<Peer | undefined> {
|
|
|
|
|
this.log.info(`Attempting to renew peer ${peerToDisconnect}`);
|
2024-07-10 15:34:16 +05:30
|
|
|
|
2024-10-11 03:17:12 +05:30
|
|
|
const newPeer = await this.peerManager.findPeers(1);
|
|
|
|
|
if (newPeer.length === 0) {
|
|
|
|
|
this.log.error(
|
|
|
|
|
"Failed to find a new peer to replace the disconnected one"
|
|
|
|
|
);
|
|
|
|
|
return undefined;
|
2024-06-19 01:52:16 -04:00
|
|
|
}
|
2024-07-10 15:34:16 +05:30
|
|
|
|
2024-10-11 03:17:12 +05:30
|
|
|
await this.peerManager.removePeer(peerToDisconnect);
|
|
|
|
|
await this.peerManager.addPeer(newPeer[0]);
|
2024-07-27 18:27:54 +05:30
|
|
|
|
2024-10-11 03:17:12 +05:30
|
|
|
this.log.info(`Successfully renewed peer. New peer: ${newPeer[0].id}`);
|
2024-07-16 18:35:24 +02:00
|
|
|
|
2024-10-11 03:17:12 +05:30
|
|
|
return newPeer[0];
|
2024-06-19 01:52:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Stops the maintain peers interval.
|
|
|
|
|
*/
|
|
|
|
|
public stopMaintainPeersInterval(): void {
|
|
|
|
|
if (this.maintainPeersIntervalId) {
|
|
|
|
|
clearInterval(this.maintainPeersIntervalId);
|
|
|
|
|
this.maintainPeersIntervalId = null;
|
|
|
|
|
this.log.info("Maintain peers interval stopped");
|
2024-10-11 03:17:12 +05:30
|
|
|
} else {
|
|
|
|
|
this.log.info("Maintain peers interval was not running");
|
2024-06-19 01:52:16 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2024-10-11 03:17:12 +05:30
|
|
|
* Checks if there are sufficient peers to send a message to.
|
|
|
|
|
* If `forceUseAllPeers` is `false` (default), returns `true` if there are any connected peers.
|
|
|
|
|
* If `forceUseAllPeers` is `true`, attempts to connect to `numPeersToUse` peers.
|
2024-06-19 01:52:16 -04:00
|
|
|
* @param options Optional options object
|
2024-10-11 03:17:12 +05:30
|
|
|
* @param options.forceUseAllPeers Optional flag to force connecting to `numPeersToUse` peers (default: false)
|
|
|
|
|
* @param options.maxAttempts Optional maximum number of attempts to reach the required number of peers (default: 3)
|
|
|
|
|
* @returns `true` if the required number of peers are connected, `false` otherwise
|
2024-06-19 01:52:16 -04:00
|
|
|
*/
|
2024-10-11 03:17:12 +05:30
|
|
|
protected async hasPeers(
|
2024-07-03 12:09:34 +05:30
|
|
|
options: Partial<ProtocolUseOptions> = {}
|
2024-10-11 03:17:12 +05:30
|
|
|
): Promise<boolean> {
|
|
|
|
|
const { forceUseAllPeers = false, maxAttempts = 3 } = options;
|
2024-06-19 01:52:16 -04:00
|
|
|
|
2024-10-11 03:17:12 +05:30
|
|
|
this.log.info(
|
|
|
|
|
`Checking for peers. forceUseAllPeers: ${forceUseAllPeers}, maxAttempts: ${maxAttempts}`
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
for (let attempts = 0; attempts < maxAttempts; attempts++) {
|
|
|
|
|
this.log.info(
|
|
|
|
|
`Attempt ${attempts + 1}/${maxAttempts} to reach required number of peers`
|
|
|
|
|
);
|
|
|
|
|
await this.maintainPeers();
|
2024-06-19 01:52:16 -04:00
|
|
|
|
2024-10-11 03:17:12 +05:30
|
|
|
if (!forceUseAllPeers && this.connectedPeers.length > 0) {
|
|
|
|
|
this.log.info(
|
|
|
|
|
`At least one peer connected (${this.connectedPeers.length}), not forcing use of all peers`
|
|
|
|
|
);
|
2024-06-19 01:52:16 -04:00
|
|
|
return true;
|
|
|
|
|
}
|
2024-10-11 03:17:12 +05:30
|
|
|
|
|
|
|
|
if (this.connectedPeers.length >= this.numPeersToUse) {
|
|
|
|
|
this.log.info(
|
|
|
|
|
`Required number of peers (${this.numPeersToUse}) reached`
|
|
|
|
|
);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.log.warn(
|
|
|
|
|
`Found only ${this.connectedPeers.length}/${this.numPeersToUse} required peers. Retrying...`
|
2024-06-19 01:52:16 -04:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-11 03:17:12 +05:30
|
|
|
this.log.error(
|
|
|
|
|
`Failed to find required number of peers (${this.numPeersToUse}) after ${maxAttempts} attempts`
|
|
|
|
|
);
|
2024-06-19 01:52:16 -04:00
|
|
|
return false;
|
2024-10-11 03:17:12 +05:30
|
|
|
}
|
2024-06-19 01:52:16 -04:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Starts an interval to maintain the peers list to `numPeersToUse`.
|
|
|
|
|
* @param interval The interval in milliseconds to maintain the peers.
|
|
|
|
|
*/
|
|
|
|
|
private async startMaintainPeersInterval(interval: number): Promise<void> {
|
2024-10-11 03:17:12 +05:30
|
|
|
this.log.info(
|
|
|
|
|
`Starting maintain peers interval with ${interval}ms interval`
|
|
|
|
|
);
|
2024-06-19 01:52:16 -04:00
|
|
|
try {
|
|
|
|
|
this.maintainPeersIntervalId = setInterval(() => {
|
2024-10-11 03:17:12 +05:30
|
|
|
this.log.info("Running scheduled peer maintenance");
|
2024-06-19 01:52:16 -04:00
|
|
|
this.maintainPeers().catch((error) => {
|
2024-10-11 03:17:12 +05:30
|
|
|
this.log.error("Error during scheduled peer maintenance:", error);
|
2024-06-19 01:52:16 -04:00
|
|
|
});
|
|
|
|
|
}, interval);
|
2024-10-11 03:17:12 +05:30
|
|
|
this.log.info("Maintain peers interval started successfully");
|
2024-06-19 01:52:16 -04:00
|
|
|
} catch (error) {
|
|
|
|
|
this.log.error("Error starting maintain peers interval:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Maintains the peers list to `numPeersToUse`.
|
|
|
|
|
*/
|
2024-10-11 03:17:12 +05:30
|
|
|
private async maintainPeers(): Promise<void> {
|
2024-06-19 01:52:16 -04:00
|
|
|
try {
|
2024-10-11 03:17:12 +05:30
|
|
|
const currentPeerCount = await this.peerManager.getPeerCount();
|
|
|
|
|
const numPeersToAdd = this.numPeersToUse - currentPeerCount;
|
2024-07-27 18:27:54 +05:30
|
|
|
|
2024-06-19 01:52:16 -04:00
|
|
|
this.log.info(
|
2024-10-11 03:17:12 +05:30
|
|
|
`Current peer count: ${currentPeerCount}, target: ${this.numPeersToUse}`
|
2024-06-19 01:52:16 -04:00
|
|
|
);
|
|
|
|
|
|
2024-10-11 03:17:12 +05:30
|
|
|
if (numPeersToAdd === 0) {
|
|
|
|
|
this.log.info("Peer count is at target, no maintenance required");
|
|
|
|
|
return;
|
2024-06-19 01:52:16 -04:00
|
|
|
}
|
|
|
|
|
|
2024-10-11 03:17:12 +05:30
|
|
|
if (numPeersToAdd > 0) {
|
|
|
|
|
this.log.info(`Attempting to add ${numPeersToAdd} peer(s)`);
|
|
|
|
|
await this.peerManager.findAndAddPeers(numPeersToAdd);
|
|
|
|
|
} else {
|
|
|
|
|
this.log.info(
|
|
|
|
|
`Attempting to remove ${Math.abs(numPeersToAdd)} excess peer(s)`
|
|
|
|
|
);
|
|
|
|
|
await this.peerManager.removeExcessPeers(Math.abs(numPeersToAdd));
|
|
|
|
|
}
|
2024-07-16 18:35:24 +02:00
|
|
|
|
2024-10-11 03:17:12 +05:30
|
|
|
const finalPeerCount = await this.peerManager.getPeerCount();
|
|
|
|
|
this.log.info(
|
|
|
|
|
`Peer maintenance completed. Initial count: ${currentPeerCount}, Final count: ${finalPeerCount}`
|
|
|
|
|
);
|
2024-06-19 01:52:16 -04:00
|
|
|
} catch (error) {
|
2024-10-11 03:17:12 +05:30
|
|
|
this.log.error("Error during peer maintenance", { error });
|
2024-07-16 18:35:24 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|