kernel-swarm/src/index.ts

311 lines
6.4 KiB
TypeScript
Raw Normal View History

2022-07-21 17:03:18 +00:00
// @ts-ignore
import DHT from "@lumeweb/dht-web";
import type { ActiveQuery } from "libkmodule";
import { addHandler, getSeed, handleMessage } from "libkmodule";
import { handlePresentSeed as handlePresentSeedModule } from "libkmodule/dist/seed.js";
2022-08-13 19:11:18 +00:00
import type { Buffer } from "buffer";
2022-08-14 11:28:40 +00:00
import { hexToBuf } from "libskynet";
2022-07-21 17:03:18 +00:00
interface DhtConnection {
dht: number;
conn: any;
}
const connections = new Map<number, DhtConnection>();
2022-08-31 19:35:24 +00:00
const dhtInstances = new Map<number, DHT>();
2022-07-21 17:03:18 +00:00
2022-08-31 19:35:24 +00:00
let defaultDht: DHT;
let moduleReadyResolve: Function;
let moduleReady: Promise<void> = new Promise((resolve) => {
moduleReadyResolve = resolve;
});
2022-07-21 17:03:18 +00:00
onmessage = handleMessage;
2022-08-31 19:35:24 +00:00
function idFactory(start = 1, step = 1, limit = 2 ** 32) {
let id = start;
return function nextId() {
const nextId = id;
id += step;
if (id >= limit) id = start;
return nextId;
};
}
const nextId = idFactory(1);
2022-07-21 17:03:18 +00:00
addHandler("presentSeed", handlePresentSeed);
addHandler("openDht", handleOpenDht);
addHandler("closeDht", handleCloseDht);
2022-07-21 17:03:18 +00:00
addHandler("connect", handleConnect);
addHandler("listenSocketEvent", handleListenSocketEvent, {
receiveUpdates: true,
});
2022-08-13 19:12:49 +00:00
addHandler("socketExists", handleSocketExists);
2022-07-21 17:03:18 +00:00
addHandler("close", handleCloseSocketEvent);
addHandler("write", handleWriteSocketEvent);
addHandler("addRelay", handleAddRelay);
addHandler("removeRelay", handleRemoveRelay);
addHandler("clearRelays", handleClearRelays);
addHandler("getRelays", handleGetRelays);
2022-08-14 11:28:40 +00:00
addHandler("getRelayServers", handleGetRelayServers);
2022-07-21 17:03:18 +00:00
addHandler("ready", handleReady);
async function handlePresentSeed(aq: ActiveQuery) {
2022-07-21 17:03:18 +00:00
const keyPair = aq.callerInput.myskyRootKeypair;
2022-08-31 19:35:24 +00:00
handlePresentSeedModule({ callerInput: { seed: keyPair } } as ActiveQuery);
if (!defaultDht) {
2022-08-31 19:35:24 +00:00
defaultDht = dhtInstances.get(await createDht()) as DHT;
}
2022-08-31 19:35:24 +00:00
moduleReadyResolve();
}
async function handleOpenDht(aq: ActiveQuery) {
const id = await createDht();
aq.respond({ dht: id });
}
async function handleCloseDht(aq: ActiveQuery) {
const { dht = null } = aq.callerInput;
2022-07-21 17:03:18 +00:00
if (!dht) {
aq.reject("Invalid DHT id");
return;
2022-07-21 17:03:18 +00:00
}
if (dht === defaultDht) {
aq.reject("Cannot close default DHT");
return;
}
dhtInstances.delete(dht);
Array.from(connections.values())
.filter((item) => item.dht === dht)
.forEach((item) => {
item.conn.end();
});
aq.respond();
}
async function createDht(): Promise<number> {
const dhtInstance = new DHT({ keyPair: await getSeed() });
const id = nextId();
dhtInstances.set(id, dhtInstance);
return id;
2022-07-21 17:03:18 +00:00
}
async function handleConnect(aq: ActiveQuery) {
const { pubkey, options = {} } = aq.callerInput;
let socket: any;
2022-08-31 19:35:24 +00:00
const dht = await getDht(aq);
2022-07-21 17:03:18 +00:00
try {
// @ts-ignore
socket = await dht.connect(
typeof pubkey === "string" ? hexToBuf(pubkey).shift() : pubkey,
2022-07-21 17:03:18 +00:00
options
);
} catch (e: any) {
aq.reject(e);
return;
}
const id = nextId();
socket.on("open", () => {
2022-08-31 19:35:24 +00:00
let dhtId: any = [...dhtInstances.entries()].filter(
(item) => item[1] === dht
);
dhtId = dhtId.shift()[0];
setDhtConnection(id, dhtId as number, socket);
2022-07-21 17:03:18 +00:00
aq.respond({ id });
});
socket.on("end", () => {
deleteDhtConnection(id);
});
2022-07-21 17:03:18 +00:00
socket.on("error", (e: any) => {
deleteDhtConnection(id);
2022-07-21 17:03:18 +00:00
aq.reject(e);
});
}
function handleListenSocketEvent(aq: ActiveQuery) {
const { event = null } = aq.callerInput;
const socket = validateConnection(aq);
if (!socket) {
2022-07-21 17:03:18 +00:00
return;
}
if (!event) {
aq.reject("Invalid event");
return;
}
2022-09-19 12:11:54 +00:00
let responded = false;
const respond = () => {
if (responded) {
return;
}
responded = true;
aq.respond();
};
2022-07-21 17:03:18 +00:00
const cb = (data: Buffer) => {
aq.sendUpdate(data);
};
socket.on(event, cb);
socket.on("close", () => {
2022-09-19 12:11:54 +00:00
socket.off(event, cb);
respond();
2022-07-21 17:03:18 +00:00
});
aq.setReceiveUpdate?.((data: any) => {
switch (data?.action) {
case "off":
2022-09-19 12:11:54 +00:00
socket.off(event, cb);
respond();
2022-07-21 17:03:18 +00:00
break;
}
});
}
2022-08-13 19:12:49 +00:00
async function handleSocketExists(aq: ActiveQuery) {
const { id = null } = aq.callerInput;
aq.respond(hasDhtConnection(Number(id)));
}
2022-07-21 17:03:18 +00:00
function handleCloseSocketEvent(aq: ActiveQuery) {
const socket = validateConnection(aq);
2022-07-21 17:03:18 +00:00
if (!socket) {
2022-07-21 17:03:18 +00:00
return;
}
socket.end();
2022-07-21 17:03:18 +00:00
aq.respond();
}
function handleWriteSocketEvent(aq: ActiveQuery) {
const socket = validateConnection(aq);
2022-07-21 17:03:18 +00:00
if (!socket) {
2022-07-21 17:03:18 +00:00
return;
}
const { message = null } = aq.callerInput;
if (!message) {
aq.reject("empty message");
return false;
}
socket.write(message);
2022-07-21 17:03:18 +00:00
aq.respond();
}
function validateConnection(aq: ActiveQuery): any | boolean {
2022-07-21 17:03:18 +00:00
const { id = null } = aq.callerInput;
if (!id || !hasDhtConnection(id)) {
2022-07-21 17:03:18 +00:00
aq.reject("Invalid connection id");
return false;
}
2022-08-31 19:35:24 +00:00
return getDhtConnection(id)?.conn;
}
2022-08-31 19:35:24 +00:00
async function getDht(aq: ActiveQuery): Promise<DHT> {
await moduleReady;
let dht;
if ("callerInput" in aq && aq.callerInput) {
dht = aq.callerInput.dht ?? null;
2022-08-31 19:35:24 +00:00
if (dht && !dhtInstances.has(dht)) {
const error = "Invalid DHT id";
aq.reject(error);
throw new Error(error);
}
}
if (!dht) {
2022-08-31 19:35:24 +00:00
return defaultDht;
}
2022-08-31 19:35:24 +00:00
return dhtInstances.get(dht) as DHT;
2022-07-21 17:03:18 +00:00
}
async function handleAddRelay(aq: ActiveQuery) {
const { pubkey = null } = aq.callerInput;
if (!pubkey) {
aq.reject("invalid pubkey");
return;
}
2022-08-31 19:35:24 +00:00
const dht = await getDht(aq);
2022-07-21 17:03:18 +00:00
aq.respond(await dht.addRelay(pubkey));
}
2022-08-31 19:35:24 +00:00
async function handleRemoveRelay(aq: ActiveQuery) {
2022-07-21 17:03:18 +00:00
const { pubkey = null } = aq.callerInput;
if (!pubkey) {
aq.reject("invalid pubkey");
return;
}
2022-08-31 19:35:24 +00:00
const dht = await getDht(aq);
2022-07-21 17:03:18 +00:00
aq.respond(dht.removeRelay(pubkey));
}
2022-08-31 19:35:24 +00:00
async function handleClearRelays(aq: ActiveQuery) {
const dht = await getDht(aq);
2022-07-21 17:03:18 +00:00
dht.clearRelays();
aq.respond();
}
2022-08-31 19:35:24 +00:00
async function handleGetRelays(aq: ActiveQuery) {
aq.respond(await (await getDht(aq)).relays);
2022-08-14 11:28:40 +00:00
}
2022-08-31 19:35:24 +00:00
async function handleGetRelayServers(aq: ActiveQuery) {
aq.respond(await (await getDht(aq)).relayServers);
}
2022-07-21 17:03:18 +00:00
async function handleReady(aq: ActiveQuery) {
2022-08-31 19:35:24 +00:00
await (await getDht(aq)).ready();
2022-07-21 17:03:18 +00:00
aq.respond();
}
function setDhtConnection(id: number, dht: number, conn: any) {
connections.set(id, { dht, conn });
}
function getDhtConnection(id: number) {
return connections.get(id);
}
function hasDhtConnection(id: number) {
return connections.has(id);
}
function deleteDhtConnection(id: number) {
connections.delete(id);
}