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,