Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(wasm): Run Lumina in a Shared Worker #265

Merged
merged 40 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
f74b8de
wip
fl0rek Apr 5, 2024
cef4c0c
first pass
fl0rek Apr 9, 2024
830f8eb
add type safety
fl0rek Apr 10, 2024
ac8c77e
Type safe finish
fl0rek Apr 11, 2024
556003a
more macros less code
fl0rek Apr 12, 2024
ad9a2f5
cleanup
fl0rek Apr 12, 2024
46768ed
Start squeezing the errors through the channel
fl0rek Apr 12, 2024
2680863
cleanup
fl0rek Apr 16, 2024
0040592
Merge remote-tracking branch 'eiger/main' into feat/wasm-node-in-worker
fl0rek Apr 16, 2024
8b44387
Organise the code, add error passing instead of unwraps
fl0rek Apr 17, 2024
700ddec
appease clippy
fl0rek Apr 17, 2024
eefd831
Roll back to runtime type checking
fl0rek Apr 18, 2024
b94c5e0
cleanup, rw fix
fl0rek Apr 19, 2024
431be7a
code organisation and cleanup
fl0rek Apr 19, 2024
49a3a28
finishing touchees
fl0rek Apr 19, 2024
58496b0
pass errors through correctly, fix url blob
fl0rek Apr 23, 2024
fb8c5d6
to all the clippies I loved
fl0rek Apr 23, 2024
a74ba16
Apply suggestions from code review
fl0rek May 8, 2024
93fa8b8
Update node-wasm/src/node.rs
fl0rek May 8, 2024
618c332
Update node-wasm/src/node.rs
fl0rek May 8, 2024
5ad5a9d
fixes
fl0rek May 8, 2024
d9a779f
PR fixes
fl0rek May 8, 2024
d35c4ff
another pass
fl0rek May 8, 2024
2527b22
Merge remote-tracking branch 'eiger/main' into feat/wasm-node-in-worker
fl0rek May 8, 2024
dccdb25
mostly error cleanup
fl0rek May 8, 2024
aa6f173
clippin
fl0rek May 8, 2024
662e808
add close, consolidate errors,go back to NodeClient
fl0rek May 9, 2024
0495d05
switch to spawning worker from a static script
zvolin May 10, 2024
d3a2c92
Add Worker/SharedWorker switch for browser that need it
fl0rek May 16, 2024
e2ebffd
Merge branch 'main' into feat/wasm-node-in-worker
fl0rek May 16, 2024
0867f4b
Update node-wasm/js/worker.js
fl0rek May 17, 2024
aee53e4
Merge remote-tracking branch 'origin/main' into feat/wasm-node-in-worker
fl0rek Jun 20, 2024
db19449
rework errors to use new js errors and results
fl0rek Jun 20, 2024
17ab76a
Merge remote-tracking branch 'origin/main' into feat/wasm-node-in-worker
fl0rek Jun 20, 2024
0d3d7e5
Commit to the bit
fl0rek Jun 21, 2024
1743bf9
Apply suggestions from code review
fl0rek Jun 22, 2024
614cf9e
channel simplification 1/2
fl0rek Jun 22, 2024
48670ee
pr review
fl0rek Jun 22, 2024
d93a126
pr reviews
fl0rek Jun 24, 2024
7b4f80b
final touch
fl0rek Jun 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions node-wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ lumina-node = { workspace = true }

anyhow = "1.0.86"
console_error_panic_hook = "0.1.7"
enum-as-inner = "0.6"
futures = "0.3"
gloo-timers = "0.3"
instant = "0.1"
enum-as-inner = "0.6.0"
futures = "0.3.30"
gloo-timers = "0.3.0"
instant = "0.1.13"
js-sys = "0.3.69"
serde = { version = "1.0.203", features = ["derive"] }
serde-wasm-bindgen = "0.6.5"
serde_repr = "0.1.19"
thiserror = "1.0"
thiserror = "1.0.61"
time = { version = "0.3.36", features = ["wasm-bindgen"] }
tokio = { version = "1.38.0", features = ["sync"] }
tracing = "0.1.40"
Expand All @@ -53,12 +53,13 @@ web-sys = { version = "0.3.69", features = [
"DedicatedWorkerGlobalScope",
"MessageEvent",
"MessagePort",
"Navigator",
fl0rek marked this conversation as resolved.
Show resolved Hide resolved
"SharedWorker",
"SharedWorkerGlobalScope",
"Worker",
"WorkerGlobalScope",
"WorkerOptions",
"WorkerType"
"WorkerType",
] }

[package.metadata.docs.rs]
Expand Down
40 changes: 17 additions & 23 deletions node-wasm/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,18 @@ use libp2p::identity::Keypair;
use libp2p::multiaddr::Protocol;
use serde::{Deserialize, Serialize};
use serde_wasm_bindgen::to_value;
use tracing::info;
use wasm_bindgen::prelude::*;
use web_sys::{BroadcastChannel, SharedWorker, Worker, WorkerOptions, WorkerType};
use web_sys::BroadcastChannel;

use lumina_node::blockstore::IndexedDbBlockstore;
use lumina_node::network::{canonical_network_bootnodes, network_genesis, network_id};
use lumina_node::node::NodeConfig;
use lumina_node::store::IndexedDbStore;

use crate::error::{Context, Result};
use crate::utils::{is_chrome, js_value_from_display, JsValueToJsError, Network};
use crate::utils::{is_chrome, js_value_from_display, Network};
use crate::worker::commands::{CheckableResponseExt, NodeCommand, SingleHeaderQuery};
use crate::worker::{worker_script_url, WorkerClient};
use crate::worker::{worker_script_url, AnyWorker, WorkerClient};
use crate::wrapper::libp2p::NetworkInfoSnapshot;

const LUMINA_WORKER_NAME: &str = "lumina";
Expand Down Expand Up @@ -65,32 +64,27 @@ impl NodeDriver {
#[wasm_bindgen(constructor)]
pub async fn new(worker_type: Option<NodeWorkerKind>) -> Result<NodeDriver> {
let url = worker_script_url();
let mut opts = WorkerOptions::new();
opts.type_(WorkerType::Module);
opts.name(LUMINA_WORKER_NAME);

let default_worker_type = if is_chrome() {
// For chrome we default to running in a dedicated Worker because:
// 1. Chrome Android does not support SharedWorkers at all
// 2. On desktop Chrome, if tab running lumina is reloaded, it fails to re-connect to
// previous worker instance and doesn't create a new one, leaving it in non functioning
// limbo
let default_worker_type = if is_chrome().unwrap_or(false) {
NodeWorkerKind::Dedicated
} else {
NodeWorkerKind::Shared
};

let client = match worker_type.unwrap_or(default_worker_type) {
NodeWorkerKind::Shared => {
info!("Starting SharedWorker");
let worker = SharedWorker::new_with_worker_options(&url, &opts)
.to_error("could not create SharedWorker")?;
WorkerClient::from(worker)
}
NodeWorkerKind::Dedicated => {
info!("Starting Worker");
let worker =
Worker::new_with_options(&url, &opts).to_error("could not create Worker")?;
WorkerClient::from(worker)
}
};
let worker = AnyWorker::new(
worker_type.unwrap_or(default_worker_type),
&url,
LUMINA_WORKER_NAME,
)?;

Ok(Self { client })
Ok(Self {
client: WorkerClient::new(worker),
})
}

/// Check whether Lumina is currently running
Expand Down
52 changes: 9 additions & 43 deletions node-wasm/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! Various utilities for interacting with node from wasm.
use std::fmt::{self, Debug};

use js_sys::JsString;
use lumina_node::network;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
Expand All @@ -13,10 +12,7 @@ use tracing_subscriber::fmt::time::UtcTime;
use tracing_subscriber::prelude::*;
use tracing_web::{performance_layer, MakeConsoleWriter};
use wasm_bindgen::prelude::*;
use web_sys::{
window, Crypto, DedicatedWorkerGlobalScope, SharedWorker, SharedWorkerGlobalScope, Worker,
WorkerGlobalScope,
};
use web_sys::{Crypto, DedicatedWorkerGlobalScope, SharedWorker, SharedWorkerGlobalScope, Worker};

use crate::error::{Context, Error, Result};

Expand Down Expand Up @@ -83,27 +79,6 @@ pub(crate) fn to_jsvalue_or_undefined<T: Serialize>(value: &T) -> JsValue {
to_value(value).unwrap_or(JsValue::UNDEFINED)
}

pub(crate) trait JsValueToJsError<T> {
fn to_error<C>(self, context_fn: C) -> Result<T, JsError>
where
C: fmt::Display + Send + Sync + 'static;
}

impl<T> JsValueToJsError<T> for std::result::Result<T, JsValue> {
fn to_error<C>(self, context: C) -> Result<T, JsError>
where
C: fmt::Display + Send + Sync + 'static,
{
self.map_err(|e| {
let error_str = match e.dyn_ref::<JsString>() {
Some(s) => format!("{context}: {s}"),
None => format!("{context}"),
};
JsError::new(&error_str)
})
}
}

pub(crate) trait WorkerSelf {
type GlobalScope;

Expand Down Expand Up @@ -188,23 +163,14 @@ const CHROME_USER_AGENT_DETECTION_STR: &str = "Chrome/";
// currently there's issue with SharedWorkers on Chrome, where restarting lumina's worker
// causes all network connections to fail. Until that's resolved detect chrome and apply
// a workaround.
pub(crate) fn is_chrome() -> bool {
let mut user_agent = None;
if let Some(window) = window() {
user_agent = Some(window.navigator().user_agent());
};
if let Some(worker_scope) = JsValue::from(js_sys::global()).dyn_ref::<WorkerGlobalScope>() {
user_agent = Some(worker_scope.navigator().user_agent());
}

if let Some(user_agent) = user_agent {
user_agent
.as_deref()
.unwrap_or("")
.contains(CHROME_USER_AGENT_DETECTION_STR)
} else {
false
}
pub(crate) fn is_chrome() -> Result<bool, Error> {
js_sys::Reflect::get(&js_sys::global(), &JsValue::from_str("navigator"))
.context("failed to get `navigator` from global object")?
.dyn_into::<web_sys::Navigator>()
.context("`navigator` is not instanceof `Navigator`")?
fl0rek marked this conversation as resolved.
Show resolved Hide resolved
.user_agent()
.context("could not get UserAgent from Navigator")
.map(|user_agent| user_agent.contains(CHROME_USER_AGENT_DETECTION_STR))
}

pub(crate) fn get_crypto() -> Result<Crypto, Error> {
Expand Down
2 changes: 1 addition & 1 deletion node-wasm/src/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use crate::wrapper::libp2p::NetworkInfoSnapshot;
mod channel;
pub(crate) mod commands;

pub(crate) use channel::WorkerClient;
pub(crate) use channel::{AnyWorker, WorkerClient};

const WORKER_MESSAGE_SERVER_INCOMING_QUEUE_LENGTH: usize = 64;

Expand Down
Loading
Loading