1#![cfg(target_family = "wasm")]
2
3pub use mina_node_common::*;
4
5mod rayon;
6pub use rayon::init_rayon;
7
8mod node;
9pub use node::{Node, NodeBuilder};
10
11use ::node::{
12 account::AccountSecretKey,
13 core::{log, thread},
14 snark::{BlockVerifier, TransactionVerifier},
15 transition_frontier::genesis::GenesisConfig,
16};
17use anyhow::Context;
18use gloo_utils::format::JsValueSerdeExt;
19use ledger::proofs::provers::BlockProver;
20use mina_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::DEBUG);
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]
124pub async fn run(
125 block_producer: JsValue,
126 seed_nodes_urls: Option<Vec<String>>,
127 seed_nodes_addresses: Option<Vec<String>>,
128 genesis_config_url: Option<String>,
129) -> RpcSender {
130 let block_producer = parse_bp_key(block_producer);
131
132 let (rpc_sender_tx, rpc_sender_rx) = ::node::core::channels::oneshot::channel();
133 let _ = thread::spawn(move || {
134 wasm_bindgen_futures::spawn_local(async move {
135 let mut node = setup_node(
136 block_producer,
137 seed_nodes_urls,
138 seed_nodes_addresses,
139 genesis_config_url,
140 )
141 .await;
142 let _ = rpc_sender_tx.send(node.rpc());
143 node.run_forever().await;
144 });
145
146 keep_worker_alive_cursed_hack();
147 });
148
149 rpc_sender_rx.await.unwrap()
150}
151
152async fn setup_node(
153 block_producer: Option<AccountSecretKey>,
154 seed_nodes_urls: Option<Vec<String>>,
155 seed_nodes_addresses: Option<Vec<String>>,
156 genesis_config_url: Option<String>,
157) -> mina_node_common::Node<NodeService> {
158 let block_verifier_index = BlockVerifier::make().await;
159 let work_verifier_index = TransactionVerifier::make().await;
160
161 let genesis_config = if let Some(genesis_config_url) = genesis_config_url {
162 let bytes = ::node::core::http::get_bytes(&genesis_config_url)
163 .await
164 .expect("failed to fetch genesis config");
165 GenesisConfig::Prebuilt(bytes.into()).into()
166 } else {
167 ::node::config::DEVNET_CONFIG.clone()
168 };
169
170 let mut node_builder: NodeBuilder = NodeBuilder::new(None, genesis_config);
171 node_builder
172 .block_verifier_index(block_verifier_index.clone())
173 .work_verifier_index(work_verifier_index.clone());
174
175 let mut all_raw_peers = seed_nodes_addresses.unwrap_or_default();
177
178 if let Some(seed_nodes_urls) = seed_nodes_urls {
179 for seed_nodes_url in seed_nodes_urls {
180 let peers = ::node::core::http::get_bytes(&seed_nodes_url).await;
181 match peers {
182 Ok(s) => {
183 log::info!("Successfully fetched peers from {seed_nodes_url}");
184 all_raw_peers.extend(String::from_utf8_lossy(&s).split("\n").map(String::from));
185 }
186 Err(e) => {
187 log::error!("Failed to fetch peers from {seed_nodes_url}: {e}");
188 }
189 }
190 }
191 }
192
193 node_builder.initial_peers(
194 all_raw_peers
195 .iter()
196 .map(|s| s.trim())
197 .filter(|s| !s.is_empty())
198 .flat_map(|s| s.parse().ok())
199 .inspect(|p| log::debug!("Using peer: {p:?}")),
200 );
201
202 if let Some(bp_key) = block_producer {
203 thread::spawn(move || {
204 BlockProver::make(Some(block_verifier_index), Some(work_verifier_index));
205 });
206 node_builder.block_producer(bp_key, None);
207 }
208
209 node_builder
210 .p2p_custom_task_spawner(P2pTaskRemoteSpawner {})
211 .unwrap();
212 node_builder.gather_stats();
213 node_builder.build().context("node build failed!").unwrap()
214}
215
216fn keep_worker_alive_cursed_hack() {
217 wasm_bindgen::throw_str("Cursed hack to keep workers alive. See https://github.com/rustwasm/wasm-bindgen/issues/2945");
218}