mirror of
https://github.com/logos-messaging/logos-messaging-simulator.git
synced 2026-07-10 10:09:36 +00:00
127 lines
3.5 KiB
TypeScript
127 lines
3.5 KiB
TypeScript
import { createLightNode, WakuEvent, Protocols } from "@waku/sdk";
|
|
import type { IDecodedMessage } from "@waku/sdk";
|
|
import { program } from "commander";
|
|
import { EnrDecoder } from "@waku/enr";
|
|
|
|
const DEFAULT_NODE = "http://logos-delivery-simulator-nwaku-1:8645";
|
|
|
|
program
|
|
.option("-t, --content-topic <string>", "Content topic", "my-ctopic")
|
|
.option("-s, --shard-id <number>", "Shard ID", parseInt, 0)
|
|
.option("-c, --cluster-id <number>", "Cluster ID", parseInt, 66)
|
|
.option(
|
|
"--single-node <string>",
|
|
"example: http://logos-delivery-simulator-nwaku-1:8645",
|
|
)
|
|
.option(
|
|
"--multiple-nodes <string>",
|
|
"example: http://logos-delivery-simulator-nwaku-[1..10]:8645",
|
|
)
|
|
.parse(process.argv);
|
|
|
|
const {
|
|
contentTopic: CONTENT_TOPIC,
|
|
shardId: SHARD_ID,
|
|
clusterId: CLUSTER_ID,
|
|
singleNode: SINGLE_NODE,
|
|
multipleNodes: MULTIPLE_NODES,
|
|
} = program.opts();
|
|
|
|
if (SINGLE_NODE && MULTIPLE_NODES) {
|
|
console.error(
|
|
"Error: --single-node and --multiple-nodes are mutually exclusive.",
|
|
);
|
|
process.exit(1);
|
|
}
|
|
|
|
// Expands "http://host-[1..5]:8645" → ["http://host-1:8645", ..., "http://host-5:8645"]
|
|
function expandNodePattern(pattern: string): string[] {
|
|
const match = pattern.match(/^(.*)\[(\d+)\.\.(\d+)\](.*)$/);
|
|
if (!match) return [pattern];
|
|
|
|
const [, prefix, startStr, endStr, suffix] = match;
|
|
const start = parseInt(startStr);
|
|
const end = parseInt(endStr);
|
|
|
|
return Array.from(
|
|
{ length: end - start + 1 },
|
|
(_, i) => `${prefix}${start + i}${suffix}`,
|
|
);
|
|
}
|
|
|
|
function resolveNodeUrls(): string[] {
|
|
if (MULTIPLE_NODES) return expandNodePattern(MULTIPLE_NODES);
|
|
return [SINGLE_NODE ?? DEFAULT_NODE];
|
|
}
|
|
|
|
async function enrToMultiaddrs(enrString: string): Promise<string[]> {
|
|
try {
|
|
const enr = await EnrDecoder.fromString(enrString);
|
|
return enr.getFullMultiaddrs().map((addr) => addr.toString());
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
async function fetchBootstrapPeers(): Promise<string[]> {
|
|
const nodeUrls = resolveNodeUrls();
|
|
console.log(`Fetching peers from ${nodeUrls.length} node(s):`, nodeUrls);
|
|
|
|
const results = await Promise.allSettled(
|
|
nodeUrls.map(async (url) => {
|
|
const res = await fetch(`${url}/info`);
|
|
const { enrUri } = await res.json();
|
|
return enrToMultiaddrs(enrUri);
|
|
}),
|
|
);
|
|
|
|
const multiaddrs = results.flatMap((r) =>
|
|
r.status === "fulfilled" ? r.value : [],
|
|
);
|
|
console.log("Bootstrap peers:", multiaddrs);
|
|
return multiaddrs;
|
|
}
|
|
|
|
// Main
|
|
|
|
async function initLocalWakuClient() {
|
|
const bootstrapPeers = await fetchBootstrapPeers();
|
|
|
|
const node = await createLightNode({
|
|
defaultBootstrap: false,
|
|
bootstrapPeers,
|
|
discovery: { peerExchange: true },
|
|
libp2p: { filterMultiaddrs: false },
|
|
networkConfig: { clusterId: CLUSTER_ID },
|
|
});
|
|
|
|
node.events.addEventListener(WakuEvent.Health, ({ detail }) =>
|
|
console.log("Health:", detail),
|
|
);
|
|
|
|
await node.start();
|
|
await node.waitForPeers([Protocols.LightPush, Protocols.Filter], 30_000);
|
|
node.lightPush.start();
|
|
|
|
const topicConfig = { contentTopic: CONTENT_TOPIC, shardId: SHARD_ID };
|
|
const decoder = node.createDecoder(topicConfig);
|
|
|
|
console.log("Subscribing to filter updates...");
|
|
|
|
await node.filter.subscribe([decoder], (message: IDecodedMessage) => {
|
|
if (message?.payload) {
|
|
console.log("📩 Received message", {
|
|
topic: message.contentTopic,
|
|
size: message.payload.byteLength,
|
|
});
|
|
} else {
|
|
console.warn("⚠️ Received empty message frame.");
|
|
}
|
|
});
|
|
}
|
|
|
|
initLocalWakuClient().catch((error) => {
|
|
console.error("Fatal error:", error);
|
|
process.exit(1);
|
|
});
|