From 74498c5691f7c53f90bc7ccbe9b63598b146abe1 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 30 Mar 2023 18:05:14 -0400 Subject: [PATCH] *Add IPNS support *Re-add in DHT *Create ready helper function --- package.json | 44 +++++++++++--------- src/index.ts | 115 ++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 125 insertions(+), 34 deletions(-) diff --git a/package.json b/package.json index 771ffb6..62bf7cd 100644 --- a/package.json +++ b/package.json @@ -9,22 +9,25 @@ "build": "npm run compile && node ./dist-build/build.mjs dev" }, "dependencies": { - "@chainsafe/libp2p-noise": "^11.0.1", + "@chainsafe/libp2p-gossipsub": "^6.2.0", + "@chainsafe/libp2p-noise": "^11.0.2", "@chainsafe/libp2p-yamux": "^3.0.7", "@dao-xyz/libp2p-noise": "^11.1.3", - "@helia/unixfs": "^1.1.0", + "@helia/ipns": "^1.1.0", + "@helia/unixfs": "^1.2.1", + "@libp2p/bootstrap": "^6.0.3", "@libp2p/crypto": "^1.0.14", - "@libp2p/delegated-content-routing": "^4.0.1", - "@libp2p/delegated-peer-routing": "^4.0.1", + "@libp2p/delegated-content-routing": "^4.0.3", + "@libp2p/delegated-peer-routing": "^4.0.4", "@libp2p/interface-metrics": "^4.0.5", "@libp2p/interfaces": "^3.3.1", - "@libp2p/logger": "^2.0.6", - "@libp2p/mplex": "^7.1.1", - "@libp2p/tcp": "^6.1.2", - "@libp2p/utils": "^3.0.4", + "@libp2p/logger": "^2.0.7", + "@libp2p/mplex": "^7.1.2", + "@libp2p/tcp": "^6.1.4", + "@libp2p/utils": "^3.0.6", "@lumeweb/kernel-swarm-client": "git+https://git.lumeweb.com/LumeWeb/kernel-swarm-client.git", - "@multiformats/mafmt": "^11.1.0", - "b4a": "^1.6.2", + "@multiformats/mafmt": "^11.1.2", + "b4a": "^1.6.3", "blockstore-core": "^3.0.0", "buffer": "^6.0.3", "compact-encoding": "^2.11.0", @@ -38,6 +41,7 @@ "p-queue": "^7.3.4", "private-ip": "^3.0.0", "rewire": "^6.0.0", + "runes2": "^1.0.10", "serialize-error": "^11.0.0", "sodium-universal": "^4.0.0", "streamx": "^2.13.2", @@ -45,22 +49,22 @@ }, "devDependencies": { "@helia/interface": "^0.0.0", - "@libp2p/interface-connection": "^3.1.0", - "@libp2p/interface-peer-info": "^1.0.8", - "@libp2p/interface-transport": "^2.1.1", + "@libp2p/interface-connection": "^3.1.1", + "@libp2p/interface-peer-info": "^1.0.9", + "@libp2p/interface-transport": "^2.1.2", "@libp2p/kad-dht": "^7.0.3", - "@libp2p/peer-id": "^2.0.2", - "@libp2p/peer-id-factory": "^2.0.2", - "@libp2p/websockets": "^5.0.5", + "@libp2p/peer-id": "^2.0.3", + "@libp2p/peer-id-factory": "^2.0.3", + "@libp2p/websockets": "^5.0.7", "@lumeweb/kernel-dht-client": "git+https://git.lumeweb.com/LumeWeb/kernel-swarm-client.git", "@lumeweb/kernel-rpc-client": "git+https://git.lumeweb.com/LumeWeb/kernel-rpc-client.git", "@lumeweb/libhyperproxy": "git+https://git.lumeweb.com/LumeWeb/libhyperproxy.git", "@multiformats/multiaddr": "^11.6.1", - "@scure/bip39": "^1.1.1", + "@scure/bip39": "^1.2.0", "@skynetlabs/skynet-nodejs": "^2.9.0", "@types/b4a": "^1.6.0", "@types/events": "^3.0.0", - "@types/node": "^18.15.3", + "@types/node": "^18.15.11", "@types/read": "^0.0.29", "@types/rewire": "^2.5.28", "@types/streamx": "^2.9.1", @@ -68,7 +72,7 @@ "cli-progress": "^3.12.0", "esbuild": "^0.14.54", "http-browserify": "^1.7.0", - "hyperswarm": "^4.3.7", + "hyperswarm": "^4.4.0", "interface-store": "^3.0.4", "it-all": "^2.0.1", "it-ws": "^5.0.6", @@ -78,7 +82,7 @@ "os-browserify": "^0.3.0", "p-timeout": "^6.1.1", "path-browserify": "^1.0.1", - "prettier": "^2.8.4", + "prettier": "^2.8.7", "prom-client": "^14.2.0", "read": "^1.0.7", "stream-browserify": "^3.0.0", diff --git a/src/index.ts b/src/index.ts index e5e3dc7..26a9b2a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,11 +21,31 @@ import { delegatedPeerRouting } from "@libp2p/delegated-peer-routing"; import { noise } from "@chainsafe/libp2p-noise"; import { create as createIpfsHttpClient } from "ipfs-http-client"; import { delegatedContentRouting } from "@libp2p/delegated-content-routing"; +// @ts-ignore import type { Options } from "ipfs-core"; import { multiaddr } from "@multiformats/multiaddr"; import { DELEGATE_LIST, PROTOCOL } from "./constants.js"; import { ActiveQuery, addHandler, handleMessage } from "libkmodule"; import { createClient } from "@lumeweb/kernel-swarm-client"; +import { ipns, ipnsValidator, ipnsSelector, IPNS } from "@helia/ipns"; +import { dht, pubsub } from "@helia/ipns/routing"; +import { kadDHT } from "@libp2p/kad-dht"; +// @ts-ignore +import { gossipsub } from "@chainsafe/libp2p-gossipsub"; +import { CID } from "multiformats/cid"; +import { bases } from "multiformats/basics"; +import { substr } from "runes2"; +import { MultibaseDecoder } from "multiformats"; +import { peerIdFromCID } from "@libp2p/peer-id"; +import { bootstrap } from "@libp2p/bootstrap"; + +const basesByPrefix: { [prefix: string]: MultibaseDecoder } = Object.keys( + bases +).reduce((acc, curr) => { + // @ts-ignore + acc[bases[curr].prefix] = bases[curr]; + return acc; +}, {}); onmessage = handleMessage; @@ -37,6 +57,7 @@ let moduleLoaded: Promise = new Promise((resolve) => { let swarm; let proxy: Proxy; let fs: UnixFS; +let IPNS: IPNS; // @ts-ignore BigInt.prototype.toJSON = function () { @@ -47,26 +68,56 @@ addHandler("presentSeed", handlePresentSeed); addHandler("stat", handleStat); addHandler("ls", handleLs, { receiveUpdates: true }); addHandler("cat", handleCat, { receiveUpdates: true }); +addHandler("ipnsResolve", handleIpnsResolve); async function handlePresentSeed() { swarm = createClient(); const client = createIpfsHttpClient(getDelegateConfig()); + const libp2p = await createLibp2p({ + peerDiscovery: [ + bootstrap({ + list: [ + "/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", + "/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa", + "/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb", + "/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt", + "/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", + ], + }), + ], + transports: [hypercoreTransport({ peerManager: PeerManager.instance })], + connectionEncryption: [noise()], + connectionManager: { + autoDial: true, + }, + streamMuxers: [yamux(), mplex()], + start: false, + contentRouters: [delegatedContentRouting(client)], + peerRouters: [delegatedPeerRouting(client)], + relay: { + enabled: true, + advertise: { + enabled: false, + }, + }, + dht: kadDHT({ + validators: { + ipns: ipnsValidator, + }, + selectors: { + ipns: ipnsSelector, + }, + }), + pubsub: gossipsub(), + }); + + // @ts-ignore PeerManager.instance.ipfs = await createHelia({ blockstore: new MemoryBlockstore(), datastore: new MemoryDatastore(), - libp2p: await createLibp2p({ - transports: [hypercoreTransport({ peerManager: PeerManager.instance })], - connectionEncryption: [noise()], - streamMuxers: [yamux(), mplex()], - start: false, - contentRouters: [delegatedContentRouting(client)], - peerRouters: [delegatedPeerRouting(client)], - relay: { - enabled: false, - }, - }), + libp2p, }); proxy = new Proxy({ @@ -89,13 +140,18 @@ async function handlePresentSeed() { swarm.join(PROTOCOL); await swarm.start(); + await swarm.ready(); // @ts-ignore fs = unixfs(PeerManager.instance.ipfs); + IPNS = ipns(PeerManager.instance.ipfs as any, [ + dht(PeerManager.instance.ipfs), + pubsub(PeerManager.instance.ipfs as any), + ]); moduleLoadedResolve(); } async function handleStat(aq: ActiveQuery) { - await moduleLoaded; + await ready(); if (!("cid" in aq.callerInput)) { aq.reject("cid required"); @@ -122,7 +178,7 @@ async function handleStat(aq: ActiveQuery) { } async function handleLs(aq: ActiveQuery) { - await moduleLoaded; + await ready(); if (!("cid" in aq.callerInput)) { aq.reject("cid required"); return; @@ -147,7 +203,7 @@ async function handleLs(aq: ActiveQuery) { } async function handleCat(aq: ActiveQuery) { - await moduleLoaded; + await ready(); if (!("cid" in aq.callerInput)) { aq.reject("cid required"); @@ -173,6 +229,37 @@ async function handleCat(aq: ActiveQuery) { aq.respond(); } +async function handleIpnsResolve(aq: ActiveQuery) { + await ready(); + if (!aq.callerInput || !("cid" in aq.callerInput)) { + aq.reject("cid required"); + return; + } + + const prefix = substr(aq.callerInput.cid, 0, 1); + + if (!(prefix in basesByPrefix)) { + aq.reject("invalid multibase found in CID"); + return; + } + + const base = basesByPrefix[prefix]; + const cid = CID.parse(aq.callerInput.cid, base); + + try { + return aq.respond( + (await IPNS.resolve(peerIdFromCID(cid), aq.callerInput?.options)).asCID + ); + } catch (e: any) { + aq.reject((e as Error).message); + } +} + +async function ready() { + await moduleLoaded; + await PeerManager.instance.ipfsReady; +} + function getDelegateConfig(): Options { const delegateString = DELEGATE_LIST[Math.floor(Math.random() * DELEGATE_LIST.length)];