diff --git a/Cargo.toml b/Cargo.toml index deec6b843f..f6f7f67e61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ opt-level = 3 strip = true codegen-units = 1 # lto = true -panic = "abort" +panic = 'unwind' [profile.dev] opt-level = 0 diff --git a/README.md b/README.md index 0d91d3cded..bec7bd1166 100755 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ ## What is the Komodo DeFi Framework? -The Komodo DeFi Framework is open-source [atomic-swap](https://komodoplatform.com/en/academy/atomic-swaps/) software for seamless, decentralised, peer to peer trading between almost every blockchain asset in existence. This software works with propagation of orderbooks and swap states through the [libp2p](https://libp2p.io/) protocol and uses [Hash Time Lock Contracts (HTLCs)](https://en.bitcoinwiki.org/wiki/Hashed_Timelock_Contracts) for ensuring that the two parties in a swap either mutually complete a trade, or funds return to thier original owner. +The Komodo DeFi Framework is open-source [atomic-swap](https://komodoplatform.com/en/academy/atomic-swaps/) software for seamless, decentralized, peer to peer trading between almost every blockchain asset in existence. This software works with propagation of orderbooks and swap states through the [libp2p](https://libp2p.io/) protocol and uses [Hash Time Lock Contracts (HTLCs)](https://en.bitcoinwiki.org/wiki/Hashed_Timelock_Contracts) for ensuring that the two parties in a swap either mutually complete a trade, or funds return to thier original owner. There is no 3rd party intermediary, no proxy tokens, and at all times users remain in sole possession of their private keys. @@ -172,7 +172,7 @@ Refer to the [Komodo Developer Docs](https://developers.komodoplatform.com/basic ## Project structure -[mm2src](mm2src) - Rust code, contains some parts ported from C `as is` (e.g. `lp_ordermatch`) to reach the most essential/error prone code. Some other modules/crates are reimplemented from scratch. +[mm2src](mm2src) - Rust code, contains some parts ported from C `as is` (e.g. `lp_ordermatch`) to reach the most essential/error-prone code. Some other modules/crates are reimplemented from scratch. ## Additional docs for developers @@ -185,8 +185,8 @@ Refer to the [Komodo Developer Docs](https://developers.komodoplatform.com/basic ## Disclaimer -This repository contains the `work in progress` code of the brand new Komodo DeFi Framework (kdf) built mainly on Rust. -The current state can be considered as a alpha version. +This repository contains the `work in progress` code of the brand-new Komodo DeFi Framework (kdf) built mainly on Rust. +The current state can be considered as an alpha version. **WARNING: Use with test coins only or with assets which value does not exceed an amount you are willing to lose. This is alpha stage software! ** diff --git a/mm2src/coins/eth/eth_tests.rs b/mm2src/coins/eth/eth_tests.rs index 1cf335c782..03705ff4ca 100644 --- a/mm2src/coins/eth/eth_tests.rs +++ b/mm2src/coins/eth/eth_tests.rs @@ -1,27 +1,31 @@ use super::*; -use crate::eth::for_tests::{eth_coin_for_test, eth_coin_from_keypair}; -use crate::{DexFee, IguanaPrivKey}; -use common::{block_on, block_on_f01, now_sec}; -#[cfg(not(target_arch = "wasm32"))] -use ethkey::{Generator, Random}; +use crate::IguanaPrivKey; +use common::{block_on, block_on_f01}; use mm2_core::mm_ctx::MmCtxBuilder; -use mm2_test_helpers::for_tests::{ETH_MAINNET_CHAIN_ID, ETH_MAINNET_NODE, ETH_SEPOLIA_CHAIN_ID, ETH_SEPOLIA_NODES, + +cfg_native!( + use crate::eth::for_tests::{eth_coin_for_test, eth_coin_from_keypair}; + use crate::DexFee; + + use common::now_sec; + use ethkey::{Generator, Random}; + use mm2_test_helpers::for_tests::{ETH_MAINNET_CHAIN_ID, ETH_MAINNET_NODE, ETH_SEPOLIA_CHAIN_ID, ETH_SEPOLIA_NODES, ETH_SEPOLIA_TOKEN_CONTRACT}; -use mocktopus::mocking::*; - -/// The gas price for the tests -const GAS_PRICE: u64 = 50_000_000_000; -// `GAS_PRICE` increased by 3% -const GAS_PRICE_APPROXIMATION_ON_START_SWAP: u64 = 51_500_000_000; -// `GAS_PRICE` increased by 5% -const GAS_PRICE_APPROXIMATION_ON_ORDER_ISSUE: u64 = 52_500_000_000; -// `GAS_PRICE` increased by 7% -const GAS_PRICE_APPROXIMATION_ON_TRADE_PREIMAGE: u64 = 53_500_000_000; + use mocktopus::mocking::*; + + /// The gas price for the tests + const GAS_PRICE: u64 = 50_000_000_000; + /// `GAS_PRICE` increased by 3% + const GAS_PRICE_APPROXIMATION_ON_START_SWAP: u64 = 51_500_000_000; + /// `GAS_PRICE` increased by 5% + const GAS_PRICE_APPROXIMATION_ON_ORDER_ISSUE: u64 = 52_500_000_000; + /// `GAS_PRICE` increased by 7% + const GAS_PRICE_APPROXIMATION_ON_TRADE_PREIMAGE: u64 = 53_500_000_000; +); + // old way to add some extra gas to the returned value from gas station (non-existent now), still used in tests const GAS_PRICE_PERCENT: u64 = 10; -const TAKER_PAYMENT_SPEND_SEARCH_INTERVAL: f64 = 1.; - fn check_sum(addr: &str, expected: &str) { let actual = checksum_address(addr); assert_eq!(expected, actual); @@ -154,8 +158,11 @@ fn test_wei_from_big_decimal() { assert_eq!(expected_wei, wei); } +#[cfg(not(target_arch = "wasm32"))] #[test] fn test_wait_for_payment_spend_timeout() { + const TAKER_PAYMENT_SPEND_SEARCH_INTERVAL: f64 = 1.; + EthCoin::spend_events.mock_safe(|_, _, _, _| MockResult::Return(Box::new(futures01::future::ok(vec![])))); EthCoin::current_block.mock_safe(|_| MockResult::Return(Box::new(futures01::future::ok(900)))); @@ -304,6 +311,7 @@ fn test_add_ten_pct_one_gwei() { assert_eq!(expected, actual); } +#[cfg(not(target_arch = "wasm32"))] #[test] fn get_sender_trade_preimage() { /// Trade fee for the ETH coin is `2 * 150_000 * gas_price` always. @@ -359,6 +367,7 @@ fn get_sender_trade_preimage() { assert_eq!(actual, expected); } +#[cfg(not(target_arch = "wasm32"))] #[test] fn get_erc20_sender_trade_preimage() { const APPROVE_GAS_LIMIT: u64 = 60_000; @@ -461,6 +470,7 @@ fn get_erc20_sender_trade_preimage() { ); } +#[cfg(not(target_arch = "wasm32"))] #[test] fn get_receiver_trade_preimage() { EthCoin::get_gas_price.mock_safe(|_| MockResult::Return(Box::pin(futures::future::ok(GAS_PRICE.into())))); @@ -479,6 +489,7 @@ fn get_receiver_trade_preimage() { assert_eq!(actual, expected_fee); } +#[cfg(not(target_arch = "wasm32"))] #[test] fn test_get_fee_to_send_taker_fee() { const DEX_FEE_AMOUNT: u64 = 100_000; @@ -529,6 +540,7 @@ fn test_get_fee_to_send_taker_fee() { /// /// Please note this test doesn't work correctly now, /// because as of now [`EthCoin::get_fee_to_send_taker_fee`] doesn't process the `Exception` web3 error correctly. +#[cfg(not(target_arch = "wasm32"))] #[test] #[ignore] fn test_get_fee_to_send_taker_fee_insufficient_balance() { @@ -558,6 +570,7 @@ fn test_get_fee_to_send_taker_fee_insufficient_balance() { ); } +#[cfg(not(target_arch = "wasm32"))] #[test] fn validate_dex_fee_invalid_sender_eth() { let (_ctx, coin) = eth_coin_for_test(EthCoinType::Eth, &[ETH_MAINNET_NODE], None, ETH_MAINNET_CHAIN_ID); @@ -587,6 +600,7 @@ fn validate_dex_fee_invalid_sender_eth() { } } +#[cfg(not(target_arch = "wasm32"))] #[test] fn validate_dex_fee_invalid_sender_erc() { let (_ctx, coin) = eth_coin_for_test( @@ -624,6 +638,7 @@ fn validate_dex_fee_invalid_sender_erc() { } } +#[cfg(not(target_arch = "wasm32"))] fn sender_compressed_pub(tx: &SignedEthTx) -> [u8; 33] { let tx_pubkey = tx.public.unwrap(); let mut raw_pubkey = [0; 65]; @@ -633,6 +648,7 @@ fn sender_compressed_pub(tx: &SignedEthTx) -> [u8; 33] { secp_public.serialize() } +#[cfg(not(target_arch = "wasm32"))] #[test] fn validate_dex_fee_eth_confirmed_before_min_block() { let (_ctx, coin) = eth_coin_for_test(EthCoinType::Eth, &[ETH_MAINNET_NODE], None, ETH_MAINNET_CHAIN_ID); @@ -664,6 +680,7 @@ fn validate_dex_fee_eth_confirmed_before_min_block() { } } +#[cfg(not(target_arch = "wasm32"))] #[test] fn validate_dex_fee_erc_confirmed_before_min_block() { let (_ctx, coin) = eth_coin_for_test( @@ -704,6 +721,7 @@ fn validate_dex_fee_erc_confirmed_before_min_block() { } } +#[cfg(not(target_arch = "wasm32"))] #[test] fn test_negotiate_swap_contract_addr_no_fallback() { let (_, coin) = eth_coin_for_test(EthCoinType::Eth, &[ETH_MAINNET_NODE], None, ETH_MAINNET_CHAIN_ID); @@ -731,6 +749,7 @@ fn test_negotiate_swap_contract_addr_no_fallback() { assert_eq!(Some(slice.to_vec().into()), result); } +#[cfg(not(target_arch = "wasm32"))] #[test] fn test_negotiate_swap_contract_addr_has_fallback() { let fallback = Address::from_str("0x8500AFc0bc5214728082163326C2FF0C73f4a871").unwrap(); @@ -826,6 +845,7 @@ fn polygon_check_if_my_payment_sent() { assert_eq!(expected_hash, my_payment.tx_hash_as_bytes()); } +#[cfg(not(target_arch = "wasm32"))] #[test] fn test_message_hash() { let key_pair = Random.generate().unwrap(); @@ -844,6 +864,7 @@ fn test_message_hash() { ); } +#[cfg(not(target_arch = "wasm32"))] #[test] fn test_sign_verify_message() { let key_pair = KeyPair::from_secret_slice( @@ -868,6 +889,7 @@ fn test_sign_verify_message() { assert!(is_valid); } +#[cfg(not(target_arch = "wasm32"))] #[test] fn test_eth_extract_secret() { let key_pair = Random.generate().unwrap(); diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 1af6027e30..500963fba8 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -124,7 +124,7 @@ macro_rules! try_f { }; } -#[cfg(feature = "enable-solana")] +#[cfg(all(feature = "enable-solana", not(target_arch = "wasm32")))] macro_rules! try_tx_fus_err { ($err: expr) => { return Box::new(futures01::future::err(crate::TransactionErr::Plain(ERRL!( @@ -133,7 +133,7 @@ macro_rules! try_tx_fus_err { }; } -#[cfg(feature = "enable-solana")] +#[cfg(all(feature = "enable-solana", not(target_arch = "wasm32")))] macro_rules! try_tx_fus_opt { ($e: expr, $err: expr) => { match $e { diff --git a/mm2src/coins/z_coin/storage/walletdb/wasm/mod.rs b/mm2src/coins/z_coin/storage/walletdb/wasm/mod.rs index 907a007c55..4c68fec22c 100644 --- a/mm2src/coins/z_coin/storage/walletdb/wasm/mod.rs +++ b/mm2src/coins/z_coin/storage/walletdb/wasm/mod.rs @@ -253,7 +253,12 @@ mod wasm_test { // scan the cache let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None, + ) .await .unwrap(); @@ -293,7 +298,12 @@ mod wasm_test { // Scan the cache again let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None, + ) .await .unwrap(); @@ -347,7 +357,12 @@ mod wasm_test { // Scan the cache again let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None, + ) .await .unwrap(); @@ -436,7 +451,12 @@ mod wasm_test { // Scan the cache again let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None, + ) .await .unwrap(); @@ -520,7 +540,12 @@ mod wasm_test { // Scan the cache let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None, + ) .await .unwrap(); @@ -545,7 +570,12 @@ mod wasm_test { // Scan the cache again let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None, + ) .await .unwrap(); @@ -579,7 +609,12 @@ mod wasm_test { // Scan cache let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None, + ) .await .unwrap(); @@ -592,7 +627,12 @@ mod wasm_test { // Scan the cache again let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); let scan = blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None, + ) .await .unwrap_err(); match scan.get_inner() { @@ -611,7 +651,12 @@ mod wasm_test { blockdb.insert_block(cb2.height as u32, cb2_bytes).await.unwrap(); let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); assert!(blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None + ) .await .is_ok()); @@ -650,7 +695,12 @@ mod wasm_test { // Scan the cache let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); assert!(blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None + ) .await .is_ok()); @@ -666,7 +716,12 @@ mod wasm_test { // Scan the cache again let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); assert!(blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None + ) .await .is_ok()); @@ -703,7 +758,12 @@ mod wasm_test { // Scan the cache let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); assert!(blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None + ) .await .is_ok()); @@ -728,7 +788,12 @@ mod wasm_test { // Scan the cache again let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); let scan = blockdb - .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + .process_blocks_with_mode( + consensus_params.clone(), + BlockProcessingMode::Scan(scan, None), + None, + None, + ) .await; assert!(scan.is_ok()); @@ -767,7 +832,7 @@ mod wasm_test { // // Scan the cache // let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); // assert!(blockdb - // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan, None), None, None) // .await // .is_ok()); // @@ -787,7 +852,7 @@ mod wasm_test { // // Scan the cache // let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); // assert!(blockdb - // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan, None), None, None) // .await // .is_ok()); // @@ -832,7 +897,7 @@ mod wasm_test { // // Scan the cache // let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); // assert!(blockdb - // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan, None), None, None) // .await // .is_ok()); // @@ -863,7 +928,7 @@ mod wasm_test { // // Scan the cache // let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); // assert!(blockdb - // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan, None), None, None) // .await // .is_ok()); // @@ -1033,7 +1098,7 @@ mod wasm_test { // // Scan the cache // let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); // blockdb - // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan, None), None, None) // .await // .unwrap(); // assert_eq!(walletdb.get_balance(AccountId(0)).await.unwrap(), value); @@ -1090,7 +1155,7 @@ mod wasm_test { // // Scan the cache // let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); // blockdb - // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan, None), None, None) // .await // .unwrap(); // @@ -1126,7 +1191,7 @@ mod wasm_test { // // Scan the cache // let scan = DataConnStmtCacheWrapper::new(DataConnStmtCacheWasm(walletdb.clone())); // blockdb - // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan), None, None) + // .process_blocks_with_mode(consensus_params.clone(), BlockProcessingMode::Scan(scan, None), None, None) // .await // .unwrap(); // diff --git a/mm2src/common/common.rs b/mm2src/common/common.rs index e390893bae..f665a9a8e0 100644 --- a/mm2src/common/common.rs +++ b/mm2src/common/common.rs @@ -518,19 +518,6 @@ pub fn set_panic_hook() { })) } -/// Simulates the panic-in-panic crash. -pub fn double_panic_crash() { - struct Panicker; - impl Drop for Panicker { - fn drop(&mut self) { panic!("panic in drop") } - } - let panicker = Panicker; - if 1 < 2 { - panic!("first panic") - } - drop(panicker) // Delays the drop. -} - /// RPC response, returned by the RPC handlers. /// NB: By default the future is executed on the shared asynchronous reactor (`CORE`), /// the handler is responsible for spawning the future on another reactor if it doesn't fit the `CORE` well. diff --git a/mm2src/mm2_bin_lib/src/lib.rs b/mm2src/mm2_bin_lib/src/lib.rs index 81c532419f..c78233e64a 100644 --- a/mm2src/mm2_bin_lib/src/lib.rs +++ b/mm2src/mm2_bin_lib/src/lib.rs @@ -99,5 +99,5 @@ fn prepare_for_mm2_stop() -> PrepareForStopResult { async fn finalize_mm2_stop(ctx: MmArc) { dispatch_lp_event(ctx.clone(), StopCtxEvent.into()).await; - let _ = ctx.stop(); + let _ = ctx.stop().await; } diff --git a/mm2src/mm2_core/src/mm_ctx.rs b/mm2src/mm2_core/src/mm_ctx.rs index ec7ceb036a..0be8e66734 100644 --- a/mm2src/mm2_core/src/mm_ctx.rs +++ b/mm2src/mm2_core/src/mm_ctx.rs @@ -503,7 +503,10 @@ lazy_static! { impl MmArc { pub fn new(ctx: MmCtx) -> MmArc { MmArc(SharedRc::new(ctx)) } - pub fn stop(&self) -> Result<(), String> { + pub async fn stop(&self) -> Result<(), String> { + #[cfg(not(target_arch = "wasm32"))] + try_s!(self.close_async_connection().await); + try_s!(self.stop.pin(true)); // Notify shutdown listeners. @@ -517,6 +520,16 @@ impl MmArc { Ok(()) } + #[cfg(not(target_arch = "wasm32"))] + async fn close_async_connection(&self) -> Result<(), db_common::async_sql_conn::AsyncConnError> { + if let Some(async_conn) = self.async_sqlite_connection.as_option() { + let mut conn = async_conn.lock().await; + conn.close().await?; + } + + Ok(()) + } + #[cfg(feature = "track-ctx-pointer")] fn track_ctx_pointer(&self) { let ctx_weak = self.weak(); diff --git a/mm2src/mm2_main/Cargo.toml b/mm2src/mm2_main/Cargo.toml index 60c3e9aa62..8f84ebb90a 100644 --- a/mm2src/mm2_main/Cargo.toml +++ b/mm2src/mm2_main/Cargo.toml @@ -77,9 +77,6 @@ primitives = { path = "../mm2_bitcoin/primitives" } prost = "0.11" rand = { version = "0.7", features = ["std", "small_rng"] } rand6 = { version = "0.6", package = "rand" } -# TODO: Reduce the size of regex by disabling the features we don't use. -# cf. https://github.com/rust-lang/regex/issues/583 -regex = "1" rmp-serde = "0.14.3" rpc = { path = "../mm2_bitcoin/rpc" } rpc_task = { path = "../rpc_task" } @@ -116,7 +113,7 @@ hyper = { version = "0.14.26", features = ["client", "http2", "server", "tcp"] } rcgen = "0.10" rustls = { version = "0.21", default-features = false } rustls-pemfile = "1.0.2" -tokio = { version = "1.20", features = ["io-util", "rt-multi-thread", "net"] } +tokio = { version = "1.20", features = ["io-util", "rt-multi-thread", "net", "signal"] } [target.'cfg(windows)'.dependencies] winapi = "0.3" diff --git a/mm2src/mm2_main/src/mm2.rs b/mm2src/mm2_main/src/mm2.rs index 7dcc5572cb..734e71becf 100644 --- a/mm2src/mm2_main/src/mm2.rs +++ b/mm2src/mm2_main/src/mm2.rs @@ -42,7 +42,7 @@ #[cfg(not(target_arch = "wasm32"))] use common::block_on; use common::crash_reports::init_crash_reports; -use common::double_panic_crash; +use common::log; use common::log::LogLevel; use common::password_policy::password_policy; use mm2_core::mm_ctx::MmCtxBuilder; @@ -54,7 +54,6 @@ use lp_swap::PAYMENT_LOCKTIME; use std::sync::atomic::Ordering; use gstuff::slurp; - use serde::ser::Serialize; use serde_json::{self as json, Value as Json}; @@ -64,7 +63,6 @@ use std::process::exit; use std::ptr::null; use std::str; -mod lp_native_dex; pub use self::lp_native_dex::init_hw; pub use self::lp_native_dex::lp_init; use coins::update_coins_config; @@ -75,6 +73,7 @@ use mm2_err_handle::prelude::*; pub mod heartbeat_event; pub mod lp_dispatcher; pub mod lp_message_service; +mod lp_native_dex; pub mod lp_network; pub mod lp_ordermatch; pub mod lp_stats; @@ -160,10 +159,33 @@ pub async fn lp_main( .with_datetime(datetime.clone()) .into_mm_arc(); ctx_cb(try_s!(ctx.ffi_handle())); + + #[cfg(not(target_arch = "wasm32"))] + spawn_ctrl_c_handler(ctx.clone()); + try_s!(lp_init(ctx, version, datetime).await); Ok(()) } +/// Handles CTRL-C signals and shutdowns the KDF runtime gracefully. +/// +/// It's important to spawn this task as soon as `Ctx` is in the correct state. +#[cfg(not(target_arch = "wasm32"))] +fn spawn_ctrl_c_handler(ctx: mm2_core::mm_ctx::MmArc) { + use crate::lp_dispatcher::{dispatch_lp_event, StopCtxEvent}; + + common::executor::spawn(async move { + tokio::signal::ctrl_c() + .await + .expect("Couldn't listen for the CTRL-C signal."); + + log::info!("Wrapping things up and shutting down..."); + + dispatch_lp_event(ctx.clone(), StopCtxEvent.into()).await; + ctx.stop().await.expect("Couldn't stop the KDF runtime."); + }); +} + fn help() { const HELP_MSG: &str = r#"Command-line options. The first command-line argument is special and designates the mode. @@ -248,16 +270,6 @@ pub fn mm2_main(version: String, datetime: String) { // we're not checking them for the mode switches in order not to risk [untrusted] data being mistaken for a mode switch. let first_arg = args_os.get(1).and_then(|arg| arg.to_str()); - if first_arg == Some("panic") { - panic!("panic message") - } - if first_arg == Some("crash") { - double_panic_crash() - } - if first_arg == Some("stderr") { - eprintln!("This goes to stderr"); - return; - } if first_arg == Some("update_config") { match on_update_config(&args_os) { Ok(_) => println!("Success"), diff --git a/mm2src/mm2_main/src/rpc.rs b/mm2src/mm2_main/src/rpc.rs index 2709d76f32..1bef856e15 100644 --- a/mm2src/mm2_main/src/rpc.rs +++ b/mm2src/mm2_main/src/rpc.rs @@ -28,14 +28,11 @@ use futures::future::{join_all, FutureExt}; use http::header::{HeaderValue, ACCESS_CONTROL_ALLOW_ORIGIN, CONTENT_TYPE}; use http::request::Parts; use http::{Method, Request, Response, StatusCode}; -use lazy_static::lazy_static; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use mm2_rpc::mm_protocol::{MmRpcBuilder, MmRpcResponse, MmRpcVersion}; -use regex::Regex; use serde::Serialize; use serde_json::{self as json, Value as Json}; -use std::borrow::Cow; use std::net::SocketAddr; cfg_native! { @@ -178,35 +175,6 @@ fn response_from_dispatcher_error( response.serialize_http_response() } -pub fn escape_answer<'a, S: Into>>(input: S) -> Cow<'a, str> { - lazy_static! { - static ref REGEX: Regex = Regex::new("[<>&]").unwrap(); - } - - let input = input.into(); - let mut last_match = 0; - - if REGEX.is_match(&input) { - let matches = REGEX.find_iter(&input); - let mut output = String::with_capacity(input.len()); - for mat in matches { - let (begin, end) = (mat.start(), mat.end()); - output.push_str(&input[last_match..begin]); - match &input[begin..end] { - "<" => output.push_str("<"), - ">" => output.push_str(">"), - "&" => output.push_str("&"), - _ => unreachable!(), - } - last_match = end; - } - output.push_str(&input[last_match..]); - Cow::Owned(output) - } else { - input - } -} - async fn process_single_request(ctx: MmArc, req: Json, client: SocketAddr) -> Result>, String> { let local_only = ctx.conf["rpc_local_only"].as_bool().unwrap_or(true); if req["mmrpc"].is_null() { @@ -314,23 +282,9 @@ async fn rpc_service(req: Request, ctx_h: u32, client: SocketAddr) -> Resp let res = try_sf!(process_rpc_request(ctx, req, req_json, client).await, ACCESS_CONTROL_ALLOW_ORIGIN => rpc_cors); let (mut parts, body) = res.into_parts(); - let body_escaped = { - let body_utf8 = match std::str::from_utf8(&body) { - Ok(body_utf8) => body_utf8, - Err(_) => { - return Response::builder() - .status(500) - .header(ACCESS_CONTROL_ALLOW_ORIGIN, rpc_cors) - .header(CONTENT_TYPE, APPLICATION_JSON) - .body(Body::from(err_to_rpc_json_string("Non UTF-8 output"))) - .unwrap(); - }, - }; - let escaped = escape_answer(body_utf8); - escaped.as_bytes().to_vec() - }; parts.headers.insert(ACCESS_CONTROL_ALLOW_ORIGIN, rpc_cors); - Response::from_parts(parts, Body::from(body_escaped)) + + Response::from_parts(parts, Body::from(body)) } // TODO: This should exclude TCP internals, as including them results in having to diff --git a/mm2src/mm2_main/src/rpc/lp_commands/lp_commands_legacy.rs b/mm2src/mm2_main/src/rpc/lp_commands/lp_commands_legacy.rs index 7db7c5b02b..5ef386942c 100644 --- a/mm2src/mm2_main/src/rpc/lp_commands/lp_commands_legacy.rs +++ b/mm2src/mm2_main/src/rpc/lp_commands/lp_commands_legacy.rs @@ -21,7 +21,6 @@ use coins::{lp_coinfind, lp_coinfind_any, lp_coininit, CoinsContext, MmCoinEnum}; use common::executor::Timer; -use common::log::error; use common::{rpc_err_response, rpc_response, HyRes}; use futures::compat::Future01CompatExt; use http::Response; @@ -242,29 +241,13 @@ pub async fn my_balance(ctx: MmArc, req: Json) -> Result>, Stri Ok(try_s!(Response::builder().body(res))) } -#[cfg(not(target_arch = "wasm32"))] -async fn close_async_connection(ctx: &MmArc) { - if let Some(async_conn) = ctx.async_sqlite_connection.as_option() { - let mut conn = async_conn.lock().await; - if let Err(e) = conn.close().await { - error!("Error stopping AsyncConnection: {}", e); - } - } -} - pub async fn stop(ctx: MmArc) -> Result>, String> { dispatch_lp_event(ctx.clone(), StopCtxEvent.into()).await; // Should delay the shutdown a bit in order not to trip the "stop" RPC call in unit tests. // Stopping immediately leads to the "stop" RPC call failing with the "errno 10054" sometimes. let fut = async move { Timer::sleep(0.05).await; - - #[cfg(not(target_arch = "wasm32"))] - close_async_connection(&ctx).await; - - if let Err(e) = ctx.stop() { - error!("Error stopping MmCtx: {}", e); - } + ctx.stop().await.expect("Couldn't stop the KDF runtime."); }; // Please note we shouldn't use `MmCtx::spawner` to spawn this future,