Compare commits

...

56 Commits

Author SHA1 Message Date
semantic-release-bot c2674c9df2 chore(release): 0.0.2-develop.1 [skip ci]
## [0.0.2-develop.1](https://git.lumeweb.com/LumeWeb/kernel-ipfs/compare/v0.0.1...v0.0.2-develop.1) (2023-07-06)
2023-07-06 08:07:09 +00:00
Derrick Hammer 04438ee02c
ci: add repository to package.json 2023-07-06 01:20:14 -04:00
Derrick Hammer 15df3a86d3
ci: setup 2023-07-06 01:15:26 -04:00
Derrick Hammer 659428c812
ci: add npm-shrinkwrap.json 2023-07-06 01:14:56 -04:00
Derrick Hammer 774aab1a21
refactor: move to new sdks and build system 2023-07-06 01:14:10 -04:00
Derrick Hammer 499d2cfbf7
*Update tcp patch 2023-04-16 20:52:13 -04:00
Derrick Hammer acca3680ac
*Update deps 2023-04-16 20:51:58 -04:00
Derrick Hammer 2fd5b11582
*Update to use new multisocket proxy 2023-04-16 20:51:32 -04:00
Derrick Hammer 824881ed88
* Add support for chunked streaming in `index.ts` by setting up a next chunk promise and handling "next" and "abort" messages in the receive update callback. 2023-04-09 17:16:12 -04:00
Derrick Hammer f69ff102cc
Revert "Revert "* Remove timers.js from build.js inject.""
This reverts commit 184eed8be8.
2023-04-09 02:38:32 -04:00
Derrick Hammer 184eed8be8
Revert "* Remove timers.js from build.js inject."
This reverts commit 17bb912927.
2023-04-09 00:51:16 -04:00
Derrick Hammer 50271fad44
* Add '@lumeweb/libkernel-universal' as dependency and make socket.ts asynchronous using 'maybeGetAsyncProperty'. 2023-04-09 00:50:14 -04:00
Derrick Hammer 1dedb7b240
*patch b4a 2023-04-09 00:47:56 -04:00
Derrick Hammer 17bb912927
* Remove timers.js from build.js inject. 2023-04-08 23:35:03 -04:00
Derrick Hammer b6f1df264d
* use protomux kernel client 2023-04-08 23:24:35 -04:00
Derrick Hammer 76d9b69935
* Export TCP class and add exports for additional modules in package.json. 2023-04-08 23:24:08 -04:00
Derrick Hammer ffc62dc5be
*patch 6.1.5 not 5.0.2 2023-04-08 23:19:05 -04:00
Derrick Hammer 911c34076e
* Add new import paths for socket-to-conn and utils in package.json, and apply a patch to use "@libp2p__tcp@5.0.2" version in patchedDependencies. 2023-04-08 23:13:04 -04:00
Derrick Hammer df42526b21
*Remove unneeded deps 2023-04-06 14:10:27 -04:00
Derrick Hammer a18deea021
*update deps 2023-04-04 11:33:20 -04:00
Derrick Hammer 04bcc3f7eb
*update deps 2023-04-04 07:21:43 -04:00
Derrick Hammer cba2a126ae
*switch to iife 2023-04-04 06:30:49 -04:00
Derrick Hammer 6f5e0765d6
*add crypto-browserify 2023-04-04 06:30:35 -04:00
Derrick Hammer 6cc15997f8
*add webcrypto polyfill fork 2023-04-04 06:30:22 -04:00
Derrick Hammer 4dd54a4f8b
*Add ready method 2023-04-03 19:02:13 -04:00
Derrick Hammer 3ba792cd35
*attempt to return the normal CID before manually finding its base 2023-04-01 12:57:14 -04:00
Derrick Hammer 3955ff2c29
*Create getCID helper and have all api calls use it 2023-04-01 12:51:11 -04:00
Derrick Hammer bc10722539
*Check if we have peers when doing a IPNS lookup and if not, force reset the peer defer, then await on it 2023-03-31 11:37:04 -04:00
Derrick Hammer fd2d66d2c4
*add getActivePeers api method 2023-03-30 21:06:04 -04:00
Derrick Hammer cad93cbb21
*Wait until we have an active peer to try and do an IPNS lookup. 2023-03-30 21:05:45 -04:00
Derrick Hammer b9b8040b50
*switch to p-defer 2023-03-30 20:57:08 -04:00
Derrick Hammer fe6dac6e4a
*Add Cloudflare, Estuary, Pinata, Eternum, 8api.sh, and Storj community nodes to the bootstrap list 2023-03-30 20:18:21 -04:00
Derrick Hammer 9edc8269fa
*Pass CID as string, not object 2023-03-30 19:01:50 -04:00
Derrick Hammer 053e988ea8
*switch to indexeddb blockstore and datastore 2023-03-30 18:56:26 -04:00
Derrick Hammer f0e6c5aa02
*Need to trigger creation of ipfsReady promise 2023-03-30 18:55:36 -04:00
Derrick Hammer 74498c5691
*Add IPNS support
*Re-add in DHT
*Create ready helper function
2023-03-30 18:05:14 -04:00
Derrick Hammer 2ec2663ced
*Remove unneeded const 2023-03-17 05:37:27 -04:00
Derrick Hammer 4855c993fd
*Remove unneeded method 2023-03-17 05:37:11 -04:00
Derrick Hammer 8345026736
*Rewrite to use new IPFS proxy protocol over relay network 2023-03-17 05:36:02 -04:00
Derrick Hammer cd7f89b617
*Change to ternary and return null if there is a stream 2022-09-20 06:16:45 -04:00
Derrick Hammer 9ebee0eefd
*Add support for canceling the stream 2022-09-05 06:30:20 -04:00
Derrick Hammer c674e18229
*add queryTimeout 2022-08-31 18:34:20 -04:00
Derrick Hammer c894082b9d
*set methods to default to empty array 2022-08-31 18:26:18 -04:00
Derrick Hammer bcf4b0d998
*set relayTimeout to 30 seconds 2022-08-31 18:25:55 -04:00
Derrick Hammer 7834ed5e6d
*Need to pass methods to check in module.method format 2022-08-31 18:25:39 -04:00
Derrick Hammer e30f1797fb
*PingRPCResponse returns a string, not an object 2022-08-31 18:25:04 -04:00
Derrick Hammer d7a17ca163
*Use updated rpc client/protocol 2022-08-31 15:19:35 -04:00
Derrick Hammer 5d0f30c49d
*force is now bypassCache 2022-08-21 21:45:22 -04:00
Derrick Hammer 524372c49b
*Remove timer
*Remove check for error on response?.data
*Refactor stream logic to not stream the done message
2022-08-21 21:45:06 -04:00
Derrick Hammer afdeca0bca
*Update types
*Remove timer
2022-08-21 21:43:21 -04:00
Derrick Hammer 564306bed2
*If we have no active gateways, try to refresh the list first 2022-08-14 06:19:13 -04:00
Derrick Hammer b70c77eeda
*Remove use of aq 2022-08-13 05:41:36 -04:00
Derrick Hammer 6aca65b3e1
*Add missing resolve call 2022-08-10 17:51:41 -04:00
Derrick Hammer d778255b2a
*exclude libkernel 2022-08-06 01:14:04 -04:00
Derrick Hammer 3ca8fd9711
*remove error 2022-08-06 01:13:15 -04:00
Derrick Hammer 909e8be59e
*Initial version 2022-08-05 09:25:55 -04:00
11 changed files with 30272 additions and 0 deletions

13
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,13 @@
name: Build/Publish
on:
push:
branches:
- master
- develop
- develop-*
jobs:
main:
uses: lumeweb/github-node-deploy-workflow/.github/workflows/main.yml@master
secrets: inherit

9
.presetterrc.json Normal file
View File

@ -0,0 +1,9 @@
{
"preset": [
"@lumeweb/presetter-kernel-module-preset"
],
"config": {
"official": true,
"browser": true
}
}

1
CHANGELOG.md Normal file
View File

@ -0,0 +1 @@
## [0.0.2-develop.1](https://git.lumeweb.com/LumeWeb/kernel-ipfs/compare/v0.0.1...v0.0.2-develop.1) (2023-07-06)

29456
npm-shrinkwrap.json generated Normal file

File diff suppressed because it is too large Load Diff

47
package.json Normal file
View File

@ -0,0 +1,47 @@
{
"name": "@lumeweb/kernel-ipfs",
"version": "0.0.2-develop.1",
"type": "module",
"readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "gitea@git.lumeweb.com:LumeWeb/kernel-ipfs.git"
},
"scripts": {
"prepare": "presetter bootstrap",
"build": "shx echo 'export default undefined;' > nop.js; run build",
"semantic-release": "semantic-release",
"postinstall": "patch-package"
},
"dependencies": {
"@chainsafe/libp2p-yamux": "^4.0.2",
"@helia/interface": "^1.2.1",
"@helia/ipns": "^1.1.3",
"@helia/unixfs": "^1.4.0",
"@libp2p/bootstrap": "^8.0.0",
"@libp2p/delegated-content-routing": "^4.0.6",
"@libp2p/delegated-peer-routing": "^4.0.9",
"@libp2p/peer-id": "^2.0.4",
"@lumeweb/kernel-swarm-client": "^0.0.2-develop.6",
"@lumeweb/libhyperproxy": "^0.0.2-develop.1",
"@lumeweb/libkernel": "0.1.0-develop.14",
"@lumeweb/presetter-kernel-module-preset": "^0.1.0-develop.30",
"blockstore-idb": "^1.1.1",
"datastore-idb": "^2.1.2",
"helia": "^1.3.5",
"ipfs-core": "^0.18.1",
"ipfs-http-client": "^60.0.1",
"libp2p": "^0.42.2",
"multiformats": "^12.0.1",
"p-defer": "^4.0.0",
"path-browserify": "^1.0.1",
"runes2": "^1.1.2",
"timers-browserify": "^2.0.12"
},
"devDependencies": {
"@rollup/plugin-alias": "^5.0.0",
"os-browserify": "^0.3.0",
"patch-package": "^7.0.0",
"rollup-plugin-ignore-import": "^1.3.2"
}
}

View File

@ -0,0 +1,44 @@
diff --git a/node_modules/@libp2p/tcp/dist/src/index.js b/node_modules/@libp2p/tcp/dist/src/index.js
index fe47701..d239a6a 100644
--- a/node_modules/@libp2p/tcp/dist/src/index.js
+++ b/node_modules/@libp2p/tcp/dist/src/index.js
@@ -8,7 +8,7 @@ import { TCPListener } from './listener.js';
import { toMultiaddrConnection } from './socket-to-conn.js';
import { multiaddrToNetConfig } from './utils.js';
const log = logger('libp2p:tcp');
-class TCP {
+export class TCP {
opts;
metrics;
components;
diff --git a/node_modules/@libp2p/tcp/package.json b/node_modules/@libp2p/tcp/package.json
index e4524a9..74fa657 100644
--- a/node_modules/@libp2p/tcp/package.json
+++ b/node_modules/@libp2p/tcp/package.json
@@ -32,12 +32,20 @@
"!dist/test",
"!**/*.tsbuildinfo"
],
- "exports": {
- ".": {
- "types": "./dist/src/index.d.ts",
- "import": "./dist/src/index.js"
- }
- },
+ "exports": {
+ ".": {
+ "types": "./dist/src/index.d.ts",
+ "import": "./dist/src/index.js"
+ },
+ "./socket-to-conn": {
+ "types": "./dist/src/socket-to-conn.d.ts",
+ "import": "./dist/src/socket-to-conn.js"
+ },
+ "./utils": {
+ "types": "./dist/src/utils.d.ts",
+ "import": "./dist/src/utils.js"
+ }
+ },
"eslintConfig": {
"extends": "ipfs",
"parserOptions": {

76
rollup.config.ts Normal file
View File

@ -0,0 +1,76 @@
// @ts-nocheck
import * as import0 from "@rollup/plugin-json";
import * as import1 from "@rollup/plugin-node-resolve";
import * as import2 from "@rollup/plugin-commonjs";
import * as import3 from "@rollup/plugin-graphql";
import * as import4 from "@rollup/plugin-image";
import * as import5 from "@rollup/plugin-yaml";
import * as import6 from "rollup-plugin-postcss";
import * as import7 from "rollup-plugin-visualizer";
import * as import8 from "@rollup/plugin-wasm";
import * as import9 from "@rollup/plugin-alias";
import * as import10 from "rollup-plugin-ignore-import";
export default {
input: "build/index.js",
output: [
{
file: "lib/index.js",
format: "cjs",
sourcemap: true,
inlineDynamicImports: true,
},
],
plugins: [
import0.default(...([] as const)),
import10.default({
include: [
"**/multiSocket/tcpSocket.js",
"**/node-fetch/**",
"**/@libp2p/tcp/dist/src/listener.js",
],
exclude: [],
}),
import10.default({
include: ["**/@achingbrain/nat-port-mapper/**"],
exclude: [],
body: "export default {}; export const upnpNat = {};",
}),
import10.default({
include: ["**/@libp2p/tcp/dist/src/listener.js"],
exclude: [],
body: "export default {}; export const TCPListener = {};",
}),
import9.default({
entries: {
"node-fetch": "./nop.js",
"stream": "./nop.js",
"path": "path-browserify",
"timers": "timers-browserify",
"os": "os-browserify",
"net": "./nop.js",
},
}),
import1.default(
...([
{
browser: true,
preferBuiltins: false,
dedupe: [
"@lumeweb/libkernel",
"@lumeweb/libweb",
"@lumeweb/libportal",
],
},
] as const),
),
import2.default(
...([{ extensions: [".js", ".jsx", ".ts", ".tsx"] }] as const),
),
import3.default(...([] as const)),
import4.default(...([] as const)),
import5.default(...([] as const)),
import6.default(...([{ inject: { insertAt: "top" } }] as const)),
import7.visualizer(...([] as const)),
import8.default(...([{ targetEnv: "auto-inline" }] as const)),
],
};

8
src/constants.ts Normal file
View File

@ -0,0 +1,8 @@
export const PROTOCOL = "lumeweb.proxy.ipfs";
export const DELEGATE_LIST = [
"/dns4/node0.delegate.ipfs.io/tcp/443/https",
"/dns4/node1.delegate.ipfs.io/tcp/443/https",
"/dns4/node2.delegate.ipfs.io/tcp/443/https",
"/dns4/node3.delegate.ipfs.io/tcp/443/https",
];

404
src/index.ts Normal file
View File

@ -0,0 +1,404 @@
import { createLibp2p, Libp2p } from "libp2p";
import { createHelia } from "helia";
import { yamux } from "@chainsafe/libp2p-yamux";
// @ts-ignore
import Hyperswarm from "hyperswarm";
import { MultiSocketProxy } from "@lumeweb/libhyperproxy";
import { mplex } from "@libp2p/mplex";
import { hypercoreTransport } from "./libp2p/transport.js";
import { UnixFS, unixfs } from "@helia/unixfs";
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";
import type { Options } from "ipfs-core";
import { multiaddr } from "@multiformats/multiaddr";
import { DELEGATE_LIST, PROTOCOL } from "./constants.js";
import {
ActiveQuery,
addHandler,
handleMessage,
} from "@lumeweb/libkernel/module";
import { createClient } from "@lumeweb/kernel-swarm-client";
import { ipns, IPNS, ipnsSelector, ipnsValidator } 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";
import { IDBBlockstore } from "blockstore-idb";
import { IDBDatastore } from "datastore-idb";
import defer from "p-defer";
import { Helia } from "@helia/interface";
// @ts-ignore
import type { Components } from "libp2p/src/components.js";
const basesByPrefix: { [prefix: string]: MultibaseDecoder<any> } = Object.keys(
bases,
).reduce((acc, curr) => {
// @ts-ignore
acc[bases[curr].prefix] = bases[curr];
return acc;
}, {});
onmessage = handleMessage;
const moduleDefer = defer();
let activeIpfsPeersDefer = defer();
let networkPeersAvailable = defer();
let swarm;
let proxy: MultiSocketProxy;
let fs: UnixFS;
let IPNS: IPNS;
let ipfs: Helia;
// @ts-ignore
BigInt.prototype.toJSON = function () {
return this.toString();
};
addHandler("presentKey", handlePresentKey);
addHandler("ready", handleReady);
addHandler("stat", handleStat);
addHandler("ls", handleLs, { receiveUpdates: true });
addHandler("cat", handleCat, { receiveUpdates: true });
addHandler("ipnsResolve", handleIpnsResolve);
addHandler("getActivePeers", handleGetActivePeers);
async function handlePresentKey() {
swarm = createClient();
const client = createIpfsHttpClient(getDelegateConfig());
proxy = new MultiSocketProxy({
swarm,
listen: true,
protocol: PROTOCOL,
autostart: true,
emulateWebsocket: true,
server: false,
});
const libp2p = await createLibp2p({
peerDiscovery: [
bootstrap({
list: [
// Default Bootstrap
"/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",
// Cloudflare
"/dnsaddr/node-1.ingress.cloudflare-ipfs.com/p2p/QmcFf2FH3CEgTNHeMRGhN7HNHU1EXAxoEk6EFuSyXCsvRE",
"/dnsaddr/node-2.ingress.cloudflare-ipfs.com/p2p/QmcFmLd5ySfk2WZuJ1mfSWLDjdmHZq7rSAua4GoeSQfs1z",
"/dnsaddr/node-3.ingress.cloudflare-ipfs.com/p2p/QmcfFmzSDVbwexQ9Au2pt5YEXHK5xajwgaU6PpkbLWerMa",
"/dnsaddr/node-4.ingress.cloudflare-ipfs.com/p2p/QmcfJeB3Js1FG7T8YaZATEiaHqNKVdQfybYYkbT1knUswx",
"/dnsaddr/node-5.ingress.cloudflare-ipfs.com/p2p/QmcfVvzK4tMdFmpJjEKDUoqRgP4W9FnmJoziYX5GXJJ8eZ",
"/dnsaddr/node-6.ingress.cloudflare-ipfs.com/p2p/QmcfZD3VKrUxyP9BbyUnZDpbqDnT7cQ4WjPP8TRLXaoE7G",
"/dnsaddr/node-7.ingress.cloudflare-ipfs.com/p2p/QmcfZP2LuW4jxviTeG8fi28qjnZScACb8PEgHAc17ZEri3",
"/dnsaddr/node-8.ingress.cloudflare-ipfs.com/p2p/QmcfgsJsMtx6qJb74akCw1M24X1zFwgGo11h1cuhwQjtJP",
"/dnsaddr/node-9.ingress.cloudflare-ipfs.com/p2p/Qmcfr2FC7pFzJbTSDfYaSy1J8Uuy8ccGLeLyqJCKJvTHMi",
"/dnsaddr/node-10.ingress.cloudflare-ipfs.com/p2p/QmcfR3V5YAtHBzxVACWCzXTt26SyEkxdwhGJ6875A8BuWx",
"/dnsaddr/node-11.ingress.cloudflare-ipfs.com/p2p/Qmcfuo1TM9uUiJp6dTbm915Rf1aTqm3a3dnmCdDQLHgvL5",
"/dnsaddr/node-12.ingress.cloudflare-ipfs.com/p2p/QmcfV2sg9zaq7UUHVCGuSvT2M2rnLBAPsiE79vVyK3Cuev",
// Estuary
"/ip4/139.178.68.217/tcp/6744/p2p/12D3KooWCVXs8P7iq6ao4XhfAmKWrEeuKFWCJgqe9jGDMTqHYBjw",
"/ip4/147.75.49.71/tcp/6745/p2p/12D3KooWGBWx9gyUFTVQcKMTenQMSyE2ad9m7c9fpjS4NMjoDien",
"/ip4/147.75.86.255/tcp/6745/p2p/12D3KooWFrnuj5o3tx4fGD2ZVJRyDqTdzGnU3XYXmBbWbc8Hs8Nd",
"/ip4/3.134.223.177/tcp/6745/p2p/12D3KooWN8vAoGd6eurUSidcpLYguQiGZwt4eVgDvbgaS7kiGTup",
"/ip4/35.74.45.12/udp/6746/quic/p2p/12D3KooWLV128pddyvoG6NBvoZw7sSrgpMTPtjnpu3mSmENqhtL7",
// Pinata
"/dnsaddr/fra1-1.hostnodes.pinata.cloud/p2p/QmWaik1eJcGHq1ybTWe7sezRfqKNcDRNkeBaLnGwQJz1Cj",
"/dnsaddr/fra1-2.hostnodes.pinata.cloud/p2p/QmNfpLrQQZr5Ns9FAJKpyzgnDL2GgC6xBug1yUZozKFgu4",
"/dnsaddr/fra1-3.hostnodes.pinata.cloud/p2p/QmPo1ygpngghu5it8u4Mr3ym6SEU2Wp2wA66Z91Y1S1g29",
"/dnsaddr/nyc1-1.hostnodes.pinata.cloud/p2p/QmRjLSisUCHVpFa5ELVvX3qVPfdxajxWJEHs9kN3EcxAW6",
"/dnsaddr/nyc1-2.hostnodes.pinata.cloud/p2p/QmPySsdmbczdZYBpbi2oq2WMJ8ErbfxtkG8Mo192UHkfGP",
"/dnsaddr/nyc1-3.hostnodes.pinata.cloud/p2p/QmSarArpxemsPESa6FNkmuu9iSE1QWqPX2R3Aw6f5jq4D5",
// Eternum
"/dns4/door.eternum.io/tcp/4001/ipfs/QmVBxJ5GekATHi89H8jbXjaU6CosCnteomjNR5xar2aH3q",
// Textile
"/ip4/104.210.43.77/p2p/QmR69wtWUMm1TWnmuD4JqC1TWLZcc8iR2KrTenfZZbiztd",
// 8api.sh
"/ip4/78.46.108.24/p2p/12D3KooWGASC2jm3pmohEJXUhuStkxDitPgzvs4qMuFPaiD9x1BA",
"/ip4/65.109.19.136/p2p/12D3KooWRbWZN3GvLf9CHmozq4vnTzDD4EEoiqtRJxg5FV6Gfjmm",
// Storj
"/ip4/5.161.92.43/tcp/4001/p2p/12D3KooWFFhc8fPYnQXdWBCowxSV21EFYin3rU27p3NVgSMjN41k",
"/ip4/5.161.92.43/udp/4001/quic/p2p/12D3KooWFFhc8fPYnQXdWBCowxSV21EFYin3rU27p3NVgSMjN41k",
"/ip6/2a01:4ff:f0:3b1e::1/tcp/4001/p2p/12D3KooWFFhc8fPYnQXdWBCowxSV21EFYin3rU27p3NVgSMjN41k",
"/ip6/2a01:4ff:f0:3b1e::1/udp/4001/quic/p2p/12D3KooWFFhc8fPYnQXdWBCowxSV21EFYin3rU27p3NVgSMjN41k",
"/ip4/5.161.55.227/tcp/4001/p2p/12D3KooWSW4hoHmDXmY5rW7nCi9XmGTy3foFt72u86jNP53LTNBJ",
"/ip4/5.161.55.227/udp/4001/quic/p2p/12D3KooWSW4hoHmDXmY5rW7nCi9XmGTy3foFt72u86jNP53LTNBJ",
"/ip6/2a01:4ff:f0:1e5a::1/tcp/4001/p2p/12D3KooWSW4hoHmDXmY5rW7nCi9XmGTy3foFt72u86jNP53LTNBJ",
"/ip6/2a01:4ff:f0:1e5a::1/udp/4001/quic/p2p/12D3KooWSW4hoHmDXmY5rW7nCi9XmGTy3foFt72u86jNP53LTNBJ",
"/ip4/5.161.92.36/tcp/4001/p2p/12D3KooWSDj6JM2JmoHwE9AUUwqAFUEg9ndd3pMA8aF2bkYckZfo",
"/ip4/5.161.92.36/udp/4001/quic/p2p/12D3KooWSDj6JM2JmoHwE9AUUwqAFUEg9ndd3pMA8aF2bkYckZfo",
"/ip6/2a01:4ff:f0:3764::1/tcp/4001/p2p/12D3KooWSDj6JM2JmoHwE9AUUwqAFUEg9ndd3pMA8aF2bkYckZfo",
"/ip6/2a01:4ff:f0:3764::1/udp/4001/quic/p2p/12D3KooWSDj6JM2JmoHwE9AUUwqAFUEg9ndd3pMA8aF2bkYckZfo",
],
}) as Components,
],
transports: [hypercoreTransport({ proxy })],
connectionEncryption: [noise() as Components],
connectionManager: {
autoDial: true,
minConnections: 5,
maxConnections: 20,
},
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(),
});
const blockstore = new IDBBlockstore("ipfs_blocks");
const datastore = new IDBDatastore("ipfs_data");
await blockstore.open();
await datastore.open();
ipfs = await createHelia({
blockstore,
datastore,
// @ts-ignore
libp2p,
});
proxy.on("peerChannelOpen", async () => {
if (!ipfs.libp2p.isStarted()) {
await ipfs.libp2p.start();
networkPeersAvailable.resolve();
}
});
swarm.join(PROTOCOL);
await swarm.start();
await swarm.ready();
// @ts-ignore
fs = unixfs(ipfs);
IPNS = ipns(ipfs as any, [dht(ipfs), pubsub(ipfs as any)]);
ipfs.libp2p.addEventListener("peer:connect", () => {
if (ipfs.libp2p.getPeers().length > 0) {
activeIpfsPeersDefer.resolve();
}
});
ipfs.libp2p.addEventListener("peer:disconnect", () => {
if (ipfs.libp2p.getPeers().length === 0) {
activeIpfsPeersDefer = defer();
}
});
moduleDefer.resolve();
}
async function handleReady(aq: ActiveQuery) {
await ready();
aq.respond();
}
async function handleStat(aq: ActiveQuery) {
await ready();
if (!("cid" in aq.callerInput)) {
aq.reject("cid required");
return;
}
let aborted = false;
aq.setReceiveUpdate?.(() => {
aborted = true;
});
try {
aq.respond(
JSON.parse(
JSON.stringify(
await fs.stat(
getCID(aq.callerInput.cid),
aq.callerInput.options ?? {},
),
),
),
);
} catch (e) {
aq.reject((e as Error).message);
}
}
async function handleLs(aq: ActiveQuery) {
await ready();
if (!("cid" in aq.callerInput)) {
aq.reject("cid required");
return;
}
let aborted = false;
let nextChunk = defer();
aq.setReceiveUpdate?.((data: any) => {
switch (data) {
case "abort":
aborted = true;
break;
case "next":
nextChunk.resolve();
nextChunk = defer();
break;
}
});
const iterable = fs.ls(
getCID(aq.callerInput.cid),
aq.callerInput.options ?? {},
);
for await (const item of iterable) {
if (aborted) {
break;
}
aq.sendUpdate(JSON.parse(JSON.stringify(item)));
await nextChunk.promise;
}
aq.respond();
}
async function handleCat(aq: ActiveQuery) {
await ready();
if (!("cid" in aq.callerInput)) {
aq.reject("cid required");
return;
}
let aborted = false;
let nextChunk = defer();
aq.setReceiveUpdate?.((data: any) => {
switch (data) {
case "abort":
aborted = true;
break;
case "next":
nextChunk.resolve();
nextChunk = defer();
break;
}
});
const iterable = fs.cat(
getCID(aq.callerInput.cid),
aq.callerInput.options ?? {},
);
for await (const chunk of iterable) {
if (aborted) {
break;
}
aq.sendUpdate(chunk);
await nextChunk.promise;
}
aq.respond();
}
async function handleIpnsResolve(aq: ActiveQuery) {
await ready();
await activeIpfsPeersDefer.promise;
if (ipfs.libp2p.getPeers().length === 0) {
activeIpfsPeersDefer = defer();
}
await activeIpfsPeersDefer.promise;
if (!aq.callerInput || !("cid" in aq.callerInput)) {
aq.reject("cid required");
return;
}
try {
return aq.respond(
(
await IPNS.resolve(
peerIdFromCID(getCID(aq.callerInput.cid)),
aq.callerInput?.options,
)
).asCID.toString(),
);
} catch (e: any) {
aq.reject((e as Error).message);
}
}
function getCID(cid: string): CID {
try {
return CID.parse(cid);
} catch {}
const prefix = substr(cid, 0, 1);
if (!(prefix in basesByPrefix)) {
throw new Error("invalid multibase found in CID");
}
const base = basesByPrefix[prefix];
return CID.parse(cid, base);
}
async function handleGetActivePeers(aq: ActiveQuery) {
await ready();
aq.respond(ipfs.libp2p.getPeers());
}
async function ready() {
await moduleDefer.promise;
await networkPeersAvailable.promise;
}
function getDelegateConfig(): Options {
const delegateString =
DELEGATE_LIST[Math.floor(Math.random() * DELEGATE_LIST.length)];
const delegateAddr = multiaddr(delegateString).toOptions();
return {
// @ts-ignore
host: delegateAddr.host,
// @ts-ignore
protocol: parseInt(delegateAddr.port) === 443 ? "https" : "http",
port: delegateAddr.port,
};
}

211
src/libp2p/transport.ts Normal file
View File

@ -0,0 +1,211 @@
import { symbol } from "@libp2p/interface-transport";
// @ts-ignore
import { TCP, TCPComponents, TCPDialOptions, TCPOptions } from "@libp2p/tcp";
import { Multiaddr } from "@multiformats/multiaddr";
import { IpcSocketConnectOpts, TcpSocketConnectOpts } from "net";
import { logger } from "@libp2p/logger";
import { AbortError, CodeError } from "@libp2p/interfaces/errors";
// @ts-ignore
import { multiaddrToNetConfig } from "@libp2p/tcp/utils";
import { Connection } from "@libp2p/interface-connection";
// @ts-ignore
import { toMultiaddrConnection } from "@libp2p/tcp/socket-to-conn";
import * as mafmt from "@multiformats/mafmt";
const log = logger("libp2p:hypercore");
import isPrivateIp from "private-ip";
import { DummySocket, MultiSocketProxy, Socket } from "@lumeweb/libhyperproxy";
const CODE_P2P = 421;
const CODE_CIRCUIT = 290;
const CODE_UNIX = 400;
export interface HypercoreOptions extends TCPOptions {
proxy?: MultiSocketProxy;
}
class HypercoreTransport extends TCP {
private readonly opts?: HypercoreOptions;
private metrics: any;
constructor(components: TCPComponents, options: HypercoreOptions = {}) {
super(components, options);
this.opts = options;
if (!options.proxy) {
throw new Error("options.peerManager is required");
}
}
get [symbol](): true {
return true;
}
get [Symbol.toStringTag](): string {
return "@libp2p/hypercore";
}
async dial(ma: Multiaddr, options: TCPDialOptions): Promise<Connection> {
options.keepAlive = options.keepAlive ?? true;
// options.signal destroys the socket before 'connect' event
const socket = await this._connect(ma, options);
// Avoid uncaught errors caused by unstable connections
// @ts-ignore
socket.on("error", (err: any) => {
log("socket error", err);
});
const maConn = toMultiaddrConnection(socket as any, {
remoteAddr: ma,
socketInactivityTimeout: this.opts?.outboundSocketInactivityTimeout,
socketCloseTimeout: this.opts?.socketCloseTimeout,
metrics: this.metrics?.dialerEvents,
});
const onAbort = (): void => {
maConn.close().catch((err: any) => {
log.error("Error closing maConn after abort", err);
});
};
options.signal?.addEventListener("abort", onAbort, { once: true });
log("new outbound connection %s", maConn.remoteAddr);
const conn = await options.upgrader.upgradeOutbound(maConn);
log("outbound connection %s upgraded", maConn.remoteAddr);
options.signal?.removeEventListener("abort", onAbort);
if (options.signal?.aborted === true) {
conn.close().catch((err) => {
log.error("Error closing conn after abort", err);
});
throw new AbortError();
}
return conn;
}
async _connect(ma: Multiaddr, options: TCPDialOptions): Promise<Socket> {
if (options.signal?.aborted === true) {
throw new AbortError();
}
return await new Promise<Socket>(async (resolve, reject) => {
const start = Date.now();
const cOpts = multiaddrToNetConfig(ma) as IpcSocketConnectOpts &
TcpSocketConnectOpts;
const cOptsStr = cOpts.path ?? `${cOpts.host ?? ""}:${cOpts.port}`;
log("dialing %j", cOpts);
let rawSocket: Socket;
const onError = (err: Error): void => {
err.message = `connection error ${cOptsStr}: ${err.message}`;
this.metrics?.dialerEvents.increment({ error: true });
done(err);
};
const onTimeout = (): void => {
log("connection timeout %s", cOptsStr);
this.metrics?.dialerEvents.increment({ timeout: true });
const err = new CodeError(
`connection timeout after ${Date.now() - start}ms`,
"ERR_CONNECT_TIMEOUT",
);
// Note: this will result in onError() being called
// @ts-ignore
rawSocket?.emit("error", err);
};
const onConnect = (): void => {
log("connection opened %j", cOpts);
this.metrics?.dialerEvents.increment({ connect: true });
done();
};
const onAbort = (): void => {
log("connection aborted %j", cOpts);
this.metrics?.dialerEvents.increment({ abort: true });
// @ts-ignore
rawSocket?.destroy();
done(new AbortError());
};
const done = (err?: any): void => {
// @ts-ignore
rawSocket?.removeListener("error", onError);
// @ts-ignore
rawSocket?.removeListener("timeout", onTimeout);
// @ts-ignore
rawSocket?.removeListener("connect", onConnect);
if (options.signal != null) {
options.signal.removeEventListener("abort", onAbort);
}
if (err != null) {
reject(err);
return;
}
resolve(rawSocket as Socket);
};
try {
rawSocket = (await this.opts?.proxy?.createSocket(cOpts)) as Socket;
} catch (e: any) {
onError(e);
}
// @ts-ignore
rawSocket = rawSocket as Socket;
// @ts-ignore
rawSocket?.on("error", onError);
// @ts-ignore
rawSocket?.on("timeout", onTimeout);
// @ts-ignore
rawSocket?.on("connect", onConnect);
if (options.signal != null) {
options.signal.addEventListener("abort", onAbort);
}
(rawSocket as DummySocket)?.connect();
});
}
filter(multiaddrs: Multiaddr[]): Multiaddr[] {
multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs];
return multiaddrs.filter((ma) => {
if (ma.protoCodes().includes(CODE_CIRCUIT)) {
return false;
}
if (ma.protoCodes().includes(CODE_UNIX)) {
return true;
}
const addr = ma.nodeAddress();
if (isPrivateIp(addr.address)) {
return false;
}
return mafmt.TCP.matches(ma.decapsulateCode(CODE_P2P));
});
}
}
export function hypercoreTransport(
init: HypercoreOptions = {},
): (components?: TCPComponents) => HypercoreTransport {
return (components: TCPComponents = {}) => {
return new HypercoreTransport(components, init);
};
}

3
src/net.ts Normal file
View File

@ -0,0 +1,3 @@
export default {
}