diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index c8ac89ffe5..69b5a0ad5b 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,9 +1,11 @@ export { createEncoder, createDecoder } from "./lib/message/version_0.js"; +export { createCodec } from "./lib/message/index.js"; export type { Encoder, Decoder, DecodedMessage } from "./lib/message/version_0.js"; +export type { Codec } from "./lib/message/index.js"; export * as message from "./lib/message/index.js"; export * as waku_filter from "./lib/filter/index.js"; diff --git a/packages/core/src/lib/message/codec.ts b/packages/core/src/lib/message/codec.ts new file mode 100644 index 0000000000..c0212331bf --- /dev/null +++ b/packages/core/src/lib/message/codec.ts @@ -0,0 +1,76 @@ +import type { + ICodec, + IDecodedMessage, + IDecoder, + IEncoder, + IMessage, + IMetaSetter, + IProtoMessage, + IRoutingInfo, + PubsubTopic +} from "@waku/interfaces"; + +import { Decoder, Encoder } from "./version_0.js"; + +export class Codec implements ICodec { + private encoder: IEncoder; + private decoder: IDecoder; + + public constructor( + public contentTopic: string, + public ephemeral: boolean = false, + public routingInfo: IRoutingInfo, + public metaSetter?: IMetaSetter + ) { + this.encoder = new Encoder( + contentTopic, + ephemeral, + routingInfo, + metaSetter + ); + this.decoder = new Decoder(contentTopic, routingInfo); + } + + public get pubsubTopic(): PubsubTopic { + return this.routingInfo.pubsubTopic; + } + + public async toWire(message: IMessage): Promise { + return this.encoder.toWire(message); + } + + public async toProtoObj( + message: IMessage + ): Promise { + return this.encoder.toProtoObj(message); + } + + public fromWireToProtoObj( + bytes: Uint8Array + ): Promise { + return this.decoder.fromWireToProtoObj(bytes); + } + + public async fromProtoObj( + pubsubTopic: string, + proto: IProtoMessage + ): Promise { + return this.decoder.fromProtoObj(pubsubTopic, proto); + } +} + +type CodecParams = { + contentTopic: string; + ephemeral: boolean; + routingInfo: IRoutingInfo; + metaSetter?: IMetaSetter; +}; + +export function createCodec(params: CodecParams): Codec { + return new Codec( + params.contentTopic, + params.ephemeral, + params.routingInfo, + params.metaSetter + ); +} diff --git a/packages/core/src/lib/message/constants.ts b/packages/core/src/lib/message/constants.ts new file mode 100644 index 0000000000..0d54071228 --- /dev/null +++ b/packages/core/src/lib/message/constants.ts @@ -0,0 +1,2 @@ +export const OneMillion = BigInt(1_000_000); +export const Version = 0; diff --git a/packages/core/src/lib/message/index.ts b/packages/core/src/lib/message/index.ts index e4736e54e1..8add5fba21 100644 --- a/packages/core/src/lib/message/index.ts +++ b/packages/core/src/lib/message/index.ts @@ -1 +1,3 @@ export * as version_0 from "./version_0.js"; +export { Codec, createCodec } from "./codec.js"; +export { OneMillion, Version } from "./constants.js"; diff --git a/packages/core/src/lib/message/version_0.ts b/packages/core/src/lib/message/version_0.ts index a8706817d9..a724e07b96 100644 --- a/packages/core/src/lib/message/version_0.ts +++ b/packages/core/src/lib/message/version_0.ts @@ -13,10 +13,10 @@ import type { import { proto_message as proto } from "@waku/proto"; import { Logger } from "@waku/utils"; +import { OneMillion, Version } from "./constants.js"; + const log = new Logger("message:version-0"); -const OneMillion = BigInt(1_000_000); -export const Version = 0; export { proto }; export class DecodedMessage implements IDecodedMessage { diff --git a/packages/interfaces/src/message.ts b/packages/interfaces/src/message.ts index 1b34700010..f9ddb6df53 100644 --- a/packages/interfaces/src/message.ts +++ b/packages/interfaces/src/message.ts @@ -109,3 +109,5 @@ export interface IDecoder { proto: IProtoMessage ) => Promise; } + +export type ICodec = IEncoder & IDecoder; diff --git a/packages/interfaces/src/waku.ts b/packages/interfaces/src/waku.ts index 5c99f716e6..ed55bde5da 100644 --- a/packages/interfaces/src/waku.ts +++ b/packages/interfaces/src/waku.ts @@ -10,7 +10,7 @@ import type { IFilter } from "./filter.js"; import type { HealthStatus } from "./health_status.js"; import type { Libp2p } from "./libp2p.js"; import type { ILightPush } from "./light_push.js"; -import { IDecodedMessage, IDecoder, IEncoder } from "./message.js"; +import { ICodec, IDecodedMessage, IDecoder, IEncoder } from "./message.js"; import type { Protocols } from "./protocols.js"; import type { IRelay } from "./relay.js"; import type { ShardId } from "./sharding.js"; @@ -25,6 +25,8 @@ export type CreateEncoderParams = CreateDecoderParams & { ephemeral?: boolean; }; +export type CreateCodecParams = CreateDecoderParams & CreateEncoderParams; + export interface IWakuEvents { /** * Emitted when a connection is established or lost. @@ -188,6 +190,8 @@ export interface IWaku { waitForPeers(protocols?: Protocols[], timeoutMs?: number): Promise; /** + * @deprecated Use {@link createCodec} instead + * * Creates a decoder for Waku messages on a specific content topic. * * A decoder is used to decode messages from the Waku network format. @@ -217,6 +221,8 @@ export interface IWaku { createDecoder(params: CreateDecoderParams): IDecoder; /** + * @deprecated Use {@link createCodec} instead + * * Creates an encoder for Waku messages on a specific content topic. * * An encoder is used to encode messages into the Waku network format. @@ -246,6 +252,36 @@ export interface IWaku { */ createEncoder(params: CreateEncoderParams): IEncoder; + /** + * Creates a codec for Waku messages on a specific content topic. + * + * A codec is used to encode and decode messages from the Waku network format. + * The codec automatically handles shard configuration based on the Waku node's network settings. + * + * @param {CreateCodecParams} params - Configuration for the codec including content topic and optionally shard information and ephemeral flag + * @returns {ICodec} A codec instance configured for the specified content topic + * @throws {Error} If the shard configuration is incompatible with the node's network settings + * + * @example + * ```typescript + * // Create a codec with default network shard settings + * const codec = waku.createCodec({ + * contentTopic: "/my-app/1/chat/proto" + * }); + * + * // Create a codec with custom shard settings + * const customCodec = waku.createCodec({ + * contentTopic: "/my-app/1/chat/proto", + * ephemeral: true, + * shardInfo: { + * clusterId: 1, + * shard: 5 + * } + * }); + * ``` + */ + createCodec(params: CreateCodecParams): ICodec; + /** * @returns {boolean} `true` if the node was started and `false` otherwise */ diff --git a/packages/rln/src/message.ts b/packages/rln/src/message.ts index d3474bb052..971db17afc 100644 --- a/packages/rln/src/message.ts +++ b/packages/rln/src/message.ts @@ -17,7 +17,7 @@ export function toRLNSignal(contentTopic: string, msg: IMessage): Uint8Array { export class RlnMessage implements IRlnMessage { public pubsubTopic = ""; - public version = message.version_0.Version; + public version = message.Version; public constructor( private rlnInstance: RLNInstance, diff --git a/packages/sdk/src/waku/waku.ts b/packages/sdk/src/waku/waku.ts index 38dfec9ffa..2db9deea59 100644 --- a/packages/sdk/src/waku/waku.ts +++ b/packages/sdk/src/waku/waku.ts @@ -5,11 +5,18 @@ import { TypedEventEmitter } from "@libp2p/interface"; import type { MultiaddrInput } from "@multiformats/multiaddr"; -import { ConnectionManager, createDecoder, createEncoder } from "@waku/core"; +import { + ConnectionManager, + createCodec, + createDecoder, + createEncoder +} from "@waku/core"; import type { + CreateCodecParams, CreateDecoderParams, CreateEncoderParams, CreateNodeOptions, + ICodec, IDecodedMessage, IDecoder, IEncoder, @@ -264,6 +271,7 @@ export class WakuNode implements IWaku { params.contentTopic, params.shardId ); + return createDecoder(params.contentTopic, routingInfo); } @@ -280,6 +288,19 @@ export class WakuNode implements IWaku { }); } + public createCodec(params: CreateCodecParams): ICodec { + const routingInfo = this.createRoutingInfo( + params.contentTopic, + params.shardId + ); + + return createCodec({ + contentTopic: params.contentTopic, + ephemeral: params.ephemeral ?? false, + routingInfo: routingInfo + }); + } + private createRoutingInfo( contentTopic?: string, shardId?: number