Skip to main content

hashiverse_client_wasm/
lib.rs

1#![feature(try_blocks)]
2pub mod wasm_transport;
3pub mod wasm_bootstrap_provider;
4pub mod wasm_local_settings;
5pub mod wasm_client_storage;
6pub mod wasm_key_locker;
7pub mod wasm_parallel_pow_generator;
8pub mod with_js_context;
9pub mod hashiverse_client_wasm;
10pub mod wasm_try;
11
12use hashiverse_lib::tools::pow_generator::pow_generator;
13use hashiverse_lib::tools::types::{Hash, Pow};
14use log::{info, trace};
15use std::sync::Arc;
16use wasm_bindgen::prelude::*;
17
18/// Initialise logging and panic hook. Must be called manually before using the WASM module.
19/// Pass `verbose = true` for the main worker (logs confirmation), `false` for PoW sub-workers (silent).
20#[wasm_bindgen]
21pub fn wasm_init(verbose: bool) {
22
23    // Set up logging
24    {
25        fern::Dispatch::new()
26            .level(log::LevelFilter::Trace) // Default level
27            .level_for("wasm_bindgen", log::LevelFilter::Warn)
28            .level_for("scraper", log::LevelFilter::Warn)
29            .level_for("html5ever", log::LevelFilter::Warn)
30            .level_for("selectors", log::LevelFilter::Warn)
31            .chain(fern::Output::call(console_log::log))
32            .apply()
33            .expect("Failed to initialize logging")
34        ;
35
36        if verbose {
37            info!("Logging initialized");
38        }
39    }
40
41    console_error_panic_hook::set_once();
42    if verbose {
43        trace!("WASM module panic hook set");
44    }
45}
46
47/// Sync PoW batch computation for sub-workers. Each sub-worker calls this function
48/// with a portion of the total iteration budget.
49///
50/// Returns a colon-separated string: `salt_hex:pow_u8:hash_hex`
51#[wasm_bindgen]
52pub fn pow_compute_batch(iteration_limit: u32, pow_min: u8, data_hash_hex: String) -> Result<String, JsValue> {
53    let result: anyhow::Result<String> = try {
54        let data_hash_bytes = hex::decode(&data_hash_hex).map_err(|e| anyhow::anyhow!("Invalid data_hash_hex: {}", e))?;
55        let data_hash = Hash::from_slice(&data_hash_bytes)?;
56        let (salt, pow, hash) = pow_generator::run_pool_chunk(iteration_limit as usize, Pow(pow_min), data_hash)?;
57        format!("{}:{}:{}", hex::encode(salt), pow.0, hex::encode(hash))
58    };
59    result.map_err(wasm_try::anyhow_to_js)
60}
61
62/// Global storage for the WasmParallelPowGenerator singleton.
63static WASM_PARALLEL_POW_GENERATOR: std::sync::OnceLock<Arc<wasm_parallel_pow_generator::WasmParallelPowGenerator>> = std::sync::OnceLock::new();
64
65/// Initialize the parallel PoW worker pool. Call from TypeScript, passing an
66/// array of ready `Worker` handles that each run `HashiversePowWorker.ts`.
67#[wasm_bindgen]
68pub fn init_pow_workers(workers_js: JsValue) {
69    let workers_array: js_sys::Array = match workers_js.dyn_into() {
70        Ok(a) => a,
71        Err(_) => {
72            log::warn!("init_pow_workers: expected an Array of Workers");
73            return;
74        }
75    };
76
77    let mut workers = Vec::new();
78    for i in 0..workers_array.length() {
79        let val = workers_array.get(i);
80        match val.dyn_into::<web_sys::Worker>() {
81            Ok(w) => workers.push(w),
82            Err(_) => log::warn!("init_pow_workers: element {} is not a Worker", i),
83        }
84    }
85
86    if workers.is_empty() {
87        log::warn!("init_pow_workers: no valid Workers provided");
88        return;
89    }
90
91    let generator = wasm_parallel_pow_generator::WasmParallelPowGenerator::from_workers(workers);
92    let _ = WASM_PARALLEL_POW_GENERATOR.set(Arc::new(generator));
93}
94
95/// Get the global WasmParallelPowGenerator if initialized.
96pub fn get_wasm_parallel_pow_generator() -> Option<Arc<wasm_parallel_pow_generator::WasmParallelPowGenerator>> {
97    WASM_PARALLEL_POW_GENERATOR.get().cloned()
98}