Skip to content

Commit

Permalink
feat(node-wasm)!: Add stop() in NodeClient
Browse files Browse the repository at this point in the history
  • Loading branch information
oblique committed Sep 25, 2024
1 parent f6c703a commit 7d5f0e3
Show file tree
Hide file tree
Showing 14 changed files with 167 additions and 163 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ mime_guess = "2.0.4"
redb = "2.1.1"
rust-embed = { version = "8.4.0", features = ["interpolate-folder-path"] }
serde = "1.0.203"
serde_repr = "0.1.19"
tokio = { version = "1.38.0", features = ["macros", "rt-multi-thread"] }
tracing = "0.1.40"
tracing-appender = "0.2.3"
Expand Down
4 changes: 1 addition & 3 deletions cli/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@ use std::env::current_exe;
use anyhow::Result;
use clap::{Parser, ValueEnum};
use lumina_node::network::Network;
use serde_repr::Serialize_repr;

use crate::native;
#[cfg(feature = "browser-node")]
use crate::server;

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, ValueEnum, Serialize_repr)]
#[repr(u8)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub(crate) enum ArgNetwork {
#[default]
Mainnet,
Expand Down
47 changes: 5 additions & 42 deletions cli/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,18 @@ use std::net::SocketAddr;

use anyhow::Result;
use axum::body::Body;
use axum::extract::{Path, State};
use axum::extract::Path;
use axum::http::{header, StatusCode};
use axum::response::Response;
use axum::routing::get;
use axum::{Json, Router};
use axum::Router;
use clap::Args;
use libp2p::Multiaddr;
use lumina_node::network::canonical_network_bootnodes;
use rust_embed::RustEmbed;
use serde::Serialize;
use tokio::net::TcpListener;
use tracing::info;

use crate::common::ArgNetwork;

const SERVER_DEFAULT_BIND_ADDR: &str = "127.0.0.1:9876";

#[derive(Debug, Clone, Serialize)]
struct WasmNodeArgs {
pub network: ArgNetwork,
pub bootnodes: Vec<Multiaddr>,
}

#[derive(RustEmbed)]
#[folder = "$WASM_NODE_OUT_DIR"]
struct WasmPackage;
Expand All @@ -35,40 +24,18 @@ struct StaticResources;

#[derive(Debug, Args)]
pub(crate) struct Params {
/// Network to connect.
#[arg(short, long, value_enum, default_value_t)]
pub(crate) network: ArgNetwork,

/// Listening addresses. Can be used multiple times.
/// Listening address.
#[arg(short, long = "listen", default_value = SERVER_DEFAULT_BIND_ADDR)]
pub(crate) listen_addr: SocketAddr,

/// Bootnode multiaddr, including peer id. Can be used multiple times.
#[arg(short, long = "bootnode")]
pub(crate) bootnodes: Vec<Multiaddr>,
}

pub(crate) async fn run(args: Params) -> Result<()> {
let network = args.network.into();
let bootnodes = if args.bootnodes.is_empty() {
canonical_network_bootnodes(network).collect()
} else {
args.bootnodes
};

let state = WasmNodeArgs {
network: args.network,
bootnodes,
};

let app = Router::new()
.route("/", get(serve_index_html))
.route("/js/*path", get(serve_embedded_path::<StaticResources>))
.route("/wasm/*path", get(serve_embedded_path::<WasmPackage>))
.route("/cfg.json", get(serve_config))
.with_state(state);
.route("/wasm/*path", get(serve_embedded_path::<WasmPackage>));

info!("listening on {}", args.listen_addr);
info!("Listening on http://{}", args.listen_addr);
let listener = TcpListener::bind(&args.listen_addr).await?;

Ok(axum::serve(listener, app.into_make_service()).await?)
Expand All @@ -91,7 +58,3 @@ async fn serve_embedded_path<Source: RustEmbed>(
Err(StatusCode::NOT_FOUND)
}
}

async fn serve_config(state: State<WasmNodeArgs>) -> Json<WasmNodeArgs> {
Json(state.0)
}
16 changes: 15 additions & 1 deletion cli/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@
outline: none;
}

.button {
background-color: var(--bg1);
color: var(--fg1);
border: 1px solid var(--border);
}

.button:focus,
.button:focus-visible {
border: 1px solid var(--fg1);
outline: none;
}

.status {
margin: 1rem 0 1rem 0.5rem;
visibility: hidden;
Expand All @@ -79,6 +91,8 @@
margin-left: 0.5rem;
color: var(--fg1);
}


</style>
</head>

Expand All @@ -97,7 +111,7 @@ <h3>Bootnodes</h3>
<textarea id="bootnodes" class="config" cols=120 rows=8></textarea>

<div>
<button id="start" class="config"><b>Start!</b></button>
<button id="start-stop" class="button" id="start-stop"><b>Start</b></button>
</div>

<h2 class="event_logs">Event Logs</h2>
Expand Down
20 changes: 20 additions & 0 deletions cli/static/lumina_node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Equivalent to `../../node-wasm/js/index.js` but loads `/wasm/lumina_node_wasm.js`

import init, { NodeClient } from "/wasm/lumina_node_wasm.js"

/**
* Spawn a worker running lumina node and get the `NodeClient` connected to it.
*/
export async function spawnNode() {
await init();
let worker = new Worker(new URL("/js/worker.js", import.meta.url), { type: 'module' });
let client = await new NodeClient(worker);

// Workaround
await (new Promise(resolve => setTimeout(resolve, 500)));

return client;
}

export * from "/wasm/lumina_node_wasm.js";
export default init;
126 changes: 54 additions & 72 deletions cli/static/run_node.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,18 @@
Error.stackTraceLimit = 99; // rust stack traces can get pretty big, increase the default

import init, { NodeConfig, NodeClient } from "/wasm/lumina_node_wasm.js";

async function fetch_config() {
const response = await fetch('/cfg.json');
const json = await response.json();

console.log("Received config:", json);

let config = NodeConfig.default(json.network);
if (json.bootnodes.length !== 0) {
config.bootnodes = json.bootnodes;
}

return config;
}
import init, { NodeConfig, spawnNode } from "/js/lumina_node.js";

async function show_stats(node) {
if (!node || !await node.is_running()) {
if (!node || !await node.isRunning()) {
return;
}
const info = await node.syncer_info();
const info = await node.syncerInfo();
document.getElementById("stored-ranges").innerText = info.stored_headers.map((range) => {
return `${range.start}..${range.end}`;
}).join(", ");

let peers_ul = document.createElement('ul');
(await node.connected_peers()).forEach(peer => {
(await node.connectedPeers()).forEach(peer => {
var li = document.createElement("li");
li.innerText = peer;
li.classList.add("mono");
Expand All @@ -35,7 +21,7 @@ async function show_stats(node) {

document.getElementById("peers").replaceChildren(peers_ul);

const network_head = await node.get_network_head_header();
const network_head = await node.getNetworkHeadHeader();
if (network_head == null) {
return
}
Expand All @@ -48,44 +34,6 @@ async function show_stats(node) {
document.getElementById("block-data-square").innerText = `${square_rows}x${square_cols} shares`;
}

function bind_config(data) {
const network_div = document.getElementById("network-id");
const bootnodes_div = document.getElementById("bootnodes");

const update_config_elements = () => {
network_div.value = window.config.network;
bootnodes_div.value = window.config.bootnodes.join("\n");
}

let proxy = {
set: function(obj, prop, value) {
if (prop == "network") {
const config = NodeConfig.default(Number(value));
obj.network = config.network;
obj.bootnodes = config.bootnodes;
} else if (prop == "bootnodes") {
obj[prop] = value;
} else {
return Reflect.set(obj, prop, value);
}

update_config_elements()

return true;
}
};

window.config = new Proxy(data, proxy);
update_config_elements();

network_div.addEventListener("change", event => {
window.config.network = Number(event.target.value.trim());
});
bootnodes_div.addEventListener("change", event => {
window.config.bootnodes = event.target.value.trim().split("\n").map(multiaddr => multiaddr.trim());
});
}

function log_event(event) {
// Skip noisy events
if (event.data.get("event").type == "share_sampling_result") {
Expand All @@ -105,30 +53,64 @@ function log_event(event) {
textarea.scrollTop = textarea.scrollHeight;
}

async function main(document, window) {
await init();
function starting(document) {
document.getElementById("start-stop").disabled = true;
document.querySelectorAll('.config').forEach(elem => elem.disabled = true);
}

async function started(document, window) {
document.getElementById("peer-id").innerText = await window.node.localPeerId();
document.querySelectorAll(".status").forEach(elem => elem.style.visibility = "visible");
document.getElementById("start-stop").innerText = "Stop";
document.getElementById("start-stop").disabled = false;
}

window.node = await new NodeClient("/js/worker.js");
function stopping(document) {
document.getElementById("start-stop").disabled = true;
document.querySelectorAll('.config').forEach(elem => elem.disabled = true);
}

function stopped(document) {
document.querySelectorAll(".status").forEach(elem => elem.style.visibility = "hidden");
document.getElementById("start-stop").innerText = "Start";
document.querySelectorAll('.config').forEach(elem => elem.disabled = false);
document.getElementById("start-stop").disabled = false;
}

async function main(document, window) {
window.node = await spawnNode();

window.events = await window.node.events_channel();
window.events = await window.node.eventsChannel();
window.events.onmessage = (event) => {
log_event(event);
};

bind_config(await fetch_config());
const network_id_div = document.getElementById("network-id");
const bootnodes_div = document.getElementById("bootnodes");
const start_stop_div = document.getElementById("start-stop");

if (await window.node.is_running() === true) {
document.querySelectorAll('.config').forEach(elem => elem.disabled = true);
document.getElementById("peer-id").innerText = await window.node.local_peer_id();
document.querySelectorAll(".status").forEach(elem => elem.style.visibility = "visible");
}
window.config = NodeConfig.default(0);
bootnodes_div.value = window.config.bootnodes.join("\n");

document.getElementById("start").addEventListener("click", async () => {
document.querySelectorAll('.config').forEach(elem => elem.disabled = true);
network_id_div.addEventListener("change", event => {
window.config = NodeConfig.default(Number(event.target.value));
bootnodes_div.value = window.config.bootnodes.join("\n");
});

await window.node.start(window.config);
document.getElementById("peer-id").innerText = await window.node.local_peer_id();
document.querySelectorAll(".status").forEach(elem => elem.style.visibility = "visible");
bootnodes_div.addEventListener("change", event => {
window.config.bootnodes = event.target.value.trim().split("\n").map(multiaddr => multiaddr.trim());
});

start_stop_div.addEventListener("click", async () => {
if (await window.node.isRunning() === true) {
stopping(document);
await window.node.stop();
stopped(document);
} else {
starting(document);
await window.node.start(window.config);
await started(document, window);
}
});

setInterval(async () => await show_stats(window.node), 1000)
Expand Down
25 changes: 8 additions & 17 deletions cli/static/worker.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,12 @@
import init, { run_worker } from '/wasm/lumina_node_wasm.js';
// Equivalent to `../../node-wasm/js/worker.js` but loads `/wasm/lumina_node_wasm.js`

import init, { NodeWorker } from "/wasm/lumina_node_wasm.js";

Error.stackTraceLimit = 99;

// for SharedWorker we queue incoming connections
// for dedicated Worker we queue incoming messages (coming from the single client)
let queued = [];
if (typeof SharedWorkerGlobalScope !== 'undefined' && self instanceof SharedWorkerGlobalScope) {
onconnect = (event) => {
queued.push(event)
}
} else {
onmessage = (event) => {
queued.push(event);
}
}
init().then(async () => {
let worker = new NodeWorker(self);
console.log("starting worker: ", worker);

init().then(() => {
console.log("starting worker, queued messages: ", queued.length);
run_worker(queued);
})
await worker.run();
});
Loading

0 comments on commit 7d5f0e3

Please sign in to comment.