Compare commits

...

4 Commits

Author SHA1 Message Date
Derrick Hammer 3b6448fec8
*Update deps 2023-02-06 12:33:27 -05:00
Derrick Hammer 379649b006
*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
2023-02-06 12:33:11 -05:00
Derrick Hammer 236e445ff8
*Change ready to init
*Add new ready method that listens for a ready event but returns if activeRelay is set
2023-02-01 12:07:46 -05:00
Derrick Hammer 4e443f4ccb
*Replace dht references with swarm 2023-02-01 08:18:31 -05:00
3 changed files with 167 additions and 37 deletions

View File

@ -14,12 +14,12 @@
"type": "module",
"dependencies": {
"@lumeweb/hyperswarm-web": "git+https://git.lumeweb.com/LumeWeb/hyperswarm-web.git",
"@noble/ed25519": "^1.7.1",
"@noble/ed25519": "^1.7.2",
"b4a": "^1.6.1",
"hyperswarm": "^4.3.7",
"libkmodule": "^0.2.53",
"libskynet": "^0.0.62",
"noise-handshake": "github:LumeWeb/noise-handshake",
"noise-handshake": "^3.0.2",
"randombytes": "github:LumeWeb/randombytes-browser"
},
"devDependencies": {
@ -27,7 +27,7 @@
"@rollup/plugin-node-resolve": "^13.3.0",
"@rollup/plugin-typescript": "^8.5.0",
"@screamingvoid/sodium-universal": "^0.1.1",
"@scure/bip39": "^1.1.0",
"@scure/bip39": "^1.1.1",
"@skynetlabs/skynet-nodejs": "^2.9.0",
"@types/b4a": "^1.6.0",
"@types/jest": "^28.1.8",

72
src/addr.ts Normal file
View File

@ -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;
}

View File

@ -6,13 +6,14 @@ import { handlePresentSeed as handlePresentSeedModule } from "libkmodule/dist/se
import type { Buffer } from "buffer";
import * as ed from "@noble/ed25519";
import b4a from "b4a";
import { pubKeyToIpv6 } from "./addr.js";
interface DhtConnection {
interface SwarmConnection {
swarm: number;
conn: any;
}
const connections = new Map<number, DhtConnection>();
const connections = new Map<number, SwarmConnection>();
const swarmInstances = new Map<number, Hyperswarm>();
let defaultSwarm: Hyperswarm;
@ -37,26 +38,33 @@ const getSwarmId = idFactory();
const getSocketId = idFactory();
addHandler("presentSeed", handlePresentSeed);
addHandler("joinPeer", handleJoinPeer);
addHandler("join", handleJoin);
addHandler("getPeerByPubkey", handleGetPeerByPubkey);
addHandler("listenSocketEvent", handleListenSocketEvent, {
receiveUpdates: true,
});
addHandler("socketExists", handleSocketExists);
addHandler("close", handleCloseSocketEvent);
addHandler("socketWrite", handleWriteSocketEvent);
addHandler("addRelay", handleAddRelay);
addHandler("removeRelay", handleRemoveRelay);
addHandler("clearRelays", handleClearRelays);
addHandler("getRelays", handleGetRelays);
addHandler("init", handleInit);
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) {
const pubkey = await ed.getPublicKey(aq.callerInput.rootKey);
handlePresentSeedModule({
callerInput: {
seed: {
publicKey: await ed.getPublicKey(aq.callerInput.rootKey),
secretKey: aq.callerInput.rootKey,
secretKey: b4a.concat([aq.callerInput.rootKey, pubkey]),
},
},
} as ActiveQuery);
@ -84,7 +92,7 @@ async function createSwarm(): Promise<number> {
return id;
}
function handleListenSocketEvent(aq: ActiveQuery) {
function handleSocketListenEvent(aq: ActiveQuery) {
const { event = null } = aq.callerInput;
const socket = validateConnection(aq);
@ -118,13 +126,9 @@ function handleListenSocketEvent(aq: ActiveQuery) {
respond();
});
aq.setReceiveUpdate?.((data: any) => {
switch (data?.action) {
case "off":
socket.off(event, cb);
respond();
break;
}
aq.setReceiveUpdate?.(() => {
socket.off(event, cb);
respond();
});
}
@ -203,9 +207,9 @@ async function handleAddRelay(aq: ActiveQuery) {
return;
}
const dht = await getSwarm(aq);
const swarm = await getSwarm(aq);
aq.respond(await dht.addRelay(pubkey));
aq.respond(await swarm.addRelay(pubkey));
}
async function handleRemoveRelay(aq: ActiveQuery) {
@ -216,15 +220,15 @@ async function handleRemoveRelay(aq: ActiveQuery) {
return;
}
const dht = await getSwarm(aq);
const swarm = await getSwarm(aq);
aq.respond(dht.removeRelay(pubkey));
aq.respond(swarm.removeRelay(pubkey));
}
async function handleClearRelays(aq: ActiveQuery) {
const dht = await getSwarm(aq);
const swarm = await getSwarm(aq);
dht.clearRelays();
swarm.clearRelays();
aq.respond();
}
@ -233,7 +237,7 @@ async function handleGetRelays(aq: ActiveQuery) {
aq.respond(await (await getSwarm(aq)).relays);
}
async function handleJoinPeer(aq: ActiveQuery) {
async function handleJoin(aq: ActiveQuery) {
const { topic = null } = aq.callerInput;
const swarm = await getSwarm(aq);
@ -248,7 +252,7 @@ async function handleJoinPeer(aq: ActiveQuery) {
}
// @ts-ignore
swarm.join(topic);
swarm.join(topic, { server: false });
aq.respond();
}
async function handleGetPeerByPubkey(aq: ActiveQuery) {
@ -275,16 +279,70 @@ async function handleGetPeerByPubkey(aq: ActiveQuery) {
// @ts-ignore
const peer = swarm._allConnections.get(pubkey);
aq.respond(
[...connections.entries()].filter((conn) => {
return conn[1].conn === peer;
})[0][0]
);
aq.respond(getSwarmToSocketConnectionId(peer));
}
async function handleReady(aq: ActiveQuery) {
async function handleInit(aq: ActiveQuery) {
const swarm = await getSwarm(aq);
// @ts-ignore
await swarm.ready();
try {
await swarm.init();
} catch (e) {
aq.reject((e as Error).message);
return;
}
aq.respond();
}
async function handleReady(aq: ActiveQuery) {
const swarm = await getSwarm(aq);
if (swarm.activeRelay) {
aq.respond();
return;
}
swarm.once("ready", () => {
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;
}