js-waku/packages/tests/tests/ephemeral.node.spec.ts
fryorcraken 3842d84b55
feat!: Introduce routing info concept
Concepts are being mixed up between the global network config (static vs auto sharding), that needs to be the same of all nodes in the network, individual node configuration (eg relay node subscribing to a given shard), and the routing characteristic of a specific message (eg pubsub topic, shard).

This stops proper configuration of nwaku post 0.36.0 because we know need to be deliberate on whether nwaku nodes are running with auto or static sharding.

It also included various back and forth conversions between shards, pubsub topics, etc.

With this change, we tidy up the network configuration, and make it explicit whether it is static or auto sharded.
We also introduce the concept of routing info, which is specific to a message, and tied to the overall network configuration.

Routing info abstract pubsub topic, shard, and autosharding needs. Which should lead to easier tidy up of the pubsub concept at a later stage.

# Conflicts:
#	packages/core/src/lib/connection_manager/connection_manager.ts
#	packages/core/src/lib/metadata/metadata.ts
#	packages/interfaces/src/metadata.ts
#	packages/interfaces/src/sharding.ts
#	packages/relay/src/create.ts
#	packages/sdk/src/filter/filter.ts
#	packages/sdk/src/filter/types.ts
#	packages/sdk/src/light_push/light_push.spec.ts
#	packages/tests/tests/sharding/auto_sharding.spec.ts
#	packages/tests/tests/sharding/static_sharding.spec.ts

# Conflicts:
#	packages/sdk/src/store/store.ts
2025-07-19 13:22:45 +10:00

333 lines
8.8 KiB
TypeScript

import { createDecoder, createEncoder } from "@waku/core";
import { AutoSharding, Protocols } from "@waku/interfaces";
import type { IDecodedMessage, LightNode } from "@waku/interfaces";
import {
generatePrivateKey,
generateSymmetricKey,
getPublicKey
} from "@waku/message-encryption";
import {
createDecoder as createEciesDecoder,
createEncoder as createEciesEncoder
} from "@waku/message-encryption/ecies";
import {
createDecoder as createSymDecoder,
createEncoder as createSymEncoder
} from "@waku/message-encryption/symmetric";
import { createLightNode } from "@waku/sdk";
import { createRoutingInfo, Logger } from "@waku/utils";
import { bytesToUtf8, utf8ToBytes } from "@waku/utils/bytes";
import { expect } from "chai";
import {
afterEachCustom,
beforeEachCustom,
delay,
makeLogFileName,
NOISE_KEY_1,
NOISE_KEY_2,
ServiceNode,
tearDownNodes
} from "../src/index.js";
const log = new Logger("test:ephemeral");
const TestClusterId = 2;
const TestNetworkConfig: AutoSharding = {
clusterId: TestClusterId,
numShardsInCluster: 8
};
const TestContentTopic = "/test/1/ephemeral/utf8";
const TestRoutingInfo = createRoutingInfo(TestNetworkConfig, {
contentTopic: TestContentTopic
});
const TestEncoder = createEncoder({
contentTopic: TestContentTopic,
routingInfo: TestRoutingInfo
});
const TestDecoder = createDecoder(TestContentTopic, TestRoutingInfo);
const privateKey = generatePrivateKey();
const symKey = generateSymmetricKey();
const publicKey = getPublicKey(privateKey);
const AsymContentTopic = "/test/1/ephemeral-asym/utf8";
const SymContentTopic = "/test/1/ephemeral-sym/utf8";
const AsymEncoder = createEciesEncoder({
contentTopic: AsymContentTopic,
publicKey,
ephemeral: true,
routingInfo: TestRoutingInfo
});
const SymEncoder = createSymEncoder({
contentTopic: SymContentTopic,
symKey,
ephemeral: true,
routingInfo: TestRoutingInfo
});
const ClearEncoder = createEncoder({
contentTopic: TestContentTopic,
ephemeral: true,
routingInfo: TestRoutingInfo
});
const AsymDecoder = createEciesDecoder(
AsymContentTopic,
TestRoutingInfo,
privateKey
);
const SymDecoder = createSymDecoder(SymContentTopic, TestRoutingInfo, symKey);
describe("Waku Message Ephemeral field", function () {
let waku: LightNode;
let nwaku: ServiceNode;
afterEachCustom(this, async () => {
await tearDownNodes(nwaku, waku);
});
beforeEachCustom(this, async () => {
nwaku = new ServiceNode(makeLogFileName(this.ctx));
const contentTopics = [TestContentTopic, AsymContentTopic, SymContentTopic];
await nwaku.start({
filter: true,
lightpush: true,
store: true,
relay: true,
contentTopic: contentTopics,
clusterId: TestClusterId
});
await nwaku.ensureSubscriptionsAutosharding([
TestContentTopic,
AsymContentTopic,
SymContentTopic
]);
waku = await createLightNode({
staticNoiseKey: NOISE_KEY_1,
libp2p: { addresses: { listen: ["/ip4/0.0.0.0/tcp/0/ws"] } },
networkConfig: TestNetworkConfig
});
await waku.start();
await waku.dial(await nwaku.getMultiaddrWithId());
await waku.waitForPeers([Protocols.Filter, Protocols.LightPush]);
});
it("Ephemeral messages are not stored", async function () {
this.timeout(50_000);
const asymText =
"This message is encrypted for me using asymmetric encryption";
const symText =
"This message is encrypted for me using symmetric encryption";
const clearText = "This is a clear text message";
const asymMsg = { payload: utf8ToBytes(asymText) };
const symMsg = {
payload: utf8ToBytes(symText)
};
const clearMsg = {
payload: utf8ToBytes(clearText)
};
const [waku1, waku2, nimWakuMultiaddr] = await Promise.all([
createLightNode({
staticNoiseKey: NOISE_KEY_1,
networkConfig: TestNetworkConfig
}).then((waku) => waku.start().then(() => waku)),
createLightNode({
staticNoiseKey: NOISE_KEY_2,
networkConfig: TestNetworkConfig
}).then((waku) => waku.start().then(() => waku)),
nwaku.getMultiaddrWithId()
]);
log.info("Waku nodes created");
await Promise.all([
waku1.dial(nimWakuMultiaddr),
waku2.dial(nimWakuMultiaddr)
]);
log.info("Waku nodes connected to nwaku");
await waku.waitForPeers([Protocols.LightPush]);
log.info("Sending messages using light push");
await Promise.all([
waku1.lightPush.send(AsymEncoder, asymMsg),
waku1.lightPush.send(SymEncoder, symMsg),
waku1.lightPush.send(ClearEncoder, clearMsg)
]);
await waku2.waitForPeers([Protocols.Store]);
const messages: IDecodedMessage[] = [];
log.info("Retrieving messages from store");
for await (const msgPromises of waku2.store.queryGenerator([
AsymDecoder,
SymDecoder,
TestDecoder
])) {
for (const promise of msgPromises) {
const msg = await promise;
if (msg) {
messages.push(msg);
}
}
}
expect(messages?.length).eq(0);
await tearDownNodes([], [waku1, waku2]);
});
it("Ephemeral field is preserved - encoder v0", async function () {
this.timeout(10000);
const ephemeralEncoder = createEncoder({
contentTopic: TestContentTopic,
ephemeral: true,
routingInfo: TestRoutingInfo
});
const messages: IDecodedMessage[] = [];
const callback = (msg: IDecodedMessage): void => {
messages.push(msg);
};
await waku.filter.subscribe([TestDecoder], callback);
await delay(200);
const normalTxt = "Normal message";
const ephemeralTxt = "Ephemeral Message";
await Promise.all([
waku.lightPush.send(TestEncoder, {
payload: utf8ToBytes(normalTxt)
}),
waku.lightPush.send(ephemeralEncoder, {
payload: utf8ToBytes(ephemeralTxt)
})
]);
while (messages.length < 2) {
await delay(250);
}
const normalMsg = messages.find(
(msg) => bytesToUtf8(msg.payload) === normalTxt
);
const ephemeralMsg = messages.find(
(msg) => bytesToUtf8(msg.payload) === ephemeralTxt
);
expect(normalMsg).to.not.be.undefined;
expect(ephemeralMsg).to.not.be.undefined;
expect(normalMsg!.ephemeral).to.be.false;
expect(ephemeralMsg!.ephemeral).to.be.true;
});
it("Ephemeral field is preserved - symmetric encryption", async function () {
this.timeout(10000);
const encoder = createSymEncoder({
contentTopic: SymContentTopic,
symKey,
routingInfo: TestRoutingInfo
});
const decoder = createSymDecoder(SymContentTopic, TestRoutingInfo, symKey);
const messages: IDecodedMessage[] = [];
const callback = (msg: IDecodedMessage): void => {
messages.push(msg);
};
await waku.filter.subscribe([decoder], callback);
await delay(200);
const normalTxt = "Normal message";
const ephemeralTxt = "Ephemeral Message";
await Promise.all([
waku.lightPush.send(encoder, {
payload: utf8ToBytes(normalTxt)
}),
waku.lightPush.send(SymEncoder, {
payload: utf8ToBytes(ephemeralTxt)
})
]);
while (messages.length < 2) {
await delay(250);
}
const normalMsg = messages.find(
(msg) => bytesToUtf8(msg.payload) === normalTxt
);
const ephemeralMsg = messages.find(
(msg) => bytesToUtf8(msg.payload) === ephemeralTxt
);
expect(normalMsg).to.not.be.undefined;
expect(ephemeralMsg).to.not.be.undefined;
expect(normalMsg!.ephemeral).to.be.false;
expect(ephemeralMsg!.ephemeral).to.be.true;
});
it("Ephemeral field is preserved - asymmetric encryption", async function () {
this.timeout(10000);
const encoder = createEciesEncoder({
contentTopic: AsymContentTopic,
publicKey: publicKey,
routingInfo: TestRoutingInfo
});
const decoder = createEciesDecoder(
AsymContentTopic,
TestRoutingInfo,
privateKey
);
const messages: IDecodedMessage[] = [];
const callback = (msg: IDecodedMessage): void => {
messages.push(msg);
};
await waku.filter.subscribe([decoder], callback);
await delay(200);
const normalTxt = "Normal message";
const ephemeralTxt = "Ephemeral Message";
await Promise.all([
waku.lightPush.send(encoder, {
payload: utf8ToBytes(normalTxt)
}),
waku.lightPush.send(AsymEncoder, {
payload: utf8ToBytes(ephemeralTxt)
})
]);
while (messages.length < 2) {
await delay(250);
}
const normalMsg = messages.find(
(msg) => bytesToUtf8(msg.payload) === normalTxt
);
const ephemeralMsg = messages.find(
(msg) => bytesToUtf8(msg.payload) === ephemeralTxt
);
expect(normalMsg).to.not.be.undefined;
expect(ephemeralMsg).to.not.be.undefined;
expect(normalMsg!.ephemeral).to.be.false;
expect(ephemeralMsg!.ephemeral).to.be.true;
});
});