saito_wasm/
saitowasm.rs

1use std::io::{Error, ErrorKind};
2use std::ops::{Deref, DerefMut};
3use std::str::FromStr;
4use std::sync::Arc;
5use std::time::Duration;
6
7use crate::wasm_balance_snapshot::WasmBalanceSnapshot;
8use crate::wasm_block::WasmBlock;
9use crate::wasm_blockchain::WasmBlockchain;
10use crate::wasm_configuration::WasmConfiguration;
11use crate::wasm_io_handler::WasmIoHandler;
12use crate::wasm_nft::WasmNFT;
13use crate::wasm_peer::WasmPeer;
14use crate::wasm_slip::WasmSlip;
15use crate::wasm_time_keeper::WasmTimeKeeper;
16use crate::wasm_transaction::WasmTransaction;
17use crate::wasm_wallet::WasmWallet;
18use js_sys::{Array, BigInt, JsString, Uint8Array};
19use lazy_static::lazy_static;
20use log::{debug, error, info, trace, warn, Level, Log, Metadata, Record};
21use saito_core::core::consensus::blockchain::Blockchain;
22use saito_core::core::consensus::blockchain_sync_state::BlockchainSyncState;
23use saito_core::core::consensus::context::Context;
24use saito_core::core::consensus::mempool::Mempool;
25use saito_core::core::consensus::peers::peer_collection::PeerCollection;
26use saito_core::core::consensus::transaction::{Transaction, TransactionType};
27use saito_core::core::consensus::wallet::{Wallet, NFT};
28use saito_core::core::consensus_thread::{ConsensusEvent, ConsensusStats, ConsensusThread};
29use saito_core::core::defs::{
30    BlockId, Currency, PeerIndex, PrintForLog, SaitoPrivateKey, SaitoPublicKey, StatVariable,
31    Timestamp, CHANNEL_SAFE_BUFFER, STAT_BIN_COUNT,
32};
33use saito_core::core::io::network::{Network, PeerDisconnectType};
34use saito_core::core::io::network_event::NetworkEvent;
35use saito_core::core::io::storage::Storage;
36use saito_core::core::mining_thread::{MiningEvent, MiningThread};
37use saito_core::core::msg::api_message::ApiMessage;
38use saito_core::core::msg::message::Message;
39use saito_core::core::process::keep_time::Timer;
40use saito_core::core::process::process_event::ProcessEvent;
41use saito_core::core::process::version::Version;
42use saito_core::core::routing_thread::{RoutingEvent, RoutingStats, RoutingThread};
43use saito_core::core::stat_thread::StatThread;
44use saito_core::core::util::configuration::Configuration;
45use saito_core::core::util::crypto::{generate_keypair_from_private_key, sign};
46use saito_core::core::verification_thread::{VerificationThread, VerifyRequest};
47use secp256k1::SECP256K1;
48use std::convert::TryInto;
49use tokio::sync::mpsc::Receiver;
50use tokio::sync::{Mutex, RwLock};
51use wasm_bindgen::prelude::*;
52use web_sys::console;
53
54#[wasm_bindgen]
55pub struct SaitoWasm {
56    pub(crate) routing_thread: RoutingThread,
57    consensus_thread: ConsensusThread,
58    mining_thread: MiningThread,
59    verification_thread: VerificationThread,
60    stat_thread: StatThread,
61    receiver_for_router: Receiver<RoutingEvent>,
62    receiver_for_consensus: Receiver<ConsensusEvent>,
63    receiver_for_miner: Receiver<MiningEvent>,
64    receiver_for_verification: Receiver<VerifyRequest>,
65    receiver_for_stats: Receiver<String>,
66    pub(crate) context: Context,
67    wallet: WasmWallet,
68    blockchain: WasmBlockchain,
69}
70
71lazy_static! {
72    pub static ref SAITO: Mutex<Option<SaitoWasm>> =
73        Mutex::new(Some(new(1, true, 100_000, 0, 60, false)));
74    static ref CONFIGS: Arc<RwLock<dyn Configuration + Send + Sync>> =
75        Arc::new(RwLock::new(WasmConfiguration::new()));
76    static ref PRIVATE_KEY: Mutex<String> = Mutex::new("".to_string());
77}
78
79pub fn new(
80    haste_multiplier: u64,
81    enable_stats: bool,
82    genesis_period: BlockId,
83    social_stake: Currency,
84    social_stake_period: BlockId,
85    delete_old_blocks: bool,
86) -> SaitoWasm {
87    info!("creating new saito wasm instance");
88    console_error_panic_hook::set_once();
89
90    let wallet = Arc::new(RwLock::new(Wallet::new([0; 32], [0; 33])));
91
92    let configuration: Arc<RwLock<dyn Configuration + Send + Sync>> = CONFIGS.clone();
93
94    let channel_size = 1_000_000;
95
96    if channel_size < CHANNEL_SAFE_BUFFER * 2 {
97        error!(
98            "channel_size < CHANNEL_SAFE_BUFFER x 2 : {:?}",
99            CHANNEL_SAFE_BUFFER * 2
100        );
101        panic!("cannot continue");
102    }
103
104    let peers = Arc::new(RwLock::new(PeerCollection::default()));
105    let context = Context {
106        blockchain_lock: Arc::new(RwLock::new(Blockchain::new(
107            wallet.clone(),
108            genesis_period,
109            social_stake,
110            social_stake_period,
111        ))),
112        mempool_lock: Arc::new(RwLock::new(Mempool::new(wallet.clone()))),
113        wallet_lock: wallet.clone(),
114        config_lock: configuration.clone(),
115    };
116
117    let (sender_to_consensus, receiver_in_mempool) = tokio::sync::mpsc::channel(channel_size);
118    let (sender_to_blockchain, receiver_in_blockchain) = tokio::sync::mpsc::channel(channel_size);
119    let (sender_to_miner, receiver_in_miner) = tokio::sync::mpsc::channel(channel_size);
120    let (sender_to_stat, receiver_in_stats) = tokio::sync::mpsc::channel(channel_size);
121    let (sender_to_verification, receiver_in_verification) =
122        tokio::sync::mpsc::channel(channel_size);
123
124    let timer = Timer {
125        time_reader: Arc::new(WasmTimeKeeper {}),
126        hasten_multiplier: haste_multiplier,
127        start_time: js_sys::Date::now() as Timestamp,
128    };
129
130    SaitoWasm {
131        routing_thread: RoutingThread {
132            blockchain_lock: context.blockchain_lock.clone(),
133            mempool_lock: context.mempool_lock.clone(),
134            sender_to_consensus: sender_to_consensus.clone(),
135            sender_to_miner: sender_to_miner.clone(),
136            config_lock: context.config_lock.clone(),
137            timer: timer.clone(),
138            wallet_lock: wallet.clone(),
139            network: Network::new(
140                Box::new(WasmIoHandler {}),
141                peers.clone(),
142                context.wallet_lock.clone(),
143                context.config_lock.clone(),
144                timer.clone(),
145            ),
146            storage: Storage::new(Box::new(WasmIoHandler {})),
147            reconnection_timer: 0,
148            peer_removal_timer: 0,
149            peer_file_write_timer: 0,
150            last_emitted_block_fetch_count: 0,
151            stats: RoutingStats::new(sender_to_stat.clone()),
152            senders_to_verification: vec![sender_to_verification.clone()],
153            last_verification_thread_index: 0,
154            stat_sender: sender_to_stat.clone(),
155            blockchain_sync_state: BlockchainSyncState::new(10),
156        },
157        consensus_thread: ConsensusThread {
158            mempool_lock: context.mempool_lock.clone(),
159            blockchain_lock: context.blockchain_lock.clone(),
160            wallet_lock: context.wallet_lock.clone(),
161            generate_genesis_block: false,
162            sender_to_router: sender_to_blockchain.clone(),
163            sender_to_miner: sender_to_miner.clone(),
164            block_producing_timer: 0,
165            timer: timer.clone(),
166            network: Network::new(
167                Box::new(WasmIoHandler {}),
168                peers.clone(),
169                context.wallet_lock.clone(),
170                configuration.clone(),
171                timer.clone(),
172            ),
173            storage: Storage::new(Box::new(WasmIoHandler {})),
174            stats: ConsensusStats::new(sender_to_stat.clone()),
175            txs_for_mempool: vec![],
176            stat_sender: sender_to_stat.clone(),
177            config_lock: configuration.clone(),
178            produce_blocks_by_timer: true,
179            delete_old_blocks,
180        },
181        mining_thread: MiningThread {
182            wallet_lock: context.wallet_lock.clone(),
183            sender_to_mempool: sender_to_consensus.clone(),
184            timer: timer.clone(),
185            miner_active: false,
186            target: [0; 32],
187            target_id: 0,
188            difficulty: 0,
189            public_key: [0; 33],
190            mined_golden_tickets: 0,
191            stat_sender: sender_to_stat.clone(),
192            config_lock: configuration.clone(),
193            enabled: true,
194            mining_iterations: 1_000,
195            mining_start: 0,
196        },
197        verification_thread: VerificationThread {
198            sender_to_consensus: sender_to_consensus.clone(),
199            blockchain_lock: context.blockchain_lock.clone(),
200            peer_lock: peers.clone(),
201            wallet_lock: wallet.clone(),
202            processed_txs: StatVariable::new(
203                "verification::processed_txs".to_string(),
204                STAT_BIN_COUNT,
205                sender_to_stat.clone(),
206            ),
207            processed_blocks: StatVariable::new(
208                "verification::processed_blocks".to_string(),
209                STAT_BIN_COUNT,
210                sender_to_stat.clone(),
211            ),
212            processed_msgs: StatVariable::new(
213                "verification::processed_msgs".to_string(),
214                STAT_BIN_COUNT,
215                sender_to_stat.clone(),
216            ),
217            invalid_txs: StatVariable::new(
218                "verification::invalid_txs".to_string(),
219                STAT_BIN_COUNT,
220                sender_to_stat.clone(),
221            ),
222            stat_sender: sender_to_stat.clone(),
223        },
224        stat_thread: StatThread {
225            stat_queue: Default::default(),
226            io_interface: Box::new(WasmIoHandler {}),
227            enabled: enable_stats,
228        },
229        receiver_for_router: receiver_in_blockchain,
230        receiver_for_consensus: receiver_in_mempool,
231        receiver_for_miner: receiver_in_miner,
232        receiver_for_verification: receiver_in_verification,
233        wallet: WasmWallet::new_from(
234            context.wallet_lock.clone(),
235            Network::new(
236                Box::new(WasmIoHandler {}),
237                peers.clone(),
238                context.wallet_lock.clone(),
239                configuration.clone(),
240                timer.clone(),
241            ),
242        ),
243        blockchain: WasmBlockchain {
244            blockchain_lock: context.blockchain_lock.clone(),
245        },
246        context,
247        receiver_for_stats: receiver_in_stats,
248    }
249}
250
251struct WasmLogger {}
252
253impl Log for WasmLogger {
254    fn enabled(&self, metadata: &Metadata) -> bool {
255        metadata.level() <= log::max_level()
256    }
257
258    fn log(&self, record: &Record) {
259        if !self.enabled(record.metadata()) {
260            return;
261        }
262        log(record)
263    }
264
265    fn flush(&self) {}
266}
267pub(crate) struct Style<'s> {
268    pub trace: &'s str,
269    pub debug: &'s str,
270    pub info: &'s str,
271    pub warn: &'s str,
272    pub error: &'s str,
273    pub file_line: &'s str,
274    pub text: &'s str,
275}
276
277impl Style<'static> {
278    /// Returns default style values.
279    pub const fn default() -> Self {
280        macro_rules! bg_color {
281            ($color:expr) => {
282                concat!("color: white; padding: 0 3px; background: ", $color, ";")
283            };
284        }
285
286        Style {
287            trace: bg_color!("gray"),
288            debug: bg_color!("blue"),
289            info: bg_color!("green"),
290            warn: bg_color!("orange"),
291            error: bg_color!("darkred"),
292            file_line: "font-weight: bold; color: inherit",
293            text: "background: inherit; color: inherit",
294        }
295    }
296}
297const STYLE: Style<'static> = Style::default();
298
299pub fn log(record: &Record) {
300    let console_log = match record.level() {
301        Level::Error => console::error_4,
302        Level::Warn => console::warn_4,
303        Level::Info => console::info_4,
304        Level::Debug => console::log_4,
305        Level::Trace => console::debug_4,
306    };
307
308    let message = {
309        let message = format!(
310            "%c%c%c{text}",
311            // level = record.level(),
312            // file = record.file().unwrap_or_else(|| record.target()),
313            // line = record
314            //     .line()
315            //     .map_or_else(|| "[Unknown]".to_string(), |line| line.to_string()),
316            text = record.args(),
317        );
318        JsValue::from(&message)
319    };
320
321    let level_style = {
322        let style_str = match record.level() {
323            Level::Trace => STYLE.trace,
324            Level::Debug => STYLE.debug,
325            Level::Info => STYLE.info,
326            Level::Warn => STYLE.warn,
327            Level::Error => STYLE.error,
328        };
329
330        JsValue::from(style_str)
331    };
332
333    let file_line_style = JsValue::from_str(STYLE.file_line);
334    let text_style = JsValue::from_str(STYLE.text);
335    console_log(&message, &level_style, &file_line_style, &text_style);
336}
337
338#[wasm_bindgen]
339pub async fn initialize(
340    json: JsString,
341    private_key: JsString,
342    log_level_num: u8,
343    hasten_multiplier: u64,
344    delete_old_blocks: bool,
345) -> Result<JsValue, JsValue> {
346    // TODO : move these parameters to a config object to clean the interface
347
348    let log_level = match log_level_num {
349        0 => log::Level::Error,
350        1 => log::Level::Warn,
351        2 => log::Level::Info,
352        3 => log::Level::Debug,
353        4 => log::Level::Trace,
354        _ => log::Level::Info,
355    };
356
357    log::set_logger(&WasmLogger {}).unwrap();
358    log::set_max_level(log_level.to_level_filter());
359
360    // console_log::init_with_level(log_level).unwrap();
361
362    trace!("trace test");
363    debug!("debug test");
364    info!("initializing saito-wasm");
365
366    let mut enable_stats = true;
367    let mut genesis_period = 100_000;
368    let mut social_stake = 0;
369    let mut social_stake_period = 60;
370    {
371        info!("setting configs...");
372        let mut configs = CONFIGS.write().await;
373        info!("config lock acquired");
374
375        let str: String = json.into();
376        let config = WasmConfiguration::new_from_json(str.as_str());
377
378        if config.is_err() {
379            error!("failed parsing configs. {:?}", config.err().unwrap());
380        } else {
381            let config = config.unwrap();
382            if config.is_browser() {
383                enable_stats = false;
384            }
385            info!("config : {:?}", config);
386            configs.replace(&config);
387            genesis_period = configs.get_consensus_config().unwrap().genesis_period;
388            social_stake = configs.get_consensus_config().unwrap().default_social_stake;
389            social_stake_period = configs
390                .get_consensus_config()
391                .unwrap()
392                .default_social_stake_period;
393        }
394    }
395
396    let mut saito = SAITO.lock().await;
397
398    info!("genesis_period = {:?}", genesis_period);
399    info!("social_stake = {:?}", social_stake);
400    saito.replace(new(
401        hasten_multiplier,
402        enable_stats,
403        genesis_period,
404        social_stake,
405        social_stake_period,
406        delete_old_blocks,
407    ));
408
409    let private_key: SaitoPrivateKey = string_to_hex(private_key).or(Err(JsValue::from(
410        "Failed parsing private key string to key",
411    )))?;
412    {
413        let mut wallet = saito.as_ref().unwrap().context.wallet_lock.write().await;
414        if private_key != [0; 32] {
415            let keys = generate_keypair_from_private_key(private_key.as_slice());
416            wallet.private_key = keys.1;
417            wallet.public_key = keys.0;
418        }
419        info!("current core version : {:?}", wallet.core_version);
420    }
421
422    saito.as_mut().unwrap().stat_thread.on_init().await;
423    saito.as_mut().unwrap().mining_thread.on_init().await;
424    saito.as_mut().unwrap().verification_thread.on_init().await;
425    saito.as_mut().unwrap().routing_thread.on_init().await;
426    saito.as_mut().unwrap().consensus_thread.on_init().await;
427
428    Ok(JsValue::from("initialized"))
429}
430
431#[wasm_bindgen]
432pub async fn create_transaction(
433    public_key: JsString,
434    amount: u64,
435    fee: u64,
436    force_merge: bool,
437) -> Result<WasmTransaction, JsValue> {
438    trace!("create_transaction : {:?}", public_key.to_string());
439    let saito = SAITO.lock().await;
440    let mut wallet = saito.as_ref().unwrap().context.wallet_lock.write().await;
441    let key = string_to_key(public_key).or(Err(JsValue::from(
442        "Failed parsing public key string to key",
443    )))?;
444
445    let config_lock = saito.as_ref().unwrap().routing_thread.config_lock.clone();
446    let configs = config_lock.read().await;
447    let genesis_period = configs.get_consensus_config().unwrap().genesis_period;
448    let blockchain = saito.as_ref().unwrap().context.blockchain_lock.read().await;
449    let latest_block_id = blockchain.get_latest_block_id();
450
451    let transaction = Transaction::create(
452        &mut wallet,
453        key,
454        amount,
455        fee,
456        force_merge,
457        Some(&saito.as_ref().unwrap().consensus_thread.network),
458        latest_block_id,
459        genesis_period,
460    );
461    if transaction.is_err() {
462        error!(
463            "failed creating transaction. {:?}",
464            transaction.err().unwrap()
465        );
466        return Err(JsValue::from("Failed creating transaction"));
467    }
468    let transaction = transaction.unwrap();
469    let wasm_transaction = WasmTransaction::from_transaction(transaction);
470    Ok(wasm_transaction)
471}
472
473#[wasm_bindgen]
474pub async fn create_transaction_with_multiple_payments(
475    public_keys: js_sys::Array,
476    amounts: js_sys::BigUint64Array,
477    fee: u64,
478    _force_merge: bool,
479) -> Result<WasmTransaction, JsValue> {
480    let saito = SAITO.lock().await;
481    let mut wallet = saito.as_ref().unwrap().context.wallet_lock.write().await;
482
483    let config_lock = saito.as_ref().unwrap().routing_thread.config_lock.clone();
484    let configs = config_lock.read().await;
485    let genesis_period = configs.get_consensus_config().unwrap().genesis_period;
486    let blockchain = saito.as_ref().unwrap().context.blockchain_lock.read().await;
487    let latest_block_id = blockchain.get_latest_block_id();
488
489    let keys: Vec<SaitoPublicKey> = string_array_to_base58_keys(public_keys);
490    let amounts: Vec<Currency> = amounts.to_vec();
491
492    if keys.len() != amounts.len() {
493        return Err(JsValue::from("keys and payments have different counts"));
494    }
495
496    let transaction = Transaction::create_with_multiple_payments(
497        &mut wallet,
498        keys,
499        amounts,
500        fee,
501        Some(&saito.as_ref().unwrap().consensus_thread.network),
502        latest_block_id,
503        genesis_period,
504    );
505    if transaction.is_err() {
506        error!(
507            "failed creating transaction. {:?}",
508            transaction.err().unwrap()
509        );
510        return Err(JsValue::from("Failed creating transaction"));
511    }
512    let transaction = transaction.unwrap();
513    let wasm_transaction = WasmTransaction::from_transaction(transaction);
514    Ok(wasm_transaction)
515}
516
517#[wasm_bindgen]
518pub async fn create_bound_transaction(
519    amt: u64,
520    bid: u64,
521    tid: u64,
522    sid: u64,
523    num: u32,
524    deposit: u64,
525    change: u64,
526    data: String,
527    fee: u64,
528    recipient_public_key: JsString,
529    nft_type: JsString,
530) -> Result<WasmTransaction, JsValue> {
531    let saito = SAITO.lock().await;
532    let config_lock = saito.as_ref().unwrap().routing_thread.config_lock.clone();
533    let configs = config_lock.read().await;
534    let genesis_period = configs.get_consensus_config().unwrap().genesis_period;
535    let blockchain = saito.as_ref().unwrap().context.blockchain_lock.read().await;
536    let latest_block_id = blockchain.get_latest_block_id();
537    let mut wallet = saito.as_ref().unwrap().context.wallet_lock.write().await;
538
539    info!("Received in saitowasm.rs:");
540    info!("Amount: {}", amt);
541    info!("Bid: {}", bid);
542    info!("Tid: {}", tid);
543    info!("Sid: {}", sid);
544    info!("Num: {}", num);
545    info!("Deposit: {}", deposit);
546    info!("Change: {}", change);
547    info!("Image data JSON: {}", data);
548    info!("fee: {}", fee);
549    info!("recipient_public_key: {}", recipient_public_key);
550
551    // Convert the `data` string into a JSON object
552    let serialized_data = match serde_json::to_vec(&data) {
553        Ok(vec) => vec,
554        Err(e) => {
555            error!("Failed to serialize data: {}", e);
556            return Err(JsValue::from_str("Failed to serialize data"));
557        }
558    };
559
560    // Convert Vec<u8> into Vec<u32>
561    let serialized_data_u32: Vec<u32> = serialized_data
562        .chunks(4)
563        .map(|chunk| {
564            let mut bytes = [0u8; 4];
565            for (i, &byte) in chunk.iter().enumerate() {
566                bytes[i] = byte;
567            }
568            u32::from_le_bytes(bytes)
569        })
570        .collect();
571
572    let key = string_to_key(recipient_public_key).or(Err(JsValue::from(
573        "Failed parsing public key string to key",
574    )))?;
575
576    let transaction = wallet
577        .create_bound_transaction(
578            amt,
579            bid,
580            tid,
581            sid,
582            deposit,
583            serialized_data_u32,
584            &key,
585            Some(&saito.as_ref().unwrap().consensus_thread.network),
586            latest_block_id,
587            genesis_period,
588            nft_type.as_string().unwrap(),
589        )
590        .await;
591
592    if transaction.is_err() {
593        error!(
594            "failed creating transaction. {:?}",
595            transaction.err().unwrap()
596        );
597        return Err(JsValue::from("Failed creating transaction"));
598    }
599
600    let transaction = transaction.unwrap();
601
602    info!("wasm transaction: {:}", transaction);
603    let wasm_transaction = WasmTransaction::from_transaction(transaction);
604
605    Ok(wasm_transaction)
606}
607
608#[wasm_bindgen]
609pub async fn create_send_bound_transaction(
610    amt: u64,
611    nft_id: String,
612    data: String,
613    recipient_public_key: JsString,
614) -> Result<WasmTransaction, JsValue> {
615    let saito = SAITO.lock().await;
616    let mut wallet = saito.as_ref().unwrap().context.wallet_lock.write().await;
617
618    // Decode the NFT id hex string into a Vec<u8>
619    let nft_id_vec = hex::decode(&nft_id)
620        .map_err(|_| JsValue::from_str("Failed to decode nft_id hex string"))?;
621
622    let serialized_data =
623        serde_json::to_vec(&data).map_err(|_| JsValue::from_str("Failed to serialize data"))?;
624    let serialized_data_u32: Vec<u32> = serialized_data
625        .chunks(4)
626        .map(|chunk| {
627            let mut bytes = [0u8; 4];
628            for (i, &b) in chunk.iter().enumerate() {
629                bytes[i] = b;
630            }
631            u32::from_le_bytes(bytes)
632        })
633        .collect();
634
635    let key = string_to_key(recipient_public_key).or(Err(JsValue::from_str(
636        "Failed parsing public key string to key",
637    )))?;
638
639    let tx = wallet
640        .create_send_bound_transaction(amt, nft_id_vec, serialized_data_u32, &key)
641        .await
642        .map_err(|_| JsValue::from_str("Failed to transfer NFT transaction"))?;
643
644    let wasm_transaction = WasmTransaction::from_transaction(tx);
645    Ok(wasm_transaction)
646}
647
648#[wasm_bindgen]
649pub async fn get_nft_list() -> Result<Array, JsValue> {
650    let saito = SAITO.lock().await;
651    let wallet = saito.as_ref().unwrap().context.wallet_lock.read().await;
652
653    let nft_array = Array::new();
654    for nft in wallet.get_nft_list().iter() {
655        nft_array.push(&WasmNFT::from_nft(nft.clone()).into());
656    }
657
658    Ok(nft_array)
659}
660
661#[wasm_bindgen]
662pub async fn get_latest_block_hash() -> JsString {
663    debug!("get_latest_block_hash");
664    let saito = SAITO.lock().await;
665    let blockchain = saito.as_ref().unwrap().context.blockchain_lock.read().await;
666    let hash = blockchain.get_latest_block_hash();
667
668    hash.to_hex().into()
669}
670
671#[wasm_bindgen]
672pub async fn get_block(block_hash: JsString) -> Result<WasmBlock, JsValue> {
673    let block_hash = string_to_hex(block_hash).or(Err(JsValue::from(
674        "Failed parsing block hash string to key",
675    )))?;
676
677    let saito = SAITO.lock().await;
678    let blockchain = saito
679        .as_ref()
680        .unwrap()
681        .routing_thread
682        .blockchain_lock
683        .read()
684        .await;
685
686    let result = blockchain.get_block(&block_hash);
687
688    if result.is_none() {
689        warn!("block {:?} not found", block_hash.to_hex());
690        return Err(JsValue::from("block not found"));
691    }
692    let block = result.cloned().unwrap();
693
694    Ok(WasmBlock::from_block(block))
695}
696
697#[wasm_bindgen]
698pub async fn process_new_peer(peer_index: PeerIndex, ip: JsString) {
699    debug!("process_new_peer : {:?} - {:?}", peer_index, ip);
700    let mut saito = SAITO.lock().await;
701    let s = ip.as_string();
702    if s.is_none() {
703        debug!("cannot parse ip string : {:?}", ip);
704        return;
705    }
706    let ip = s;
707
708    saito
709        .as_mut()
710        .unwrap()
711        .routing_thread
712        .process_network_event(NetworkEvent::PeerConnectionResult {
713            result: Ok((peer_index, ip)),
714        })
715        .await;
716}
717
718#[wasm_bindgen]
719pub async fn process_stun_peer(peer_index: PeerIndex, public_key: JsString) -> Result<(), JsValue> {
720    debug!(
721        "processing stun peer with index: {:?} and public key: {:?} ",
722        peer_index, public_key
723    );
724    let mut saito = SAITO.lock().await;
725    let key: SaitoPublicKey = string_to_key(public_key.into())
726        .map_err(|e| JsValue::from_str(&format!("Failed to parse public key: {}", e)))?;
727
728    saito
729        .as_mut()
730        .unwrap()
731        .routing_thread
732        .process_network_event(NetworkEvent::AddStunPeer {
733            peer_index,
734            public_key: key,
735        })
736        .await;
737    Ok(())
738}
739
740#[wasm_bindgen]
741pub async fn remove_stun_peer(peer_index: PeerIndex) {
742    debug!(
743        "removing stun peer with index: {:?} from netowrk ",
744        peer_index
745    );
746    let mut saito = SAITO.lock().await;
747    saito
748        .as_mut()
749        .unwrap()
750        .routing_thread
751        .process_network_event(NetworkEvent::RemoveStunPeer { peer_index })
752        .await;
753}
754
755#[wasm_bindgen]
756pub async fn get_next_peer_index() -> BigInt {
757    let mut saito = SAITO.lock().await;
758    let mut peers = saito
759        .as_mut()
760        .unwrap()
761        .routing_thread
762        .network
763        .peer_lock
764        .write()
765        .await;
766
767    BigInt::from(peers.peer_counter.get_next_index())
768}
769
770#[wasm_bindgen]
771pub async fn process_peer_disconnection(peer_index: u64) {
772    debug!("process_peer_disconnection : {:?}", peer_index);
773    let mut saito = SAITO.lock().await;
774    saito
775        .as_mut()
776        .unwrap()
777        .routing_thread
778        .process_network_event(NetworkEvent::PeerDisconnected {
779            peer_index,
780            disconnect_type: PeerDisconnectType::ExternalDisconnect,
781        })
782        .await;
783}
784
785#[wasm_bindgen]
786pub async fn process_msg_buffer_from_peer(buffer: js_sys::Uint8Array, peer_index: u64) {
787    let mut saito = SAITO.lock().await;
788    let buffer = buffer.to_vec();
789
790    saito
791        .as_mut()
792        .unwrap()
793        .routing_thread
794        .process_network_event(NetworkEvent::IncomingNetworkMessage { peer_index, buffer })
795        .await;
796}
797
798#[wasm_bindgen]
799pub async fn process_fetched_block(
800    buffer: js_sys::Uint8Array,
801    hash: js_sys::Uint8Array,
802    block_id: BlockId,
803    peer_index: PeerIndex,
804) {
805    let mut saito = SAITO.lock().await;
806    saito
807        .as_mut()
808        .unwrap()
809        .routing_thread
810        .process_network_event(NetworkEvent::BlockFetched {
811            block_hash: hash.to_vec().try_into().unwrap(),
812            block_id,
813            peer_index,
814            buffer: buffer.to_vec(),
815        })
816        .await;
817}
818
819#[wasm_bindgen]
820pub async fn process_failed_block_fetch(hash: js_sys::Uint8Array, block_id: u64, peer_index: u64) {
821    let mut saito = SAITO.lock().await;
822    saito
823        .as_mut()
824        .unwrap()
825        .routing_thread
826        .process_network_event(NetworkEvent::BlockFetchFailed {
827            block_hash: hash.to_vec().try_into().unwrap(),
828            peer_index,
829            block_id,
830        })
831        .await;
832}
833
834#[wasm_bindgen]
835pub async fn process_timer_event(duration_in_ms: u64) {
836    let mut saito = SAITO.lock().await;
837    let saito = saito.as_mut().unwrap();
838
839    let duration = Duration::from_millis(duration_in_ms);
840    const EVENT_LIMIT: u32 = 100;
841    let mut event_counter = 0;
842
843    while let Ok(event) = saito.receiver_for_router.try_recv() {
844        let _result = saito.routing_thread.process_event(event).await;
845        event_counter += 1;
846        if event_counter >= EVENT_LIMIT {
847            break;
848        }
849        if !saito.routing_thread.is_ready_to_process() {
850            break;
851        }
852    }
853
854    saito.routing_thread.process_timer_event(duration).await;
855
856    event_counter = 0;
857    while let Ok(event) = saito.receiver_for_consensus.try_recv() {
858        let _result = saito.consensus_thread.process_event(event).await;
859        event_counter += 1;
860        if event_counter >= EVENT_LIMIT {
861            break;
862        }
863        if !saito.consensus_thread.is_ready_to_process() {
864            break;
865        }
866    }
867
868    saito.consensus_thread.process_timer_event(duration).await;
869
870    event_counter = 0;
871    while let Ok(event) = saito.receiver_for_verification.try_recv() {
872        let _result = saito.verification_thread.process_event(event).await;
873        event_counter += 1;
874        if event_counter >= EVENT_LIMIT {
875            break;
876        }
877        if !saito.verification_thread.is_ready_to_process() {
878            break;
879        }
880    }
881
882    saito
883        .verification_thread
884        .process_timer_event(duration)
885        .await;
886
887    event_counter = 0;
888    while let Ok(event) = saito.receiver_for_miner.try_recv() {
889        let _result = saito.mining_thread.process_event(event).await;
890        event_counter += 1;
891        if event_counter >= EVENT_LIMIT {
892            break;
893        }
894        if !saito.mining_thread.is_ready_to_process() {
895            break;
896        }
897    }
898
899    saito.mining_thread.process_timer_event(duration).await;
900
901    saito.stat_thread.process_timer_event(duration).await;
902
903    event_counter = 0;
904    while let Ok(event) = saito.receiver_for_stats.try_recv() {
905        let _result = saito.stat_thread.process_event(event).await;
906        event_counter += 1;
907        if event_counter >= EVENT_LIMIT {
908            break;
909        }
910        if !saito.stat_thread.is_ready_to_process() {
911            break;
912        }
913    }
914}
915
916#[wasm_bindgen]
917pub async fn process_stat_interval(current_time: Timestamp) {
918    let mut saito = SAITO.lock().await;
919
920    saito
921        .as_mut()
922        .unwrap()
923        .routing_thread
924        .on_stat_interval(current_time)
925        .await;
926
927    saito
928        .as_mut()
929        .unwrap()
930        .consensus_thread
931        .on_stat_interval(current_time)
932        .await;
933
934    saito
935        .as_mut()
936        .unwrap()
937        .verification_thread
938        .on_stat_interval(current_time)
939        .await;
940
941    saito
942        .as_mut()
943        .unwrap()
944        .mining_thread
945        .on_stat_interval(current_time)
946        .await;
947}
948
949#[wasm_bindgen]
950pub fn hash(buffer: Uint8Array) -> JsString {
951    let buffer: Vec<u8> = buffer.to_vec();
952    let hash = saito_core::core::util::crypto::hash(&buffer);
953    let str = hash.to_hex();
954    let str: js_sys::JsString = str.into();
955    str
956}
957
958#[wasm_bindgen]
959pub fn sign_buffer(buffer: Uint8Array, private_key: JsString) -> Result<JsString, JsValue> {
960    let buffer = buffer.to_vec();
961    let key = string_to_hex(private_key).or(Err(JsValue::from(
962        "Failed parsing private key string to key",
963    )))?;
964    let result = sign(&buffer, &key);
965
966    let signature = result.to_hex();
967    Ok(signature.into())
968}
969
970#[wasm_bindgen]
971pub fn verify_signature(buffer: Uint8Array, signature: JsString, public_key: JsString) -> bool {
972    let sig = string_to_hex(signature);
973    if sig.is_err() {
974        error!("signature is invalid");
975        return false;
976    }
977    let sig = sig.unwrap();
978    let key = string_to_key(public_key);
979    if key.is_err() {
980        error!(
981            "failed parsing public key from string. {:?}",
982            key.err().unwrap()
983        );
984        return false;
985    }
986    let buffer = buffer.to_vec();
987    let h = saito_core::core::util::crypto::hash(&buffer);
988    saito_core::core::util::crypto::verify_signature(&h, &sig, &key.unwrap())
989}
990
991#[wasm_bindgen]
992pub async fn get_peers() -> Array {
993    let saito = SAITO.lock().await;
994    let peers = saito
995        .as_ref()
996        .unwrap()
997        .routing_thread
998        .network
999        .peer_lock
1000        .read()
1001        .await;
1002    let valid_peer_count = peers
1003        .index_to_peers
1004        .iter()
1005        .filter(|(_index, peer)| peer.get_public_key().is_some())
1006        .count();
1007    let array = Array::new_with_length(valid_peer_count as u32);
1008    let mut array_index = 0;
1009    for (_i, (_peer_index, peer)) in peers.index_to_peers.iter().enumerate() {
1010        if peer.get_public_key().is_none() {
1011            continue;
1012        }
1013        let peer = peer.clone();
1014        array.set(
1015            array_index as u32,
1016            JsValue::from(WasmPeer::new_from_peer(peer)),
1017        );
1018        array_index += 1;
1019    }
1020    array
1021}
1022
1023#[wasm_bindgen]
1024pub async fn get_peer(peer_index: u64) -> Option<WasmPeer> {
1025    let saito = SAITO.lock().await;
1026    let peers = saito
1027        .as_ref()
1028        .unwrap()
1029        .routing_thread
1030        .network
1031        .peer_lock
1032        .read()
1033        .await;
1034    let peer = peers.find_peer_by_index(peer_index);
1035    if peer.is_none() || peer.unwrap().get_public_key().is_none() {
1036        warn!("peer not found");
1037        return None;
1038    }
1039    let peer = peer.cloned().unwrap();
1040    Some(WasmPeer::new_from_peer(peer))
1041}
1042
1043#[wasm_bindgen]
1044pub async fn get_account_slips(public_key: JsString) -> Result<Array, JsValue> {
1045    let saito = SAITO.lock().await;
1046    let blockchain = saito
1047        .as_ref()
1048        .unwrap()
1049        .routing_thread
1050        .blockchain_lock
1051        .read()
1052        .await;
1053    let key = string_to_key(public_key).or(Err(JsValue::from(
1054        "Failed parsing public key string to key",
1055    )))?;
1056    let mut slips = blockchain.get_slips_for(key);
1057    let array = js_sys::Array::new_with_length(slips.len() as u32);
1058
1059    for (index, slip) in slips.drain(..).enumerate() {
1060        let wasm_slip = WasmSlip::new_from_slip(slip);
1061        array.set(index as u32, JsValue::from(wasm_slip));
1062    }
1063
1064    Ok(array)
1065}
1066
1067#[wasm_bindgen]
1068pub async fn get_balance_snapshot(keys: js_sys::Array) -> WasmBalanceSnapshot {
1069    let saito = SAITO.lock().await;
1070    let config_lock = saito.as_ref().unwrap().routing_thread.config_lock.clone();
1071    let configs = config_lock.read().await;
1072    let keys: Vec<SaitoPublicKey> = string_array_to_base58_keys(keys);
1073
1074    let blockchain = saito
1075        .as_ref()
1076        .unwrap()
1077        .routing_thread
1078        .blockchain_lock
1079        .read()
1080        .await;
1081    let snapshot = blockchain.get_balance_snapshot(keys, configs.deref());
1082
1083    WasmBalanceSnapshot::new(snapshot)
1084}
1085
1086#[wasm_bindgen]
1087pub async fn update_from_balance_snapshot(snapshot: WasmBalanceSnapshot) {
1088    let saito = SAITO.lock().await;
1089    let mut wallet = saito
1090        .as_ref()
1091        .unwrap()
1092        .routing_thread
1093        .wallet_lock
1094        .write()
1095        .await;
1096    wallet.update_from_balance_snapshot(
1097        snapshot.get_snapshot(),
1098        Some(&saito.as_ref().unwrap().routing_thread.network),
1099    );
1100}
1101
1102#[wasm_bindgen]
1103pub fn generate_private_key() -> JsString {
1104    info!("generate_private_key");
1105    let (_, private_key) = generate_keys_wasm();
1106    private_key.to_hex().into()
1107}
1108
1109#[wasm_bindgen]
1110pub fn generate_public_key(private_key: JsString) -> Result<JsString, JsValue> {
1111    info!("generate_public_key");
1112    let private_key: SaitoPrivateKey = string_to_hex(private_key).or(Err(JsValue::from(
1113        "Failed parsing private key string to key",
1114    )))?;
1115    let (public_key, _) = generate_keypair_from_private_key(&private_key);
1116    Ok(public_key.to_base58().into())
1117}
1118
1119#[wasm_bindgen]
1120pub async fn propagate_transaction(tx: &WasmTransaction) {
1121    trace!("propagate_transaction");
1122
1123    let mut saito = SAITO.lock().await;
1124    let mut tx = tx.clone().tx;
1125    {
1126        let wallet = saito
1127            .as_ref()
1128            .unwrap()
1129            .routing_thread
1130            .wallet_lock
1131            .read()
1132            .await;
1133        tx.generate(&wallet.public_key, 0, 0);
1134    }
1135    saito
1136        .as_mut()
1137        .unwrap()
1138        .consensus_thread
1139        .process_event(ConsensusEvent::NewTransaction { transaction: tx })
1140        .await;
1141}
1142
1143#[wasm_bindgen]
1144pub async fn send_api_call(buffer: Uint8Array, msg_index: u32, peer_index: PeerIndex) {
1145    trace!("send_api_call : {:?}", peer_index);
1146    let saito = SAITO.lock().await;
1147    let api_message = ApiMessage {
1148        msg_index,
1149        data: buffer.to_vec(),
1150    };
1151    let message = Message::ApplicationMessage(api_message);
1152    let buffer = message.serialize();
1153    if peer_index == 0 {
1154        saito
1155            .as_ref()
1156            .unwrap()
1157            .routing_thread
1158            .network
1159            .io_interface
1160            .send_message_to_all(buffer.as_slice(), vec![])
1161            .await
1162            .unwrap();
1163    } else {
1164        saito
1165            .as_ref()
1166            .unwrap()
1167            .routing_thread
1168            .network
1169            .io_interface
1170            .send_message(peer_index, buffer.as_slice())
1171            .await
1172            .unwrap();
1173    }
1174}
1175
1176#[wasm_bindgen]
1177pub async fn send_api_success(buffer: Uint8Array, msg_index: u32, peer_index: PeerIndex) {
1178    trace!("send_api_success : {:?}", peer_index);
1179    let saito = SAITO.lock().await;
1180    let api_message = ApiMessage {
1181        msg_index,
1182        data: buffer.to_vec(),
1183    };
1184    let message = Message::Result(api_message);
1185    let buffer = message.serialize();
1186
1187    saito
1188        .as_ref()
1189        .unwrap()
1190        .routing_thread
1191        .network
1192        .io_interface
1193        .send_message(peer_index, buffer.as_slice())
1194        .await
1195        .unwrap();
1196}
1197
1198#[wasm_bindgen]
1199pub async fn send_api_error(buffer: Uint8Array, msg_index: u32, peer_index: PeerIndex) {
1200    trace!("send_api_error : {:?}", peer_index);
1201    let saito = SAITO.lock().await;
1202    let api_message = ApiMessage {
1203        msg_index,
1204        data: buffer.to_vec(),
1205    };
1206    let message = Message::Error(api_message);
1207    let buffer = message.serialize();
1208
1209    saito
1210        .as_ref()
1211        .unwrap()
1212        .routing_thread
1213        .network
1214        .io_interface
1215        .send_message(peer_index, buffer.as_slice())
1216        .await
1217        .unwrap();
1218}
1219
1220#[wasm_bindgen]
1221pub async fn get_wallet() -> WasmWallet {
1222    let saito = SAITO.lock().await;
1223    return saito.as_ref().unwrap().wallet.clone();
1224}
1225
1226#[wasm_bindgen]
1227pub async fn get_blockchain() -> WasmBlockchain {
1228    let saito = SAITO.lock().await;
1229    saito.as_ref().unwrap().blockchain.clone()
1230}
1231
1232#[wasm_bindgen]
1233pub async fn get_mempool_txs() -> js_sys::Array {
1234    let saito = SAITO.lock().await;
1235    let mempool = saito
1236        .as_ref()
1237        .unwrap()
1238        .consensus_thread
1239        .mempool_lock
1240        .read()
1241        .await;
1242    let txs = js_sys::Array::new_with_length(mempool.transactions.len() as u32);
1243    for (index, (_, tx)) in mempool.transactions.iter().enumerate() {
1244        let wasm_tx = WasmTransaction::from_transaction(tx.clone());
1245        txs.set(index as u32, JsValue::from(wasm_tx));
1246    }
1247
1248    txs
1249}
1250
1251#[wasm_bindgen]
1252pub async fn set_wallet_version(major: u8, minor: u8, patch: u16) {
1253    let saito = SAITO.lock().await;
1254    let mut wallet = saito.as_ref().unwrap().wallet.wallet.write().await;
1255    wallet.wallet_version = Version {
1256        major,
1257        minor,
1258        patch,
1259    };
1260}
1261
1262#[wasm_bindgen]
1263pub fn is_valid_public_key(key: JsString) -> bool {
1264    let result = string_to_key(key);
1265    if result.is_err() {
1266        return false;
1267    }
1268    let key: SaitoPublicKey = result.unwrap();
1269    saito_core::core::util::crypto::is_valid_public_key(&key)
1270}
1271
1272#[wasm_bindgen]
1273pub async fn write_issuance_file(threshold: Currency) {
1274    let mut saito = SAITO.lock().await;
1275    let _configs_lock = saito.as_mut().unwrap().routing_thread.config_lock.clone();
1276    let _mempool_lock = saito.as_mut().unwrap().routing_thread.mempool_lock.clone();
1277    let blockchain_lock = saito
1278        .as_mut()
1279        .unwrap()
1280        .routing_thread
1281        .blockchain_lock
1282        .clone();
1283    let mut storage = &mut saito.as_mut().unwrap().consensus_thread.storage;
1284    // let _list = storage.load_block_name_list().await.unwrap();
1285
1286    let blockchain = blockchain_lock.write().await;
1287    blockchain
1288        .write_issuance_file(threshold, "./data/issuance.file", &mut storage)
1289        .await;
1290}
1291
1292#[wasm_bindgen]
1293pub async fn disable_producing_blocks_by_timer() {
1294    let mut saito = SAITO.lock().await;
1295    saito
1296        .as_mut()
1297        .unwrap()
1298        .consensus_thread
1299        .produce_blocks_by_timer = false;
1300    // saito.as_mut().unwrap().mining_thread.enabled = true;
1301}
1302#[wasm_bindgen]
1303pub async fn produce_block_with_gt() -> bool {
1304    let mut saito = SAITO.lock().await;
1305
1306    let config_lock = saito.as_ref().unwrap().routing_thread.config_lock.clone();
1307    let blockchain_lock = saito.as_ref().unwrap().blockchain.blockchain_lock.clone();
1308    let mempool_lock = saito
1309        .as_ref()
1310        .unwrap()
1311        .consensus_thread
1312        .mempool_lock
1313        .clone();
1314    let wallet_lock = saito.as_ref().unwrap().wallet.wallet.clone();
1315
1316    let configs = config_lock.read().await;
1317    let blockchain = blockchain_lock.read().await;
1318
1319    let genesis_period = configs.get_consensus_config().unwrap().genesis_period;
1320    let latest_block_id = blockchain.get_latest_block_id();
1321
1322    let mut mempool = mempool_lock.write().await;
1323    let public_key;
1324    let private_key;
1325    {
1326        let wallet = wallet_lock.read().await;
1327        public_key = wallet.public_key;
1328        private_key = wallet.private_key;
1329    }
1330
1331    let gt_tx: Transaction;
1332
1333    {
1334        let miner = &mut saito.as_mut().unwrap().mining_thread;
1335        if miner.target == [0; 32] {
1336            let blockchain = blockchain_lock.read().await;
1337            if let Some(block) = blockchain.get_latest_block() {
1338                miner.difficulty = block.difficulty;
1339                miner.target = block.hash;
1340                miner.target_id = block.id;
1341            } else {
1342                warn!("couldn't find the latest block");
1343            }
1344        }
1345        info!("mining for a gt. target : {:?}", miner.target.to_hex());
1346
1347        loop {
1348            if let Some(gt) = miner.mine().await {
1349                info!("gt found : {:?}", gt.target.to_hex());
1350
1351                let transaction =
1352                    Wallet::create_golden_ticket_transaction(gt, &public_key, &private_key).await;
1353
1354                gt_tx = transaction;
1355                break;
1356            }
1357        }
1358    }
1359
1360    {
1361        let mut wallet = wallet_lock.write().await;
1362        if let Ok(mut tx) = Transaction::create(
1363            &mut wallet,
1364            public_key,
1365            0,
1366            0,
1367            false,
1368            None,
1369            latest_block_id,
1370            genesis_period,
1371        ) {
1372            drop(wallet);
1373            info!("created tx");
1374            tx.transaction_type = TransactionType::Vip;
1375            tx.sign(&private_key);
1376            info!("tx signed");
1377            mempool.add_transaction_if_validates(tx, &blockchain).await;
1378            info!("Tx added to mempool");
1379        }
1380    }
1381
1382    let timestamp = saito
1383        .as_ref()
1384        .unwrap()
1385        .consensus_thread
1386        .timer
1387        .get_timestamp_in_ms();
1388
1389    info!("waiting till a block is produced");
1390    for _ in 0..1000 {
1391        if let Some(block) = saito
1392            .as_mut()
1393            .unwrap()
1394            .consensus_thread
1395            .produce_block(
1396                timestamp,
1397                Some(&gt_tx),
1398                mempool.deref_mut(),
1399                blockchain.deref(),
1400                configs.deref(),
1401            )
1402            .await
1403        {
1404            info!("produced block with gt");
1405            drop(mempool);
1406            drop(blockchain);
1407            drop(configs);
1408            saito
1409                .as_mut()
1410                .unwrap()
1411                .consensus_thread
1412                .process_event(ConsensusEvent::BlockFetched {
1413                    peer_index: 0,
1414                    block,
1415                })
1416                .await;
1417            return true;
1418        }
1419    }
1420    warn!("couldn't produce block");
1421    false
1422}
1423
1424#[wasm_bindgen]
1425pub async fn produce_block_without_gt() -> bool {
1426    let mut saito = SAITO.lock().await;
1427
1428    let config_lock = saito.as_ref().unwrap().routing_thread.config_lock.clone();
1429    let blockchain_lock = saito.as_ref().unwrap().blockchain.blockchain_lock.clone();
1430    let mempool_lock = saito
1431        .as_ref()
1432        .unwrap()
1433        .consensus_thread
1434        .mempool_lock
1435        .clone();
1436    let wallet_lock = saito.as_ref().unwrap().wallet.wallet.clone();
1437
1438    let configs = config_lock.read().await;
1439    let blockchain = blockchain_lock.read().await;
1440
1441    let genesis_period = configs.get_consensus_config().unwrap().genesis_period;
1442    let latest_block_id = blockchain.get_latest_block_id();
1443
1444    let mut mempool = mempool_lock.write().await;
1445    let public_key;
1446    let private_key;
1447    {
1448        let wallet = wallet_lock.read().await;
1449        public_key = wallet.public_key;
1450        private_key = wallet.private_key;
1451    }
1452    {
1453        info!(
1454            "clearing {:?} gts from mempool...",
1455            mempool.golden_tickets.len()
1456        );
1457        mempool.golden_tickets.clear();
1458    }
1459    {
1460        let mut wallet = wallet_lock.write().await;
1461        if let Ok(mut tx) = Transaction::create(
1462            &mut wallet,
1463            public_key,
1464            0,
1465            0,
1466            false,
1467            None,
1468            latest_block_id,
1469            genesis_period,
1470        ) {
1471            drop(wallet);
1472            info!("created tx");
1473            tx.transaction_type = TransactionType::Vip;
1474            tx.sign(&private_key);
1475            info!("tx signed");
1476            mempool.add_transaction_if_validates(tx, &blockchain).await;
1477            info!("Tx added to mempool");
1478        }
1479    }
1480
1481    let timestamp = saito
1482        .as_ref()
1483        .unwrap()
1484        .consensus_thread
1485        .timer
1486        .get_timestamp_in_ms();
1487
1488    info!("waiting till a block is produced");
1489    for _ in 0..1000 {
1490        if let Some(block) = saito
1491            .as_mut()
1492            .unwrap()
1493            .consensus_thread
1494            .produce_block(
1495                timestamp,
1496                None,
1497                mempool.deref_mut(),
1498                blockchain.deref(),
1499                configs.deref(),
1500            )
1501            .await
1502        {
1503            info!("produced block with gt");
1504            drop(mempool);
1505            drop(blockchain);
1506            drop(configs);
1507            saito
1508                .as_mut()
1509                .unwrap()
1510                .consensus_thread
1511                .process_event(ConsensusEvent::BlockFetched {
1512                    peer_index: 0,
1513                    block,
1514                })
1515                .await;
1516            return true;
1517        }
1518    }
1519    warn!("couldn't produce block");
1520    false
1521}
1522
1523pub fn generate_keys_wasm() -> (SaitoPublicKey, SaitoPrivateKey) {
1524    let (mut secret_key, mut public_key) =
1525        SECP256K1.generate_keypair(&mut rand::rngs::OsRng::default());
1526    while public_key.serialize().to_base58().len() != 44 {
1527        // sometimes secp256k1 address is too big to store in 44 base-58 digits
1528        let keypair_tuple = SECP256K1.generate_keypair(&mut rand::rngs::OsRng::default());
1529        secret_key = keypair_tuple.0;
1530        public_key = keypair_tuple.1;
1531    }
1532    let mut secret_bytes = [0u8; 32];
1533    for i in 0..32 {
1534        secret_bytes[i] = secret_key[i];
1535    }
1536    (public_key.serialize(), secret_bytes)
1537}
1538
1539pub fn string_to_key<T: TryFrom<Vec<u8>> + PrintForLog<T>>(key: JsString) -> Result<T, Error>
1540where
1541    <T as TryFrom<Vec<u8>>>::Error: std::fmt::Debug,
1542{
1543    let str = key.as_string();
1544    if str.is_none() {
1545        error!("cannot convert wasm string to rust string");
1546        return Err(Error::from(ErrorKind::InvalidInput));
1547    }
1548
1549    let str = str.unwrap();
1550    if str.is_empty() {
1551        // debug!("cannot convert empty string to key");
1552        return Err(Error::from(ErrorKind::InvalidInput));
1553    }
1554
1555    let key = T::from_base58(str.as_str());
1556    if key.is_err() {
1557        // error!(
1558        //     "failed parsing key : {:?}. str : {:?}",
1559        //     key.err().unwrap(),
1560        //     str
1561        // );
1562        return Err(Error::from(ErrorKind::InvalidInput));
1563    }
1564    let key = key.unwrap();
1565    Ok(key)
1566}
1567
1568pub fn string_to_hex<T: TryFrom<Vec<u8>> + PrintForLog<T>>(key: JsString) -> Result<T, Error>
1569where
1570    <T as TryFrom<Vec<u8>>>::Error: std::fmt::Debug,
1571{
1572    let str = key.as_string();
1573    if str.is_none() {
1574        // error!("cannot convert wasm string to rust string");
1575        return Err(Error::from(ErrorKind::InvalidInput));
1576    }
1577
1578    let str = str.unwrap();
1579    if str.is_empty() {
1580        debug!("cannot convert empty string to hex");
1581        return Err(Error::from(ErrorKind::InvalidInput));
1582    }
1583
1584    let key = T::from_hex(str.as_str());
1585    if key.is_err() {
1586        error!(
1587            "failed parsing hex : {:?}. str : {:?}",
1588            key.err().unwrap(),
1589            str
1590        );
1591        return Err(Error::from(ErrorKind::InvalidInput));
1592    }
1593    let key = key.unwrap();
1594    Ok(key)
1595}
1596
1597pub fn string_array_to_base58_keys<T: TryFrom<Vec<u8>> + PrintForLog<T>>(
1598    array: js_sys::Array,
1599) -> Vec<T> {
1600    let array: Vec<T> = array
1601        .to_vec()
1602        .drain(..)
1603        .filter_map(|key| {
1604            let key: String = key.as_string()?;
1605            let key = T::from_base58(key.as_str());
1606            if key.is_err() {
1607                return None;
1608            }
1609            let key: T = key.unwrap();
1610            Some(key)
1611        })
1612        .collect();
1613    array
1614}
1615
1616// #[cfg(test)]
1617// mod test {
1618//     use js_sys::JsString;
1619//     use saito_core::common::defs::SaitoPublicKey;
1620//
1621//     use crate::saitowasm::string_to_key;
1622//
1623//     #[test]
1624//     fn string_to_key_test() {
1625//         let empty_key = [
1626//             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1627//             0, 0, 0, 0,
1628//         ];
1629//         let key = string_to_key(JsString::from(""));
1630//         assert!(key.is_ok());
1631//         let key: SaitoPublicKey = key.unwrap();
1632//         assert_eq!(key, empty_key);
1633//     }
1634// }