Skip to content

Commit

Permalink
improv: local bootstrap node set up w/ example (#200)
Browse files Browse the repository at this point in the history
Co-authored-by: droak <[email protected]>
  • Loading branch information
elielnfinic and d-roak authored Oct 23, 2024
1 parent 1abdbae commit 742ea7f
Show file tree
Hide file tree
Showing 12 changed files with 395 additions and 14 deletions.
4 changes: 4 additions & 0 deletions examples/local-bootstrap/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
dist/
docs/
node_modules/
public/static
29 changes: 29 additions & 0 deletions examples/local-bootstrap/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Topology Protocol Example

This is an example of Topology Protocol usage in a local environment after a user has run a custom bootstrap node and tries to connect it and test the chat room system.

## How to run locally

After cloning the repository, run the following commands:

```bash
cd ts-topology/examples/local-env/chat
yarn
yarn build
yarn dev
```


In your browser, open the URL displayed in the terminal. You will see a form to connect to the bootstrap node. Copy the `peerId` from the terminal where you started the node and paste it in the form. Click "Connect".
By default, the bootstrap node is running on `127.0.0.1:50000` using `ws` but you can change these values and connect to your node.


Debugging is made easier by setting the mode in `webpack.config.js` to "development":

```js
module.exports = {
mode: "development",
...
}
```

27 changes: 27 additions & 0 deletions examples/local-bootstrap/asconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"entries": ["./src/objects/chat.ts"],
"targets": {
"debug": {
"outFile": "dist/asc/debug.wasm",
"textFile": "dist/asc/debug.wat",
"sourceMap": true,
"debug": true
},
"release": {
"outFile": "dist/asc/release.wasm",
"textFile": "dist/asc/release.wat",
"sourceMap": false,
"optimizeLevel": 3,
"shrinkLevel": 0,
"converge": false,
"noAssert": false
}
},
"options": {
"lib": {
"@topology-foundation/crdt": [
"./node_modules/@topology-foundation/blueprints/src/index.asc.ts"
]
}
}
}
44 changes: 44 additions & 0 deletions examples/local-bootstrap/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Topology - Local env</title>
</head>
<body>
<div>
<h1>Topology Protocol - Local bootstrap connect</h1>
<p>Test the connection to a boostrap node and connect your local peers.</p>
<p>
<fieldset id="fieldset_connect_bootstrap_node">
<legend>Connect to a bootstrap node</legend>
<form id="form_connect_to_bootstrap_node" type="post">
<table>
<tr><td>Host address type</td><td>
<select id="bootstrap_node_host_address_type">
<option value="ip4">IPv4 address</option>
<option value="dns4">DNS address</option>
</select>
</td></tr>
<tr><td><span id="bootstrap_addr_type"></span></td><td><input type="text" value="127.0.0.1" placeholder="0.0.0.0" id="bootstrap_node_addr"/></td></tr>
<tr><td>Port</td><td><input type="text" value="50000" placeholder="00000" id="bootstrap_node_port"/></td></tr>
<tr><td>Peer ID</td><td><input type="text" placeholder="12D3..." id="bootstrap_node_peer_id"/></td></tr>
<tr><td>Web socket protocol</td><td>
<input type="radio" id="ws" name="websocket_protocol" checked>
<label for="ws">ws</label>
<input type="radio" id="wss" name="websocket_protocol">
<label for="wss">wss</label>
</td></tr>
<tr><td></td><td><button type="submit">Connect</button></td></tr>
</table>
</form>
</fieldset>
</p>
<p>Connecting to bootstrap node: <span id="bootstrapNode"></span></p>
<p>Current peer ID: <span id="peerId"></span></p>
<p>peers: <span id="peers"></span></p>
</div>

<script type="module" src="/src/index.ts"></script>
</body>
</html>
35 changes: 35 additions & 0 deletions examples/local-bootstrap/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "topology-example-localdev",
"version": "0.2.0",
"description": "Topology Protocol Local Dev Example",
"main": "src/index.ts",
"repository": "https://github.com/topology-foundation/ts-topology.git",
"license": "MIT",
"scripts": {
"asbuild": "asc --config asconfig.json --target release",
"build": "vite build",
"clean": "rm -rf dist/ node_modules/",
"dev": "vite serve",
"start": "ts-node ./src/index.ts"
},
"dependencies": {
"@topology-foundation/blueprints": "0.2.0",
"@topology-foundation/network": "0.2.0",
"@topology-foundation/node": "0.2.0",
"@topology-foundation/object": "0.2.0",
"assemblyscript": "^0.27.29",
"crypto-browserify": "^3.12.0",
"process": "^0.11.10",
"stream-browserify": "^3.0.0",
"ts-node": "^10.9.2",
"uint8arrays": "^5.1.0",
"vm-browserify": "^1.1.2"
},
"devDependencies": {
"@types/node": "^22.5.4",
"ts-loader": "^9.5.1",
"typescript": "^5.5.4",
"vite": "^5.4.6",
"vite-plugin-node-polyfills": "^0.22.0"
}
}
120 changes: 120 additions & 0 deletions examples/local-bootstrap/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import {
TopologyNode,
type TopologyNodeConfig,
} from "@topology-foundation/node";

const local_peer_id = "12D3KooWC6sm9iwmYbeQJCJipKTRghmABNz1wnpJANvSMabvecwJ";
// This is the IP of the local bootstrap node, replace it with the IP of the local node
const local_bootstrap_peer_ip = "127.0.0.1";

if (!local_peer_id) {
console.error(
"topology::network::start::bootstrap: Set local_peer_id in `/examples/local-bootstrap/src/index.ts` file with the peer id of the local bootstrap node",
);
process.exit(1);
}

let node: TopologyNode;
let peers: string[] = [];

const render = () => {
const element_peerId = <HTMLDivElement>document.getElementById("peerId");
element_peerId.innerHTML = node.networkNode.peerId;

const element_peers = <HTMLDivElement>document.getElementById("peers");
element_peers.innerHTML = `[${peers.join(", ")}]`;
};

async function initTopologyNode() {
if (node) {
node.addCustomGroupMessageHandler("", (e) => {
peers = node.networkNode.getAllPeers();
render();
});
}
}

async function main() {
const select_address_type = <HTMLSelectElement>(
document.getElementById("bootstrap_node_host_address_type")
);
const address_type_label = <HTMLSpanElement>(
document.getElementById("bootstrap_addr_type")
);
const bootstrap_node_addr = <HTMLInputElement>(
document.getElementById("bootstrap_node_addr")
);

// Default to IP4
select_address_type.value = "ip4";
address_type_label.innerText = "IP address";

select_address_type?.addEventListener("change", (e) => {
const val = select_address_type.value;
if (val === "ip4") {
address_type_label.innerText = "IP address";
bootstrap_node_addr.placeholder = "0.0.0.0";
bootstrap_node_addr.value = "127.0.0.1";
} else if (val === "dns4") {
address_type_label.innerText = "DNS address";
bootstrap_node_addr.placeholder = "example.com";
bootstrap_node_addr.value = "";
}
});

const connect_form = <HTMLFormElement>(
document.getElementById("form_connect_to_bootstrap_node")
);
connect_form?.addEventListener("submit", async (e) => {
e.preventDefault();
const bootstrap_node_port: HTMLInputElement = <HTMLInputElement>(
document.getElementById("bootstrap_node_port")
);
const bootstrap_node_peer_id: HTMLInputElement = <HTMLInputElement>(
document.getElementById("bootstrap_node_peer_id")
);

if (
!bootstrap_node_addr.value ||
!bootstrap_node_port.value ||
!bootstrap_node_peer_id.value
) {
alert("Please fill in all the fields");
return;
}

const is_ws: HTMLInputElement = <HTMLInputElement>(
document.getElementById("ws")
);

const ws_protocl = is_ws.checked ? "ws" : "wss";
const field_set = <HTMLFieldSetElement>(
document.getElementById("fieldset_connect_bootstrap_node")
);
try {
node = new TopologyNode({
network_config: {
bootstrap_peers: [
`/${select_address_type.value}/${bootstrap_node_addr.value}/tcp/${bootstrap_node_port.value}/${ws_protocl}/p2p/${bootstrap_node_peer_id.value}`,
],
bootstrap: false,
},
});

await node.start();
field_set.style.backgroundColor = "rgba(0, 255, 0, 0.2)";
initTopologyNode();
render();
} catch (e) {
field_set.style.backgroundColor = "rgba(255, 0, 0, 0.2)";
alert("Failed to connect to the bootstrap node");
return;
}
});

render();

// generic message handler
}

main();
14 changes: 14 additions & 0 deletions examples/local-bootstrap/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"rootDir": ".",
"strict": true,
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"allowJs": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
24 changes: 24 additions & 0 deletions examples/local-bootstrap/vite.config.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import path from "node:path";
import { defineConfig } from "vite";
import { nodePolyfills } from "vite-plugin-node-polyfills";

export default defineConfig({
build: {
target: "esnext",
},
plugins: [
nodePolyfills({
overrides: {},
}),
],
optimizeDeps: {
esbuildOptions: {
target: "esnext",
},
},
resolve: {
alias: {
"@topology-foundation": path.resolve(__dirname, "../../packages"),
},
},
});
39 changes: 25 additions & 14 deletions packages/network/src/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import type {
import { pubsubPeerDiscovery } from "@libp2p/pubsub-peer-discovery";
import { webRTC, webRTCDirect } from "@libp2p/webrtc";
import { webSockets } from "@libp2p/websockets";
import * as filters from "@libp2p/websockets/filters";
import { webTransport } from "@libp2p/webtransport";
import { multiaddr } from "@multiformats/multiaddr";
import { type Libp2p, createLibp2p } from "libp2p";
Expand Down Expand Up @@ -63,6 +64,26 @@ export class TopologyNetworkNode {
);
}

const _bootstrapNodesList = this._config?.bootstrap_peers
? this._config.bootstrap_peers
: [
"/dns4/relay.droak.sh/tcp/443/wss/p2p/Qma3GsJmB47xYuyahPZPSadh1avvxfyYQwk8R3UnFrQ6aP",
];

const _pubsubPeerDiscovery = pubsubPeerDiscovery({
interval: 10_000,
topics: ["topology::discovery"],
});

const _peerDiscovery = _bootstrapNodesList.length
? [
_pubsubPeerDiscovery,
bootstrap({
list: _bootstrapNodesList,
}),
]
: [_pubsubPeerDiscovery];

this._node = await createLibp2p({
privateKey,
addresses: {
Expand All @@ -75,19 +96,7 @@ export class TopologyNetworkNode {
},
},
metrics: this._config?.browser_metrics ? devToolsMetrics() : undefined,
peerDiscovery: [
pubsubPeerDiscovery({
interval: 10_000,
topics: ["topology::discovery"],
}),
bootstrap({
list: this._config?.bootstrap_peers
? this._config.bootstrap_peers
: [
"/dns4/relay.droak.sh/tcp/443/wss/p2p/Qma3GsJmB47xYuyahPZPSadh1avvxfyYQwk8R3UnFrQ6aP",
],
}),
],
peerDiscovery: _peerDiscovery,
services: {
autonat: autoNAT(),
dcutr: dcutr(),
Expand All @@ -102,7 +111,9 @@ export class TopologyNetworkNode {
}),
webRTC(),
webRTCDirect(),
webSockets(),
webSockets({
filter: filters.all,
}),
webTransport(),
],
});
Expand Down
13 changes: 13 additions & 0 deletions packages/node/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@ For more information on what are the commands available, run:
topology-node --help
```

#### Running a bootstrap node
You can run a bootstrap node using the following command:

```bash
pnpm relay --config configs/bootstrap.json
```

If you want to run a local bootstrap node, you can use the following command:

```bash
pnpm relay --config configs/local-bootstrap.json
```

### Integration

To integrate the Topology Node into an existing application, you can install it using:
Expand Down
Loading

0 comments on commit 742ea7f

Please sign in to comment.