Skip to content

Instantly share code, notes, and snippets.

@bellbind
Last active August 8, 2024 19:20
Show Gist options
  • Save bellbind/fbbb4eb9512e62c589057aa677866b5f to your computer and use it in GitHub Desktop.
Save bellbind/fbbb4eb9512e62c589057aa677866b5f to your computer and use it in GitHub Desktop.
[helia] connect browser to server on localhost with archived webRTCStar
export * from "@libp2p/bootstrap";
export * from "libp2p/circuit-relay";
export * from "@helia/unixfs";
export * from "helia";
export * from "@multiformats/multiaddr";
export * from "multiformats";
export * from "@libp2p/webrtc-star";
export * from "@libp2p/webrtc";
export * from "@libp2p/websockets";
export * from "@libp2p/webtransport";
export * from "@libp2p/websockets/filters";
<!doctype html>
<html>
<head>
<link rel="icon" href="data:image/x-icon," />
<meta http-equiv="Content-Security-Policy" content="default-src * data: ws: wss: http: https: 'self' 'unsafe-inline' 'unsafe-eval';" />
<script type="importmap">
{
"imports": {
"helia": "./lib-helia.js",
"@helia/unixfs": "./lib-helia-unixfs.js",
"@libp2p/bootstrap": "./lib-bootstrap.js",
"libp2p/circuit-relay": "./lib-circuit.js",
"@libp2p/webrtc": "./lib-webrtc.js",
"@libp2p/webtransport": "./lib-webtransport.js",
"@libp2p/websockets": "./lib-websockets.js",
"@libp2p/websockets/filters": "./lib-ws-filters.js",
"@libp2p/webrtc-star": "./lib-webrtc-star.js",
"multiformats/cid": "./lib-multiformats.js",
"@multiformats/multiaddr": "./lib-multiaddr.js"
}
}
</script>
<script type="module" src="./helia-on-browser.js"></script>
</head>
<body>
Open JavaScript console to check result
</body>
</html>
// npm i helia @helia/unixfs
// const helia = Helia, {unixfs} = HeliaUnixfs, {CID} = Multiformats; // from CDN
import * as helia from "helia";
import {unixfs} from "@helia/unixfs";
import {CID} from "multiformats/cid";
import {multiaddr} from "@multiformats/multiaddr";
import {bootstrap} from "@libp2p/bootstrap";
import {circuitRelayTransport} from "libp2p/circuit-relay";
import {webRTC, webRTCDirect} from "@libp2p/webrtc";
import {webTransport} from "@libp2p/webtransport";
import {webSockets} from "@libp2p/websockets";
import {webRTCStar} from "@libp2p/webrtc-star";
import * as filters from "@libp2p/websockets/filters";
//NOTE: not implement rs[Symbol.asyncIterator] in browser impls
const rsWithAi = rs => {
if (!(Symbol.asyncIterator in rs)) rs[Symbol.asyncIterator] = async function *() {
const reader = rs.getReader();
try {
while (true) {
const {value, done} = await reader.read();
if (done) return;
yield value;
}
} finally {
reader.releaseLock();
}
};
return rs;
};
// https://github.com/ipfs/helia/blob/main/packages/helia/src/utils/bootstrappers.ts
const bootstrapConfig = {
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',
]
};
const star = webRTCStar();
const node1 = await helia.createHelia({
libp2p: {
// https://github.com/ipfs/helia/blob/main/packages/helia/src/utils/libp2p-defaults.browser.ts#L27
addresses: {
listen: [
"/webrtc", "/wss", "/ws",
"/ip4/127.0.0.1/tcp/9090/ws/p2p-webrtc-star", // see
],
},
transports: [
webRTC(), webRTCDirect(),
webTransport(),
// https://github.com/libp2p/js-libp2p-websockets#libp2p-usage-example
webSockets({filter: filters.all}),
circuitRelayTransport({discoverRelays: 1}),
star.transport,
],
peerDiscovery: [bootstrap(bootstrapConfig), star.discovery],
// https://github.com/libp2p/js-libp2p/blob/master/doc/CONFIGURATION.md#configuring-connection-gater
connectionGater: {
denyDialMultiaddr: async (...args) => false,
},
},
}); // tcp network, stored on memory (not use files)
console.log("[createHelia]");
const node1fs = unixfs(node1);
//IMPORTANT: must await libp2p.getMultiaddrs().length > 0
while (node1.libp2p.getMultiaddrs().length === 0) await new Promise(f => setTimeout(f, 500));
console.log("[libp2p.getMultiaddrs]", node1.libp2p.getMultiaddrs().map(ma => `${ma}`));
// libp2p dialProtocol examples
const proto = "/my-echo/0.1";
const handler = ({connection, stream}) => {
stream.sink(async function* () {
for await (const bufs of stream.source) {
yield bufs.slice().slice();
}
}());
};
await node1.libp2p.handle(proto, handler);
const send = async (ma, msg) => {
if (typeof ma === "string") ma = multiaddr(ma);
const stream = await node1.libp2p.dialProtocol(ma, proto);
stream.sink(async function* () {
yield (new TextEncoder().encode(msg));
}());
for await (const bufs of stream.source) {
return new TextDecoder().decode(bufs.slice().slice());
}
};
// for web console
window.ctx = {
helia, CID, multiaddr, node1, node1fs,
maStar: localId => multiaddr(`/ip4/127.0.0.1/tcp/9090/ws/p2p-webrtc-star/p2p/${localId}`),
maP2p: localId => multiaddr(`/p2p/${localId}`),
cidHw: CID.parse("bafkreid7qoywk77r7rj3slobqfekdvs57qwuwh5d2z3sqsw52iabe3mqne"), // served on helia-wrtc-star.mjs
send,
};
/*
// example data
const blob = new Blob([new TextEncoder().encode("Hello World!")], {type: "text/plain;charset=utf-8"});
console.log("[Blob]", blob);
// publish blob as CID with addByteStream()
const cid = await ctx.node1fs.addByteStream(rsWithAi(blob.stream()));
//const cid = await ctx.node1fs.addBytes(new Uint8Array(await blob.arrayBuffer()));
console.log("[unixfs.addByteStream]", cid);
const cidStr = cid.toString();
const cidAlt = CID.parse(cidStr);
const ret1 = await ctx.node1.pins.add(cidAlt); //NOTE: pins not accept CID string
console.log("[pins.add]", ret1);
// NOTE: helia's pins needs stored blocks in blockstore (e.g. cannnot pin before stat()/ls()/cat())
const stat = await ctx.node1fs.stat(cidStr);
console.log("[unixfs.stat]", stat);
// get CID object from CID string with ls() from @helia/unixfs
for await (const entry of ctx.node1fs.ls(cidStr)) console.log("[unixfs.ls]", entry.cid);
// retrieve data with cat(cid or cid-string)
const u8as = [];
for await (const u8a of ctx.node1fs.cat(cidStr)) {
console.log("[unixfs.cat]", u8a);
u8as.push(u8a.slice());
}
console.log("[Blob.text]", await (new Blob(u8as).text()));
*/
// stop only helia nodes; unixfs is just a wrapper
//console.log("[helia.stop]", await node1.stop());
// helia
import * as helia from "helia";
import {unixfs} from "@helia/unixfs";
import {CID} from "multiformats/cid";
import {multiaddr} from "@multiformats/multiaddr";
// modules required for helia creation on nodejs
// transports
import {tcp} from "@libp2p/tcp";
import {webSockets} from "@libp2p/websockets";
import {webRTC, webRTCDirect} from "@libp2p/webrtc";
import {circuitRelayTransport, circuitRelayServer} from "libp2p/circuit-relay";
// peerDiscovery
import {mdns} from "@libp2p/mdns";
import {bootstrap} from "@libp2p/bootstrap";
// contentRouters
import {ipniContentRouting} from "@libp2p/ipni-content-routing";
//wrtc-star
import {sigServer} from "@libp2p/webrtc-star-signalling-server";
import {webRTCStar} from "@libp2p/webrtc-star";
import wrtc from "@koush/wrtc";
// repl
import * as repl from "node:repl";
// for webrtc-star
const sig = await sigServer({
host: "0.0.0.0",
port: 9090,
});
const sigAddr = "/ip4/127.0.0.1/tcp/9090/ws/p2p-webrtc-star";
// https://github.com/ipfs/helia/blob/main/packages/helia/src/utils/bootstrappers.ts
const bootstrapConfig = {
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',
]
};
const star = webRTCStar({wrtc});
// https://github.com/ipfs/helia/blob/main/packages/helia/src/index.ts
const node = await helia.createHelia({libp2p: {
addresses: {
listen: [
"/ip4/0.0.0.0/tcp/0",
"/ip4/0.0.0.0/tcp/0/ws",
//"/ip4/0.0.0.0/tcp/0/wss",
sigAddr,
]
},
transports: [
tcp(),
webSockets({websocket: {rejectUnauthorized: false}}),
circuitRelayTransport({discoverRelays: 1}),
star.transport,
],
peerDiscovery: [mdns(), bootstrap(bootstrapConfig), star.discovery],
// from https://github.com/libp2p/js-libp2p-webtransport/blob/main/examples/fetch-file-from-kubo/src/libp2p.ts
connectionGater: {denyDialMultiaddr: async () => false},
}});
// libp2p dialProtocol examples
const proto = "/my-echo/0.1";
const handler = ({connection, stream}) => {
stream.sink(async function* () {
for await (const bufs of stream.source) {
yield bufs.slice().slice();
}
}());
};
await node.libp2p.handle(proto, handler);
const send = async (ma, msg) => {
if (typeof ma === "string") ma = multiaddr(ma);
const stream = await node.libp2p.dialProtocol(ma, proto);
stream.sink(async function* () {
yield (new TextEncoder().encode(msg));
}());
for await (const bufs of stream.source) {
return new TextDecoder().decode(bufs.slice().slice());
}
};
// ipfs examples
console.log("[multiaddrs]");
console.log(node.libp2p.getMultiaddrs().map(ma => `${ma}`));
console.log("[serve example data] try to access the CID from other node");
const nodefs = unixfs(node);
const blob = new Blob([new TextEncoder().encode("Hello World!")], {type: "text/plain;charset=utf-8"});
const cid = await nodefs.addByteStream(blob.stream());
console.log(cid);
const cidStr = cid.toString();
const cidAlt = CID.parse(cidStr);
const ret1 = await node.pins.add(cidAlt); //NOTE: pins not accept CID string
console.log(ret1);
// control with repl
console.log("To stop with Ctrl+D");
const stop = () => Promise.all([node.stop(), sig.stop()]);
const rs = repl.start({
prompt: "> ",
});
rs.once("exit", () => {
stop().then(() => {
console.log("node and sig stopped...");
rs.close();
}).catch(console.error);
});
Object.assign(rs.context, {
node, nodefs, CID, multiaddr, send,
});
//await Promise.all([node.stop(), sig.stop()]);
{
"devDependencies": {
"@helia/unixfs": "^1.3.0",
"@koush/wrtc": "^0.5.3",
"@libp2p/bootstrap": "^8.0.0",
"@libp2p/mdns": "^8.0.0",
"@libp2p/tcp": "^7.0.3",
"@libp2p/webrtc": "^2.0.11",
"@libp2p/webrtc-star": "^7.0.0",
"@libp2p/webrtc-star-signalling-server": "^4.0.0",
"@libp2p/websockets": "^6.0.3",
"@multiformats/multiaddr": "^12.1.3",
"esbuild": "^0.18.3",
"helia": "^1.3.2",
"http-server": "^14.1.1",
"multiformats": "^12.0.1"
},
"scripts": {
"local": "node helia-wrtc-star.mjs",
"build:helia": "esbuild dep-helia.js --bundle --format=esm --target=chrome114 --define:global=window --keep-names --outfile=lib-helia.js",
"build:unixfs": "esbuild dep-helia-unixfs.js --bundle --format=esm --target=chrome114 --define:global=window --outfile=lib-helia-unixfs.js",
"build:multiformats": "esbuild dep-multiformats.js --bundle --format=esm --target=chrome114 --define:global=window --outfile=lib-multiformats.js",
"build:multiaddr": "esbuild dep-multiaddr.js --bundle --format=esm --target=chrome114 --define:global=window --outfile=lib-multiaddr.js",
"build:bootstrap": "esbuild dep-bootstrap.js --bundle --format=esm --target=chrome114 --define:global=window --outfile=lib-bootstrap.js",
"build:circuit": "esbuild dep-circuit.js --bundle --format=esm --target=chrome114 --define:global=window --outfile=lib-circuit.js",
"build:webrtc": "esbuild dep-webrtc.js --bundle --format=esm --target=chrome114 --define:global=window --outfile=lib-webrtc.js",
"build:webtransport": "esbuild dep-webtransport.js --bundle --format=esm --target=chrome114 --define:global=window --outfile=lib-webtransport.js",
"build:websockets": "esbuild dep-websockets.js --bundle --format=esm --target=chrome114 --define:global=window --outfile=lib-websockets.js",
"build:webrtc-star": "esbuild dep-webrtc-star.js --bundle --format=esm --target=chrome114 --define:global=window --outfile=lib-webrtc-star.js",
"build:ws-filters": "esbuild dep-ws-filters.js --bundle --format=esm --target=chrome114 --define:global=window --outfile=lib-ws-filters.js",
"build": "npm run build:helia && npm run build:unixfs && npm run build:multiformats && npm run build:multiaddr && npm run build:bootstrap && npm run build:circuit && npm run build:webrtc && npm run build:webtransport && npm run build:websockets && npm run build:ws-filters && npm run build:webrtc-star",
"server": "http-server"
}
}
@bellbind
Copy link
Author

bellbind commented Jun 23, 2023

How to play browser to server connection

  1. setup:
$ npm i
$ npm run build
  1. run helia node on nodejs:
$ node helia-wrtc-star.mjs
  1. run web server :
$ npm run server
  1. access http://localhost:8080/helia-on-browser.html, then open Web Console for trying to:
  • check exsitence of multiaddr .../p2p-webrtc-star/... address
    • ctx.node1.libp2p.getMultiaddrs().map(ma => ma.toString())
  • dial to local helia on nodejs multiaddr: /p2p/... or /ip4/127.0.0.1/tcp/9090/ws/p2p-webrtc-star/p2p/...:
    • await ctx.node1.libp2p.dial(ctx.maP2p("12D3...."))
  • resolve content(stat, ls, cat) with cid served from local helia on nodejs
    • stat = await ctx.node1fs.stat(ctx.cidHw)
    • for await (const entry of ctx.node1fs.ls(ctx.cidHw)) console.log("[unixfs.ls]", entry.cid);
const u8as = [];
for await (const u8a of ctx.node1fs.cat(ctx.cidHw)) {
  console.log("[unixfs.cat]", u8a);
  u8as.push(u8a.slice());
}
console.log("[Blob.text]", await (new Blob(u8as).text()));
  1. try to send and receive with libp2p.dialProtocol() with send()
  • Do await send("/p2p/12D3...", "hello") on nodejs repl or web console

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment