1#![cfg(target_family = "wasm")]
2
3use ::node::transition_frontier::genesis::GenesisConfig;
4pub use openmina_node_common::*;
5
6mod rayon;
7pub use rayon::init_rayon;
8
9mod node;
10pub use node::{Node, NodeBuilder};
11
12use ::node::{
13 account::AccountSecretKey,
14 core::thread,
15 snark::{BlockVerifier, TransactionVerifier},
16};
17use anyhow::Context;
18use gloo_utils::format::JsValueSerdeExt;
19use ledger::proofs::provers::BlockProver;
20use openmina_node_common::rpc::RpcSender;
21use wasm_bindgen::prelude::*;
22
23use crate::node::P2pTaskRemoteSpawner;
24
25#[wasm_bindgen(start)]
27fn main() {
28 thread::main_thread_init();
29 wasm_bindgen_futures::spawn_local(async {
30 console_error_panic_hook::set_once();
31 tracing::initialize(tracing::Level::INFO);
32
33 init_rayon().await.unwrap();
34 });
35}
36
37#[wasm_bindgen]
38pub fn build_env() -> JsValue {
39 JsValue::from_serde(&::node::BuildEnv::get()).unwrap_or_default()
40}
41
42fn parse_bp_key(key: JsValue) -> Option<AccountSecretKey> {
43 if key.is_falsy() {
44 return None;
45 }
46
47 if key.is_string() {
48 return Some(
49 key.as_string()
50 .unwrap()
51 .parse()
52 .expect("failed to parse passed block producer keys"),
53 );
54 }
55
56 let (encrypted, password) = if key.is_array() {
57 let arr: js_sys::Array = key.into();
58 let password = arr
59 .at(1)
60 .as_string()
61 .expect("invalid block_producer password");
62 let encrypted = arr
63 .at(0)
64 .into_serde()
65 .expect("block_producer encrypted key decode failed");
66 (encrypted, password)
67 } else {
68 panic!("unsupported block_producer keys type: {key:?}");
69 };
70
71 Some(
72 AccountSecretKey::from_encrypted(&encrypted, &password)
73 .expect("block_producer secret key decrypt failed"),
74 )
75}
76
77#[wasm_bindgen]
78pub async fn run(
79 block_producer: JsValue,
80 seed_nodes_url: Option<String>,
81 genesis_config_url: Option<String>,
82) -> RpcSender {
83 let block_producer = parse_bp_key(block_producer);
84
85 let (rpc_sender_tx, rpc_sender_rx) = ::node::core::channels::oneshot::channel();
86 let _ = thread::spawn(move || {
87 wasm_bindgen_futures::spawn_local(async move {
88 let mut node = setup_node(block_producer, seed_nodes_url, genesis_config_url).await;
89 let _ = rpc_sender_tx.send(node.rpc());
90 node.run_forever().await;
91 });
92
93 keep_worker_alive_cursed_hack();
94 });
95
96 rpc_sender_rx.await.unwrap()
97}
98
99async fn setup_node(
100 block_producer: Option<AccountSecretKey>,
101 seed_nodes_url: Option<String>,
102 genesis_config_url: Option<String>,
103) -> openmina_node_common::Node<NodeService> {
104 let block_verifier_index = BlockVerifier::make().await;
105 let work_verifier_index = TransactionVerifier::make().await;
106
107 let genesis_config = if let Some(genesis_config_url) = genesis_config_url {
108 let bytes = ::node::core::http::get_bytes(&genesis_config_url)
109 .await
110 .expect("failed to fetch genesis config");
111 GenesisConfig::Prebuilt(bytes.into()).into()
112 } else {
113 ::node::config::DEVNET_CONFIG.clone()
114 };
115
116 let mut node_builder: NodeBuilder = NodeBuilder::new(None, genesis_config);
117 node_builder
118 .block_verifier_index(block_verifier_index.clone())
119 .work_verifier_index(work_verifier_index.clone());
120
121 if let Some(seed_nodes_url) = seed_nodes_url {
123 let peers = ::node::core::http::get_bytes(&seed_nodes_url)
124 .await
125 .expect("failed to fetch seed nodes");
126 node_builder.initial_peers(
127 String::from_utf8_lossy(&peers)
128 .split("\n")
129 .filter(|s| !s.trim().is_empty())
130 .map(|s| s.trim().parse().expect("failed to parse seed node addr")),
131 );
132 }
133
134 if let Some(bp_key) = block_producer {
135 thread::spawn(move || {
136 BlockProver::make(Some(block_verifier_index), Some(work_verifier_index));
137 });
138 node_builder.block_producer(bp_key, None);
139 }
140
141 node_builder
142 .p2p_custom_task_spawner(P2pTaskRemoteSpawner {})
143 .unwrap();
144 node_builder.gather_stats();
145 node_builder.build().context("node build failed!").unwrap()
146}
147
148fn keep_worker_alive_cursed_hack() {
149 wasm_bindgen::throw_str("Cursed hack to keep workers alive. See https://github.com/rustwasm/wasm-bindgen/issues/2945");
150}