mirror of
https://github.com/waku-org/js-waku.git
synced 2025-01-12 21:44:33 +00:00
chore(lightpush)!: move protocol implementation to @waku/sdk
(1/n) (#1964)
* chore: decouple `Filter` between `core` and `sdk` moves `SubscriptionManager` to `sdk` side * chore: update package dependencies also update peer deps in sdk * chore: update imports * chore: update tests * chore(side-change): update lightpush * chore: update size-limit import * chore(sdk): update dependencies
This commit is contained in:
parent
e5e8cd5e17
commit
5fb100602b
@ -44,7 +44,7 @@ module.exports = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Waku Filter",
|
name: "Waku Filter",
|
||||||
path: "packages/core/bundle/index.js",
|
path: "packages/sdk/bundle/index.js",
|
||||||
import: "{ wakuFilter }",
|
import: "{ wakuFilter }",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
72
package-lock.json
generated
72
package-lock.json
generated
@ -36494,15 +36494,14 @@
|
|||||||
},
|
},
|
||||||
"packages/core": {
|
"packages/core": {
|
||||||
"name": "@waku/core",
|
"name": "@waku/core",
|
||||||
"version": "0.0.27",
|
"version": "0.0.28",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@libp2p/ping": "^1.0.12",
|
"@libp2p/ping": "^1.0.12",
|
||||||
"@waku/enr": "^0.0.21",
|
"@waku/enr": "^0.0.22",
|
||||||
"@waku/interfaces": "0.0.22",
|
"@waku/interfaces": "0.0.23",
|
||||||
"@waku/message-hash": "^0.1.11",
|
|
||||||
"@waku/proto": "0.0.6",
|
"@waku/proto": "0.0.6",
|
||||||
"@waku/utils": "0.0.15",
|
"@waku/utils": "0.0.16",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"it-all": "^3.0.4",
|
"it-all": "^3.0.4",
|
||||||
"it-length-prefixed": "^9.0.4",
|
"it-length-prefixed": "^9.0.4",
|
||||||
@ -36538,7 +36537,6 @@
|
|||||||
"@multiformats/multiaddr": "^12.0.0",
|
"@multiformats/multiaddr": "^12.0.0",
|
||||||
"@waku/enr": "^0.0.21",
|
"@waku/enr": "^0.0.21",
|
||||||
"@waku/interfaces": "0.0.22",
|
"@waku/interfaces": "0.0.22",
|
||||||
"@waku/message-hash": "^0.1.11",
|
|
||||||
"@waku/proto": "0.0.6",
|
"@waku/proto": "0.0.6",
|
||||||
"@waku/utils": "0.0.15",
|
"@waku/utils": "0.0.15",
|
||||||
"libp2p": "^1.1.2"
|
"libp2p": "^1.1.2"
|
||||||
@ -36557,11 +36555,11 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@waku/core": "0.0.27",
|
"@waku/core": "0.0.28",
|
||||||
"@waku/enr": "0.0.21",
|
"@waku/enr": "0.0.22",
|
||||||
"@waku/interfaces": "0.0.22",
|
"@waku/interfaces": "0.0.23",
|
||||||
"@waku/proto": "^0.0.6",
|
"@waku/proto": "^0.0.6",
|
||||||
"@waku/utils": "0.0.15",
|
"@waku/utils": "0.0.16",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"dns-query": "^0.11.2",
|
"dns-query": "^0.11.2",
|
||||||
"hi-base32": "^0.5.1",
|
"hi-base32": "^0.5.1",
|
||||||
@ -36608,7 +36606,7 @@
|
|||||||
},
|
},
|
||||||
"packages/enr": {
|
"packages/enr": {
|
||||||
"name": "@waku/enr",
|
"name": "@waku/enr",
|
||||||
"version": "0.0.21",
|
"version": "0.0.22",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ethersproject/rlp": "^5.7.0",
|
"@ethersproject/rlp": "^5.7.0",
|
||||||
@ -36616,7 +36614,7 @@
|
|||||||
"@libp2p/peer-id": "^4.0.4",
|
"@libp2p/peer-id": "^4.0.4",
|
||||||
"@multiformats/multiaddr": "^12.0.0",
|
"@multiformats/multiaddr": "^12.0.0",
|
||||||
"@noble/secp256k1": "^1.7.1",
|
"@noble/secp256k1": "^1.7.1",
|
||||||
"@waku/utils": "0.0.15",
|
"@waku/utils": "0.0.16",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"js-sha3": "^0.9.2"
|
"js-sha3": "^0.9.2"
|
||||||
},
|
},
|
||||||
@ -36628,7 +36626,7 @@
|
|||||||
"@types/chai": "^4.3.11",
|
"@types/chai": "^4.3.11",
|
||||||
"@types/mocha": "^10.0.6",
|
"@types/mocha": "^10.0.6",
|
||||||
"@waku/build-utils": "*",
|
"@waku/build-utils": "*",
|
||||||
"@waku/interfaces": "0.0.22",
|
"@waku/interfaces": "0.0.23",
|
||||||
"chai": "^4.3.10",
|
"chai": "^4.3.10",
|
||||||
"cspell": "^8.6.1",
|
"cspell": "^8.6.1",
|
||||||
"fast-check": "^3.15.1",
|
"fast-check": "^3.15.1",
|
||||||
@ -36657,7 +36655,7 @@
|
|||||||
},
|
},
|
||||||
"packages/interfaces": {
|
"packages/interfaces": {
|
||||||
"name": "@waku/interfaces",
|
"name": "@waku/interfaces",
|
||||||
"version": "0.0.22",
|
"version": "0.0.23",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@waku/proto": "^0.0.6"
|
"@waku/proto": "^0.0.6"
|
||||||
@ -36675,14 +36673,14 @@
|
|||||||
},
|
},
|
||||||
"packages/message-encryption": {
|
"packages/message-encryption": {
|
||||||
"name": "@waku/message-encryption",
|
"name": "@waku/message-encryption",
|
||||||
"version": "0.0.25",
|
"version": "0.0.26",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@noble/secp256k1": "^1.7.1",
|
"@noble/secp256k1": "^1.7.1",
|
||||||
"@waku/core": "0.0.27",
|
"@waku/core": "0.0.28",
|
||||||
"@waku/interfaces": "0.0.22",
|
"@waku/interfaces": "0.0.23",
|
||||||
"@waku/proto": "0.0.6",
|
"@waku/proto": "0.0.6",
|
||||||
"@waku/utils": "0.0.15",
|
"@waku/utils": "0.0.16",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"js-sha3": "^0.9.2",
|
"js-sha3": "^0.9.2",
|
||||||
"uint8arrays": "^5.0.1"
|
"uint8arrays": "^5.0.1"
|
||||||
@ -36719,11 +36717,11 @@
|
|||||||
},
|
},
|
||||||
"packages/message-hash": {
|
"packages/message-hash": {
|
||||||
"name": "@waku/message-hash",
|
"name": "@waku/message-hash",
|
||||||
"version": "0.1.11",
|
"version": "0.1.12",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@noble/hashes": "^1.3.2",
|
"@noble/hashes": "^1.3.2",
|
||||||
"@waku/utils": "0.0.15"
|
"@waku/utils": "0.0.16"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-commonjs": "^25.0.7",
|
"@rollup/plugin-commonjs": "^25.0.7",
|
||||||
@ -36733,7 +36731,7 @@
|
|||||||
"@types/debug": "^4.1.12",
|
"@types/debug": "^4.1.12",
|
||||||
"@types/mocha": "^10.0.6",
|
"@types/mocha": "^10.0.6",
|
||||||
"@waku/build-utils": "*",
|
"@waku/build-utils": "*",
|
||||||
"@waku/interfaces": "0.0.22",
|
"@waku/interfaces": "0.0.23",
|
||||||
"chai": "^4.3.10",
|
"chai": "^4.3.10",
|
||||||
"cspell": "^8.6.1",
|
"cspell": "^8.6.1",
|
||||||
"fast-check": "^3.15.1",
|
"fast-check": "^3.15.1",
|
||||||
@ -36801,15 +36799,15 @@
|
|||||||
},
|
},
|
||||||
"packages/relay": {
|
"packages/relay": {
|
||||||
"name": "@waku/relay",
|
"name": "@waku/relay",
|
||||||
"version": "0.0.10",
|
"version": "0.0.11",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chainsafe/libp2p-gossipsub": "^12.0.0",
|
"@chainsafe/libp2p-gossipsub": "^12.0.0",
|
||||||
"@noble/hashes": "^1.3.2",
|
"@noble/hashes": "^1.3.2",
|
||||||
"@waku/core": "0.0.27",
|
"@waku/core": "0.0.28",
|
||||||
"@waku/interfaces": "0.0.22",
|
"@waku/interfaces": "0.0.23",
|
||||||
"@waku/proto": "0.0.6",
|
"@waku/proto": "0.0.6",
|
||||||
"@waku/utils": "0.0.15",
|
"@waku/utils": "0.0.16",
|
||||||
"chai": "^4.3.10",
|
"chai": "^4.3.10",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"fast-check": "^3.15.1"
|
"fast-check": "^3.15.1"
|
||||||
@ -36842,7 +36840,7 @@
|
|||||||
},
|
},
|
||||||
"packages/sdk": {
|
"packages/sdk": {
|
||||||
"name": "@waku/sdk",
|
"name": "@waku/sdk",
|
||||||
"version": "0.0.23",
|
"version": "0.0.24",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chainsafe/libp2p-noise": "^14.1.0",
|
"@chainsafe/libp2p-noise": "^14.1.0",
|
||||||
@ -36852,11 +36850,12 @@
|
|||||||
"@libp2p/ping": "^1.0.12",
|
"@libp2p/ping": "^1.0.12",
|
||||||
"@libp2p/websockets": "^8.0.11",
|
"@libp2p/websockets": "^8.0.11",
|
||||||
"@noble/hashes": "^1.3.3",
|
"@noble/hashes": "^1.3.3",
|
||||||
"@waku/core": "0.0.27",
|
"@waku/core": "0.0.28",
|
||||||
"@waku/discovery": "0.0.1",
|
"@waku/discovery": "0.0.1",
|
||||||
"@waku/interfaces": "0.0.22",
|
"@waku/interfaces": "0.0.23",
|
||||||
"@waku/relay": "0.0.10",
|
"@waku/proto": "^0.0.6",
|
||||||
"@waku/utils": "0.0.15",
|
"@waku/relay": "0.0.11",
|
||||||
|
"@waku/utils": "0.0.16",
|
||||||
"libp2p": "^1.1.2"
|
"libp2p": "^1.1.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -36876,10 +36875,11 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@libp2p/bootstrap": "^10",
|
"@libp2p/bootstrap": "^10",
|
||||||
"@waku/core": "0.0.27",
|
"@waku/core": "0.0.28",
|
||||||
"@waku/interfaces": "0.0.22",
|
"@waku/interfaces": "0.0.23",
|
||||||
"@waku/relay": "0.0.10",
|
"@waku/message-hash": "^0.1.12",
|
||||||
"@waku/utils": "0.0.15"
|
"@waku/relay": "0.0.11",
|
||||||
|
"@waku/utils": "0.0.16"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@libp2p/bootstrap": {
|
"@libp2p/bootstrap": {
|
||||||
@ -36940,11 +36940,11 @@
|
|||||||
},
|
},
|
||||||
"packages/utils": {
|
"packages/utils": {
|
||||||
"name": "@waku/utils",
|
"name": "@waku/utils",
|
||||||
"version": "0.0.15",
|
"version": "0.0.16",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@noble/hashes": "^1.3.2",
|
"@noble/hashes": "^1.3.2",
|
||||||
"@waku/interfaces": "0.0.22",
|
"@waku/interfaces": "0.0.23",
|
||||||
"chai": "^4.3.10",
|
"chai": "^4.3.10",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"uint8arrays": "^5.0.1"
|
"uint8arrays": "^5.0.1"
|
||||||
|
@ -75,7 +75,6 @@
|
|||||||
"@libp2p/ping": "^1.0.12",
|
"@libp2p/ping": "^1.0.12",
|
||||||
"@waku/enr": "^0.0.22",
|
"@waku/enr": "^0.0.22",
|
||||||
"@waku/interfaces": "0.0.23",
|
"@waku/interfaces": "0.0.23",
|
||||||
"@waku/message-hash": "^0.1.12",
|
|
||||||
"@waku/proto": "0.0.6",
|
"@waku/proto": "0.0.6",
|
||||||
"@waku/utils": "0.0.16",
|
"@waku/utils": "0.0.16",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
@ -111,7 +110,6 @@
|
|||||||
"libp2p": "^1.1.2",
|
"libp2p": "^1.1.2",
|
||||||
"@waku/enr": "^0.0.21",
|
"@waku/enr": "^0.0.21",
|
||||||
"@waku/interfaces": "0.0.22",
|
"@waku/interfaces": "0.0.22",
|
||||||
"@waku/message-hash": "^0.1.11",
|
|
||||||
"@waku/proto": "0.0.6",
|
"@waku/proto": "0.0.6",
|
||||||
"@waku/utils": "0.0.15"
|
"@waku/utils": "0.0.15"
|
||||||
},
|
},
|
||||||
|
@ -7,7 +7,7 @@ export type {
|
|||||||
export * as message from "./lib/message/index.js";
|
export * as message from "./lib/message/index.js";
|
||||||
|
|
||||||
export * as waku_filter from "./lib/filter/index.js";
|
export * as waku_filter from "./lib/filter/index.js";
|
||||||
export { wakuFilter, FilterCodecs } from "./lib/filter/index.js";
|
export { FilterCore, FilterCodecs } from "./lib/filter/index.js";
|
||||||
|
|
||||||
export * as waku_light_push from "./lib/light_push/index.js";
|
export * as waku_light_push from "./lib/light_push/index.js";
|
||||||
export { LightPushCodec, LightPushCore } from "./lib/light_push/index.js";
|
export { LightPushCodec, LightPushCore } from "./lib/light_push/index.js";
|
||||||
|
@ -1,30 +1,13 @@
|
|||||||
import { Stream } from "@libp2p/interface";
|
|
||||||
import type { Peer } from "@libp2p/interface";
|
import type { Peer } from "@libp2p/interface";
|
||||||
import type { IncomingStreamData } from "@libp2p/interface-internal";
|
import type { IncomingStreamData } from "@libp2p/interface-internal";
|
||||||
import type {
|
import type {
|
||||||
Callback,
|
|
||||||
ContentTopic,
|
ContentTopic,
|
||||||
IAsyncIterator,
|
IBaseProtocolCore,
|
||||||
IDecodedMessage,
|
|
||||||
IDecoder,
|
|
||||||
IFilter,
|
|
||||||
IProtoMessage,
|
|
||||||
IReceiver,
|
|
||||||
Libp2p,
|
Libp2p,
|
||||||
ProtocolCreateOptions,
|
ProtocolCreateOptions,
|
||||||
PubsubTopic,
|
PubsubTopic
|
||||||
SingleShardInfo,
|
|
||||||
Unsubscribe
|
|
||||||
} from "@waku/interfaces";
|
} from "@waku/interfaces";
|
||||||
import { DefaultPubsubTopic } from "@waku/interfaces";
|
|
||||||
import { messageHashStr } from "@waku/message-hash";
|
|
||||||
import { WakuMessage } from "@waku/proto";
|
import { WakuMessage } from "@waku/proto";
|
||||||
import {
|
|
||||||
ensurePubsubTopicIsConfigured,
|
|
||||||
groupByContentTopic,
|
|
||||||
singleShardInfoToPubsubTopic,
|
|
||||||
toAsyncIterator
|
|
||||||
} from "@waku/utils";
|
|
||||||
import { Logger } from "@waku/utils";
|
import { Logger } from "@waku/utils";
|
||||||
import all from "it-all";
|
import all from "it-all";
|
||||||
import * as lp from "it-length-prefixed";
|
import * as lp from "it-length-prefixed";
|
||||||
@ -40,68 +23,81 @@ import {
|
|||||||
|
|
||||||
const log = new Logger("filter:v2");
|
const log = new Logger("filter:v2");
|
||||||
|
|
||||||
type SubscriptionCallback<T extends IDecodedMessage> = {
|
|
||||||
decoders: IDecoder<T>[];
|
|
||||||
callback: Callback<T>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const FilterCodecs = {
|
export const FilterCodecs = {
|
||||||
SUBSCRIBE: "/vac/waku/filter-subscribe/2.0.0-beta1",
|
SUBSCRIBE: "/vac/waku/filter-subscribe/2.0.0-beta1",
|
||||||
PUSH: "/vac/waku/filter-push/2.0.0-beta1"
|
PUSH: "/vac/waku/filter-push/2.0.0-beta1"
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
export class FilterCore extends BaseProtocol implements IBaseProtocolCore {
|
||||||
* A subscription object refers to a subscription to a given pubsub topic.
|
|
||||||
*/
|
|
||||||
class Subscription {
|
|
||||||
readonly peers: Peer[];
|
|
||||||
private readonly pubsubTopic: PubsubTopic;
|
|
||||||
private newStream: (peer: Peer) => Promise<Stream>;
|
|
||||||
readonly receivedMessagesHashStr: string[] = [];
|
|
||||||
|
|
||||||
private subscriptionCallbacks: Map<
|
|
||||||
ContentTopic,
|
|
||||||
SubscriptionCallback<IDecodedMessage>
|
|
||||||
>;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private handleIncomingMessage: (
|
||||||
pubsubTopic: PubsubTopic,
|
pubsubTopic: PubsubTopic,
|
||||||
remotePeers: Peer[],
|
wakuMessage: WakuMessage
|
||||||
newStream: (peer: Peer) => Promise<Stream>
|
) => Promise<void>,
|
||||||
|
libp2p: Libp2p,
|
||||||
|
options?: ProtocolCreateOptions
|
||||||
) {
|
) {
|
||||||
this.peers = remotePeers;
|
super(
|
||||||
this.pubsubTopic = pubsubTopic;
|
FilterCodecs.SUBSCRIBE,
|
||||||
this.newStream = newStream;
|
libp2p.components,
|
||||||
this.subscriptionCallbacks = new Map();
|
log,
|
||||||
}
|
options!.pubsubTopics!,
|
||||||
|
options
|
||||||
async subscribe<T extends IDecodedMessage>(
|
|
||||||
decoders: IDecoder<T> | IDecoder<T>[],
|
|
||||||
callback: Callback<T>
|
|
||||||
): Promise<void> {
|
|
||||||
const decodersArray = Array.isArray(decoders) ? decoders : [decoders];
|
|
||||||
|
|
||||||
// check that all decoders are configured for the same pubsub topic as this subscription
|
|
||||||
decodersArray.forEach((decoder) => {
|
|
||||||
if (decoder.pubsubTopic !== this.pubsubTopic) {
|
|
||||||
throw new Error(
|
|
||||||
`Pubsub topic not configured: decoder is configured for pubsub topic ${decoder.pubsubTopic} but this subscription is for pubsub topic ${this.pubsubTopic}. Please create a new Subscription for the different pubsub topic.`
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
libp2p.handle(FilterCodecs.PUSH, this.onRequest.bind(this)).catch((e) => {
|
||||||
|
log.error("Failed to register ", FilterCodecs.PUSH, e);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const decodersGroupedByCT = groupByContentTopic(decodersArray);
|
private onRequest(streamData: IncomingStreamData): void {
|
||||||
const contentTopics = Array.from(decodersGroupedByCT.keys());
|
const { connection, stream } = streamData;
|
||||||
|
const { remotePeer } = connection;
|
||||||
|
log.info(`Received message from ${remotePeer.toString()}`);
|
||||||
|
try {
|
||||||
|
pipe(stream, lp.decode, async (source) => {
|
||||||
|
for await (const bytes of source) {
|
||||||
|
const response = FilterPushRpc.decode(bytes.slice());
|
||||||
|
|
||||||
const promises = this.peers.map(async (peer) => {
|
const { pubsubTopic, wakuMessage } = response;
|
||||||
const stream = await this.newStream(peer);
|
|
||||||
|
if (!wakuMessage) {
|
||||||
|
log.error("Received empty message");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pubsubTopic) {
|
||||||
|
log.error("Pubsub topic missing from push message");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.handleIncomingMessage(pubsubTopic, wakuMessage);
|
||||||
|
}
|
||||||
|
}).then(
|
||||||
|
() => {
|
||||||
|
log.info("Receiving pipe closed.");
|
||||||
|
},
|
||||||
|
(e) => {
|
||||||
|
log.error("Error with receiving pipe", e);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
log.error("Error decoding message", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async subscribe(
|
||||||
|
pubsubTopic: PubsubTopic,
|
||||||
|
peer: Peer,
|
||||||
|
contentTopics: ContentTopic[]
|
||||||
|
): Promise<void> {
|
||||||
|
const stream = await this.getStream(peer);
|
||||||
|
|
||||||
const request = FilterSubscribeRpc.createSubscribeRequest(
|
const request = FilterSubscribeRpc.createSubscribeRequest(
|
||||||
this.pubsubTopic,
|
pubsubTopic,
|
||||||
contentTopics
|
contentTopics
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await pipe(
|
const res = await pipe(
|
||||||
[request.encode()],
|
[request.encode()],
|
||||||
lp.encode,
|
lp.encode,
|
||||||
@ -124,73 +120,53 @@ class Subscription {
|
|||||||
`Filter subscribe request ${requestId} failed with status code ${statusCode}: ${statusDesc}`
|
`Filter subscribe request ${requestId} failed with status code ${statusCode}: ${statusDesc}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(
|
|
||||||
"Subscribed to peer ",
|
|
||||||
peer.id.toString(),
|
|
||||||
"for content topics",
|
|
||||||
contentTopics
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error(
|
|
||||||
"Error subscribing to peer: " +
|
|
||||||
peer.id.toString() +
|
|
||||||
" for content topics: " +
|
|
||||||
contentTopics +
|
|
||||||
": " +
|
|
||||||
e
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const results = await Promise.allSettled(promises);
|
|
||||||
|
|
||||||
this.handleErrors(results, "subscribe");
|
|
||||||
|
|
||||||
// Save the callback functions by content topics so they
|
|
||||||
// can easily be removed (reciprocally replaced) if `unsubscribe` (reciprocally `subscribe`)
|
|
||||||
// is called for those content topics
|
|
||||||
decodersGroupedByCT.forEach((decoders, contentTopic) => {
|
|
||||||
// Cast the type because a given `subscriptionCallbacks` map may hold
|
|
||||||
// Decoder that decode to different implementations of `IDecodedMessage`
|
|
||||||
const subscriptionCallback = {
|
|
||||||
decoders,
|
|
||||||
callback
|
|
||||||
} as unknown as SubscriptionCallback<IDecodedMessage>;
|
|
||||||
|
|
||||||
// The callback and decoder may override previous values, this is on
|
|
||||||
// purpose as the user may call `subscribe` to refresh the subscription
|
|
||||||
this.subscriptionCallbacks.set(contentTopic, subscriptionCallback);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async unsubscribe(contentTopics: ContentTopic[]): Promise<void> {
|
async unsubscribe(
|
||||||
const promises = this.peers.map(async (peer) => {
|
pubsubTopic: PubsubTopic,
|
||||||
const stream = await this.newStream(peer);
|
peer: Peer,
|
||||||
|
contentTopics: ContentTopic[]
|
||||||
|
): Promise<void> {
|
||||||
|
const stream = await this.getStream(peer);
|
||||||
const unsubscribeRequest = FilterSubscribeRpc.createUnsubscribeRequest(
|
const unsubscribeRequest = FilterSubscribeRpc.createUnsubscribeRequest(
|
||||||
this.pubsubTopic,
|
pubsubTopic,
|
||||||
contentTopics
|
contentTopics
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
|
||||||
await pipe([unsubscribeRequest.encode()], lp.encode, stream.sink);
|
await pipe([unsubscribeRequest.encode()], lp.encode, stream.sink);
|
||||||
} catch (error) {
|
|
||||||
throw new Error("Error unsubscribing: " + error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
contentTopics.forEach((contentTopic: string) => {
|
async unsubscribeAll(pubsubTopic: PubsubTopic, peer: Peer): Promise<void> {
|
||||||
this.subscriptionCallbacks.delete(contentTopic);
|
const stream = await this.getStream(peer);
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const results = await Promise.allSettled(promises);
|
const request = FilterSubscribeRpc.createUnsubscribeAllRequest(pubsubTopic);
|
||||||
|
|
||||||
this.handleErrors(results, "unsubscribe");
|
const res = await pipe(
|
||||||
|
[request.encode()],
|
||||||
|
lp.encode,
|
||||||
|
stream,
|
||||||
|
lp.decode,
|
||||||
|
async (source) => await all(source)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!res || !res.length) {
|
||||||
|
throw Error(
|
||||||
|
`No response received for request ${request.requestId}: ${res}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async ping(): Promise<void> {
|
const { statusCode, requestId, statusDesc } =
|
||||||
const promises = this.peers.map(async (peer) => {
|
FilterSubscribeResponse.decode(res[0].slice());
|
||||||
const stream = await this.newStream(peer);
|
|
||||||
|
if (statusCode < 200 || statusCode >= 300) {
|
||||||
|
throw new Error(
|
||||||
|
`Filter unsubscribe all request ${requestId} failed with status code ${statusCode}: ${statusDesc}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async ping(peer: Peer): Promise<void> {
|
||||||
|
const stream = await this.getStream(peer);
|
||||||
|
|
||||||
const request = FilterSubscribeRpc.createSubscriberPingRequest();
|
const request = FilterSubscribeRpc.createSubscriberPingRequest();
|
||||||
|
|
||||||
@ -222,319 +198,5 @@ class Subscription {
|
|||||||
log.error("Error pinging: ", error);
|
log.error("Error pinging: ", error);
|
||||||
throw error; // Rethrow the actual error instead of wrapping it
|
throw error; // Rethrow the actual error instead of wrapping it
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
const results = await Promise.allSettled(promises);
|
|
||||||
|
|
||||||
this.handleErrors(results, "ping");
|
|
||||||
}
|
|
||||||
|
|
||||||
async unsubscribeAll(): Promise<void> {
|
|
||||||
const promises = this.peers.map(async (peer) => {
|
|
||||||
const stream = await this.newStream(peer);
|
|
||||||
|
|
||||||
const request = FilterSubscribeRpc.createUnsubscribeAllRequest(
|
|
||||||
this.pubsubTopic
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await pipe(
|
|
||||||
[request.encode()],
|
|
||||||
lp.encode,
|
|
||||||
stream,
|
|
||||||
lp.decode,
|
|
||||||
async (source) => await all(source)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!res || !res.length) {
|
|
||||||
throw Error(
|
|
||||||
`No response received for request ${request.requestId}: ${res}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { statusCode, requestId, statusDesc } =
|
|
||||||
FilterSubscribeResponse.decode(res[0].slice());
|
|
||||||
|
|
||||||
if (statusCode < 200 || statusCode >= 300) {
|
|
||||||
throw new Error(
|
|
||||||
`Filter unsubscribe all request ${requestId} failed with status code ${statusCode}: ${statusDesc}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.subscriptionCallbacks.clear();
|
|
||||||
log.info(
|
|
||||||
`Unsubscribed from all content topics for pubsub topic ${this.pubsubTopic}`
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
throw new Error(
|
|
||||||
"Error unsubscribing from all content topics: " + error
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const results = await Promise.allSettled(promises);
|
|
||||||
|
|
||||||
this.handleErrors(results, "unsubscribeAll");
|
|
||||||
}
|
|
||||||
|
|
||||||
async processMessage(message: WakuMessage): Promise<void> {
|
|
||||||
const hashedMessageStr = messageHashStr(
|
|
||||||
this.pubsubTopic,
|
|
||||||
message as IProtoMessage
|
|
||||||
);
|
|
||||||
if (this.receivedMessagesHashStr.includes(hashedMessageStr)) {
|
|
||||||
log.info("Message already received, skipping");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.receivedMessagesHashStr.push(hashedMessageStr);
|
|
||||||
|
|
||||||
const { contentTopic } = message;
|
|
||||||
const subscriptionCallback = this.subscriptionCallbacks.get(contentTopic);
|
|
||||||
if (!subscriptionCallback) {
|
|
||||||
log.error("No subscription callback available for ", contentTopic);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
log.info(
|
|
||||||
"Processing message with content topic ",
|
|
||||||
contentTopic,
|
|
||||||
" on pubsub topic ",
|
|
||||||
this.pubsubTopic
|
|
||||||
);
|
|
||||||
await pushMessage(subscriptionCallback, this.pubsubTopic, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter out only the rejected promises and extract & handle their reasons
|
|
||||||
private handleErrors(
|
|
||||||
results: PromiseSettledResult<void>[],
|
|
||||||
type: "ping" | "subscribe" | "unsubscribe" | "unsubscribeAll"
|
|
||||||
): void {
|
|
||||||
const errors = results
|
|
||||||
.filter(
|
|
||||||
(result): result is PromiseRejectedResult =>
|
|
||||||
result.status === "rejected"
|
|
||||||
)
|
|
||||||
.map((rejectedResult) => rejectedResult.reason);
|
|
||||||
|
|
||||||
if (errors.length === this.peers.length) {
|
|
||||||
const errorCounts = new Map<string, number>();
|
|
||||||
// TODO: streamline error logging with https://github.com/orgs/waku-org/projects/2/views/1?pane=issue&itemId=42849952
|
|
||||||
errors.forEach((error) => {
|
|
||||||
const message = error instanceof Error ? error.message : String(error);
|
|
||||||
errorCounts.set(message, (errorCounts.get(message) || 0) + 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
const uniqueErrorMessages = Array.from(
|
|
||||||
errorCounts,
|
|
||||||
([message, count]) => `${message} (occurred ${count} times)`
|
|
||||||
).join(", ");
|
|
||||||
throw new Error(`Error ${type} all peers: ${uniqueErrorMessages}`);
|
|
||||||
} else if (errors.length > 0) {
|
|
||||||
// TODO: handle renewing faulty peers with new peers (https://github.com/waku-org/js-waku/issues/1463)
|
|
||||||
log.warn(
|
|
||||||
`Some ${type} failed. These will be refreshed with new peers`,
|
|
||||||
errors
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
log.info(`${type} successful for all peers`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const DEFAULT_NUM_PEERS = 3;
|
|
||||||
|
|
||||||
class Filter extends BaseProtocol implements IReceiver {
|
|
||||||
private activeSubscriptions = new Map<string, Subscription>();
|
|
||||||
|
|
||||||
private getActiveSubscription(
|
|
||||||
pubsubTopic: PubsubTopic
|
|
||||||
): Subscription | undefined {
|
|
||||||
return this.activeSubscriptions.get(pubsubTopic);
|
|
||||||
}
|
|
||||||
|
|
||||||
private setActiveSubscription(
|
|
||||||
pubsubTopic: PubsubTopic,
|
|
||||||
subscription: Subscription
|
|
||||||
): Subscription {
|
|
||||||
this.activeSubscriptions.set(pubsubTopic, subscription);
|
|
||||||
return subscription;
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: Remove when FilterCore and FilterSDK are introduced
|
|
||||||
private readonly numPeersToUse: number;
|
|
||||||
|
|
||||||
constructor(libp2p: Libp2p, options?: ProtocolCreateOptions) {
|
|
||||||
super(
|
|
||||||
FilterCodecs.SUBSCRIBE,
|
|
||||||
libp2p.components,
|
|
||||||
log,
|
|
||||||
options!.pubsubTopics!,
|
|
||||||
options
|
|
||||||
);
|
|
||||||
|
|
||||||
this.numPeersToUse = options?.numPeersToUse ?? DEFAULT_NUM_PEERS;
|
|
||||||
|
|
||||||
libp2p.handle(FilterCodecs.PUSH, this.onRequest.bind(this)).catch((e) => {
|
|
||||||
log.error("Failed to register ", FilterCodecs.PUSH, e);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.activeSubscriptions = new Map();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new subscription to the given pubsub topic.
|
|
||||||
* The subscription is made to multiple peers for decentralization.
|
|
||||||
* @param pubsubTopicShardInfo The pubsub topic to subscribe to.
|
|
||||||
* @returns The subscription object.
|
|
||||||
*/
|
|
||||||
async createSubscription(
|
|
||||||
pubsubTopicShardInfo: SingleShardInfo | PubsubTopic = DefaultPubsubTopic
|
|
||||||
): Promise<Subscription> {
|
|
||||||
const pubsubTopic =
|
|
||||||
typeof pubsubTopicShardInfo == "string"
|
|
||||||
? pubsubTopicShardInfo
|
|
||||||
: singleShardInfoToPubsubTopic(pubsubTopicShardInfo);
|
|
||||||
|
|
||||||
ensurePubsubTopicIsConfigured(pubsubTopic, this.pubsubTopics);
|
|
||||||
|
|
||||||
const peers = await this.getPeers({
|
|
||||||
maxBootstrapPeers: 1,
|
|
||||||
numPeers: this.numPeersToUse
|
|
||||||
});
|
|
||||||
if (peers.length === 0) {
|
|
||||||
throw new Error("No peer found to initiate subscription.");
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info(
|
|
||||||
`Creating filter subscription with ${peers.length} peers: `,
|
|
||||||
peers.map((peer) => peer.id.toString())
|
|
||||||
);
|
|
||||||
|
|
||||||
const subscription =
|
|
||||||
this.getActiveSubscription(pubsubTopic) ??
|
|
||||||
this.setActiveSubscription(
|
|
||||||
pubsubTopic,
|
|
||||||
new Subscription(pubsubTopic, peers, this.getStream.bind(this))
|
|
||||||
);
|
|
||||||
|
|
||||||
return subscription;
|
|
||||||
}
|
|
||||||
|
|
||||||
public toSubscriptionIterator<T extends IDecodedMessage>(
|
|
||||||
decoders: IDecoder<T> | IDecoder<T>[]
|
|
||||||
): Promise<IAsyncIterator<T>> {
|
|
||||||
return toAsyncIterator(this, decoders);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is used to satisfy the `IReceiver` interface.
|
|
||||||
*
|
|
||||||
* @hidden
|
|
||||||
*
|
|
||||||
* @param decoders The decoders to use for the subscription.
|
|
||||||
* @param callback The callback function to use for the subscription.
|
|
||||||
* @param opts Optional protocol options for the subscription.
|
|
||||||
*
|
|
||||||
* @returns A Promise that resolves to a function that unsubscribes from the subscription.
|
|
||||||
*
|
|
||||||
* @remarks
|
|
||||||
* This method should not be used directly.
|
|
||||||
* Instead, use `createSubscription` to create a new subscription.
|
|
||||||
*/
|
|
||||||
async subscribe<T extends IDecodedMessage>(
|
|
||||||
decoders: IDecoder<T> | IDecoder<T>[],
|
|
||||||
callback: Callback<T>
|
|
||||||
): Promise<Unsubscribe> {
|
|
||||||
const subscription = await this.createSubscription();
|
|
||||||
|
|
||||||
await subscription.subscribe(decoders, callback);
|
|
||||||
|
|
||||||
const contentTopics = Array.from(
|
|
||||||
groupByContentTopic(
|
|
||||||
Array.isArray(decoders) ? decoders : [decoders]
|
|
||||||
).keys()
|
|
||||||
);
|
|
||||||
|
|
||||||
return async () => {
|
|
||||||
await subscription.unsubscribe(contentTopics);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private onRequest(streamData: IncomingStreamData): void {
|
|
||||||
const { connection, stream } = streamData;
|
|
||||||
const { remotePeer } = connection;
|
|
||||||
log.info(`Received message from ${remotePeer.toString()}`);
|
|
||||||
try {
|
|
||||||
pipe(stream, lp.decode, async (source) => {
|
|
||||||
for await (const bytes of source) {
|
|
||||||
const response = FilterPushRpc.decode(bytes.slice());
|
|
||||||
|
|
||||||
const { pubsubTopic, wakuMessage } = response;
|
|
||||||
|
|
||||||
if (!wakuMessage) {
|
|
||||||
log.error("Received empty message");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pubsubTopic) {
|
|
||||||
log.error("Pubsub topic missing from push message");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const subscription = this.getActiveSubscription(pubsubTopic);
|
|
||||||
|
|
||||||
if (!subscription) {
|
|
||||||
log.error(
|
|
||||||
`No subscription locally registered for topic ${pubsubTopic}`
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await subscription.processMessage(wakuMessage);
|
|
||||||
}
|
|
||||||
}).then(
|
|
||||||
() => {
|
|
||||||
log.info("Receiving pipe closed.");
|
|
||||||
},
|
|
||||||
(e) => {
|
|
||||||
log.error("Error with receiving pipe", e);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
log.error("Error decoding message", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function wakuFilter(
|
|
||||||
init: ProtocolCreateOptions = { pubsubTopics: [] }
|
|
||||||
): (libp2p: Libp2p) => IFilter {
|
|
||||||
return (libp2p: Libp2p) => new Filter(libp2p, init);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function pushMessage<T extends IDecodedMessage>(
|
|
||||||
subscriptionCallback: SubscriptionCallback<T>,
|
|
||||||
pubsubTopic: PubsubTopic,
|
|
||||||
message: WakuMessage
|
|
||||||
): Promise<void> {
|
|
||||||
const { decoders, callback } = subscriptionCallback;
|
|
||||||
|
|
||||||
const { contentTopic } = message;
|
|
||||||
if (!contentTopic) {
|
|
||||||
log.warn("Message has no content topic, skipping");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const decodePromises = decoders.map((dec) =>
|
|
||||||
dec
|
|
||||||
.fromProtoObj(pubsubTopic, message as IProtoMessage)
|
|
||||||
.then((decoded) => decoded || Promise.reject("Decoding failed"))
|
|
||||||
);
|
|
||||||
|
|
||||||
const decodedMessage = await Promise.any(decodePromises);
|
|
||||||
|
|
||||||
await callback(decodedMessage);
|
|
||||||
} catch (e) {
|
|
||||||
log.error("Error decoding message", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ export async function waitForRemotePeer(
|
|||||||
if (!waku.filter)
|
if (!waku.filter)
|
||||||
throw new Error("Cannot wait for Filter peer: protocol not mounted");
|
throw new Error("Cannot wait for Filter peer: protocol not mounted");
|
||||||
promises.push(
|
promises.push(
|
||||||
waitForConnectedPeer(waku.filter, waku.libp2p.services.metadata)
|
waitForConnectedPeer(waku.filter.protocol, waku.libp2p.services.metadata)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,11 @@ import type { PeerId } from "@libp2p/interface";
|
|||||||
|
|
||||||
import type { IDecodedMessage, IDecoder, SingleShardInfo } from "./message.js";
|
import type { IDecodedMessage, IDecoder, SingleShardInfo } from "./message.js";
|
||||||
import type { ContentTopic, PubsubTopic } from "./misc.js";
|
import type { ContentTopic, PubsubTopic } from "./misc.js";
|
||||||
import type { Callback, IBaseProtocolCore } from "./protocols.js";
|
import type {
|
||||||
|
Callback,
|
||||||
|
IBaseProtocolCore,
|
||||||
|
IBaseProtocolSDK
|
||||||
|
} from "./protocols.js";
|
||||||
import type { IReceiver } from "./receiver.js";
|
import type { IReceiver } from "./receiver.js";
|
||||||
|
|
||||||
export type ContentFilter = {
|
export type ContentFilter = {
|
||||||
@ -22,8 +26,10 @@ export interface IFilterSubscription {
|
|||||||
unsubscribeAll(): Promise<void>;
|
unsubscribeAll(): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type IFilter = IReceiver &
|
export type IFilter = IReceiver & IBaseProtocolCore;
|
||||||
IBaseProtocolCore & {
|
|
||||||
|
export type IFilterSDK = IReceiver &
|
||||||
|
IBaseProtocolSDK & { protocol: IBaseProtocolCore } & {
|
||||||
createSubscription(
|
createSubscription(
|
||||||
pubsubTopicShardInfo?: SingleShardInfo | PubsubTopic,
|
pubsubTopicShardInfo?: SingleShardInfo | PubsubTopic,
|
||||||
peerId?: PeerId
|
peerId?: PeerId
|
||||||
|
@ -2,7 +2,7 @@ import type { PeerId, Stream } from "@libp2p/interface";
|
|||||||
import type { Multiaddr } from "@multiformats/multiaddr";
|
import type { Multiaddr } from "@multiformats/multiaddr";
|
||||||
|
|
||||||
import { IConnectionManager } from "./connection_manager.js";
|
import { IConnectionManager } from "./connection_manager.js";
|
||||||
import type { IFilter } from "./filter.js";
|
import type { IFilterSDK } from "./filter.js";
|
||||||
import type { Libp2p } from "./libp2p.js";
|
import type { Libp2p } from "./libp2p.js";
|
||||||
import type { ILightPushSDK } from "./light_push.js";
|
import type { ILightPushSDK } from "./light_push.js";
|
||||||
import { Protocols } from "./protocols.js";
|
import { Protocols } from "./protocols.js";
|
||||||
@ -13,7 +13,7 @@ export interface Waku {
|
|||||||
libp2p: Libp2p;
|
libp2p: Libp2p;
|
||||||
relay?: IRelay;
|
relay?: IRelay;
|
||||||
store?: IStoreSDK;
|
store?: IStoreSDK;
|
||||||
filter?: IFilter;
|
filter?: IFilterSDK;
|
||||||
lightPush?: ILightPushSDK;
|
lightPush?: ILightPushSDK;
|
||||||
|
|
||||||
connectionManager: IConnectionManager;
|
connectionManager: IConnectionManager;
|
||||||
@ -32,7 +32,7 @@ export interface Waku {
|
|||||||
export interface LightNode extends Waku {
|
export interface LightNode extends Waku {
|
||||||
relay: undefined;
|
relay: undefined;
|
||||||
store: IStoreSDK;
|
store: IStoreSDK;
|
||||||
filter: IFilter;
|
filter: IFilterSDK;
|
||||||
lightPush: ILightPushSDK;
|
lightPush: ILightPushSDK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,6 +46,6 @@ export interface RelayNode extends Waku {
|
|||||||
export interface FullNode extends Waku {
|
export interface FullNode extends Waku {
|
||||||
relay: IRelay;
|
relay: IRelay;
|
||||||
store: IStoreSDK;
|
store: IStoreSDK;
|
||||||
filter: IFilter;
|
filter: IFilterSDK;
|
||||||
lightPush: ILightPushSDK;
|
lightPush: ILightPushSDK;
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,7 @@
|
|||||||
"@waku/core": "0.0.28",
|
"@waku/core": "0.0.28",
|
||||||
"@waku/discovery": "0.0.1",
|
"@waku/discovery": "0.0.1",
|
||||||
"@waku/interfaces": "0.0.23",
|
"@waku/interfaces": "0.0.23",
|
||||||
|
"@waku/proto": "^0.0.6",
|
||||||
"@waku/relay": "0.0.11",
|
"@waku/relay": "0.0.11",
|
||||||
"@waku/utils": "0.0.16",
|
"@waku/utils": "0.0.16",
|
||||||
"libp2p": "^1.1.2"
|
"libp2p": "^1.1.2"
|
||||||
@ -89,10 +90,11 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@libp2p/bootstrap": "^10",
|
"@libp2p/bootstrap": "^10",
|
||||||
"@waku/core": "0.0.27",
|
"@waku/core": "0.0.28",
|
||||||
"@waku/interfaces": "0.0.22",
|
"@waku/interfaces": "0.0.23",
|
||||||
"@waku/relay": "0.0.10",
|
"@waku/message-hash": "^0.1.12",
|
||||||
"@waku/utils": "0.0.15"
|
"@waku/relay": "0.0.11",
|
||||||
|
"@waku/utils": "0.0.16"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@waku/interfaces": {
|
"@waku/interfaces": {
|
||||||
|
@ -13,6 +13,7 @@ export * from "./waku.js";
|
|||||||
|
|
||||||
export { createLightNode, createNode } from "./light-node/index.js";
|
export { createLightNode, createNode } from "./light-node/index.js";
|
||||||
export { wakuLightPush } from "./protocols/light_push.js";
|
export { wakuLightPush } from "./protocols/light_push.js";
|
||||||
|
export { wakuFilter } from "./protocols/filter.js";
|
||||||
export { wakuStore } from "./protocols/store.js";
|
export { wakuStore } from "./protocols/store.js";
|
||||||
|
|
||||||
export * as waku from "@waku/core";
|
export * as waku from "@waku/core";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { wakuFilter } from "@waku/core";
|
|
||||||
import { type Libp2pComponents, type LightNode } from "@waku/interfaces";
|
import { type Libp2pComponents, type LightNode } from "@waku/interfaces";
|
||||||
|
|
||||||
|
import { wakuFilter } from "../protocols/filter.js";
|
||||||
import { wakuLightPush } from "../protocols/light_push.js";
|
import { wakuLightPush } from "../protocols/light_push.js";
|
||||||
import { wakuStore } from "../protocols/store.js";
|
import { wakuStore } from "../protocols/store.js";
|
||||||
import { createLibp2pAndUpdateOptions } from "../utils/libp2p.js";
|
import { createLibp2pAndUpdateOptions } from "../utils/libp2p.js";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { IBaseProtocolSDK } from "..";
|
import { IBaseProtocolSDK } from "@waku/interfaces";
|
||||||
|
|
||||||
interface Options {
|
interface Options {
|
||||||
numPeersToUse?: number;
|
numPeersToUse?: number;
|
||||||
|
351
packages/sdk/src/protocols/filter.ts
Normal file
351
packages/sdk/src/protocols/filter.ts
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
import type { Peer } from "@libp2p/interface";
|
||||||
|
import { FilterCore } from "@waku/core";
|
||||||
|
import {
|
||||||
|
type Callback,
|
||||||
|
ContentTopic,
|
||||||
|
DefaultPubsubTopic,
|
||||||
|
type IAsyncIterator,
|
||||||
|
type IDecodedMessage,
|
||||||
|
type IDecoder,
|
||||||
|
type IFilterSDK,
|
||||||
|
IProtoMessage,
|
||||||
|
type Libp2p,
|
||||||
|
type ProtocolCreateOptions,
|
||||||
|
type PubsubTopic,
|
||||||
|
type SingleShardInfo,
|
||||||
|
type Unsubscribe
|
||||||
|
} from "@waku/interfaces";
|
||||||
|
import { messageHashStr } from "@waku/message-hash";
|
||||||
|
import { WakuMessage } from "@waku/proto";
|
||||||
|
import {
|
||||||
|
ensurePubsubTopicIsConfigured,
|
||||||
|
groupByContentTopic,
|
||||||
|
Logger,
|
||||||
|
singleShardInfoToPubsubTopic,
|
||||||
|
toAsyncIterator
|
||||||
|
} from "@waku/utils";
|
||||||
|
|
||||||
|
import { BaseProtocolSDK } from "./base_protocol";
|
||||||
|
|
||||||
|
type SubscriptionCallback<T extends IDecodedMessage> = {
|
||||||
|
decoders: IDecoder<T>[];
|
||||||
|
callback: Callback<T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const log = new Logger("sdk:filter");
|
||||||
|
|
||||||
|
export class SubscriptionManager {
|
||||||
|
private readonly pubsubTopic: PubsubTopic;
|
||||||
|
readonly peers: Peer[];
|
||||||
|
readonly receivedMessagesHashStr: string[] = [];
|
||||||
|
|
||||||
|
private subscriptionCallbacks: Map<
|
||||||
|
ContentTopic,
|
||||||
|
SubscriptionCallback<IDecodedMessage>
|
||||||
|
>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
pubsubTopic: PubsubTopic,
|
||||||
|
remotePeers: Peer[],
|
||||||
|
private protocol: FilterCore
|
||||||
|
) {
|
||||||
|
this.peers = remotePeers;
|
||||||
|
this.pubsubTopic = pubsubTopic;
|
||||||
|
this.subscriptionCallbacks = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
async subscribe<T extends IDecodedMessage>(
|
||||||
|
decoders: IDecoder<T> | IDecoder<T>[],
|
||||||
|
callback: Callback<T>
|
||||||
|
): Promise<void> {
|
||||||
|
const decodersArray = Array.isArray(decoders) ? decoders : [decoders];
|
||||||
|
|
||||||
|
// check that all decoders are configured for the same pubsub topic as this subscription
|
||||||
|
decodersArray.forEach((decoder) => {
|
||||||
|
if (decoder.pubsubTopic !== this.pubsubTopic) {
|
||||||
|
throw new Error(
|
||||||
|
`Pubsub topic not configured: decoder is configured for pubsub topic ${decoder.pubsubTopic} but this subscription is for pubsub topic ${this.pubsubTopic}. Please create a new Subscription for the different pubsub topic.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const decodersGroupedByCT = groupByContentTopic(decodersArray);
|
||||||
|
const contentTopics = Array.from(decodersGroupedByCT.keys());
|
||||||
|
|
||||||
|
const promises = this.peers.map(async (peer) => {
|
||||||
|
await this.protocol.subscribe(this.pubsubTopic, peer, contentTopics);
|
||||||
|
});
|
||||||
|
|
||||||
|
const results = await Promise.allSettled(promises);
|
||||||
|
|
||||||
|
this.handleErrors(results, "subscribe");
|
||||||
|
|
||||||
|
// Save the callback functions by content topics so they
|
||||||
|
// can easily be removed (reciprocally replaced) if `unsubscribe` (reciprocally `subscribe`)
|
||||||
|
// is called for those content topics
|
||||||
|
decodersGroupedByCT.forEach((decoders, contentTopic) => {
|
||||||
|
// Cast the type because a given `subscriptionCallbacks` map may hold
|
||||||
|
// Decoder that decode to different implementations of `IDecodedMessage`
|
||||||
|
const subscriptionCallback = {
|
||||||
|
decoders,
|
||||||
|
callback
|
||||||
|
} as unknown as SubscriptionCallback<IDecodedMessage>;
|
||||||
|
|
||||||
|
// The callback and decoder may override previous values, this is on
|
||||||
|
// purpose as the user may call `subscribe` to refresh the subscription
|
||||||
|
this.subscriptionCallbacks.set(contentTopic, subscriptionCallback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async unsubscribe(contentTopics: ContentTopic[]): Promise<void> {
|
||||||
|
const promises = this.peers.map(async (peer) => {
|
||||||
|
await this.protocol.unsubscribe(this.pubsubTopic, peer, contentTopics);
|
||||||
|
|
||||||
|
contentTopics.forEach((contentTopic: string) => {
|
||||||
|
this.subscriptionCallbacks.delete(contentTopic);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const results = await Promise.allSettled(promises);
|
||||||
|
|
||||||
|
this.handleErrors(results, "unsubscribe");
|
||||||
|
}
|
||||||
|
|
||||||
|
async ping(): Promise<void> {
|
||||||
|
const promises = this.peers.map(async (peer) => {
|
||||||
|
await this.protocol.ping(peer);
|
||||||
|
});
|
||||||
|
|
||||||
|
const results = await Promise.allSettled(promises);
|
||||||
|
|
||||||
|
this.handleErrors(results, "ping");
|
||||||
|
}
|
||||||
|
|
||||||
|
async unsubscribeAll(): Promise<void> {
|
||||||
|
const promises = this.peers.map(async (peer) => {
|
||||||
|
await this.protocol.unsubscribeAll(this.pubsubTopic, peer);
|
||||||
|
});
|
||||||
|
|
||||||
|
const results = await Promise.allSettled(promises);
|
||||||
|
|
||||||
|
this.subscriptionCallbacks.clear();
|
||||||
|
|
||||||
|
this.handleErrors(results, "unsubscribeAll");
|
||||||
|
}
|
||||||
|
|
||||||
|
async processIncomingMessage(message: WakuMessage): Promise<void> {
|
||||||
|
const hashedMessageStr = messageHashStr(
|
||||||
|
this.pubsubTopic,
|
||||||
|
message as IProtoMessage
|
||||||
|
);
|
||||||
|
if (this.receivedMessagesHashStr.includes(hashedMessageStr)) {
|
||||||
|
log.info("Message already received, skipping");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.receivedMessagesHashStr.push(hashedMessageStr);
|
||||||
|
|
||||||
|
const { contentTopic } = message;
|
||||||
|
const subscriptionCallback = this.subscriptionCallbacks.get(contentTopic);
|
||||||
|
if (!subscriptionCallback) {
|
||||||
|
log.error("No subscription callback available for ", contentTopic);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log.info(
|
||||||
|
"Processing message with content topic ",
|
||||||
|
contentTopic,
|
||||||
|
" on pubsub topic ",
|
||||||
|
this.pubsubTopic
|
||||||
|
);
|
||||||
|
await pushMessage(subscriptionCallback, this.pubsubTopic, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter out only the rejected promises and extract & handle their reasons
|
||||||
|
private handleErrors(
|
||||||
|
results: PromiseSettledResult<void>[],
|
||||||
|
type: "ping" | "subscribe" | "unsubscribe" | "unsubscribeAll"
|
||||||
|
): void {
|
||||||
|
const errors = results
|
||||||
|
.filter(
|
||||||
|
(result): result is PromiseRejectedResult =>
|
||||||
|
result.status === "rejected"
|
||||||
|
)
|
||||||
|
.map((rejectedResult) => rejectedResult.reason);
|
||||||
|
|
||||||
|
if (errors.length === this.peers.length) {
|
||||||
|
const errorCounts = new Map<string, number>();
|
||||||
|
// TODO: streamline error logging with https://github.com/orgs/waku-org/projects/2/views/1?pane=issue&itemId=42849952
|
||||||
|
errors.forEach((error) => {
|
||||||
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
|
errorCounts.set(message, (errorCounts.get(message) || 0) + 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
const uniqueErrorMessages = Array.from(
|
||||||
|
errorCounts,
|
||||||
|
([message, count]) => `${message} (occurred ${count} times)`
|
||||||
|
).join(", ");
|
||||||
|
throw new Error(`Error ${type} all peers: ${uniqueErrorMessages}`);
|
||||||
|
} else if (errors.length > 0) {
|
||||||
|
// TODO: handle renewing faulty peers with new peers (https://github.com/waku-org/js-waku/issues/1463)
|
||||||
|
log.warn(
|
||||||
|
`Some ${type} failed. These will be refreshed with new peers`,
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log.info(`${type} successful for all peers`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FilterSDK extends BaseProtocolSDK implements IFilterSDK {
|
||||||
|
public readonly protocol: FilterCore;
|
||||||
|
|
||||||
|
private activeSubscriptions = new Map<string, SubscriptionManager>();
|
||||||
|
private async handleIncomingMessage(
|
||||||
|
pubsubTopic: PubsubTopic,
|
||||||
|
wakuMessage: WakuMessage
|
||||||
|
): Promise<void> {
|
||||||
|
const subscription = this.getActiveSubscription(pubsubTopic);
|
||||||
|
if (!subscription) {
|
||||||
|
log.error(`No subscription locally registered for topic ${pubsubTopic}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await subscription.processIncomingMessage(wakuMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(libp2p: Libp2p, options?: ProtocolCreateOptions) {
|
||||||
|
super({ numPeersToUse: options?.numPeersToUse });
|
||||||
|
this.protocol = new FilterCore(
|
||||||
|
this.handleIncomingMessage.bind(this),
|
||||||
|
libp2p,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
this.activeSubscriptions = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: move to SubscriptionManager
|
||||||
|
private getActiveSubscription(
|
||||||
|
pubsubTopic: PubsubTopic
|
||||||
|
): SubscriptionManager | undefined {
|
||||||
|
return this.activeSubscriptions.get(pubsubTopic);
|
||||||
|
}
|
||||||
|
|
||||||
|
private setActiveSubscription(
|
||||||
|
pubsubTopic: PubsubTopic,
|
||||||
|
subscription: SubscriptionManager
|
||||||
|
): SubscriptionManager {
|
||||||
|
this.activeSubscriptions.set(pubsubTopic, subscription);
|
||||||
|
return subscription;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new subscription to the given pubsub topic.
|
||||||
|
* The subscription is made to multiple peers for decentralization.
|
||||||
|
* @param pubsubTopicShardInfo The pubsub topic to subscribe to.
|
||||||
|
* @returns The subscription object.
|
||||||
|
*/
|
||||||
|
async createSubscription(
|
||||||
|
pubsubTopicShardInfo: SingleShardInfo | PubsubTopic = DefaultPubsubTopic
|
||||||
|
): Promise<SubscriptionManager> {
|
||||||
|
const pubsubTopic =
|
||||||
|
typeof pubsubTopicShardInfo == "string"
|
||||||
|
? pubsubTopicShardInfo
|
||||||
|
: singleShardInfoToPubsubTopic(pubsubTopicShardInfo);
|
||||||
|
|
||||||
|
ensurePubsubTopicIsConfigured(pubsubTopic, this.protocol.pubsubTopics);
|
||||||
|
|
||||||
|
const peers = await this.protocol.getPeers();
|
||||||
|
if (peers.length === 0) {
|
||||||
|
throw new Error("No peer found to initiate subscription.");
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info(
|
||||||
|
`Creating filter subscription with ${peers.length} peers: `,
|
||||||
|
peers.map((peer) => peer.id.toString())
|
||||||
|
);
|
||||||
|
|
||||||
|
const subscription =
|
||||||
|
this.getActiveSubscription(pubsubTopic) ??
|
||||||
|
this.setActiveSubscription(
|
||||||
|
pubsubTopic,
|
||||||
|
new SubscriptionManager(pubsubTopic, peers, this.protocol)
|
||||||
|
);
|
||||||
|
|
||||||
|
return subscription;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: remove this dependency on IReceiver
|
||||||
|
/**
|
||||||
|
* This method is used to satisfy the `IReceiver` interface.
|
||||||
|
*
|
||||||
|
* @hidden
|
||||||
|
*
|
||||||
|
* @param decoders The decoders to use for the subscription.
|
||||||
|
* @param callback The callback function to use for the subscription.
|
||||||
|
* @param opts Optional protocol options for the subscription.
|
||||||
|
*
|
||||||
|
* @returns A Promise that resolves to a function that unsubscribes from the subscription.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* This method should not be used directly.
|
||||||
|
* Instead, use `createSubscription` to create a new subscription.
|
||||||
|
*/
|
||||||
|
async subscribe<T extends IDecodedMessage>(
|
||||||
|
decoders: IDecoder<T> | IDecoder<T>[],
|
||||||
|
callback: Callback<T>
|
||||||
|
): Promise<Unsubscribe> {
|
||||||
|
const subscription = await this.createSubscription();
|
||||||
|
|
||||||
|
await subscription.subscribe(decoders, callback);
|
||||||
|
|
||||||
|
const contentTopics = Array.from(
|
||||||
|
groupByContentTopic(
|
||||||
|
Array.isArray(decoders) ? decoders : [decoders]
|
||||||
|
).keys()
|
||||||
|
);
|
||||||
|
|
||||||
|
return async () => {
|
||||||
|
await subscription.unsubscribe(contentTopics);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public toSubscriptionIterator<T extends IDecodedMessage>(
|
||||||
|
decoders: IDecoder<T> | IDecoder<T>[]
|
||||||
|
): Promise<IAsyncIterator<T>> {
|
||||||
|
return toAsyncIterator(this, decoders);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function wakuFilter(
|
||||||
|
init: ProtocolCreateOptions
|
||||||
|
): (libp2p: Libp2p) => IFilterSDK {
|
||||||
|
return (libp2p: Libp2p) => new FilterSDK(libp2p, init);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function pushMessage<T extends IDecodedMessage>(
|
||||||
|
subscriptionCallback: SubscriptionCallback<T>,
|
||||||
|
pubsubTopic: PubsubTopic,
|
||||||
|
message: WakuMessage
|
||||||
|
): Promise<void> {
|
||||||
|
const { decoders, callback } = subscriptionCallback;
|
||||||
|
|
||||||
|
const { contentTopic } = message;
|
||||||
|
if (!contentTopic) {
|
||||||
|
log.warn("Message has no content topic, skipping");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const decodePromises = decoders.map((dec) =>
|
||||||
|
dec
|
||||||
|
.fromProtoObj(pubsubTopic, message as IProtoMessage)
|
||||||
|
.then((decoded) => decoded || Promise.reject("Decoding failed"))
|
||||||
|
);
|
||||||
|
|
||||||
|
const decodedMessage = await Promise.any(decodePromises);
|
||||||
|
|
||||||
|
await callback(decodedMessage);
|
||||||
|
} catch (e) {
|
||||||
|
log.error("Error decoding message", e);
|
||||||
|
}
|
||||||
|
}
|
@ -14,14 +14,13 @@ import { ensurePubsubTopicIsConfigured, Logger } from "@waku/utils";
|
|||||||
|
|
||||||
import { BaseProtocolSDK } from "./base_protocol.js";
|
import { BaseProtocolSDK } from "./base_protocol.js";
|
||||||
|
|
||||||
const DEFAULT_NUM_PEERS = 3;
|
|
||||||
const log = new Logger("sdk:light-push");
|
const log = new Logger("sdk:light-push");
|
||||||
|
|
||||||
export class LightPushSDK extends BaseProtocolSDK implements ILightPushSDK {
|
class LightPushSDK extends BaseProtocolSDK implements ILightPushSDK {
|
||||||
public readonly protocol: LightPushCore;
|
public readonly protocol: LightPushCore;
|
||||||
|
|
||||||
constructor(libp2p: Libp2p, options?: ProtocolCreateOptions) {
|
constructor(libp2p: Libp2p, options?: ProtocolCreateOptions) {
|
||||||
super({ numPeersToUse: options?.numPeersToUse ?? DEFAULT_NUM_PEERS });
|
super({ numPeersToUse: options?.numPeersToUse });
|
||||||
this.protocol = new LightPushCore(libp2p, options);
|
this.protocol = new LightPushCore(libp2p, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { wakuFilter } from "@waku/core";
|
|
||||||
import { type FullNode, type RelayNode } from "@waku/interfaces";
|
import { type FullNode, type RelayNode } from "@waku/interfaces";
|
||||||
import { RelayCreateOptions, wakuRelay } from "@waku/relay";
|
import { RelayCreateOptions, wakuRelay } from "@waku/relay";
|
||||||
|
|
||||||
|
import { wakuFilter } from "../protocols/filter.js";
|
||||||
import { wakuLightPush } from "../protocols/light_push.js";
|
import { wakuLightPush } from "../protocols/light_push.js";
|
||||||
import { wakuStore } from "../protocols/store.js";
|
import { wakuStore } from "../protocols/store.js";
|
||||||
import { createLibp2pAndUpdateOptions } from "../utils/libp2p.js";
|
import { createLibp2pAndUpdateOptions } from "../utils/libp2p.js";
|
||||||
|
@ -4,7 +4,7 @@ import { multiaddr, Multiaddr, MultiaddrInput } from "@multiformats/multiaddr";
|
|||||||
import { ConnectionManager, DecodedMessage } from "@waku/core";
|
import { ConnectionManager, DecodedMessage } from "@waku/core";
|
||||||
import type {
|
import type {
|
||||||
Callback,
|
Callback,
|
||||||
IFilter,
|
IFilterSDK,
|
||||||
IFilterSubscription,
|
IFilterSubscription,
|
||||||
ILightPushSDK,
|
ILightPushSDK,
|
||||||
IRelay,
|
IRelay,
|
||||||
@ -56,7 +56,7 @@ export class WakuNode implements Waku {
|
|||||||
public libp2p: Libp2p;
|
public libp2p: Libp2p;
|
||||||
public relay?: IRelay;
|
public relay?: IRelay;
|
||||||
public store?: IStoreSDK;
|
public store?: IStoreSDK;
|
||||||
public filter?: IFilter;
|
public filter?: IFilterSDK;
|
||||||
public lightPush?: ILightPushSDK;
|
public lightPush?: ILightPushSDK;
|
||||||
public connectionManager: ConnectionManager;
|
public connectionManager: ConnectionManager;
|
||||||
public readonly pubsubTopics: PubsubTopic[];
|
public readonly pubsubTopics: PubsubTopic[];
|
||||||
@ -66,7 +66,7 @@ export class WakuNode implements Waku {
|
|||||||
libp2p: Libp2p,
|
libp2p: Libp2p,
|
||||||
store?: (libp2p: Libp2p) => IStoreSDK,
|
store?: (libp2p: Libp2p) => IStoreSDK,
|
||||||
lightPush?: (libp2p: Libp2p) => ILightPushSDK,
|
lightPush?: (libp2p: Libp2p) => ILightPushSDK,
|
||||||
filter?: (libp2p: Libp2p) => IFilter,
|
filter?: (libp2p: Libp2p) => IFilterSDK,
|
||||||
relay?: (libp2p: Libp2p) => IRelay
|
relay?: (libp2p: Libp2p) => IRelay
|
||||||
) {
|
) {
|
||||||
if (options.pubsubTopics.length == 0) {
|
if (options.pubsubTopics.length == 0) {
|
||||||
@ -166,7 +166,7 @@ export class WakuNode implements Waku {
|
|||||||
}
|
}
|
||||||
if (_protocols.includes(Protocols.Filter)) {
|
if (_protocols.includes(Protocols.Filter)) {
|
||||||
if (this.filter) {
|
if (this.filter) {
|
||||||
codecs.push(this.filter.multicodec);
|
codecs.push(this.filter.protocol.multicodec);
|
||||||
} else {
|
} else {
|
||||||
log.error(
|
log.error(
|
||||||
"Filter codec not included in dial codec: protocol not mounted locally"
|
"Filter codec not included in dial codec: protocol not mounted locally"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { wakuFilter } from "@waku/core";
|
import { wakuFilter } from "@waku/sdk";
|
||||||
import {
|
import {
|
||||||
bytesToUtf8,
|
bytesToUtf8,
|
||||||
createEncoder,
|
createEncoder,
|
||||||
|
@ -200,7 +200,7 @@ describe("Wait for remote peer", function () {
|
|||||||
await waku2.dial(multiAddrWithId);
|
await waku2.dial(multiAddrWithId);
|
||||||
await waitForRemotePeer(waku2, [Protocols.Filter]);
|
await waitForRemotePeer(waku2, [Protocols.Filter]);
|
||||||
|
|
||||||
const peers = (await waku2.filter.connectedPeers()).map((peer) =>
|
const peers = (await waku2.filter.protocol.connectedPeers()).map((peer) =>
|
||||||
peer.id.toString()
|
peer.id.toString()
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -232,8 +232,8 @@ describe("Wait for remote peer", function () {
|
|||||||
Protocols.LightPush
|
Protocols.LightPush
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const filterPeers = (await waku2.filter.connectedPeers()).map((peer) =>
|
const filterPeers = (await waku2.filter.protocol.connectedPeers()).map(
|
||||||
peer.id.toString()
|
(peer) => peer.id.toString()
|
||||||
);
|
);
|
||||||
const storePeers = (await waku2.store.protocol.connectedPeers()).map(
|
const storePeers = (await waku2.store.protocol.connectedPeers()).map(
|
||||||
(peer) => peer.id.toString()
|
(peer) => peer.id.toString()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user