*Create helper getSwarmToSocketConnectionId
*on join method, disable server mode *rename handleJoinPeer to handleJoin *rename handleSocketListenEvent to handleSocketListenEvent *on socketListenEvent, if setReceiveUpdate is ever called, assume we want to terminate * Ensure full 64 bit private key is passed to handlePresentSeedModule *Re-organize api methods *Add new socket methods *Add socketGetInfo method which creates an IPV6 address from the pubkey
This commit is contained in:
parent
236e445ff8
commit
379649b006
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
The following is based on https://github.com/yggdrasil-network/yggdrasil-go/blob/develop/src/address/address.go, which is licensed LGPL3. Full credit to them for the idea and original algorithm
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function pubKeyToIpv6(publicKey: Uint8Array) {
|
||||||
|
const keySize = 32;
|
||||||
|
if (publicKey.length !== keySize) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const buf = new Uint8Array(keySize);
|
||||||
|
for (let i = 0; i < keySize; i++) {
|
||||||
|
buf[i] = buf[i] = publicKey[i] ^ 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prefix = [0x02];
|
||||||
|
const ones = getLeadingOnes(buf);
|
||||||
|
const nodeId = getTruncatedNodeID(buf);
|
||||||
|
|
||||||
|
const addr = new Uint8Array(prefix.length + 1 + nodeId.length);
|
||||||
|
addr.set(prefix, 0);
|
||||||
|
addr[prefix.length] = ones;
|
||||||
|
addr.set(nodeId, prefix.length + 1);
|
||||||
|
|
||||||
|
const result = [];
|
||||||
|
for (let i = 0; i < 8; i++) {
|
||||||
|
const num1 = addr[i * 2].toString(16).padStart(2, "0");
|
||||||
|
const num2 = addr[i * 2 + 1].toString(16).padStart(2, "0");
|
||||||
|
result.push(`${num1}${num2}`);
|
||||||
|
}
|
||||||
|
return result.join(":");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLeadingOnes(buf: Uint8Array) {
|
||||||
|
let done = false;
|
||||||
|
let ones = 0;
|
||||||
|
for (let i = 0; i < buf.length * 8; i++) {
|
||||||
|
const bit = (buf[i >>> 3] & (0x80 >> (i & 7))) >> (7 - (i & 7));
|
||||||
|
if (!done && bit !== 0) {
|
||||||
|
ones++;
|
||||||
|
} else if (!done && bit === 0) {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ones;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTruncatedNodeID(buf: Uint8Array) {
|
||||||
|
const result = [];
|
||||||
|
let done = false;
|
||||||
|
let bits = 0;
|
||||||
|
let nBits = 0;
|
||||||
|
for (let i = 0; i < buf.length * 8; i++) {
|
||||||
|
const bit = (buf[i >>> 3] & (0x80 >> (i & 7))) >> (7 - (i & 7));
|
||||||
|
if (!done && bit !== 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!done && bit === 0) {
|
||||||
|
done = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bits = (bits << 1) | bit;
|
||||||
|
nBits++;
|
||||||
|
if (nBits === 8) {
|
||||||
|
nBits = 0;
|
||||||
|
result.push(bits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
87
src/index.ts
87
src/index.ts
|
@ -6,13 +6,14 @@ import { handlePresentSeed as handlePresentSeedModule } from "libkmodule/dist/se
|
||||||
import type { Buffer } from "buffer";
|
import type { Buffer } from "buffer";
|
||||||
import * as ed from "@noble/ed25519";
|
import * as ed from "@noble/ed25519";
|
||||||
import b4a from "b4a";
|
import b4a from "b4a";
|
||||||
|
import { pubKeyToIpv6 } from "./addr.js";
|
||||||
|
|
||||||
interface DhtConnection {
|
interface SwarmConnection {
|
||||||
swarm: number;
|
swarm: number;
|
||||||
conn: any;
|
conn: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const connections = new Map<number, DhtConnection>();
|
const connections = new Map<number, SwarmConnection>();
|
||||||
const swarmInstances = new Map<number, Hyperswarm>();
|
const swarmInstances = new Map<number, Hyperswarm>();
|
||||||
|
|
||||||
let defaultSwarm: Hyperswarm;
|
let defaultSwarm: Hyperswarm;
|
||||||
|
@ -37,27 +38,33 @@ const getSwarmId = idFactory();
|
||||||
const getSocketId = idFactory();
|
const getSocketId = idFactory();
|
||||||
|
|
||||||
addHandler("presentSeed", handlePresentSeed);
|
addHandler("presentSeed", handlePresentSeed);
|
||||||
addHandler("joinPeer", handleJoinPeer);
|
addHandler("join", handleJoin);
|
||||||
addHandler("getPeerByPubkey", handleGetPeerByPubkey);
|
addHandler("getPeerByPubkey", handleGetPeerByPubkey);
|
||||||
addHandler("listenSocketEvent", handleListenSocketEvent, {
|
|
||||||
receiveUpdates: true,
|
|
||||||
});
|
|
||||||
addHandler("socketExists", handleSocketExists);
|
|
||||||
addHandler("close", handleCloseSocketEvent);
|
|
||||||
addHandler("socketWrite", handleWriteSocketEvent);
|
|
||||||
addHandler("addRelay", handleAddRelay);
|
addHandler("addRelay", handleAddRelay);
|
||||||
addHandler("removeRelay", handleRemoveRelay);
|
addHandler("removeRelay", handleRemoveRelay);
|
||||||
addHandler("clearRelays", handleClearRelays);
|
addHandler("clearRelays", handleClearRelays);
|
||||||
addHandler("getRelays", handleGetRelays);
|
addHandler("getRelays", handleGetRelays);
|
||||||
addHandler("init", handleInit);
|
addHandler("init", handleInit);
|
||||||
addHandler("ready", handleReady);
|
addHandler("ready", handleReady);
|
||||||
|
addHandler("listenConnections", handleListenConnections, {
|
||||||
|
receiveUpdates: true,
|
||||||
|
});
|
||||||
|
addHandler("socketGetInfo", handleGetSocketInfo);
|
||||||
|
|
||||||
|
addHandler("socketExists", handleSocketExists);
|
||||||
|
addHandler("socketListenEvent", handleSocketListenEvent, {
|
||||||
|
receiveUpdates: true,
|
||||||
|
});
|
||||||
|
addHandler("socketWrite", handleWriteSocketEvent);
|
||||||
|
addHandler("socketClose", handleCloseSocketEvent);
|
||||||
async function handlePresentSeed(aq: ActiveQuery) {
|
async function handlePresentSeed(aq: ActiveQuery) {
|
||||||
|
const pubkey = await ed.getPublicKey(aq.callerInput.rootKey);
|
||||||
handlePresentSeedModule({
|
handlePresentSeedModule({
|
||||||
callerInput: {
|
callerInput: {
|
||||||
seed: {
|
seed: {
|
||||||
publicKey: await ed.getPublicKey(aq.callerInput.rootKey),
|
publicKey: await ed.getPublicKey(aq.callerInput.rootKey),
|
||||||
secretKey: aq.callerInput.rootKey,
|
secretKey: b4a.concat([aq.callerInput.rootKey, pubkey]),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as ActiveQuery);
|
} as ActiveQuery);
|
||||||
|
@ -85,7 +92,7 @@ async function createSwarm(): Promise<number> {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleListenSocketEvent(aq: ActiveQuery) {
|
function handleSocketListenEvent(aq: ActiveQuery) {
|
||||||
const { event = null } = aq.callerInput;
|
const { event = null } = aq.callerInput;
|
||||||
|
|
||||||
const socket = validateConnection(aq);
|
const socket = validateConnection(aq);
|
||||||
|
@ -119,13 +126,9 @@ function handleListenSocketEvent(aq: ActiveQuery) {
|
||||||
respond();
|
respond();
|
||||||
});
|
});
|
||||||
|
|
||||||
aq.setReceiveUpdate?.((data: any) => {
|
aq.setReceiveUpdate?.(() => {
|
||||||
switch (data?.action) {
|
|
||||||
case "off":
|
|
||||||
socket.off(event, cb);
|
socket.off(event, cb);
|
||||||
respond();
|
respond();
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +237,7 @@ async function handleGetRelays(aq: ActiveQuery) {
|
||||||
aq.respond(await (await getSwarm(aq)).relays);
|
aq.respond(await (await getSwarm(aq)).relays);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleJoinPeer(aq: ActiveQuery) {
|
async function handleJoin(aq: ActiveQuery) {
|
||||||
const { topic = null } = aq.callerInput;
|
const { topic = null } = aq.callerInput;
|
||||||
|
|
||||||
const swarm = await getSwarm(aq);
|
const swarm = await getSwarm(aq);
|
||||||
|
@ -249,7 +252,7 @@ async function handleJoinPeer(aq: ActiveQuery) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
swarm.join(topic);
|
swarm.join(topic, { server: false });
|
||||||
aq.respond();
|
aq.respond();
|
||||||
}
|
}
|
||||||
async function handleGetPeerByPubkey(aq: ActiveQuery) {
|
async function handleGetPeerByPubkey(aq: ActiveQuery) {
|
||||||
|
@ -276,11 +279,7 @@ async function handleGetPeerByPubkey(aq: ActiveQuery) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const peer = swarm._allConnections.get(pubkey);
|
const peer = swarm._allConnections.get(pubkey);
|
||||||
|
|
||||||
aq.respond(
|
aq.respond(getSwarmToSocketConnectionId(peer));
|
||||||
[...connections.entries()].filter((conn) => {
|
|
||||||
return conn[1].conn === peer;
|
|
||||||
})[0][0]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleInit(aq: ActiveQuery) {
|
async function handleInit(aq: ActiveQuery) {
|
||||||
|
@ -305,3 +304,45 @@ async function handleReady(aq: ActiveQuery) {
|
||||||
aq.respond();
|
aq.respond();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
async function handleListenConnections(aq: ActiveQuery) {
|
||||||
|
const swarm = await getSwarm(aq);
|
||||||
|
|
||||||
|
const listener = (peer: any) => {
|
||||||
|
aq.sendUpdate(getSwarmToSocketConnectionId(peer));
|
||||||
|
};
|
||||||
|
|
||||||
|
swarm.on("connection", listener);
|
||||||
|
|
||||||
|
aq.setReceiveUpdate?.(() => {
|
||||||
|
swarm.off("connection", listener);
|
||||||
|
aq.respond();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleGetSocketInfo(aq: ActiveQuery) {
|
||||||
|
const socket = validateConnection(aq);
|
||||||
|
|
||||||
|
if (!socket) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
aq.respond({
|
||||||
|
remotePublicKey: socket.remotePublicKey,
|
||||||
|
publicKey: socket.publicKey,
|
||||||
|
rawStream: {
|
||||||
|
remoteHost: pubKeyToIpv6(socket.remotePublicKey),
|
||||||
|
remotePort: 0,
|
||||||
|
remoteFamily: "IPv6",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSwarmToSocketConnectionId(socket: any) {
|
||||||
|
for (const conn of connections) {
|
||||||
|
if (conn[1].conn === socket) {
|
||||||
|
return conn[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue