move relay functionality to separate package

This commit is contained in:
Sasha 2024-07-23 15:47:42 +02:00
parent a739ada33a
commit a4e8aab124
No known key found for this signature in database
23 changed files with 384 additions and 422 deletions

View File

@ -9,10 +9,10 @@
"packages/message-hash",
"packages/enr",
"packages/core",
"packages/relay",
"packages/discovery",
"packages/message-encryption",
"packages/sdk",
"packages/relay",
"packages/tests",
"packages/browser-tests",
"packages/build-utils",

View File

@ -42,10 +42,3 @@ export interface RelayNode extends Waku {
filter: undefined;
lightPush: undefined;
}
export interface FullNode extends Waku {
relay: IRelay;
store: IStoreSDK;
filter: IFilterSDK;
lightPush: ILightPushSDK;
}

View File

@ -52,6 +52,7 @@
"@chainsafe/libp2p-gossipsub": "^12.0.0",
"@noble/hashes": "^1.3.2",
"@waku/core": "0.0.30",
"@waku/sdk": "0.0.26",
"@waku/interfaces": "0.0.25",
"@waku/proto": "0.0.7",
"@waku/utils": "0.0.18",
@ -67,6 +68,7 @@
"rollup": "^4.12.0"
},
"peerDependencies": {
"@waku/sdk": "0.0.26",
"@waku/core": "0.0.30",
"@waku/interfaces": "0.0.25",
"@waku/proto": "0.0.7",

View File

@ -0,0 +1,40 @@
import { type RelayNode } from "@waku/interfaces";
import {
createLibp2pAndUpdateOptions,
CreateWakuNodeOptions,
WakuNode,
WakuOptions
} from "@waku/sdk";
import { RelayCreateOptions, wakuGossipSub, wakuRelay } from "./relay.js";
/**
* Create a Waku node that uses Waku Relay to send and receive messages,
* enabling some privacy preserving properties.
* * @remarks
* This function creates a Relay Node using the Waku Relay protocol.
* While it is technically possible to use this function in a browser environment,
* it is not recommended due to potential performance issues and limited browser capabilities.
* If you are developing a browser-based application, consider alternative approaches like creating a Light Node
* or use this function with caution.
*/
export async function createRelayNode(
options: CreateWakuNodeOptions & Partial<RelayCreateOptions> = {
pubsubTopics: []
}
): Promise<RelayNode> {
options = {
...options,
libp2p: {
...options.libp2p,
services: {
pubsub: wakuGossipSub(options)
}
}
};
const libp2p = await createLibp2pAndUpdateOptions(options);
const relay = wakuRelay(options?.pubsubTopics || [])(libp2p);
return new WakuNode(options as WakuOptions, libp2p, {}, relay) as RelayNode;
}

View File

@ -1,317 +1,2 @@
import {
GossipSub,
GossipSubComponents,
GossipsubMessage,
GossipsubOpts
} from "@chainsafe/libp2p-gossipsub";
import type { PeerIdStr, TopicStr } from "@chainsafe/libp2p-gossipsub/types";
import { SignaturePolicy } from "@chainsafe/libp2p-gossipsub/types";
import type { PubSub as Libp2pPubsub, PeerId } from "@libp2p/interface";
import { sha256 } from "@noble/hashes/sha256";
import {
ActiveSubscriptions,
Callback,
DefaultPubsubTopic,
IAsyncIterator,
IDecodedMessage,
IDecoder,
IEncoder,
IMessage,
IRelay,
Libp2p,
ProtocolCreateOptions,
ProtocolError,
PubsubTopic,
SDKProtocolResult
} from "@waku/interfaces";
import { isWireSizeUnderCap, toAsyncIterator } from "@waku/utils";
import { pushOrInitMapSet } from "@waku/utils";
import { Logger } from "@waku/utils";
import { RelayCodecs } from "./constants.js";
import { messageValidator } from "./message_validator.js";
import { TopicOnlyDecoder } from "./topic_only_message.js";
const log = new Logger("relay");
export type Observer<T extends IDecodedMessage> = {
decoder: IDecoder<T>;
callback: Callback<T>;
};
export type RelayCreateOptions = ProtocolCreateOptions & GossipsubOpts;
export type ContentTopic = string;
/**
* Implements the [Waku v2 Relay protocol](https://rfc.vac.dev/spec/11/).
* Throws if libp2p.pubsub does not support Waku Relay
*/
class Relay implements IRelay {
public readonly pubsubTopics: Set<PubsubTopic>;
private defaultDecoder: IDecoder<IDecodedMessage>;
public static multicodec: string = RelayCodecs[0];
public readonly gossipSub: GossipSub;
/**
* observers called when receiving new message.
* Observers under key `""` are always called.
*/
private observers: Map<PubsubTopic, Map<ContentTopic, Set<unknown>>>;
public constructor(libp2p: Libp2p, pubsubTopics: PubsubTopic[]) {
if (!this.isRelayPubsub(libp2p.services.pubsub)) {
throw Error(
`Failed to initialize Relay. libp2p.pubsub does not support ${Relay.multicodec}`
);
}
this.gossipSub = libp2p.services.pubsub as GossipSub;
this.pubsubTopics = new Set(pubsubTopics);
if (this.gossipSub.isStarted()) {
this.subscribeToAllTopics();
}
this.observers = new Map();
// Default PubsubTopic decoder
// TODO: User might want to decide what decoder should be used (e.g. for RLN)
this.defaultDecoder = new TopicOnlyDecoder();
}
/**
* Mounts the gossipsub protocol onto the libp2p node
* and subscribes to all the topics.
*
* @override
* @returns {void}
*/
public async start(): Promise<void> {
if (this.gossipSub.isStarted()) {
throw Error("GossipSub already started.");
}
await this.gossipSub.start();
this.subscribeToAllTopics();
}
/**
* Send Waku message.
*/
public async send(
encoder: IEncoder,
message: IMessage
): Promise<SDKProtocolResult> {
const successes: PeerId[] = [];
const { pubsubTopic } = encoder;
if (!this.pubsubTopics.has(pubsubTopic)) {
log.error("Failed to send waku relay: topic not configured");
return {
successes,
failures: [
{
error: ProtocolError.TOPIC_NOT_CONFIGURED
}
]
};
}
const msg = await encoder.toWire(message);
if (!msg) {
log.error("Failed to encode message, aborting publish");
return {
successes,
failures: [
{
error: ProtocolError.ENCODE_FAILED
}
]
};
}
if (!isWireSizeUnderCap(msg)) {
log.error("Failed to send waku relay: message is bigger that 1MB");
return {
successes,
failures: [
{
error: ProtocolError.SIZE_TOO_BIG
}
]
};
}
const { recipients } = await this.gossipSub.publish(pubsubTopic, msg);
return {
successes: recipients,
failures: []
};
}
public subscribe<T extends IDecodedMessage>(
decoders: IDecoder<T> | IDecoder<T>[],
callback: Callback<T>
): () => void {
const observers: Array<[PubsubTopic, Observer<T>]> = [];
for (const decoder of Array.isArray(decoders) ? decoders : [decoders]) {
const { pubsubTopic } = decoder;
const ctObs: Map<ContentTopic, Set<Observer<T>>> = this.observers.get(
pubsubTopic
) ?? new Map();
const observer = { pubsubTopic, decoder, callback };
pushOrInitMapSet(ctObs, decoder.contentTopic, observer);
this.observers.set(pubsubTopic, ctObs);
observers.push([pubsubTopic, observer]);
}
return () => {
this.removeObservers(observers);
};
}
private removeObservers<T extends IDecodedMessage>(
observers: Array<[PubsubTopic, Observer<T>]>
): void {
for (const [pubsubTopic, observer] of observers) {
const ctObs = this.observers.get(pubsubTopic);
if (!ctObs) continue;
const contentTopic = observer.decoder.contentTopic;
const _obs = ctObs.get(contentTopic);
if (!_obs) continue;
_obs.delete(observer);
ctObs.set(contentTopic, _obs);
this.observers.set(pubsubTopic, ctObs);
}
}
public toSubscriptionIterator<T extends IDecodedMessage>(
decoders: IDecoder<T> | IDecoder<T>[]
): Promise<IAsyncIterator<T>> {
return toAsyncIterator(this, decoders);
}
public getActiveSubscriptions(): ActiveSubscriptions {
const map = new Map();
for (const pubsubTopic of this.pubsubTopics) {
map.set(pubsubTopic, Array.from(this.observers.keys()));
}
return map;
}
public getMeshPeers(topic: TopicStr = DefaultPubsubTopic): PeerIdStr[] {
return this.gossipSub.getMeshPeers(topic);
}
private subscribeToAllTopics(): void {
for (const pubsubTopic of this.pubsubTopics) {
this.gossipSubSubscribe(pubsubTopic);
}
}
private async processIncomingMessage<T extends IDecodedMessage>(
pubsubTopic: string,
bytes: Uint8Array
): Promise<void> {
const topicOnlyMsg = await this.defaultDecoder.fromWireToProtoObj(bytes);
if (!topicOnlyMsg || !topicOnlyMsg.contentTopic) {
log.warn("Message does not have a content topic, skipping");
return;
}
// Retrieve the map of content topics for the given pubsubTopic
const contentTopicMap = this.observers.get(pubsubTopic);
if (!contentTopicMap) {
return;
}
// Retrieve the set of observers for the given contentTopic
const observers = contentTopicMap.get(topicOnlyMsg.contentTopic) as Set<
Observer<T>
>;
if (!observers) {
return;
}
await Promise.all(
Array.from(observers).map(({ decoder, callback }) => {
return (async () => {
try {
const protoMsg = await decoder.fromWireToProtoObj(bytes);
if (!protoMsg) {
log.error(
"Internal error: message previously decoded failed on 2nd pass."
);
return;
}
const msg = await decoder.fromProtoObj(pubsubTopic, protoMsg);
if (msg) {
await callback(msg);
} else {
log.error(
"Failed to decode messages on",
topicOnlyMsg.contentTopic
);
}
} catch (error) {
log.error("Error while decoding message:", error);
}
})();
})
);
}
/**
* Subscribe to a pubsub topic and start emitting Waku messages to observers.
*
* @override
*/
private gossipSubSubscribe(pubsubTopic: string): void {
this.gossipSub.addEventListener(
"gossipsub:message",
(event: CustomEvent<GossipsubMessage>) => {
if (event.detail.msg.topic !== pubsubTopic) return;
this.processIncomingMessage(
event.detail.msg.topic,
event.detail.msg.data
).catch((e) => log.error("Failed to process incoming message", e));
}
);
this.gossipSub.topicValidators.set(pubsubTopic, messageValidator);
this.gossipSub.subscribe(pubsubTopic);
}
private isRelayPubsub(pubsub: Libp2pPubsub | undefined): boolean {
return pubsub?.multicodecs?.includes(Relay.multicodec) ?? false;
}
}
export function wakuRelay(
pubsubTopics: PubsubTopic[]
): (libp2p: Libp2p) => IRelay {
return (libp2p: Libp2p) => new Relay(libp2p, pubsubTopics);
}
export function wakuGossipSub(
init: Partial<RelayCreateOptions> = {}
): (components: GossipSubComponents) => GossipSub {
return (components: GossipSubComponents) => {
init = {
...init,
msgIdFn: ({ data }) => sha256(data),
// Ensure that no signature is included nor expected in the messages.
globalSignaturePolicy: SignaturePolicy.StrictNoSign,
fallbackToFloodsub: false
};
const pubsub = new GossipSub(components, init);
pubsub.multicodecs = RelayCodecs;
return pubsub;
};
}
export * from "./relay.js";
export * from "./create.js";

317
packages/relay/src/relay.ts Normal file
View File

@ -0,0 +1,317 @@
import {
GossipSub,
GossipSubComponents,
GossipsubMessage,
GossipsubOpts
} from "@chainsafe/libp2p-gossipsub";
import type { PeerIdStr, TopicStr } from "@chainsafe/libp2p-gossipsub/types";
import { SignaturePolicy } from "@chainsafe/libp2p-gossipsub/types";
import type { PubSub as Libp2pPubsub, PeerId } from "@libp2p/interface";
import { sha256 } from "@noble/hashes/sha256";
import {
ActiveSubscriptions,
Callback,
DefaultPubsubTopic,
IAsyncIterator,
IDecodedMessage,
IDecoder,
IEncoder,
IMessage,
IRelay,
Libp2p,
ProtocolCreateOptions,
ProtocolError,
PubsubTopic,
SDKProtocolResult
} from "@waku/interfaces";
import { isWireSizeUnderCap, toAsyncIterator } from "@waku/utils";
import { pushOrInitMapSet } from "@waku/utils";
import { Logger } from "@waku/utils";
import { RelayCodecs } from "./constants.js";
import { messageValidator } from "./message_validator.js";
import { TopicOnlyDecoder } from "./topic_only_message.js";
const log = new Logger("relay");
export type Observer<T extends IDecodedMessage> = {
decoder: IDecoder<T>;
callback: Callback<T>;
};
export type RelayCreateOptions = ProtocolCreateOptions & GossipsubOpts;
export type ContentTopic = string;
/**
* Implements the [Waku v2 Relay protocol](https://rfc.vac.dev/spec/11/).
* Throws if libp2p.pubsub does not support Waku Relay
*/
class Relay implements IRelay {
public readonly pubsubTopics: Set<PubsubTopic>;
private defaultDecoder: IDecoder<IDecodedMessage>;
public static multicodec: string = RelayCodecs[0];
public readonly gossipSub: GossipSub;
/**
* observers called when receiving new message.
* Observers under key `""` are always called.
*/
private observers: Map<PubsubTopic, Map<ContentTopic, Set<unknown>>>;
public constructor(libp2p: Libp2p, pubsubTopics: PubsubTopic[]) {
if (!this.isRelayPubsub(libp2p.services.pubsub)) {
throw Error(
`Failed to initialize Relay. libp2p.pubsub does not support ${Relay.multicodec}`
);
}
this.gossipSub = libp2p.services.pubsub as GossipSub;
this.pubsubTopics = new Set(pubsubTopics);
if (this.gossipSub.isStarted()) {
this.subscribeToAllTopics();
}
this.observers = new Map();
// Default PubsubTopic decoder
// TODO: User might want to decide what decoder should be used (e.g. for RLN)
this.defaultDecoder = new TopicOnlyDecoder();
}
/**
* Mounts the gossipsub protocol onto the libp2p node
* and subscribes to all the topics.
*
* @override
* @returns {void}
*/
public async start(): Promise<void> {
if (this.gossipSub.isStarted()) {
throw Error("GossipSub already started.");
}
await this.gossipSub.start();
this.subscribeToAllTopics();
}
/**
* Send Waku message.
*/
public async send(
encoder: IEncoder,
message: IMessage
): Promise<SDKProtocolResult> {
const successes: PeerId[] = [];
const { pubsubTopic } = encoder;
if (!this.pubsubTopics.has(pubsubTopic)) {
log.error("Failed to send waku relay: topic not configured");
return {
successes,
failures: [
{
error: ProtocolError.TOPIC_NOT_CONFIGURED
}
]
};
}
const msg = await encoder.toWire(message);
if (!msg) {
log.error("Failed to encode message, aborting publish");
return {
successes,
failures: [
{
error: ProtocolError.ENCODE_FAILED
}
]
};
}
if (!isWireSizeUnderCap(msg)) {
log.error("Failed to send waku relay: message is bigger that 1MB");
return {
successes,
failures: [
{
error: ProtocolError.SIZE_TOO_BIG
}
]
};
}
const { recipients } = await this.gossipSub.publish(pubsubTopic, msg);
return {
successes: recipients,
failures: []
};
}
public subscribe<T extends IDecodedMessage>(
decoders: IDecoder<T> | IDecoder<T>[],
callback: Callback<T>
): () => void {
const observers: Array<[PubsubTopic, Observer<T>]> = [];
for (const decoder of Array.isArray(decoders) ? decoders : [decoders]) {
const { pubsubTopic } = decoder;
const ctObs: Map<ContentTopic, Set<Observer<T>>> = this.observers.get(
pubsubTopic
) ?? new Map();
const observer = { pubsubTopic, decoder, callback };
pushOrInitMapSet(ctObs, decoder.contentTopic, observer);
this.observers.set(pubsubTopic, ctObs);
observers.push([pubsubTopic, observer]);
}
return () => {
this.removeObservers(observers);
};
}
private removeObservers<T extends IDecodedMessage>(
observers: Array<[PubsubTopic, Observer<T>]>
): void {
for (const [pubsubTopic, observer] of observers) {
const ctObs = this.observers.get(pubsubTopic);
if (!ctObs) continue;
const contentTopic = observer.decoder.contentTopic;
const _obs = ctObs.get(contentTopic);
if (!_obs) continue;
_obs.delete(observer);
ctObs.set(contentTopic, _obs);
this.observers.set(pubsubTopic, ctObs);
}
}
public toSubscriptionIterator<T extends IDecodedMessage>(
decoders: IDecoder<T> | IDecoder<T>[]
): Promise<IAsyncIterator<T>> {
return toAsyncIterator(this, decoders);
}
public getActiveSubscriptions(): ActiveSubscriptions {
const map = new Map();
for (const pubsubTopic of this.pubsubTopics) {
map.set(pubsubTopic, Array.from(this.observers.keys()));
}
return map;
}
public getMeshPeers(topic: TopicStr = DefaultPubsubTopic): PeerIdStr[] {
return this.gossipSub.getMeshPeers(topic);
}
private subscribeToAllTopics(): void {
for (const pubsubTopic of this.pubsubTopics) {
this.gossipSubSubscribe(pubsubTopic);
}
}
private async processIncomingMessage<T extends IDecodedMessage>(
pubsubTopic: string,
bytes: Uint8Array
): Promise<void> {
const topicOnlyMsg = await this.defaultDecoder.fromWireToProtoObj(bytes);
if (!topicOnlyMsg || !topicOnlyMsg.contentTopic) {
log.warn("Message does not have a content topic, skipping");
return;
}
// Retrieve the map of content topics for the given pubsubTopic
const contentTopicMap = this.observers.get(pubsubTopic);
if (!contentTopicMap) {
return;
}
// Retrieve the set of observers for the given contentTopic
const observers = contentTopicMap.get(topicOnlyMsg.contentTopic) as Set<
Observer<T>
>;
if (!observers) {
return;
}
await Promise.all(
Array.from(observers).map(({ decoder, callback }) => {
return (async () => {
try {
const protoMsg = await decoder.fromWireToProtoObj(bytes);
if (!protoMsg) {
log.error(
"Internal error: message previously decoded failed on 2nd pass."
);
return;
}
const msg = await decoder.fromProtoObj(pubsubTopic, protoMsg);
if (msg) {
await callback(msg);
} else {
log.error(
"Failed to decode messages on",
topicOnlyMsg.contentTopic
);
}
} catch (error) {
log.error("Error while decoding message:", error);
}
})();
})
);
}
/**
* Subscribe to a pubsub topic and start emitting Waku messages to observers.
*
* @override
*/
private gossipSubSubscribe(pubsubTopic: string): void {
this.gossipSub.addEventListener(
"gossipsub:message",
(event: CustomEvent<GossipsubMessage>) => {
if (event.detail.msg.topic !== pubsubTopic) return;
this.processIncomingMessage(
event.detail.msg.topic,
event.detail.msg.data
).catch((e) => log.error("Failed to process incoming message", e));
}
);
this.gossipSub.topicValidators.set(pubsubTopic, messageValidator);
this.gossipSub.subscribe(pubsubTopic);
}
private isRelayPubsub(pubsub: Libp2pPubsub | undefined): boolean {
return pubsub?.multicodecs?.includes(Relay.multicodec) ?? false;
}
}
export function wakuRelay(
pubsubTopics: PubsubTopic[]
): (libp2p: Libp2p) => IRelay {
return (libp2p: Libp2p) => new Relay(libp2p, pubsubTopics);
}
export function wakuGossipSub(
init: Partial<RelayCreateOptions> = {}
): (components: GossipSubComponents) => GossipSub {
return (components: GossipSubComponents) => {
init = {
...init,
msgIdFn: ({ data }) => sha256(data),
// Ensure that no signature is included nor expected in the messages.
globalSignaturePolicy: SignaturePolicy.StrictNoSign,
fallbackToFloodsub: false
};
const pubsub = new GossipSub(components, init);
pubsub.multicodecs = RelayCodecs;
return pubsub;
};
}

View File

@ -8,10 +8,6 @@
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./relay": {
"types": "./dist/relay-node/index.d.ts",
"import": "./dist/relay-node/index.js"
}
},
"typesVersions": {
@ -72,7 +68,6 @@
"@waku/discovery": "0.0.3",
"@waku/interfaces": "0.0.25",
"@waku/proto": "^0.0.7",
"@waku/relay": "0.0.13",
"@waku/utils": "0.0.18",
"libp2p": "^1.1.2"
},
@ -93,7 +88,6 @@
"@waku/core": "0.0.30",
"@waku/interfaces": "0.0.25",
"@waku/message-hash": "^0.1.14",
"@waku/relay": "0.0.13",
"@waku/utils": "0.0.18"
},
"peerDependenciesMeta": {

View File

@ -1,2 +1,2 @@
export { createLightNode } from "./create.js";
export { defaultLibp2p } from "./libp2p.js";
export { defaultLibp2p, createLibp2pAndUpdateOptions } from "./libp2p.js";

View File

@ -1,4 +1,3 @@
import type { GossipSub } from "@chainsafe/libp2p-gossipsub";
import { noise } from "@chainsafe/libp2p-noise";
import { bootstrap } from "@libp2p/bootstrap";
import { identify } from "@libp2p/identify";
@ -15,7 +14,6 @@ import {
type Libp2pComponents,
type ShardInfo
} from "@waku/interfaces";
import { wakuGossipSub } from "@waku/relay";
import { ensureShardingConfigured, Logger } from "@waku/utils";
import { createLibp2p } from "libp2p";
@ -27,10 +25,6 @@ import {
import { defaultPeerDiscoveries } from "./discovery.js";
type PubsubService = {
pubsub?: (components: Libp2pComponents) => GossipSub;
};
type MetadataService = {
metadata?: (components: Libp2pComponents) => IMetadata;
};
@ -39,7 +33,6 @@ const logger = new Logger("sdk:create");
export async function defaultLibp2p(
shardInfo?: ShardInfo,
wakuGossipSub?: PubsubService["pubsub"],
options?: Partial<CreateLibp2pOptions>,
userAgent?: string
): Promise<Libp2p> {
@ -56,10 +49,6 @@ export async function defaultLibp2p(
/* eslint-enable no-console */
}
const pubsubService: PubsubService = wakuGossipSub
? { pubsub: wakuGossipSub }
: {};
const metadataService: MetadataService = shardInfo
? { metadata: wakuMetadata(shardInfo) }
: {};
@ -83,7 +72,6 @@ export async function defaultLibp2p(
options?.pingMaxInboundStreams ?? DefaultPingMaxInboundStreams
}),
...metadataService,
...pubsubService,
...options?.services
}
}) as any as Libp2p; // TODO: make libp2p include it;
@ -109,7 +97,6 @@ export async function createLibp2pAndUpdateOptions(
const libp2p = await defaultLibp2p(
shardInfo,
wakuGossipSub(options),
libp2pOptions,
options?.userAgent
);

View File

@ -10,7 +10,11 @@ export { utf8ToBytes, bytesToUtf8 } from "@waku/utils/bytes";
export * from "./utils/content_topic.js";
export * from "./waku.js";
export { createLightNode, defaultLibp2p } from "./create/index.js";
export {
createLightNode,
defaultLibp2p,
createLibp2pAndUpdateOptions
} from "./create/index.js";
export { wakuLightPush } from "./protocols/light_push.js";
export { wakuFilter } from "./protocols/filter.js";
export { wakuStore } from "./protocols/store.js";
@ -18,4 +22,3 @@ export { wakuStore } from "./protocols/store.js";
export * as waku from "@waku/core";
export * as utils from "@waku/utils";
export * from "@waku/interfaces";
export * as relay from "@waku/relay";

View File

@ -1,55 +0,0 @@
import { type FullNode, type RelayNode } from "@waku/interfaces";
import { RelayCreateOptions } from "@waku/relay";
import { createLibp2pAndUpdateOptions } from "../create/libp2p.js";
import { CreateWakuNodeOptions, WakuNode, WakuOptions } from "../waku.js";
/**
* Create a Waku node that uses Waku Relay to send and receive messages,
* enabling some privacy preserving properties.
* * @remarks
* This function creates a Relay Node using the Waku Relay protocol.
* While it is technically possible to use this function in a browser environment,
* it is not recommended due to potential performance issues and limited browser capabilities.
* If you are developing a browser-based application, consider alternative approaches like creating a Light Node
* or use this function with caution.
*/
export async function createRelayNode(
options: CreateWakuNodeOptions & Partial<RelayCreateOptions> = {
pubsubTopics: []
}
): Promise<RelayNode> {
const libp2p = await createLibp2pAndUpdateOptions(options);
return new WakuNode(options as WakuOptions, libp2p, {
relay: true
}) as RelayNode;
}
/**
* Create a Waku node that uses all Waku protocols.
*
* This helper is not recommended except if:
* - you are interfacing with nwaku v0.11 or below
* - you are doing some form of testing
*
* If you are building a full node, it is recommended to use
* [nwaku](github.com/status-im/nwaku) and its JSON RPC API or wip REST API.
*
* @see https://github.com/status-im/nwaku/issues/1085
* @internal
*/
export async function createFullNode(
options: CreateWakuNodeOptions & Partial<RelayCreateOptions> = {
pubsubTopics: []
}
): Promise<FullNode> {
const libp2p = await createLibp2pAndUpdateOptions(options);
return new WakuNode(options as WakuOptions, libp2p, {
filter: true,
lightpush: true,
relay: true,
store: true
}) as FullNode;
}

View File

@ -16,7 +16,6 @@ import type {
Waku
} from "@waku/interfaces";
import { Protocols } from "@waku/interfaces";
import { wakuRelay } from "@waku/relay";
import { Logger } from "@waku/utils";
import { wakuFilter } from "./protocols/filter.js";
@ -61,7 +60,6 @@ type ProtocolsEnabled = {
filter?: boolean;
lightpush?: boolean;
store?: boolean;
relay?: boolean;
};
export class WakuNode implements Waku {
@ -76,20 +74,21 @@ export class WakuNode implements Waku {
public constructor(
options: WakuOptions,
libp2p: Libp2p,
protocolsEnabled: ProtocolsEnabled
protocolsEnabled: ProtocolsEnabled,
relay?: IRelay
) {
if (options.pubsubTopics.length == 0) {
throw new Error("At least one pubsub topic must be provided");
}
this.pubsubTopics = options.pubsubTopics;
this.relay = relay;
this.libp2p = libp2p;
this.pubsubTopics = options.pubsubTopics;
protocolsEnabled = {
filter: false,
lightpush: false,
store: false,
relay: false,
...protocolsEnabled
};
@ -124,11 +123,6 @@ export class WakuNode implements Waku {
this.filter = filter(libp2p);
}
if (protocolsEnabled.relay) {
const relay = wakuRelay(this.pubsubTopics);
this.relay = relay(libp2p);
}
log.info(
"Waku node created",
peerId,

View File

@ -54,6 +54,7 @@
"@libp2p/peer-id": "^4.0.4",
"@waku/core": "*",
"@waku/enr": "*",
"@waku/relay": "*",
"@waku/interfaces": "*",
"@waku/utils": "*",
"app-root-path": "^3.1.0",

View File

@ -5,8 +5,8 @@ import {
Protocols,
ShardingParams
} from "@waku/interfaces";
import { createRelayNode } from "@waku/relay";
import { createLightNode, WakuNode } from "@waku/sdk";
import { createRelayNode } from "@waku/sdk/relay";
import { Logger, shardInfoToPubsubTopics } from "@waku/utils";
import { Context } from "mocha";

View File

@ -1,7 +1,7 @@
import { Multiaddr } from "@multiformats/multiaddr";
import { EConnectionStateEvents, LightNode, Protocols } from "@waku/interfaces";
import { createRelayNode } from "@waku/relay";
import { createLightNode } from "@waku/sdk";
import { createRelayNode } from "@waku/sdk/relay";
import { expect } from "chai";
import {

View File

@ -2,7 +2,7 @@ import { waitForRemotePeer } from "@waku/core";
import { EnrDecoder } from "@waku/enr";
import type { RelayNode } from "@waku/interfaces";
import { Protocols } from "@waku/interfaces";
import { createRelayNode } from "@waku/sdk/relay";
import { createRelayNode } from "@waku/relay";
import { expect } from "chai";
import {

View File

@ -1,7 +1,7 @@
import type { PeerId } from "@libp2p/interface";
import { DecodedMessage, waitForRemotePeer } from "@waku/core";
import { Protocols, RelayNode } from "@waku/interfaces";
import { createRelayNode } from "@waku/sdk/relay";
import { createRelayNode } from "@waku/relay";
import { bytesToUtf8, utf8ToBytes } from "@waku/utils/bytes";
import { expect } from "chai";

View File

@ -11,7 +11,7 @@ import {
SingleShardInfo
} from "@waku/interfaces";
import { Protocols } from "@waku/interfaces";
import { createRelayNode } from "@waku/sdk/relay";
import { createRelayNode } from "@waku/relay";
import {
contentTopicToPubsubTopic,
pubsubTopicToSingleShardInfo,

View File

@ -1,6 +1,6 @@
import { createDecoder, createEncoder } from "@waku/core";
import { RelayNode } from "@waku/interfaces";
import { createRelayNode } from "@waku/sdk/relay";
import { createRelayNode } from "@waku/relay";
import { utf8ToBytes } from "@waku/utils/bytes";
import { expect } from "chai";

View File

@ -5,7 +5,7 @@ import {
ShardInfo,
ShardingParams
} from "@waku/interfaces";
import { createRelayNode } from "@waku/sdk/relay";
import { createRelayNode } from "@waku/relay";
import { contentTopicToPubsubTopic, Logger } from "@waku/utils";
import { Context } from "mocha";

View File

@ -116,8 +116,9 @@ export async function startAndConnectLightNode(
const wakuConnections = waku.libp2p.getConnections();
const nwakuPeers = await instance.peers();
// TODO: return throw when nwaku releases following fix https://github.com/waku-org/nwaku/pull/2923
if (wakuConnections.length < 1 || nwakuPeers.length < 1) {
throw new Error(
log.error(
`Expected at least 1 peer in each node. Got waku connections: ${wakuConnections.length} and nwaku: ${nwakuPeers.length}`
);
}

View File

@ -1,8 +1,8 @@
import { waitForRemotePeer } from "@waku/core";
import type { LightNode, RelayNode } from "@waku/interfaces";
import { Protocols } from "@waku/interfaces";
import { createRelayNode } from "@waku/relay";
import { createLightNode } from "@waku/sdk";
import { createRelayNode } from "@waku/sdk/relay";
import { expect } from "chai";
import {

View File

@ -8,12 +8,12 @@ import {
createDecoder,
createEncoder
} from "@waku/message-encryption/symmetric";
import { createRelayNode } from "@waku/relay";
import {
createLightNode,
createEncoder as createPlainEncoder,
DefaultUserAgent
} from "@waku/sdk";
import { createRelayNode } from "@waku/sdk/relay";
import { bytesToUtf8, utf8ToBytes } from "@waku/utils/bytes";
import { expect } from "chai";