mirror of https://github.com/status-im/js-waku.git
feat!: add and implement IReceiver (#1219)
- remove extend Relay by GossipSub and use it as public property; - detach GossipSub initialisation;
This commit is contained in:
parent
e8f750fa2b
commit
e11e5b4870
|
@ -19,7 +19,11 @@ export * as waku_light_push from "./lib/light_push/index.js";
|
||||||
export { wakuLightPush, LightPushCodec } from "./lib/light_push/index.js";
|
export { wakuLightPush, LightPushCodec } from "./lib/light_push/index.js";
|
||||||
|
|
||||||
export * as waku_relay from "./lib/relay/index.js";
|
export * as waku_relay from "./lib/relay/index.js";
|
||||||
export { wakuRelay, RelayCreateOptions } from "./lib/relay/index.js";
|
export {
|
||||||
|
wakuRelay,
|
||||||
|
RelayCreateOptions,
|
||||||
|
wakuGossipSub,
|
||||||
|
} from "./lib/relay/index.js";
|
||||||
|
|
||||||
export * as waku_store from "./lib/store/index.js";
|
export * as waku_store from "./lib/store/index.js";
|
||||||
export {
|
export {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import type { Libp2p } from "@libp2p/interface-libp2p";
|
||||||
import type { Peer } from "@libp2p/interface-peer-store";
|
import type { Peer } from "@libp2p/interface-peer-store";
|
||||||
import type { IncomingStreamData } from "@libp2p/interface-registrar";
|
import type { IncomingStreamData } from "@libp2p/interface-registrar";
|
||||||
import type {
|
import type {
|
||||||
|
ActiveSubscriptions,
|
||||||
Callback,
|
Callback,
|
||||||
IDecodedMessage,
|
IDecodedMessage,
|
||||||
IDecoder,
|
IDecoder,
|
||||||
|
@ -58,19 +59,20 @@ class Filter extends BaseProtocol implements IFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param decoders Array of Decoders to use to decode messages, it also specifies the content topics.
|
* @param decoders Decoder or array of Decoders to use to decode messages, it also specifies the content topics.
|
||||||
* @param callback A function that will be called on each message returned by the filter.
|
* @param callback A function that will be called on each message returned by the filter.
|
||||||
* @param opts The FilterSubscriptionOpts used to narrow which messages are returned, and which peer to connect to.
|
* @param opts The FilterSubscriptionOpts used to narrow which messages are returned, and which peer to connect to.
|
||||||
* @returns Unsubscribe function that can be used to end the subscription.
|
* @returns Unsubscribe function that can be used to end the subscription.
|
||||||
*/
|
*/
|
||||||
async subscribe<T extends IDecodedMessage>(
|
async subscribe<T extends IDecodedMessage>(
|
||||||
decoders: IDecoder<T>[],
|
decoders: IDecoder<T> | IDecoder<T>[],
|
||||||
callback: Callback<T>,
|
callback: Callback<T>,
|
||||||
opts?: ProtocolOptions
|
opts?: ProtocolOptions
|
||||||
): Promise<UnsubscribeFunction> {
|
): Promise<UnsubscribeFunction> {
|
||||||
|
const decodersArray = Array.isArray(decoders) ? decoders : [decoders];
|
||||||
const { pubSubTopic = DefaultPubSubTopic } = this.options;
|
const { pubSubTopic = DefaultPubSubTopic } = this.options;
|
||||||
|
|
||||||
const contentTopics = Array.from(groupByContentTopic(decoders).keys());
|
const contentTopics = Array.from(groupByContentTopic(decodersArray).keys());
|
||||||
|
|
||||||
const contentFilters = contentTopics.map((contentTopic) => ({
|
const contentFilters = contentTopics.map((contentTopic) => ({
|
||||||
contentTopic,
|
contentTopic,
|
||||||
|
@ -109,7 +111,11 @@ class Filter extends BaseProtocol implements IFilter {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
const subscription: Subscription<T> = { callback, decoders, pubSubTopic };
|
const subscription: Subscription<T> = {
|
||||||
|
callback,
|
||||||
|
decoders: decodersArray,
|
||||||
|
pubSubTopic,
|
||||||
|
};
|
||||||
this.subscriptions.set(requestId, subscription);
|
this.subscriptions.set(requestId, subscription);
|
||||||
|
|
||||||
return async () => {
|
return async () => {
|
||||||
|
@ -118,6 +124,22 @@ class Filter extends BaseProtocol implements IFilter {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getActiveSubscriptions(): ActiveSubscriptions {
|
||||||
|
const map: ActiveSubscriptions = new Map();
|
||||||
|
const subscriptions = this.subscriptions as Map<
|
||||||
|
RequestID,
|
||||||
|
Subscription<IDecodedMessage>
|
||||||
|
>;
|
||||||
|
|
||||||
|
for (const item of subscriptions.values()) {
|
||||||
|
const values = map.get(item.pubSubTopic) || [];
|
||||||
|
const nextValues = item.decoders.map((decoder) => decoder.contentTopic);
|
||||||
|
map.set(item.pubSubTopic, [...values, ...nextValues]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
private onRequest(streamData: IncomingStreamData): void {
|
private onRequest(streamData: IncomingStreamData): void {
|
||||||
log("Receiving message push");
|
log("Receiving message push");
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -6,6 +6,8 @@ import {
|
||||||
} from "@chainsafe/libp2p-gossipsub";
|
} from "@chainsafe/libp2p-gossipsub";
|
||||||
import type { PeerIdStr, TopicStr } from "@chainsafe/libp2p-gossipsub/types";
|
import type { PeerIdStr, TopicStr } from "@chainsafe/libp2p-gossipsub/types";
|
||||||
import { SignaturePolicy } from "@chainsafe/libp2p-gossipsub/types";
|
import { SignaturePolicy } from "@chainsafe/libp2p-gossipsub/types";
|
||||||
|
import type { Libp2p } from "@libp2p/interface-libp2p";
|
||||||
|
import type { PubSub } from "@libp2p/interface-pubsub";
|
||||||
import type {
|
import type {
|
||||||
ActiveSubscriptions,
|
ActiveSubscriptions,
|
||||||
Callback,
|
Callback,
|
||||||
|
@ -20,8 +22,8 @@ import type {
|
||||||
import debug from "debug";
|
import debug from "debug";
|
||||||
|
|
||||||
import { DefaultPubSubTopic } from "../constants.js";
|
import { DefaultPubSubTopic } from "../constants.js";
|
||||||
|
import { groupByContentTopic } from "../group_by.js";
|
||||||
import { TopicOnlyDecoder } from "../message/topic_only_message.js";
|
import { TopicOnlyDecoder } from "../message/topic_only_message.js";
|
||||||
import { pushOrInitMapSet } from "../push_or_init_map.js";
|
|
||||||
|
|
||||||
import * as constants from "./constants.js";
|
import * as constants from "./constants.js";
|
||||||
import { messageValidator } from "./message_validator.js";
|
import { messageValidator } from "./message_validator.js";
|
||||||
|
@ -38,14 +40,14 @@ export type ContentTopic = string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements the [Waku v2 Relay protocol](https://rfc.vac.dev/spec/11/).
|
* Implements the [Waku v2 Relay protocol](https://rfc.vac.dev/spec/11/).
|
||||||
* Must be passed as a `pubsub` module to a `Libp2p` instance.
|
* Throws if libp2p.pubsub does not support Waku Relay
|
||||||
*
|
|
||||||
* @implements {require('libp2p-interfaces/src/pubsub')}
|
|
||||||
*/
|
*/
|
||||||
class Relay extends GossipSub implements IRelay {
|
class Relay implements IRelay {
|
||||||
private readonly pubSubTopic: string;
|
private readonly pubSubTopic: string;
|
||||||
defaultDecoder: IDecoder<IDecodedMessage>;
|
private defaultDecoder: IDecoder<IDecodedMessage>;
|
||||||
|
|
||||||
public static multicodec: string = constants.RelayCodecs[0];
|
public static multicodec: string = constants.RelayCodecs[0];
|
||||||
|
public readonly gossipSub: GossipSub;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* observers called when receiving new message.
|
* observers called when receiving new message.
|
||||||
|
@ -53,21 +55,20 @@ class Relay extends GossipSub implements IRelay {
|
||||||
*/
|
*/
|
||||||
private observers: Map<ContentTopic, Set<unknown>>;
|
private observers: Map<ContentTopic, Set<unknown>>;
|
||||||
|
|
||||||
constructor(
|
constructor(libp2p: Libp2p, options?: Partial<RelayCreateOptions>) {
|
||||||
components: GossipSubComponents,
|
if (!this.isRelayPubSub(libp2p.pubsub)) {
|
||||||
options?: Partial<RelayCreateOptions>
|
throw Error(
|
||||||
) {
|
`Failed to initialize Relay. libp2p.pubsub does not support ${Relay.multicodec}`
|
||||||
options = Object.assign(options ?? {}, {
|
);
|
||||||
// Ensure that no signature is included nor expected in the messages.
|
}
|
||||||
globalSignaturePolicy: SignaturePolicy.StrictNoSign,
|
|
||||||
fallbackToFloodsub: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
super(components, options);
|
|
||||||
this.multicodecs = constants.RelayCodecs;
|
|
||||||
|
|
||||||
|
this.gossipSub = libp2p.pubsub as GossipSub;
|
||||||
this.pubSubTopic = options?.pubSubTopic ?? DefaultPubSubTopic;
|
this.pubSubTopic = options?.pubSubTopic ?? DefaultPubSubTopic;
|
||||||
|
|
||||||
|
if (this.gossipSub.isStarted()) {
|
||||||
|
this.gossipSubSubscribe(this.pubSubTopic);
|
||||||
|
}
|
||||||
|
|
||||||
this.observers = new Map();
|
this.observers = new Map();
|
||||||
|
|
||||||
// TODO: User might want to decide what decoder should be used (e.g. for RLN)
|
// TODO: User might want to decide what decoder should be used (e.g. for RLN)
|
||||||
|
@ -82,8 +83,12 @@ class Relay extends GossipSub implements IRelay {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
public async start(): Promise<void> {
|
public async start(): Promise<void> {
|
||||||
await super.start();
|
if (this.gossipSub.isStarted()) {
|
||||||
this.subscribe(this.pubSubTopic);
|
throw Error("GossipSub already started.");
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.gossipSub.start();
|
||||||
|
this.gossipSubSubscribe(this.pubSubTopic);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -96,7 +101,7 @@ class Relay extends GossipSub implements IRelay {
|
||||||
return { recipients: [] };
|
return { recipients: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.publish(this.pubSubTopic, msg);
|
return this.gossipSub.publish(this.pubSubTopic, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -104,22 +109,38 @@ class Relay extends GossipSub implements IRelay {
|
||||||
*
|
*
|
||||||
* @returns Function to delete the observer
|
* @returns Function to delete the observer
|
||||||
*/
|
*/
|
||||||
addObserver<T extends IDecodedMessage>(
|
public subscribe<T extends IDecodedMessage>(
|
||||||
decoder: IDecoder<T>,
|
decoders: IDecoder<T> | IDecoder<T>[],
|
||||||
callback: Callback<T>
|
callback: Callback<T>
|
||||||
): () => void {
|
): () => void {
|
||||||
const observer = {
|
const contentTopicToObservers = Array.isArray(decoders)
|
||||||
decoder,
|
? toObservers(decoders, callback)
|
||||||
callback,
|
: toObservers([decoders], callback);
|
||||||
};
|
|
||||||
const contentTopic = decoder.contentTopic;
|
|
||||||
|
|
||||||
pushOrInitMapSet(this.observers, contentTopic, observer);
|
for (const contentTopic of contentTopicToObservers.keys()) {
|
||||||
|
const currObservers = this.observers.get(contentTopic) || new Set();
|
||||||
|
const newObservers =
|
||||||
|
contentTopicToObservers.get(contentTopic) || new Set();
|
||||||
|
|
||||||
|
this.observers.set(contentTopic, union(currObservers, newObservers));
|
||||||
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
const observers = this.observers.get(contentTopic);
|
for (const contentTopic of contentTopicToObservers.keys()) {
|
||||||
if (observers) {
|
const currentObservers = this.observers.get(contentTopic) || new Set();
|
||||||
observers.delete(observer);
|
const observersToRemove =
|
||||||
|
contentTopicToObservers.get(contentTopic) || new Set();
|
||||||
|
|
||||||
|
const nextObservers = leftMinusJoin(
|
||||||
|
currentObservers,
|
||||||
|
observersToRemove
|
||||||
|
);
|
||||||
|
|
||||||
|
if (nextObservers.size) {
|
||||||
|
this.observers.set(contentTopic, nextObservers);
|
||||||
|
} else {
|
||||||
|
this.observers.delete(contentTopic);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -130,6 +151,10 @@ class Relay extends GossipSub implements IRelay {
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getMeshPeers(topic?: TopicStr): PeerIdStr[] {
|
||||||
|
return this.gossipSub.getMeshPeers(topic ?? this.pubSubTopic);
|
||||||
|
}
|
||||||
|
|
||||||
private async processIncomingMessage<T extends IDecodedMessage>(
|
private async processIncomingMessage<T extends IDecodedMessage>(
|
||||||
pubSubTopic: string,
|
pubSubTopic: string,
|
||||||
bytes: Uint8Array
|
bytes: Uint8Array
|
||||||
|
@ -168,8 +193,8 @@ class Relay extends GossipSub implements IRelay {
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
subscribe(pubSubTopic: string): void {
|
private gossipSubSubscribe(pubSubTopic: string): void {
|
||||||
this.addEventListener(
|
this.gossipSub.addEventListener(
|
||||||
"gossipsub:message",
|
"gossipsub:message",
|
||||||
async (event: CustomEvent<GossipsubMessage>) => {
|
async (event: CustomEvent<GossipsubMessage>) => {
|
||||||
if (event.detail.msg.topic !== pubSubTopic) return;
|
if (event.detail.msg.topic !== pubSubTopic) return;
|
||||||
|
@ -182,24 +207,76 @@ class Relay extends GossipSub implements IRelay {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
this.topicValidators.set(pubSubTopic, messageValidator);
|
this.gossipSub.topicValidators.set(pubSubTopic, messageValidator);
|
||||||
super.subscribe(pubSubTopic);
|
this.gossipSub.subscribe(pubSubTopic);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsubscribe(pubSubTopic: TopicStr): void {
|
private isRelayPubSub(pubsub: PubSub): boolean {
|
||||||
super.unsubscribe(pubSubTopic);
|
return pubsub?.multicodecs?.includes(Relay.multicodec) || false;
|
||||||
this.topicValidators.delete(pubSubTopic);
|
|
||||||
}
|
|
||||||
|
|
||||||
getMeshPeers(topic?: TopicStr): PeerIdStr[] {
|
|
||||||
return super.getMeshPeers(topic ?? this.pubSubTopic);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Relay.multicodec = constants.RelayCodecs[constants.RelayCodecs.length - 1];
|
|
||||||
|
|
||||||
export function wakuRelay(
|
export function wakuRelay(
|
||||||
init: Partial<RelayCreateOptions> = {}
|
init: Partial<ProtocolCreateOptions> = {}
|
||||||
): (components: GossipSubComponents) => IRelay {
|
): (libp2p: Libp2p) => IRelay {
|
||||||
return (components: GossipSubComponents) => new Relay(components, init);
|
return (libp2p: Libp2p) => new Relay(libp2p, init);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function wakuGossipSub(
|
||||||
|
init: Partial<RelayCreateOptions> = {}
|
||||||
|
): (components: GossipSubComponents) => GossipSub {
|
||||||
|
return (components: GossipSubComponents) => {
|
||||||
|
init = {
|
||||||
|
...init,
|
||||||
|
// Ensure that no signature is included nor expected in the messages.
|
||||||
|
globalSignaturePolicy: SignaturePolicy.StrictNoSign,
|
||||||
|
fallbackToFloodsub: false,
|
||||||
|
};
|
||||||
|
const pubsub = new GossipSub(components, init);
|
||||||
|
pubsub.multicodecs = constants.RelayCodecs;
|
||||||
|
return pubsub;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function toObservers<T extends IDecodedMessage>(
|
||||||
|
decoders: IDecoder<T>[],
|
||||||
|
callback: Callback<T>
|
||||||
|
): Map<ContentTopic, Set<Observer<T>>> {
|
||||||
|
const contentTopicToDecoders = Array.from(
|
||||||
|
groupByContentTopic(decoders).entries()
|
||||||
|
);
|
||||||
|
|
||||||
|
const contentTopicToObserversEntries = contentTopicToDecoders.map(
|
||||||
|
([contentTopic, decoders]) =>
|
||||||
|
[
|
||||||
|
contentTopic,
|
||||||
|
new Set(
|
||||||
|
decoders.map(
|
||||||
|
(decoder) =>
|
||||||
|
({
|
||||||
|
decoder,
|
||||||
|
callback,
|
||||||
|
} as Observer<T>)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
] as [ContentTopic, Set<Observer<T>>]
|
||||||
|
);
|
||||||
|
|
||||||
|
return new Map(contentTopicToObserversEntries);
|
||||||
|
}
|
||||||
|
|
||||||
|
function union(left: Set<unknown>, right: Set<unknown>): Set<unknown> {
|
||||||
|
for (const val of right.values()) {
|
||||||
|
left.add(val);
|
||||||
|
}
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
function leftMinusJoin(left: Set<unknown>, right: Set<unknown>): Set<unknown> {
|
||||||
|
for (const val of right.values()) {
|
||||||
|
if (left.has(val)) {
|
||||||
|
left.delete(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return left;
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,7 +105,7 @@ async function waitForGossipSubPeerInMesh(waku: IRelay): Promise<void> {
|
||||||
let peers = waku.getMeshPeers();
|
let peers = waku.getMeshPeers();
|
||||||
|
|
||||||
while (peers.length == 0) {
|
while (peers.length == 0) {
|
||||||
await pEvent(waku, "gossipsub:heartbeat");
|
await pEvent(waku.gossipSub, "gossipsub:heartbeat");
|
||||||
peers = waku.getMeshPeers();
|
peers = waku.getMeshPeers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import type { Stream } from "@libp2p/interface-connection";
|
import type { Stream } from "@libp2p/interface-connection";
|
||||||
import type { Libp2p } from "@libp2p/interface-libp2p";
|
import type { Libp2p } from "@libp2p/interface-libp2p";
|
||||||
import type { PeerId } from "@libp2p/interface-peer-id";
|
import type { PeerId } from "@libp2p/interface-peer-id";
|
||||||
import type { PubSub } from "@libp2p/interface-pubsub";
|
|
||||||
import type { Multiaddr } from "@multiformats/multiaddr";
|
import type { Multiaddr } from "@multiformats/multiaddr";
|
||||||
import type {
|
import type {
|
||||||
IFilter,
|
IFilter,
|
||||||
|
@ -14,7 +13,6 @@ import { Protocols } from "@waku/interfaces";
|
||||||
import debug from "debug";
|
import debug from "debug";
|
||||||
|
|
||||||
import { ConnectionManager } from "./connection_manager.js";
|
import { ConnectionManager } from "./connection_manager.js";
|
||||||
import * as relayConstants from "./relay/constants.js";
|
|
||||||
|
|
||||||
export const DefaultPingKeepAliveValueSecs = 0;
|
export const DefaultPingKeepAliveValueSecs = 0;
|
||||||
export const DefaultRelayKeepAliveValueSecs = 5 * 60;
|
export const DefaultRelayKeepAliveValueSecs = 5 * 60;
|
||||||
|
@ -57,7 +55,8 @@ export class WakuNode implements Waku {
|
||||||
libp2p: Libp2p,
|
libp2p: Libp2p,
|
||||||
store?: (libp2p: Libp2p) => IStore,
|
store?: (libp2p: Libp2p) => IStore,
|
||||||
lightPush?: (libp2p: Libp2p) => ILightPush,
|
lightPush?: (libp2p: Libp2p) => ILightPush,
|
||||||
filter?: (libp2p: Libp2p) => IFilter
|
filter?: (libp2p: Libp2p) => IFilter,
|
||||||
|
relay?: (libp2p: Libp2p) => IRelay
|
||||||
) {
|
) {
|
||||||
this.libp2p = libp2p;
|
this.libp2p = libp2p;
|
||||||
|
|
||||||
|
@ -71,8 +70,8 @@ export class WakuNode implements Waku {
|
||||||
this.lightPush = lightPush(libp2p);
|
this.lightPush = lightPush(libp2p);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isRelay(libp2p.pubsub)) {
|
if (relay) {
|
||||||
this.relay = libp2p.pubsub;
|
this.relay = relay(libp2p);
|
||||||
}
|
}
|
||||||
|
|
||||||
const pingKeepAlive =
|
const pingKeepAlive =
|
||||||
|
@ -120,7 +119,9 @@ export class WakuNode implements Waku {
|
||||||
const codecs: string[] = [];
|
const codecs: string[] = [];
|
||||||
if (_protocols.includes(Protocols.Relay)) {
|
if (_protocols.includes(Protocols.Relay)) {
|
||||||
if (this.relay) {
|
if (this.relay) {
|
||||||
this.relay.multicodecs.forEach((codec) => codecs.push(codec));
|
this.relay.gossipSub.multicodecs.forEach((codec: string) =>
|
||||||
|
codecs.push(codec)
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
log(
|
log(
|
||||||
"Relay codec not included in dial codec: protocol not mounted locally"
|
"Relay codec not included in dial codec: protocol not mounted locally"
|
||||||
|
@ -188,16 +189,3 @@ export class WakuNode implements Waku {
|
||||||
return localMultiaddr + "/p2p/" + this.libp2p.peerId.toString();
|
return localMultiaddr + "/p2p/" + this.libp2p.peerId.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isRelay(pubsub: PubSub): pubsub is IRelay {
|
|
||||||
if (pubsub) {
|
|
||||||
try {
|
|
||||||
return pubsub.multicodecs.includes(
|
|
||||||
relayConstants.RelayCodecs[relayConstants.RelayCodecs.length - 1]
|
|
||||||
);
|
|
||||||
// Exception is expected if `libp2p` was not instantiated with pubsub
|
|
||||||
// eslint-disable-next-line no-empty
|
|
||||||
} catch (e) {}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import type { GossipSub } from "@chainsafe/libp2p-gossipsub";
|
||||||
import { noise } from "@chainsafe/libp2p-noise";
|
import { noise } from "@chainsafe/libp2p-noise";
|
||||||
import type { Libp2p } from "@libp2p/interface-libp2p";
|
import type { Libp2p } from "@libp2p/interface-libp2p";
|
||||||
import type { PeerDiscovery } from "@libp2p/interface-peer-discovery";
|
import type { PeerDiscovery } from "@libp2p/interface-peer-discovery";
|
||||||
|
@ -8,6 +9,7 @@ import {
|
||||||
DefaultUserAgent,
|
DefaultUserAgent,
|
||||||
RelayCreateOptions,
|
RelayCreateOptions,
|
||||||
wakuFilter,
|
wakuFilter,
|
||||||
|
wakuGossipSub,
|
||||||
wakuLightPush,
|
wakuLightPush,
|
||||||
WakuNode,
|
WakuNode,
|
||||||
WakuOptions,
|
WakuOptions,
|
||||||
|
@ -17,7 +19,6 @@ import {
|
||||||
import { enrTree, wakuDnsDiscovery } from "@waku/dns-discovery";
|
import { enrTree, wakuDnsDiscovery } from "@waku/dns-discovery";
|
||||||
import type {
|
import type {
|
||||||
FullNode,
|
FullNode,
|
||||||
IRelay,
|
|
||||||
LightNode,
|
LightNode,
|
||||||
ProtocolCreateOptions,
|
ProtocolCreateOptions,
|
||||||
RelayNode,
|
RelayNode,
|
||||||
|
@ -85,12 +86,21 @@ export async function createRelayNode(
|
||||||
}
|
}
|
||||||
|
|
||||||
const libp2p = await defaultLibp2p(
|
const libp2p = await defaultLibp2p(
|
||||||
wakuRelay(options),
|
wakuGossipSub(options),
|
||||||
libp2pOptions,
|
libp2pOptions,
|
||||||
options?.userAgent
|
options?.userAgent
|
||||||
);
|
);
|
||||||
|
|
||||||
return new WakuNode(options ?? {}, libp2p) as RelayNode;
|
const relay = wakuRelay(options);
|
||||||
|
|
||||||
|
return new WakuNode(
|
||||||
|
options ?? {},
|
||||||
|
libp2p,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
relay
|
||||||
|
) as RelayNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -117,7 +127,7 @@ export async function createFullNode(
|
||||||
}
|
}
|
||||||
|
|
||||||
const libp2p = await defaultLibp2p(
|
const libp2p = await defaultLibp2p(
|
||||||
wakuRelay(options),
|
wakuGossipSub(options),
|
||||||
libp2pOptions,
|
libp2pOptions,
|
||||||
options?.userAgent
|
options?.userAgent
|
||||||
);
|
);
|
||||||
|
@ -125,13 +135,15 @@ export async function createFullNode(
|
||||||
const store = wakuStore(options);
|
const store = wakuStore(options);
|
||||||
const lightPush = wakuLightPush(options);
|
const lightPush = wakuLightPush(options);
|
||||||
const filter = wakuFilter(options);
|
const filter = wakuFilter(options);
|
||||||
|
const relay = wakuRelay(options);
|
||||||
|
|
||||||
return new WakuNode(
|
return new WakuNode(
|
||||||
options ?? {},
|
options ?? {},
|
||||||
libp2p,
|
libp2p,
|
||||||
store,
|
store,
|
||||||
lightPush,
|
lightPush,
|
||||||
filter
|
filter,
|
||||||
|
relay
|
||||||
) as FullNode;
|
) as FullNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +154,7 @@ export function defaultPeerDiscovery(): (
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function defaultLibp2p(
|
export async function defaultLibp2p(
|
||||||
wakuRelay?: (components: Libp2pComponents) => IRelay,
|
wakuGossipSub?: (components: Libp2pComponents) => GossipSub,
|
||||||
options?: Partial<Libp2pOptions>,
|
options?: Partial<Libp2pOptions>,
|
||||||
userAgent?: string
|
userAgent?: string
|
||||||
): Promise<Libp2p> {
|
): Promise<Libp2p> {
|
||||||
|
@ -157,7 +169,7 @@ export async function defaultLibp2p(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as Libp2pOptions,
|
} as Libp2pOptions,
|
||||||
wakuRelay ? { pubsub: wakuRelay } : {},
|
wakuGossipSub ? { pubsub: wakuGossipSub } : {},
|
||||||
options ?? {}
|
options ?? {}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,4 @@
|
||||||
import type { IDecodedMessage, IDecoder } from "./message.js";
|
import type { PointToPointProtocol } from "./protocols.js";
|
||||||
import type {
|
import type { IReceiver } from "./receiver.js";
|
||||||
Callback,
|
|
||||||
PointToPointProtocol,
|
|
||||||
ProtocolOptions,
|
|
||||||
} from "./protocols.js";
|
|
||||||
|
|
||||||
export interface IFilter extends PointToPointProtocol {
|
export type IFilter = IReceiver & PointToPointProtocol;
|
||||||
subscribe: <T extends IDecodedMessage>(
|
|
||||||
decoders: IDecoder<T>[],
|
|
||||||
callback: Callback<T>,
|
|
||||||
opts?: ProtocolOptions
|
|
||||||
) => Promise<() => Promise<void>>;
|
|
||||||
}
|
|
||||||
|
|
|
@ -9,3 +9,4 @@ export * from "./store.js";
|
||||||
export * from "./waku.js";
|
export * from "./waku.js";
|
||||||
export * from "./connection_manager.js";
|
export * from "./connection_manager.js";
|
||||||
export * from "./sender.js";
|
export * from "./sender.js";
|
||||||
|
export * from "./receiver.js";
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
import type { IDecodedMessage, IDecoder } from "./message.js";
|
||||||
|
import type { Callback, ProtocolOptions } from "./protocols.js";
|
||||||
|
|
||||||
|
type Unsubscribe = () => void | Promise<void>;
|
||||||
|
type PubSubTopic = string;
|
||||||
|
type ContentTopic = string;
|
||||||
|
|
||||||
|
export type ActiveSubscriptions = Map<PubSubTopic, ContentTopic[]>;
|
||||||
|
|
||||||
|
export interface IReceiver {
|
||||||
|
subscribe: <T extends IDecodedMessage>(
|
||||||
|
decoders: IDecoder<T> | IDecoder<T>[],
|
||||||
|
callback: Callback<T>,
|
||||||
|
opts?: ProtocolOptions
|
||||||
|
) => Unsubscribe | Promise<Unsubscribe>;
|
||||||
|
getActiveSubscriptions: () => ActiveSubscriptions;
|
||||||
|
}
|
|
@ -1,21 +1,13 @@
|
||||||
import type { GossipSub } from "@chainsafe/libp2p-gossipsub";
|
import type { GossipSub } from "@chainsafe/libp2p-gossipsub";
|
||||||
|
import type { PeerIdStr, TopicStr } from "@chainsafe/libp2p-gossipsub/types";
|
||||||
|
|
||||||
import type { IDecodedMessage, IDecoder } from "./message.js";
|
import { IReceiver } from "./receiver.js";
|
||||||
import type { Callback } from "./protocols.js";
|
|
||||||
import type { ISender } from "./sender.js";
|
import type { ISender } from "./sender.js";
|
||||||
|
|
||||||
type PubSubTopic = string;
|
|
||||||
type ContentTopic = string;
|
|
||||||
|
|
||||||
export type ActiveSubscriptions = Map<PubSubTopic, ContentTopic[]>;
|
|
||||||
|
|
||||||
interface IRelayAPI {
|
interface IRelayAPI {
|
||||||
addObserver: <T extends IDecodedMessage>(
|
readonly gossipSub: GossipSub;
|
||||||
decoder: IDecoder<T>,
|
start: () => Promise<void>;
|
||||||
callback: Callback<T>
|
getMeshPeers: (topic?: TopicStr) => PeerIdStr[];
|
||||||
) => () => void;
|
|
||||||
getMeshPeers: () => string[];
|
|
||||||
getActiveSubscriptions: () => ActiveSubscriptions | undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type IRelay = IRelayAPI & GossipSub & ISender;
|
export type IRelay = IRelayAPI & ISender & IReceiver;
|
||||||
|
|
|
@ -78,7 +78,7 @@ describe("Waku Filter", () => {
|
||||||
messageCount++;
|
messageCount++;
|
||||||
expect(msg.contentTopic).to.eq(TestContentTopic);
|
expect(msg.contentTopic).to.eq(TestContentTopic);
|
||||||
};
|
};
|
||||||
await waku.filter.subscribe([TestDecoder], callback);
|
await waku.filter.subscribe(TestDecoder, callback);
|
||||||
|
|
||||||
await delay(200);
|
await delay(200);
|
||||||
await waku.lightPush.send(TestEncoder, {
|
await waku.lightPush.send(TestEncoder, {
|
||||||
|
|
|
@ -121,7 +121,7 @@ describe("Waku Relay [node only]", () => {
|
||||||
|
|
||||||
const receivedMsgPromise: Promise<DecodedMessage> = new Promise(
|
const receivedMsgPromise: Promise<DecodedMessage> = new Promise(
|
||||||
(resolve) => {
|
(resolve) => {
|
||||||
waku2.relay.addObserver(TestDecoder, resolve);
|
waku2.relay.subscribe([TestDecoder], resolve);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -152,12 +152,12 @@ describe("Waku Relay [node only]", () => {
|
||||||
const barDecoder = createDecoder(barContentTopic);
|
const barDecoder = createDecoder(barContentTopic);
|
||||||
|
|
||||||
const fooMessages: DecodedMessage[] = [];
|
const fooMessages: DecodedMessage[] = [];
|
||||||
waku2.relay.addObserver(fooDecoder, (msg) => {
|
waku2.relay.subscribe([fooDecoder], (msg) => {
|
||||||
fooMessages.push(msg);
|
fooMessages.push(msg);
|
||||||
});
|
});
|
||||||
|
|
||||||
const barMessages: DecodedMessage[] = [];
|
const barMessages: DecodedMessage[] = [];
|
||||||
waku2.relay.addObserver(barDecoder, (msg) => {
|
waku2.relay.subscribe([barDecoder], (msg) => {
|
||||||
barMessages.push(msg);
|
barMessages.push(msg);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -207,10 +207,10 @@ describe("Waku Relay [node only]", () => {
|
||||||
const symDecoder = createSymDecoder(symTopic, symKey);
|
const symDecoder = createSymDecoder(symTopic, symKey);
|
||||||
|
|
||||||
const msgs: DecodedMessage[] = [];
|
const msgs: DecodedMessage[] = [];
|
||||||
waku2.relay.addObserver(eciesDecoder, (wakuMsg) => {
|
waku2.relay.subscribe([eciesDecoder], (wakuMsg) => {
|
||||||
msgs.push(wakuMsg);
|
msgs.push(wakuMsg);
|
||||||
});
|
});
|
||||||
waku2.relay.addObserver(symDecoder, (wakuMsg) => {
|
waku2.relay.subscribe([symDecoder], (wakuMsg) => {
|
||||||
msgs.push(wakuMsg);
|
msgs.push(wakuMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -239,10 +239,10 @@ describe("Waku Relay [node only]", () => {
|
||||||
// The promise **fails** if we receive a message on this observer.
|
// The promise **fails** if we receive a message on this observer.
|
||||||
const receivedMsgPromise: Promise<DecodedMessage> = new Promise(
|
const receivedMsgPromise: Promise<DecodedMessage> = new Promise(
|
||||||
(resolve, reject) => {
|
(resolve, reject) => {
|
||||||
const deleteObserver = waku2.relay.addObserver(
|
const deleteObserver = waku2.relay.subscribe(
|
||||||
createDecoder(contentTopic),
|
[createDecoder(contentTopic)],
|
||||||
reject
|
reject
|
||||||
);
|
) as () => void;
|
||||||
deleteObserver();
|
deleteObserver();
|
||||||
setTimeout(resolve, 500);
|
setTimeout(resolve, 500);
|
||||||
}
|
}
|
||||||
|
@ -313,7 +313,7 @@ describe("Waku Relay [node only]", () => {
|
||||||
|
|
||||||
const waku2ReceivedMsgPromise: Promise<DecodedMessage> = new Promise(
|
const waku2ReceivedMsgPromise: Promise<DecodedMessage> = new Promise(
|
||||||
(resolve) => {
|
(resolve) => {
|
||||||
waku2.relay.addObserver(TestDecoder, resolve);
|
waku2.relay.subscribe([TestDecoder], resolve);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -321,7 +321,7 @@ describe("Waku Relay [node only]", () => {
|
||||||
// pubsub topic.
|
// pubsub topic.
|
||||||
const waku3NoMsgPromise: Promise<DecodedMessage> = new Promise(
|
const waku3NoMsgPromise: Promise<DecodedMessage> = new Promise(
|
||||||
(resolve, reject) => {
|
(resolve, reject) => {
|
||||||
waku3.relay.addObserver(TestDecoder, reject);
|
waku3.relay.subscribe([TestDecoder], reject);
|
||||||
setTimeout(resolve, 1000);
|
setTimeout(resolve, 1000);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -401,7 +401,7 @@ describe("Waku Relay [node only]", () => {
|
||||||
|
|
||||||
const receivedMsgPromise: Promise<DecodedMessage> = new Promise(
|
const receivedMsgPromise: Promise<DecodedMessage> = new Promise(
|
||||||
(resolve) => {
|
(resolve) => {
|
||||||
waku.relay.addObserver<DecodedMessage>(TestDecoder, (msg) =>
|
waku.relay.subscribe<DecodedMessage>(TestDecoder, (msg) =>
|
||||||
resolve(msg)
|
resolve(msg)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -472,7 +472,7 @@ describe("Waku Relay [node only]", () => {
|
||||||
|
|
||||||
const waku2ReceivedMsgPromise: Promise<DecodedMessage> = new Promise(
|
const waku2ReceivedMsgPromise: Promise<DecodedMessage> = new Promise(
|
||||||
(resolve) => {
|
(resolve) => {
|
||||||
waku2.relay.addObserver(TestDecoder, resolve);
|
waku2.relay.subscribe(TestDecoder, resolve);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -178,7 +178,7 @@ describe("Decryption Keys", () => {
|
||||||
|
|
||||||
const receivedMsgPromise: Promise<DecodedMessage> = new Promise(
|
const receivedMsgPromise: Promise<DecodedMessage> = new Promise(
|
||||||
(resolve) => {
|
(resolve) => {
|
||||||
waku2.relay.addObserver(decoder, resolve);
|
waku2.relay.subscribe([decoder], resolve);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue