2022-07-21 17:03:18 +00:00
|
|
|
// @ts-ignore
|
|
|
|
import DHT from "@lumeweb/dht-web";
|
|
|
|
import type { ActiveQuery } from "libkmodule";
|
2022-08-03 16:01:11 +00:00
|
|
|
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
|
|
|
|
2022-08-03 16:01:11 +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);
|
2022-08-03 16:01:11 +00:00
|
|
|
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);
|
2022-08-14 10:49:24 +00:00
|
|
|
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);
|
|
|
|
|
2022-08-03 16:01:11 +00:00
|
|
|
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);
|
2022-08-03 16:01:11 +00:00
|
|
|
if (!defaultDht) {
|
2022-08-31 19:35:24 +00:00
|
|
|
defaultDht = dhtInstances.get(await createDht()) as DHT;
|
2022-08-03 16:01:11 +00:00
|
|
|
}
|
2022-08-31 19:35:24 +00:00
|
|
|
moduleReadyResolve();
|
2022-08-03 16:01:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2022-08-03 16:01:11 +00:00
|
|
|
aq.reject("Invalid DHT id");
|
|
|
|
return;
|
2022-07-21 17:03:18 +00:00
|
|
|
}
|
2022-08-03 16:01:11 +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-08-03 16:01:11 +00:00
|
|
|
|
2022-07-21 17:03:18 +00:00
|
|
|
try {
|
|
|
|
// @ts-ignore
|
|
|
|
socket = await dht.connect(
|
2022-08-14 00:05:51 +00:00
|
|
|
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 });
|
|
|
|
});
|
|
|
|
|
2022-08-03 16:01:11 +00:00
|
|
|
socket.on("end", () => {
|
|
|
|
deleteDhtConnection(id);
|
|
|
|
});
|
|
|
|
|
2022-07-21 17:03:18 +00:00
|
|
|
socket.on("error", (e: any) => {
|
2022-08-03 16:01:11 +00:00
|
|
|
deleteDhtConnection(id);
|
2022-07-21 17:03:18 +00:00
|
|
|
aq.reject(e);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleListenSocketEvent(aq: ActiveQuery) {
|
|
|
|
const { event = null } = aq.callerInput;
|
|
|
|
|
2022-08-03 16:01:11 +00:00
|
|
|
const socket = validateConnection(aq);
|
|
|
|
|
|
|
|
if (!socket) {
|
2022-07-21 17:03:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!event) {
|
|
|
|
aq.reject("Invalid event");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const cb = (data: Buffer) => {
|
|
|
|
aq.sendUpdate(data);
|
|
|
|
};
|
|
|
|
|
|
|
|
socket.on(event, cb);
|
|
|
|
socket.on("close", () => {
|
|
|
|
socket.off(socket, cb);
|
|
|
|
aq.respond();
|
|
|
|
});
|
|
|
|
|
|
|
|
aq.setReceiveUpdate?.((data: any) => {
|
|
|
|
switch (data?.action) {
|
|
|
|
case "off":
|
|
|
|
socket.off(socket, cb);
|
|
|
|
aq.respond();
|
|
|
|
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) {
|
2022-08-03 16:01:11 +00:00
|
|
|
const socket = validateConnection(aq);
|
2022-07-21 17:03:18 +00:00
|
|
|
|
2022-08-03 16:01:11 +00:00
|
|
|
if (!socket) {
|
2022-07-21 17:03:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-08-03 16:01:11 +00:00
|
|
|
socket.end();
|
2022-07-21 17:03:18 +00:00
|
|
|
|
|
|
|
aq.respond();
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleWriteSocketEvent(aq: ActiveQuery) {
|
2022-08-03 16:01:11 +00:00
|
|
|
const socket = validateConnection(aq);
|
2022-07-21 17:03:18 +00:00
|
|
|
|
2022-08-03 16:01:11 +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;
|
|
|
|
}
|
|
|
|
|
2022-08-03 16:01:11 +00:00
|
|
|
socket.write(message);
|
2022-07-21 17:03:18 +00:00
|
|
|
|
|
|
|
aq.respond();
|
|
|
|
}
|
|
|
|
|
2022-08-03 16:01:11 +00:00
|
|
|
function validateConnection(aq: ActiveQuery): any | boolean {
|
2022-07-21 17:03:18 +00:00
|
|
|
const { id = null } = aq.callerInput;
|
|
|
|
|
2022-08-03 16:01:11 +00:00
|
|
|
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-03 16:01:11 +00:00
|
|
|
}
|
|
|
|
|
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-03 16:01:11 +00:00
|
|
|
|
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);
|
|
|
|
}
|
2022-08-03 16:01:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!dht) {
|
2022-08-31 19:35:24 +00:00
|
|
|
return defaultDht;
|
2022-08-03 16:01:11 +00:00
|
|
|
}
|
|
|
|
|
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-08-03 16:01:11 +00:00
|
|
|
|
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-08-14 10:49:24 +00:00
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
}
|
2022-08-03 16:01:11 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|