UNPKG

@waku/core

Version:

TypeScript implementation of the Waku v2 protocol

148 lines 5.73 kB
import { Logger, pubsubTopicToSingleShardInfo } from "@waku/utils"; import { utf8ToBytes } from "@waku/utils/bytes"; import { createEncoder } from "../message/version_0.js"; const RelayPingContentTopic = "/relay-ping/1/ping/null"; const log = new Logger("keep-alive"); export class KeepAliveManager { relay; libp2p; options; pingKeepAliveTimers = new Map(); relayKeepAliveTimers = new Map(); constructor({ options, relay, libp2p }) { this.options = options; this.relay = relay; this.libp2p = libp2p; this.onPeerConnect = this.onPeerConnect.bind(this); this.onPeerDisconnect = this.onPeerDisconnect.bind(this); } start() { this.libp2p.addEventListener("peer:connect", this.onPeerConnect); this.libp2p.addEventListener("peer:disconnect", this.onPeerDisconnect); } stop() { this.libp2p.removeEventListener("peer:connect", this.onPeerConnect); this.libp2p.removeEventListener("peer:disconnect", this.onPeerDisconnect); for (const timer of this.pingKeepAliveTimers.values()) { clearInterval(timer); } for (const timerArray of this.relayKeepAliveTimers.values()) { for (const timer of timerArray) { clearInterval(timer); } } this.pingKeepAliveTimers.clear(); this.relayKeepAliveTimers.clear(); } onPeerConnect(evt) { const peerId = evt.detail; this.startPingForPeer(peerId); } onPeerDisconnect(evt) { const peerId = evt.detail; this.stopPingForPeer(peerId); } startPingForPeer(peerId) { // Just in case a timer already exists for this peer this.stopPingForPeer(peerId); this.startLibp2pPing(peerId); this.startRelayPing(peerId); } stopPingForPeer(peerId) { this.stopLibp2pPing(peerId); this.stopRelayPing(peerId); } startLibp2pPing(peerId) { if (this.options.pingKeepAlive === 0) { log.warn(`Ping keep alive is disabled pingKeepAlive:${this.options.pingKeepAlive}, skipping start for libp2p ping`); return; } const peerIdStr = peerId.toString(); if (this.pingKeepAliveTimers.has(peerIdStr)) { log.warn(`Ping already started for peer: ${peerIdStr}, skipping start for libp2p ping`); return; } const interval = setInterval(() => { void this.pingLibp2p(peerId); }, this.options.pingKeepAlive * 1000); this.pingKeepAliveTimers.set(peerIdStr, interval); } stopLibp2pPing(peerId) { const peerIdStr = peerId.toString(); if (!this.pingKeepAliveTimers.has(peerIdStr)) { log.warn(`Ping not started for peer: ${peerIdStr}, skipping stop for ping`); return; } clearInterval(this.pingKeepAliveTimers.get(peerIdStr)); this.pingKeepAliveTimers.delete(peerIdStr); } startRelayPing(peerId) { if (!this.relay) { return; } if (this.options.relayKeepAlive === 0) { log.warn(`Relay keep alive is disabled relayKeepAlive:${this.options.relayKeepAlive}, skipping start for relay ping`); return; } if (this.relayKeepAliveTimers.has(peerId.toString())) { log.warn(`Relay ping already started for peer: ${peerId.toString()}, skipping start for relay ping`); return; } const intervals = []; for (const topic of this.relay.pubsubTopics) { const meshPeers = this.relay.getMeshPeers(topic); if (!meshPeers.includes(peerId.toString())) { log.warn(`Peer: ${peerId.toString()} is not in the mesh for topic: ${topic}, skipping start for relay ping`); continue; } const encoder = createEncoder({ pubsubTopicShardInfo: pubsubTopicToSingleShardInfo(topic), contentTopic: RelayPingContentTopic, ephemeral: true }); const interval = setInterval(() => { void this.pingRelay(encoder); }, this.options.relayKeepAlive * 1000); intervals.push(interval); } this.relayKeepAliveTimers.set(peerId.toString(), intervals); } stopRelayPing(peerId) { if (!this.relay) { return; } const peerIdStr = peerId.toString(); if (!this.relayKeepAliveTimers.has(peerIdStr)) { log.warn(`Relay ping not started for peer: ${peerIdStr}, skipping stop for relay ping`); return; } this.relayKeepAliveTimers.get(peerIdStr)?.map(clearInterval); this.relayKeepAliveTimers.delete(peerIdStr); } async pingRelay(encoder) { try { log.info("Sending Waku Relay ping message"); await this.relay.send(encoder, { payload: new Uint8Array([1]) }); } catch (e) { log.error("Failed to send relay ping", e); } } async pingLibp2p(peerId) { try { log.info(`Pinging libp2p peer (${peerId.toString()})`); const ping = await this.libp2p.services.ping.ping(peerId); log.info(`Ping succeeded (${peerId.toString()})`, ping); await this.libp2p.peerStore.merge(peerId, { metadata: { ping: utf8ToBytes(ping.toString()) } }); log.info(`Ping updated for peer (${peerId.toString()})`); } catch (e) { log.error(`Ping failed for peer (${peerId.toString()})`, e); } } } //# sourceMappingURL=keep_alive_manager.js.map