From 9638f6db10882224a370e6293f509596742be0d0 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Tue, 27 Jul 2021 15:55:11 +1000 Subject: [PATCH] Introduced new `relayKeepAlive` option ..on `Waku` with a default to 5min to send ping messages over relay to ensure the relay stream stays open. This is a workaround until [js-libp2p#744](https://github.com/libp2p/js-libp2p/issues/744) is done as there are issues when TCP(?) timeouts and the stream gets closed. --- CHANGELOG.md | 3 ++ src/lib/waku.ts | 73 ++++++++++++++++++++++++--------- src/lib/waku_relay/constants.ts | 2 + 3 files changed, 59 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce920c0759..d558f83bb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Breaking**: Renamed `WakuRelay.(add|delete)PrivateDecryptionKey` to `WakuRelay.(add|delete)DecryptionKey` to make it clearer that it accepts both symmetric keys and asymmetric private keys. - Upgrade libp2p to 0.32.0. - **Breaking**: Rename `keepAlive` option to `pingKeepAlive`. +- Introduced new `relayKeepAlive` option on `Waku` with a default to 59s to send ping messages over relay to ensure the relay stream stays open. + This is a workaround until [js-libp2p#744](https://github.com/libp2p/js-libp2p/issues/744) is done as there are issues when TCP(?) timeouts and the stream gets closed + ([#185](https://github.com/status-im/js-waku/issues/185), [js-libp2p#939](https://github.com/libp2p/js-libp2p/issues/939)) ### Fixed - Align `WakuMessage` readme example with actual code behaviour. diff --git a/src/lib/waku.ts b/src/lib/waku.ts index 9e24168fc9..5369e4a0c0 100644 --- a/src/lib/waku.ts +++ b/src/lib/waku.ts @@ -16,11 +16,16 @@ import { Multiaddr, multiaddr } from 'multiaddr'; import PeerId from 'peer-id'; import { WakuLightPush } from './waku_light_push'; +import { WakuMessage } from './waku_message'; import { RelayCodecs, WakuRelay } from './waku_relay'; +import { RelayPingContentTopic } from './waku_relay/constants'; import { StoreCodec, WakuStore } from './waku_store'; const websocketsTransportKey = Websockets.prototype[Symbol.toStringTag]; +const DefaultPingKeepAliveValueSecs = 0; +const DefaultRelayKeepAliveValueSecs = 5 * 60; + export interface CreateOptions { /** * The PubSub Topic to use. Defaults to {@link DefaultPubsubTopic}. @@ -40,9 +45,16 @@ export interface CreateOptions { * Set keep alive frequency in seconds: Waku will send a `/ipfs/ping/1.0.0` * request to each peer after the set number of seconds. Set to 0 to disable. * - * @default 0 + * @default {@link DefaultPingKeepAliveValueSecs} */ pingKeepAlive?: number; + /** + * Set keep alive frequency in seconds: Waku will send a ping message over + * relay to each peer after the set number of seconds. Set to 0 to disable. + * + * @default {@link DefaultRelayKeepAliveValueSecs} + */ + relayKeepAlive?: number; /** * You can pass options to the `Libp2p` instance used by {@link Waku} using the {@link CreateOptions.libp2p} property. * This property is the same type than the one passed to [`Libp2p.create`](https://github.com/libp2p/js-libp2p/blob/master/doc/API.md#create) @@ -70,6 +82,9 @@ export class Waku { private pingKeepAliveTimers: { [peer: string]: ReturnType; }; + private relayKeepAliveTimers: { + [peer: string]: ReturnType; + }; private constructor( options: CreateOptions, @@ -82,21 +97,20 @@ export class Waku { this.store = store; this.lightPush = lightPush; this.pingKeepAliveTimers = {}; + this.relayKeepAliveTimers = {}; - const pingKeepAlive = options.pingKeepAlive || 0; + const pingKeepAlive = + options.pingKeepAlive || DefaultPingKeepAliveValueSecs; + const relayKeepAlive = + options.relayKeepAlive || DefaultRelayKeepAliveValueSecs; - if (pingKeepAlive !== 0) { - libp2p.connectionManager.on('peer:connect', (connection: Connection) => { - this.startPingKeepAlive(connection.remotePeer, pingKeepAlive); - }); + libp2p.connectionManager.on('peer:connect', (connection: Connection) => { + this.startKeepAlive(connection.remotePeer, pingKeepAlive, relayKeepAlive); + }); - libp2p.connectionManager.on( - 'peer:disconnect', - (connection: Connection) => { - this.stopPingKeepAlive(connection.remotePeer); - } - ); - } + libp2p.connectionManager.on('peer:disconnect', (connection: Connection) => { + this.stopKeepAlive(connection.remotePeer); + }); } /** @@ -214,21 +228,42 @@ export class Waku { return localMultiaddr + '/p2p/' + this.libp2p.peerId.toB58String(); } - private startPingKeepAlive(peerId: PeerId, periodSecs: number): void { + private startKeepAlive( + peerId: PeerId, + pingPeriodSecs: number, + relayPeriodSecs: number + ): void { // Just in case a timer already exist for this peer - this.stopPingKeepAlive(peerId); + this.stopKeepAlive(peerId); const peerIdStr = peerId.toB58String(); - this.pingKeepAliveTimers[peerIdStr] = setInterval(() => { - Ping(this.libp2p, peerId); - }, periodSecs * 1000); + + if (pingPeriodSecs !== 0) { + this.pingKeepAliveTimers[peerIdStr] = setInterval(() => { + Ping(this.libp2p, peerId); + }, pingPeriodSecs * 1000); + } + + if (relayPeriodSecs !== 0) { + this.relayKeepAliveTimers[peerIdStr] = setInterval(() => { + WakuMessage.fromBytes(new Uint8Array(), { + contentTopic: RelayPingContentTopic, + }).then((wakuMsg) => this.relay.send(wakuMsg)); + }, relayPeriodSecs * 1000); + } } - private stopPingKeepAlive(peerId: PeerId): void { + private stopKeepAlive(peerId: PeerId): void { const peerIdStr = peerId.toB58String(); + if (this.pingKeepAliveTimers[peerIdStr]) { clearInterval(this.pingKeepAliveTimers[peerIdStr]); delete this.pingKeepAliveTimers[peerIdStr]; } + + if (this.relayKeepAliveTimers[peerIdStr]) { + clearInterval(this.relayKeepAliveTimers[peerIdStr]); + delete this.relayKeepAliveTimers[peerIdStr]; + } } } diff --git a/src/lib/waku_relay/constants.ts b/src/lib/waku_relay/constants.ts index b4b17fe4ea..d67481ddf1 100644 --- a/src/lib/waku_relay/constants.ts +++ b/src/lib/waku_relay/constants.ts @@ -14,6 +14,8 @@ export const RelayCodecs = [ */ export const DefaultPubsubTopic = '/waku/2/default-waku/proto'; +export const RelayPingContentTopic = '/relay-ping/1/ping/null'; + /** * RelayGossipFactor affects how many peers we will emit gossip to at each heartbeat. * We will send gossip to RelayGossipFactor * (total number of non-mesh peers), or