From ea33b9cd8a97cad192712feedca120d2b7138f25 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Thu, 22 Jul 2021 16:34:27 +1000 Subject: [PATCH 1/2] Rename `keepAlive` option to `pingKeepAlive` In preparation for introducing a relay keep alive feature. --- .cspell.json | 1 + CHANGELOG.md | 1 + src/lib/waku.ts | 32 ++++++++++++++++---------------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/.cspell.json b/.cspell.json index 05cefddfe4..332c5586ee 100644 --- a/.cspell.json +++ b/.cspell.json @@ -35,6 +35,7 @@ "ihave", "ihaves", "ineed", + "ipfs", "iwant", "jdev", "keccak", diff --git a/CHANGELOG.md b/CHANGELOG.md index da07485998..ce920c0759 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - **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`. ### Fixed - Align `WakuMessage` readme example with actual code behaviour. diff --git a/src/lib/waku.ts b/src/lib/waku.ts index ea2f93f23b..9e24168fc9 100644 --- a/src/lib/waku.ts +++ b/src/lib/waku.ts @@ -37,12 +37,12 @@ export interface CreateOptions { */ pubsubTopic?: string; /** - * Set keep alive frequency in seconds: Waku will send a ping request to each peer - * after the set number of seconds. Set to 0 to disable the keep alive feature + * 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 */ - keepAlive?: number; + pingKeepAlive?: 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) @@ -67,7 +67,7 @@ export class Waku { public store: WakuStore; public lightPush: WakuLightPush; - private keepAliveTimers: { + private pingKeepAliveTimers: { [peer: string]: ReturnType; }; @@ -81,19 +81,19 @@ export class Waku { this.relay = libp2p.pubsub as unknown as WakuRelay; this.store = store; this.lightPush = lightPush; - this.keepAliveTimers = {}; + this.pingKeepAliveTimers = {}; - const keepAlive = options.keepAlive || 0; + const pingKeepAlive = options.pingKeepAlive || 0; - if (keepAlive !== 0) { + if (pingKeepAlive !== 0) { libp2p.connectionManager.on('peer:connect', (connection: Connection) => { - this.startKeepAlive(connection.remotePeer, keepAlive); + this.startPingKeepAlive(connection.remotePeer, pingKeepAlive); }); libp2p.connectionManager.on( 'peer:disconnect', (connection: Connection) => { - this.stopKeepAlive(connection.remotePeer); + this.stopPingKeepAlive(connection.remotePeer); } ); } @@ -214,21 +214,21 @@ export class Waku { return localMultiaddr + '/p2p/' + this.libp2p.peerId.toB58String(); } - private startKeepAlive(peerId: PeerId, periodSecs: number): void { + private startPingKeepAlive(peerId: PeerId, periodSecs: number): void { // Just in case a timer already exist for this peer - this.stopKeepAlive(peerId); + this.stopPingKeepAlive(peerId); const peerIdStr = peerId.toB58String(); - this.keepAliveTimers[peerIdStr] = setInterval(() => { + this.pingKeepAliveTimers[peerIdStr] = setInterval(() => { Ping(this.libp2p, peerId); }, periodSecs * 1000); } - private stopKeepAlive(peerId: PeerId): void { + private stopPingKeepAlive(peerId: PeerId): void { const peerIdStr = peerId.toB58String(); - if (this.keepAliveTimers[peerIdStr]) { - clearInterval(this.keepAliveTimers[peerIdStr]); - delete this.keepAliveTimers[peerIdStr]; + if (this.pingKeepAliveTimers[peerIdStr]) { + clearInterval(this.pingKeepAliveTimers[peerIdStr]); + delete this.pingKeepAliveTimers[peerIdStr]; } } } From 9638f6db10882224a370e6293f509596742be0d0 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Tue, 27 Jul 2021 15:55:11 +1000 Subject: [PATCH 2/2] 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