From 8b1170d9f33208e1c260d94d9ef9a7eb492e1047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C3=96zkan?= Date: Thu, 15 Aug 2024 16:22:43 +0300 Subject: [PATCH 01/29] feat(cosmos): komodo-defi-proxy support (#2173) This commit does the following: * Adds new signature algorithm for komodo-defi-proxy (coin-agnostic, pubkey-only mode) * Refactors NFT and ETH proxy to use the new algorithm * Implements proxy support for Tendermint (including WebSocket connections) BREAKING CHANGES: * Updates to Tendermint activation payloads: - 'rpc_urls' field (previously a list of plain string values) is replaced with 'nodes' (a list of JSON objects). * All RPC methods fields controlling komodo-defi-proxy are renamed to 'komodo_proxy', this affects various activations, including ETH/EVM. --- Cargo.lock | 13 ++ Cargo.toml | 1 + mm2src/coins/Cargo.toml | 1 + mm2src/coins/eth.rs | 55 +----- mm2src/coins/eth/for_tests.rs | 2 +- mm2src/coins/eth/v2_activation.rs | 170 ++++-------------- .../eth/web3_transport/http_transport.rs | 107 +++++------ mm2src/coins/eth/web3_transport/mod.rs | 57 ------ .../eth/web3_transport/websocket_transport.rs | 46 +++-- mm2src/coins/nft.rs | 60 ++++--- mm2src/coins/nft/nft_structs.rs | 4 +- .../tendermint/rpc/tendermint_native_rpc.rs | 71 ++++++-- .../tendermint/rpc/tendermint_wasm_rpc.rs | 46 +++-- .../tendermint/tendermint_balance_events.rs | 56 ++++-- mm2src/coins/tendermint/tendermint_coin.rs | 81 ++++++--- .../src/erc20_token_activation.rs | 4 +- .../src/eth_with_token_activation.rs | 2 +- .../src/tendermint_with_assets_activation.rs | 10 +- mm2src/common/Cargo.toml | 5 +- mm2src/common/common.rs | 2 + mm2src/mm2_core/src/mm_ctx.rs | 2 - mm2src/mm2_main/src/lp_native_dex.rs | 34 ++-- mm2src/mm2_main/src/ordermatch_tests.rs | 19 +- .../src/rpc/lp_commands/lp_commands_legacy.rs | 4 +- mm2src/mm2_net/Cargo.toml | 3 +- mm2src/mm2_net/src/p2p.rs | 14 +- mm2src/mm2_net/src/transport.rs | 18 -- mm2src/mm2_p2p/src/behaviours/atomicdex.rs | 33 ++-- mm2src/mm2_p2p/src/lib.rs | 2 +- mm2src/mm2_test_helpers/src/for_tests.rs | 6 +- mm2src/proxy_signature/Cargo.toml | 14 ++ mm2src/proxy_signature/src/lib.rs | 152 ++++++++++++++++ 32 files changed, 611 insertions(+), 483 deletions(-) create mode 100644 mm2src/proxy_signature/Cargo.toml create mode 100644 mm2src/proxy_signature/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index b711cd8deb..681184de65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1083,6 +1083,7 @@ dependencies = [ "prost", "prost-build", "protobuf", + "proxy_signature", "rand 0.7.3", "regex", "reqwest", @@ -5727,6 +5728,18 @@ dependencies = [ "protobuf-codegen", ] +[[package]] +name = "proxy_signature" +version = "0.1.0" +dependencies = [ + "chrono", + "http 0.2.12", + "libp2p", + "rand 0.7.3", + "serde", + "serde_json", +] + [[package]] name = "qstring" version = "0.7.2" diff --git a/Cargo.toml b/Cargo.toml index 4de3f093e9..758587e557 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ members = [ "mm2src/mm2_p2p", "mm2src/mm2_rpc", "mm2src/mm2_state_machine", + "mm2src/proxy_signature", "mm2src/rpc_task", "mm2src/trezor", ] diff --git a/mm2src/coins/Cargo.toml b/mm2src/coins/Cargo.toml index e5f5f6837c..3f37c3d74e 100644 --- a/mm2src/coins/Cargo.toml +++ b/mm2src/coins/Cargo.toml @@ -85,6 +85,7 @@ parking_lot = { version = "0.12.0", features = ["nightly"] } primitives = { path = "../mm2_bitcoin/primitives" } prost = "0.11" protobuf = "2.20" +proxy_signature = { path = "../proxy_signature" } rand = { version = "0.7", features = ["std", "small_rng"] } regex = "1" reqwest = { version = "0.11.9", default-features = false, features = ["json"], optional = true } diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 7974e29668..0d50f70024 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -58,7 +58,7 @@ use common::executor::{abortable_queue::AbortableQueue, AbortOnDropHandle, Abort AbortedError, SpawnAbortable, Timer}; use common::log::{debug, error, info, warn}; use common::number_type_casting::SafeTypeCastingNumbers; -use common::{get_utc_timestamp, now_sec, small_rng, DEX_FEE_ADDR_RAW_PUBKEY}; +use common::{now_sec, small_rng, DEX_FEE_ADDR_RAW_PUBKEY}; use crypto::privkey::key_pair_from_secret; use crypto::{Bip44Chain, CryptoCtx, CryptoCtxError, GlobalHDAccountArc, KeyPairPolicy}; use derive_more::Display; @@ -78,7 +78,6 @@ use http::Uri; use instant::Instant; use mm2_core::mm_ctx::{MmArc, MmWeak}; use mm2_event_stream::behaviour::{EventBehaviour, EventInitStatus}; -use mm2_net::transport::{KomodefiProxyAuthValidation, ProxyAuthValidationGenerator}; use mm2_number::bigdecimal_custom::CheckedDivision; use mm2_number::{BigDecimal, BigUint, MmNumber}; #[cfg(test)] use mocktopus::macros::*; @@ -292,9 +291,6 @@ impl Default for EthGasLimit { } } -/// Lifetime of generated signed message for proxy-auth requests -const PROXY_AUTH_SIGNED_MESSAGE_LIFETIME_SEC: i64 = 90; - /// Max transaction type according to EIP-2718 const ETH_MAX_TX_TYPE: u64 = 0x7f; @@ -5790,45 +5786,6 @@ impl TryToAddress for Option { } } -pub trait KomodoDefiAuthMessages { - fn proxy_auth_sign_message_hash(message: String) -> Option<[u8; 32]>; - fn generate_proxy_auth_signed_validation( - generator: ProxyAuthValidationGenerator, - ) -> SignatureResult; -} - -impl KomodoDefiAuthMessages for EthCoin { - fn proxy_auth_sign_message_hash(message: String) -> Option<[u8; 32]> { - let message_prefix = "atomicDEX Auth Ethereum Signed Message:\n"; - let prefix_len = CompactInteger::from(message_prefix.len()); - - let mut stream = Stream::new(); - prefix_len.serialize(&mut stream); - stream.append_slice(message_prefix.as_bytes()); - stream.append_slice(message.len().to_string().as_bytes()); - stream.append_slice(message.as_bytes()); - - Some(keccak256(&stream.out()).take()) - } - - fn generate_proxy_auth_signed_validation( - generator: ProxyAuthValidationGenerator, - ) -> SignatureResult { - let timestamp_message = get_utc_timestamp() + PROXY_AUTH_SIGNED_MESSAGE_LIFETIME_SEC; - - let message_hash = EthCoin::proxy_auth_sign_message_hash(timestamp_message.to_string()) - .ok_or(SignatureError::PrefixNotFound)?; - let signature = sign(&generator.secret, &H256::from(message_hash))?; - - Ok(KomodefiProxyAuthValidation { - coin_ticker: generator.coin_ticker, - address: generator.address, - timestamp_message, - signature: format!("0x{}", signature), - }) - } -} - fn validate_fee_impl(coin: EthCoin, validate_fee_args: EthValidateFeeArgs<'_>) -> ValidatePaymentFut<()> { let fee_tx_hash = validate_fee_args.fee_tx_hash.to_owned(); let sender_addr = try_f!( @@ -6282,10 +6239,7 @@ pub async fn eth_coin_from_conf_and_request( Some("ws") | Some("wss") => { const TMP_SOCKET_CONNECTION: Duration = Duration::from_secs(20); - let node = WebsocketTransportNode { - uri: uri.clone(), - gui_auth: false, - }; + let node = WebsocketTransportNode { uri: uri.clone() }; let websocket_transport = WebsocketTransport::with_event_handlers(node, event_handlers.clone()); // Temporarily start the connection loop (we close the connection once we have the client version below). @@ -6300,7 +6254,10 @@ pub async fn eth_coin_from_conf_and_request( Web3Transport::Websocket(websocket_transport) }, Some("http") | Some("https") => { - let node = HttpTransportNode { uri, gui_auth: false }; + let node = HttpTransportNode { + uri, + komodo_proxy: false, + }; Web3Transport::new_http_with_event_handlers(node, event_handlers.clone()) }, diff --git a/mm2src/coins/eth/for_tests.rs b/mm2src/coins/eth/for_tests.rs index 2b294eba4b..d3b8ece3ac 100644 --- a/mm2src/coins/eth/for_tests.rs +++ b/mm2src/coins/eth/for_tests.rs @@ -33,7 +33,7 @@ pub(crate) fn eth_coin_from_keypair( for url in urls.iter() { let node = HttpTransportNode { uri: url.parse().unwrap(), - gui_auth: false, + komodo_proxy: false, }; let transport = Web3Transport::new_http(node); let web3 = Web3::new(transport); diff --git a/mm2src/coins/eth/v2_activation.rs b/mm2src/coins/eth/v2_activation.rs index 24f15fc127..dffa0a4e17 100644 --- a/mm2src/coins/eth/v2_activation.rs +++ b/mm2src/coins/eth/v2_activation.rs @@ -1,4 +1,5 @@ use super::*; +use crate::eth::web3_transport::http_transport::HttpTransport; use crate::hd_wallet::{load_hd_accounts_from_storage, HDAccountsMutex, HDPathAccountToAddressId, HDWalletCoinStorage, HDWalletStorageError, DEFAULT_GAP_LIMIT}; use crate::nft::get_nfts_for_activation; @@ -12,6 +13,8 @@ use instant::Instant; use mm2_err_handle::common_errors::WithInternal; #[cfg(target_arch = "wasm32")] use mm2_metamask::{from_metamask_error, MetamaskError, MetamaskRpcError, WithMetamaskRpcError}; +use mm2_net::p2p::P2PContext; +use proxy_signature::RawMessage; use rpc_task::RpcTaskError; use std::sync::atomic::Ordering; use url::Url; @@ -195,7 +198,7 @@ pub struct EthActivationV2Request { pub struct EthNode { pub url: String, #[serde(default)] - pub gui_auth: bool, + pub komodo_proxy: bool, } #[derive(Display, Serialize, SerializeErrorType)] @@ -337,7 +340,7 @@ pub enum NftProviderEnum { Moralis { url: Url, #[serde(default)] - proxy_auth: bool, + komodo_proxy: bool, }, } @@ -399,24 +402,6 @@ impl EthCoin { Some(d) => d as u8, }; - let web3_instances: Vec = self - .web3_instances - .lock() - .await - .iter() - .map(|node| { - let mut transport = node.web3.transport().clone(); - if let Some(auth) = transport.proxy_auth_validation_generator_as_mut() { - auth.coin_ticker = ticker.clone(); - } - let web3 = Web3::new(transport); - Web3Instance { - web3, - is_parity: node.is_parity, - } - }) - .collect(); - let required_confirmations = activation_params .required_confirmations .unwrap_or_else(|| conf["required_confirmations"].as_u64().unwrap_or(1)) @@ -449,7 +434,7 @@ impl EthCoin { contract_supports_watchers: self.contract_supports_watchers, decimals, ticker, - web3_instances: AsyncMutex::new(web3_instances), + web3_instances: AsyncMutex::new(self.web3_instances.lock().await.clone()), history_sync_state: Mutex::new(self.history_sync_state.lock().unwrap().clone()), swap_txfee_policy: Mutex::new(SwapTxFeePolicy::Internal), max_eth_tx_type, @@ -480,7 +465,7 @@ impl EthCoin { pub async fn initialize_global_nft( &self, original_url: &Url, - proxy_auth: &bool, + komodo_proxy: bool, ) -> MmResult { let chain = Chain::from_ticker(self.ticker())?; let ticker = chain.to_nft_ticker().to_string(); @@ -488,27 +473,10 @@ impl EthCoin { let ctx = MmArc::from_weak(&self.ctx) .ok_or_else(|| String::from("No context")) .map_err(EthTokenActivationError::InternalError)?; + let p2p_ctx = P2PContext::fetch_from_mm_arc(&ctx); let conf = coin_conf(&ctx, &ticker); - let web3_instances: Vec = self - .web3_instances - .lock() - .await - .iter() - .map(|node| { - let mut transport = node.web3.transport().clone(); - if let Some(auth) = transport.proxy_auth_validation_generator_as_mut() { - auth.coin_ticker = ticker.clone(); - } - let web3 = Web3::new(transport); - Web3Instance { - web3, - is_parity: node.is_parity, - } - }) - .collect(); - let required_confirmations = AtomicU64::new( conf["required_confirmations"] .as_u64() @@ -522,10 +490,17 @@ impl EthCoin { // Todo: support HD wallet for NFTs, currently we get nfts for enabled address only and there might be some issues when activating NFTs while ETH is activated with HD wallet let my_address = self.derivation_method.single_addr_or_err().await?; - let my_address_str = display_eth_address(&my_address); - let signed_message = nft_signed_message(*proxy_auth, &chain, my_address_str, self.priv_key_policy()).await?; + let proxy_sign = if komodo_proxy { + let uri = Uri::from_str(original_url.as_ref()) + .map_err(|e| EthTokenActivationError::InternalError(e.to_string()))?; + let proxy_sign = RawMessage::sign(p2p_ctx.keypair(), &uri, 0, common::PROXY_REQUEST_EXPIRATION_SEC) + .map_err(|e| EthTokenActivationError::InternalError(e.to_string()))?; + Some(proxy_sign) + } else { + None + }; - let nft_infos = get_nfts_for_activation(&chain, &my_address, original_url, signed_message.as_ref()).await?; + let nft_infos = get_nfts_for_activation(&chain, &my_address, original_url, proxy_sign).await?; let coin_type = EthCoinType::Nft { platform: self.ticker.clone(), }; @@ -544,7 +519,7 @@ impl EthCoin { swap_v2_contracts: self.swap_v2_contracts, fallback_swap_contract: self.fallback_swap_contract, contract_supports_watchers: self.contract_supports_watchers, - web3_instances: AsyncMutex::new(web3_instances), + web3_instances: AsyncMutex::new(self.web3_instances.lock().await.clone()), decimals: self.decimals, history_sync_state: Mutex::new(self.history_sync_state.lock().unwrap().clone()), swap_txfee_policy: Mutex::new(SwapTxFeePolicy::Internal), @@ -565,28 +540,6 @@ impl EthCoin { } } -pub(crate) async fn nft_signed_message( - proxy_auth: bool, - chain: &Chain, - my_address: String, - priv_key_policy: &EthPrivKeyPolicy, -) -> MmResult, GenerateSignedMessageError> { - if !proxy_auth { - return Ok(None); - } - - let secret = priv_key_policy.activated_key_or_err()?.secret().clone(); - let validation_generator = ProxyAuthValidationGenerator { - coin_ticker: chain.to_nft_ticker().to_string(), - secret, - address: my_address, - }; - - let signed_message = EthCoin::generate_proxy_auth_signed_validation(validation_generator)?; - - Ok(Some(signed_message)) -} - /// Activate eth coin from coin config and private key build policy, /// version 2 of the activation function, with no intrinsic tokens creation pub async fn eth_coin_from_conf_and_request_v2( @@ -641,33 +594,9 @@ pub async fn eth_coin_from_conf_and_request_v2( let chain_id = conf["chain_id"].as_u64().ok_or(EthActivationV2Error::ChainIdNotSet)?; let web3_instances = match (req.rpc_mode, &priv_key_policy) { - ( - EthRpcMode::Default, - EthPrivKeyPolicy::Iguana(key_pair) - | EthPrivKeyPolicy::HDWallet { - activated_key: key_pair, - .. - }, - ) => { - let auth_address = key_pair.address(); - let auth_address_str = display_eth_address(&auth_address); - build_web3_instances(ctx, ticker.to_string(), auth_address_str, key_pair, req.nodes.clone()).await? - }, - (EthRpcMode::Default, EthPrivKeyPolicy::Trezor) => { - let crypto_ctx = CryptoCtx::from_ctx(ctx)?; - let secp256k1_key_pair = crypto_ctx.mm2_internal_key_pair(); - let auth_key_pair = KeyPair::from_secret_slice(secp256k1_key_pair.private_ref()) - .map_to_mm(|_| EthActivationV2Error::InternalError("could not get internal keypair".to_string()))?; - let auth_address = auth_key_pair.address(); - let auth_address_str = display_eth_address(&auth_address); - build_web3_instances( - ctx, - ticker.to_string(), - auth_address_str, - &auth_key_pair, - req.nodes.clone(), - ) - .await? + (EthRpcMode::Default, EthPrivKeyPolicy::Iguana(_) | EthPrivKeyPolicy::HDWallet { .. }) + | (EthRpcMode::Default, EthPrivKeyPolicy::Trezor) => { + build_web3_instances(ctx, ticker.to_string(), req.nodes.clone()).await? }, #[cfg(target_arch = "wasm32")] (EthRpcMode::Metamask, EthPrivKeyPolicy::Metamask(_)) => { @@ -855,8 +784,6 @@ pub(crate) async fn build_address_and_priv_key_policy( async fn build_web3_instances( ctx: &MmArc, coin_ticker: String, - address: String, - key_pair: &KeyPair, mut eth_nodes: Vec, ) -> MmResult, EthActivationV2Error> { if eth_nodes.is_empty() { @@ -869,6 +796,7 @@ async fn build_web3_instances( let event_handlers = rpc_event_handlers_for_eth_transport(ctx, coin_ticker.clone()); + let p2p_ctx = P2PContext::fetch_from_mm_arc(ctx); let mut web3_instances = Vec::with_capacity(eth_nodes.len()); for eth_node in eth_nodes { let uri: Uri = eth_node @@ -880,19 +808,12 @@ async fn build_web3_instances( Some("ws") | Some("wss") => { const TMP_SOCKET_CONNECTION: Duration = Duration::from_secs(20); - let node = WebsocketTransportNode { - uri: uri.clone(), - gui_auth: eth_node.gui_auth, - }; + let node = WebsocketTransportNode { uri: uri.clone() }; let mut websocket_transport = WebsocketTransport::with_event_handlers(node, event_handlers.clone()); - if eth_node.gui_auth { - websocket_transport.proxy_auth_validation_generator = Some(ProxyAuthValidationGenerator { - coin_ticker: coin_ticker.clone(), - secret: key_pair.secret().clone(), - address: address.clone(), - }); + if eth_node.komodo_proxy { + websocket_transport.proxy_sign_keypair = Some(p2p_ctx.keypair().clone()); } // Temporarily start the connection loop (we close the connection once we have the client version below). @@ -909,16 +830,17 @@ async fn build_web3_instances( Some("http") | Some("https") => { let node = HttpTransportNode { uri, - gui_auth: eth_node.gui_auth, + komodo_proxy: eth_node.komodo_proxy, }; - build_http_transport( - coin_ticker.clone(), - address.clone(), - key_pair, - node, - event_handlers.clone(), - ) + let komodo_proxy = node.komodo_proxy; + let mut http_transport = HttpTransport::with_event_handlers(node, event_handlers.clone()); + + if komodo_proxy { + http_transport.proxy_sign_keypair = Some(p2p_ctx.keypair().clone()); + } + + Web3Transport::from(http_transport) }, _ => { return MmError::err(EthActivationV2Error::InvalidPayload(format!( @@ -951,28 +873,6 @@ async fn build_web3_instances( Ok(web3_instances) } -fn build_http_transport( - coin_ticker: String, - address: String, - key_pair: &KeyPair, - node: HttpTransportNode, - event_handlers: Vec, -) -> Web3Transport { - use crate::eth::web3_transport::http_transport::HttpTransport; - - let gui_auth = node.gui_auth; - let mut http_transport = HttpTransport::with_event_handlers(node, event_handlers); - - if gui_auth { - http_transport.proxy_auth_validation_generator = Some(ProxyAuthValidationGenerator { - coin_ticker, - secret: key_pair.secret().clone(), - address, - }); - } - Web3Transport::from(http_transport) -} - #[cfg(target_arch = "wasm32")] async fn build_metamask_transport( ctx: &MmArc, diff --git a/mm2src/coins/eth/web3_transport/http_transport.rs b/mm2src/coins/eth/web3_transport/http_transport.rs index 09cdb24b43..5d6ad98a26 100644 --- a/mm2src/coins/eth/web3_transport/http_transport.rs +++ b/mm2src/coins/eth/web3_transport/http_transport.rs @@ -1,9 +1,10 @@ -use crate::eth::web3_transport::generate_auth_header; use crate::eth::{web3_transport::Web3SendOut, RpcTransportEventHandler, RpcTransportEventHandlerShared, Web3RpcError}; -use common::{APPLICATION_JSON, X_AUTH_PAYLOAD}; +use common::APPLICATION_JSON; +use common::X_AUTH_PAYLOAD; use http::header::CONTENT_TYPE; use jsonrpc_core::{Call, Response}; -use mm2_net::transport::{KomodefiProxyAuthValidation, ProxyAuthValidationGenerator}; +use mm2_net::p2p::Keypair; +use proxy_signature::RawMessage; use serde_json::Value as Json; use std::ops::Deref; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; @@ -12,13 +13,6 @@ use web3::error::{Error, TransportError}; use web3::helpers::{build_request, to_result_from_output, to_string}; use web3::{RequestId, Transport}; -#[derive(Clone, Serialize)] -pub struct QuicknodePayload<'a> { - #[serde(flatten)] - pub request: &'a Call, - pub signed_message: KomodefiProxyAuthValidation, -} - /// Deserialize bytes RPC response into `Result`. /// Implementation copied from Web3 HTTP transport pub(crate) fn de_rpc_response(response: T, rpc_url: &str) -> Result @@ -46,13 +40,13 @@ pub struct HttpTransport { pub(crate) last_request_failed: Arc, node: HttpTransportNode, event_handlers: Vec, - pub(crate) proxy_auth_validation_generator: Option, + pub(crate) proxy_sign_keypair: Option, } #[derive(Clone, Debug)] pub struct HttpTransportNode { pub(crate) uri: http::Uri, - pub(crate) gui_auth: bool, + pub(crate) komodo_proxy: bool, } impl HttpTransport { @@ -63,7 +57,7 @@ impl HttpTransport { id: Arc::new(AtomicUsize::new(0)), node, event_handlers: Default::default(), - proxy_auth_validation_generator: None, + proxy_sign_keypair: None, last_request_failed: Arc::new(AtomicBool::new(false)), } } @@ -74,7 +68,7 @@ impl HttpTransport { id: Arc::new(AtomicUsize::new(0)), node, event_handlers, - proxy_auth_validation_generator: None, + proxy_sign_keypair: None, last_request_failed: Arc::new(AtomicBool::new(false)), } } @@ -109,34 +103,32 @@ async fn send_request(request: Call, transport: HttpTransport) -> Result signed_message, - Err(e) => { - return Err(request_failed_error(request, e)); - }, - }; - - transport - .event_handlers - .on_outgoing_request(serialized_request.as_bytes()); + transport.event_handlers.on_outgoing_request(request_bytes); - let mut req = http::Request::new(serialized_request.into_bytes()); + let mut req = http::Request::new(request_bytes.to_owned()); *req.method_mut() = http::Method::POST; *req.uri_mut() = transport.node.uri.clone(); - // Insert auth header if present - if let Some(auth_value) = auth_header { - match HeaderValue::from_str(&auth_value) { - Ok(header_value) => { - req.headers_mut().insert(X_AUTH_PAYLOAD, header_value); - }, - Err(e) => { - return Err(request_failed_error(request, Web3RpcError::Internal(e.to_string()))); - }, - } - } req.headers_mut() .insert(CONTENT_TYPE, HeaderValue::from_static(APPLICATION_JSON)); + + if let Some(proxy_sign_keypair) = &transport.proxy_sign_keypair { + let proxy_sign = RawMessage::sign( + proxy_sign_keypair, + &transport.node.uri, + request_bytes.len(), + common::PROXY_REQUEST_EXPIRATION_SEC, + ) + .map_err(|e| request_failed_error(request.clone(), Web3RpcError::Internal(e.to_string())))?; + + let proxy_sign_serialized = serde_json::to_string(&proxy_sign) + .map_err(|e| request_failed_error(request.clone(), Web3RpcError::Internal(e.to_string())))?; + + req.headers_mut() + .insert(X_AUTH_PAYLOAD, proxy_sign_serialized.parse().unwrap()); + } + let timeout = Timer::sleep(REQUEST_TIMEOUT_S); let req = Box::pin(slurp_req(req)); let rc = select(req, timeout).await; @@ -194,22 +186,30 @@ async fn send_request(request: Call, transport: HttpTransport) -> Result Result { let serialized_request = to_string(&request); - - let auth_header = match generate_auth_header(&transport.proxy_auth_validation_generator, transport.node.gui_auth) { - Ok(signed_message) => signed_message, - Err(e) => { - return Err(request_failed_error( - request, - Web3RpcError::Transport(format!("Server: '{}', error: {}", transport.node.uri, e)), - )); - }, + let request_bytes = serialized_request.as_bytes(); + + let proxy_sign_header = if let Some(proxy_sign_keypair) = &transport.proxy_sign_keypair { + let proxy_sign = RawMessage::sign( + proxy_sign_keypair, + &transport.node.uri, + request_bytes.len(), + common::PROXY_REQUEST_EXPIRATION_SEC, + ) + .map_err(|e| request_failed_error(request.clone(), Web3RpcError::Internal(e.to_string())))?; + + let proxy_sign_serialized = serde_json::to_string(&proxy_sign) + .map_err(|e| request_failed_error(request.clone(), Web3RpcError::Internal(e.to_string())))?; + + Some(proxy_sign_serialized) + } else { + None }; match send_request_once( serialized_request, &transport.node.uri, &transport.event_handlers, - auth_header, + proxy_sign_header, ) .await { @@ -230,7 +230,7 @@ async fn send_request_once( request_payload: String, uri: &http::Uri, event_handlers: &Vec, - auth_header: Option, + proxy_sign_header: Option, ) -> Result { use http::header::ACCEPT; use mm2_net::wasm::http::FetchRequest; @@ -238,16 +238,19 @@ async fn send_request_once( // account for outgoing traffic event_handlers.on_outgoing_request(request_payload.as_bytes()); - let mut fetch_request = FetchRequest::post(&uri.to_string()) + let mut request = FetchRequest::post(&uri.to_string()); + + request = request .cors() .body_utf8(request_payload) .header(ACCEPT.as_str(), APPLICATION_JSON) .header(CONTENT_TYPE.as_str(), APPLICATION_JSON); - // Insert auth header if present - if let Some(auth_value) = auth_header { - fetch_request = fetch_request.header(X_AUTH_PAYLOAD, &auth_value); + + if let Some(proxy_sign_header) = proxy_sign_header { + request = request.header(X_AUTH_PAYLOAD, &proxy_sign_header); } - let (status_code, response_str) = fetch_request + + let (status_code, response_str) = request .request_str() .await .map_err(|e| Error::Transport(TransportError::Message(ERRL!("{:?}", e))))?; diff --git a/mm2src/coins/eth/web3_transport/mod.rs b/mm2src/coins/eth/web3_transport/mod.rs index 7cbd70e81b..e064e6025a 100644 --- a/mm2src/coins/eth/web3_transport/mod.rs +++ b/mm2src/coins/eth/web3_transport/mod.rs @@ -2,15 +2,11 @@ use ethereum_types::U256; use futures::future::BoxFuture; use jsonrpc_core::Call; #[cfg(target_arch = "wasm32")] use mm2_metamask::MetamaskResult; -use mm2_net::transport::{KomodefiProxyAuthValidation, ProxyAuthValidationGenerator}; use serde_json::Value as Json; use serde_json::Value; use std::sync::atomic::Ordering; -use web3::helpers::to_string; use web3::{Error, RequestId, Transport}; -use self::http_transport::QuicknodePayload; -use super::{EthCoin, KomodoDefiAuthMessages, Web3RpcError}; use crate::RpcTransportEventHandlerShared; pub(crate) mod http_transport; @@ -66,15 +62,6 @@ impl Web3Transport { pub fn new_http(node: http_transport::HttpTransportNode) -> Web3Transport { http_transport::HttpTransport::new(node).into() } - - pub fn proxy_auth_validation_generator_as_mut(&mut self) -> Option<&mut ProxyAuthValidationGenerator> { - match self { - Web3Transport::Http(http) => http.proxy_auth_validation_generator.as_mut(), - Web3Transport::Websocket(websocket) => websocket.proxy_auth_validation_generator.as_mut(), - #[cfg(target_arch = "wasm32")] - Web3Transport::Metamask(_) => None, - } - } } impl Transport for Web3Transport { @@ -133,47 +120,3 @@ pub struct FeeHistoryResult { #[serde(rename = "reward")] pub priority_rewards: Option>>, } - -/// Generates a Quicknode payload JSON string by inserting a signed message into the request payload. -pub(super) fn handle_quicknode_payload( - proxy_auth_validation_generator: &Option, - request: &Call, -) -> Result { - let signed_message = generate_signed_message(proxy_auth_validation_generator)?; - - let auth_request = QuicknodePayload { - request, - signed_message, - }; - - Ok(to_string(&auth_request)) -} - -/// Generates a signed message JSON string if proxy authentication is enabled. -pub(crate) fn generate_auth_header( - proxy_auth_validation_generator: &Option, - gui_auth: bool, -) -> Result, Web3RpcError> { - if !gui_auth { - return Ok(None); - } - let signed_message = generate_signed_message(proxy_auth_validation_generator)?; - Ok(Some(serde_json::to_string(&signed_message)?)) -} - -/// Generates a signed message using the provided ProxyAuthValidationGenerator -fn generate_signed_message( - proxy_auth_validation_generator: &Option, -) -> Result { - let generator = proxy_auth_validation_generator - .clone() - .ok_or_else(|| Web3RpcError::Internal("ProxyAuthValidationGenerator is not provided".to_string()))?; - - let signed_message = EthCoin::generate_proxy_auth_signed_validation(generator).map_err(|e| { - Web3RpcError::Internal(format!( - "KomodefiProxyAuthValidation signed message generation failed. Error: {:?}", - e - )) - })?; - Ok(signed_message) -} diff --git a/mm2src/coins/eth/web3_transport/websocket_transport.rs b/mm2src/coins/eth/web3_transport/websocket_transport.rs index 951ed4d2c6..d17ef99d7e 100644 --- a/mm2src/coins/eth/web3_transport/websocket_transport.rs +++ b/mm2src/coins/eth/web3_transport/websocket_transport.rs @@ -5,7 +5,6 @@ //! less bandwidth. This efficiency is achieved by avoiding the handling of TCP handshakes (connection reusability) //! for each request. -use super::handle_quicknode_payload; use super::http_transport::de_rpc_response; use crate::eth::eth_rpc::ETH_RPC_REQUEST_TIMEOUT; use crate::eth::web3_transport::Web3SendOut; @@ -21,7 +20,8 @@ use futures_ticker::Ticker; use futures_util::{FutureExt, SinkExt, StreamExt}; use instant::{Duration, Instant}; use jsonrpc_core::Call; -use mm2_net::transport::ProxyAuthValidationGenerator; +use mm2_net::p2p::Keypair; +use proxy_signature::{ProxySign, RawMessage}; use std::sync::atomic::AtomicBool; use std::sync::{atomic::{AtomicUsize, Ordering}, Arc}; @@ -37,7 +37,6 @@ const KEEPALIVE_DURATION: Duration = Duration::from_secs(10); #[derive(Clone, Debug)] pub(crate) struct WebsocketTransportNode { pub(crate) uri: http::Uri, - pub(crate) gui_auth: bool, } #[derive(Clone, Debug)] @@ -46,7 +45,7 @@ pub struct WebsocketTransport { pub(crate) last_request_failed: Arc, node: WebsocketTransportNode, event_handlers: Vec, - pub(crate) proxy_auth_validation_generator: Option, + pub(crate) proxy_sign_keypair: Option, controller_channel: Arc, connection_guard: Arc>, } @@ -93,7 +92,7 @@ impl WebsocketTransport { } .into(), connection_guard: Arc::new(AsyncMutex::new(())), - proxy_auth_validation_generator: None, + proxy_sign_keypair: None, last_request_failed: Arc::new(AtomicBool::new(false)), } } @@ -340,25 +339,40 @@ async fn send_request( request_id: RequestId, event_handlers: Vec, ) -> Result { - let mut serialized_request = to_string(&request); + /// komodo-defi-proxy expects proxy signatures in the socket messages as they + /// cannot be provided through headers. + #[derive(Serialize)] + struct ProxyWrapper<'a> { + #[serde(flatten)] + pub request: &'a Call, + pub proxy_sign: ProxySign, + } - if transport.node.gui_auth { - match handle_quicknode_payload(&transport.proxy_auth_validation_generator, &request) { - Ok(r) => serialized_request = r, - Err(e) => { - return Err(Error::Transport(TransportError::Message(format!( - "Couldn't generate signed message payload for {:?}. Error: {e}", - request - )))); - }, + let mut serialized_request = to_string(&request); + let request_bytes = serialized_request.as_bytes().to_owned(); + + if let Some(proxy_sign_keypair) = &transport.proxy_sign_keypair { + let proxy_sign = RawMessage::sign( + proxy_sign_keypair, + &transport.node.uri, + request_bytes.len(), + common::PROXY_REQUEST_EXPIRATION_SEC, + ) + .map_err(|e| Error::Transport(TransportError::Message(e.to_string())))?; + + let wrapper = ProxyWrapper { + request: &request, + proxy_sign, }; + + serialized_request = serde_json::to_string(&wrapper)?; } let mut tx = transport.controller_channel.tx.lock().await; let (notification_sender, notification_receiver) = futures::channel::oneshot::channel::>(); - event_handlers.on_outgoing_request(serialized_request.as_bytes()); + event_handlers.on_outgoing_request(&request_bytes); tx.send(ControllerMessage::Request(WsRequest { request_id, diff --git a/mm2src/coins/nft.rs b/mm2src/coins/nft.rs index 50267b96a1..2298ae9648 100644 --- a/mm2src/coins/nft.rs +++ b/mm2src/coins/nft.rs @@ -1,5 +1,8 @@ +use http::Uri; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::{MmError, MmResult}; +use mm2_net::p2p::P2PContext; +use proxy_signature::{ProxySign, RawMessage}; use url::Url; pub(crate) mod nft_errors; @@ -29,7 +32,7 @@ use ethereum_types::{Address, H256}; use futures::compat::Future01CompatExt; use futures::future::try_join_all; use mm2_err_handle::map_to_mm::MapToMmResult; -use mm2_net::transport::{send_post_request_to_uri, KomodefiProxyAuthValidation}; +use mm2_net::transport::send_post_request_to_uri; use mm2_number::BigUint; use regex::Regex; use serde::Deserialize; @@ -42,7 +45,6 @@ use web3::types::TransactionId; #[cfg(not(target_arch = "wasm32"))] use mm2_net::native_http::send_request_to_uri; -use crate::eth::v2_activation::nft_signed_message; #[cfg(target_arch = "wasm32")] use mm2_net::wasm::http::send_request_to_uri; @@ -215,6 +217,7 @@ async fn process_transfers_confirmations( /// sync with the latest data from the source, validates against spam contract addresses and phishing domains. pub async fn update_nft(ctx: MmArc, req: UpdateNftReq) -> MmResult<(), UpdateNftError> { let nft_ctx = NftCtx::from_ctx(&ctx).map_to_mm(GetNftInfoError::Internal)?; + let p2p_ctx = P2PContext::fetch_from_mm_arc(&ctx); let storage = nft_ctx.lock_db().await?; for chain in req.chains.iter() { @@ -237,13 +240,20 @@ pub async fn update_nft(ctx: MmArc, req: UpdateNftReq) -> MmResult<(), UpdateNft }) }, }; - let my_address = eth_coin.my_address()?; - let signed_message = nft_signed_message(req.proxy_auth, chain, my_address, ð_coin.priv_key_policy).await?; + let proxy_sign = if req.komodo_proxy { + let uri = Uri::from_str(req.url.as_ref()).map_err(|e| UpdateNftError::Internal(e.to_string()))?; + let proxy_sign = RawMessage::sign(p2p_ctx.keypair(), &uri, 0, common::PROXY_REQUEST_EXPIRATION_SEC) + .map_err(|e| UpdateNftError::Internal(e.to_string()))?; + Some(proxy_sign) + } else { + None + }; + let wrapper = UrlSignWrapper { chain, orig_url: &req.url, url_antispam: &req.url_antispam, - signed_message: signed_message.as_ref(), + proxy_sign, }; let nft_transfers = get_moralis_nft_transfers(&ctx, from_block, eth_coin, &wrapper).await?; @@ -459,26 +469,23 @@ fn prepare_uri_for_blocklist_endpoint( /// `possible_phishing` flags are set to true. pub async fn refresh_nft_metadata(ctx: MmArc, req: RefreshMetadataReq) -> MmResult<(), UpdateNftError> { let nft_ctx = NftCtx::from_ctx(&ctx).map_to_mm(GetNftInfoError::Internal)?; + let p2p_ctx = P2PContext::fetch_from_mm_arc(&ctx); let storage = nft_ctx.lock_db().await?; - // TODO activate and use global NFT instead of ETH coin after adding enable nft using coin conf support - let coin_enum = lp_coinfind_or_err(&ctx, req.chain.to_ticker()).await?; - let eth_coin = match coin_enum { - MmCoinEnum::EthCoin(eth_coin) => eth_coin, - _ => { - return MmError::err(UpdateNftError::CoinDoesntSupportNft { - coin: coin_enum.ticker().to_owned(), - }) - }, + let proxy_sign = if req.komodo_proxy { + let uri = Uri::from_str(req.url.as_ref()).map_err(|e| UpdateNftError::Internal(e.to_string()))?; + let proxy_sign = RawMessage::sign(p2p_ctx.keypair(), &uri, 0, common::PROXY_REQUEST_EXPIRATION_SEC) + .map_err(|e| UpdateNftError::Internal(e.to_string()))?; + Some(proxy_sign) + } else { + None }; - let my_address = eth_coin.my_address()?; - let signed_message = nft_signed_message(req.proxy_auth, &req.chain, my_address, ð_coin.priv_key_policy).await?; let wrapper = UrlSignWrapper { chain: &req.chain, orig_url: &req.url, url_antispam: &req.url_antispam, - signed_message: signed_message.as_ref(), + proxy_sign, }; let token_address_str = eth_addr_to_hex(&req.token_address); @@ -634,7 +641,7 @@ async fn get_moralis_nft_list(ctx: &MmArc, wrapper: &UrlSignWrapper<'_>) -> MmRe loop { // Create a new URL instance from uri_without_cursor and modify its query to include the cursor if present let uri = format!("{}{}", uri_without_cursor, cursor); - let response = build_and_send_request(uri.as_str(), wrapper.signed_message).await?; + let response = build_and_send_request(uri.as_str(), &wrapper.proxy_sign).await?; if let Some(nfts_list) = response["result"].as_array() { for nft_json in nfts_list { let nft_moralis = NftFromMoralis::deserialize(nft_json)?; @@ -666,7 +673,7 @@ pub(crate) async fn get_nfts_for_activation( chain: &Chain, my_address: &Address, orig_url: &Url, - signed_message: Option<&KomodefiProxyAuthValidation>, + proxy_sign: Option, ) -> MmResult, GetNftInfoError> { let mut nfts_map = HashMap::new(); let uri_without_cursor = construct_moralis_uri_for_nft(orig_url, ð_addr_to_hex(my_address), chain)?; @@ -676,7 +683,7 @@ pub(crate) async fn get_nfts_for_activation( loop { // Create a new URL instance from uri_without_cursor and modify its query to include the cursor if present let uri = format!("{}{}", uri_without_cursor, cursor); - let response = build_and_send_request(uri.as_str(), signed_message).await?; + let response = build_and_send_request(uri.as_str(), &proxy_sign).await?; if let Some(nfts_list) = response["result"].as_array() { process_nft_list_for_activation(nfts_list, chain, &mut nfts_map)?; // if cursor is not null, there are other NFTs on next page, @@ -757,7 +764,7 @@ async fn get_moralis_nft_transfers( loop { // Create a new URL instance from uri_without_cursor and modify its query to include the cursor if present let uri = format!("{}{}", uri_without_cursor, cursor); - let response = build_and_send_request(uri.as_str(), wrapper.signed_message).await?; + let response = build_and_send_request(uri.as_str(), &wrapper.proxy_sign).await?; if let Some(transfer_list) = response["result"].as_array() { process_transfer_list(transfer_list, chain, wallet_address.as_str(), ð_coin, &mut res_list).await?; // if the cursor is not null, there are other NFTs transfers on next page, @@ -899,7 +906,7 @@ async fn get_moralis_metadata( .append_pair(MORALIS_FORMAT_QUERY_NAME, MORALIS_FORMAT_QUERY_VALUE); drop_mutability!(uri); - let response = build_and_send_request(uri.as_str(), wrapper.signed_message).await?; + let response = build_and_send_request(uri.as_str(), &wrapper.proxy_sign).await?; let nft_moralis: NftFromMoralis = serde_json::from_str(&response.to_string())?; let contract_type = match nft_moralis.contract_type { Some(contract_type) => contract_type, @@ -1563,14 +1570,11 @@ struct UrlSignWrapper<'a> { chain: &'a Chain, orig_url: &'a Url, url_antispam: &'a Url, - signed_message: Option<&'a KomodefiProxyAuthValidation>, + proxy_sign: Option, } -async fn build_and_send_request( - uri: &str, - signed_message: Option<&KomodefiProxyAuthValidation>, -) -> MmResult { - let payload = signed_message.map(|msg| serde_json::to_string(&msg)).transpose()?; +async fn build_and_send_request(uri: &str, proxy_sign: &Option) -> MmResult { + let payload = proxy_sign.as_ref().map(|msg| serde_json::to_string(&msg)).transpose()?; let response = send_request_to_uri(uri, payload.as_deref()).await?; Ok(response) } diff --git a/mm2src/coins/nft/nft_structs.rs b/mm2src/coins/nft/nft_structs.rs index 6cc5b13c61..f772b92f56 100644 --- a/mm2src/coins/nft/nft_structs.rs +++ b/mm2src/coins/nft/nft_structs.rs @@ -99,7 +99,7 @@ pub struct RefreshMetadataReq { /// with spam contracts or if domain fields in the fetched metadata match known phishing domains. pub(crate) url_antispam: Url, #[serde(default)] - pub(crate) proxy_auth: bool, + pub(crate) komodo_proxy: bool, } /// Represents blockchains which are supported by NFT feature. @@ -663,7 +663,7 @@ pub struct UpdateNftReq { /// with spam contracts or if domain fields in the fetched metadata match known phishing domains. pub(crate) url_antispam: Url, #[serde(default)] - pub(crate) proxy_auth: bool, + pub(crate) komodo_proxy: bool, } /// Represents a unique identifier for an NFT, consisting of its token address and token ID. diff --git a/mm2src/coins/tendermint/rpc/tendermint_native_rpc.rs b/mm2src/coins/tendermint/rpc/tendermint_native_rpc.rs index 27ae5e7a0e..6b4dfb9ca4 100644 --- a/mm2src/coins/tendermint/rpc/tendermint_native_rpc.rs +++ b/mm2src/coins/tendermint/rpc/tendermint_native_rpc.rs @@ -5,6 +5,8 @@ use cosmrs::tendermint::block::Height; use cosmrs::tendermint::evidence::Evidence; use cosmrs::tendermint::Genesis; use cosmrs::tendermint::Hash; +use http::Uri; +use mm2_net::p2p::Keypair; use serde::{de::DeserializeOwned, Serialize}; use std::fmt; use std::time::Duration; @@ -292,22 +294,25 @@ pub struct HttpClient { impl HttpClient { /// Construct a new Tendermint RPC HTTP/S client connecting to the given /// URL. - pub fn new(url: U) -> Result + pub fn new(url: U, proxy_sign_keypair: Option) -> Result where U: TryInto, { let url = url.try_into()?; Ok(Self { inner: if url.0.is_secure() { - sealed::HttpClient::new_https(url.try_into()?) + sealed::HttpClient::new_https(url.try_into()?, proxy_sign_keypair) } else { - sealed::HttpClient::new_http(url.try_into()?) + sealed::HttpClient::new_http(url.try_into()?, proxy_sign_keypair) }, }) } #[inline] - pub fn uri(&self) -> http::Uri { self.inner.uri() } + pub fn uri(&self) -> Uri { self.inner.uri() } + + #[inline] + pub fn proxy_sign_keypair(&self) -> &Option { self.inner.proxy_sign_keypair() } } #[async_trait] @@ -370,11 +375,15 @@ impl TryFrom for hyper::Uri { mod sealed { use common::log::debug; + use common::X_AUTH_PAYLOAD; + use http::HeaderValue; use hyper::body::Buf; use hyper::client::connect::Connect; use hyper::client::HttpConnector; use hyper::{header, Uri}; use hyper_rustls::{HttpsConnector, HttpsConnectorBuilder}; + use mm2_net::p2p::Keypair; + use proxy_signature::RawMessage; use std::io::Read; use tendermint_rpc::{Error, Response, SimpleRequest}; @@ -392,10 +401,17 @@ mod sealed { pub struct HyperClient { uri: Uri, inner: hyper::Client, + proxy_sign_keypair: Option, } impl HyperClient { - pub fn new(uri: Uri, inner: hyper::Client) -> Self { Self { uri, inner } } + pub fn new(uri: Uri, inner: hyper::Client, proxy_sign_keypair: Option) -> Self { + Self { + uri, + inner, + proxy_sign_keypair, + } + } } impl HyperClient @@ -421,21 +437,41 @@ mod sealed { impl HyperClient { /// Build a request using the given Tendermint RPC request. pub fn build_request(&self, request: R) -> Result, Error> { - let request_body = request.into_json(); + let body_bytes = request.into_json().into_bytes(); + let body_size = body_bytes.len(); let mut request = hyper::Request::builder() .method("POST") .uri(&self.uri) - .body(hyper::Body::from(request_body.into_bytes())) + .body(hyper::Body::from(body_bytes)) .map_err(|e| Error::client_internal(e.to_string()))?; { + let request_uri = request.uri().clone(); let headers = request.headers_mut(); - headers.insert(header::CONTENT_TYPE, "application/json".parse().unwrap()); + headers.insert(header::CONTENT_TYPE, HeaderValue::from_static(common::APPLICATION_JSON)); headers.insert( header::USER_AGENT, format!("tendermint.rs/{}", env!("CARGO_PKG_VERSION")).parse().unwrap(), ); + + if let Some(proxy_sign_keypair) = &self.proxy_sign_keypair { + let proxy_sign = RawMessage::sign( + proxy_sign_keypair, + &request_uri, + body_size, + common::PROXY_REQUEST_EXPIRATION_SEC, + ) + .map_err(|e| Error::client_internal(e.to_string()))?; + + let proxy_sign_serialized = + serde_json::to_string(&proxy_sign).map_err(|e| Error::client_internal(e.to_string()))?; + + let header_value = HeaderValue::from_str(&proxy_sign_serialized) + .map_err(|e| Error::client_internal(e.to_string()))?; + + headers.insert(X_AUTH_PAYLOAD, header_value); + } } Ok(request) @@ -454,10 +490,16 @@ mod sealed { } impl HttpClient { - pub fn new_http(uri: Uri) -> Self { Self::Http(HyperClient::new(uri, hyper::Client::new())) } + pub fn new_http(uri: Uri, proxy_sign_keypair: Option) -> Self { + Self::Http(HyperClient::new(uri, hyper::Client::new(), proxy_sign_keypair)) + } - pub fn new_https(uri: Uri) -> Self { - Self::Https(HyperClient::new(uri, hyper::Client::builder().build(https_connector()))) + pub fn new_https(uri: Uri, proxy_sign_keypair: Option) -> Self { + Self::Https(HyperClient::new( + uri, + hyper::Client::builder().build(https_connector()), + proxy_sign_keypair, + )) } pub async fn perform(&self, request: R) -> Result @@ -476,6 +518,13 @@ mod sealed { HttpClient::Https(client) => client.uri.clone(), } } + + pub fn proxy_sign_keypair(&self) -> &Option { + match self { + HttpClient::Http(client) => &client.proxy_sign_keypair, + HttpClient::Https(client) => &client.proxy_sign_keypair, + } + } } async fn response_to_string(response: hyper::Response) -> Result { diff --git a/mm2src/coins/tendermint/rpc/tendermint_wasm_rpc.rs b/mm2src/coins/tendermint/rpc/tendermint_wasm_rpc.rs index 3909ce2c25..4b5d6a0652 100644 --- a/mm2src/coins/tendermint/rpc/tendermint_wasm_rpc.rs +++ b/mm2src/coins/tendermint/rpc/tendermint_wasm_rpc.rs @@ -1,11 +1,13 @@ -use common::APPLICATION_JSON; +use common::{APPLICATION_JSON, PROXY_REQUEST_EXPIRATION_SEC, X_AUTH_PAYLOAD}; use cosmrs::tendermint::block::Height; use derive_more::Display; use http::header::{ACCEPT, CONTENT_TYPE}; use http::uri::InvalidUri; use http::{StatusCode, Uri}; +use mm2_net::p2p::Keypair; use mm2_net::transport::SlurpError; use mm2_net::wasm::http::FetchRequest; +use proxy_signature::RawMessage; use std::str::FromStr; use tendermint_rpc::endpoint::{abci_info, broadcast}; pub use tendermint_rpc::endpoint::{abci_query::{AbciQuery, Request as AbciRequest}, @@ -20,6 +22,7 @@ use tendermint_rpc::Response; #[derive(Debug, Clone)] pub struct HttpClient { uri: String, + proxy_sign_keypair: Option, } #[derive(Debug, Display)] @@ -35,6 +38,7 @@ impl From for HttpClientInitError { pub enum PerformError { TendermintRpc(TendermintRpcError), Slurp(SlurpError), + Internal(String), #[display(fmt = "Request failed with status code {}, response {}", status_code, response)] StatusCode { status_code: StatusCode, @@ -51,27 +55,43 @@ impl From for PerformError { } impl HttpClient { - pub(crate) fn new(url: &str) -> Result { + pub(crate) fn new(url: &str, proxy_sign_keypair: Option) -> Result { Uri::from_str(url)?; - Ok(HttpClient { uri: url.to_owned() }) + Ok(HttpClient { + uri: url.to_owned(), + proxy_sign_keypair, + }) } #[inline] pub fn uri(&self) -> http::Uri { Uri::from_str(&self.uri).expect("This should never happen.") } + #[inline] + pub fn proxy_sign_keypair(&self) -> &Option { &self.proxy_sign_keypair } + pub(crate) async fn perform(&self, request: R) -> Result where R: SimpleRequest, { - let request_str = request.into_json(); - let (status_code, response_str) = FetchRequest::post(&self.uri) - .cors() - .body_utf8(request_str) - .header(ACCEPT.as_str(), APPLICATION_JSON) - .header(CONTENT_TYPE.as_str(), APPLICATION_JSON) - .request_str() - .await - .map_err(|e| e.into_inner())?; + let body_bytes = request.into_json().into_bytes(); + let body_size = body_bytes.len(); + + let mut req = FetchRequest::post(&self.uri).cors().body_bytes(body_bytes); + req = req.header(ACCEPT.as_str(), APPLICATION_JSON); + req = req.header(CONTENT_TYPE.as_str(), APPLICATION_JSON); + + if let Some(proxy_sign_keypair) = &self.proxy_sign_keypair { + let proxy_sign = RawMessage::sign(proxy_sign_keypair, &self.uri(), body_size, PROXY_REQUEST_EXPIRATION_SEC) + .map_err(|e| PerformError::Internal(e.to_string()))?; + + let proxy_sign_serialized = + serde_json::to_string(&proxy_sign).map_err(|e| PerformError::Internal(e.to_string()))?; + + req = req.header(X_AUTH_PAYLOAD, &proxy_sign_serialized); + } + + let (status_code, response_str) = req.request_str().await.map_err(|e| e.into_inner())?; + if !status_code.is_success() { return Err(PerformError::StatusCode { status_code, @@ -118,7 +138,7 @@ mod tests { #[wasm_bindgen_test] async fn test_get_abci_info() { - let client = HttpClient::new("https://rpc.sentry-02.theta-testnet.polypore.xyz").unwrap(); + let client = HttpClient::new("https://rpc.sentry-02.theta-testnet.polypore.xyz", None).unwrap(); client.abci_info().await.unwrap(); } } diff --git a/mm2src/coins/tendermint/tendermint_balance_events.rs b/mm2src/coins/tendermint/tendermint_balance_events.rs index 7b8cd8ca03..a7a5fd94cf 100644 --- a/mm2src/coins/tendermint/tendermint_balance_events.rs +++ b/mm2src/coins/tendermint/tendermint_balance_events.rs @@ -1,14 +1,15 @@ use async_trait::async_trait; use common::{executor::{AbortSettings, SpawnAbortable}, - http_uri_to_ws_address, log}; + http_uri_to_ws_address, log, PROXY_REQUEST_EXPIRATION_SEC}; use futures::channel::oneshot::{self, Receiver, Sender}; use futures_util::{SinkExt, StreamExt}; -use jsonrpc_core::MethodCall; use jsonrpc_core::{Id as RpcId, Params as RpcParams, Value as RpcValue, Version as RpcVersion}; use mm2_core::mm_ctx::MmArc; use mm2_event_stream::{behaviour::{EventBehaviour, EventInitStatus}, ErrorEventName, Event, EventName, EventStreamConfiguration}; +use mm2_net::p2p::Keypair; use mm2_number::BigDecimal; +use proxy_signature::RawMessage; use std::collections::{HashMap, HashSet}; use super::TendermintCoin; @@ -21,15 +22,28 @@ impl EventBehaviour for TendermintCoin { fn error_event_name() -> ErrorEventName { ErrorEventName::CoinBalanceError } async fn handle(self, _interval: f64, tx: oneshot::Sender) { - fn generate_subscription_query(query_filter: String) -> String { + fn generate_subscription_query( + query_filter: String, + proxy_sign_keypair: &Option, + uri: &http::Uri, + ) -> String { let mut params = serde_json::Map::with_capacity(1); params.insert("query".to_owned(), RpcValue::String(query_filter)); - let q = MethodCall { - id: RpcId::Num(0), - jsonrpc: Some(RpcVersion::V2), - method: "subscribe".to_owned(), - params: RpcParams::Map(params), + let mut q = json!({ + "id": RpcId::Num(0), + "jsonrpc": Some(RpcVersion::V2), + "method": "subscribe".to_owned(), + "params": RpcParams::Map(params), + }); + + const BODY_SIZE: usize = 0; + if let Some(proxy_sign_keypair) = proxy_sign_keypair { + if let Ok(proxy_sign) = + RawMessage::sign(proxy_sign_keypair, uri, BODY_SIZE, PROXY_REQUEST_EXPIRATION_SEC) + { + q["proxy_sign"] = serde_json::to_value(proxy_sign).expect("This should never happen"); + } }; serde_json::to_string(&q).expect("This should never happen") @@ -48,25 +62,33 @@ impl EventBehaviour for TendermintCoin { let account_id = self.account_id.to_string(); let mut current_balances: HashMap = HashMap::new(); - let receiver_q = generate_subscription_query(format!("coin_received.receiver = '{}'", account_id)); - let receiver_q = tokio_tungstenite_wasm::Message::Text(receiver_q); - - let spender_q = generate_subscription_query(format!("coin_spent.spender = '{}'", account_id)); - let spender_q = tokio_tungstenite_wasm::Message::Text(spender_q); - tx.send(EventInitStatus::Success) .expect("Receiver is dropped, which should never happen."); loop { - let node_uri = match self.rpc_client().await { - Ok(client) => client.uri(), + let client = match self.rpc_client().await { + Ok(client) => client, Err(e) => { log::error!("{e}"); continue; }, }; - let socket_address = format!("{}/{}", http_uri_to_ws_address(node_uri), "websocket"); + let receiver_q = generate_subscription_query( + format!("coin_received.receiver = '{}'", account_id), + client.proxy_sign_keypair(), + &client.uri(), + ); + let receiver_q = tokio_tungstenite_wasm::Message::Text(receiver_q); + + let spender_q = generate_subscription_query( + format!("coin_spent.spender = '{}'", account_id), + client.proxy_sign_keypair(), + &client.uri(), + ); + let spender_q = tokio_tungstenite_wasm::Message::Text(spender_q); + + let socket_address = format!("{}/{}", http_uri_to_ws_address(client.uri()), "websocket"); let mut wsocket = match tokio_tungstenite_wasm::connect(&socket_address).await { Ok(ws) => ws, diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index f3854ba4ee..8049c6e121 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -65,6 +65,7 @@ use keys::{KeyPair, Public}; use mm2_core::mm_ctx::{MmArc, MmWeak}; use mm2_err_handle::prelude::*; use mm2_git::{FileMetadata, GitController, GithubClient, RepositoryOperations, GITHUB_API_URI}; +use mm2_net::p2p::P2PContext; use mm2_number::MmNumber; use parking_lot::Mutex as PaMutex; use primitives::hash::H256; @@ -133,6 +134,22 @@ impl TendermintKeyPair { } } +#[derive(Clone, Deserialize)] +pub struct RpcNode { + url: String, + komodo_proxy: bool, +} + +impl RpcNode { + #[cfg(test)] + fn for_test(url: &str) -> Self { + Self { + url: url.to_string(), + komodo_proxy: false, + } + } +} + #[async_trait] pub trait TendermintCommons { fn platform_denom(&self) -> &Denom; @@ -616,12 +633,12 @@ impl TendermintCoin { ticker: String, conf: TendermintConf, protocol_info: TendermintProtocolInfo, - rpc_urls: Vec, + nodes: Vec, tx_history: bool, activation_policy: TendermintActivationPolicy, is_keplr_from_ledger: bool, ) -> MmResult { - if rpc_urls.is_empty() { + if nodes.is_empty() { return MmError::err(TendermintInitError { ticker, kind: TendermintInitErrorKind::EmptyRpcUrls, @@ -635,7 +652,7 @@ impl TendermintCoin { kind: TendermintInitErrorKind::CouldNotGenerateAccountId(e.to_string()), })?; - let rpc_clients = clients_from_urls(rpc_urls.as_ref()).mm_err(|kind| TendermintInitError { + let rpc_clients = clients_from_urls(ctx, nodes).mm_err(|kind| TendermintInitError { ticker: ticker.clone(), kind, })?; @@ -2064,18 +2081,28 @@ impl TendermintCoin { } } -fn clients_from_urls(rpc_urls: &[String]) -> MmResult, TendermintInitErrorKind> { - if rpc_urls.is_empty() { +fn clients_from_urls(ctx: &MmArc, nodes: Vec) -> MmResult, TendermintInitErrorKind> { + if nodes.is_empty() { return MmError::err(TendermintInitErrorKind::EmptyRpcUrls); } + + let p2p_keypair = if nodes.iter().any(|n| n.komodo_proxy) { + let p2p_ctx = P2PContext::fetch_from_mm_arc(ctx); + Some(p2p_ctx.keypair().clone()) + } else { + None + }; + let mut clients = Vec::new(); let mut errors = Vec::new(); + // check that all urls are valid // keep all invalid urls in one vector to show all of them in error - for url in rpc_urls.iter() { - match HttpClient::new(url.as_str()) { + for node in nodes.iter() { + let proxy_sign_keypair = if node.komodo_proxy { p2p_keypair.clone() } else { None }; + match HttpClient::new(node.url.as_str(), proxy_sign_keypair) { Ok(client) => clients.push(client), - Err(e) => errors.push(format!("Url {} is invalid, got error {}", url, e)), + Err(e) => errors.push(format!("Url {} is invalid, got error {}", node.url, e)), } } drop_mutability!(clients); @@ -3361,7 +3388,7 @@ pub mod tendermint_coin_tests { #[test] fn test_htlc_create_and_claim() { - let rpc_urls = vec![IRIS_TESTNET_RPC_URL.to_string()]; + let nodes = vec![RpcNode::for_test(IRIS_TESTNET_RPC_URL)]; let protocol_conf = get_iris_protocol(); @@ -3382,7 +3409,7 @@ pub mod tendermint_coin_tests { "IRIS".to_string(), conf, protocol_conf, - rpc_urls, + nodes, false, activation_policy, false, @@ -3430,7 +3457,7 @@ pub mod tendermint_coin_tests { fee, timeout_height, TX_DEFAULT_MEMO.into(), - Duration::from_secs(10), + Duration::from_secs(20), ); block_on(async { send_tx_fut.await.unwrap(); @@ -3487,7 +3514,7 @@ pub mod tendermint_coin_tests { #[test] fn try_query_claim_htlc_txs_and_get_secret() { - let rpc_urls = vec![IRIS_TESTNET_RPC_URL.to_string()]; + let nodes = vec![RpcNode::for_test(IRIS_TESTNET_RPC_URL)]; let protocol_conf = get_iris_usdc_ibc_protocol(); @@ -3508,7 +3535,7 @@ pub mod tendermint_coin_tests { "USDC-IBC".to_string(), conf, protocol_conf, - rpc_urls, + nodes, false, activation_policy, false, @@ -3550,7 +3577,7 @@ pub mod tendermint_coin_tests { #[test] fn wait_for_tx_spend_test() { - let rpc_urls = vec![IRIS_TESTNET_RPC_URL.to_string()]; + let nodes = vec![RpcNode::for_test(IRIS_TESTNET_RPC_URL)]; let protocol_conf = get_iris_usdc_ibc_protocol(); @@ -3571,7 +3598,7 @@ pub mod tendermint_coin_tests { "USDC-IBC".to_string(), conf, protocol_conf, - rpc_urls, + nodes, false, activation_policy, false, @@ -3624,7 +3651,7 @@ pub mod tendermint_coin_tests { #[test] fn validate_taker_fee_test() { - let rpc_urls = vec![IRIS_TESTNET_RPC_URL.to_string()]; + let nodes = vec![RpcNode::for_test(IRIS_TESTNET_RPC_URL)]; let protocol_conf = get_iris_protocol(); @@ -3645,7 +3672,7 @@ pub mod tendermint_coin_tests { "IRIS-TEST".to_string(), conf, protocol_conf, - rpc_urls, + nodes, false, activation_policy, false, @@ -3821,7 +3848,7 @@ pub mod tendermint_coin_tests { #[test] fn validate_payment_test() { - let rpc_urls = vec![IRIS_TESTNET_RPC_URL.to_string()]; + let nodes = vec![RpcNode::for_test(IRIS_TESTNET_RPC_URL)]; let protocol_conf = get_iris_protocol(); @@ -3842,7 +3869,7 @@ pub mod tendermint_coin_tests { "IRIS-TEST".to_string(), conf, protocol_conf, - rpc_urls, + nodes, false, activation_policy, false, @@ -3904,7 +3931,7 @@ pub mod tendermint_coin_tests { #[test] fn test_search_for_swap_tx_spend_spent() { - let rpc_urls = vec![IRIS_TESTNET_RPC_URL.to_string()]; + let nodes = vec![RpcNode::for_test(IRIS_TESTNET_RPC_URL)]; let protocol_conf = get_iris_protocol(); @@ -3925,7 +3952,7 @@ pub mod tendermint_coin_tests { "IRIS-TEST".to_string(), conf, protocol_conf, - rpc_urls, + nodes, false, activation_policy, false, @@ -3980,7 +4007,7 @@ pub mod tendermint_coin_tests { #[test] fn test_search_for_swap_tx_spend_refunded() { - let rpc_urls = vec![IRIS_TESTNET_RPC_URL.to_string()]; + let nodes = vec![RpcNode::for_test(IRIS_TESTNET_RPC_URL)]; let protocol_conf = get_iris_protocol(); @@ -4001,7 +4028,7 @@ pub mod tendermint_coin_tests { "IRIS-TEST".to_string(), conf, protocol_conf, - rpc_urls, + nodes, false, activation_policy, false, @@ -4054,7 +4081,7 @@ pub mod tendermint_coin_tests { #[test] fn test_get_tx_status_code_or_none() { - let rpc_urls = vec![IRIS_TESTNET_RPC_URL.to_string()]; + let nodes = vec![RpcNode::for_test(IRIS_TESTNET_RPC_URL)]; let protocol_conf = get_iris_usdc_ibc_protocol(); let conf = TendermintConf { @@ -4073,7 +4100,7 @@ pub mod tendermint_coin_tests { "USDC-IBC".to_string(), conf, protocol_conf, - rpc_urls, + nodes, false, activation_policy, false, @@ -4109,7 +4136,7 @@ pub mod tendermint_coin_tests { fn test_wait_for_confirmations() { const CHECK_INTERVAL: u64 = 2; - let rpc_urls = vec![IRIS_TESTNET_RPC_URL.to_string()]; + let nodes = vec![RpcNode::for_test(IRIS_TESTNET_RPC_URL)]; let protocol_conf = get_iris_usdc_ibc_protocol(); let conf = TendermintConf { @@ -4128,7 +4155,7 @@ pub mod tendermint_coin_tests { "USDC-IBC".to_string(), conf, protocol_conf, - rpc_urls, + nodes, false, activation_policy, false, diff --git a/mm2src/coins_activation/src/erc20_token_activation.rs b/mm2src/coins_activation/src/erc20_token_activation.rs index 8ce240b8a5..0f7f4edbd9 100644 --- a/mm2src/coins_activation/src/erc20_token_activation.rs +++ b/mm2src/coins_activation/src/erc20_token_activation.rs @@ -176,8 +176,8 @@ impl TokenActivationOps for EthCoin { )); } let nft_global = match &nft_init_params.provider { - NftProviderEnum::Moralis { url, proxy_auth } => { - platform_coin.initialize_global_nft(url, proxy_auth).await? + NftProviderEnum::Moralis { url, komodo_proxy } => { + platform_coin.initialize_global_nft(url, *komodo_proxy).await? }, }; let nfts = nft_global.nfts_infos.lock().await.clone(); diff --git a/mm2src/coins_activation/src/eth_with_token_activation.rs b/mm2src/coins_activation/src/eth_with_token_activation.rs index acb791a2d5..8cee5094f2 100644 --- a/mm2src/coins_activation/src/eth_with_token_activation.rs +++ b/mm2src/coins_activation/src/eth_with_token_activation.rs @@ -291,7 +291,7 @@ impl PlatformCoinWithTokensActivationOps for EthCoin { ) -> Result, MmError> { let (url, proxy_auth) = match &activation_request.nft_req { Some(nft_req) => match &nft_req.provider { - NftProviderEnum::Moralis { url, proxy_auth } => (url, proxy_auth), + NftProviderEnum::Moralis { url, komodo_proxy } => (url, *komodo_proxy), }, None => return Ok(None), }; diff --git a/mm2src/coins_activation/src/tendermint_with_assets_activation.rs b/mm2src/coins_activation/src/tendermint_with_assets_activation.rs index 5a79bbeb6e..349e37b23d 100644 --- a/mm2src/coins_activation/src/tendermint_with_assets_activation.rs +++ b/mm2src/coins_activation/src/tendermint_with_assets_activation.rs @@ -11,9 +11,9 @@ use async_trait::async_trait; use coins::hd_wallet::HDPathAccountToAddressId; use coins::my_tx_history_v2::TxHistoryStorage; use coins::tendermint::tendermint_tx_history_v2::tendermint_history_loop; -use coins::tendermint::{tendermint_priv_key_policy, TendermintActivationPolicy, TendermintCoin, TendermintCommons, - TendermintConf, TendermintInitError, TendermintInitErrorKind, TendermintProtocolInfo, - TendermintPublicKey, TendermintToken, TendermintTokenActivationParams, +use coins::tendermint::{tendermint_priv_key_policy, RpcNode, TendermintActivationPolicy, TendermintCoin, + TendermintCommons, TendermintConf, TendermintInitError, TendermintInitErrorKind, + TendermintProtocolInfo, TendermintPublicKey, TendermintToken, TendermintTokenActivationParams, TendermintTokenInitError, TendermintTokenProtocolInfo}; use coins::{CoinBalance, CoinProtocol, MarketCoinOps, MmCoin, MmCoinEnum, PrivKeyBuildPolicy}; use common::executor::{AbortSettings, SpawnAbortable}; @@ -40,7 +40,7 @@ impl RegisterTokenInfo for TendermintCoin { #[derive(Clone, Deserialize)] pub struct TendermintActivationParams { - rpc_urls: Vec, + nodes: Vec, pub tokens_params: Vec>, #[serde(default)] tx_history: bool, @@ -265,7 +265,7 @@ impl PlatformCoinWithTokensActivationOps for TendermintCoin { ticker, conf, protocol_conf, - activation_request.rpc_urls, + activation_request.nodes, activation_request.tx_history, activation_policy, is_keplr_from_ledger, diff --git a/mm2src/common/Cargo.toml b/mm2src/common/Cargo.toml index 6549d02cc1..6a5395b360 100644 --- a/mm2src/common/Cargo.toml +++ b/mm2src/common/Cargo.toml @@ -25,6 +25,7 @@ fnv = "1.0.6" futures01 = { version = "0.1", package = "futures" } futures = { version = "0.3", package = "futures", features = ["compat", "async-await", "thread-pool"] } futures-timer = "3.0" +gstuff = "0.7" hex = "0.4.2" http = "0.2" http-body = "0.1" @@ -49,7 +50,6 @@ instant = { version = "0.1.12" } [target.'cfg(target_arch = "wasm32")'.dependencies] chrono = { version = "0.4", features = ["wasmbind"] } -gstuff = { version = "0.7", features = ["nightly"] } instant = { version = "0.1.12", features = ["wasm-bindgen"] } js-sys = "0.3.27" serde_repr = "0.1.6" @@ -62,7 +62,6 @@ web-sys = { version = "0.3.55", features = ["console", "CloseEvent", "DomExcepti [target.'cfg(not(target_arch = "wasm32"))'.dependencies] anyhow = "1.0" chrono = "0.4" -gstuff = { version = "0.7", features = ["nightly"] } hyper = { version = "0.14.26", features = ["client", "http2", "server", "tcp"] } # using webpki-tokio to avoid rejecting valid certificates # got "invalid certificate: UnknownIssuer" for https://ropsten.infura.io on iOS using default-features @@ -79,4 +78,4 @@ findshlibs = "0.5" [build-dependencies] cc = "1.0" -gstuff = { version = "0.7", features = ["nightly"] } +gstuff = "0.7" diff --git a/mm2src/common/common.rs b/mm2src/common/common.rs index 6d1c03d8a1..c98fe610d2 100644 --- a/mm2src/common/common.rs +++ b/mm2src/common/common.rs @@ -204,6 +204,8 @@ pub const SATOSHIS: u64 = 100_000_000; pub const DEX_FEE_ADDR_PUBKEY: &str = "03bc2c7ba671bae4a6fc835244c9762b41647b9827d4780a89a949b984a8ddcc06"; +pub const PROXY_REQUEST_EXPIRATION_SEC: i64 = 15; + lazy_static! { pub static ref DEX_FEE_ADDR_RAW_PUBKEY: Vec = hex::decode(DEX_FEE_ADDR_PUBKEY).expect("DEX_FEE_ADDR_PUBKEY is expected to be a hexadecimal string"); diff --git a/mm2src/mm2_core/src/mm_ctx.rs b/mm2src/mm2_core/src/mm_ctx.rs index af0a6adece..e891f0b649 100644 --- a/mm2src/mm2_core/src/mm_ctx.rs +++ b/mm2src/mm2_core/src/mm_ctx.rs @@ -94,7 +94,6 @@ pub struct MmCtx { pub dispatcher_ctx: Mutex>>, pub message_service_ctx: Mutex>>, pub p2p_ctx: Mutex>>, - pub peer_id: Constructible, pub account_ctx: Mutex>>, /// The context belonging to the `coins` crate: `CoinsContext`. pub coins_ctx: Mutex>>, @@ -164,7 +163,6 @@ impl MmCtx { dispatcher_ctx: Mutex::new(None), message_service_ctx: Mutex::new(None), p2p_ctx: Mutex::new(None), - peer_id: Constructible::default(), account_ctx: Mutex::new(None), coins_ctx: Mutex::new(None), coins_activation_ctx: Mutex::new(None), diff --git a/mm2src/mm2_main/src/lp_native_dex.rs b/mm2src/mm2_main/src/lp_native_dex.rs index 9ab5d97809..3b73947ffb 100644 --- a/mm2src/mm2_main/src/lp_native_dex.rs +++ b/mm2src/mm2_main/src/lp_native_dex.rs @@ -29,7 +29,7 @@ use mm2_core::mm_ctx::{MmArc, MmCtx}; use mm2_err_handle::common_errors::InternalError; use mm2_err_handle::prelude::*; use mm2_event_stream::behaviour::{EventBehaviour, EventInitStatus}; -use mm2_libp2p::behaviours::atomicdex::{GossipsubConfig, DEPRECATED_NETID_LIST}; +use mm2_libp2p::behaviours::atomicdex::{generate_ed25519_keypair, GossipsubConfig, DEPRECATED_NETID_LIST}; use mm2_libp2p::{spawn_gossipsub, AdexBehaviourError, NodeType, RelayAddress, RelayAddressError, SeedNodeInfo, SwarmRuntime, WssCerts}; use mm2_metrics::mm_gauge; @@ -558,6 +558,20 @@ async fn kick_start(ctx: MmArc) -> MmInitResult<()> { Ok(()) } +fn get_p2p_key(ctx: &MmArc, i_am_seed: bool) -> P2PResult<[u8; 32]> { + // TODO: Use persistent peer ID regardless the node type. + if i_am_seed { + if let Ok(crypto_ctx) = CryptoCtx::from_ctx(ctx) { + let key = sha256(crypto_ctx.mm2_internal_privkey_slice()); + return Ok(key.take()); + } + } + + let mut p2p_key = [0; 32]; + common::os_rng(&mut p2p_key).map_err(|e| P2PInitError::Internal(e.to_string()))?; + Ok(p2p_key) +} + pub async fn init_p2p(ctx: MmArc) -> P2PResult<()> { let i_am_seed = ctx.conf["i_am_seed"].as_bool().unwrap_or(false); let netid = ctx.netid(); @@ -569,13 +583,8 @@ pub async fn init_p2p(ctx: MmArc) -> P2PResult<()> { let seednodes = seednodes(&ctx)?; let ctx_on_poll = ctx.clone(); - let force_p2p_key = if i_am_seed { - let crypto_ctx = CryptoCtx::from_ctx(&ctx).mm_err(|e| P2PInitError::Internal(e.to_string()))?; - let key = sha256(crypto_ctx.mm2_internal_privkey_slice()); - Some(key.take()) - } else { - None - }; + + let p2p_key = get_p2p_key(&ctx, i_am_seed)?; let node_type = if i_am_seed { relay_node_type(&ctx).await? @@ -590,9 +599,8 @@ pub async fn init_p2p(ctx: MmArc) -> P2PResult<()> { .try_into() .unwrap_or(usize::MAX); - let mut gossipsub_config = GossipsubConfig::new(netid, spawner, node_type); + let mut gossipsub_config = GossipsubConfig::new(netid, spawner, node_type, p2p_key); gossipsub_config.to_dial(seednodes); - gossipsub_config.force_key(force_p2p_key); gossipsub_config.max_num_streams(max_num_streams); let spawn_result = spawn_gossipsub(gossipsub_config, move |swarm| { @@ -625,9 +633,9 @@ pub async fn init_p2p(ctx: MmArc) -> P2PResult<()> { ); }) .await; - let (cmd_tx, event_rx, peer_id) = spawn_result?; - ctx.peer_id.pin(peer_id.to_string()).map_to_mm(P2PInitError::Internal)?; - let p2p_context = P2PContext::new(cmd_tx); + let (cmd_tx, event_rx, _peer_id) = spawn_result?; + + let p2p_context = P2PContext::new(cmd_tx, generate_ed25519_keypair(p2p_key)); p2p_context.store_to_mm_arc(&ctx); let fut = p2p_event_process_loop(ctx.weak(), event_rx, i_am_seed); diff --git a/mm2src/mm2_main/src/ordermatch_tests.rs b/mm2src/mm2_main/src/ordermatch_tests.rs index 0faa806f33..38d17af4ed 100644 --- a/mm2src/mm2_main/src/ordermatch_tests.rs +++ b/mm2src/mm2_main/src/ordermatch_tests.rs @@ -6,6 +6,7 @@ use crypto::privkey::key_pair_from_seed; use db_common::sqlite::rusqlite::Connection; use futures::{channel::mpsc, StreamExt}; use mm2_core::mm_ctx::{MmArc, MmCtx}; +use mm2_libp2p::behaviours::atomicdex::generate_ed25519_keypair; use mm2_libp2p::AdexBehaviourCmd; use mm2_libp2p::{decode_message, PeerId}; use mm2_net::p2p::P2PContext; @@ -938,7 +939,14 @@ fn test_taker_order_cancellable() { fn prepare_for_cancel_by(ctx: &MmArc) -> mpsc::Receiver { let (tx, rx) = mpsc::channel(10); - let p2p_ctx = P2PContext::new(tx); + + let p2p_key = { + let crypto_ctx = CryptoCtx::from_ctx(ctx).unwrap(); + let key = bitcrypto::sha256(crypto_ctx.mm2_internal_privkey_slice()); + key.take() + }; + + let p2p_ctx = P2PContext::new(tx, generate_ed25519_keypair(p2p_key)); p2p_ctx.store_to_mm_arc(ctx); let ordermatch_ctx = OrdermatchContext::from_ctx(ctx).unwrap(); @@ -1671,7 +1679,14 @@ fn pubkey_and_secret_for_test(passphrase: &str) -> (String, [u8; 32]) { fn init_p2p_context(ctx: &MmArc) -> (mpsc::Sender, mpsc::Receiver) { let (cmd_tx, cmd_rx) = mpsc::channel(10); - let p2p_context = P2PContext::new(cmd_tx.clone()); + + let p2p_key = { + let crypto_ctx = CryptoCtx::from_ctx(ctx).unwrap(); + let key = bitcrypto::sha256(crypto_ctx.mm2_internal_privkey_slice()); + key.take() + }; + + let p2p_context = P2PContext::new(cmd_tx.clone(), generate_ed25519_keypair(p2p_key)); p2p_context.store_to_mm_arc(ctx); (cmd_tx, cmd_rx) } 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 9e7140d829..2769539d9b 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 @@ -372,7 +372,9 @@ pub async fn get_relay_mesh(ctx: MmArc) -> Result>, String> { } pub async fn get_my_peer_id(ctx: MmArc) -> Result>, String> { - let peer_id = try_s!(ctx.peer_id.ok_or("Peer ID is not initialized")); + let p2p_ctx = P2PContext::fetch_from_mm_arc(&ctx); + let peer_id = p2p_ctx.peer_id().to_string(); + let result = json!({ "result": peer_id, }); diff --git a/mm2src/mm2_net/Cargo.toml b/mm2src/mm2_net/Cargo.toml index 844f07e1f3..d92e0c08b6 100644 --- a/mm2src/mm2_net/Cargo.toml +++ b/mm2src/mm2_net/Cargo.toml @@ -19,6 +19,7 @@ common = { path = "../common" } derive_more = "0.99" ethkey = { git = "https://github.com/KomodoPlatform/mm2-parity-ethereum.git", rev = "mm2-v2.1.1" } futures = { version = "0.3", package = "futures", features = ["compat", "async-await", "thread-pool"] } +gstuff = "0.7" http = "0.2" lazy_static = "1.4" mm2_core = { path = "../mm2_core" } @@ -35,7 +36,6 @@ thiserror = "1.0.30" [target.'cfg(target_arch = "wasm32")'.dependencies] base64 = "0.21.7" futures-util = "0.3" -gstuff = { version = "0.7", features = ["nightly"] } mm2_state_machine = { path = "../mm2_state_machine"} http-body = "0.4" httparse = "1.8.0" @@ -55,7 +55,6 @@ web-sys = { version = "0.3.55", features = ["console", "CloseEvent", "DomExcepti [target.'cfg(not(target_arch = "wasm32"))'.dependencies] futures-util = { version = "0.3" } hyper = { version = "0.14.26", features = ["client", "http2", "server", "tcp", "stream"] } -gstuff = { version = "0.7", features = ["nightly"] } rustls = { version = "0.21", default-features = false } tokio = { version = "1.20" } tokio-rustls = { version = "0.24", default-features = false } diff --git a/mm2src/mm2_net/src/p2p.rs b/mm2src/mm2_net/src/p2p.rs index d76cd550bd..30625cbeb5 100644 --- a/mm2src/mm2_net/src/p2p.rs +++ b/mm2src/mm2_net/src/p2p.rs @@ -1,20 +1,32 @@ use mm2_core::mm_ctx::MmArc; use mm2_libp2p::behaviours::atomicdex::AdexCmdTx; +use mm2_libp2p::PeerId; use parking_lot::Mutex; use std::sync::Arc; +pub use mm2_libp2p::Keypair; + pub struct P2PContext { /// Using Mutex helps to prevent cloning which can actually result to channel being unbounded in case of using 1 tx clone per 1 message. pub cmd_tx: Mutex, + /// Host's keypair used for address derivation of peer and message signing. + keypair: Keypair, } impl P2PContext { - pub fn new(cmd_tx: AdexCmdTx) -> Self { + pub fn new(cmd_tx: AdexCmdTx, keypair: Keypair) -> Self { P2PContext { cmd_tx: Mutex::new(cmd_tx), + keypair, } } + #[inline(always)] + pub fn keypair(&self) -> &Keypair { &self.keypair } + + #[inline(always)] + pub fn peer_id(&self) -> PeerId { self.keypair.public().to_peer_id() } + pub fn store_to_mm_arc(self, ctx: &MmArc) { *ctx.p2p_ctx.lock().unwrap() = Some(Arc::new(self)) } pub fn fetch_from_mm_arc(ctx: &MmArc) -> Arc { diff --git a/mm2src/mm2_net/src/transport.rs b/mm2src/mm2_net/src/transport.rs index dd966feb84..750d2561c2 100644 --- a/mm2src/mm2_net/src/transport.rs +++ b/mm2src/mm2_net/src/transport.rs @@ -1,6 +1,5 @@ use common::jsonrpc_client::JsonRpcErrorType; use derive_more::Display; -use ethkey::Secret; use http::{HeaderMap, StatusCode}; use mm2_err_handle::prelude::*; use serde::{Deserialize, Serialize}; @@ -69,23 +68,6 @@ where }) } -#[derive(Clone, Debug)] -pub struct ProxyAuthValidationGenerator { - pub coin_ticker: String, - pub secret: Secret, - pub address: String, -} - -/// Proxy-auth specific data-type that needed in order to perform proxy-auth calls. -/// Represents a signed message used for authenticating and validating requests processed by the proxy. -#[derive(Clone, Serialize)] -pub struct KomodefiProxyAuthValidation { - pub coin_ticker: String, - pub address: String, - pub timestamp_message: i64, - pub signature: String, -} - /// Errors encountered when making HTTP requests to fetch information from a URI. #[derive(Clone, Debug, Deserialize, Display, PartialEq, Serialize)] pub enum GetInfoFromUriError { diff --git a/mm2src/mm2_p2p/src/behaviours/atomicdex.rs b/mm2src/mm2_p2p/src/behaviours/atomicdex.rs index c1bcea5476..db907711ba 100644 --- a/mm2src/mm2_p2p/src/behaviours/atomicdex.rs +++ b/mm2src/mm2_p2p/src/behaviours/atomicdex.rs @@ -18,7 +18,6 @@ use libp2p::{identity, noise, PeerId, Swarm}; use libp2p::{Multiaddr, Transport}; use log::{debug, error, info}; use rand::seq::SliceRandom; -use rand::Rng; use std::collections::hash_map::DefaultHasher; use std::collections::HashMap; use std::hash::{Hash, Hasher}; @@ -563,18 +562,10 @@ impl From for AdexBehaviourError { fn from(e: PublishError) -> Self { AdexBehaviourError::PublishError(e) } } -fn generate_ed25519_keypair(rng: &mut R, force_key: Option<[u8; 32]>) -> identity::Keypair { - let mut raw_key = match force_key { - Some(key) => key, - None => { - let mut key = [0; 32]; - rng.fill_bytes(&mut key); - key - }, - }; - let secret = identity::ed25519::SecretKey::try_from_bytes(&mut raw_key).expect("Secret length is 32 bytes"); +pub fn generate_ed25519_keypair(mut p2p_key: [u8; 32]) -> identity::Keypair { + let secret = identity::ed25519::SecretKey::try_from_bytes(&mut p2p_key).expect("Secret length is 32 bytes"); let keypair = identity::ed25519::Keypair::from(secret); - keypair.into() + identity::Keypair::from(keypair) } /// Custom types mapping the complex associated types of AtomicDexBehaviour to the ExpandedSwarm @@ -596,7 +587,7 @@ fn start_gossipsub( ) -> Result<(Sender, AdexEventRx, PeerId), AdexBehaviourError> { let i_am_relay = config.node_type.is_relay(); let mut rng = rand::thread_rng(); - let local_key = generate_ed25519_keypair(&mut rng, config.force_key); + let local_key = generate_ed25519_keypair(config.p2p_key); let local_peer_id = PeerId::from(local_key.public()); info!("Local peer id: {:?}", local_peer_id); @@ -1081,7 +1072,7 @@ impl NetworkBehaviour for AtomicDexBehaviour { pub struct GossipsubConfig { netid: u16, - force_key: Option<[u8; 32]>, + p2p_key: [u8; 32], runtime: SwarmRuntime, to_dial: Vec, node_type: NodeType, @@ -1091,9 +1082,12 @@ pub struct GossipsubConfig { impl GossipsubConfig { #[cfg(test)] pub(crate) fn new_for_tests(runtime: SwarmRuntime, to_dial: Vec, node_type: NodeType) -> Self { + let mut p2p_key = [0u8; 32]; + common::os_rng(&mut p2p_key).unwrap(); + GossipsubConfig { netid: 333, - force_key: None, + p2p_key, runtime, to_dial, node_type, @@ -1101,10 +1095,10 @@ impl GossipsubConfig { } } - pub fn new(netid: u16, runtime: SwarmRuntime, node_type: NodeType) -> Self { + pub fn new(netid: u16, runtime: SwarmRuntime, node_type: NodeType, p2p_key: [u8; 32]) -> Self { GossipsubConfig { netid, - force_key: None, + p2p_key, runtime, to_dial: vec![], node_type, @@ -1117,11 +1111,6 @@ impl GossipsubConfig { self } - pub fn force_key(&mut self, force_key: Option<[u8; 32]>) -> &mut Self { - self.force_key = force_key; - self - } - pub fn max_num_streams(&mut self, max_num_streams: usize) -> &mut Self { self.max_num_streams = max_num_streams; self diff --git a/mm2src/mm2_p2p/src/lib.rs b/mm2src/mm2_p2p/src/lib.rs index 03d6a301db..e9a8f78ad4 100644 --- a/mm2src/mm2_p2p/src/lib.rs +++ b/mm2src/mm2_p2p/src/lib.rs @@ -29,7 +29,7 @@ pub use behaviours::request_response::RequestResponseBehaviourEvent; // libp2p related re-exports pub use libp2p::identity::DecodingError; -pub use libp2p::identity::{secp256k1::PublicKey as Libp2pSecpPublic, PublicKey as Libp2pPublic}; +pub use libp2p::identity::{secp256k1::PublicKey as Libp2pSecpPublic, Keypair, PublicKey as Libp2pPublic, SigningError}; pub use libp2p::{Multiaddr, PeerId}; // relay-address related re-exports diff --git a/mm2src/mm2_test_helpers/src/for_tests.rs b/mm2src/mm2_test_helpers/src/for_tests.rs index b2f0b537c7..e650ef4293 100644 --- a/mm2src/mm2_test_helpers/src/for_tests.rs +++ b/mm2src/mm2_test_helpers/src/for_tests.rs @@ -2909,6 +2909,7 @@ pub async fn enable_tendermint( tx_history: bool, ) -> Json { let ibc_requests: Vec<_> = ibc_assets.iter().map(|ticker| json!({ "ticker": ticker })).collect(); + let nodes: Vec = rpc_urls.iter().map(|u| json!({"url": u, "komodo_proxy": false })).collect(); let request = json!({ "userpass": mm.userpass, @@ -2917,7 +2918,7 @@ pub async fn enable_tendermint( "params": { "ticker": coin, "tokens_params": ibc_requests, - "rpc_urls": rpc_urls, + "nodes": nodes, "tx_history": tx_history } }); @@ -2945,6 +2946,7 @@ pub async fn enable_tendermint_without_balance( tx_history: bool, ) -> Json { let ibc_requests: Vec<_> = ibc_assets.iter().map(|ticker| json!({ "ticker": ticker })).collect(); + let nodes: Vec = rpc_urls.iter().map(|u| json!({"url": u, "komodo_proxy": false })).collect(); let request = json!({ "userpass": mm.userpass, @@ -2953,7 +2955,7 @@ pub async fn enable_tendermint_without_balance( "params": { "ticker": coin, "tokens_params": ibc_requests, - "rpc_urls": rpc_urls, + "nodes": nodes, "tx_history": tx_history, "get_balances": false } diff --git a/mm2src/proxy_signature/Cargo.toml b/mm2src/proxy_signature/Cargo.toml new file mode 100644 index 0000000000..5392b9862a --- /dev/null +++ b/mm2src/proxy_signature/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "proxy_signature" +version = "0.1.0" +edition = "2018" + +[dependencies] +chrono = "0.4" +http = "0.2" +libp2p = { git = "https://github.com/KomodoPlatform/rust-libp2p.git", tag = "k-0.52.4", default-features = false, features = ["identify"] } +serde = "1" +serde_json = { version = "1", features = ["preserve_order", "raw_value"] } + +[dev-dependencies] +rand = { version = "0.7", features = ["std", "small_rng"] } diff --git a/mm2src/proxy_signature/src/lib.rs b/mm2src/proxy_signature/src/lib.rs new file mode 100644 index 0000000000..86f57a3431 --- /dev/null +++ b/mm2src/proxy_signature/src/lib.rs @@ -0,0 +1,152 @@ +use chrono::Utc; +use http::Uri; +use libp2p::identity::{Keypair, PublicKey, SigningError}; +use serde::{Deserialize, Serialize}; + +/// Represents a message and its corresponding signature. +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +pub struct ProxySign { + /// Signature of the raw message. + pub signature_bytes: Vec, + /// Unique address of the sign's owner. + pub address: String, + /// The raw message that has been signed. + pub raw_message: RawMessage, +} + +/// Essential type that contains information required for generating signed messages (see `ProxySign`). +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +pub struct RawMessage { + /// This field is used to verify the proxy sign on the Komodo DeFi proxy side. + pub uri: String, + /// This field is used to check the payload size on the komodo-defi-proxy side. + /// Along with the `uri` field it helps confirm that the proxy sign matches the request. + pub body_size: usize, + pub public_key_encoded: Vec, + pub expires_at: i64, +} + +impl RawMessage { + fn new(uri: &Uri, body_size: usize, public_key_encoded: Vec, expires_in_seconds: i64) -> Self { + RawMessage { + uri: uri.to_string(), + body_size, + public_key_encoded, + expires_at: Utc::now().timestamp() + expires_in_seconds, + } + } + + /// Generates a byte vector representation of `self`. + fn encode(&self) -> Vec { + const PREFIX: &str = "Encoded Message for KDP\n"; + let mut bytes = PREFIX.as_bytes().to_owned(); + bytes.extend(self.public_key_encoded.clone()); + bytes.extend(self.uri.to_string().as_bytes().to_owned()); + bytes.extend(self.body_size.to_ne_bytes().to_owned()); + bytes.extend(self.expires_at.to_ne_bytes().to_owned()); + bytes + } + + /// Generates `ProxySign` using the provided keypair and coin ticker. + pub fn sign( + keypair: &Keypair, + uri: &Uri, + body_size: usize, + expires_in_seconds: i64, + ) -> Result { + let public_key_encoded = keypair.public().encode_protobuf(); + let address = keypair.public().to_peer_id().to_string(); + let raw_message = RawMessage::new(uri, body_size, public_key_encoded, expires_in_seconds); + let signature_bytes = keypair.sign(&raw_message.encode())?; + + Ok(ProxySign { + raw_message, + address, + signature_bytes, + }) + } +} + +impl ProxySign { + /// Validates if the message is still valid based on its expiration time and signature verification. + pub fn is_valid_message(&self) -> bool { + if Utc::now().timestamp() > self.raw_message.expires_at { + return false; + } + + let Ok(public_key) = PublicKey::try_decode_protobuf(&self.raw_message.public_key_encoded) else { return false }; + + if self.address != public_key.to_peer_id().to_string() { + return false; + } + + public_key.verify(&self.raw_message.encode(), &self.signature_bytes) + } +} + +#[cfg(test)] +pub mod proxy_signature_tests { + use libp2p::identity; + use rand::RngCore; + + use super::*; + + fn generate_ed25519_keypair(mut p2p_key: [u8; 32]) -> identity::Keypair { + let secret = identity::ed25519::SecretKey::try_from_bytes(&mut p2p_key).expect("Secret length is 32 bytes"); + let keypair = identity::ed25519::Keypair::from(secret); + identity::Keypair::from(keypair) + } + + fn os_rng(dest: &mut [u8]) -> Result<(), rand::Error> { rand::rngs::OsRng.try_fill_bytes(dest) } + + fn random_keypair() -> Keypair { + let mut p2p_key = [0u8; 32]; + os_rng(&mut p2p_key).unwrap(); + generate_ed25519_keypair(p2p_key) + } + + #[test] + fn sign_and_verify() { + let keypair = random_keypair(); + let signed_proxy_message = RawMessage::sign(&keypair, &Uri::from_static("http://example.com"), 0, 5).unwrap(); + assert!(signed_proxy_message.is_valid_message()); + } + + #[test] + fn expired_signature() { + let keypair = random_keypair(); + let signed_proxy_message = RawMessage::sign(&keypair, &Uri::from_static("http://example.com"), 0, -1).unwrap(); + assert!(!signed_proxy_message.is_valid_message()); + } + + #[test] + fn dirty_raw_message() { + let keypair = random_keypair(); + let mut signed_proxy_message = + RawMessage::sign(&keypair, &Uri::from_static("http://example.com"), 0, 5).unwrap(); + signed_proxy_message.raw_message.uri = "http://demo.com".to_string(); + assert!(!signed_proxy_message.is_valid_message()); + + let mut signed_proxy_message = + RawMessage::sign(&keypair, &Uri::from_static("http://example.com"), 0, 5).unwrap(); + signed_proxy_message.raw_message.body_size += 1; + assert!(!signed_proxy_message.is_valid_message()); + + let mut signed_proxy_message = + RawMessage::sign(&keypair, &Uri::from_static("http://example.com"), 0, 5).unwrap(); + signed_proxy_message.raw_message.expires_at += 1; + assert!(!signed_proxy_message.is_valid_message()); + } + + #[test] + fn verify_peer_id() { + let expected_address = "12D3KooWJPtxrHVDPoETNfPJY4WWVzX7Ti4WPemtXDgb5qmFrDiv"; + + let p2p_key = [123u8; 32]; + let keypair = generate_ed25519_keypair(p2p_key); + assert_eq!(keypair.public().to_peer_id().to_string(), expected_address); + + let signed_proxy_message = RawMessage::sign(&keypair, &Uri::from_static("http://example.com"), 0, 5).unwrap(); + assert_eq!(signed_proxy_message.address, expected_address); + } +} From 8dfd0a9e593a3e442957315563880cb2194044b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C3=96zkan?= Date: Fri, 16 Aug 2024 11:46:36 +0300 Subject: [PATCH 02/29] use default value for `komodo_proxy` (#2192) Signed-off-by: onur-ozkan --- mm2src/coins/tendermint/tendermint_coin.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index 8049c6e121..bc5e12f6ff 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -137,6 +137,7 @@ impl TendermintKeyPair { #[derive(Clone, Deserialize)] pub struct RpcNode { url: String, + #[serde(default)] komodo_proxy: bool, } From 25fc41211f91322876830b3436ae25f79a1204a5 Mon Sep 17 00:00:00 2001 From: Kadan Stadelmann Date: Tue, 20 Aug 2024 01:45:14 +0200 Subject: [PATCH 03/29] docs(README): update commit badges to use dev branch (#2193) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ff05f9d501..0d91d3cded 100755 --- a/README.md +++ b/README.md @@ -12,10 +12,10 @@ downloads - last commit + last commit - +
From 932669ae0494cb14264e3080b9b1d421b52559eb Mon Sep 17 00:00:00 2001 From: shamardy <39480341+shamardy@users.noreply.github.com> Date: Sat, 24 Aug 2024 03:41:19 +0300 Subject: [PATCH 04/29] fix(cors): allow OPTIONS request to KDF server (#2191) --- .github/workflows/test.yml | 2 +- mm2src/mm2_main/src/rpc.rs | 68 +++++++++++++++++++++++++++----------- 2 files changed, 49 insertions(+), 21 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 19cb9c5d67..a9790f9b13 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -213,7 +213,7 @@ jobs: - name: Test run: | - wget -O - https://raw.githubusercontent.com/KomodoPlatform/komodo/master/zcutil/fetch-params-alt.sh | bash + wget -O - https://raw.githubusercontent.com/KomodoPlatform/komodo/v0.8.1/zcutil/fetch-params-alt.sh | bash cargo test --test 'docker_tests_main' --features run-docker-tests --no-fail-fast wasm: diff --git a/mm2src/mm2_main/src/rpc.rs b/mm2src/mm2_main/src/rpc.rs index c7b483d952..2709d76f32 100644 --- a/mm2src/mm2_main/src/rpc.rs +++ b/mm2src/mm2_main/src/rpc.rs @@ -236,6 +236,8 @@ async fn process_single_request(ctx: MmArc, req: Json, client: SocketAddr) -> Re #[cfg(not(target_arch = "wasm32"))] async fn rpc_service(req: Request, ctx_h: u32, client: SocketAddr) -> Response { + const NON_ALLOWED_CHARS: &[char] = &['<', '>', '&']; + /// Unwraps a result or propagates its error 500 response with the specified headers (if they are present). macro_rules! try_sf { ($value: expr $(, $header_key:expr => $header_val:expr)*) => { @@ -269,39 +271,65 @@ async fn rpc_service(req: Request, ctx_h: u32, client: SocketAddr) -> Resp // https://github.com/artemii235/SuperNET/issues/219 let rpc_cors = match ctx.conf["rpccors"].as_str() { Some(s) => try_sf!(HeaderValue::from_str(s)), - None => HeaderValue::from_static("http://localhost:3000"), + None => { + if ctx.is_https() { + HeaderValue::from_static("https://localhost:3000") + } else { + HeaderValue::from_static("http://localhost:3000") + } + }, }; // Convert the native Hyper stream into a portable stream of `Bytes`. let (req, req_body) = req.into_parts(); - let req_bytes = try_sf!(hyper::body::to_bytes(req_body).await, ACCESS_CONTROL_ALLOW_ORIGIN => rpc_cors); - let req_str = String::from_utf8_lossy(req_bytes.as_ref()); - let is_invalid_input = req_str.chars().any(|c| c == '<' || c == '>' || c == '&'); - if is_invalid_input { + + if req.method == Method::OPTIONS { return Response::builder() - .status(500) - .header(CONTENT_TYPE, APPLICATION_JSON) - .body(Body::from(err_to_rpc_json_string("Invalid input"))) + .status(StatusCode::OK) + .header(ACCESS_CONTROL_ALLOW_ORIGIN, rpc_cors) + .header("Access-Control-Allow-Methods", "POST, OPTIONS") + .header("Access-Control-Allow-Headers", "Content-Type") + .header("Access-Control-Max-Age", "3600") + .body(Body::empty()) .unwrap(); } - let req_json: Json = try_sf!(json::from_slice(&req_bytes), ACCESS_CONTROL_ALLOW_ORIGIN => rpc_cors); - 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(); - parts.headers.insert(ACCESS_CONTROL_ALLOW_ORIGIN, rpc_cors); - let body_escaped = match std::str::from_utf8(&body) { - Ok(body_utf8) => { - let escaped = escape_answer(body_utf8); - escaped.as_bytes().to_vec() - }, - Err(_) => { + let req_json = { + let req_bytes = try_sf!(hyper::body::to_bytes(req_body).await, ACCESS_CONTROL_ALLOW_ORIGIN => rpc_cors); + let req_str = String::from_utf8_lossy(req_bytes.as_ref()); + let is_invalid_input = req_str.chars().any(|c| NON_ALLOWED_CHARS.contains(&c)); + if is_invalid_input { 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"))) + .body(Body::from(err_to_rpc_json_string(&format!( + "Invalid input: contains one or more of the following non-allowed characters: {:?}", + NON_ALLOWED_CHARS + )))) .unwrap(); - }, + } + try_sf!(json::from_slice(&req_bytes), ACCESS_CONTROL_ALLOW_ORIGIN => rpc_cors) + }; + + 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)) } From f6e83488049e579b61c9afcb58ce3d8a8bcec312 Mon Sep 17 00:00:00 2001 From: Alina Sharon <52405288+laruh@users.noreply.github.com> Date: Fri, 30 Aug 2024 16:51:13 +0700 Subject: [PATCH 05/29] feat(eth-swap): eth tpu v2 methods, eth docker test enhancements (#2169) This commit implements EVM TPU taker methods and adds some enhancements for eth docker tests. --- mm2src/coins/coin_errors.rs | 20 +- mm2src/coins/eth.rs | 100 +- mm2src/coins/eth/eth_swap_v2.rs | 824 ++++++++++++- mm2src/coins/eth/nft_swap_v2/errors.rs | 40 +- mm2src/coins/eth/nft_swap_v2/mod.rs | 181 +-- mm2src/coins/eth/nft_swap_v2/structs.rs | 15 - mm2src/coins/eth/v2_activation.rs | 113 +- mm2src/coins/lp_coins.rs | 144 ++- mm2src/coins/qrc20.rs | 4 +- mm2src/coins/test_coin.rs | 14 +- mm2src/coins/utxo/qtum_delegation.rs | 6 +- mm2src/coins/utxo/utxo_common.rs | 21 +- mm2src/coins/utxo/utxo_standard.rs | 68 +- mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs | 27 +- mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs | 20 +- .../tests/docker_tests/docker_tests_common.rs | 114 +- .../tests/docker_tests/eth_docker_tests.rs | 1085 ++++++++++++++--- .../tests/docker_tests/swap_proto_v2_tests.rs | 52 +- .../contract_bytes/maker_swap_v2_bytes | 2 +- .../contract_bytes/nft_maker_swap_v2_bytes | 2 +- .../contract_bytes/nft_swap_contract_bytes | 1 - .../contract_bytes/taker_swap_v2_bytes | 2 +- mm2src/mm2_test_helpers/src/for_tests.rs | 36 +- mm2src/proxy_signature/src/lib.rs | 4 +- 24 files changed, 2217 insertions(+), 678 deletions(-) delete mode 100644 mm2src/mm2_test_helpers/contract_bytes/nft_swap_contract_bytes diff --git a/mm2src/coins/coin_errors.rs b/mm2src/coins/coin_errors.rs index 34b0c46486..aead751eb0 100644 --- a/mm2src/coins/coin_errors.rs +++ b/mm2src/coins/coin_errors.rs @@ -1,4 +1,5 @@ -use crate::eth::nft_swap_v2::errors::{Erc721FunctionError, HtlcParamsError, PaymentStatusErr, PrepareTxDataError}; +use crate::eth::eth_swap_v2::{PaymentStatusErr, ValidatePaymentV2Err}; +use crate::eth::nft_swap_v2::errors::{Erc721FunctionError, HtlcParamsError, PrepareTxDataError}; use crate::eth::{EthAssocTypesError, EthNftAssocTypesError, Web3RpcError}; use crate::{utxo::rpc_clients::UtxoRpcError, NumConversError, UnexpectedDerivationMethod}; use enum_derives::EnumFromStringify; @@ -50,6 +51,7 @@ pub enum ValidatePaymentError { TimelockOverflow(TryFromIntError), #[display(fmt = "Nft Protocol is not supported yet!")] NftProtocolNotSupported, + InvalidData(String), } impl From for ValidatePaymentError { @@ -84,9 +86,8 @@ impl From for ValidatePaymentError { fn from(err: PaymentStatusErr) -> Self { match err { PaymentStatusErr::Transport(e) => Self::Transport(e), - PaymentStatusErr::AbiError(e) - | PaymentStatusErr::Internal(e) - | PaymentStatusErr::TxDeserializationError(e) => Self::InternalError(e), + PaymentStatusErr::ABIError(e) | PaymentStatusErr::Internal(e) => Self::InternalError(e), + PaymentStatusErr::InvalidData(e) => Self::InvalidData(e), } } } @@ -95,7 +96,16 @@ impl From for ValidatePaymentError { fn from(err: HtlcParamsError) -> Self { match err { HtlcParamsError::WrongPaymentTx(e) => ValidatePaymentError::WrongPaymentTx(e), - HtlcParamsError::TxDeserializationError(e) => ValidatePaymentError::TxDeserializationError(e), + HtlcParamsError::ABIError(e) | HtlcParamsError::InvalidData(e) => ValidatePaymentError::InvalidData(e), + } + } +} + +impl From for ValidatePaymentError { + fn from(err: ValidatePaymentV2Err) -> Self { + match err { + ValidatePaymentV2Err::UnexpectedPaymentState(e) => ValidatePaymentError::UnexpectedPaymentState(e), + ValidatePaymentV2Err::WrongPaymentTx(e) => ValidatePaymentError::WrongPaymentTx(e), } } } diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 0d50f70024..a6cd170627 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -157,7 +157,7 @@ mod eip1559_gas_fee; pub(crate) use eip1559_gas_fee::FeePerGasEstimated; use eip1559_gas_fee::{BlocknativeGasApiCaller, FeePerGasSimpleEstimator, GasApiConfig, GasApiProvider, InfuraGasApiCaller}; -mod eth_swap_v2; +pub(crate) mod eth_swap_v2; /// https://github.com/artemii235/etomic-swap/blob/master/contracts/EtomicSwap.sol /// Dev chain (195.201.137.5:8565) contract address: 0x83965C539899cC0F918552e5A26915de40ee8852 @@ -827,9 +827,18 @@ impl EthCoinImpl { /// The id used to differentiate payments on Etomic swap smart contract pub(crate) fn etomic_swap_id(&self, time_lock: u32, secret_hash: &[u8]) -> Vec { let timelock_bytes = time_lock.to_le_bytes(); + self.generate_etomic_swap_id(&timelock_bytes, secret_hash) + } - let mut input = Vec::with_capacity(timelock_bytes.len() + secret_hash.len()); - input.extend_from_slice(&timelock_bytes); + /// The id used to differentiate payments on Etomic swap v2 smart contracts + pub(crate) fn etomic_swap_id_v2(&self, time_lock: u64, secret_hash: &[u8]) -> Vec { + let timelock_bytes = time_lock.to_le_bytes(); + self.generate_etomic_swap_id(&timelock_bytes, secret_hash) + } + + fn generate_etomic_swap_id(&self, time_lock_bytes: &[u8], secret_hash: &[u8]) -> Vec { + let mut input = Vec::with_capacity(time_lock_bytes.len() + secret_hash.len()); + input.extend_from_slice(time_lock_bytes); input.extend_from_slice(secret_hash); sha256(&input).to_vec() } @@ -7031,32 +7040,38 @@ impl Eip1559Ops for EthCoin { #[async_trait] impl TakerCoinSwapOpsV2 for EthCoin { + /// Wrapper for [EthCoin::send_taker_funding_impl] async fn send_taker_funding(&self, args: SendTakerFundingArgs<'_>) -> Result { self.send_taker_funding_impl(args).await } - async fn validate_taker_funding(&self, _args: ValidateTakerFundingArgs<'_, Self>) -> ValidateSwapV2TxResult { - todo!() + /// Wrapper for [EthCoin::validate_taker_funding_impl] + async fn validate_taker_funding(&self, args: ValidateTakerFundingArgs<'_, Self>) -> ValidateSwapV2TxResult { + self.validate_taker_funding_impl(args).await } - async fn refund_taker_funding_timelock(&self, _args: RefundPaymentArgs<'_>) -> Result { - todo!() + async fn refund_taker_funding_timelock( + &self, + args: RefundTakerPaymentArgs<'_>, + ) -> Result { + self.refund_taker_payment_with_timelock_impl(args).await } async fn refund_taker_funding_secret( &self, - _args: RefundFundingSecretArgs<'_, Self>, + args: RefundFundingSecretArgs<'_, Self>, ) -> Result { - todo!() + self.refund_taker_funding_secret_impl(args).await } + /// Wrapper for [EthCoin::search_for_taker_funding_spend_impl] async fn search_for_taker_funding_spend( &self, - _tx: &Self::Tx, + tx: &Self::Tx, _from_block: u64, _secret_hash: &[u8], ) -> Result>, SearchForFundingSpendErr> { - todo!() + self.search_for_taker_funding_spend_impl(tx).await } /// Eth doesnt have preimages @@ -7080,18 +7095,21 @@ impl TakerCoinSwapOpsV2 for EthCoin { Ok(()) } - /// Eth doesnt use multisig + /// Wrapper for [EthCoin::taker_payment_approve] async fn sign_and_send_taker_funding_spend( &self, _preimage: &TxPreimageWithSig, - _args: &GenTakerFundingSpendArgs<'_, Self>, + args: &GenTakerFundingSpendArgs<'_, Self>, _swap_unique_data: &[u8], ) -> Result { - todo!() + self.taker_payment_approve(args).await } - async fn refund_combined_taker_payment(&self, _args: RefundPaymentArgs<'_>) -> Result { - todo!() + async fn refund_combined_taker_payment( + &self, + args: RefundTakerPaymentArgs<'_>, + ) -> Result { + self.refund_taker_payment_with_timelock_impl(args).await } /// Eth doesnt have preimages @@ -7115,23 +7133,25 @@ impl TakerCoinSwapOpsV2 for EthCoin { Ok(()) } + /// Wrapper for [EthCoin::sign_and_broadcast_taker_payment_spend_impl] async fn sign_and_broadcast_taker_payment_spend( &self, _preimage: &TxPreimageWithSig, - _gen_args: &GenTakerPaymentSpendArgs<'_, Self>, - _secret: &[u8], + gen_args: &GenTakerPaymentSpendArgs<'_, Self>, + secret: &[u8], _swap_unique_data: &[u8], ) -> Result { - todo!() + self.sign_and_broadcast_taker_payment_spend_impl(gen_args, secret).await } + /// Wrapper for [EthCoin::wait_for_taker_payment_spend_impl] async fn wait_for_taker_payment_spend( &self, - _taker_payment: &Self::Tx, + taker_payment: &Self::Tx, _from_block: u64, - _wait_until: u64, + wait_until: u64, ) -> MmResult { - todo!() + self.wait_for_taker_payment_spend_impl(taker_payment, wait_until).await } } @@ -7162,3 +7182,39 @@ impl CommonSwapOpsV2 for EthCoin { self.derive_htlc_pubkey_v2(swap_unique_data).to_bytes() } } + +#[cfg(all(feature = "for-tests", not(target_arch = "wasm32")))] +impl EthCoin { + pub async fn set_coin_type(&self, new_coin_type: EthCoinType) -> EthCoin { + let coin = EthCoinImpl { + ticker: self.ticker.clone(), + coin_type: new_coin_type, + priv_key_policy: self.priv_key_policy.clone(), + derivation_method: Arc::clone(&self.derivation_method), + sign_message_prefix: self.sign_message_prefix.clone(), + swap_contract_address: self.swap_contract_address, + swap_v2_contracts: self.swap_v2_contracts, + fallback_swap_contract: self.fallback_swap_contract, + contract_supports_watchers: self.contract_supports_watchers, + web3_instances: AsyncMutex::new(self.web3_instances.lock().await.clone()), + decimals: self.decimals, + history_sync_state: Mutex::new(self.history_sync_state.lock().unwrap().clone()), + required_confirmations: AtomicU64::new( + self.required_confirmations.load(std::sync::atomic::Ordering::SeqCst), + ), + swap_txfee_policy: Mutex::new(self.swap_txfee_policy.lock().unwrap().clone()), + max_eth_tx_type: self.max_eth_tx_type, + ctx: self.ctx.clone(), + chain_id: self.chain_id, + trezor_coin: self.trezor_coin.clone(), + logs_block_range: self.logs_block_range, + address_nonce_locks: Arc::clone(&self.address_nonce_locks), + erc20_tokens_infos: Arc::clone(&self.erc20_tokens_infos), + nfts_infos: Arc::clone(&self.nfts_infos), + platform_fee_estimator_state: Arc::clone(&self.platform_fee_estimator_state), + gas_limit: EthGasLimit::default(), + abortable_system: self.abortable_system.create_subsystem().unwrap(), + }; + EthCoin(Arc::new(coin)) + } +} diff --git a/mm2src/coins/eth/eth_swap_v2.rs b/mm2src/coins/eth/eth_swap_v2.rs index 295fd7af2c..0d61ac6004 100644 --- a/mm2src/coins/eth/eth_swap_v2.rs +++ b/mm2src/coins/eth/eth_swap_v2.rs @@ -1,12 +1,30 @@ use super::eth::{wei_from_big_decimal, EthCoin, EthCoinType, SignedEthTx, TAKER_SWAP_V2}; -use super::{SendTakerFundingArgs, Transaction, TransactionErr}; +use super::{decode_contract_call, get_function_input_data, ParseCoinAssocTypes, RefundFundingSecretArgs, + RefundTakerPaymentArgs, SendTakerFundingArgs, SwapTxTypeWithSecretHash, TakerPaymentStateV2, Transaction, + TransactionErr, ValidateSwapV2TxError, ValidateSwapV2TxResult, ValidateTakerFundingArgs}; +use crate::{FundingTxSpend, GenTakerFundingSpendArgs, GenTakerPaymentSpendArgs, SearchForFundingSpendErr, + WaitForTakerPaymentSpendError}; +use common::executor::Timer; +use common::now_sec; use enum_derives::EnumFromStringify; -use ethabi::Token; +use ethabi::{Contract, Function, Token}; use ethcore_transaction::Action; use ethereum_types::{Address, Public, U256}; use ethkey::public_to_address; use futures::compat::Future01CompatExt; +use mm2_err_handle::prelude::{MapToMmResult, MmError, MmResult}; +use mm2_number::BigDecimal; use std::convert::TryInto; +use web3::types::{Transaction as Web3Tx, TransactionId}; + +/// ZERO_VALUE is used to represent a 0 amount in transactions where the value is encoded in the transaction input data. +/// This is typically used in function calls where the value is not directly transferred with the transaction, such as in +/// `spendTakerPayment` where the [amount](https://github.com/KomodoPlatform/etomic-swap/blob/5e15641cbf41766cd5b37b4d71842c270773f788/contracts/EtomicSwapTakerV2.sol#L166) +/// is provided as part of the input data rather than as an Ether value +pub(crate) const ZERO_VALUE: u32 = 0; +const ETH_TAKER_PAYMENT: &str = "ethTakerPayment"; +const ERC20_TAKER_PAYMENT: &str = "erc20TakerPayment"; +const TAKER_PAYMENT_APPROVE: &str = "takerPaymentApprove"; struct TakerFundingArgs { dex_fee: U256, @@ -14,11 +32,24 @@ struct TakerFundingArgs { maker_address: Address, taker_secret_hash: [u8; 32], maker_secret_hash: [u8; 32], - funding_time_lock: u32, - payment_time_lock: u32, + funding_time_lock: u64, + payment_time_lock: u64, +} + +struct TakerRefundArgs { + dex_fee: U256, + payment_amount: U256, + maker_address: Address, + taker_secret: [u8; 32], + taker_secret_hash: [u8; 32], + maker_secret_hash: [u8; 32], + payment_time_lock: u64, + token_address: Address, } impl EthCoin { + /// Calls `"ethTakerPayment"` or `"erc20TakerPayment"` swap contract methods. + /// Returns taker sent payment transaction. pub(crate) async fn send_taker_funding_impl( &self, args: SendTakerFundingArgs<'_>, @@ -37,22 +68,22 @@ impl EthCoin { )); let funding_args = { let maker_address = public_to_address(&Public::from_slice(args.maker_pub)); - let funding_time_lock: u32 = try_tx_s!(args.funding_time_lock.try_into()); - let payment_time_lock: u32 = try_tx_s!(args.payment_time_lock.try_into()); TakerFundingArgs { dex_fee, payment_amount, maker_address, taker_secret_hash: try_tx_s!(args.taker_secret_hash.try_into()), maker_secret_hash: try_tx_s!(args.maker_secret_hash.try_into()), - funding_time_lock, - payment_time_lock, + funding_time_lock: args.funding_time_lock, + payment_time_lock: args.payment_time_lock, } }; match &self.coin_type { EthCoinType::Eth => { let data = try_tx_s!(self.prepare_taker_eth_funding_data(&funding_args).await); - let eth_total_payment = dex_fee + payment_amount; + let eth_total_payment = payment_amount.checked_add(dex_fee).ok_or_else(|| { + TransactionErr::Plain(ERRL!("Overflow occurred while calculating eth_total_payment")) + })?; self.sign_and_send_transaction( eth_total_payment, Action::Call(taker_swap_v2_contract), @@ -87,7 +118,7 @@ impl EthCoin { })?; } self.sign_and_send_transaction( - U256::from(0), + U256::from(ZERO_VALUE), Action::Call(taker_swap_v2_contract), data, // TODO need new consts and params for v2 calls. now it uses v1 @@ -96,15 +127,371 @@ impl EthCoin { .compat() .await }, - EthCoinType::Nft { .. } => Err(TransactionErr::ProtocolNotSupported( + EthCoinType::Nft { .. } => Err(TransactionErr::ProtocolNotSupported(ERRL!( + "NFT protocol is not supported for ETH and ERC20 Swaps" + ))), + } + } + + pub(crate) async fn validate_taker_funding_impl( + &self, + args: ValidateTakerFundingArgs<'_, Self>, + ) -> ValidateSwapV2TxResult { + if let EthCoinType::Nft { .. } = self.coin_type { + return MmError::err(ValidateSwapV2TxError::ProtocolNotSupported( "NFT protocol is not supported for ETH and ERC20 Swaps".to_string(), - )), + )); } + let taker_swap_v2_contract = self + .swap_v2_contracts + .as_ref() + .map(|contracts| contracts.taker_swap_v2_contract) + .ok_or_else(|| { + ValidateSwapV2TxError::Internal("Expected swap_v2_contracts to be Some, but found None".to_string()) + })?; + validate_payment_args(args.taker_secret_hash, args.maker_secret_hash, &args.trading_amount) + .map_err(ValidateSwapV2TxError::Internal)?; + let taker_address = public_to_address(args.taker_pub); + let swap_id = self.etomic_swap_id_v2(args.payment_time_lock, args.maker_secret_hash); + let taker_status = self + .payment_status_v2( + taker_swap_v2_contract, + Token::FixedBytes(swap_id.clone()), + &TAKER_SWAP_V2, + EthPaymentType::TakerPayments, + 3, + ) + .await?; + + let tx_from_rpc = self.transaction(TransactionId::Hash(args.funding_tx.tx_hash())).await?; + let tx_from_rpc = tx_from_rpc.as_ref().ok_or_else(|| { + ValidateSwapV2TxError::TxDoesNotExist(format!( + "Didn't find provided tx {:?} on ETH node", + args.funding_tx.tx_hash() + )) + })?; + validate_from_to_and_status( + tx_from_rpc, + taker_address, + taker_swap_v2_contract, + taker_status, + TakerPaymentStateV2::PaymentSent as u8, + )?; + + let validation_args = { + let dex_fee = wei_from_big_decimal(&args.dex_fee.fee_amount().into(), self.decimals)?; + let payment_amount = wei_from_big_decimal(&(args.trading_amount + args.premium_amount), self.decimals)?; + TakerValidationArgs { + swap_id, + amount: payment_amount, + dex_fee, + receiver: self.my_addr().await, + taker_secret_hash: args.taker_secret_hash, + maker_secret_hash: args.maker_secret_hash, + funding_time_lock: args.funding_time_lock, + payment_time_lock: args.payment_time_lock, + } + }; + match self.coin_type { + EthCoinType::Eth => { + let function = TAKER_SWAP_V2.function(ETH_TAKER_PAYMENT)?; + let decoded = decode_contract_call(function, &tx_from_rpc.input.0)?; + validate_eth_taker_payment_data(&decoded, &validation_args, function, tx_from_rpc.value)?; + }, + EthCoinType::Erc20 { token_addr, .. } => { + let function = TAKER_SWAP_V2.function(ERC20_TAKER_PAYMENT)?; + let decoded = decode_contract_call(function, &tx_from_rpc.input.0)?; + validate_erc20_taker_payment_data(&decoded, &validation_args, function, token_addr)?; + }, + EthCoinType::Nft { .. } => unreachable!(), + } + Ok(()) } + /// Taker approves payment calling `takerPaymentApprove` for EVM based chains. + /// Function accepts taker payment transaction, returns taker approve payment transaction. + pub(crate) async fn taker_payment_approve( + &self, + args: &GenTakerFundingSpendArgs<'_, Self>, + ) -> Result { + // TODO need new consts and params for v2 calls, here should be common `gas_limit.taker_approve` param for Eth and Erc20 + let gas_limit = match self.coin_type { + EthCoinType::Eth | EthCoinType::Erc20 { .. } => U256::from(self.gas_limit.eth_payment), + EthCoinType::Nft { .. } => { + return Err(TransactionErr::ProtocolNotSupported(ERRL!( + "NFT protocol is not supported for ETH and ERC20 Swaps" + ))) + }, + }; + let (taker_swap_v2_contract, send_func, token_address) = self + .taker_swap_v2_details(ETH_TAKER_PAYMENT, ERC20_TAKER_PAYMENT) + .await?; + let decoded = try_tx_s!(decode_contract_call(send_func, args.funding_tx.unsigned().data())); + let taker_status = try_tx_s!( + self.payment_status_v2( + taker_swap_v2_contract, + decoded[0].clone(), + &TAKER_SWAP_V2, + EthPaymentType::TakerPayments, + 3, + ) + .await + ); + validate_payment_state(args.funding_tx, taker_status, TakerPaymentStateV2::PaymentSent as u8) + .map_err(|e| TransactionErr::Plain(ERRL!("{}", e)))?; + let data = try_tx_s!( + self.prepare_taker_payment_approve_data(args, decoded, token_address) + .await + ); + let approve_tx = self + .sign_and_send_transaction( + U256::from(ZERO_VALUE), + Action::Call(taker_swap_v2_contract), + data, + gas_limit, + ) + .compat() + .await?; + Ok(approve_tx) + } + + pub(crate) async fn refund_taker_payment_with_timelock_impl( + &self, + args: RefundTakerPaymentArgs<'_>, + ) -> Result { + let (token_address, gas_limit) = match &self.coin_type { + // TODO need new consts and params for v2 calls + EthCoinType::Eth => (Address::default(), self.gas_limit.eth_sender_refund), + EthCoinType::Erc20 { + platform: _, + token_addr, + } => (*token_addr, self.gas_limit.erc20_sender_refund), + EthCoinType::Nft { .. } => { + return Err(TransactionErr::ProtocolNotSupported(ERRL!( + "NFT protocol is not supported for ETH and ERC20 Swaps" + ))) + }, + }; + + let taker_swap_v2_contract = self + .swap_v2_contracts + .as_ref() + .map(|contracts| contracts.taker_swap_v2_contract) + .ok_or_else(|| TransactionErr::Plain(ERRL!("Expected swap_v2_contracts to be Some, but found None")))?; + let dex_fee = try_tx_s!(wei_from_big_decimal( + &args.dex_fee.fee_amount().to_decimal(), + self.decimals + )); + let payment_amount = try_tx_s!(wei_from_big_decimal( + &(args.trading_amount + args.premium_amount), + self.decimals + )); + let maker_address = public_to_address(&Public::from_slice(args.maker_pub)); + let (maker_secret_hash, taker_secret_hash) = match args.tx_type_with_secret_hash { + SwapTxTypeWithSecretHash::TakerPaymentV2 { + maker_secret_hash, + taker_secret_hash, + } => (maker_secret_hash, taker_secret_hash), + _ => { + return Err(TransactionErr::Plain(ERRL!( + "Unsupported swap tx type for timelock refund" + ))) + }, + }; + + let args = TakerRefundArgs { + dex_fee, + payment_amount, + maker_address, + taker_secret: [0u8; 32], + taker_secret_hash: try_tx_s!(taker_secret_hash.try_into()), + maker_secret_hash: try_tx_s!(maker_secret_hash.try_into()), + payment_time_lock: args.time_lock, + token_address, + }; + let data = try_tx_s!(self.prepare_taker_refund_payment_timelock_data(args).await); + + self.sign_and_send_transaction( + U256::from(ZERO_VALUE), + Action::Call(taker_swap_v2_contract), + data, + U256::from(gas_limit), + ) + .compat() + .await + } + + pub(crate) async fn refund_taker_funding_secret_impl( + &self, + args: RefundFundingSecretArgs<'_, Self>, + ) -> Result { + let (token_address, gas_limit) = match &self.coin_type { + // TODO need new consts and params for v2 calls + EthCoinType::Eth => (Address::default(), self.gas_limit.eth_sender_refund), + EthCoinType::Erc20 { + platform: _, + token_addr, + } => (*token_addr, self.gas_limit.erc20_sender_refund), + EthCoinType::Nft { .. } => { + return Err(TransactionErr::ProtocolNotSupported(ERRL!( + "NFT protocol is not supported for ETH and ERC20 Swaps" + ))) + }, + }; + + let taker_swap_v2_contract = self + .swap_v2_contracts + .as_ref() + .map(|contracts| contracts.taker_swap_v2_contract) + .ok_or_else(|| TransactionErr::Plain(ERRL!("Expected swap_v2_contracts to be Some, but found None")))?; + let taker_secret = try_tx_s!(args.taker_secret.try_into()); + let maker_secret_hash = try_tx_s!(args.maker_secret_hash.try_into()); + let dex_fee = try_tx_s!(wei_from_big_decimal( + &args.dex_fee.fee_amount().to_decimal(), + self.decimals + )); + let payment_amount = try_tx_s!(wei_from_big_decimal( + &(args.trading_amount + args.premium_amount), + self.decimals + )); + let maker_address = public_to_address(args.maker_pubkey); + + let refund_args = TakerRefundArgs { + dex_fee, + payment_amount, + maker_address, + taker_secret, + taker_secret_hash: [0u8; 32], + maker_secret_hash, + payment_time_lock: args.payment_time_lock, + token_address, + }; + let data = try_tx_s!(self.prepare_taker_refund_payment_secret_data(&refund_args).await); + + self.sign_and_send_transaction( + U256::from(ZERO_VALUE), + Action::Call(taker_swap_v2_contract), + data, + U256::from(gas_limit), + ) + .compat() + .await + } + + /// Checks that taker payment state is `TakerApproved`. + /// Accepts a taker-approved payment transaction and returns it if the state is correct. + pub(crate) async fn search_for_taker_funding_spend_impl( + &self, + tx: &SignedEthTx, + ) -> Result>, SearchForFundingSpendErr> { + let (decoded, taker_swap_v2_contract) = self + .get_decoded_and_swap_contract(tx, TAKER_PAYMENT_APPROVE) + .await + .map_err(|e| SearchForFundingSpendErr::Internal(ERRL!("{}", e)))?; + let taker_status = self + .payment_status_v2( + taker_swap_v2_contract, + decoded[0].clone(), // id from takerPaymentApprove + &TAKER_SWAP_V2, + EthPaymentType::TakerPayments, + 3, + ) + .await + .map_err(|e| SearchForFundingSpendErr::Internal(ERRL!("{}", e)))?; + if taker_status == U256::from(TakerPaymentStateV2::TakerApproved as u8) { + return Ok(Some(FundingTxSpend::TransferredToTakerPayment(tx.clone()))); + } + Ok(None) + } + + /// Taker swap contract `spendTakerPayment` method is called for EVM based chains. + /// Returns maker spent payment transaction. + pub(crate) async fn sign_and_broadcast_taker_payment_spend_impl( + &self, + gen_args: &GenTakerPaymentSpendArgs<'_, Self>, + secret: &[u8], + ) -> Result { + // TODO need new consts and params for v2 calls + let gas_limit = match self.coin_type { + EthCoinType::Eth => U256::from(self.gas_limit.eth_receiver_spend), + EthCoinType::Erc20 { .. } => U256::from(self.gas_limit.erc20_receiver_spend), + EthCoinType::Nft { .. } => { + return Err(TransactionErr::ProtocolNotSupported(ERRL!( + "NFT protocol is not supported for ETH and ERC20 Swaps" + ))) + }, + }; + let (taker_swap_v2_contract, approve_func, token_address) = self + .taker_swap_v2_details(TAKER_PAYMENT_APPROVE, TAKER_PAYMENT_APPROVE) + .await?; + let decoded = try_tx_s!(decode_contract_call(approve_func, gen_args.taker_tx.unsigned().data())); + let taker_status = try_tx_s!( + self.payment_status_v2( + taker_swap_v2_contract, + decoded[0].clone(), + &TAKER_SWAP_V2, + EthPaymentType::TakerPayments, + 3, + ) + .await + ); + validate_payment_state( + gen_args.taker_tx, + taker_status, + TakerPaymentStateV2::TakerApproved as u8, + ) + .map_err(|e| TransactionErr::Plain(ERRL!("{}", e)))?; + let data = try_tx_s!( + self.prepare_spend_taker_payment_data(gen_args, secret, decoded, token_address) + .await + ); + let spend_payment_tx = self + .sign_and_send_transaction( + U256::from(ZERO_VALUE), + Action::Call(taker_swap_v2_contract), + data, + gas_limit, + ) + .compat() + .await?; + Ok(spend_payment_tx) + } + + /// Checks that taker payment state is `MakerSpent`. + /// Accepts maker spent payment transaction and returns it if payment status is correct. + pub(crate) async fn wait_for_taker_payment_spend_impl( + &self, + taker_payment: &SignedEthTx, + wait_until: u64, + ) -> MmResult { + let (decoded, taker_swap_v2_contract) = self + .get_decoded_and_swap_contract(taker_payment, "spendTakerPayment") + .await?; + loop { + let taker_status = self + .payment_status_v2( + taker_swap_v2_contract, + decoded[0].clone(), // id from spendTakerPayment + &TAKER_SWAP_V2, + EthPaymentType::TakerPayments, + 3, + ) + .await?; + if taker_status == U256::from(TakerPaymentStateV2::MakerSpent as u8) { + return Ok(taker_payment.clone()); + } + let now = now_sec(); + if now > wait_until { + return MmError::err(WaitForTakerPaymentSpendError::Timeout { wait_until, now }); + } + Timer::sleep(10.).await; + } + } + + /// Prepares data for EtomicSwapTakerV2 contract [ethTakerPayment](https://github.com/KomodoPlatform/etomic-swap/blob/5e15641cbf41766cd5b37b4d71842c270773f788/contracts/EtomicSwapTakerV2.sol#L44) method async fn prepare_taker_eth_funding_data(&self, args: &TakerFundingArgs) -> Result, PrepareTxDataError> { - let function = TAKER_SWAP_V2.function("ethTakerPayment")?; - let id = self.etomic_swap_id(args.payment_time_lock, &args.maker_secret_hash); + let function = TAKER_SWAP_V2.function(ETH_TAKER_PAYMENT)?; + let id = self.etomic_swap_id_v2(args.payment_time_lock, &args.maker_secret_hash); let data = function.encode_input(&[ Token::FixedBytes(id), Token::Uint(args.dex_fee), @@ -117,18 +504,19 @@ impl EthCoin { Ok(data) } + /// Prepares data for EtomicSwapTakerV2 contract [erc20TakerPayment](https://github.com/KomodoPlatform/etomic-swap/blob/5e15641cbf41766cd5b37b4d71842c270773f788/contracts/EtomicSwapTakerV2.sol#L83) method async fn prepare_taker_erc20_funding_data( &self, args: &TakerFundingArgs, - token_addr: Address, + token_address: Address, ) -> Result, PrepareTxDataError> { - let function = TAKER_SWAP_V2.function("erc20TakerPayment")?; - let id = self.etomic_swap_id(args.payment_time_lock, &args.maker_secret_hash); + let function = TAKER_SWAP_V2.function(ERC20_TAKER_PAYMENT)?; + let id = self.etomic_swap_id_v2(args.payment_time_lock, &args.maker_secret_hash); let data = function.encode_input(&[ Token::FixedBytes(id), Token::Uint(args.payment_amount), Token::Uint(args.dex_fee), - Token::Address(token_addr), + Token::Address(token_address), Token::Address(args.maker_address), Token::FixedBytes(args.taker_secret_hash.to_vec()), Token::FixedBytes(args.maker_secret_hash.to_vec()), @@ -137,14 +525,406 @@ impl EthCoin { ])?; Ok(data) } + + /// Prepares data for EtomicSwapTakerV2 contract [refundTakerPaymentTimelock](https://github.com/KomodoPlatform/etomic-swap/blob/5e15641cbf41766cd5b37b4d71842c270773f788/contracts/EtomicSwapTakerV2.sol#L208) method + async fn prepare_taker_refund_payment_timelock_data( + &self, + args: TakerRefundArgs, + ) -> Result, PrepareTxDataError> { + let function = TAKER_SWAP_V2.function("refundTakerPaymentTimelock")?; + let id = self.etomic_swap_id_v2(args.payment_time_lock, &args.maker_secret_hash); + let data = function.encode_input(&[ + Token::FixedBytes(id), + Token::Uint(args.payment_amount), + Token::Uint(args.dex_fee), + Token::Address(args.maker_address), + Token::FixedBytes(args.taker_secret_hash.to_vec()), + Token::FixedBytes(args.maker_secret_hash.to_vec()), + Token::Address(args.token_address), + ])?; + Ok(data) + } + + /// Prepares data for EtomicSwapTakerV2 contract [refundTakerPaymentSecret](https://github.com/KomodoPlatform/etomic-swap/blob/5e15641cbf41766cd5b37b4d71842c270773f788/contracts/EtomicSwapTakerV2.sol#L267) method + async fn prepare_taker_refund_payment_secret_data( + &self, + args: &TakerRefundArgs, + ) -> Result, PrepareTxDataError> { + let function = TAKER_SWAP_V2.function("refundTakerPaymentSecret")?; + let id = self.etomic_swap_id_v2(args.payment_time_lock, &args.maker_secret_hash); + let data = function.encode_input(&[ + Token::FixedBytes(id), + Token::Uint(args.payment_amount), + Token::Uint(args.dex_fee), + Token::Address(args.maker_address), + Token::FixedBytes(args.taker_secret.to_vec()), + Token::FixedBytes(args.maker_secret_hash.to_vec()), + Token::Address(args.token_address), + ])?; + Ok(data) + } + + /// This function constructs the encoded transaction input data required to approve the taker payment ([takerPaymentApprove](https://github.com/KomodoPlatform/etomic-swap/blob/5e15641cbf41766cd5b37b4d71842c270773f788/contracts/EtomicSwapTakerV2.sol#L128)). + /// The `decoded` parameter should contain the transaction input data from the `ethTakerPayment` or `erc20TakerPayment` function of the EtomicSwapTakerV2 contract. + async fn prepare_taker_payment_approve_data( + &self, + args: &GenTakerFundingSpendArgs<'_, Self>, + decoded: Vec, + token_address: Address, + ) -> Result, PrepareTxDataError> { + let function = TAKER_SWAP_V2.function(TAKER_PAYMENT_APPROVE)?; + let data = match self.coin_type { + EthCoinType::Eth => { + check_decoded_length(&decoded, 7)?; + let dex_fee = match &decoded[1] { + Token::Uint(value) => value, + _ => return Err(PrepareTxDataError::Internal("Invalid token type for dex fee".into())), + }; + let amount = args + .funding_tx + .unsigned() + .value() + .checked_sub(*dex_fee) + .ok_or_else(|| { + PrepareTxDataError::Internal("Underflow occurred while calculating amount".into()) + })?; + function.encode_input(&[ + decoded[0].clone(), // id from ethTakerPayment + Token::Uint(amount), // calculated payment amount (tx value - dexFee) + decoded[1].clone(), // dexFee from ethTakerPayment + decoded[2].clone(), // receiver from ethTakerPayment + Token::FixedBytes(args.taker_secret_hash.to_vec()), + Token::FixedBytes(args.maker_secret_hash.to_vec()), + Token::Address(token_address), // should be zero address Address::default() + ])? + }, + EthCoinType::Erc20 { .. } => { + check_decoded_length(&decoded, 9)?; + function.encode_input(&[ + decoded[0].clone(), // id from erc20TakerPayment + decoded[1].clone(), // amount from erc20TakerPayment + decoded[2].clone(), // dexFee from erc20TakerPayment + decoded[4].clone(), // receiver from erc20TakerPayment + Token::FixedBytes(args.taker_secret_hash.to_vec()), + Token::FixedBytes(args.maker_secret_hash.to_vec()), + Token::Address(token_address), // erc20 token address from EthCoinType::Erc20 + ])? + }, + EthCoinType::Nft { .. } => { + return Err(PrepareTxDataError::Internal("EthCoinType must be ETH or ERC20".into())) + }, + }; + Ok(data) + } + + /// Prepares data for EtomicSwapTakerV2 contract [spendTakerPayment](https://github.com/KomodoPlatform/etomic-swap/blob/5e15641cbf41766cd5b37b4d71842c270773f788/contracts/EtomicSwapTakerV2.sol#L164) method + async fn prepare_spend_taker_payment_data( + &self, + args: &GenTakerPaymentSpendArgs<'_, Self>, + secret: &[u8], + decoded: Vec, + token_address: Address, + ) -> Result, PrepareTxDataError> { + check_decoded_length(&decoded, 7)?; + let function = TAKER_SWAP_V2.function("spendTakerPayment")?; + let taker_address = public_to_address(args.taker_pub); + let data = function.encode_input(&[ + decoded[0].clone(), // id from takerPaymentApprove + decoded[1].clone(), // amount from takerPaymentApprove + decoded[2].clone(), // dexFee from takerPaymentApprove + Token::Address(taker_address), // taker address + decoded[4].clone(), // takerSecretHash from ethTakerPayment + Token::FixedBytes(secret.to_vec()), // makerSecret + Token::Address(token_address), // tokenAddress + ])?; + Ok(data) + } + + /// Retrieves the payment status from a given smart contract address based on the swap ID and state type. + pub(crate) async fn payment_status_v2( + &self, + swap_address: Address, + swap_id: Token, + contract_abi: &Contract, + payment_type: EthPaymentType, + state_index: usize, + ) -> Result { + let function_name = payment_type.as_str(); + let function = contract_abi.function(function_name)?; + let data = function.encode_input(&[swap_id])?; + let bytes = self + .call_request(self.my_addr().await, swap_address, None, Some(data.into())) + .await?; + let decoded_tokens = function.decode_output(&bytes.0)?; + + let state = decoded_tokens.get(state_index).ok_or_else(|| { + PaymentStatusErr::Internal(format!( + "Payment status must contain 'state' as the {} token", + state_index + )) + })?; + match state { + Token::Uint(state) => Ok(*state), + _ => Err(PaymentStatusErr::InvalidData(format!( + "Payment status must be Uint, got {:?}", + state + ))), + } + } + + /// Retrieves the taker smart contract address, the corresponding function, and the token address. + /// + /// Depending on the coin type (ETH or ERC20), it fetches the appropriate function name and token address. + /// Returns an error if the coin type is NFT or if the `swap_v2_contracts` is None. + async fn taker_swap_v2_details( + &self, + eth_func_name: &str, + erc20_func_name: &str, + ) -> Result<(Address, &Function, Address), TransactionErr> { + let (func, token_address) = match self.coin_type { + EthCoinType::Eth => (try_tx_s!(TAKER_SWAP_V2.function(eth_func_name)), Address::default()), + EthCoinType::Erc20 { token_addr, .. } => (try_tx_s!(TAKER_SWAP_V2.function(erc20_func_name)), token_addr), + EthCoinType::Nft { .. } => { + return Err(TransactionErr::ProtocolNotSupported(ERRL!( + "NFT protocol is not supported for ETH and ERC20 Swaps" + ))) + }, + }; + let taker_swap_v2_contract = self + .swap_v2_contracts + .as_ref() + .map(|contracts| contracts.taker_swap_v2_contract) + .ok_or_else(|| TransactionErr::Plain(ERRL!("Expected swap_v2_contracts to be Some, but found None")))?; + Ok((taker_swap_v2_contract, func, token_address)) + } + + async fn get_decoded_and_swap_contract( + &self, + tx: &SignedEthTx, + function_name: &str, + ) -> Result<(Vec, Address), PrepareTxDataError> { + let decoded = { + let func = match self.coin_type { + EthCoinType::Eth | EthCoinType::Erc20 { .. } => TAKER_SWAP_V2.function(function_name)?, + EthCoinType::Nft { .. } => { + return Err(PrepareTxDataError::Internal( + "NFT protocol is not supported for ETH and ERC20 Swaps".to_string(), + )); + }, + }; + decode_contract_call(func, tx.unsigned().data())? + }; + let taker_swap_v2_contract = self + .swap_v2_contracts + .as_ref() + .map(|contracts| contracts.taker_swap_v2_contract) + .ok_or_else(|| { + PrepareTxDataError::Internal("Expected swap_v2_contracts to be Some, but found None".to_string()) + })?; + + Ok((decoded, taker_swap_v2_contract)) + } +} + +#[derive(Debug, Display, EnumFromStringify)] +pub(crate) enum PrepareTxDataError { + #[from_stringify("ethabi::Error")] + #[display(fmt = "ABI error: {}", _0)] + ABIError(String), + #[display(fmt = "Internal error: {}", _0)] + Internal(String), +} + +// TODO validate premium when add its support in swap_v2 +fn validate_payment_args<'a>( + taker_secret_hash: &'a [u8], + maker_secret_hash: &'a [u8], + trading_amount: &BigDecimal, +) -> Result<(), String> { + if !is_positive(trading_amount) { + return Err("trading_amount must be a positive value".to_string()); + } + if taker_secret_hash.len() != 32 { + return Err("taker_secret_hash must be 32 bytes".to_string()); + } + if maker_secret_hash.len() != 32 { + return Err("maker_secret_hash must be 32 bytes".to_string()); + } + Ok(()) +} + +/// function to check if BigDecimal is a positive value +#[inline(always)] +fn is_positive(amount: &BigDecimal) -> bool { amount > &BigDecimal::from(0) } + +pub(crate) fn validate_from_to_and_status( + tx_from_rpc: &Web3Tx, + expected_from: Address, + expected_to: Address, + status: U256, + expected_status: u8, +) -> Result<(), MmError> { + if status != U256::from(expected_status) { + return MmError::err(ValidatePaymentV2Err::UnexpectedPaymentState(format!( + "Payment state is not `PaymentSent`, got {}", + status + ))); + } + if tx_from_rpc.from != Some(expected_from) { + return MmError::err(ValidatePaymentV2Err::WrongPaymentTx(format!( + "Payment tx {:?} was sent from wrong address, expected {:?}", + tx_from_rpc, expected_from + ))); + } + // (in NFT case) as NFT owner calls "safeTransferFrom" directly, then in Transaction 'to' field we expect token_address + if tx_from_rpc.to != Some(expected_to) { + return MmError::err(ValidatePaymentV2Err::WrongPaymentTx(format!( + "Payment tx {:?} was sent to wrong address, expected {:?}", + tx_from_rpc, expected_to, + ))); + } + Ok(()) +} + +struct TakerValidationArgs<'a> { + swap_id: Vec, + amount: U256, + dex_fee: U256, + receiver: Address, + taker_secret_hash: &'a [u8], + maker_secret_hash: &'a [u8], + funding_time_lock: u64, + payment_time_lock: u64, +} + +/// Validation function for ETH taker payment data +fn validate_eth_taker_payment_data( + decoded: &[Token], + args: &TakerValidationArgs, + func: &Function, + tx_value: U256, +) -> Result<(), MmError> { + let checks = vec![ + (0, Token::FixedBytes(args.swap_id.clone()), "id"), + (1, Token::Uint(args.dex_fee), "dexFee"), + (2, Token::Address(args.receiver), "receiver"), + (3, Token::FixedBytes(args.taker_secret_hash.to_vec()), "takerSecretHash"), + (4, Token::FixedBytes(args.maker_secret_hash.to_vec()), "makerSecretHash"), + (5, Token::Uint(U256::from(args.funding_time_lock)), "preApproveLockTime"), + (6, Token::Uint(U256::from(args.payment_time_lock)), "paymentLockTime"), + ]; + + for (index, expected_token, field_name) in checks { + let token = get_function_input_data(decoded, func, index).map_to_mm(ValidateSwapV2TxError::Internal)?; + if token != expected_token { + return MmError::err(ValidateSwapV2TxError::WrongPaymentTx(format!( + "ETH Taker Payment `{}` {:?} is invalid, expected {:?}", + field_name, + decoded.get(index), + expected_token + ))); + } + } + let total = args.amount.checked_add(args.dex_fee).ok_or_else(|| { + ValidateSwapV2TxError::Overflow("Overflow occurred while calculating total payment".to_string()) + })?; + if total != tx_value { + return MmError::err(ValidateSwapV2TxError::WrongPaymentTx(format!( + "ETH Taker Payment amount, is invalid, expected {:?}, got {:?}", + total, tx_value + ))); + } + Ok(()) +} + +/// Validation function for ERC20 taker payment data +fn validate_erc20_taker_payment_data( + decoded: &[Token], + args: &TakerValidationArgs, + func: &Function, + token_addr: Address, +) -> Result<(), MmError> { + let checks = vec![ + (0, Token::FixedBytes(args.swap_id.clone()), "id"), + (1, Token::Uint(args.amount), "amount"), + (2, Token::Uint(args.dex_fee), "dexFee"), + (3, Token::Address(token_addr), "tokenAddress"), + (4, Token::Address(args.receiver), "receiver"), + (5, Token::FixedBytes(args.taker_secret_hash.to_vec()), "takerSecretHash"), + (6, Token::FixedBytes(args.maker_secret_hash.to_vec()), "makerSecretHash"), + (7, Token::Uint(U256::from(args.funding_time_lock)), "preApproveLockTime"), + (8, Token::Uint(U256::from(args.payment_time_lock)), "paymentLockTime"), + ]; + + for (index, expected_token, field_name) in checks { + let token = get_function_input_data(decoded, func, index).map_to_mm(ValidateSwapV2TxError::Internal)?; + if token != expected_token { + return MmError::err(ValidateSwapV2TxError::WrongPaymentTx(format!( + "ERC20 Taker Payment `{}` {:?} is invalid, expected {:?}", + field_name, + decoded.get(index), + expected_token + ))); + } + } + Ok(()) +} + +pub(crate) fn validate_payment_state( + tx: &SignedEthTx, + state: U256, + expected_state: u8, +) -> Result<(), PrepareTxDataError> { + if state != U256::from(expected_state) { + return Err(PrepareTxDataError::Internal(format!( + "Payment {:?} state is not `{}`, got `{}`", + tx, expected_state, state + ))); + } + Ok(()) +} + +#[derive(Debug, Display)] +pub(crate) enum ValidatePaymentV2Err { + UnexpectedPaymentState(String), + WrongPaymentTx(String), +} + +pub(crate) enum EthPaymentType { + MakerPayments, + TakerPayments, +} + +impl EthPaymentType { + pub(crate) fn as_str(&self) -> &'static str { + match self { + EthPaymentType::MakerPayments => "makerPayments", + EthPaymentType::TakerPayments => "takerPayments", + } + } } -#[allow(dead_code)] #[derive(Debug, Display, EnumFromStringify)] -enum PrepareTxDataError { +pub(crate) enum PaymentStatusErr { #[from_stringify("ethabi::Error")] - #[display(fmt = "Abi error: {}", _0)] - AbiError(String), + #[display(fmt = "ABI error: {}", _0)] + ABIError(String), + #[from_stringify("web3::Error")] + #[display(fmt = "Transport error: {}", _0)] + Transport(String), #[display(fmt = "Internal error: {}", _0)] Internal(String), + #[display(fmt = "Invalid data error: {}", _0)] + InvalidData(String), +} + +fn check_decoded_length(decoded: &Vec, expected_len: usize) -> Result<(), PrepareTxDataError> { + if decoded.len() != expected_len { + return Err(PrepareTxDataError::Internal(format!( + "Invalid number of tokens in decoded. Expected {}, found {}", + expected_len, + decoded.len() + ))); + } + Ok(()) } diff --git a/mm2src/coins/eth/nft_swap_v2/errors.rs b/mm2src/coins/eth/nft_swap_v2/errors.rs index e66cd437d0..02cb3c7626 100644 --- a/mm2src/coins/eth/nft_swap_v2/errors.rs +++ b/mm2src/coins/eth/nft_swap_v2/errors.rs @@ -1,41 +1,27 @@ +pub(crate) use crate::eth::eth_swap_v2::PrepareTxDataError; use enum_derives::EnumFromStringify; #[derive(Debug, Display)] pub(crate) enum Erc721FunctionError { - AbiError(String), + #[display(fmt = "ABI error: {}", _0)] + ABIError(String), FunctionNotFound(String), } -#[derive(Debug, Display)] +#[derive(Debug, Display, EnumFromStringify)] pub(crate) enum HtlcParamsError { WrongPaymentTx(String), - TxDeserializationError(String), -} - -#[derive(Debug, Display, EnumFromStringify)] -pub(crate) enum PaymentStatusErr { - #[from_stringify("ethabi::Error")] - #[display(fmt = "Abi error: {}", _0)] - AbiError(String), - #[from_stringify("web3::Error")] - #[display(fmt = "Transport error: {}", _0)] - Transport(String), - #[display(fmt = "Internal error: {}", _0)] - Internal(String), - #[display(fmt = "Tx deserialization error: {}", _0)] - TxDeserializationError(String), -} - -#[derive(Debug, Display, EnumFromStringify)] -pub(crate) enum PrepareTxDataError { #[from_stringify("ethabi::Error")] - #[display(fmt = "Abi error: {}", _0)] - AbiError(String), - #[display(fmt = "Internal error: {}", _0)] - Internal(String), - Erc721FunctionError(Erc721FunctionError), + #[display(fmt = "ABI error: {}", _0)] + ABIError(String), + InvalidData(String), } impl From for PrepareTxDataError { - fn from(e: Erc721FunctionError) -> Self { Self::Erc721FunctionError(e) } + fn from(e: Erc721FunctionError) -> Self { + match e { + Erc721FunctionError::ABIError(e) => PrepareTxDataError::ABIError(e), + Erc721FunctionError::FunctionNotFound(e) => PrepareTxDataError::Internal(e), + } + } } diff --git a/mm2src/coins/eth/nft_swap_v2/mod.rs b/mm2src/coins/eth/nft_swap_v2/mod.rs index 52bff94978..d5de6dcc86 100644 --- a/mm2src/coins/eth/nft_swap_v2/mod.rs +++ b/mm2src/coins/eth/nft_swap_v2/mod.rs @@ -6,15 +6,16 @@ use ethkey::public_to_address; use futures::compat::Future01CompatExt; use mm2_err_handle::prelude::{MapToMmResult, MmError, MmResult}; use mm2_number::BigDecimal; -use std::convert::TryInto; -use web3::types::{Transaction as Web3Tx, TransactionId}; +use web3::types::TransactionId; pub(crate) mod errors; -use errors::{Erc721FunctionError, HtlcParamsError, PaymentStatusErr, PrepareTxDataError}; +use errors::{Erc721FunctionError, HtlcParamsError, PrepareTxDataError}; mod structs; -use structs::{ExpectedHtlcParams, PaymentType, ValidationParams}; +use structs::{ExpectedHtlcParams, ValidationParams}; use super::ContractType; +use crate::eth::eth_swap_v2::{validate_from_to_and_status, validate_payment_state, EthPaymentType, PaymentStatusErr, + ZERO_VALUE}; use crate::eth::{decode_contract_call, EthCoin, EthCoinType, MakerPaymentStateV2, SignedEthTx, ERC1155_CONTRACT, ERC721_CONTRACT, NFT_MAKER_SWAP_V2}; use crate::{ParseCoinAssocTypes, RefundNftMakerPaymentArgs, SendNftMakerPaymentArgs, SpendNftMakerPaymentArgs, @@ -37,7 +38,7 @@ impl EthCoin { let data = try_tx_s!(self.prepare_nft_maker_payment_v2_data(&args, htlc_data).await); self.sign_and_send_transaction( - 0.into(), + ZERO_VALUE.into(), Action::Call(*args.nft_swap_info.token_address), data, U256::from(self.gas_limit.eth_max_trade_gas), // TODO: fix to a more accurate const or estimated value @@ -45,9 +46,9 @@ impl EthCoin { .compat() .await }, - EthCoinType::Eth | EthCoinType::Erc20 { .. } => Err(TransactionErr::ProtocolNotSupported( - "ETH and ERC20 Protocols are not supported for NFT Swaps".to_string(), - )), + EthCoinType::Eth | EthCoinType::Erc20 { .. } => Err(TransactionErr::ProtocolNotSupported(ERRL!( + "ETH and ERC20 protocols are not supported for NFT swaps." + ))), } } @@ -65,20 +66,18 @@ impl EthCoin { contract_type, ) .map_err(ValidatePaymentError::InternalError)?; + // TODO use swap contract address from self let etomic_swap_contract = args.nft_swap_info.swap_contract_address; let token_address = args.nft_swap_info.token_address; let maker_address = public_to_address(args.maker_pub); - let time_lock_u32 = args - .time_lock - .try_into() - .map_err(ValidatePaymentError::TimelockOverflow)?; - let swap_id = self.etomic_swap_id(time_lock_u32, args.maker_secret_hash); + let swap_id = self.etomic_swap_id_v2(args.time_lock, args.maker_secret_hash); let maker_status = self .payment_status_v2( *etomic_swap_contract, Token::FixedBytes(swap_id.clone()), &NFT_MAKER_SWAP_V2, - PaymentType::MakerPayments, + EthPaymentType::MakerPayments, + 2, ) .await?; let tx_from_rpc = self @@ -90,7 +89,13 @@ impl EthCoin { args.maker_payment_tx.tx_hash() )) })?; - validate_from_to_and_maker_status(tx_from_rpc, maker_address, *token_address, maker_status).await?; + validate_from_to_and_status( + tx_from_rpc, + maker_address, + *token_address, + maker_status, + MakerPaymentStateV2::PaymentSent as u8, + )?; let (decoded, bytes_index) = get_decoded_tx_data_and_bytes_index(contract_type, &tx_from_rpc.input.0)?; @@ -149,13 +154,14 @@ impl EthCoin { &NFT_MAKER_SWAP_V2, &decoded, bytes_index, - PaymentType::MakerPayments, + EthPaymentType::MakerPayments, + 2 ) .await ); let data = try_tx_s!(self.prepare_spend_nft_maker_v2_data(&args, decoded, htlc_params, state)); self.sign_and_send_transaction( - 0.into(), + ZERO_VALUE.into(), Action::Call(*etomic_swap_contract), data, U256::from(self.gas_limit.eth_max_trade_gas), // TODO: fix to a more accurate const or estimated value @@ -163,9 +169,9 @@ impl EthCoin { .compat() .await }, - EthCoinType::Eth | EthCoinType::Erc20 { .. } => Err(TransactionErr::ProtocolNotSupported( - "ETH and ERC20 Protocols are not supported for NFT Swaps".to_string(), - )), + EthCoinType::Eth | EthCoinType::Erc20 { .. } => Err(TransactionErr::ProtocolNotSupported(ERRL!( + "ETH and ERC20 protocols are not supported for NFT swaps." + ))), } } @@ -187,14 +193,15 @@ impl EthCoin { &NFT_MAKER_SWAP_V2, &decoded, bytes_index, - PaymentType::MakerPayments, + EthPaymentType::MakerPayments, + 2 ) .await ); let data = try_tx_s!(self.prepare_refund_nft_maker_payment_v2_timelock(&args, decoded, htlc_params, state)); self.sign_and_send_transaction( - 0.into(), + ZERO_VALUE.into(), Action::Call(*etomic_swap_contract), data, U256::from(self.gas_limit.eth_max_trade_gas), // TODO: fix to a more accurate const or estimated value @@ -202,9 +209,9 @@ impl EthCoin { .compat() .await }, - EthCoinType::Eth | EthCoinType::Erc20 { .. } => Err(TransactionErr::ProtocolNotSupported( - "ETH and ERC20 Protocols are not supported for NFT Swaps".to_string(), - )), + EthCoinType::Eth | EthCoinType::Erc20 { .. } => Err(TransactionErr::ProtocolNotSupported(ERRL!( + "ETH and ERC20 protocols are not supported for NFT swaps." + ))), } } @@ -226,7 +233,8 @@ impl EthCoin { &NFT_MAKER_SWAP_V2, &decoded, bytes_index, - PaymentType::MakerPayments, + EthPaymentType::MakerPayments, + 2 ) .await ); @@ -234,7 +242,7 @@ impl EthCoin { let data = try_tx_s!(self.prepare_refund_nft_maker_payment_v2_secret(&args, decoded, htlc_params, state)); self.sign_and_send_transaction( - 0.into(), + ZERO_VALUE.into(), Action::Call(*etomic_swap_contract), data, U256::from(self.gas_limit.eth_max_trade_gas), // TODO: fix to a more accurate const or estimated value @@ -242,9 +250,9 @@ impl EthCoin { .compat() .await }, - EthCoinType::Eth | EthCoinType::Erc20 { .. } => Err(TransactionErr::ProtocolNotSupported( - "ETH and ERC20 Protocols are not supported for NFT Swaps".to_string(), - )), + EthCoinType::Eth | EthCoinType::Erc20 { .. } => Err(TransactionErr::ProtocolNotSupported(ERRL!( + "ETH and ERC20 protocols are not supported for NFT swaps." + ))), } } @@ -282,50 +290,18 @@ impl EthCoin { fn prepare_htlc_data(&self, args: &SendNftMakerPaymentArgs<'_, Self>) -> Result, PrepareTxDataError> { let taker_address = public_to_address(args.taker_pub); - let time_lock_u32 = args - .time_lock - .try_into() - .map_err(|e| PrepareTxDataError::Internal(ERRL!("{}", e)))?; - let id = self.etomic_swap_id(time_lock_u32, args.maker_secret_hash); + let id = self.etomic_swap_id_v2(args.time_lock, args.maker_secret_hash); let encoded = ethabi::encode(&[ Token::FixedBytes(id), Token::Address(taker_address), Token::Address(*args.nft_swap_info.token_address), Token::FixedBytes(args.taker_secret_hash.to_vec()), Token::FixedBytes(args.maker_secret_hash.to_vec()), - Token::Uint(U256::from(time_lock_u32)), + Token::Uint(U256::from(args.time_lock)), ]); Ok(encoded) } - /// Retrieves the payment status from a given smart contract address based on the swap ID and state type. - async fn payment_status_v2( - &self, - swap_address: Address, - swap_id: Token, - contract_abi: &Contract, - state_type: PaymentType, - ) -> Result { - let function_name = state_type.as_str(); - let function = contract_abi.function(function_name)?; - let data = function.encode_input(&[swap_id])?; - let bytes = self - .call_request(self.my_addr().await, swap_address, None, Some(data.into())) - .await?; - let decoded_tokens = function.decode_output(&bytes.0)?; - - let state = decoded_tokens - .get(2) - .ok_or_else(|| PaymentStatusErr::Internal(ERRL!("Payment status must contain 'state' as the 2nd token")))?; - match state { - Token::Uint(state) => Ok(*state), - _ => Err(PaymentStatusErr::Internal(ERRL!( - "Payment status must be Uint, got {:?}", - state - ))), - } - } - /// Prepares the encoded transaction data for spending a maker's NFT payment on the blockchain. /// /// This function selects the appropriate contract function based on the NFT's contract type (ERC1155 or ERC721) @@ -428,28 +404,30 @@ impl EthCoin { contract_abi: &Contract, decoded_data: &[Token], index: usize, - state_type: PaymentType, + payment_type: EthPaymentType, + state_index: usize, ) -> Result<(U256, Vec), PaymentStatusErr> { let data_bytes = match decoded_data.get(index) { Some(Token::Bytes(data_bytes)) => data_bytes, _ => { - return Err(PaymentStatusErr::TxDeserializationError(ERRL!( + return Err(PaymentStatusErr::InvalidData(ERRL!( "Failed to decode HTLCParams from data_bytes" ))) }, }; - let htlc_params = match ethabi::decode(htlc_params(), data_bytes) { - Ok(htlc_params) => htlc_params, - Err(_) => { - return Err(PaymentStatusErr::TxDeserializationError(ERRL!( - "Failed to decode HTLCParams from data_bytes" - ))) - }, - }; + let htlc_params = + ethabi::decode(htlc_params(), data_bytes).map_err(|e| PaymentStatusErr::ABIError(ERRL!("{}", e)))?; let state = self - .payment_status_v2(swap_address, htlc_params[0].clone(), contract_abi, state_type) + .payment_status_v2( + swap_address, + // swap_id has 0 index + htlc_params[0].clone(), + contract_abi, + payment_type, + state_index, + ) .await?; Ok((state, htlc_params)) @@ -495,20 +473,13 @@ fn decode_and_validate_htlc_params( let data_bytes = match decoded.get(index) { Some(Token::Bytes(bytes)) => bytes, _ => { - return MmError::err(HtlcParamsError::TxDeserializationError( + return MmError::err(HtlcParamsError::InvalidData( "Expected Bytes for HTLCParams data".to_string(), )) }, }; - let decoded_params = match ethabi::decode(htlc_params(), data_bytes) { - Ok(params) => params, - Err(_) => { - return MmError::err(HtlcParamsError::TxDeserializationError( - "Failed to decode HTLCParams from data_bytes".to_string(), - )) - }, - }; + let decoded_params = ethabi::decode(htlc_params(), data_bytes)?; let expected_taker_secret_hash = Token::FixedBytes(expected_params.taker_secret_hash.clone()); let expected_maker_secret_hash = Token::FixedBytes(expected_params.maker_secret_hash.clone()); @@ -587,34 +558,6 @@ fn validate_payment_args<'a>( Ok(()) } -async fn validate_from_to_and_maker_status( - tx_from_rpc: &Web3Tx, - expected_from: Address, - expected_to: Address, - maker_status: U256, -) -> ValidatePaymentResult<()> { - if maker_status != U256::from(MakerPaymentStateV2::PaymentSent as u8) { - return MmError::err(ValidatePaymentError::UnexpectedPaymentState(format!( - "NFT Maker Payment state is not PAYMENT_STATE_SENT, got {}", - maker_status - ))); - } - if tx_from_rpc.from != Some(expected_from) { - return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "NFT Maker Payment tx {:?} was sent from wrong address, expected {:?}", - tx_from_rpc, expected_from - ))); - } - // As NFT owner calls "safeTransferFrom" directly, then in Transaction 'to' field we expect token_address - if tx_from_rpc.to != Some(expected_to) { - return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( - "NFT Maker Payment tx {:?} was sent to wrong address, expected {:?}", - tx_from_rpc, expected_to, - ))); - } - Ok(()) -} - /// Identifies the correct `"safeTransferFrom"` function based on the contract type (either ERC1155 or ERC721) /// and decodes the provided contract call bytes using the ABI of the identified function. Additionally, it returns /// the index position of the "bytes" field within the function's parameters. @@ -637,7 +580,7 @@ pub(crate) fn get_decoded_tx_data_and_bytes_index( fn erc721_transfer_with_data<'a>() -> Result<&'a ethabi::Function, Erc721FunctionError> { let functions = ERC721_CONTRACT .functions_by_name("safeTransferFrom") - .map_err(|e| Erc721FunctionError::AbiError(ERRL!("{}", e)))?; + .map_err(|e| Erc721FunctionError::ABIError(ERRL!("{}", e)))?; // Find the correct function variant by inspecting the input parameters. let function = functions @@ -656,15 +599,3 @@ fn erc721_transfer_with_data<'a>() -> Result<&'a ethabi::Function, Erc721Functio })?; Ok(function) } - -fn validate_payment_state(tx: &SignedEthTx, state: U256, expected_state: u8) -> Result<(), PrepareTxDataError> { - if state != U256::from(expected_state) { - return Err(PrepareTxDataError::Internal(ERRL!( - "Payment {:?} state is not {}, got {}", - tx, - expected_state, - state - ))); - } - Ok(()) -} diff --git a/mm2src/coins/eth/nft_swap_v2/structs.rs b/mm2src/coins/eth/nft_swap_v2/structs.rs index a0c129bf4b..2866d81a01 100644 --- a/mm2src/coins/eth/nft_swap_v2/structs.rs +++ b/mm2src/coins/eth/nft_swap_v2/structs.rs @@ -16,18 +16,3 @@ pub(crate) struct ValidationParams<'a> { // Optional, as it's not needed for ERC721 pub(crate) amount: Option, } - -#[allow(dead_code)] -pub(crate) enum PaymentType { - MakerPayments, - TakerPayments, -} - -impl PaymentType { - pub(crate) fn as_str(&self) -> &'static str { - match self { - PaymentType::MakerPayments => "makerPayments", - PaymentType::TakerPayments => "takerPayments", - } - } -} diff --git a/mm2src/coins/eth/v2_activation.rs b/mm2src/coins/eth/v2_activation.rs index dffa0a4e17..15af41b3c2 100644 --- a/mm2src/coins/eth/v2_activation.rs +++ b/mm2src/coins/eth/v2_activation.rs @@ -541,7 +541,7 @@ impl EthCoin { } /// Activate eth coin from coin config and private key build policy, -/// version 2 of the activation function, with no intrinsic tokens creation +/// version 2 of the activation function, with no intrinsic tokens creation. pub async fn eth_coin_from_conf_and_request_v2( ctx: &MmArc, ticker: &str, @@ -796,7 +796,6 @@ async fn build_web3_instances( let event_handlers = rpc_event_handlers_for_eth_transport(ctx, coin_ticker.clone()); - let p2p_ctx = P2PContext::fetch_from_mm_arc(ctx); let mut web3_instances = Vec::with_capacity(eth_nodes.len()); for eth_node in eth_nodes { let uri: Uri = eth_node @@ -804,51 +803,7 @@ async fn build_web3_instances( .parse() .map_err(|_| EthActivationV2Error::InvalidPayload(format!("{} could not be parsed.", eth_node.url)))?; - let transport = match uri.scheme_str() { - Some("ws") | Some("wss") => { - const TMP_SOCKET_CONNECTION: Duration = Duration::from_secs(20); - - let node = WebsocketTransportNode { uri: uri.clone() }; - - let mut websocket_transport = WebsocketTransport::with_event_handlers(node, event_handlers.clone()); - - if eth_node.komodo_proxy { - websocket_transport.proxy_sign_keypair = Some(p2p_ctx.keypair().clone()); - } - - // Temporarily start the connection loop (we close the connection once we have the client version below). - // Ideally, it would be much better to not do this workaround, which requires a lot of refactoring or - // dropping websocket support on parity nodes. - let fut = websocket_transport - .clone() - .start_connection_loop(Some(Instant::now() + TMP_SOCKET_CONNECTION)); - let settings = AbortSettings::info_on_abort(format!("connection loop stopped for {:?}", uri)); - ctx.spawner().spawn_with_settings(fut, settings); - - Web3Transport::Websocket(websocket_transport) - }, - Some("http") | Some("https") => { - let node = HttpTransportNode { - uri, - komodo_proxy: eth_node.komodo_proxy, - }; - - let komodo_proxy = node.komodo_proxy; - let mut http_transport = HttpTransport::with_event_handlers(node, event_handlers.clone()); - - if komodo_proxy { - http_transport.proxy_sign_keypair = Some(p2p_ctx.keypair().clone()); - } - - Web3Transport::from(http_transport) - }, - _ => { - return MmError::err(EthActivationV2Error::InvalidPayload(format!( - "Invalid node address '{uri}'. Only http(s) and ws(s) nodes are supported" - ))); - }, - }; - + let transport = create_transport(ctx, &uri, ð_node, &event_handlers)?; let web3 = Web3::new(transport); let version = match web3.web3().client_version().await { Ok(v) => v, @@ -873,6 +828,70 @@ async fn build_web3_instances( Ok(web3_instances) } +fn create_transport( + ctx: &MmArc, + uri: &Uri, + eth_node: &EthNode, + event_handlers: &[RpcTransportEventHandlerShared], +) -> MmResult { + match uri.scheme_str() { + Some("ws") | Some("wss") => Ok(create_websocket_transport(ctx, uri, eth_node, event_handlers)), + Some("http") | Some("https") => Ok(create_http_transport(ctx, uri, eth_node, event_handlers)), + _ => MmError::err(EthActivationV2Error::InvalidPayload(format!( + "Invalid node address '{uri}'. Only http(s) and ws(s) nodes are supported" + ))), + } +} + +fn create_websocket_transport( + ctx: &MmArc, + uri: &Uri, + eth_node: &EthNode, + event_handlers: &[RpcTransportEventHandlerShared], +) -> Web3Transport { + const TMP_SOCKET_CONNECTION: Duration = Duration::from_secs(20); + + let node = WebsocketTransportNode { uri: uri.clone() }; + + let mut websocket_transport = WebsocketTransport::with_event_handlers(node, event_handlers.to_owned()); + + if eth_node.komodo_proxy { + websocket_transport.proxy_sign_keypair = Some(P2PContext::fetch_from_mm_arc(ctx).keypair().clone()); + } + + // Temporarily start the connection loop (we close the connection once we have the client version below). + // Ideally, it would be much better to not do this workaround, which requires a lot of refactoring or + // dropping websocket support on parity nodes. + let fut = websocket_transport + .clone() + .start_connection_loop(Some(Instant::now() + TMP_SOCKET_CONNECTION)); + let settings = AbortSettings::info_on_abort(format!("connection loop stopped for {:?}", uri)); + ctx.spawner().spawn_with_settings(fut, settings); + + Web3Transport::Websocket(websocket_transport) +} + +fn create_http_transport( + ctx: &MmArc, + uri: &Uri, + eth_node: &EthNode, + event_handlers: &[RpcTransportEventHandlerShared], +) -> Web3Transport { + let node = HttpTransportNode { + uri: uri.clone(), + komodo_proxy: eth_node.komodo_proxy, + }; + + let komodo_proxy = node.komodo_proxy; + let mut http_transport = HttpTransport::with_event_handlers(node, event_handlers.to_owned()); + + if komodo_proxy { + http_transport.proxy_sign_keypair = Some(P2PContext::fetch_from_mm_arc(ctx).keypair().clone()); + } + + Web3Transport::from(http_transport) +} + #[cfg(target_arch = "wasm32")] async fn build_metamask_transport( ctx: &MmArc, diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 4e5d281b33..8ca714a34f 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -240,6 +240,7 @@ use coin_errors::{MyAddressError, ValidatePaymentError, ValidatePaymentFut, Vali pub mod coins_tests; pub mod eth; +use eth::eth_swap_v2::{PaymentStatusErr, ValidatePaymentV2Err}; use eth::GetValidEthWithdrawAddError; use eth::{eth_coin_from_conf_and_request, get_eth_address, EthCoin, EthGasDetailsErr, EthTxFeeDetails, GetEthAddressError, SignedEthTx}; @@ -319,6 +320,7 @@ pub mod z_coin; use crate::coin_balance::{BalanceObjectOps, HDWalletBalanceObject}; use z_coin::{ZCoin, ZcoinProtocolInfo}; #[cfg(feature = "enable-sia")] pub mod sia; +use crate::eth::eth_swap_v2::PrepareTxDataError; #[cfg(feature = "enable-sia")] use sia::SiaCoin; pub type TransactionFut = Box + Send>; @@ -885,7 +887,10 @@ pub enum SwapTxTypeWithSecretHash<'a> { taker_secret_hash: &'a [u8], }, /// Taker payment v2 - TakerPaymentV2 { maker_secret_hash: &'a [u8] }, + TakerPaymentV2 { + maker_secret_hash: &'a [u8], + taker_secret_hash: &'a [u8], + }, } impl<'a> SwapTxTypeWithSecretHash<'a> { @@ -907,7 +912,7 @@ impl<'a> SwapTxTypeWithSecretHash<'a> { my_public, other_public, ), - SwapTxTypeWithSecretHash::TakerPaymentV2 { maker_secret_hash } => { + SwapTxTypeWithSecretHash::TakerPaymentV2 { maker_secret_hash, .. } => { swap_proto_v2_scripts::taker_payment_script(time_lock, maker_secret_hash, my_public, other_public) }, } @@ -921,7 +926,7 @@ impl<'a> SwapTxTypeWithSecretHash<'a> { maker_secret_hash, taker_secret_hash, } => [*maker_secret_hash, *taker_secret_hash].concat(), - SwapTxTypeWithSecretHash::TakerPaymentV2 { maker_secret_hash } => maker_secret_hash.to_vec(), + SwapTxTypeWithSecretHash::TakerPaymentV2 { maker_secret_hash, .. } => maker_secret_hash.to_vec(), } } } @@ -985,6 +990,32 @@ pub struct RefundPaymentArgs<'a> { pub watcher_reward: bool, } +#[derive(Debug)] +pub struct RefundMakerPaymentTimelockArgs<'a> { + pub payment_tx: &'a [u8], + pub time_lock: u64, + pub taker_pub: &'a [u8], + pub tx_type_with_secret_hash: SwapTxTypeWithSecretHash<'a>, + pub swap_contract_address: &'a Option, + pub swap_unique_data: &'a [u8], + pub watcher_reward: bool, +} + +#[derive(Debug)] +pub struct RefundTakerPaymentArgs<'a> { + pub payment_tx: &'a [u8], + pub time_lock: u64, + pub maker_pub: &'a [u8], + pub tx_type_with_secret_hash: SwapTxTypeWithSecretHash<'a>, + pub swap_unique_data: &'a [u8], + pub watcher_reward: bool, + pub dex_fee: &'a DexFee, + /// Additional reward for maker (premium) + pub premium_amount: BigDecimal, + /// Actual volume of taker's payment + pub trading_amount: BigDecimal, +} + /// Helper struct wrapping arguments for [SwapOps::check_if_my_payment_sent]. #[derive(Clone, Debug)] pub struct CheckIfMyPaymentSentArgs<'a> { @@ -1310,8 +1341,6 @@ pub struct RefundFundingSecretArgs<'a, Coin: ParseCoinAssocTypes + ?Sized> { pub taker_secret: &'a [u8], pub taker_secret_hash: &'a [u8], pub maker_secret_hash: &'a [u8], - pub swap_contract_address: &'a Option, - /// DEX fee pub dex_fee: &'a DexFee, /// Additional reward for maker (premium) pub premium_amount: BigDecimal, @@ -1343,12 +1372,18 @@ pub struct GenTakerFundingSpendArgs<'a, Coin: ParseCoinAssocTypes + ?Sized> { pub struct ValidateTakerFundingArgs<'a, Coin: ParseCoinAssocTypes + ?Sized> { /// Taker funding transaction pub funding_tx: &'a Coin::Tx, - /// Taker will be able to refund the payment after this timestamp - pub time_lock: u64, + /// In EVM case: The timestamp after which the taker can refund the funding transaction if the taker hasn't pre-approved the transaction + /// In UTXO case: Taker will be able to refund the payment after this timestamp + pub funding_time_lock: u64, + /// In EVM case: The timestamp after which the taker can refund the payment transaction if the maker hasn't claimed it by revealing their secret. + /// UTXO doesn't use it + pub payment_time_lock: u64, /// The hash of the secret generated by taker pub taker_secret_hash: &'a [u8], + /// The hash of the secret generated by maker + pub maker_secret_hash: &'a [u8], /// Taker's pubkey - pub other_pub: &'a Coin::Pubkey, + pub taker_pub: &'a Coin::Pubkey, /// DEX fee amount pub dex_fee: &'a DexFee, /// Additional reward for maker (premium) @@ -1429,23 +1464,36 @@ impl From for TxGenError { } /// Enum covering error cases that can happen during swap v2 transaction validation. -#[derive(Debug, Display)] +#[derive(Debug, Display, EnumFromStringify)] pub enum ValidateSwapV2TxError { /// Payment sent to wrong address or has invalid amount. InvalidDestinationOrAmount(String), /// Error during conversion of BigDecimal amount to coin's specific monetary units (satoshis, wei, etc.). NumConversion(String), /// RPC error. + #[from_stringify("web3::Error")] Rpc(String), /// Serialized tx bytes don't match ones received from coin's RPC. #[display(fmt = "Tx bytes {:02x} don't match ones received from rpc {:02x}", actual, from_rpc)] - TxBytesMismatch { from_rpc: BytesJson, actual: BytesJson }, + TxBytesMismatch { + from_rpc: BytesJson, + actual: BytesJson, + }, /// Provided transaction doesn't have output with specific index TxLacksOfOutputs, - /// Input payment timelock overflows the type used by specific coin. - LocktimeOverflow(String), + /// Indicates that overflow occurred, either while calculating a total payment or converting the timelock. + Overflow(String), /// Internal error + #[from_stringify("ethabi::Error")] Internal(String), + /// Payment transaction is in unexpected state. E.g., `Uninitialized` instead of `PaymentSent` for ETH payment. + UnexpectedPaymentState(String), + /// Payment transaction doesn't exist on-chain. + TxDoesNotExist(String), + /// Transaction has wrong properties, for example, it has been sent to a wrong address. + WrongPaymentTx(String), + ProtocolNotSupported(String), + InvalidData(String), } impl From for ValidateSwapV2TxError { @@ -1456,6 +1504,33 @@ impl From for ValidateSwapV2TxError { fn from(err: UtxoRpcError) -> Self { ValidateSwapV2TxError::Rpc(err.to_string()) } } +impl From for ValidateSwapV2TxError { + fn from(err: PaymentStatusErr) -> Self { + match err { + PaymentStatusErr::Internal(e) => ValidateSwapV2TxError::Internal(e), + PaymentStatusErr::Transport(e) => ValidateSwapV2TxError::Rpc(e), + PaymentStatusErr::ABIError(e) | PaymentStatusErr::InvalidData(e) => ValidateSwapV2TxError::InvalidData(e), + } + } +} + +impl From for ValidateSwapV2TxError { + fn from(err: ValidatePaymentV2Err) -> Self { + match err { + ValidatePaymentV2Err::UnexpectedPaymentState(e) => ValidateSwapV2TxError::UnexpectedPaymentState(e), + ValidatePaymentV2Err::WrongPaymentTx(e) => ValidateSwapV2TxError::WrongPaymentTx(e), + } + } +} + +impl From for ValidateSwapV2TxError { + fn from(err: PrepareTxDataError) -> Self { + match err { + PrepareTxDataError::ABIError(e) | PrepareTxDataError::Internal(e) => ValidateSwapV2TxError::Internal(e), + } + } +} + /// Enum covering error cases that can happen during taker funding spend preimage validation. #[derive(Debug, Display, EnumFromStringify)] pub enum ValidateTakerFundingSpendPreimageError { @@ -1634,7 +1709,7 @@ pub struct ValidateNftMakerPaymentArgs<'a, Coin: ParseCoinAssocTypes + ParseNftA pub nft_swap_info: &'a NftSwapInfo<'a, Coin>, } -pub struct RefundMakerPaymentArgs<'a, Coin: ParseCoinAssocTypes + ?Sized> { +pub struct RefundMakerPaymentSecretArgs<'a, Coin: ParseCoinAssocTypes + ?Sized> { /// Maker payment tx pub maker_payment_tx: &'a Coin::Tx, /// Maker will be able to refund the payment after this timestamp @@ -1716,12 +1791,15 @@ pub trait MakerCoinSwapOpsV2: ParseCoinAssocTypes + CommonSwapOpsV2 + Send + Syn async fn validate_maker_payment_v2(&self, args: ValidateMakerPaymentArgs<'_, Self>) -> ValidatePaymentResult<()>; /// Refund maker payment transaction using timelock path - async fn refund_maker_payment_v2_timelock(&self, args: RefundPaymentArgs<'_>) -> Result; + async fn refund_maker_payment_v2_timelock( + &self, + args: RefundMakerPaymentTimelockArgs<'_>, + ) -> Result; /// Refund maker payment transaction using immediate refund path async fn refund_maker_payment_v2_secret( &self, - args: RefundMakerPaymentArgs<'_, Self>, + args: RefundMakerPaymentSecretArgs<'_, Self>, ) -> Result; /// Spend maker payment transaction @@ -1761,7 +1839,7 @@ pub trait MakerNftSwapOpsV2: ParseCoinAssocTypes + ParseNftAssocTypes + Send + S } /// Enum representing errors that can occur while waiting for taker payment spend. -#[derive(Display)] +#[derive(Display, Debug, EnumFromStringify)] pub enum WaitForTakerPaymentSpendError { /// Timeout error variant, indicating that the wait for taker payment spend has timed out. #[display( @@ -1775,9 +1853,14 @@ pub enum WaitForTakerPaymentSpendError { /// The current timestamp when the timeout occurred. now: u64, }, - /// Invalid input transaction error variant, containing additional information about the error. InvalidInputTx(String), + Internal(String), + #[from_stringify("ethabi::Error")] + #[display(fmt = "ABI error: {}", _0)] + ABIError(String), + InvalidData(String), + Transport(String), } impl From for WaitForTakerPaymentSpendError { @@ -1793,6 +1876,26 @@ impl From for WaitForTakerPaymentSpendError { } } +impl From for WaitForTakerPaymentSpendError { + fn from(e: PaymentStatusErr) -> Self { + match e { + PaymentStatusErr::ABIError(e) => Self::ABIError(e), + PaymentStatusErr::Transport(e) => Self::Transport(e), + PaymentStatusErr::Internal(e) => Self::Internal(e), + PaymentStatusErr::InvalidData(e) => Self::InvalidData(e), + } + } +} + +impl From for WaitForTakerPaymentSpendError { + fn from(e: PrepareTxDataError) -> Self { + match e { + PrepareTxDataError::ABIError(e) => Self::ABIError(e), + PrepareTxDataError::Internal(e) => Self::Internal(e), + } + } +} + /// Enum representing different ways a funding transaction can be spent. /// /// This enum is generic over types that implement the `ParseCoinAssocTypes` trait. @@ -1838,6 +1941,7 @@ pub enum SearchForFundingSpendErr { Rpc(String), /// Variant indicating an error during conversion of the `from_block` argument with associated `TryFromIntError`. FromBlockConversionErr(TryFromIntError), + Internal(String), } /// Operations specific to taker coin in [Trading Protocol Upgrade implementation](https://github.com/KomodoPlatform/komodo-defi-framework/issues/1895) @@ -1851,7 +1955,8 @@ pub trait TakerCoinSwapOpsV2: ParseCoinAssocTypes + CommonSwapOpsV2 + Send + Syn async fn validate_taker_funding(&self, args: ValidateTakerFundingArgs<'_, Self>) -> ValidateSwapV2TxResult; /// Refunds taker funding transaction using time-locked path without secret reveal. - async fn refund_taker_funding_timelock(&self, args: RefundPaymentArgs<'_>) -> Result; + async fn refund_taker_funding_timelock(&self, args: RefundTakerPaymentArgs<'_>) + -> Result; /// Reclaims taker funding transaction using immediate refund path with secret reveal. async fn refund_taker_funding_secret( @@ -1890,7 +1995,8 @@ pub trait TakerCoinSwapOpsV2: ParseCoinAssocTypes + CommonSwapOpsV2 + Send + Syn ) -> Result; /// Refunds taker payment transaction. - async fn refund_combined_taker_payment(&self, args: RefundPaymentArgs<'_>) -> Result; + async fn refund_combined_taker_payment(&self, args: RefundTakerPaymentArgs<'_>) + -> Result; /// Generates and signs taker payment spend preimage. The preimage and signature should be /// shared with maker to proceed with protocol execution. diff --git a/mm2src/coins/qrc20.rs b/mm2src/coins/qrc20.rs index 3ee9e7761b..b1d250c755 100644 --- a/mm2src/coins/qrc20.rs +++ b/mm2src/coins/qrc20.rs @@ -452,11 +452,11 @@ pub enum Qrc20AbiError { #[display(fmt = "Invalid QRC20 ABI params: {}", _0)] InvalidParams(String), #[display(fmt = "QRC20 ABI error: {}", _0)] - AbiError(String), + ABIError(String), } impl From for Qrc20AbiError { - fn from(e: ethabi::Error) -> Qrc20AbiError { Qrc20AbiError::AbiError(e.to_string()) } + fn from(e: ethabi::Error) -> Qrc20AbiError { Qrc20AbiError::ABIError(e.to_string()) } } impl From for ValidatePaymentError { diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index a5eaff2220..10b057ee77 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -1,8 +1,8 @@ #![allow(clippy::all)] use super::{CoinBalance, CommonSwapOpsV2, FundingTxSpend, HistorySyncState, MarketCoinOps, MmCoin, RawTransactionFut, - RawTransactionRequest, SearchForFundingSpendErr, SwapOps, TradeFee, TransactionEnum, TransactionFut, - WaitForTakerPaymentSpendError}; + RawTransactionRequest, RefundTakerPaymentArgs, SearchForFundingSpendErr, SwapOps, TradeFee, + TransactionEnum, TransactionFut, WaitForTakerPaymentSpendError}; use crate::coin_errors::ValidatePaymentResult; use crate::{coin_errors::MyAddressError, BalanceFut, CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinFutSpawner, ConfirmPaymentInput, FeeApproxStage, FoundSwapTxSpend, GenPreimageResult, GenTakerFundingSpendArgs, @@ -472,7 +472,10 @@ impl TakerCoinSwapOpsV2 for TestCoin { unimplemented!() } - async fn refund_taker_funding_timelock(&self, args: RefundPaymentArgs<'_>) -> Result { + async fn refund_taker_funding_timelock( + &self, + args: RefundTakerPaymentArgs<'_>, + ) -> Result { todo!() } @@ -517,7 +520,10 @@ impl TakerCoinSwapOpsV2 for TestCoin { todo!() } - async fn refund_combined_taker_payment(&self, args: RefundPaymentArgs<'_>) -> Result { + async fn refund_combined_taker_payment( + &self, + args: RefundTakerPaymentArgs<'_>, + ) -> Result { unimplemented!() } diff --git a/mm2src/coins/utxo/qtum_delegation.rs b/mm2src/coins/utxo/qtum_delegation.rs index ad9aec86e1..f7573fafbf 100644 --- a/mm2src/coins/utxo/qtum_delegation.rs +++ b/mm2src/coins/utxo/qtum_delegation.rs @@ -45,7 +45,7 @@ pub enum QtumStakingAbiError { #[display(fmt = "Invalid QRC20 ABI params: {}", _0)] InvalidParams(String), #[display(fmt = "QRC20 ABI error: {}", _0)] - AbiError(String), + ABIError(String), #[display(fmt = "Qtum POD error: {}", _0)] PodSigningError(String), #[display(fmt = "Internal error: {}", _0)] @@ -56,7 +56,7 @@ impl From for QtumStakingAbiError { fn from(e: Qrc20AbiError) -> Self { match e { Qrc20AbiError::InvalidParams(e) => QtumStakingAbiError::InvalidParams(e), - Qrc20AbiError::AbiError(e) => QtumStakingAbiError::AbiError(e), + Qrc20AbiError::ABIError(e) => QtumStakingAbiError::ABIError(e), } } } @@ -66,7 +66,7 @@ impl From for DelegationError { } impl From for QtumStakingAbiError { - fn from(e: ethabi::Error) -> QtumStakingAbiError { QtumStakingAbiError::AbiError(e.to_string()) } + fn from(e: ethabi::Error) -> QtumStakingAbiError { QtumStakingAbiError::ABIError(e.to_string()) } } impl From for DelegationError { diff --git a/mm2src/coins/utxo/utxo_common.rs b/mm2src/coins/utxo/utxo_common.rs index 12c18f602c..175454e1da 100644 --- a/mm2src/coins/utxo/utxo_common.rs +++ b/mm2src/coins/utxo/utxo_common.rs @@ -15,7 +15,7 @@ use crate::watcher_common::validate_watcher_reward; use crate::{scan_for_new_addresses_impl, CanRefundHtlc, CoinBalance, CoinWithDerivationMethod, ConfirmPaymentInput, DexFee, GenPreimageResult, GenTakerFundingSpendArgs, GenTakerPaymentSpendArgs, GetWithdrawSenderAddress, RawTransactionError, RawTransactionRequest, RawTransactionRes, RawTransactionResult, - RefundFundingSecretArgs, RefundMakerPaymentArgs, RefundPaymentArgs, RewardTarget, + RefundFundingSecretArgs, RefundMakerPaymentSecretArgs, RefundPaymentArgs, RewardTarget, SearchForSwapTxSpendInput, SendMakerPaymentArgs, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SendTakerFundingArgs, SignRawTransactionEnum, SignRawTransactionRequest, SignUtxoTransactionParams, SignatureError, SignatureResult, SpendMakerPaymentArgs, SpendPaymentArgs, SwapOps, @@ -2871,13 +2871,12 @@ pub async fn wait_for_output_spend_impl( wait_until: u64, check_every: f64, ) -> MmResult { + let script_pubkey = &tx + .outputs + .get(output_index) + .or_mm_err(|| WaitForOutputSpendErr::NoOutputWithIndex(output_index))? + .script_pubkey; loop { - let script_pubkey = &tx - .outputs - .get(output_index) - .or_mm_err(|| WaitForOutputSpendErr::NoOutputWithIndex(output_index))? - .script_pubkey; - match coin .rpc_client .find_output_spend( @@ -4886,14 +4885,14 @@ where let expected_amount_sat = sat_from_big_decimal(&total_expected_amount, coin.as_ref().decimals)?; let time_lock = args - .time_lock + .funding_time_lock .try_into() - .map_to_mm(|e: TryFromIntError| ValidateSwapV2TxError::LocktimeOverflow(e.to_string()))?; + .map_to_mm(|e: TryFromIntError| ValidateSwapV2TxError::Overflow(e.to_string()))?; let redeem_script = swap_proto_v2_scripts::taker_funding_script( time_lock, args.taker_secret_hash, - args.other_pub, + args.taker_pub, maker_htlc_key_pair.public(), ); let expected_output = TransactionOutput { @@ -5069,7 +5068,7 @@ pub async fn spend_maker_payment_v2( /// Common implementation of maker payment v2 reclaim for UTXO coins using immediate refund path with secret reveal. pub async fn refund_maker_payment_v2_secret( coin: T, - args: RefundMakerPaymentArgs<'_, T>, + args: RefundMakerPaymentSecretArgs<'_, T>, ) -> Result where T: UtxoCommonOps + SwapOps, diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index 6baaf9d180..d55ce40420 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -27,18 +27,18 @@ use crate::{CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinBalance, CoinWithDeriva GenTakerPaymentSpendArgs, GetWithdrawSenderAddress, IguanaBalanceOps, IguanaPrivKey, MakerCoinSwapOpsV2, MakerSwapTakerCoin, MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, PrivKeyBuildPolicy, RawTransactionRequest, RawTransactionResult, RefundError, - RefundFundingSecretArgs, RefundMakerPaymentArgs, RefundPaymentArgs, RefundResult, - SearchForFundingSpendErr, SearchForSwapTxSpendInput, SendMakerPaymentArgs, - SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SendTakerFundingArgs, SignRawTransactionRequest, - SignatureResult, SpendMakerPaymentArgs, SpendPaymentArgs, SwapOps, SwapTxTypeWithSecretHash, - TakerCoinSwapOpsV2, TakerSwapMakerCoin, ToBytes, TradePreimageValue, TransactionFut, TransactionResult, - TxMarshalingErr, TxPreimageWithSig, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, - ValidateMakerPaymentArgs, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, - ValidatePaymentInput, ValidateSwapV2TxResult, ValidateTakerFundingArgs, - ValidateTakerFundingSpendPreimageResult, ValidateTakerPaymentSpendPreimageResult, - ValidateWatcherSpendInput, VerificationResult, WaitForHTLCTxSpendArgs, WaitForTakerPaymentSpendError, - WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, - WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFut}; + RefundFundingSecretArgs, RefundMakerPaymentSecretArgs, RefundMakerPaymentTimelockArgs, RefundPaymentArgs, + RefundResult, RefundTakerPaymentArgs, SearchForFundingSpendErr, SearchForSwapTxSpendInput, + SendMakerPaymentArgs, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SendTakerFundingArgs, + SignRawTransactionRequest, SignatureResult, SpendMakerPaymentArgs, SpendPaymentArgs, SwapOps, + SwapTxTypeWithSecretHash, TakerCoinSwapOpsV2, TakerSwapMakerCoin, ToBytes, TradePreimageValue, + TransactionFut, TransactionResult, TxMarshalingErr, TxPreimageWithSig, ValidateAddressResult, + ValidateFeeArgs, ValidateInstructionsErr, ValidateMakerPaymentArgs, ValidateOtherPubKeyErr, + ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, ValidateSwapV2TxResult, + ValidateTakerFundingArgs, ValidateTakerFundingSpendPreimageResult, + ValidateTakerPaymentSpendPreimageResult, ValidateWatcherSpendInput, VerificationResult, + WaitForHTLCTxSpendArgs, WaitForTakerPaymentSpendError, WatcherOps, WatcherReward, WatcherRewardError, + WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFut}; use common::executor::{AbortableSystem, AbortedError}; use futures::{FutureExt, TryFutureExt}; use mm2_metrics::MetricsArc; @@ -637,13 +637,25 @@ impl MakerCoinSwapOpsV2 for UtxoStandardCoin { .await } - async fn refund_maker_payment_v2_timelock(&self, args: RefundPaymentArgs<'_>) -> Result { + async fn refund_maker_payment_v2_timelock( + &self, + args: RefundMakerPaymentTimelockArgs<'_>, + ) -> Result { + let args = RefundPaymentArgs { + payment_tx: args.payment_tx, + time_lock: args.time_lock, + other_pubkey: args.taker_pub, + tx_type_with_secret_hash: args.tx_type_with_secret_hash, + swap_contract_address: &None, + swap_unique_data: args.swap_unique_data, + watcher_reward: args.watcher_reward, + }; utxo_common::refund_htlc_payment(self.clone(), args).await } async fn refund_maker_payment_v2_secret( &self, - args: RefundMakerPaymentArgs<'_, Self>, + args: RefundMakerPaymentSecretArgs<'_, Self>, ) -> Result { utxo_common::refund_maker_payment_v2_secret(self.clone(), args).await } @@ -663,7 +675,19 @@ impl TakerCoinSwapOpsV2 for UtxoStandardCoin { utxo_common::validate_taker_funding(self, args).await } - async fn refund_taker_funding_timelock(&self, args: RefundPaymentArgs<'_>) -> Result { + async fn refund_taker_funding_timelock( + &self, + args: RefundTakerPaymentArgs<'_>, + ) -> Result { + let args = RefundPaymentArgs { + payment_tx: args.payment_tx, + time_lock: args.time_lock, + other_pubkey: args.maker_pub, + tx_type_with_secret_hash: args.tx_type_with_secret_hash, + swap_contract_address: &None, + swap_unique_data: args.swap_unique_data, + watcher_reward: args.watcher_reward, + }; utxo_common::refund_htlc_payment(self.clone(), args).await } @@ -775,7 +799,19 @@ impl TakerCoinSwapOpsV2 for UtxoStandardCoin { utxo_common::sign_and_send_taker_funding_spend(self, preimage, args, &htlc_keypair).await } - async fn refund_combined_taker_payment(&self, args: RefundPaymentArgs<'_>) -> Result { + async fn refund_combined_taker_payment( + &self, + args: RefundTakerPaymentArgs<'_>, + ) -> Result { + let args = RefundPaymentArgs { + payment_tx: args.payment_tx, + time_lock: args.time_lock, + other_pubkey: args.maker_pub, + tx_type_with_secret_hash: args.tx_type_with_secret_hash, + swap_contract_address: &None, + swap_unique_data: args.swap_unique_data, + watcher_reward: args.watcher_reward, + }; utxo_common::refund_htlc_payment(self.clone(), args).await } diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs b/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs index 6ec1dcf360..6029bd61b2 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs @@ -9,8 +9,8 @@ use crate::lp_swap::{swap_v2_pb::*, NO_REFUND_FEE}; use async_trait::async_trait; use bitcrypto::{dhash160, sha256}; use coins::{CanRefundHtlc, ConfirmPaymentInput, DexFee, FeeApproxStage, FundingTxSpend, GenTakerFundingSpendArgs, - GenTakerPaymentSpendArgs, MakerCoinSwapOpsV2, MmCoin, ParseCoinAssocTypes, RefundMakerPaymentArgs, - RefundPaymentArgs, SearchForFundingSpendErr, SendMakerPaymentArgs, SwapTxTypeWithSecretHash, + GenTakerPaymentSpendArgs, MakerCoinSwapOpsV2, MmCoin, ParseCoinAssocTypes, RefundMakerPaymentSecretArgs, + RefundMakerPaymentTimelockArgs, SearchForFundingSpendErr, SendMakerPaymentArgs, SwapTxTypeWithSecretHash, TakerCoinSwapOpsV2, ToBytes, TradePreimageValue, Transaction, TxPreimageWithSig, ValidateTakerFundingArgs}; use common::executor::abortable_queue::AbortableQueue; use common::executor::{AbortableSystem, Timer}; @@ -353,7 +353,8 @@ pub struct MakerSwapStateMachine { + | e @ SearchForFundingSpendErr::FromBlockConversionErr(_) + | e @ SearchForFundingSpendErr::Internal(_) => { let next_state = MakerPaymentRefundRequired { maker_coin_start_block: self.maker_coin_start_block, taker_coin_start_block: self.taker_coin_start_block, @@ -1446,7 +1451,7 @@ impl> = Mutex::new(Vec::with_capacity(18)); - pub static ref MM_CTX: MmArc = MmCtxBuilder::new().into_mm_arc(); + pub static ref MM_CTX: MmArc = MmCtxBuilder::new().with_conf(json!({"use_trading_proto_v2": true})).into_mm_arc(); /// We need a second `MmCtx` instance when we use the same private keys for Maker and Taker across various tests. /// When enabling coins for both Maker and Taker, two distinct coin instances are created. /// This means that different instances of the same coin should have separate global nonce locks. /// Utilizing different `MmCtx` instances allows us to assign Maker and Taker coins to separate `CoinsCtx`. /// This approach addresses the `replacement transaction` issue, which occurs when different transactions share the same nonce. - pub static ref MM_CTX1: MmArc = MmCtxBuilder::new().into_mm_arc(); + pub static ref MM_CTX1: MmArc = MmCtxBuilder::new().with_conf(json!({"use_trading_proto_v2": true})).into_mm_arc(); pub static ref GETH_WEB3: Web3 = Web3::new(Http::new(GETH_RPC_URL).unwrap()); pub static ref SEPOLIA_WEB3: Web3 = Web3::new(Http::new(SEPOLIA_RPC_URL).unwrap()); // Mutex used to prevent nonce re-usage during funding addresses used in tests @@ -80,28 +79,25 @@ pub static mut QTUM_CONF_PATH: Option = None; pub static mut GETH_ACCOUNT: H160Eth = H160Eth::zero(); /// ERC20 token address on Geth dev node pub static mut GETH_ERC20_CONTRACT: H160Eth = H160Eth::zero(); +pub static mut SEPOLIA_ERC20_CONTRACT: H160Eth = H160Eth::zero(); /// Swap contract address on Geth dev node pub static mut GETH_SWAP_CONTRACT: H160Eth = H160Eth::zero(); /// Maker Swap V2 contract address on Geth dev node pub static mut GETH_MAKER_SWAP_V2: H160Eth = H160Eth::zero(); /// Taker Swap V2 contract address on Geth dev node pub static mut GETH_TAKER_SWAP_V2: H160Eth = H160Eth::zero(); +pub static mut SEPOLIA_TAKER_SWAP_V2: H160Eth = H160Eth::zero(); +pub static mut SEPOLIA_MAKER_SWAP_V2: H160Eth = H160Eth::zero(); /// Swap contract (with watchers support) address on Geth dev node pub static mut GETH_WATCHERS_SWAP_CONTRACT: H160Eth = H160Eth::zero(); /// ERC721 token address on Geth dev node pub static mut GETH_ERC721_CONTRACT: H160Eth = H160Eth::zero(); /// ERC1155 token address on Geth dev node pub static mut GETH_ERC1155_CONTRACT: H160Eth = H160Eth::zero(); -/// Nft Swap contract address on Geth dev node -pub static mut GETH_NFT_SWAP_CONTRACT: H160Eth = H160Eth::zero(); /// NFT Maker Swap V2 contract address on Geth dev node pub static mut GETH_NFT_MAKER_SWAP_V2: H160Eth = H160Eth::zero(); /// NFT Maker Swap V2 contract address on Sepolia testnet pub static mut SEPOLIA_ETOMIC_MAKER_NFT_SWAP_V2: H160Eth = H160Eth::zero(); -/// ERC721 token address on Sepolia testnet -pub static mut SEPOLIA_ERC721_CONTRACT: H160Eth = H160Eth::zero(); -/// ERC1155 token address on Sepolia testnet -pub static mut SEPOLIA_ERC1155_CONTRACT: H160Eth = H160Eth::zero(); pub static GETH_RPC_URL: &str = "http://127.0.0.1:8545"; pub static SEPOLIA_RPC_URL: &str = "https://ethereum-sepolia-rpc.publicnode.com"; @@ -134,9 +130,7 @@ pub const ERC721_TEST_TOKEN_BYTES: &str = include_str!("../../../mm2_test_helpers/contract_bytes/erc721_test_token_bytes"); pub const ERC1155_TEST_TOKEN_BYTES: &str = include_str!("../../../mm2_test_helpers/contract_bytes/erc1155_test_token_bytes"); -pub const NFT_SWAP_CONTRACT_BYTES: &str = - include_str!("../../../mm2_test_helpers/contract_bytes/nft_swap_contract_bytes"); -/// https://github.com/KomodoPlatform/etomic-swap/blob/006e6fd52334530f23624a2139d0eb5299c4cd10/contracts/EtomicSwapMakerNftV2Test.sol +/// https://github.com/KomodoPlatform/etomic-swap/blob/5e15641cbf41766cd5b37b4d71842c270773f788/contracts/EtomicSwapMakerNftV2.sol pub const NFT_MAKER_SWAP_V2_BYTES: &str = include_str!("../../../mm2_test_helpers/contract_bytes/nft_maker_swap_v2_bytes"); /// https://github.com/KomodoPlatform/etomic-swap/blob/5e15641cbf41766cd5b37b4d71842c270773f788/contracts/EtomicSwapMakerNftV2.sol @@ -1444,50 +1438,6 @@ pub fn init_geth_node() { thread::sleep(Duration::from_millis(100)); } - let dex_fee_address = Token::Address(geth_account()); - let params = ethabi::encode(&[dex_fee_address]); - let nft_swap_data = format!("{}{}", NFT_SWAP_CONTRACT_BYTES, hex::encode(params)); - - let tx_request_deploy_nft_swap_contract = TransactionRequest { - from: GETH_ACCOUNT, - to: None, - gas: None, - gas_price: None, - value: None, - data: Some(hex::decode(nft_swap_data).unwrap().into()), - nonce: None, - condition: None, - transaction_type: None, - access_list: None, - max_fee_per_gas: None, - max_priority_fee_per_gas: None, - }; - let deploy_nft_swap_tx_hash = - block_on(GETH_WEB3.eth().send_transaction(tx_request_deploy_nft_swap_contract)).unwrap(); - log!("Sent deploy nft swap contract transaction {:?}", deploy_swap_tx_hash); - - loop { - let deploy_nft_swap_tx_receipt = - match block_on(GETH_WEB3.eth().transaction_receipt(deploy_nft_swap_tx_hash)) { - Ok(receipt) => receipt, - Err(_) => { - thread::sleep(Duration::from_millis(100)); - continue; - }, - }; - - if let Some(receipt) = deploy_nft_swap_tx_receipt { - GETH_NFT_SWAP_CONTRACT = receipt.contract_address.unwrap(); - log!( - "GETH_NFT_SWAP_CONTRACT {:?}, receipt.status {:?}", - GETH_NFT_SWAP_CONTRACT, - receipt.status - ); - break; - } - thread::sleep(Duration::from_millis(100)); - } - let tx_request_deploy_nft_maker_swap_v2_contract = TransactionRequest { from: GETH_ACCOUNT, to: None, @@ -1535,50 +1485,6 @@ pub fn init_geth_node() { thread::sleep(Duration::from_millis(100)); } - let dex_fee_address = Token::Address(geth_account()); - let params = ethabi::encode(&[dex_fee_address]); - let nft_swap_data = format!("{}{}", NFT_SWAP_CONTRACT_BYTES, hex::encode(params)); - - let tx_request_deploy_nft_swap_contract = TransactionRequest { - from: GETH_ACCOUNT, - to: None, - gas: None, - gas_price: None, - value: None, - data: Some(hex::decode(nft_swap_data).unwrap().into()), - nonce: None, - condition: None, - transaction_type: None, - access_list: None, - max_fee_per_gas: None, - max_priority_fee_per_gas: None, - }; - let deploy_nft_swap_tx_hash = - block_on(GETH_WEB3.eth().send_transaction(tx_request_deploy_nft_swap_contract)).unwrap(); - log!("Sent deploy nft swap contract transaction {:?}", deploy_swap_tx_hash); - - loop { - let deploy_nft_swap_tx_receipt = - match block_on(GETH_WEB3.eth().transaction_receipt(deploy_nft_swap_tx_hash)) { - Ok(receipt) => receipt, - Err(_) => { - thread::sleep(Duration::from_millis(100)); - continue; - }, - }; - - if let Some(receipt) = deploy_nft_swap_tx_receipt { - GETH_NFT_SWAP_CONTRACT = receipt.contract_address.unwrap(); - log!( - "GETH_NFT_SWAP_CONTRACT {:?}, receipt.status {:?}", - GETH_NFT_SWAP_CONTRACT, - receipt.status - ); - break; - } - thread::sleep(Duration::from_millis(100)); - } - let name = Token::String("MyNFT".into()); let symbol = Token::String("MNFT".into()); let params = ethabi::encode(&[name, symbol]); @@ -1658,8 +1564,10 @@ pub fn init_geth_node() { } SEPOLIA_ETOMIC_MAKER_NFT_SWAP_V2 = EthAddress::from_str("0x9eb88cd58605d8fb9b14652d6152727f7e95fb4d").unwrap(); - SEPOLIA_ERC721_CONTRACT = EthAddress::from_str("0xbac1c9f2087f39caaa4e93412c6412809186870e").unwrap(); - SEPOLIA_ERC1155_CONTRACT = EthAddress::from_str("0xfb53b8764be6033d89ceacafa36631b09d60a1d2").unwrap(); + SEPOLIA_ERC20_CONTRACT = EthAddress::from_str("0xF7b5F8E8555EF7A743f24D3E974E23A3C6cB6638").unwrap(); + SEPOLIA_TAKER_SWAP_V2 = EthAddress::from_str("0x7Cc9F2c1c3B797D09B9d1CCd7FDcD2539a4b3874").unwrap(); + // TODO update this + SEPOLIA_MAKER_SWAP_V2 = EthAddress::from_str("0x7Cc9F2c1c3B797D09B9d1CCd7FDcD2539a4b3874").unwrap(); let alice_passphrase = get_passphrase!(".env.client", "ALICE_PASSPHRASE").unwrap(); let alice_keypair = key_pair_from_seed(&alice_passphrase).unwrap(); diff --git a/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs b/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs index 7d56820de7..3a898b0f2f 100644 --- a/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs @@ -1,116 +1,92 @@ use super::docker_tests_common::{random_secp256k1_secret, ERC1155_TEST_ABI, ERC721_TEST_ABI, GETH_ACCOUNT, GETH_ERC1155_CONTRACT, GETH_ERC20_CONTRACT, GETH_ERC721_CONTRACT, GETH_MAKER_SWAP_V2, - GETH_NFT_MAKER_SWAP_V2, GETH_NFT_SWAP_CONTRACT, GETH_NONCE_LOCK, GETH_RPC_URL, - GETH_SWAP_CONTRACT, GETH_TAKER_SWAP_V2, GETH_WATCHERS_SWAP_CONTRACT, GETH_WEB3, - MM_CTX, SEPOLIA_ERC1155_CONTRACT, SEPOLIA_ERC721_CONTRACT, - SEPOLIA_ETOMIC_MAKER_NFT_SWAP_V2, SEPOLIA_NONCE_LOCK, SEPOLIA_RPC_URL, SEPOLIA_WEB3}; + GETH_NFT_MAKER_SWAP_V2, GETH_NONCE_LOCK, GETH_RPC_URL, GETH_SWAP_CONTRACT, + GETH_TAKER_SWAP_V2, GETH_WATCHERS_SWAP_CONTRACT, GETH_WEB3, MM_CTX, MM_CTX1, + SEPOLIA_ERC20_CONTRACT, SEPOLIA_ETOMIC_MAKER_NFT_SWAP_V2, SEPOLIA_MAKER_SWAP_V2, + SEPOLIA_NONCE_LOCK, SEPOLIA_RPC_URL, SEPOLIA_TAKER_SWAP_V2, SEPOLIA_WEB3}; use crate::common::Future01CompatExt; use bitcrypto::{dhash160, sha256}; use coins::eth::gas_limit::ETH_MAX_TRADE_GAS; -use coins::eth::{checksum_address, eth_addr_to_hex, eth_coin_from_conf_and_request, EthCoin, SignedEthTx, ERC20_ABI}; +use coins::eth::v2_activation::{eth_coin_from_conf_and_request_v2, EthActivationV2Request, EthNode}; +use coins::eth::{checksum_address, eth_addr_to_hex, eth_coin_from_conf_and_request, EthCoin, EthCoinType, + EthPrivKeyBuildPolicy, SignedEthTx, SwapV2Contracts, ERC20_ABI}; use coins::nft::nft_structs::{Chain, ContractType, NftInfo}; use coins::{lp_coinfind, CoinProtocol, CoinWithDerivationMethod, CoinsContext, CommonSwapOpsV2, ConfirmPaymentInput, - DerivationMethod, Eip1559Ops, FoundSwapTxSpend, MakerNftSwapOpsV2, MarketCoinOps, MmCoinEnum, - MmCoinStruct, NftSwapInfo, ParseCoinAssocTypes, ParseNftAssocTypes, PrivKeyBuildPolicy, - RefundNftMakerPaymentArgs, RefundPaymentArgs, SearchForSwapTxSpendInput, SendNftMakerPaymentArgs, - SendPaymentArgs, SpendNftMakerPaymentArgs, SpendPaymentArgs, SwapOps, SwapTxFeePolicy, - SwapTxTypeWithSecretHash, ToBytes, Transaction, ValidateNftMakerPaymentArgs}; + DerivationMethod, DexFee, Eip1559Ops, FoundSwapTxSpend, FundingTxSpend, GenTakerFundingSpendArgs, + GenTakerPaymentSpendArgs, MakerNftSwapOpsV2, MarketCoinOps, MmCoinEnum, MmCoinStruct, NftSwapInfo, + ParseCoinAssocTypes, ParseNftAssocTypes, PrivKeyBuildPolicy, RefundFundingSecretArgs, + RefundNftMakerPaymentArgs, RefundPaymentArgs, RefundTakerPaymentArgs, SearchForSwapTxSpendInput, + SendNftMakerPaymentArgs, SendPaymentArgs, SendTakerFundingArgs, SpendNftMakerPaymentArgs, + SpendPaymentArgs, SwapOps, SwapTxFeePolicy, SwapTxTypeWithSecretHash, TakerCoinSwapOpsV2, ToBytes, + Transaction, TxPreimageWithSig, ValidateNftMakerPaymentArgs, ValidateTakerFundingArgs}; use common::{block_on, now_sec}; use crypto::Secp256k1Secret; -use ethcore_transaction::Action; use ethereum_types::U256; use futures01::Future; use mm2_core::mm_ctx::MmArc; use mm2_number::{BigDecimal, BigUint}; -use mm2_test_helpers::for_tests::{erc20_dev_conf, eth_dev_conf, nft_dev_conf, nft_sepolia_conf}; +use mm2_test_helpers::for_tests::{erc20_dev_conf, eth_dev_conf, eth_sepolia_conf, nft_dev_conf, sepolia_erc20_dev_conf}; +use serde_json::Value as Json; +use std::str::FromStr; use std::thread; use std::time::Duration; use web3::contract::{Contract, Options}; use web3::ethabi::Token; use web3::types::{Address, BlockNumber, TransactionRequest, H256}; -#[allow(dead_code)] const SEPOLIA_MAKER_PRIV: &str = "6e2f3a6223b928a05a3a3622b0c3f3573d03663b704a61a6eb73326de0487928"; -#[allow(dead_code)] const SEPOLIA_TAKER_PRIV: &str = "e0be82dca60ff7e4c6d6db339ac9e1ae249af081dba2110bddd281e711608f16"; const NFT_ETH: &str = "NFT_ETH"; +const ETH: &str = "ETH"; +const ERC20: &str = "ERC20DEV"; /// # Safety /// /// GETH_ACCOUNT is set once during initialization before tests start pub fn geth_account() -> Address { unsafe { GETH_ACCOUNT } } - /// # Safety /// /// GETH_SWAP_CONTRACT is set once during initialization before tests start pub fn swap_contract() -> Address { unsafe { GETH_SWAP_CONTRACT } } - -#[allow(dead_code)] /// # Safety /// /// GETH_MAKER_SWAP_V2 is set once during initialization before tests start pub fn maker_swap_v2() -> Address { unsafe { GETH_MAKER_SWAP_V2 } } - -#[allow(dead_code)] /// # Safety /// /// GETH_TAKER_SWAP_V2 is set once during initialization before tests start pub fn taker_swap_v2() -> Address { unsafe { GETH_TAKER_SWAP_V2 } } - -#[allow(dead_code)] -/// # Safety -/// -/// GETH_NFT_SWAP_CONTRACT is set once during initialization before tests start -pub fn nft_swap_contract() -> Address { unsafe { GETH_NFT_SWAP_CONTRACT } } - +pub fn sepolia_taker_swap_v2() -> Address { unsafe { SEPOLIA_TAKER_SWAP_V2 } } +pub fn sepolia_maker_swap_v2() -> Address { unsafe { SEPOLIA_MAKER_SWAP_V2 } } /// # Safety /// /// GETH_NFT_MAKER_SWAP_V2 is set once during initialization before tests start pub fn geth_nft_maker_swap_v2() -> Address { unsafe { GETH_NFT_MAKER_SWAP_V2 } } - /// # Safety /// /// GETH_WATCHERS_SWAP_CONTRACT is set once during initialization before tests start pub fn watchers_swap_contract() -> Address { unsafe { GETH_WATCHERS_SWAP_CONTRACT } } - /// # Safety /// /// GETH_ERC20_CONTRACT is set once during initialization before tests start pub fn erc20_contract() -> Address { unsafe { GETH_ERC20_CONTRACT } } - +pub fn sepolia_erc20_contract() -> Address { unsafe { SEPOLIA_ERC20_CONTRACT } } /// Return ERC20 dev token contract address in checksum format pub fn erc20_contract_checksum() -> String { checksum_address(&format!("{:02x}", erc20_contract())) } - -#[allow(dead_code)] +pub fn sepolia_erc20_contract_checksum() -> String { checksum_address(&format!("{:02x}", sepolia_erc20_contract())) } /// # Safety /// /// GETH_ERC721_CONTRACT is set once during initialization before tests start pub fn geth_erc721_contract() -> Address { unsafe { GETH_ERC721_CONTRACT } } - -#[allow(dead_code)] /// # Safety /// /// GETH_ERC1155_CONTRACT is set once during initialization before tests start pub fn geth_erc1155_contract() -> Address { unsafe { GETH_ERC1155_CONTRACT } } - -#[allow(dead_code)] /// # Safety /// /// SEPOLIA_ETOMIC_MAKER_NFT_SWAP_V2 address is set once during initialization before tests start pub fn sepolia_etomic_maker_nft() -> Address { unsafe { SEPOLIA_ETOMIC_MAKER_NFT_SWAP_V2 } } -#[allow(dead_code)] -/// # Safety -/// -/// SEPOLIA_ERC721_CONTRACT address is set once during initialization before tests start -pub fn sepolia_erc721() -> Address { unsafe { SEPOLIA_ERC721_CONTRACT } } - -#[allow(dead_code)] -/// # Safety -/// -/// SEPOLIA_ERC1155_CONTRACT address is set once during initialization before tests start -pub fn sepolia_erc1155() -> Address { unsafe { SEPOLIA_ERC1155_CONTRACT } } - fn wait_for_confirmation(tx_hash: H256) { thread::sleep(Duration::from_millis(2000)); loop { @@ -233,7 +209,7 @@ fn mint_erc1155(to_addr: Address, token_id: U256, amount: U256) { } fn geth_erc1155_balance(wallet_addr: Address, token_id: U256) -> U256 { - let _guard = SEPOLIA_NONCE_LOCK.lock().unwrap(); + let _guard = GETH_NONCE_LOCK.lock().unwrap(); let erc1155_contract = Contract::from_json(GETH_WEB3.eth(), geth_erc1155_contract(), ERC1155_TEST_ABI.as_bytes()).unwrap(); block_on(erc1155_contract.query( @@ -360,29 +336,47 @@ pub enum TestNftType { /// Generates a global NFT coin instance with a random private key and an initial 100 ETH balance. /// Optionally mints a specified NFT (either ERC721 or ERC1155) to the global NFT address, /// with details recorded in the `nfts_infos` field based on the provided `nft_type`. -#[allow(dead_code)] -pub fn global_nft_with_random_privkey(swap_contract_address: Address, nft_type: Option) -> EthCoin { - let nft_conf = nft_dev_conf(); - let req = json!({ - "method": "enable", - "coin": "NFT_ETH", - "urls": [GETH_RPC_URL], - "swap_contract_address": swap_contract_address, - }); - - let global_nft = block_on(eth_coin_from_conf_and_request( - &MM_CTX, - "NFT_ETH", - &nft_conf, - &req, - CoinProtocol::NFT { - platform: "ETH".to_string(), - }, - PrivKeyBuildPolicy::IguanaPrivKey(random_secp256k1_secret()), +fn global_nft_with_random_privkey( + swap_v2_contracts: SwapV2Contracts, + swap_contract_address: Address, + fallback_swap_contract_address: Address, + nft_type: Option, + nft_ticker: String, + platform_ticker: String, +) -> EthCoin { + let build_policy = EthPrivKeyBuildPolicy::IguanaPrivKey(random_secp256k1_secret()); + let node = EthNode { + url: GETH_RPC_URL.to_string(), + komodo_proxy: false, + }; + let platform_request = EthActivationV2Request { + nodes: vec![node], + rpc_mode: Default::default(), + swap_contract_address, + swap_v2_contracts: Some(swap_v2_contracts), + fallback_swap_contract: Some(fallback_swap_contract_address), + contract_supports_watchers: false, + mm2: None, + required_confirmations: None, + priv_key_policy: Default::default(), + enable_params: Default::default(), + path_to_address: Default::default(), + gap_limit: None, + }; + let coin = block_on(eth_coin_from_conf_and_request_v2( + &MM_CTX1, + nft_ticker.as_str(), + &nft_dev_conf(), + platform_request, + build_policy, )) .unwrap(); - let my_address = block_on(global_nft.my_addr()); + let coin_type = EthCoinType::Nft { + platform: platform_ticker, + }; + let global_nft = block_on(coin.set_coin_type(coin_type)); + let my_address = block_on(coin.my_addr()); fill_eth(my_address, U256::from(10).pow(U256::from(20))); if let Some(nft_type) = nft_type { @@ -402,114 +396,77 @@ pub fn global_nft_with_random_privkey(swap_contract_address: Address, nft_type: }, } } - global_nft } -#[allow(dead_code)] -/// Can be used to generate global NFT from Sepolia Maker/Taker priv keys. -fn global_nft_from_privkey( - ctx: &MmArc, - swap_contract_address: Address, - secret: &'static str, - nft_type: Option, -) -> EthCoin { - let nft_conf = nft_sepolia_conf(); - let req = json!({ - "method": "enable", - "coin": "NFT_ETH", - "urls": [SEPOLIA_RPC_URL], - "swap_contract_address": swap_contract_address, - }); +/// Can be used to generate coin from Sepolia Maker/Taker priv keys. +fn sepolia_coin_from_privkey(ctx: &MmArc, secret: &'static str, ticker: &str, conf: &Json, erc20: bool) -> EthCoin { + let swap_addr = SwapAddresses { + swap_v2_contracts: SwapV2Contracts { + maker_swap_v2_contract: sepolia_maker_swap_v2(), + taker_swap_v2_contract: sepolia_taker_swap_v2(), + nft_maker_swap_v2_contract: sepolia_etomic_maker_nft(), + }, + swap_contract_address: sepolia_taker_swap_v2(), + fallback_swap_contract_address: sepolia_taker_swap_v2(), + }; let priv_key = Secp256k1Secret::from(secret); - let global_nft = block_on(eth_coin_from_conf_and_request( + let build_policy = EthPrivKeyBuildPolicy::IguanaPrivKey(priv_key); + + let node = EthNode { + url: SEPOLIA_RPC_URL.to_string(), + komodo_proxy: false, + }; + let platform_request = EthActivationV2Request { + nodes: vec![node], + rpc_mode: Default::default(), + swap_contract_address: swap_addr.swap_contract_address, + swap_v2_contracts: Some(swap_addr.swap_v2_contracts), + fallback_swap_contract: Some(swap_addr.fallback_swap_contract_address), + contract_supports_watchers: false, + mm2: None, + required_confirmations: None, + priv_key_policy: Default::default(), + enable_params: Default::default(), + path_to_address: Default::default(), + gap_limit: None, + }; + let coin = block_on(eth_coin_from_conf_and_request_v2( ctx, - NFT_ETH, - &nft_conf, - &req, - CoinProtocol::NFT { - platform: "ETH".to_string(), - }, - PrivKeyBuildPolicy::IguanaPrivKey(priv_key), + ticker, + conf, + platform_request, + build_policy, )) .unwrap(); + let coin = if erc20 { + let coin_type = EthCoinType::Erc20 { + platform: ETH.to_string(), + token_addr: sepolia_erc20_contract(), + }; + block_on(coin.set_coin_type(coin_type)) + } else { + coin + }; let coins_ctx = CoinsContext::from_ctx(ctx).unwrap(); let mut coins = block_on(coins_ctx.lock_coins()); coins.insert( - global_nft.ticker().into(), - MmCoinStruct::new(MmCoinEnum::EthCoin(global_nft.clone())), + coin.ticker().into(), + MmCoinStruct::new(MmCoinEnum::EthCoin(coin.clone())), ); - - if let Some(nft_type) = nft_type { - match nft_type { - TestNftType::Erc1155 { token_id, amount } => { - block_on(fill_erc1155_info(&global_nft, sepolia_erc1155(), token_id, amount)); - }, - TestNftType::Erc721 { token_id } => { - block_on(fill_erc721_info(&global_nft, sepolia_erc721(), token_id)); - }, - } - } - - global_nft + coin } -#[allow(dead_code)] -fn send_safe_transfer_from( - global_nft: &EthCoin, - token_address: Address, - from_address: Address, - to_address: Address, - nft_type: TestNftType, -) -> web3::Result { - let _guard = SEPOLIA_NONCE_LOCK.lock().unwrap(); - - let contract = match nft_type { - TestNftType::Erc1155 { .. } => { - Contract::from_json(SEPOLIA_WEB3.eth(), token_address, ERC1155_TEST_ABI.as_bytes()).unwrap() - }, - TestNftType::Erc721 { .. } => { - Contract::from_json(SEPOLIA_WEB3.eth(), token_address, ERC721_TEST_ABI.as_bytes()).unwrap() +fn get_or_create_sepolia_coin(ctx: &MmArc, priv_key: &'static str, ticker: &str, conf: &Json, erc20: bool) -> EthCoin { + match block_on(lp_coinfind(ctx, ticker)).unwrap() { + None => sepolia_coin_from_privkey(ctx, priv_key, ticker, conf, erc20), + Some(mm_coin) => match mm_coin { + MmCoinEnum::EthCoin(coin) => coin, + _ => panic!("Unexpected coin type found. Expected MmCoinEnum::EthCoin"), }, - }; - let tokens = match nft_type { - TestNftType::Erc1155 { token_id, amount } => vec![ - Token::Address(from_address), - Token::Address(to_address), - Token::Uint(U256::from(token_id)), - Token::Uint(U256::from(amount)), - Token::Bytes(vec![]), - ], - TestNftType::Erc721 { token_id } => vec![ - Token::Address(from_address), - Token::Address(to_address), - Token::Uint(U256::from(token_id)), - ], - }; - - let data = contract - .abi() - .function("safeTransferFrom") - .unwrap() - .encode_input(&tokens) - .unwrap(); - - let result = block_on( - global_nft - .sign_and_send_transaction( - 0.into(), - Action::Call(token_address), - data, - U256::from(ETH_MAX_TRADE_GAS), - ) - .compat(), - ) - .unwrap(); - - log!("Transaction sent: {:?}", result); - Ok(result) + } } /// Fills the private key's public address with ETH and ERC20 tokens @@ -902,7 +859,6 @@ fn send_and_spend_erc20_maker_payment_priority_fee() { send_and_spend_erc20_maker_payment_impl(SwapTxFeePolicy::Medium); } -#[allow(dead_code)] /// Wait for all pending transactions for the given address to be confirmed fn wait_pending_transactions(wallet_address: Address) { let _guard = SEPOLIA_NONCE_LOCK.lock().unwrap(); @@ -913,7 +869,10 @@ fn wait_pending_transactions(wallet_address: Address) { let pending_nonce = block_on(web3.eth().transaction_count(wallet_address, Some(BlockNumber::Pending))).unwrap(); if latest_nonce == pending_nonce { - log!("All pending transactions have been confirmed."); + log!( + "All pending transactions have been confirmed. Latest nonce: {}", + latest_nonce + ); break; } else { log!( @@ -926,29 +885,18 @@ fn wait_pending_transactions(wallet_address: Address) { } } -#[allow(dead_code)] -fn get_or_create_nft(ctx: &MmArc, priv_key: &'static str, nft_type: Option) -> EthCoin { - match block_on(lp_coinfind(ctx, NFT_ETH)).unwrap() { - None => global_nft_from_privkey(ctx, sepolia_etomic_maker_nft(), priv_key, nft_type), - Some(mm_coin) => match mm_coin { - MmCoinEnum::EthCoin(nft) => nft, - _ => panic!("Unexpected coin type found. Expected MmCoinEnum::EthCoin"), - }, - } -} - #[test] fn send_and_spend_erc721_maker_payment() { - thread::sleep(Duration::from_secs(11)); let token_id = 1u32; let time_lock = now_sec() + 1000; + let activation = NftActivationV2Args::init(); let setup = setup_test( token_id, None, ContractType::Erc721, - geth_nft_maker_swap_v2(), geth_erc721_contract(), time_lock, + activation, ); let maker_payment = send_nft_maker_payment(&setup, 1.into()); @@ -974,17 +922,17 @@ fn send_and_spend_erc721_maker_payment() { #[test] fn send_and_spend_erc1155_maker_payment() { - thread::sleep(Duration::from_secs(3)); let token_id = 1u32; let amount = 3u32; let time_lock = now_sec() + 1000; + let activation = NftActivationV2Args::init(); let setup = setup_test( token_id, Some(amount), ContractType::Erc1155, - geth_nft_maker_swap_v2(), geth_erc1155_contract(), time_lock, + activation, ); let maker_address = block_on(setup.maker_global_nft.my_addr()); @@ -1049,16 +997,16 @@ fn test_nonce_lock() { #[test] fn send_and_refund_erc721_maker_payment_timelock() { - thread::sleep(Duration::from_secs(39)); let token_id = 2u32; let time_lock_to_refund = now_sec() - 1000; + let activation = NftActivationV2Args::init(); let setup = setup_test( token_id, None, ContractType::Erc721, - geth_nft_maker_swap_v2(), geth_erc721_contract(), time_lock_to_refund, + activation, ); let maker_payment_to_refund = send_nft_maker_payment(&setup, 1.into()); @@ -1090,17 +1038,17 @@ fn send_and_refund_erc721_maker_payment_timelock() { #[test] fn send_and_refund_erc1155_maker_payment_timelock() { - thread::sleep(Duration::from_secs(29)); let token_id = 2u32; let amount = 3u32; let time_lock_to_refund = now_sec() - 1000; + let activation = NftActivationV2Args::init(); let setup = setup_test( token_id, Some(amount), ContractType::Erc1155, - geth_nft_maker_swap_v2(), geth_erc1155_contract(), time_lock_to_refund, + activation, ); let maker_address = block_on(setup.maker_global_nft.my_addr()); @@ -1140,17 +1088,16 @@ fn send_and_refund_erc1155_maker_payment_timelock() { #[test] fn send_and_refund_erc721_maker_payment_secret() { - thread::sleep(Duration::from_secs(5)); let token_id = 3u32; let time_lock_to_refund = now_sec() + 1000; - + let activation = NftActivationV2Args::init(); let setup = setup_test( token_id, None, ContractType::Erc721, - geth_nft_maker_swap_v2(), geth_erc721_contract(), time_lock_to_refund, + activation, ); let maker_payment_to_refund = send_nft_maker_payment(&setup, 1.into()); @@ -1182,17 +1129,17 @@ fn send_and_refund_erc721_maker_payment_secret() { #[test] fn send_and_refund_erc1155_maker_payment_secret() { - thread::sleep(Duration::from_secs(2)); let token_id = 3u32; let amount = 3u32; let time_lock_to_refund = now_sec() + 1000; + let activation = NftActivationV2Args::init(); let setup = setup_test( token_id, Some(amount), ContractType::Erc1155, - geth_nft_maker_swap_v2(), geth_erc1155_contract(), time_lock_to_refund, + activation, ); let maker_address = block_on(setup.maker_global_nft.my_addr()); @@ -1252,14 +1199,37 @@ pub struct TestNftSwapInfo { /// Etomic swap contract address pub swap_contract_address: Coin::ContractAddress, } +struct NftActivationV2Args { + swap_contract_address: Address, + fallback_swap_contract_address: Address, + swap_v2_contracts: SwapV2Contracts, + nft_ticker: String, + platform_ticker: String, +} + +impl NftActivationV2Args { + fn init() -> Self { + Self { + swap_contract_address: swap_contract(), + fallback_swap_contract_address: swap_contract(), + swap_v2_contracts: SwapV2Contracts { + maker_swap_v2_contract: maker_swap_v2(), + taker_swap_v2_contract: taker_swap_v2(), + nft_maker_swap_v2_contract: geth_nft_maker_swap_v2(), + }, + nft_ticker: NFT_ETH.to_string(), + platform_ticker: "ETH".to_string(), + } + } +} fn setup_test( token_id: u32, amount: Option, contract_type: ContractType, - swap_contract_address: Address, token_contract: Address, time_lock: u64, + activation: NftActivationV2Args, ) -> NftTestSetup { let nft_type = match contract_type { ContractType::Erc721 => TestNftType::Erc721 { token_id }, @@ -1269,8 +1239,22 @@ fn setup_test( }, }; - let maker_global_nft = global_nft_with_random_privkey(swap_contract_address, Some(nft_type)); - let taker_global_nft = global_nft_with_random_privkey(swap_contract_address, None); + let maker_global_nft = global_nft_with_random_privkey( + activation.swap_v2_contracts, + activation.swap_contract_address, + activation.fallback_swap_contract_address, + Some(nft_type), + activation.nft_ticker.clone(), + activation.platform_ticker.clone(), + ); + let taker_global_nft = global_nft_with_random_privkey( + activation.swap_v2_contracts, + activation.swap_contract_address, + activation.fallback_swap_contract_address, + None, + activation.nft_ticker, + activation.platform_ticker, + ); let maker_secret = vec![1; 32]; let maker_secret_hash = sha256(&maker_secret).to_vec(); let taker_secret = vec![0; 32]; @@ -1282,7 +1266,7 @@ fn setup_test( token_address: token_contract, token_id, contract_type, - swap_contract_address, + swap_contract_address: activation.swap_v2_contracts.nft_maker_swap_v2_contract, }; NftTestSetup { @@ -1316,7 +1300,7 @@ fn send_nft_maker_payment(setup: &NftTestSetup, amount: BigDecimal) -> SignedEth block_on(setup.maker_global_nft.send_nft_maker_payment_v2(send_payment_args)).unwrap() } -fn wait_for_confirmations(global_nft: &EthCoin, tx: &SignedEthTx, wait_seconds: u64) { +fn wait_for_confirmations(coin: &EthCoin, tx: &SignedEthTx, wait_seconds: u64) { let confirm_input = ConfirmPaymentInput { payment_tx: tx.tx_hex(), confirmations: 1, @@ -1324,7 +1308,7 @@ fn wait_for_confirmations(global_nft: &EthCoin, tx: &SignedEthTx, wait_seconds: wait_until: now_sec() + wait_seconds, check_every: 1, }; - global_nft.wait_for_confirmations(confirm_input).wait().unwrap(); + coin.wait_for_confirmations(confirm_input).wait().unwrap(); } fn validate_nft_maker_payment(setup: &NftTestSetup, maker_payment: &SignedEthTx, amount: BigDecimal) { @@ -1393,3 +1377,720 @@ enum RefundType { Timelock, Secret, } + +#[derive(Copy, Clone)] +struct SwapAddresses { + swap_v2_contracts: SwapV2Contracts, + swap_contract_address: Address, + fallback_swap_contract_address: Address, +} + +#[allow(dead_code)] +/// Needed for Geth taker or maker swap v2 tests +impl SwapAddresses { + fn init() -> Self { + Self { + swap_contract_address: swap_contract(), + fallback_swap_contract_address: swap_contract(), + swap_v2_contracts: SwapV2Contracts { + maker_swap_v2_contract: maker_swap_v2(), + taker_swap_v2_contract: taker_swap_v2(), + nft_maker_swap_v2_contract: geth_nft_maker_swap_v2(), + }, + } + } +} + +#[allow(dead_code)] +/// Needed for eth or erc20 v2 activation in Geth tests +fn eth_coin_v2_activation_with_random_privkey( + ticker: &str, + conf: &Json, + swap_addr: SwapAddresses, + erc20: bool, +) -> EthCoin { + let build_policy = EthPrivKeyBuildPolicy::IguanaPrivKey(random_secp256k1_secret()); + let node = EthNode { + url: GETH_RPC_URL.to_string(), + komodo_proxy: false, + }; + let platform_request = EthActivationV2Request { + nodes: vec![node], + rpc_mode: Default::default(), + swap_contract_address: swap_addr.swap_contract_address, + swap_v2_contracts: Some(swap_addr.swap_v2_contracts), + fallback_swap_contract: Some(swap_addr.fallback_swap_contract_address), + contract_supports_watchers: false, + mm2: None, + required_confirmations: None, + priv_key_policy: Default::default(), + enable_params: Default::default(), + path_to_address: Default::default(), + gap_limit: None, + }; + let coin = block_on(eth_coin_from_conf_and_request_v2( + &MM_CTX1, + ticker, + conf, + platform_request, + build_policy, + )) + .unwrap(); + let my_address = block_on(coin.my_addr()); + fill_eth(my_address, U256::from(10).pow(U256::from(20))); + fill_erc20(my_address, U256::from(10000000000u64)); + if erc20 { + let coin_type = EthCoinType::Erc20 { + platform: ETH.to_string(), + token_addr: erc20_contract(), + }; + let coin = block_on(coin.set_coin_type(coin_type)); + return coin; + } + coin +} + +#[ignore] +#[test] +fn send_and_refund_taker_funding_by_secret_eth() { + // sepolia test + thread::sleep(Duration::from_secs(5)); + let taker_coin = get_or_create_sepolia_coin(&MM_CTX1, SEPOLIA_TAKER_PRIV, ETH, ð_sepolia_conf(), false); + let maker_coin = get_or_create_sepolia_coin(&MM_CTX, SEPOLIA_MAKER_PRIV, ETH, ð_sepolia_conf(), false); + + let taker_secret = vec![0; 32]; + let taker_secret_hash = sha256(&taker_secret).to_vec(); + let maker_secret = vec![1; 32]; + let maker_secret_hash = sha256(&maker_secret).to_vec(); + let funding_time_lock = now_sec() + 3000; + let payment_time_lock = now_sec() + 1000; + + let taker_address = block_on(taker_coin.my_addr()); + + let dex_fee = &DexFee::Standard("0.00001".into()); + let trading_amount = BigDecimal::from_str("0.0001").unwrap(); + + let maker_pub = &maker_coin.derive_htlc_pubkey_v2(&[]); + let payment_args = SendTakerFundingArgs { + funding_time_lock, + payment_time_lock, + taker_secret_hash: &taker_secret_hash, + maker_secret_hash: &maker_secret_hash, + maker_pub: maker_pub.as_bytes(), + dex_fee, + premium_amount: BigDecimal::default(), + trading_amount: trading_amount.clone(), + swap_unique_data: &[], + }; + + wait_pending_transactions(Address::from_slice(taker_address.as_bytes())); + thread::sleep(Duration::from_secs(2)); + let funding_tx = block_on(taker_coin.send_taker_funding(payment_args)).unwrap(); + log!("Taker sent ETH funding, tx hash: {:02x}", funding_tx.tx_hash()); + + let refund_args = RefundFundingSecretArgs { + funding_tx: &funding_tx, + funding_time_lock, + payment_time_lock, + maker_pubkey: maker_pub, + taker_secret: &taker_secret, + taker_secret_hash: &taker_secret_hash, + maker_secret_hash: &maker_secret_hash, + dex_fee, + premium_amount: Default::default(), + trading_amount, + swap_unique_data: &[], + watcher_reward: false, + }; + wait_pending_transactions(Address::from_slice(taker_address.as_bytes())); + let funding_tx_refund = block_on(taker_coin.refund_taker_funding_secret(refund_args)).unwrap(); + log!( + "Taker refunded ETH funding by secret, tx hash: {:02x}", + funding_tx_refund.tx_hash() + ); + + wait_for_confirmations(&taker_coin, &funding_tx_refund, 100); +} + +#[ignore] +#[test] +fn send_and_refund_taker_funding_by_secret_erc20() { + thread::sleep(Duration::from_secs(130)); + let erc20_conf = &sepolia_erc20_dev_conf(&sepolia_erc20_contract_checksum()); + let taker_coin = get_or_create_sepolia_coin(&MM_CTX1, SEPOLIA_TAKER_PRIV, ERC20, erc20_conf, true); + let maker_coin = get_or_create_sepolia_coin(&MM_CTX1, SEPOLIA_MAKER_PRIV, ERC20, erc20_conf, true); + + let taker_secret = vec![0; 32]; + let taker_secret_hash = sha256(&taker_secret).to_vec(); + let maker_secret = vec![1; 32]; + let maker_secret_hash = sha256(&maker_secret).to_vec(); + + let taker_address = block_on(taker_coin.my_addr()); + + let funding_time_lock = now_sec() + 3000; + let payment_time_lock = now_sec() + 1000; + + let dex_fee = &DexFee::Standard("0.00001".into()); + let trading_amount = BigDecimal::from_str("0.0001").unwrap(); + + let maker_pub = &maker_coin.derive_htlc_pubkey_v2(&[]); + let payment_args = SendTakerFundingArgs { + funding_time_lock, + payment_time_lock, + taker_secret_hash: &taker_secret_hash, + maker_secret_hash: &maker_secret_hash, + maker_pub: maker_pub.as_bytes(), + dex_fee, + premium_amount: BigDecimal::default(), + trading_amount: trading_amount.clone(), + swap_unique_data: &[], + }; + + wait_pending_transactions(Address::from_slice(taker_address.as_bytes())); + let funding_tx = block_on(taker_coin.send_taker_funding(payment_args)).unwrap(); + log!("Taker sent ERC20 funding, tx hash: {:02x}", funding_tx.tx_hash()); + wait_for_confirmations(&taker_coin, &funding_tx, 200); + + let refund_args = RefundFundingSecretArgs { + funding_tx: &funding_tx, + funding_time_lock, + payment_time_lock, + maker_pubkey: &taker_coin.derive_htlc_pubkey_v2(&[]), + taker_secret: &taker_secret, + taker_secret_hash: &taker_secret_hash, + maker_secret_hash: &maker_secret_hash, + dex_fee, + premium_amount: Default::default(), + trading_amount, + swap_unique_data: &[], + watcher_reward: false, + }; + wait_pending_transactions(Address::from_slice(taker_address.as_bytes())); + let funding_tx_refund = block_on(taker_coin.refund_taker_funding_secret(refund_args)).unwrap(); + log!( + "Taker refunded ERC20 funding by secret, tx hash: {:02x}", + funding_tx_refund.tx_hash() + ); + wait_for_confirmations(&taker_coin, &funding_tx_refund, 200); +} + +#[ignore] +#[test] +fn send_and_refund_taker_funding_exceed_pre_approve_timelock_eth() { + thread::sleep(Duration::from_secs(12)); + // sepolia test + let taker_coin = get_or_create_sepolia_coin(&MM_CTX1, SEPOLIA_TAKER_PRIV, ETH, ð_sepolia_conf(), false); + let maker_coin = get_or_create_sepolia_coin(&MM_CTX, SEPOLIA_MAKER_PRIV, ETH, ð_sepolia_conf(), false); + + let taker_secret = vec![0; 32]; + let taker_secret_hash = sha256(&taker_secret).to_vec(); + let maker_secret = vec![1; 32]; + let maker_secret_hash = sha256(&maker_secret).to_vec(); + + let taker_address = block_on(taker_coin.my_addr()); + + // if TakerPaymentState is `PaymentSent` then timestamp should exceed payment pre-approve lock time (funding_time_lock) + let funding_time_lock = now_sec() - 3000; + let payment_time_lock = now_sec() + 1000; + + let dex_fee = &DexFee::Standard("0.00001".into()); + let trading_amount = BigDecimal::from_str("0.0001").unwrap(); + + let maker_pub = &maker_coin.derive_htlc_pubkey_v2(&[]); + let payment_args = SendTakerFundingArgs { + funding_time_lock, + payment_time_lock, + taker_secret_hash: &taker_secret_hash, + maker_secret_hash: &maker_secret_hash, + maker_pub: maker_pub.as_bytes(), + dex_fee, + premium_amount: BigDecimal::default(), + trading_amount: trading_amount.clone(), + swap_unique_data: &[], + }; + wait_pending_transactions(Address::from_slice(taker_address.as_bytes())); + let funding_tx = block_on(taker_coin.send_taker_funding(payment_args)).unwrap(); + log!("Taker sent ETH funding, tx hash: {:02x}", funding_tx.tx_hash()); + wait_for_confirmations(&taker_coin, &funding_tx, 100); + + let tx_type_with_secret_hash = SwapTxTypeWithSecretHash::TakerPaymentV2 { + maker_secret_hash: &maker_secret_hash, + taker_secret_hash: &taker_secret_hash, + }; + + let refund_args = RefundTakerPaymentArgs { + payment_tx: &funding_tx.to_bytes(), + time_lock: payment_time_lock, + maker_pub: maker_pub.as_bytes(), + tx_type_with_secret_hash, + swap_unique_data: &[], + watcher_reward: false, + dex_fee, + premium_amount: BigDecimal::default(), + trading_amount, + }; + wait_pending_transactions(Address::from_slice(taker_address.as_bytes())); + let funding_tx_refund = block_on(taker_coin.refund_taker_funding_timelock(refund_args)).unwrap(); + log!( + "Taker refunded ETH funding after pre-approval lock time was exceeded, tx hash: {:02x}", + funding_tx_refund.tx_hash() + ); + + wait_for_confirmations(&taker_coin, &funding_tx_refund, 100); +} + +#[ignore] +#[test] +fn taker_send_approve_and_spend_eth() { + // sepolia test + let taker_coin = get_or_create_sepolia_coin(&MM_CTX1, SEPOLIA_TAKER_PRIV, ETH, ð_sepolia_conf(), false); + let maker_coin = get_or_create_sepolia_coin(&MM_CTX, SEPOLIA_MAKER_PRIV, ETH, ð_sepolia_conf(), false); + + let taker_secret = vec![0; 32]; + let taker_secret_hash = sha256(&taker_secret).to_vec(); + let maker_secret = vec![1; 32]; + let maker_secret_hash = sha256(&maker_secret).to_vec(); + let funding_time_lock = now_sec() + 3000; + let payment_time_lock = now_sec() + 600; + + let taker_address = block_on(taker_coin.my_addr()); + let maker_address = block_on(maker_coin.my_addr()); + + let dex_fee = &DexFee::Standard("0.00001".into()); + let trading_amount = BigDecimal::from_str("0.0001").unwrap(); + + let maker_pub = &maker_coin.derive_htlc_pubkey_v2(&[]); + let payment_args = SendTakerFundingArgs { + funding_time_lock, + payment_time_lock, + taker_secret_hash: &taker_secret_hash, + maker_secret_hash: &maker_secret_hash, + maker_pub: maker_pub.as_bytes(), + dex_fee, + premium_amount: BigDecimal::default(), + trading_amount: trading_amount.clone(), + swap_unique_data: &[], + }; + wait_pending_transactions(Address::from_slice(taker_address.as_bytes())); + let funding_tx = block_on(taker_coin.send_taker_funding(payment_args)).unwrap(); + log!("Taker sent ETH funding, tx hash: {:02x}", funding_tx.tx_hash()); + wait_for_confirmations(&taker_coin, &funding_tx, 100); + + let taker_pub = &taker_coin.derive_htlc_pubkey_v2(&[]); + let validate = ValidateTakerFundingArgs { + funding_tx: &funding_tx, + funding_time_lock, + payment_time_lock, + taker_secret_hash: &taker_secret_hash, + maker_secret_hash: &maker_secret_hash, + taker_pub, + dex_fee, + premium_amount: Default::default(), + trading_amount: trading_amount.clone(), + swap_unique_data: &[], + }; + block_on(maker_coin.validate_taker_funding(validate)).unwrap(); + + let approve_args = GenTakerFundingSpendArgs { + funding_tx: &funding_tx, + maker_pub, + taker_pub, + funding_time_lock, + taker_secret_hash: &taker_secret_hash, + taker_payment_time_lock: funding_time_lock, + maker_secret_hash: &maker_secret_hash, + }; + let preimage = TxPreimageWithSig { + preimage: funding_tx.clone(), + signature: taker_coin.parse_signature(&[0u8; 65]).unwrap(), + }; + wait_pending_transactions(Address::from_slice(taker_address.as_bytes())); + let taker_approve_tx = + block_on(taker_coin.sign_and_send_taker_funding_spend(&preimage, &approve_args, &[])).unwrap(); + log!( + "Taker approved ETH payment, tx hash: {:02x}", + taker_approve_tx.tx_hash() + ); + wait_for_confirmations(&taker_coin, &taker_approve_tx, 100); + + wait_pending_transactions(Address::from_slice(maker_address.as_bytes())); + let check_taker_approved_tx = block_on(maker_coin.search_for_taker_funding_spend(&taker_approve_tx, 0u64, &[])) + .unwrap() + .unwrap(); + match check_taker_approved_tx { + FundingTxSpend::TransferredToTakerPayment(tx) => { + assert_eq!(tx, taker_approve_tx); + }, + FundingTxSpend::RefundedTimelock(_) | FundingTxSpend::RefundedSecret { .. } => { + panic!("Wrong FundingTxSpend variant, expected TransferredToTakerPayment") + }, + }; + + let dex_fee_pub = sepolia_taker_swap_v2(); + let spend_args = GenTakerPaymentSpendArgs { + taker_tx: &taker_approve_tx, + time_lock: payment_time_lock, + maker_secret_hash: &maker_secret_hash, + maker_pub, + maker_address: &maker_address, + taker_pub, + dex_fee_pub: dex_fee_pub.as_bytes(), + dex_fee, + premium_amount: Default::default(), + trading_amount, + }; + wait_pending_transactions(Address::from_slice(maker_address.as_bytes())); + let spend_tx = + block_on(maker_coin.sign_and_broadcast_taker_payment_spend(&preimage, &spend_args, &maker_secret, &[])) + .unwrap(); + log!("Maker spent ETH payment, tx hash: {:02x}", spend_tx.tx_hash()); + wait_for_confirmations(&maker_coin, &spend_tx, 100); + block_on(taker_coin.wait_for_taker_payment_spend(&spend_tx, 0u64, payment_time_lock)).unwrap(); +} + +#[ignore] +#[test] +fn taker_send_approve_and_spend_erc20() { + // sepolia test + thread::sleep(Duration::from_secs(9)); + let erc20_conf = &sepolia_erc20_dev_conf(&sepolia_erc20_contract_checksum()); + let taker_coin = get_or_create_sepolia_coin(&MM_CTX1, SEPOLIA_TAKER_PRIV, ERC20, erc20_conf, true); + let maker_coin = get_or_create_sepolia_coin(&MM_CTX, SEPOLIA_MAKER_PRIV, ERC20, erc20_conf, true); + + let taker_secret = vec![0; 32]; + let taker_secret_hash = sha256(&taker_secret).to_vec(); + let maker_secret = vec![1; 32]; + let maker_secret_hash = sha256(&maker_secret).to_vec(); + let funding_time_lock = now_sec() + 3000; + let payment_time_lock = now_sec() + 600; + + let taker_address = block_on(taker_coin.my_addr()); + let maker_address = block_on(maker_coin.my_addr()); + + let dex_fee = &DexFee::Standard("0.00001".into()); + let trading_amount = BigDecimal::from_str("0.0001").unwrap(); + + let maker_pub = &maker_coin.derive_htlc_pubkey_v2(&[]); + let payment_args = SendTakerFundingArgs { + funding_time_lock, + payment_time_lock, + taker_secret_hash: &taker_secret_hash, + maker_secret_hash: &maker_secret_hash, + maker_pub: maker_pub.as_bytes(), + dex_fee, + premium_amount: BigDecimal::default(), + trading_amount: trading_amount.clone(), + swap_unique_data: &[], + }; + wait_pending_transactions(Address::from_slice(taker_address.as_bytes())); + let funding_tx = block_on(taker_coin.send_taker_funding(payment_args)).unwrap(); + log!("Taker sent ERC20 funding, tx hash: {:02x}", funding_tx.tx_hash()); + wait_for_confirmations(&taker_coin, &funding_tx, 100); + + let taker_pub = &taker_coin.derive_htlc_pubkey_v2(&[]); + let validate = ValidateTakerFundingArgs { + funding_tx: &funding_tx, + funding_time_lock, + payment_time_lock, + taker_secret_hash: &taker_secret_hash, + maker_secret_hash: &maker_secret_hash, + taker_pub, + dex_fee, + premium_amount: Default::default(), + trading_amount: trading_amount.clone(), + swap_unique_data: &[], + }; + block_on(maker_coin.validate_taker_funding(validate)).unwrap(); + + let approve_args = GenTakerFundingSpendArgs { + funding_tx: &funding_tx, + maker_pub, + taker_pub, + funding_time_lock, + taker_secret_hash: &taker_secret_hash, + taker_payment_time_lock: funding_time_lock, + maker_secret_hash: &maker_secret_hash, + }; + let preimage = TxPreimageWithSig { + preimage: funding_tx.clone(), + signature: taker_coin.parse_signature(&[0u8; 65]).unwrap(), + }; + wait_pending_transactions(Address::from_slice(taker_address.as_bytes())); + let taker_approve_tx = + block_on(taker_coin.sign_and_send_taker_funding_spend(&preimage, &approve_args, &[])).unwrap(); + log!( + "Taker approved ERC20 payment, tx hash: {:02x}", + taker_approve_tx.tx_hash() + ); + wait_for_confirmations(&taker_coin, &taker_approve_tx, 100); + + wait_pending_transactions(Address::from_slice(maker_address.as_bytes())); + let check_taker_approved_tx = block_on(maker_coin.search_for_taker_funding_spend(&taker_approve_tx, 0u64, &[])) + .unwrap() + .unwrap(); + match check_taker_approved_tx { + FundingTxSpend::TransferredToTakerPayment(tx) => { + assert_eq!(tx, taker_approve_tx); + }, + FundingTxSpend::RefundedTimelock(_) | FundingTxSpend::RefundedSecret { .. } => { + panic!("Wrong FundingTxSpend variant, expected TransferredToTakerPayment") + }, + }; + + let dex_fee_pub = sepolia_taker_swap_v2(); + let spend_args = GenTakerPaymentSpendArgs { + taker_tx: &taker_approve_tx, + time_lock: payment_time_lock, + maker_secret_hash: &maker_secret_hash, + maker_pub, + maker_address: &maker_address, + taker_pub, + dex_fee_pub: dex_fee_pub.as_bytes(), + dex_fee, + premium_amount: Default::default(), + trading_amount, + }; + wait_pending_transactions(Address::from_slice(maker_address.as_bytes())); + let spend_tx = + block_on(maker_coin.sign_and_broadcast_taker_payment_spend(&preimage, &spend_args, &maker_secret, &[])) + .unwrap(); + log!("Maker spent ERC20 payment, tx hash: {:02x}", spend_tx.tx_hash()); + block_on(taker_coin.wait_for_taker_payment_spend(&spend_tx, 0u64, payment_time_lock)).unwrap(); +} + +#[ignore] +#[test] +fn send_and_refund_taker_funding_exceed_payment_timelock_eth() { + // sepolia test + thread::sleep(Duration::from_secs(25)); + let taker_coin = get_or_create_sepolia_coin(&MM_CTX1, SEPOLIA_TAKER_PRIV, ETH, ð_sepolia_conf(), false); + let maker_coin = get_or_create_sepolia_coin(&MM_CTX, SEPOLIA_MAKER_PRIV, ETH, ð_sepolia_conf(), false); + + let taker_secret = vec![0; 32]; + let taker_secret_hash = sha256(&taker_secret).to_vec(); + let maker_secret = vec![1; 32]; + let maker_secret_hash = sha256(&maker_secret).to_vec(); + let funding_time_lock = now_sec() + 3000; + let payment_time_lock = now_sec() - 1000; + + let taker_address = block_on(taker_coin.my_addr()); + + let dex_fee = &DexFee::Standard("0.00001".into()); + let trading_amount = BigDecimal::from_str("0.0001").unwrap(); + + let maker_pub = &maker_coin.derive_htlc_pubkey_v2(&[]); + let payment_args = SendTakerFundingArgs { + funding_time_lock, + payment_time_lock, + taker_secret_hash: &taker_secret_hash, + maker_secret_hash: &maker_secret_hash, + maker_pub: maker_pub.as_bytes(), + dex_fee, + premium_amount: BigDecimal::default(), + trading_amount: trading_amount.clone(), + swap_unique_data: &[], + }; + wait_pending_transactions(Address::from_slice(taker_address.as_bytes())); + let funding_tx = block_on(taker_coin.send_taker_funding(payment_args)).unwrap(); + log!("Taker sent ETH funding, tx hash: {:02x}", funding_tx.tx_hash()); + wait_for_confirmations(&taker_coin, &funding_tx, 100); + + let taker_pub = &taker_coin.derive_htlc_pubkey_v2(&[]); + let approve_args = GenTakerFundingSpendArgs { + funding_tx: &funding_tx, + maker_pub, + taker_pub, + funding_time_lock, + taker_secret_hash: &taker_secret_hash, + taker_payment_time_lock: funding_time_lock, + maker_secret_hash: &maker_secret_hash, + }; + let preimage = TxPreimageWithSig { + preimage: funding_tx.clone(), + signature: taker_coin.parse_signature(&[0u8; 65]).unwrap(), + }; + wait_pending_transactions(Address::from_slice(taker_address.as_bytes())); + let taker_approve_tx = + block_on(taker_coin.sign_and_send_taker_funding_spend(&preimage, &approve_args, &[])).unwrap(); + log!( + "Taker approved ETH payment, tx hash: {:02x}", + taker_approve_tx.tx_hash() + ); + wait_for_confirmations(&taker_coin, &taker_approve_tx, 100); + + let tx_type_with_secret_hash = SwapTxTypeWithSecretHash::TakerPaymentV2 { + maker_secret_hash: &maker_secret_hash, + taker_secret_hash: &taker_secret_hash, + }; + let refund_args = RefundTakerPaymentArgs { + payment_tx: &funding_tx.to_bytes(), + time_lock: payment_time_lock, + maker_pub: maker_pub.as_bytes(), + tx_type_with_secret_hash, + swap_unique_data: &[], + watcher_reward: false, + dex_fee, + premium_amount: BigDecimal::default(), + trading_amount, + }; + wait_pending_transactions(Address::from_slice(taker_address.as_bytes())); + let funding_tx_refund = block_on(taker_coin.refund_taker_funding_timelock(refund_args)).unwrap(); + log!( + "Taker refunded ETH funding after payment lock time was exceeded, tx hash: {:02x}", + funding_tx_refund.tx_hash() + ); + wait_for_confirmations(&taker_coin, &funding_tx_refund, 100); +} + +#[ignore] +#[test] +fn send_and_refund_taker_funding_exceed_payment_timelock_erc20() { + // sepolia test + thread::sleep(Duration::from_secs(28)); + let erc20_conf = &sepolia_erc20_dev_conf(&sepolia_erc20_contract_checksum()); + let taker_coin = get_or_create_sepolia_coin(&MM_CTX1, SEPOLIA_TAKER_PRIV, ERC20, erc20_conf, true); + let maker_coin = get_or_create_sepolia_coin(&MM_CTX, SEPOLIA_MAKER_PRIV, ERC20, erc20_conf, true); + + let taker_secret = vec![0; 32]; + let taker_secret_hash = sha256(&taker_secret).to_vec(); + let maker_secret = vec![1; 32]; + let maker_secret_hash = sha256(&maker_secret).to_vec(); + let funding_time_lock = now_sec() + 29; + let payment_time_lock = now_sec() + 15; + + let taker_address = block_on(taker_coin.my_addr()); + + let dex_fee = &DexFee::Standard("0.00001".into()); + let trading_amount = BigDecimal::from_str("0.0001").unwrap(); + + let maker_pub = &maker_coin.derive_htlc_pubkey_v2(&[]); + let payment_args = SendTakerFundingArgs { + funding_time_lock, + payment_time_lock, + taker_secret_hash: &taker_secret_hash, + maker_secret_hash: &maker_secret_hash, + maker_pub: maker_pub.as_bytes(), + dex_fee, + premium_amount: BigDecimal::default(), + trading_amount: trading_amount.clone(), + swap_unique_data: &[], + }; + wait_pending_transactions(Address::from_slice(taker_address.as_bytes())); + let funding_tx = block_on(taker_coin.send_taker_funding(payment_args)).unwrap(); + log!("Taker sent ERC20 funding, tx hash: {:02x}", funding_tx.tx_hash()); + wait_for_confirmations(&taker_coin, &funding_tx, 100); + thread::sleep(Duration::from_secs(16)); + + let taker_pub = &taker_coin.derive_htlc_pubkey_v2(&[]); + let approve_args = GenTakerFundingSpendArgs { + funding_tx: &funding_tx, + maker_pub, + taker_pub, + funding_time_lock, + taker_secret_hash: &taker_secret_hash, + taker_payment_time_lock: funding_time_lock, + maker_secret_hash: &maker_secret_hash, + }; + let preimage = TxPreimageWithSig { + preimage: funding_tx.clone(), + signature: taker_coin.parse_signature(&[0u8; 65]).unwrap(), + }; + wait_pending_transactions(Address::from_slice(taker_address.as_bytes())); + let taker_approve_tx = + block_on(taker_coin.sign_and_send_taker_funding_spend(&preimage, &approve_args, &[])).unwrap(); + log!( + "Taker approved ERC20 payment, tx hash: {:02x}", + taker_approve_tx.tx_hash() + ); + wait_for_confirmations(&taker_coin, &taker_approve_tx, 100); + + let tx_type_with_secret_hash = SwapTxTypeWithSecretHash::TakerPaymentV2 { + maker_secret_hash: &maker_secret_hash, + taker_secret_hash: &taker_secret_hash, + }; + let refund_args = RefundTakerPaymentArgs { + payment_tx: &funding_tx.to_bytes(), + time_lock: payment_time_lock, + maker_pub: maker_pub.as_bytes(), + tx_type_with_secret_hash, + swap_unique_data: &[], + watcher_reward: false, + dex_fee, + premium_amount: BigDecimal::default(), + trading_amount, + }; + wait_pending_transactions(Address::from_slice(taker_address.as_bytes())); + let funding_tx_refund = block_on(taker_coin.refund_taker_funding_timelock(refund_args)).unwrap(); + log!( + "Taker refunded ERC20 funding after payment lock time was exceeded, tx hash: {:02x}", + funding_tx_refund.tx_hash() + ); + wait_for_confirmations(&taker_coin, &funding_tx_refund, 100); +} + +#[ignore] +#[test] +fn send_and_refund_taker_funding_exceed_pre_approve_timelock_erc20() { + // sepolia test + thread::sleep(Duration::from_secs(200)); + let erc20_conf = &sepolia_erc20_dev_conf(&sepolia_erc20_contract_checksum()); + let taker_coin = get_or_create_sepolia_coin(&MM_CTX1, SEPOLIA_TAKER_PRIV, ERC20, erc20_conf, true); + let maker_coin = get_or_create_sepolia_coin(&MM_CTX1, SEPOLIA_MAKER_PRIV, ERC20, erc20_conf, true); + + let taker_secret = vec![0; 32]; + let taker_secret_hash = sha256(&taker_secret).to_vec(); + let maker_secret = vec![1; 32]; + let maker_secret_hash = sha256(&maker_secret).to_vec(); + + let taker_address = block_on(taker_coin.my_addr()); + + // if TakerPaymentState is `PaymentSent` then timestamp should exceed payment pre-approve lock time (funding_time_lock) + let funding_time_lock = now_sec() + 29; + let payment_time_lock = now_sec() + 1000; + + let dex_fee = &DexFee::Standard("0.00001".into()); + let trading_amount = BigDecimal::from_str("0.0001").unwrap(); + + let maker_pub = &maker_coin.derive_htlc_pubkey_v2(&[]); + let payment_args = SendTakerFundingArgs { + funding_time_lock, + payment_time_lock, + taker_secret_hash: &taker_secret_hash, + maker_secret_hash: &maker_secret_hash, + maker_pub: maker_pub.as_bytes(), + dex_fee, + premium_amount: BigDecimal::default(), + trading_amount: trading_amount.clone(), + swap_unique_data: &[], + }; + wait_pending_transactions(Address::from_slice(taker_address.as_bytes())); + let funding_tx = block_on(taker_coin.send_taker_funding(payment_args)).unwrap(); + log!("Taker sent ERC20 funding, tx hash: {:02x}", funding_tx.tx_hash()); + wait_for_confirmations(&taker_coin, &funding_tx, 150); + thread::sleep(Duration::from_secs(29)); + + let tx_type_with_secret_hash = SwapTxTypeWithSecretHash::TakerPaymentV2 { + maker_secret_hash: &maker_secret_hash, + taker_secret_hash: &taker_secret_hash, + }; + + let refund_args = RefundTakerPaymentArgs { + payment_tx: &funding_tx.to_bytes(), + time_lock: payment_time_lock, + maker_pub: maker_pub.as_bytes(), + tx_type_with_secret_hash, + swap_unique_data: &[], + watcher_reward: false, + dex_fee, + premium_amount: BigDecimal::default(), + trading_amount, + }; + wait_pending_transactions(Address::from_slice(taker_address.as_bytes())); + thread::sleep(Duration::from_secs(3)); + let funding_tx_refund = block_on(taker_coin.refund_taker_funding_timelock(refund_args)).unwrap(); + log!( + "Taker refunded ERC20 funding after pre-approval lock time was exceeded, tx hash: {:02x}", + funding_tx_refund.tx_hash() + ); + wait_for_confirmations(&taker_coin, &funding_tx_refund, 150); +} diff --git a/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs b/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs index f6491dcef7..672ef88aa5 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs @@ -2,9 +2,10 @@ use crate::{generate_utxo_coin_with_random_privkey, MYCOIN, MYCOIN1}; use bitcrypto::dhash160; use coins::utxo::UtxoCommonOps; use coins::{ConfirmPaymentInput, DexFee, FundingTxSpend, GenTakerFundingSpendArgs, GenTakerPaymentSpendArgs, - MakerCoinSwapOpsV2, MarketCoinOps, ParseCoinAssocTypes, RefundFundingSecretArgs, RefundMakerPaymentArgs, - RefundPaymentArgs, SendMakerPaymentArgs, SendTakerFundingArgs, SwapTxTypeWithSecretHash, - TakerCoinSwapOpsV2, Transaction, ValidateMakerPaymentArgs, ValidateTakerFundingArgs}; + MakerCoinSwapOpsV2, MarketCoinOps, ParseCoinAssocTypes, RefundFundingSecretArgs, + RefundMakerPaymentSecretArgs, RefundMakerPaymentTimelockArgs, RefundTakerPaymentArgs, + SendMakerPaymentArgs, SendTakerFundingArgs, SwapTxTypeWithSecretHash, TakerCoinSwapOpsV2, Transaction, + ValidateMakerPaymentArgs, ValidateTakerFundingArgs}; use common::{block_on, now_sec, DEX_FEE_ADDR_RAW_PUBKEY}; use futures01::Future; use mm2_number::MmNumber; @@ -55,9 +56,11 @@ fn send_and_refund_taker_funding_timelock() { let validate_args = ValidateTakerFundingArgs { funding_tx: &taker_funding_utxo_tx, - time_lock: funding_time_lock, + payment_time_lock: 0, + funding_time_lock, taker_secret_hash, - other_pub: maker_pub, + maker_secret_hash: &[], + taker_pub: maker_pub, dex_fee, premium_amount: "0.1".parse().unwrap(), trading_amount: 1.into(), @@ -65,16 +68,18 @@ fn send_and_refund_taker_funding_timelock() { }; block_on(coin.validate_taker_funding(validate_args)).unwrap(); - let refund_args = RefundPaymentArgs { + let refund_args = RefundTakerPaymentArgs { payment_tx: &serialize(&taker_funding_utxo_tx).take(), time_lock: funding_time_lock, - other_pubkey: coin.my_public_key().unwrap(), + maker_pub: coin.my_public_key().unwrap(), tx_type_with_secret_hash: SwapTxTypeWithSecretHash::TakerFunding { taker_secret_hash: &[0; 20], }, swap_unique_data: &[], - swap_contract_address: &None, watcher_reward: false, + dex_fee, + premium_amount: Default::default(), + trading_amount: Default::default(), }; let refund_tx = block_on(coin.refund_taker_funding_timelock(refund_args)).unwrap(); @@ -137,9 +142,11 @@ fn send_and_refund_taker_funding_secret() { let validate_args = ValidateTakerFundingArgs { funding_tx: &taker_funding_utxo_tx, - time_lock: funding_time_lock, + funding_time_lock, + payment_time_lock: 0, taker_secret_hash, - other_pub: maker_pub, + maker_secret_hash: &[], + taker_pub: maker_pub, dex_fee, premium_amount: "0.1".parse().unwrap(), trading_amount: 1.into(), @@ -155,7 +162,6 @@ fn send_and_refund_taker_funding_secret() { taker_secret: &taker_secret, taker_secret_hash, maker_secret_hash: &[], - swap_contract_address: &None, dex_fee, premium_amount: "0.1".parse().unwrap(), trading_amount: 1.into(), @@ -228,9 +234,11 @@ fn send_and_spend_taker_funding() { let validate_args = ValidateTakerFundingArgs { funding_tx: &taker_funding_utxo_tx, - time_lock: funding_time_lock, + payment_time_lock: 0, + funding_time_lock, taker_secret_hash, - other_pub: taker_pub, + maker_secret_hash: &[], + taker_pub, dex_fee, premium_amount: "0.1".parse().unwrap(), trading_amount: 1.into(), @@ -317,9 +325,11 @@ fn send_and_spend_taker_payment_dex_fee_burn() { let validate_args = ValidateTakerFundingArgs { funding_tx: &taker_funding_utxo_tx, - time_lock: funding_time_lock, + funding_time_lock, + payment_time_lock: 0, taker_secret_hash, - other_pub: taker_pub, + maker_secret_hash, + taker_pub, dex_fee, premium_amount: 0.into(), trading_amount: 777.into(), @@ -422,9 +432,11 @@ fn send_and_spend_taker_payment_standard_dex_fee() { let validate_args = ValidateTakerFundingArgs { funding_tx: &taker_funding_utxo_tx, - time_lock: funding_time_lock, + funding_time_lock, + payment_time_lock: 0, taker_secret_hash, - other_pub: taker_pub, + maker_secret_hash, + taker_pub, dex_fee, premium_amount: 0.into(), trading_amount: 777.into(), @@ -528,10 +540,10 @@ fn send_and_refund_maker_payment_timelock() { }; block_on(coin.validate_maker_payment_v2(validate_args)).unwrap(); - let refund_args = RefundPaymentArgs { + let refund_args = RefundMakerPaymentTimelockArgs { payment_tx: &serialize(&maker_payment).take(), time_lock, - other_pubkey: coin.my_public_key().unwrap(), + taker_pub: coin.my_public_key().unwrap(), tx_type_with_secret_hash: SwapTxTypeWithSecretHash::MakerPaymentV2 { taker_secret_hash, maker_secret_hash, @@ -592,7 +604,7 @@ fn send_and_refund_maker_payment_taker_secret() { }; block_on(coin.validate_maker_payment_v2(validate_args)).unwrap(); - let refund_args = RefundMakerPaymentArgs { + let refund_args = RefundMakerPaymentSecretArgs { maker_payment_tx: &maker_payment, time_lock, taker_secret_hash, diff --git a/mm2src/mm2_test_helpers/contract_bytes/maker_swap_v2_bytes b/mm2src/mm2_test_helpers/contract_bytes/maker_swap_v2_bytes index 5b8bda7350..5ac667f370 100644 --- a/mm2src/mm2_test_helpers/contract_bytes/maker_swap_v2_bytes +++ b/mm2src/mm2_test_helpers/contract_bytes/maker_swap_v2_bytes @@ -1 +1 @@ -6080604052348015600e575f80fd5b50611d9f8061001c5f395ff3fe608060405260043610610054575f3560e01c80631299a27a146100585780637466be601461008057806374a4788a1461009c5780639b949dee146100c4578063a53bc126146100ec578063efccb9eb14610114575b5f80fd5b348015610063575f80fd5b5061007e60048036038101906100799190611427565b610152565b005b61009a600480360381019061009591906114e9565b61044b565b005b3480156100a7575f80fd5b506100c260048036038101906100bd9190611427565b6106fc565b005b3480156100cf575f80fd5b506100ea60048036038101906100e59190611427565b6109f5565b005b3480156100f7575f80fd5b50610112600480360381019061010d9190611560565b610ced565b005b34801561011f575f80fd5b5061013a600480360381019061013591906115fd565b610fd2565b604051610149939291906116e4565b60405180910390f35b6001600381111561016657610165611671565b5b5f808881526020019081526020015f205f0160189054906101000a900460ff16600381111561019857610197611671565b5b146101d8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101cf90611799565b60405180910390fd5b5f6003863387876002886040516020016101f291906117d7565b60405160208183030381529060405260405161020e9190611843565b602060405180830381855afa158015610229573d5f803e3d5ffd5b5050506040513d601f19601f8201168201806040525081019061024c919061186d565b87604051602001610262969594939291906118fd565b60405160208183030381529060405260405161027e9190611843565b602060405180830381855afa158015610299573d5f803e3d5ffd5b5050506040515160601b90505f808881526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614610323576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161031a906119b6565b60405180910390fd5b60025f808981526020019081526020015f205f0160186101000a81548160ff0219169083600381111561035957610358611671565b5b02179055507fad62ed075fe8969df63026f45152d6e996a0697a736a8de92ee85ae9c9958cf08760405161038d91906119e3565b60405180910390a15f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610411573373ffffffffffffffffffffffffffffffffffffffff166108fc8790811502906040515f60405180830381858888f1935050505015801561040b573d5f803e3d5ffd5b50610442565b5f82905061044033888373ffffffffffffffffffffffffffffffffffffffff1661101e9092919063ffffffff16565b505b50505050505050565b5f600381111561045e5761045d611671565b5b5f808781526020019081526020015f205f0160189054906101000a900460ff1660038111156104905761048f611671565b5b146104d0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104c790611a6c565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff160361053e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161053590611ad4565b60405180910390fd5b5f3411610580576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161057790611b62565b60405180910390fd5b5f600334863387875f60405160200161059e969594939291906118fd565b6040516020818303038152906040526040516105ba9190611843565b602060405180830381855afa1580156105d5573d5f803e3d5ffd5b5050506040515160601b90506040518060600160405280826bffffffffffffffffffffffff191681526020018363ffffffff1681526020016001600381111561062157610620611671565b5b8152505f808881526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548163ffffffff021916908363ffffffff1602179055506040820151815f0160186101000a81548160ff021916908360038111156106b5576106b4611671565b5b02179055509050507ff1dc11bbb6d7542c4267ecf1d370ff4c7092518633ecae9939e8488f4e53d2ad866040516106ec91906119e3565b60405180910390a1505050505050565b600160038111156107105761070f611671565b5b5f808881526020019081526020015f205f0160189054906101000a900460ff16600381111561074257610741611671565b5b14610782576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161077990611799565b60405180910390fd5b5f600386863360028860405160200161079b91906117d7565b6040516020818303038152906040526040516107b79190611843565b602060405180830381855afa1580156107d2573d5f803e3d5ffd5b5050506040513d601f19601f820116820180604052508101906107f5919061186d565b878760405160200161080c969594939291906118fd565b6040516020818303038152906040526040516108289190611843565b602060405180830381855afa158015610843573d5f803e3d5ffd5b5050506040515160601b90505f808881526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff1916146108cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108c4906119b6565b60405180910390fd5b60035f808981526020019081526020015f205f0160186101000a81548160ff0219169083600381111561090357610902611671565b5b02179055507fac509cdcc7ddb189f81fff6f4824f5c95076e64c3bdce542c50feaa6779afd738760405161093791906119e3565b60405180910390a15f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036109bb573373ffffffffffffffffffffffffffffffffffffffff166108fc8790811502906040515f60405180830381858888f193505050501580156109b5573d5f803e3d5ffd5b506109ec565b5f8290506109ea33888373ffffffffffffffffffffffffffffffffffffffff1661101e9092919063ffffffff16565b505b50505050505050565b60016003811115610a0957610a08611671565b5b5f808881526020019081526020015f205f0160189054906101000a900460ff166003811115610a3b57610a3a611671565b5b14610a7b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a7290611799565b60405180910390fd5b5f6003868633878787604051602001610a99969594939291906118fd565b604051602081830303815290604052604051610ab59190611843565b602060405180830381855afa158015610ad0573d5f803e3d5ffd5b5050506040515160601b90505f808881526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614610b5a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b51906119b6565b60405180910390fd5b5f808881526020019081526020015f205f0160149054906101000a900463ffffffff1663ffffffff16421015610bc5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bbc90611bf0565b60405180910390fd5b60035f808981526020019081526020015f205f0160186101000a81548160ff02191690836003811115610bfb57610bfa611671565b5b02179055507f5dedc4f52b757d9112d09ca0b2f022927104d54e3f54da091587e8ad1921907287604051610c2f91906119e3565b60405180910390a15f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610cb3573373ffffffffffffffffffffffffffffffffffffffff166108fc8790811502906040515f60405180830381858888f19350505050158015610cad573d5f803e3d5ffd5b50610ce4565b5f829050610ce233888373ffffffffffffffffffffffffffffffffffffffff1661101e9092919063ffffffff16565b505b50505050505050565b5f6003811115610d0057610cff611671565b5b5f808981526020019081526020015f205f0160189054906101000a900460ff166003811115610d3257610d31611671565b5b14610d72576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d6990611a6c565b60405180910390fd5b5f8611610db4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610dab90611c58565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1603610e22576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e1990611ad4565b60405180910390fd5b5f600387863387878b604051602001610e40969594939291906118fd565b604051602081830303815290604052604051610e5c9190611843565b602060405180830381855afa158015610e77573d5f803e3d5ffd5b5050506040515160601b90506040518060600160405280826bffffffffffffffffffffffff191681526020018363ffffffff16815260200160016003811115610ec357610ec2611671565b5b8152505f808a81526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548163ffffffff021916908363ffffffff1602179055506040820151815f0160186101000a81548160ff02191690836003811115610f5757610f56611671565b5b02179055509050507ff1dc11bbb6d7542c4267ecf1d370ff4c7092518633ecae9939e8488f4e53d2ad88604051610f8e91906119e3565b60405180910390a15f869050610fc733308a8473ffffffffffffffffffffffffffffffffffffffff1661109d909392919063ffffffff16565b505050505050505050565b5f602052805f5260405f205f91509050805f015f9054906101000a900460601b90805f0160149054906101000a900463ffffffff1690805f0160189054906101000a900460ff16905083565b611098838473ffffffffffffffffffffffffffffffffffffffff1663a9059cbb8585604051602401611051929190611c94565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505061111f565b505050565b611119848573ffffffffffffffffffffffffffffffffffffffff166323b872dd8686866040516024016110d293929190611cbb565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505061111f565b50505050565b5f611149828473ffffffffffffffffffffffffffffffffffffffff166111b490919063ffffffff16565b90505f81511415801561116d57508080602001905181019061116b9190611d25565b155b156111af57826040517f5274afe70000000000000000000000000000000000000000000000000000000081526004016111a69190611d50565b60405180910390fd5b505050565b60606111c183835f6111c9565b905092915050565b60608147101561121057306040517fcd7860590000000000000000000000000000000000000000000000000000000081526004016112079190611d50565b60405180910390fd5b5f808573ffffffffffffffffffffffffffffffffffffffff1684866040516112389190611843565b5f6040518083038185875af1925050503d805f8114611272576040519150601f19603f3d011682016040523d82523d5f602084013e611277565b606091505b5091509150611287868383611292565b925050509392505050565b6060826112a7576112a28261131f565b611317565b5f82511480156112cd57505f8473ffffffffffffffffffffffffffffffffffffffff163b145b1561130f57836040517f9996b3150000000000000000000000000000000000000000000000000000000081526004016113069190611d50565b60405180910390fd5b819050611318565b5b9392505050565b5f815111156113315780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f80fd5b5f819050919050565b61137981611367565b8114611383575f80fd5b50565b5f8135905061139481611370565b92915050565b5f819050919050565b6113ac8161139a565b81146113b6575f80fd5b50565b5f813590506113c7816113a3565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6113f6826113cd565b9050919050565b611406816113ec565b8114611410575f80fd5b50565b5f81359050611421816113fd565b92915050565b5f805f805f8060c0878903121561144157611440611363565b5b5f61144e89828a01611386565b965050602061145f89828a016113b9565b955050604061147089828a01611413565b945050606061148189828a01611386565b935050608061149289828a01611386565b92505060a06114a389828a01611413565b9150509295509295509295565b5f63ffffffff82169050919050565b6114c8816114b0565b81146114d2575f80fd5b50565b5f813590506114e3816114bf565b92915050565b5f805f805f60a0868803121561150257611501611363565b5b5f61150f88828901611386565b955050602061152088828901611413565b945050604061153188828901611386565b935050606061154288828901611386565b9250506080611553888289016114d5565b9150509295509295909350565b5f805f805f805f60e0888a03121561157b5761157a611363565b5b5f6115888a828b01611386565b97505060206115998a828b016113b9565b96505060406115aa8a828b01611413565b95505060606115bb8a828b01611413565b94505060806115cc8a828b01611386565b93505060a06115dd8a828b01611386565b92505060c06115ee8a828b016114d5565b91505092959891949750929550565b5f6020828403121561161257611611611363565b5b5f61161f84828501611386565b91505092915050565b5f7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000082169050919050565b61165c81611628565b82525050565b61166b816114b0565b82525050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b600481106116af576116ae611671565b5b50565b5f8190506116bf8261169e565b919050565b5f6116ce826116b2565b9050919050565b6116de816116c4565b82525050565b5f6060820190506116f75f830186611653565b6117046020830185611662565b61171160408301846116d5565b949350505050565b5f82825260208201905092915050565b7f496e76616c6964207061796d656e742073746174652e204d75737420626520505f8201527f61796d656e7453656e7400000000000000000000000000000000000000000000602082015250565b5f611783602a83611719565b915061178e82611729565b604082019050919050565b5f6020820190508181035f8301526117b081611777565b9050919050565b5f819050919050565b6117d16117cc82611367565b6117b7565b82525050565b5f6117e282846117c0565b60208201915081905092915050565b5f81519050919050565b5f81905092915050565b8281835e5f83830152505050565b5f61181d826117f1565b61182781856117fb565b9350611837818560208601611805565b80840191505092915050565b5f61184e8284611813565b915081905092915050565b5f8151905061186781611370565b92915050565b5f6020828403121561188257611881611363565b5b5f61188f84828501611859565b91505092915050565b5f819050919050565b6118b26118ad8261139a565b611898565b82525050565b5f8160601b9050919050565b5f6118ce826118b8565b9050919050565b5f6118df826118c4565b9050919050565b6118f76118f2826113ec565b6118d5565b82525050565b5f61190882896118a1565b60208201915061191882886118e6565b60148201915061192882876118e6565b60148201915061193882866117c0565b60208201915061194882856117c0565b60208201915061195882846118e6565b601482019150819050979650505050505050565b7f496e76616c6964207061796d656e7448617368000000000000000000000000005f82015250565b5f6119a0601383611719565b91506119ab8261196c565b602082019050919050565b5f6020820190508181035f8301526119cd81611994565b9050919050565b6119dd81611367565b82525050565b5f6020820190506119f65f8301846119d4565b92915050565b7f4d616b6572207061796d656e7420697320616c726561647920696e697469616c5f8201527f697a656400000000000000000000000000000000000000000000000000000000602082015250565b5f611a56602483611719565b9150611a61826119fc565b604082019050919050565b5f6020820190508181035f830152611a8381611a4a565b9050919050565b7f54616b6572206d757374206e6f74206265207a65726f206164647265737300005f82015250565b5f611abe601e83611719565b9150611ac982611a8a565b602082019050919050565b5f6020820190508181035f830152611aeb81611ab2565b9050919050565b7f4554482076616c7565206d7573742062652067726561746572207468616e207a5f8201527f65726f0000000000000000000000000000000000000000000000000000000000602082015250565b5f611b4c602383611719565b9150611b5782611af2565b604082019050919050565b5f6020820190508181035f830152611b7981611b40565b9050919050565b7f43757272656e742074696d657374616d70206469646e277420657863656564205f8201527f7061796d656e7420726566756e64206c6f636b2074696d650000000000000000602082015250565b5f611bda603883611719565b9150611be582611b80565b604082019050919050565b5f6020820190508181035f830152611c0781611bce565b9050919050565b7f416d6f756e74206d757374206e6f74206265207a65726f0000000000000000005f82015250565b5f611c42601783611719565b9150611c4d82611c0e565b602082019050919050565b5f6020820190508181035f830152611c6f81611c36565b9050919050565b611c7f816113ec565b82525050565b611c8e8161139a565b82525050565b5f604082019050611ca75f830185611c76565b611cb46020830184611c85565b9392505050565b5f606082019050611cce5f830186611c76565b611cdb6020830185611c76565b611ce86040830184611c85565b949350505050565b5f8115159050919050565b611d0481611cf0565b8114611d0e575f80fd5b50565b5f81519050611d1f81611cfb565b92915050565b5f60208284031215611d3a57611d39611363565b5b5f611d4784828501611d11565b91505092915050565b5f602082019050611d635f830184611c76565b9291505056fea2646970667358221220565f86096f083c176a08cc3c220c01bd63eb7268584f034f3380299de3dac57f64736f6c63430008190033 \ No newline at end of file +6080604052348015600e575f80fd5b50611d9f8061001c5f395ff3fe608060405260043610610054575f3560e01c80631299a27a146100585780637466be601461008057806374a4788a1461009c5780639b949dee146100c4578063a53bc126146100ec578063efccb9eb14610114575b5f80fd5b348015610063575f80fd5b5061007e60048036038101906100799190611427565b610152565b005b61009a600480360381019061009591906114e9565b61044b565b005b3480156100a7575f80fd5b506100c260048036038101906100bd9190611427565b6106fc565b005b3480156100cf575f80fd5b506100ea60048036038101906100e59190611427565b6109f5565b005b3480156100f7575f80fd5b50610112600480360381019061010d9190611560565b610ced565b005b34801561011f575f80fd5b5061013a600480360381019061013591906115fd565b610fd2565b604051610149939291906116e4565b60405180910390f35b6001600381111561016657610165611671565b5b5f808881526020019081526020015f205f0160189054906101000a900460ff16600381111561019857610197611671565b5b146101d8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101cf90611799565b60405180910390fd5b5f6003863387876002886040516020016101f291906117d7565b60405160208183030381529060405260405161020e9190611843565b602060405180830381855afa158015610229573d5f803e3d5ffd5b5050506040513d601f19601f8201168201806040525081019061024c919061186d565b87604051602001610262969594939291906118fd565b60405160208183030381529060405260405161027e9190611843565b602060405180830381855afa158015610299573d5f803e3d5ffd5b5050506040515160601b90505f808881526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614610323576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161031a906119b6565b60405180910390fd5b60025f808981526020019081526020015f205f0160186101000a81548160ff0219169083600381111561035957610358611671565b5b02179055507fad62ed075fe8969df63026f45152d6e996a0697a736a8de92ee85ae9c9958cf08760405161038d91906119e3565b60405180910390a15f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610411573373ffffffffffffffffffffffffffffffffffffffff166108fc8790811502906040515f60405180830381858888f1935050505015801561040b573d5f803e3d5ffd5b50610442565b5f82905061044033888373ffffffffffffffffffffffffffffffffffffffff1661101e9092919063ffffffff16565b505b50505050505050565b5f600381111561045e5761045d611671565b5b5f808781526020019081526020015f205f0160189054906101000a900460ff1660038111156104905761048f611671565b5b146104d0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104c790611a6c565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff160361053e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161053590611ad4565b60405180910390fd5b5f3411610580576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161057790611b62565b60405180910390fd5b5f600334863387875f60405160200161059e969594939291906118fd565b6040516020818303038152906040526040516105ba9190611843565b602060405180830381855afa1580156105d5573d5f803e3d5ffd5b5050506040515160601b90506040518060600160405280826bffffffffffffffffffffffff191681526020018363ffffffff1681526020016001600381111561062157610620611671565b5b8152505f808881526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548163ffffffff021916908363ffffffff1602179055506040820151815f0160186101000a81548160ff021916908360038111156106b5576106b4611671565b5b02179055509050507ff1dc11bbb6d7542c4267ecf1d370ff4c7092518633ecae9939e8488f4e53d2ad866040516106ec91906119e3565b60405180910390a1505050505050565b600160038111156107105761070f611671565b5b5f808881526020019081526020015f205f0160189054906101000a900460ff16600381111561074257610741611671565b5b14610782576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161077990611799565b60405180910390fd5b5f600386863360028860405160200161079b91906117d7565b6040516020818303038152906040526040516107b79190611843565b602060405180830381855afa1580156107d2573d5f803e3d5ffd5b5050506040513d601f19601f820116820180604052508101906107f5919061186d565b878760405160200161080c969594939291906118fd565b6040516020818303038152906040526040516108289190611843565b602060405180830381855afa158015610843573d5f803e3d5ffd5b5050506040515160601b90505f808881526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff1916146108cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108c4906119b6565b60405180910390fd5b60035f808981526020019081526020015f205f0160186101000a81548160ff0219169083600381111561090357610902611671565b5b02179055507fac509cdcc7ddb189f81fff6f4824f5c95076e64c3bdce542c50feaa6779afd738760405161093791906119e3565b60405180910390a15f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036109bb573373ffffffffffffffffffffffffffffffffffffffff166108fc8790811502906040515f60405180830381858888f193505050501580156109b5573d5f803e3d5ffd5b506109ec565b5f8290506109ea33888373ffffffffffffffffffffffffffffffffffffffff1661101e9092919063ffffffff16565b505b50505050505050565b60016003811115610a0957610a08611671565b5b5f808881526020019081526020015f205f0160189054906101000a900460ff166003811115610a3b57610a3a611671565b5b14610a7b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a7290611799565b60405180910390fd5b5f6003868633878787604051602001610a99969594939291906118fd565b604051602081830303815290604052604051610ab59190611843565b602060405180830381855afa158015610ad0573d5f803e3d5ffd5b5050506040515160601b90505f808881526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614610b5a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b51906119b6565b60405180910390fd5b5f808881526020019081526020015f205f0160149054906101000a900463ffffffff1663ffffffff16421015610bc5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bbc90611bf0565b60405180910390fd5b60035f808981526020019081526020015f205f0160186101000a81548160ff02191690836003811115610bfb57610bfa611671565b5b02179055507f5dedc4f52b757d9112d09ca0b2f022927104d54e3f54da091587e8ad1921907287604051610c2f91906119e3565b60405180910390a15f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610cb3573373ffffffffffffffffffffffffffffffffffffffff166108fc8790811502906040515f60405180830381858888f19350505050158015610cad573d5f803e3d5ffd5b50610ce4565b5f829050610ce233888373ffffffffffffffffffffffffffffffffffffffff1661101e9092919063ffffffff16565b505b50505050505050565b5f6003811115610d0057610cff611671565b5b5f808981526020019081526020015f205f0160189054906101000a900460ff166003811115610d3257610d31611671565b5b14610d72576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d6990611a6c565b60405180910390fd5b5f8611610db4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610dab90611c58565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1603610e22576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e1990611ad4565b60405180910390fd5b5f600387863387878b604051602001610e40969594939291906118fd565b604051602081830303815290604052604051610e5c9190611843565b602060405180830381855afa158015610e77573d5f803e3d5ffd5b5050506040515160601b90506040518060600160405280826bffffffffffffffffffffffff191681526020018363ffffffff16815260200160016003811115610ec357610ec2611671565b5b8152505f808a81526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548163ffffffff021916908363ffffffff1602179055506040820151815f0160186101000a81548160ff02191690836003811115610f5757610f56611671565b5b02179055509050507ff1dc11bbb6d7542c4267ecf1d370ff4c7092518633ecae9939e8488f4e53d2ad88604051610f8e91906119e3565b60405180910390a15f869050610fc733308a8473ffffffffffffffffffffffffffffffffffffffff1661109d909392919063ffffffff16565b505050505050505050565b5f602052805f5260405f205f91509050805f015f9054906101000a900460601b90805f0160149054906101000a900463ffffffff1690805f0160189054906101000a900460ff16905083565b611098838473ffffffffffffffffffffffffffffffffffffffff1663a9059cbb8585604051602401611051929190611c94565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505061111f565b505050565b611119848573ffffffffffffffffffffffffffffffffffffffff166323b872dd8686866040516024016110d293929190611cbb565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505061111f565b50505050565b5f611149828473ffffffffffffffffffffffffffffffffffffffff166111b490919063ffffffff16565b90505f81511415801561116d57508080602001905181019061116b9190611d25565b155b156111af57826040517f5274afe70000000000000000000000000000000000000000000000000000000081526004016111a69190611d50565b60405180910390fd5b505050565b60606111c183835f6111c9565b905092915050565b60608147101561121057306040517fcd7860590000000000000000000000000000000000000000000000000000000081526004016112079190611d50565b60405180910390fd5b5f808573ffffffffffffffffffffffffffffffffffffffff1684866040516112389190611843565b5f6040518083038185875af1925050503d805f8114611272576040519150601f19603f3d011682016040523d82523d5f602084013e611277565b606091505b5091509150611287868383611292565b925050509392505050565b6060826112a7576112a28261131f565b611317565b5f82511480156112cd57505f8473ffffffffffffffffffffffffffffffffffffffff163b145b1561130f57836040517f9996b3150000000000000000000000000000000000000000000000000000000081526004016113069190611d50565b60405180910390fd5b819050611318565b5b9392505050565b5f815111156113315780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f80fd5b5f819050919050565b61137981611367565b8114611383575f80fd5b50565b5f8135905061139481611370565b92915050565b5f819050919050565b6113ac8161139a565b81146113b6575f80fd5b50565b5f813590506113c7816113a3565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6113f6826113cd565b9050919050565b611406816113ec565b8114611410575f80fd5b50565b5f81359050611421816113fd565b92915050565b5f805f805f8060c0878903121561144157611440611363565b5b5f61144e89828a01611386565b965050602061145f89828a016113b9565b955050604061147089828a01611413565b945050606061148189828a01611386565b935050608061149289828a01611386565b92505060a06114a389828a01611413565b9150509295509295509295565b5f63ffffffff82169050919050565b6114c8816114b0565b81146114d2575f80fd5b50565b5f813590506114e3816114bf565b92915050565b5f805f805f60a0868803121561150257611501611363565b5b5f61150f88828901611386565b955050602061152088828901611413565b945050604061153188828901611386565b935050606061154288828901611386565b9250506080611553888289016114d5565b9150509295509295909350565b5f805f805f805f60e0888a03121561157b5761157a611363565b5b5f6115888a828b01611386565b97505060206115998a828b016113b9565b96505060406115aa8a828b01611413565b95505060606115bb8a828b01611413565b94505060806115cc8a828b01611386565b93505060a06115dd8a828b01611386565b92505060c06115ee8a828b016114d5565b91505092959891949750929550565b5f6020828403121561161257611611611363565b5b5f61161f84828501611386565b91505092915050565b5f7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000082169050919050565b61165c81611628565b82525050565b61166b816114b0565b82525050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b600481106116af576116ae611671565b5b50565b5f8190506116bf8261169e565b919050565b5f6116ce826116b2565b9050919050565b6116de816116c4565b82525050565b5f6060820190506116f75f830186611653565b6117046020830185611662565b61171160408301846116d5565b949350505050565b5f82825260208201905092915050565b7f496e76616c6964207061796d656e742073746174652e204d75737420626520505f8201527f61796d656e7453656e7400000000000000000000000000000000000000000000602082015250565b5f611783602a83611719565b915061178e82611729565b604082019050919050565b5f6020820190508181035f8301526117b081611777565b9050919050565b5f819050919050565b6117d16117cc82611367565b6117b7565b82525050565b5f6117e282846117c0565b60208201915081905092915050565b5f81519050919050565b5f81905092915050565b8281835e5f83830152505050565b5f61181d826117f1565b61182781856117fb565b9350611837818560208601611805565b80840191505092915050565b5f61184e8284611813565b915081905092915050565b5f8151905061186781611370565b92915050565b5f6020828403121561188257611881611363565b5b5f61188f84828501611859565b91505092915050565b5f819050919050565b6118b26118ad8261139a565b611898565b82525050565b5f8160601b9050919050565b5f6118ce826118b8565b9050919050565b5f6118df826118c4565b9050919050565b6118f76118f2826113ec565b6118d5565b82525050565b5f61190882896118a1565b60208201915061191882886118e6565b60148201915061192882876118e6565b60148201915061193882866117c0565b60208201915061194882856117c0565b60208201915061195882846118e6565b601482019150819050979650505050505050565b7f496e76616c6964207061796d656e7448617368000000000000000000000000005f82015250565b5f6119a0601383611719565b91506119ab8261196c565b602082019050919050565b5f6020820190508181035f8301526119cd81611994565b9050919050565b6119dd81611367565b82525050565b5f6020820190506119f65f8301846119d4565b92915050565b7f4d616b6572207061796d656e7420697320616c726561647920696e697469616c5f8201527f697a656400000000000000000000000000000000000000000000000000000000602082015250565b5f611a56602483611719565b9150611a61826119fc565b604082019050919050565b5f6020820190508181035f830152611a8381611a4a565b9050919050565b7f54616b6572206d757374206e6f74206265207a65726f206164647265737300005f82015250565b5f611abe601e83611719565b9150611ac982611a8a565b602082019050919050565b5f6020820190508181035f830152611aeb81611ab2565b9050919050565b7f4554482076616c7565206d7573742062652067726561746572207468616e207a5f8201527f65726f0000000000000000000000000000000000000000000000000000000000602082015250565b5f611b4c602383611719565b9150611b5782611af2565b604082019050919050565b5f6020820190508181035f830152611b7981611b40565b9050919050565b7f43757272656e742074696d657374616d70206469646e277420657863656564205f8201527f7061796d656e7420726566756e64206c6f636b2074696d650000000000000000602082015250565b5f611bda603883611719565b9150611be582611b80565b604082019050919050565b5f6020820190508181035f830152611c0781611bce565b9050919050565b7f416d6f756e74206d757374206e6f74206265207a65726f0000000000000000005f82015250565b5f611c42601783611719565b9150611c4d82611c0e565b602082019050919050565b5f6020820190508181035f830152611c6f81611c36565b9050919050565b611c7f816113ec565b82525050565b611c8e8161139a565b82525050565b5f604082019050611ca75f830185611c76565b611cb46020830184611c85565b9392505050565b5f606082019050611cce5f830186611c76565b611cdb6020830185611c76565b611ce86040830184611c85565b949350505050565b5f8115159050919050565b611d0481611cf0565b8114611d0e575f80fd5b50565b5f81519050611d1f81611cfb565b92915050565b5f60208284031215611d3a57611d39611363565b5b5f611d4784828501611d11565b91505092915050565b5f602082019050611d635f830184611c76565b9291505056fea26469706673582212200d7b67fe7b5e6829da1f56f14431b4ee61dc3a66fb17a79f0587fda0329d09ba64736f6c634300081a0033 \ No newline at end of file diff --git a/mm2src/mm2_test_helpers/contract_bytes/nft_maker_swap_v2_bytes b/mm2src/mm2_test_helpers/contract_bytes/nft_maker_swap_v2_bytes index 93922e9594..ea4cd8c817 100644 --- a/mm2src/mm2_test_helpers/contract_bytes/nft_maker_swap_v2_bytes +++ b/mm2src/mm2_test_helpers/contract_bytes/nft_maker_swap_v2_bytes @@ -1 +1 @@ -6080604052348015600e575f80fd5b506131fa8061001c5f395ff3fe608060405234801561000f575f80fd5b50600436106100cd575f3560e01c8063bc197c811161008a578063eb4ab61211610064578063eb4ab61214610209578063efccb9eb14610225578063f0e9fcd114610257578063f23a6e6114610273576100cd565b8063bc197c81146101a1578063c8d9009b146101d1578063c92cd12d146101ed576100cd565b806301ffc9a7146100d157806305ec158d146101015780630f235fce1461011d578063150b7a02146101395780636e6bf6d214610169578063b27e46fb14610185575b5f80fd5b6100eb60048036038101906100e69190612005565b6102a3565b6040516100f8919061204a565b60405180910390f35b61011b60048036038101906101169190612123565b610384565b005b610137600480360381019061013291906121c0565b610644565b005b610153600480360381019061014e91906122aa565b6108fe565b604051610160919061233d565b60405180910390f35b610183600480360381019061017e91906121c0565b610d4d565b005b61019f600480360381019061019a9190612123565b611008565b005b6101bb60048036038101906101b691906123ab565b6112c7565b6040516101c8919061233d565b60405180910390f35b6101eb60048036038101906101e691906121c0565b611303565b005b61020760048036038101906102029190612123565b61162c565b005b610223600480360381019061021e9190612482565b61195a565b005b61023f600480360381019061023a91906124e6565b6119d1565b60405161024e939291906125dc565b60405180910390f35b610271600480360381019061026c9190612611565b611a1d565b005b61028d60048036038101906102889190612661565b611a91565b60405161029a919061233d565b60405180910390f35b5f7f4e2312e0000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061036d57507f150b7a02000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061037d575061037c82611f25565b5b9050919050565b6001600381111561039857610397612569565b5b5f808981526020019081526020015f205f0160189054906101000a900460ff1660038111156103ca576103c9612569565b5b1461040a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161040190612777565b60405180910390fd5b5f6003873360028960405160200161042291906127b5565b60405160208183030381529060405260405161043e9190612821565b602060405180830381855afa158015610459573d5f803e3d5ffd5b5050506040513d601f19601f8201168201806040525081019061047c919061284b565b8888888860405160200161049697969594939291906128db565b6040516020818303038152906040526040516104b29190612821565b602060405180830381855afa1580156104cd573d5f803e3d5ffd5b5050506040515160601b90505f808981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614610557576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161054e906129a5565b60405180910390fd5b60035f808a81526020019081526020015f205f0160186101000a81548160ff0219169083600381111561058d5761058c612569565b5b02179055507fac509cdcc7ddb189f81fff6f4824f5c95076e64c3bdce542c50feaa6779afd73886040516105c191906129d2565b60405180910390a15f8490508073ffffffffffffffffffffffffffffffffffffffff1663f242432a303387876040518563ffffffff1660e01b815260040161060c9493929190612a3c565b5f604051808303815f87803b158015610623575f80fd5b505af1158015610635573d5f803e3d5ffd5b50505050505050505050505050565b6001600381111561065857610657612569565b5b5f808881526020019081526020015f205f0160189054906101000a900460ff16600381111561068a57610689612569565b5b146106ca576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c190612777565b60405180910390fd5b5f60038633878787876040516020016106e896959493929190612a92565b6040516020818303038152906040526040516107049190612821565b602060405180830381855afa15801561071f573d5f803e3d5ffd5b5050506040515160601b90505f808881526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff1916146107a9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107a0906129a5565b60405180910390fd5b5f808881526020019081526020015f205f0160149054906101000a900463ffffffff1663ffffffff16421015610814576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161080b90612b71565b60405180910390fd5b60035f808981526020019081526020015f205f0160186101000a81548160ff0219169083600381111561084a57610849612569565b5b02179055507f5dedc4f52b757d9112d09ca0b2f022927104d54e3f54da091587e8ad192190728760405161087e91906129d2565b60405180910390a15f8390508073ffffffffffffffffffffffffffffffffffffffff166342842e0e3033866040518463ffffffff1660e01b81526004016108c793929190612b8f565b5f604051808303815f87803b1580156108de575f80fd5b505af11580156108f0573d5f803e3d5ffd5b505050505050505050505050565b5f80838381019061090f9190612d17565b90505f600381111561092457610923612569565b5b5f80835f015181526020019081526020015f205f0160189054906101000a900460ff16600381111561095957610958612569565b5b14610999576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161099090612db2565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff16816020015173ffffffffffffffffffffffffffffffffffffffff1603610a0b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a0290612e1a565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff16816040015173ffffffffffffffffffffffffffffffffffffffff1603610a7d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a7490612e82565b60405180910390fd5b806040015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610aef576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ae690612f10565b60405180910390fd5b8573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1614610b5d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5490612f78565b60405180910390fd5b610b6a8160200151611f8e565b15610baa576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ba190612fe0565b60405180910390fd5b5f60038260200151888460600151856080015186604001518b604051602001610bd896959493929190612a92565b604051602081830303815290604052604051610bf49190612821565b602060405180830381855afa158015610c0f573d5f803e3d5ffd5b5050506040515160601b90506040518060600160405280826bffffffffffffffffffffffff191681526020018360a0015163ffffffff16815260200160016003811115610c5f57610c5e612569565b5b8152505f80845f015181526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548163ffffffff021916908363ffffffff1602179055506040820151815f0160186101000a81548160ff02191690836003811115610cf657610cf5612569565b5b02179055509050507ff1dc11bbb6d7542c4267ecf1d370ff4c7092518633ecae9939e8488f4e53d2ad825f0151604051610d3091906129d2565b60405180910390a163150b7a0260e01b9250505095945050505050565b60016003811115610d6157610d60612569565b5b5f808881526020019081526020015f205f0160189054906101000a900460ff166003811115610d9357610d92612569565b5b14610dd3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610dca90612777565b60405180910390fd5b5f60038633600288604051602001610deb91906127b5565b604051602081830303815290604052604051610e079190612821565b602060405180830381855afa158015610e22573d5f803e3d5ffd5b5050506040513d601f19601f82011682018060405250810190610e45919061284b565b878787604051602001610e5d96959493929190612a92565b604051602081830303815290604052604051610e799190612821565b602060405180830381855afa158015610e94573d5f803e3d5ffd5b5050506040515160601b90505f808881526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614610f1e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f15906129a5565b60405180910390fd5b60035f808981526020019081526020015f205f0160186101000a81548160ff02191690836003811115610f5457610f53612569565b5b02179055507fac509cdcc7ddb189f81fff6f4824f5c95076e64c3bdce542c50feaa6779afd7387604051610f8891906129d2565b60405180910390a15f8390508073ffffffffffffffffffffffffffffffffffffffff166342842e0e3033866040518463ffffffff1660e01b8152600401610fd193929190612b8f565b5f604051808303815f87803b158015610fe8575f80fd5b505af1158015610ffa573d5f803e3d5ffd5b505050505050505050505050565b6001600381111561101c5761101b612569565b5b5f808981526020019081526020015f205f0160189054906101000a900460ff16600381111561104e5761104d612569565b5b1461108e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161108590612777565b60405180910390fd5b5f6003873388888888886040516020016110ae97969594939291906128db565b6040516020818303038152906040526040516110ca9190612821565b602060405180830381855afa1580156110e5573d5f803e3d5ffd5b5050506040515160601b90505f808981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff19161461116f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611166906129a5565b60405180910390fd5b5f808981526020019081526020015f205f0160149054906101000a900463ffffffff1663ffffffff164210156111da576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111d190612b71565b60405180910390fd5b60035f808a81526020019081526020015f205f0160186101000a81548160ff021916908360038111156112105761120f612569565b5b02179055507f5dedc4f52b757d9112d09ca0b2f022927104d54e3f54da091587e8ad192190728860405161124491906129d2565b60405180910390a15f8490508073ffffffffffffffffffffffffffffffffffffffff1663f242432a303387876040518563ffffffff1660e01b815260040161128f9493929190612a3c565b5f604051808303815f87803b1580156112a6575f80fd5b505af11580156112b8573d5f803e3d5ffd5b50505050505050505050505050565b5f6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112fa90613048565b60405180910390fd5b6001600381111561131757611316612569565b5b5f808881526020019081526020015f205f0160189054906101000a900460ff16600381111561134957611348612569565b5b14611389576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161138090612777565b60405180910390fd5b3273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146113f7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113ee906130b0565b60405180910390fd5b5f600333878760028860405160200161141091906127b5565b60405160208183030381529060405260405161142c9190612821565b602060405180830381855afa158015611447573d5f803e3d5ffd5b5050506040513d601f19601f8201168201806040525081019061146a919061284b565b878760405160200161148196959493929190612a92565b60405160208183030381529060405260405161149d9190612821565b602060405180830381855afa1580156114b8573d5f803e3d5ffd5b5050506040515160601b90505f808881526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614611542576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611539906129a5565b60405180910390fd5b60025f808981526020019081526020015f205f0160186101000a81548160ff0219169083600381111561157857611577612569565b5b02179055507fad62ed075fe8969df63026f45152d6e996a0697a736a8de92ee85ae9c9958cf0876040516115ac91906129d2565b60405180910390a15f8390508073ffffffffffffffffffffffffffffffffffffffff166342842e0e3033866040518463ffffffff1660e01b81526004016115f593929190612b8f565b5f604051808303815f87803b15801561160c575f80fd5b505af115801561161e573d5f803e3d5ffd5b505050505050505050505050565b600160038111156116405761163f612569565b5b5f808981526020019081526020015f205f0160189054906101000a900460ff16600381111561167257611671612569565b5b146116b2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116a990612777565b60405180910390fd5b3273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611720576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611717906130b0565b60405180910390fd5b5f600333888860028960405160200161173991906127b5565b6040516020818303038152906040526040516117559190612821565b602060405180830381855afa158015611770573d5f803e3d5ffd5b5050506040513d601f19601f82011682018060405250810190611793919061284b565b8888886040516020016117ac97969594939291906128db565b6040516020818303038152906040526040516117c89190612821565b602060405180830381855afa1580156117e3573d5f803e3d5ffd5b5050506040515160601b90505f808981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff19161461186d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611864906129a5565b60405180910390fd5b60025f808a81526020019081526020015f205f0160186101000a81548160ff021916908360038111156118a3576118a2612569565b5b02179055507fad62ed075fe8969df63026f45152d6e996a0697a736a8de92ee85ae9c9958cf0886040516118d791906129d2565b60405180910390a15f8490508073ffffffffffffffffffffffffffffffffffffffff1663f242432a303387876040518563ffffffff1660e01b81526004016119229493929190612a3c565b5f604051808303815f87803b158015611939575f80fd5b505af115801561194b573d5f803e3d5ffd5b50505050505050505050505050565b5f8490508073ffffffffffffffffffffffffffffffffffffffff1663f242432a308487876040518563ffffffff1660e01b815260040161199d9493929190612a3c565b5f604051808303815f87803b1580156119b4575f80fd5b505af11580156119c6573d5f803e3d5ffd5b505050505050505050565b5f602052805f5260405f205f91509050805f015f9054906101000a900460601b90805f0160149054906101000a900463ffffffff1690805f0160189054906101000a900460ff16905083565b5f8390508073ffffffffffffffffffffffffffffffffffffffff166342842e0e3084866040518463ffffffff1660e01b8152600401611a5e93929190612b8f565b5f604051808303815f87803b158015611a75575f80fd5b505af1158015611a87573d5f803e3d5ffd5b5050505050505050565b5f808383810190611aa29190612d17565b90505f6003811115611ab757611ab6612569565b5b5f80835f015181526020019081526020015f205f0160189054906101000a900460ff166003811115611aec57611aeb612569565b5b14611b2c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b239061313e565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff16816020015173ffffffffffffffffffffffffffffffffffffffff1603611b9e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b9590612e1a565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff16816040015173ffffffffffffffffffffffffffffffffffffffff1603611c10576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c0790612e82565b60405180910390fd5b806040015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611c82576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c7990612f10565b60405180910390fd5b8673ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614611cf0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611ce790612f78565b60405180910390fd5b5f8511611d32576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611d29906131a6565b60405180910390fd5b611d3f8160200151611f8e565b15611d7f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611d7690612fe0565b60405180910390fd5b5f60038260200151898460600151856080015186604001518c8c604051602001611daf97969594939291906128db565b604051602081830303815290604052604051611dcb9190612821565b602060405180830381855afa158015611de6573d5f803e3d5ffd5b5050506040515160601b90506040518060600160405280826bffffffffffffffffffffffff191681526020018360a0015163ffffffff16815260200160016003811115611e3657611e35612569565b5b8152505f80845f015181526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548163ffffffff021916908363ffffffff1602179055506040820151815f0160186101000a81548160ff02191690836003811115611ecd57611ecc612569565b5b02179055509050507ff1dc11bbb6d7542c4267ecf1d370ff4c7092518633ecae9939e8488f4e53d2ad825f0151604051611f0791906129d2565b60405180910390a163f23a6e6160e01b925050509695505050505050565b5f7f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b5f80823b90505f8111915050919050565b5f604051905090565b5f80fd5b5f80fd5b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b611fe481611fb0565b8114611fee575f80fd5b50565b5f81359050611fff81611fdb565b92915050565b5f6020828403121561201a57612019611fa8565b5b5f61202784828501611ff1565b91505092915050565b5f8115159050919050565b61204481612030565b82525050565b5f60208201905061205d5f83018461203b565b92915050565b5f819050919050565b61207581612063565b811461207f575f80fd5b50565b5f813590506120908161206c565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6120bf82612096565b9050919050565b6120cf816120b5565b81146120d9575f80fd5b50565b5f813590506120ea816120c6565b92915050565b5f819050919050565b612102816120f0565b811461210c575f80fd5b50565b5f8135905061211d816120f9565b92915050565b5f805f805f805f60e0888a03121561213e5761213d611fa8565b5b5f61214b8a828b01612082565b975050602061215c8a828b016120dc565b965050604061216d8a828b01612082565b955050606061217e8a828b01612082565b945050608061218f8a828b016120dc565b93505060a06121a08a828b0161210f565b92505060c06121b18a828b0161210f565b91505092959891949750929550565b5f805f805f8060c087890312156121da576121d9611fa8565b5b5f6121e789828a01612082565b96505060206121f889828a016120dc565b955050604061220989828a01612082565b945050606061221a89828a01612082565b935050608061222b89828a016120dc565b92505060a061223c89828a0161210f565b9150509295509295509295565b5f80fd5b5f80fd5b5f80fd5b5f8083601f84011261226a57612269612249565b5b8235905067ffffffffffffffff8111156122875761228661224d565b5b6020830191508360018202830111156122a3576122a2612251565b5b9250929050565b5f805f805f608086880312156122c3576122c2611fa8565b5b5f6122d0888289016120dc565b95505060206122e1888289016120dc565b94505060406122f28882890161210f565b935050606086013567ffffffffffffffff81111561231357612312611fac565b5b61231f88828901612255565b92509250509295509295909350565b61233781611fb0565b82525050565b5f6020820190506123505f83018461232e565b92915050565b5f8083601f84011261236b5761236a612249565b5b8235905067ffffffffffffffff8111156123885761238761224d565b5b6020830191508360208202830111156123a4576123a3612251565b5b9250929050565b5f805f805f805f8060a0898b0312156123c7576123c6611fa8565b5b5f6123d48b828c016120dc565b98505060206123e58b828c016120dc565b975050604089013567ffffffffffffffff81111561240657612405611fac565b5b6124128b828c01612356565b9650965050606089013567ffffffffffffffff81111561243557612434611fac565b5b6124418b828c01612356565b9450945050608089013567ffffffffffffffff81111561246457612463611fac565b5b6124708b828c01612255565b92509250509295985092959890939650565b5f805f806080858703121561249a57612499611fa8565b5b5f6124a7878288016120dc565b94505060206124b88782880161210f565b93505060406124c98782880161210f565b92505060606124da878288016120dc565b91505092959194509250565b5f602082840312156124fb576124fa611fa8565b5b5f61250884828501612082565b91505092915050565b5f7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000082169050919050565b61254581612511565b82525050565b5f63ffffffff82169050919050565b6125638161254b565b82525050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b600481106125a7576125a6612569565b5b50565b5f8190506125b782612596565b919050565b5f6125c6826125aa565b9050919050565b6125d6816125bc565b82525050565b5f6060820190506125ef5f83018661253c565b6125fc602083018561255a565b61260960408301846125cd565b949350505050565b5f805f6060848603121561262857612627611fa8565b5b5f612635868287016120dc565b93505060206126468682870161210f565b9250506040612657868287016120dc565b9150509250925092565b5f805f805f8060a0878903121561267b5761267a611fa8565b5b5f61268889828a016120dc565b965050602061269989828a016120dc565b95505060406126aa89828a0161210f565b94505060606126bb89828a0161210f565b935050608087013567ffffffffffffffff8111156126dc576126db611fac565b5b6126e889828a01612255565b92509250509295509295509295565b5f82825260208201905092915050565b7f496e76616c6964207061796d656e742073746174652e204d75737420626520505f8201527f61796d656e7453656e7400000000000000000000000000000000000000000000602082015250565b5f612761602a836126f7565b915061276c82612707565b604082019050919050565b5f6020820190508181035f83015261278e81612755565b9050919050565b5f819050919050565b6127af6127aa82612063565b612795565b82525050565b5f6127c0828461279e565b60208201915081905092915050565b5f81519050919050565b5f81905092915050565b8281835e5f83830152505050565b5f6127fb826127cf565b61280581856127d9565b93506128158185602086016127e3565b80840191505092915050565b5f61282c82846127f1565b915081905092915050565b5f815190506128458161206c565b92915050565b5f602082840312156128605761285f611fa8565b5b5f61286d84828501612837565b91505092915050565b5f8160601b9050919050565b5f61288c82612876565b9050919050565b5f61289d82612882565b9050919050565b6128b56128b0826120b5565b612893565b82525050565b5f819050919050565b6128d56128d0826120f0565b6128bb565b82525050565b5f6128e6828a6128a4565b6014820191506128f682896128a4565b601482019150612906828861279e565b602082019150612916828761279e565b60208201915061292682866128a4565b60148201915061293682856128c4565b60208201915061294682846128c4565b60208201915081905098975050505050505050565b7f496e76616c6964207061796d656e7448617368000000000000000000000000005f82015250565b5f61298f6013836126f7565b915061299a8261295b565b602082019050919050565b5f6020820190508181035f8301526129bc81612983565b9050919050565b6129cc81612063565b82525050565b5f6020820190506129e55f8301846129c3565b92915050565b6129f4816120b5565b82525050565b612a03816120f0565b82525050565b5f82825260208201905092915050565b50565b5f612a275f83612a09565b9150612a3282612a19565b5f82019050919050565b5f60a082019050612a4f5f8301876129eb565b612a5c60208301866129eb565b612a6960408301856129fa565b612a7660608301846129fa565b8181036080830152612a8781612a1c565b905095945050505050565b5f612a9d82896128a4565b601482019150612aad82886128a4565b601482019150612abd828761279e565b602082019150612acd828661279e565b602082019150612add82856128a4565b601482019150612aed82846128c4565b602082019150819050979650505050505050565b7f43757272656e742074696d657374616d70206469646e277420657863656564205f8201527f7061796d656e7420726566756e64206c6f636b2074696d650000000000000000602082015250565b5f612b5b6038836126f7565b9150612b6682612b01565b604082019050919050565b5f6020820190508181035f830152612b8881612b4f565b9050919050565b5f606082019050612ba25f8301866129eb565b612baf60208301856129eb565b612bbc60408301846129fa565b949350505050565b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b612c0e82612bc8565b810181811067ffffffffffffffff82111715612c2d57612c2c612bd8565b5b80604052505050565b5f612c3f611f9f565b9050612c4b8282612c05565b919050565b612c598161254b565b8114612c63575f80fd5b50565b5f81359050612c7481612c50565b92915050565b5f60c08284031215612c8f57612c8e612bc4565b5b612c9960c0612c36565b90505f612ca884828501612082565b5f830152506020612cbb848285016120dc565b6020830152506040612ccf848285016120dc565b6040830152506060612ce384828501612082565b6060830152506080612cf784828501612082565b60808301525060a0612d0b84828501612c66565b60a08301525092915050565b5f60c08284031215612d2c57612d2b611fa8565b5b5f612d3984828501612c7a565b91505092915050565b7f4d616b657220455243373231207061796d656e74206d75737420626520556e695f8201527f6e697469616c697a656400000000000000000000000000000000000000000000602082015250565b5f612d9c602a836126f7565b9150612da782612d42565b604082019050919050565b5f6020820190508181035f830152612dc981612d90565b9050919050565b7f54616b6572206d757374206e6f74206265207a65726f206164647265737300005f82015250565b5f612e04601e836126f7565b9150612e0f82612dd0565b602082019050919050565b5f6020820190508181035f830152612e3181612df8565b9050919050565b7f546f6b656e206d757374206e6f74206265207a65726f206164647265737300005f82015250565b5f612e6c601e836126f7565b9150612e7782612e38565b602082019050919050565b5f6020820190508181035f830152612e9981612e60565b9050919050565b7f546f6b656e206164647265737320646f6573206e6f74206d617463682073656e5f8201527f6465720000000000000000000000000000000000000000000000000000000000602082015250565b5f612efa6023836126f7565b9150612f0582612ea0565b604082019050919050565b5f6020820190508181035f830152612f2781612eee565b9050919050565b7f4f70657261746f72206d757374206265207468652073656e64657200000000005f82015250565b5f612f62601b836126f7565b9150612f6d82612f2e565b602082019050919050565b5f6020820190508181035f830152612f8f81612f56565b9050919050565b7f54616b65722063616e6e6f74206265206120636f6e74726163740000000000005f82015250565b5f612fca601a836126f7565b9150612fd582612f96565b602082019050919050565b5f6020820190508181035f830152612ff781612fbe565b9050919050565b7f4261746368207472616e7366657273206e6f7420737570706f727465640000005f82015250565b5f613032601d836126f7565b915061303d82612ffe565b602082019050919050565b5f6020820190508181035f83015261305f81613026565b9050919050565b7f43616c6c6572206d75737420626520616e20454f4100000000000000000000005f82015250565b5f61309a6015836126f7565b91506130a582613066565b602082019050919050565b5f6020820190508181035f8301526130c78161308e565b9050919050565b7f4d616b65722045524331313535207061796d656e74206d75737420626520556e5f8201527f696e697469616c697a6564000000000000000000000000000000000000000000602082015250565b5f613128602b836126f7565b9150613133826130ce565b604082019050919050565b5f6020820190508181035f8301526131558161311c565b9050919050565b7f56616c7565206d7573742062652067726561746572207468616e2030000000005f82015250565b5f613190601c836126f7565b915061319b8261315c565b602082019050919050565b5f6020820190508181035f8301526131bd81613184565b905091905056fea2646970667358221220c7235610f4c08b95d1e01bd31ff0b8b671c0f18c33212760a2ce3b09e6ad77e964736f6c63430008190033 \ No newline at end of file +6080604052348015600e575f80fd5b50612ffd8061001c5f395ff3fe608060405234801561000f575f80fd5b50600436106100a7575f3560e01c8063b27e46fb1161006f578063b27e46fb1461015f578063bc197c811461017b578063c8d9009b146101ab578063c92cd12d146101c7578063efccb9eb146101e3578063f23a6e6114610215576100a7565b806301ffc9a7146100ab57806305ec158d146100db5780630f235fce146100f7578063150b7a02146101135780636e6bf6d214610143575b5f80fd5b6100c560048036038101906100c09190611ebc565b610245565b6040516100d29190611f01565b60405180910390f35b6100f560048036038101906100f09190611fda565b610326565b005b610111600480360381019061010c9190612077565b6105e6565b005b61012d60048036038101906101289190612161565b6108a0565b60405161013a91906121f4565b60405180910390f35b61015d60048036038101906101589190612077565b610cef565b005b61017960048036038101906101749190611fda565b610faa565b005b61019560048036038101906101909190612262565b611269565b6040516101a291906121f4565b60405180910390f35b6101c560048036038101906101c09190612077565b6112a5565b005b6101e160048036038101906101dc9190611fda565b6115ce565b005b6101fd60048036038101906101f89190612339565b6118fc565b60405161020c9392919061242f565b60405180910390f35b61022f600480360381019061022a9190612464565b611948565b60405161023c91906121f4565b60405180910390f35b5f7f4e2312e0000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061030f57507f150b7a02000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061031f575061031e82611ddc565b5b9050919050565b6001600381111561033a576103396123bc565b5b5f808981526020019081526020015f205f0160189054906101000a900460ff16600381111561036c5761036b6123bc565b5b146103ac576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103a39061257a565b60405180910390fd5b5f600387336002896040516020016103c491906125b8565b6040516020818303038152906040526040516103e09190612624565b602060405180830381855afa1580156103fb573d5f803e3d5ffd5b5050506040513d601f19601f8201168201806040525081019061041e919061264e565b8888888860405160200161043897969594939291906126de565b6040516020818303038152906040526040516104549190612624565b602060405180830381855afa15801561046f573d5f803e3d5ffd5b5050506040515160601b90505f808981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff1916146104f9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104f0906127a8565b60405180910390fd5b60035f808a81526020019081526020015f205f0160186101000a81548160ff0219169083600381111561052f5761052e6123bc565b5b02179055507fac509cdcc7ddb189f81fff6f4824f5c95076e64c3bdce542c50feaa6779afd738860405161056391906127d5565b60405180910390a15f8490508073ffffffffffffffffffffffffffffffffffffffff1663f242432a303387876040518563ffffffff1660e01b81526004016105ae949392919061283f565b5f604051808303815f87803b1580156105c5575f80fd5b505af11580156105d7573d5f803e3d5ffd5b50505050505050505050505050565b600160038111156105fa576105f96123bc565b5b5f808881526020019081526020015f205f0160189054906101000a900460ff16600381111561062c5761062b6123bc565b5b1461066c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106639061257a565b60405180910390fd5b5f600386338787878760405160200161068a96959493929190612895565b6040516020818303038152906040526040516106a69190612624565b602060405180830381855afa1580156106c1573d5f803e3d5ffd5b5050506040515160601b90505f808881526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff19161461074b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610742906127a8565b60405180910390fd5b5f808881526020019081526020015f205f0160149054906101000a900463ffffffff1663ffffffff164210156107b6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107ad90612974565b60405180910390fd5b60035f808981526020019081526020015f205f0160186101000a81548160ff021916908360038111156107ec576107eb6123bc565b5b02179055507f5dedc4f52b757d9112d09ca0b2f022927104d54e3f54da091587e8ad192190728760405161082091906127d5565b60405180910390a15f8390508073ffffffffffffffffffffffffffffffffffffffff166342842e0e3033866040518463ffffffff1660e01b815260040161086993929190612992565b5f604051808303815f87803b158015610880575f80fd5b505af1158015610892573d5f803e3d5ffd5b505050505050505050505050565b5f8083838101906108b19190612b1a565b90505f60038111156108c6576108c56123bc565b5b5f80835f015181526020019081526020015f205f0160189054906101000a900460ff1660038111156108fb576108fa6123bc565b5b1461093b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161093290612bb5565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff16816020015173ffffffffffffffffffffffffffffffffffffffff16036109ad576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109a490612c1d565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff16816040015173ffffffffffffffffffffffffffffffffffffffff1603610a1f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a1690612c85565b60405180910390fd5b806040015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610a91576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a8890612d13565b60405180910390fd5b8573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1614610aff576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610af690612d7b565b60405180910390fd5b610b0c8160200151611e45565b15610b4c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b4390612de3565b60405180910390fd5b5f60038260200151888460600151856080015186604001518b604051602001610b7a96959493929190612895565b604051602081830303815290604052604051610b969190612624565b602060405180830381855afa158015610bb1573d5f803e3d5ffd5b5050506040515160601b90506040518060600160405280826bffffffffffffffffffffffff191681526020018360a0015163ffffffff16815260200160016003811115610c0157610c006123bc565b5b8152505f80845f015181526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548163ffffffff021916908363ffffffff1602179055506040820151815f0160186101000a81548160ff02191690836003811115610c9857610c976123bc565b5b02179055509050507ff1dc11bbb6d7542c4267ecf1d370ff4c7092518633ecae9939e8488f4e53d2ad825f0151604051610cd291906127d5565b60405180910390a163150b7a0260e01b9250505095945050505050565b60016003811115610d0357610d026123bc565b5b5f808881526020019081526020015f205f0160189054906101000a900460ff166003811115610d3557610d346123bc565b5b14610d75576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d6c9061257a565b60405180910390fd5b5f60038633600288604051602001610d8d91906125b8565b604051602081830303815290604052604051610da99190612624565b602060405180830381855afa158015610dc4573d5f803e3d5ffd5b5050506040513d601f19601f82011682018060405250810190610de7919061264e565b878787604051602001610dff96959493929190612895565b604051602081830303815290604052604051610e1b9190612624565b602060405180830381855afa158015610e36573d5f803e3d5ffd5b5050506040515160601b90505f808881526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614610ec0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610eb7906127a8565b60405180910390fd5b60035f808981526020019081526020015f205f0160186101000a81548160ff02191690836003811115610ef657610ef56123bc565b5b02179055507fac509cdcc7ddb189f81fff6f4824f5c95076e64c3bdce542c50feaa6779afd7387604051610f2a91906127d5565b60405180910390a15f8390508073ffffffffffffffffffffffffffffffffffffffff166342842e0e3033866040518463ffffffff1660e01b8152600401610f7393929190612992565b5f604051808303815f87803b158015610f8a575f80fd5b505af1158015610f9c573d5f803e3d5ffd5b505050505050505050505050565b60016003811115610fbe57610fbd6123bc565b5b5f808981526020019081526020015f205f0160189054906101000a900460ff166003811115610ff057610fef6123bc565b5b14611030576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110279061257a565b60405180910390fd5b5f60038733888888888860405160200161105097969594939291906126de565b60405160208183030381529060405260405161106c9190612624565b602060405180830381855afa158015611087573d5f803e3d5ffd5b5050506040515160601b90505f808981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614611111576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611108906127a8565b60405180910390fd5b5f808981526020019081526020015f205f0160149054906101000a900463ffffffff1663ffffffff1642101561117c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161117390612974565b60405180910390fd5b60035f808a81526020019081526020015f205f0160186101000a81548160ff021916908360038111156111b2576111b16123bc565b5b02179055507f5dedc4f52b757d9112d09ca0b2f022927104d54e3f54da091587e8ad19219072886040516111e691906127d5565b60405180910390a15f8490508073ffffffffffffffffffffffffffffffffffffffff1663f242432a303387876040518563ffffffff1660e01b8152600401611231949392919061283f565b5f604051808303815f87803b158015611248575f80fd5b505af115801561125a573d5f803e3d5ffd5b50505050505050505050505050565b5f6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161129c90612e4b565b60405180910390fd5b600160038111156112b9576112b86123bc565b5b5f808881526020019081526020015f205f0160189054906101000a900460ff1660038111156112eb576112ea6123bc565b5b1461132b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113229061257a565b60405180910390fd5b3273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611399576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161139090612eb3565b60405180910390fd5b5f60033387876002886040516020016113b291906125b8565b6040516020818303038152906040526040516113ce9190612624565b602060405180830381855afa1580156113e9573d5f803e3d5ffd5b5050506040513d601f19601f8201168201806040525081019061140c919061264e565b878760405160200161142396959493929190612895565b60405160208183030381529060405260405161143f9190612624565b602060405180830381855afa15801561145a573d5f803e3d5ffd5b5050506040515160601b90505f808881526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff1916146114e4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114db906127a8565b60405180910390fd5b60025f808981526020019081526020015f205f0160186101000a81548160ff0219169083600381111561151a576115196123bc565b5b02179055507fad62ed075fe8969df63026f45152d6e996a0697a736a8de92ee85ae9c9958cf08760405161154e91906127d5565b60405180910390a15f8390508073ffffffffffffffffffffffffffffffffffffffff166342842e0e3033866040518463ffffffff1660e01b815260040161159793929190612992565b5f604051808303815f87803b1580156115ae575f80fd5b505af11580156115c0573d5f803e3d5ffd5b505050505050505050505050565b600160038111156115e2576115e16123bc565b5b5f808981526020019081526020015f205f0160189054906101000a900460ff166003811115611614576116136123bc565b5b14611654576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161164b9061257a565b60405180910390fd5b3273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146116c2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116b990612eb3565b60405180910390fd5b5f60033388886002896040516020016116db91906125b8565b6040516020818303038152906040526040516116f79190612624565b602060405180830381855afa158015611712573d5f803e3d5ffd5b5050506040513d601f19601f82011682018060405250810190611735919061264e565b88888860405160200161174e97969594939291906126de565b60405160208183030381529060405260405161176a9190612624565b602060405180830381855afa158015611785573d5f803e3d5ffd5b5050506040515160601b90505f808981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff19161461180f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611806906127a8565b60405180910390fd5b60025f808a81526020019081526020015f205f0160186101000a81548160ff02191690836003811115611845576118446123bc565b5b02179055507fad62ed075fe8969df63026f45152d6e996a0697a736a8de92ee85ae9c9958cf08860405161187991906127d5565b60405180910390a15f8490508073ffffffffffffffffffffffffffffffffffffffff1663f242432a303387876040518563ffffffff1660e01b81526004016118c4949392919061283f565b5f604051808303815f87803b1580156118db575f80fd5b505af11580156118ed573d5f803e3d5ffd5b50505050505050505050505050565b5f602052805f5260405f205f91509050805f015f9054906101000a900460601b90805f0160149054906101000a900463ffffffff1690805f0160189054906101000a900460ff16905083565b5f8083838101906119599190612b1a565b90505f600381111561196e5761196d6123bc565b5b5f80835f015181526020019081526020015f205f0160189054906101000a900460ff1660038111156119a3576119a26123bc565b5b146119e3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119da90612f41565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff16816020015173ffffffffffffffffffffffffffffffffffffffff1603611a55576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611a4c90612c1d565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff16816040015173ffffffffffffffffffffffffffffffffffffffff1603611ac7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611abe90612c85565b60405180910390fd5b806040015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611b39576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b3090612d13565b60405180910390fd5b8673ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614611ba7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b9e90612d7b565b60405180910390fd5b5f8511611be9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611be090612fa9565b60405180910390fd5b611bf68160200151611e45565b15611c36576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c2d90612de3565b60405180910390fd5b5f60038260200151898460600151856080015186604001518c8c604051602001611c6697969594939291906126de565b604051602081830303815290604052604051611c829190612624565b602060405180830381855afa158015611c9d573d5f803e3d5ffd5b5050506040515160601b90506040518060600160405280826bffffffffffffffffffffffff191681526020018360a0015163ffffffff16815260200160016003811115611ced57611cec6123bc565b5b8152505f80845f015181526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548163ffffffff021916908363ffffffff1602179055506040820151815f0160186101000a81548160ff02191690836003811115611d8457611d836123bc565b5b02179055509050507ff1dc11bbb6d7542c4267ecf1d370ff4c7092518633ecae9939e8488f4e53d2ad825f0151604051611dbe91906127d5565b60405180910390a163f23a6e6160e01b925050509695505050505050565b5f7f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b5f80823b90505f8111915050919050565b5f604051905090565b5f80fd5b5f80fd5b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b611e9b81611e67565b8114611ea5575f80fd5b50565b5f81359050611eb681611e92565b92915050565b5f60208284031215611ed157611ed0611e5f565b5b5f611ede84828501611ea8565b91505092915050565b5f8115159050919050565b611efb81611ee7565b82525050565b5f602082019050611f145f830184611ef2565b92915050565b5f819050919050565b611f2c81611f1a565b8114611f36575f80fd5b50565b5f81359050611f4781611f23565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f611f7682611f4d565b9050919050565b611f8681611f6c565b8114611f90575f80fd5b50565b5f81359050611fa181611f7d565b92915050565b5f819050919050565b611fb981611fa7565b8114611fc3575f80fd5b50565b5f81359050611fd481611fb0565b92915050565b5f805f805f805f60e0888a031215611ff557611ff4611e5f565b5b5f6120028a828b01611f39565b97505060206120138a828b01611f93565b96505060406120248a828b01611f39565b95505060606120358a828b01611f39565b94505060806120468a828b01611f93565b93505060a06120578a828b01611fc6565b92505060c06120688a828b01611fc6565b91505092959891949750929550565b5f805f805f8060c0878903121561209157612090611e5f565b5b5f61209e89828a01611f39565b96505060206120af89828a01611f93565b95505060406120c089828a01611f39565b94505060606120d189828a01611f39565b93505060806120e289828a01611f93565b92505060a06120f389828a01611fc6565b9150509295509295509295565b5f80fd5b5f80fd5b5f80fd5b5f8083601f84011261212157612120612100565b5b8235905067ffffffffffffffff81111561213e5761213d612104565b5b60208301915083600182028301111561215a57612159612108565b5b9250929050565b5f805f805f6080868803121561217a57612179611e5f565b5b5f61218788828901611f93565b955050602061219888828901611f93565b94505060406121a988828901611fc6565b935050606086013567ffffffffffffffff8111156121ca576121c9611e63565b5b6121d68882890161210c565b92509250509295509295909350565b6121ee81611e67565b82525050565b5f6020820190506122075f8301846121e5565b92915050565b5f8083601f84011261222257612221612100565b5b8235905067ffffffffffffffff81111561223f5761223e612104565b5b60208301915083602082028301111561225b5761225a612108565b5b9250929050565b5f805f805f805f8060a0898b03121561227e5761227d611e5f565b5b5f61228b8b828c01611f93565b985050602061229c8b828c01611f93565b975050604089013567ffffffffffffffff8111156122bd576122bc611e63565b5b6122c98b828c0161220d565b9650965050606089013567ffffffffffffffff8111156122ec576122eb611e63565b5b6122f88b828c0161220d565b9450945050608089013567ffffffffffffffff81111561231b5761231a611e63565b5b6123278b828c0161210c565b92509250509295985092959890939650565b5f6020828403121561234e5761234d611e5f565b5b5f61235b84828501611f39565b91505092915050565b5f7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000082169050919050565b61239881612364565b82525050565b5f63ffffffff82169050919050565b6123b68161239e565b82525050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b600481106123fa576123f96123bc565b5b50565b5f81905061240a826123e9565b919050565b5f612419826123fd565b9050919050565b6124298161240f565b82525050565b5f6060820190506124425f83018661238f565b61244f60208301856123ad565b61245c6040830184612420565b949350505050565b5f805f805f8060a0878903121561247e5761247d611e5f565b5b5f61248b89828a01611f93565b965050602061249c89828a01611f93565b95505060406124ad89828a01611fc6565b94505060606124be89828a01611fc6565b935050608087013567ffffffffffffffff8111156124df576124de611e63565b5b6124eb89828a0161210c565b92509250509295509295509295565b5f82825260208201905092915050565b7f496e76616c6964207061796d656e742073746174652e204d75737420626520505f8201527f61796d656e7453656e7400000000000000000000000000000000000000000000602082015250565b5f612564602a836124fa565b915061256f8261250a565b604082019050919050565b5f6020820190508181035f83015261259181612558565b9050919050565b5f819050919050565b6125b26125ad82611f1a565b612598565b82525050565b5f6125c382846125a1565b60208201915081905092915050565b5f81519050919050565b5f81905092915050565b8281835e5f83830152505050565b5f6125fe826125d2565b61260881856125dc565b93506126188185602086016125e6565b80840191505092915050565b5f61262f82846125f4565b915081905092915050565b5f8151905061264881611f23565b92915050565b5f6020828403121561266357612662611e5f565b5b5f6126708482850161263a565b91505092915050565b5f8160601b9050919050565b5f61268f82612679565b9050919050565b5f6126a082612685565b9050919050565b6126b86126b382611f6c565b612696565b82525050565b5f819050919050565b6126d86126d382611fa7565b6126be565b82525050565b5f6126e9828a6126a7565b6014820191506126f982896126a7565b60148201915061270982886125a1565b60208201915061271982876125a1565b60208201915061272982866126a7565b60148201915061273982856126c7565b60208201915061274982846126c7565b60208201915081905098975050505050505050565b7f496e76616c6964207061796d656e7448617368000000000000000000000000005f82015250565b5f6127926013836124fa565b915061279d8261275e565b602082019050919050565b5f6020820190508181035f8301526127bf81612786565b9050919050565b6127cf81611f1a565b82525050565b5f6020820190506127e85f8301846127c6565b92915050565b6127f781611f6c565b82525050565b61280681611fa7565b82525050565b5f82825260208201905092915050565b50565b5f61282a5f8361280c565b91506128358261281c565b5f82019050919050565b5f60a0820190506128525f8301876127ee565b61285f60208301866127ee565b61286c60408301856127fd565b61287960608301846127fd565b818103608083015261288a8161281f565b905095945050505050565b5f6128a082896126a7565b6014820191506128b082886126a7565b6014820191506128c082876125a1565b6020820191506128d082866125a1565b6020820191506128e082856126a7565b6014820191506128f082846126c7565b602082019150819050979650505050505050565b7f43757272656e742074696d657374616d70206469646e277420657863656564205f8201527f7061796d656e7420726566756e64206c6f636b2074696d650000000000000000602082015250565b5f61295e6038836124fa565b915061296982612904565b604082019050919050565b5f6020820190508181035f83015261298b81612952565b9050919050565b5f6060820190506129a55f8301866127ee565b6129b260208301856127ee565b6129bf60408301846127fd565b949350505050565b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b612a11826129cb565b810181811067ffffffffffffffff82111715612a3057612a2f6129db565b5b80604052505050565b5f612a42611e56565b9050612a4e8282612a08565b919050565b612a5c8161239e565b8114612a66575f80fd5b50565b5f81359050612a7781612a53565b92915050565b5f60c08284031215612a9257612a916129c7565b5b612a9c60c0612a39565b90505f612aab84828501611f39565b5f830152506020612abe84828501611f93565b6020830152506040612ad284828501611f93565b6040830152506060612ae684828501611f39565b6060830152506080612afa84828501611f39565b60808301525060a0612b0e84828501612a69565b60a08301525092915050565b5f60c08284031215612b2f57612b2e611e5f565b5b5f612b3c84828501612a7d565b91505092915050565b7f4d616b657220455243373231207061796d656e74206d75737420626520556e695f8201527f6e697469616c697a656400000000000000000000000000000000000000000000602082015250565b5f612b9f602a836124fa565b9150612baa82612b45565b604082019050919050565b5f6020820190508181035f830152612bcc81612b93565b9050919050565b7f54616b6572206d757374206e6f74206265207a65726f206164647265737300005f82015250565b5f612c07601e836124fa565b9150612c1282612bd3565b602082019050919050565b5f6020820190508181035f830152612c3481612bfb565b9050919050565b7f546f6b656e206d757374206e6f74206265207a65726f206164647265737300005f82015250565b5f612c6f601e836124fa565b9150612c7a82612c3b565b602082019050919050565b5f6020820190508181035f830152612c9c81612c63565b9050919050565b7f546f6b656e206164647265737320646f6573206e6f74206d617463682073656e5f8201527f6465720000000000000000000000000000000000000000000000000000000000602082015250565b5f612cfd6023836124fa565b9150612d0882612ca3565b604082019050919050565b5f6020820190508181035f830152612d2a81612cf1565b9050919050565b7f4f70657261746f72206d757374206265207468652073656e64657200000000005f82015250565b5f612d65601b836124fa565b9150612d7082612d31565b602082019050919050565b5f6020820190508181035f830152612d9281612d59565b9050919050565b7f54616b65722063616e6e6f74206265206120636f6e74726163740000000000005f82015250565b5f612dcd601a836124fa565b9150612dd882612d99565b602082019050919050565b5f6020820190508181035f830152612dfa81612dc1565b9050919050565b7f4261746368207472616e7366657273206e6f7420737570706f727465640000005f82015250565b5f612e35601d836124fa565b9150612e4082612e01565b602082019050919050565b5f6020820190508181035f830152612e6281612e29565b9050919050565b7f43616c6c6572206d75737420626520616e20454f4100000000000000000000005f82015250565b5f612e9d6015836124fa565b9150612ea882612e69565b602082019050919050565b5f6020820190508181035f830152612eca81612e91565b9050919050565b7f4d616b65722045524331313535207061796d656e74206d75737420626520556e5f8201527f696e697469616c697a6564000000000000000000000000000000000000000000602082015250565b5f612f2b602b836124fa565b9150612f3682612ed1565b604082019050919050565b5f6020820190508181035f830152612f5881612f1f565b9050919050565b7f56616c7565206d7573742062652067726561746572207468616e2030000000005f82015250565b5f612f93601c836124fa565b9150612f9e82612f5f565b602082019050919050565b5f6020820190508181035f830152612fc081612f87565b905091905056fea26469706673582212208adfd9bc3010e8e9bf1d503f3e439ea5632ee575d696af59d80e0a2268d48d7e64736f6c634300081a0033 \ No newline at end of file diff --git a/mm2src/mm2_test_helpers/contract_bytes/nft_swap_contract_bytes b/mm2src/mm2_test_helpers/contract_bytes/nft_swap_contract_bytes deleted file mode 100644 index 44c1930271..0000000000 --- a/mm2src/mm2_test_helpers/contract_bytes/nft_swap_contract_bytes +++ /dev/null @@ -1 +0,0 @@ -60a060405234801562000010575f80fd5b50604051620055a2380380620055a2833981810160405281019062000036919062000147565b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603620000a7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016200009e90620001fb565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff1681525050506200021b565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6200011182620000e6565b9050919050565b620001238162000105565b81146200012e575f80fd5b50565b5f81519050620001418162000118565b92915050565b5f602082840312156200015f576200015e620000e2565b5b5f6200016e8482850162000131565b91505092915050565b5f82825260208201905092915050565b7f66656541646472657373206d757374206e6f74206265207a65726f20616464725f8201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b5f620001e360238362000177565b9150620001f08262000187565b604082019050919050565b5f6020820190508181035f8301526200021481620001d5565b9050919050565b608051615360620002425f395f8181612aef01528181612b8a0152612f4801526153605ff3fe608060405260043610610113575f3560e01c80639b4603f21161009f578063cc90c19911610063578063cc90c1991461038e578063d6a71eb4146103b6578063e06cf966146103de578063efccb9eb14610408578063f23a6e611461044657610113565b80639b4603f2146102be578063b27e46fb146102da578063bc197c8114610302578063c8d9009b1461033e578063c92cd12d1461036657610113565b8063150b7a02116100e6578063150b7a02146101cb5780633e6af5f21461020757806346b95ac71461022f57806365e266171461026e5780636e6bf6d21461029657610113565b806301ffc9a71461011757806305ec158d146101535780630f235fce1461017b578063146e5b24146101a3575b5f80fd5b348015610122575f80fd5b5061013d6004803603810190610138919061386f565b610482565b60405161014a91906138b4565b60405180910390f35b34801561015e575f80fd5b506101796004803603810190610174919061398d565b610563565b005b348015610186575f80fd5b506101a1600480360381019061019c9190613a2a565b610823565b005b3480156101ae575f80fd5b506101c960048036038101906101c49190613ab3565b610add565b005b3480156101d6575f80fd5b506101f160048036038101906101ec9190613bb1565b610cc3565b6040516101fe9190613c44565b60405180910390f35b348015610212575f80fd5b5061022d60048036038101906102289190613ab3565b611112565b005b34801561023a575f80fd5b5061025560048036038101906102509190613c5d565b611423565b6040516102659493929190613d53565b60405180910390f35b348015610279575f80fd5b50610294600480360381019061028f9190613ab3565b611485565b005b3480156102a1575f80fd5b506102bc60048036038101906102b79190613a2a565b6118e9565b005b6102d860048036038101906102d39190613dc0565b611ba4565b005b3480156102e5575f80fd5b5061030060048036038101906102fb919061398d565b611eda565b005b34801561030d575f80fd5b5061032860048036038101906103239190613eb2565b612199565b6040516103359190613c44565b60405180910390f35b348015610349575f80fd5b50610364600480360381019061035f9190613a2a565b6121d5565b005b348015610371575f80fd5b5061038c6004803603810190610387919061398d565b6124fe565b005b348015610399575f80fd5b506103b460048036038101906103af9190613ab3565b61282c565b005b3480156103c1575f80fd5b506103dc60048036038101906103d79190613f89565b612bdc565b005b3480156103e9575f80fd5b506103f2612f46565b6040516103ff919061405c565b60405180910390f35b348015610413575f80fd5b5061042e60048036038101906104299190613c5d565b612f6a565b60405161043d939291906140bb565b60405180910390f35b348015610451575f80fd5b5061046c600480360381019061046791906140f0565b612fb6565b6040516104799190613c44565b60405180910390f35b5f7f4e2312e0000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061054c57507f150b7a02000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061055c575061055b8261344a565b5b9050919050565b6001600381111561057757610576613ce0565b5b5f808981526020019081526020015f205f0160189054906101000a900460ff1660038111156105a9576105a8613ce0565b5b146105e9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105e090614206565b60405180910390fd5b5f600387336002896040516020016106019190614244565b60405160208183030381529060405260405161061d91906142ca565b602060405180830381855afa158015610638573d5f803e3d5ffd5b5050506040513d601f19601f8201168201806040525081019061065b91906142f4565b888888886040516020016106759796959493929190614384565b60405160208183030381529060405260405161069191906142ca565b602060405180830381855afa1580156106ac573d5f803e3d5ffd5b5050506040515160601b90505f808981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614610736576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161072d9061444e565b60405180910390fd5b60035f808a81526020019081526020015f205f0160186101000a81548160ff0219169083600381111561076c5761076b613ce0565b5b02179055507fac509cdcc7ddb189f81fff6f4824f5c95076e64c3bdce542c50feaa6779afd73886040516107a0919061447b565b60405180910390a15f8490508073ffffffffffffffffffffffffffffffffffffffff1663f242432a303387876040518563ffffffff1660e01b81526004016107eb94939291906144d6565b5f604051808303815f87803b158015610802575f80fd5b505af1158015610814573d5f803e3d5ffd5b50505050505050505050505050565b6001600381111561083757610836613ce0565b5b5f808881526020019081526020015f205f0160189054906101000a900460ff16600381111561086957610868613ce0565b5b146108a9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a090614206565b60405180910390fd5b5f60038633878787876040516020016108c79695949392919061452c565b6040516020818303038152906040526040516108e391906142ca565b602060405180830381855afa1580156108fe573d5f803e3d5ffd5b5050506040515160601b90505f808881526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614610988576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161097f9061444e565b60405180910390fd5b5f808881526020019081526020015f205f0160149054906101000a900463ffffffff1663ffffffff164210156109f3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109ea9061460b565b60405180910390fd5b60035f808981526020019081526020015f205f0160186101000a81548160ff02191690836003811115610a2957610a28613ce0565b5b02179055507f5dedc4f52b757d9112d09ca0b2f022927104d54e3f54da091587e8ad1921907287604051610a5d919061447b565b60405180910390a15f8390508073ffffffffffffffffffffffffffffffffffffffff166342842e0e3033866040518463ffffffff1660e01b8152600401610aa693929190614629565b5f604051808303815f87803b158015610abd575f80fd5b505af1158015610acf573d5f803e3d5ffd5b505050505050505050505050565b60016004811115610af157610af0613ce0565b5b60015f8981526020019081526020015f205f01601c9054906101000a900460ff166004811115610b2457610b23613ce0565b5b14610b64576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5b90614206565b60405180910390fd5b5f600387878733888888604051602001610b84979695949392919061465e565b604051602081830303815290604052604051610ba091906142ca565b602060405180830381855afa158015610bbb573d5f803e3d5ffd5b5050506040515160601b905060015f8981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614610c46576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c3d9061444e565b60405180910390fd5b600260015f8a81526020019081526020015f205f01601c6101000a81548160ff02191690836004811115610c7d57610c7c613ce0565b5b02179055507f9c45e43e2ef051f70491ffd5221bf02ab37e1324128714ef9610df5f24fc9fb588604051610cb1919061447b565b60405180910390a15050505050505050565b5f808383810190610cd49190614807565b90505f6003811115610ce957610ce8613ce0565b5b5f80835f015181526020019081526020015f205f0160189054906101000a900460ff166003811115610d1e57610d1d613ce0565b5b14610d5e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d55906148a2565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff16816020015173ffffffffffffffffffffffffffffffffffffffff1603610dd0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610dc79061490a565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff16816040015173ffffffffffffffffffffffffffffffffffffffff1603610e42576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e3990614972565b60405180910390fd5b806040015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610eb4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610eab90614a00565b60405180910390fd5b8573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1614610f22576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f1990614a68565b60405180910390fd5b610f2f81602001516134b3565b15610f6f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f6690614ad0565b60405180910390fd5b5f60038260200151888460600151856080015186604001518b604051602001610f9d9695949392919061452c565b604051602081830303815290604052604051610fb991906142ca565b602060405180830381855afa158015610fd4573d5f803e3d5ffd5b5050506040515160601b90506040518060600160405280826bffffffffffffffffffffffff191681526020018360a0015163ffffffff1681526020016001600381111561102457611023613ce0565b5b8152505f80845f015181526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548163ffffffff021916908363ffffffff1602179055506040820151815f0160186101000a81548160ff021916908360038111156110bb576110ba613ce0565b5b02179055509050507ff1dc11bbb6d7542c4267ecf1d370ff4c7092518633ecae9939e8488f4e53d2ad825f01516040516110f5919061447b565b60405180910390a163150b7a0260e01b9250505095945050505050565b6001600481111561112657611125613ce0565b5b60015f8981526020019081526020015f205f01601c9054906101000a900460ff16600481111561115957611158613ce0565b5b14611199576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161119090614206565b60405180910390fd5b5f6003878787336002896040516020016111b39190614244565b6040516020818303038152906040526040516111cf91906142ca565b602060405180830381855afa1580156111ea573d5f803e3d5ffd5b5050506040513d601f19601f8201168201806040525081019061120d91906142f4565b8888604051602001611225979695949392919061465e565b60405160208183030381529060405260405161124191906142ca565b602060405180830381855afa15801561125c573d5f803e3d5ffd5b5050506040515160601b905060015f8981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff1916146112e7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112de9061444e565b60405180910390fd5b600460015f8a81526020019081526020015f205f01601c6101000a81548160ff0219169083600481111561131e5761131d613ce0565b5b02179055507f45169a52eef651b20a81474b50b8a5d83225225fcd097ef3cf7952d9ab304f278885604051611354929190614aee565b60405180910390a15f86886113699190614b42565b90505f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036113e7573373ffffffffffffffffffffffffffffffffffffffff166108fc8290811502906040515f60405180830381858888f193505050501580156113e1573d5f803e3d5ffd5b50611418565b5f83905061141633838373ffffffffffffffffffffffffffffffffffffffff166134c49092919063ffffffff16565b505b505050505050505050565b6001602052805f5260405f205f91509050805f015f9054906101000a900460601b90805f0160149054906101000a900463ffffffff1690805f0160189054906101000a900463ffffffff1690805f01601c9054906101000a900460ff16905084565b6001600481111561149957611498613ce0565b5b60015f8981526020019081526020015f205f01601c9054906101000a900460ff1660048111156114cc576114cb613ce0565b5b148061151c5750600260048111156114e7576114e6613ce0565b5b60015f8981526020019081526020015f205f01601c9054906101000a900460ff16600481111561151a57611519613ce0565b5b145b61155b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161155290614be5565b60405180910390fd5b5f60038787873388888860405160200161157b979695949392919061465e565b60405160208183030381529060405260405161159791906142ca565b602060405180830381855afa1580156115b2573d5f803e3d5ffd5b5050506040515160601b905060015f8981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff19161461163d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116349061444e565b60405180910390fd5b6002600481111561165157611650613ce0565b5b60015f8a81526020019081526020015f205f01601c9054906101000a900460ff16600481111561168457611683613ce0565b5b036116f65760015f8981526020019081526020015f205f0160189054906101000a900463ffffffff1663ffffffff164210156116f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116ec9061460b565b60405180910390fd5b5b6001600481111561170a57611709613ce0565b5b60015f8a81526020019081526020015f205f01601c9054906101000a900460ff16600481111561173d5761173c613ce0565b5b036117af5760015f8981526020019081526020015f205f0160149054906101000a900463ffffffff1663ffffffff164210156117ae576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117a590614c73565b60405180910390fd5b5b600460015f8a81526020019081526020015f205f01601c6101000a81548160ff021916908360048111156117e6576117e5613ce0565b5b02179055507fbdd7a4be6d82798a500b59077706b12d3f45acf5504828919f92501307b2b9538860405161181a919061447b565b60405180910390a15f868861182f9190614b42565b90505f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036118ad573373ffffffffffffffffffffffffffffffffffffffff166108fc8290811502906040515f60405180830381858888f193505050501580156118a7573d5f803e3d5ffd5b506118de565b5f8390506118dc33838373ffffffffffffffffffffffffffffffffffffffff166134c49092919063ffffffff16565b505b505050505050505050565b600160038111156118fd576118fc613ce0565b5b5f808881526020019081526020015f205f0160189054906101000a900460ff16600381111561192f5761192e613ce0565b5b1461196f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161196690614206565b60405180910390fd5b5f600386336002886040516020016119879190614244565b6040516020818303038152906040526040516119a391906142ca565b602060405180830381855afa1580156119be573d5f803e3d5ffd5b5050506040513d601f19601f820116820180604052508101906119e191906142f4565b8787876040516020016119f99695949392919061452c565b604051602081830303815290604052604051611a1591906142ca565b602060405180830381855afa158015611a30573d5f803e3d5ffd5b5050506040515160601b90505f808881526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614611aba576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611ab19061444e565b60405180910390fd5b60035f808981526020019081526020015f205f0160186101000a81548160ff02191690836003811115611af057611aef613ce0565b5b02179055507fac509cdcc7ddb189f81fff6f4824f5c95076e64c3bdce542c50feaa6779afd7387604051611b24919061447b565b60405180910390a15f8390508073ffffffffffffffffffffffffffffffffffffffff166342842e0e3033866040518463ffffffff1660e01b8152600401611b6d93929190614629565b5f604051808303815f87803b158015611b84575f80fd5b505af1158015611b96573d5f803e3d5ffd5b505050505050505050505050565b5f6004811115611bb757611bb6613ce0565b5b60015f8981526020019081526020015f205f01601c9054906101000a900460ff166004811115611bea57611be9613ce0565b5b14611c2a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c2190614d01565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1603611c98576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c8f90614d8f565b60405180910390fd5b5f3411611cda576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611cd190614e1d565b60405180910390fd5b853411611d1c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611d1390614eab565b60405180910390fd5b5f60038734611d2b9190614ec9565b88883389895f604051602001611d47979695949392919061465e565b604051602081830303815290604052604051611d6391906142ca565b602060405180830381855afa158015611d7e573d5f803e3d5ffd5b5050506040515160601b90506040518060800160405280826bffffffffffffffffffffffff191681526020018463ffffffff1681526020018363ffffffff16815260200160016004811115611dd657611dd5613ce0565b5b81525060015f8a81526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548163ffffffff021916908363ffffffff1602179055506040820151815f0160186101000a81548163ffffffff021916908363ffffffff1602179055506060820151815f01601c6101000a81548160ff02191690836004811115611e9157611e90613ce0565b5b02179055509050507ffc6cdccd1d98ded12074a9ebc7f6ab74fed1814ff57f4fb5202464d8938bd93588604051611ec8919061447b565b60405180910390a15050505050505050565b60016003811115611eee57611eed613ce0565b5b5f808981526020019081526020015f205f0160189054906101000a900460ff166003811115611f2057611f1f613ce0565b5b14611f60576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611f5790614206565b60405180910390fd5b5f600387338888888888604051602001611f809796959493929190614384565b604051602081830303815290604052604051611f9c91906142ca565b602060405180830381855afa158015611fb7573d5f803e3d5ffd5b5050506040515160601b90505f808981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614612041576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120389061444e565b60405180910390fd5b5f808981526020019081526020015f205f0160149054906101000a900463ffffffff1663ffffffff164210156120ac576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120a39061460b565b60405180910390fd5b60035f808a81526020019081526020015f205f0160186101000a81548160ff021916908360038111156120e2576120e1613ce0565b5b02179055507f5dedc4f52b757d9112d09ca0b2f022927104d54e3f54da091587e8ad1921907288604051612116919061447b565b60405180910390a15f8490508073ffffffffffffffffffffffffffffffffffffffff1663f242432a303387876040518563ffffffff1660e01b815260040161216194939291906144d6565b5f604051808303815f87803b158015612178575f80fd5b505af115801561218a573d5f803e3d5ffd5b50505050505050505050505050565b5f6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016121cc90614f46565b60405180910390fd5b600160038111156121e9576121e8613ce0565b5b5f808881526020019081526020015f205f0160189054906101000a900460ff16600381111561221b5761221a613ce0565b5b1461225b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161225290614206565b60405180910390fd5b3273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146122c9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016122c090614fae565b60405180910390fd5b5f60033387876002886040516020016122e29190614244565b6040516020818303038152906040526040516122fe91906142ca565b602060405180830381855afa158015612319573d5f803e3d5ffd5b5050506040513d601f19601f8201168201806040525081019061233c91906142f4565b87876040516020016123539695949392919061452c565b60405160208183030381529060405260405161236f91906142ca565b602060405180830381855afa15801561238a573d5f803e3d5ffd5b5050506040515160601b90505f808881526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614612414576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161240b9061444e565b60405180910390fd5b60025f808981526020019081526020015f205f0160186101000a81548160ff0219169083600381111561244a57612449613ce0565b5b02179055507fad62ed075fe8969df63026f45152d6e996a0697a736a8de92ee85ae9c9958cf08760405161247e919061447b565b60405180910390a15f8390508073ffffffffffffffffffffffffffffffffffffffff166342842e0e3033866040518463ffffffff1660e01b81526004016124c793929190614629565b5f604051808303815f87803b1580156124de575f80fd5b505af11580156124f0573d5f803e3d5ffd5b505050505050505050505050565b6001600381111561251257612511613ce0565b5b5f808981526020019081526020015f205f0160189054906101000a900460ff16600381111561254457612543613ce0565b5b14612584576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161257b90614206565b60405180910390fd5b3273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146125f2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016125e990614fae565b60405180910390fd5b5f600333888860028960405160200161260b9190614244565b60405160208183030381529060405260405161262791906142ca565b602060405180830381855afa158015612642573d5f803e3d5ffd5b5050506040513d601f19601f8201168201806040525081019061266591906142f4565b88888860405160200161267e9796959493929190614384565b60405160208183030381529060405260405161269a91906142ca565b602060405180830381855afa1580156126b5573d5f803e3d5ffd5b5050506040515160601b90505f808981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff19161461273f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016127369061444e565b60405180910390fd5b60025f808a81526020019081526020015f205f0160186101000a81548160ff0219169083600381111561277557612774613ce0565b5b02179055507fad62ed075fe8969df63026f45152d6e996a0697a736a8de92ee85ae9c9958cf0886040516127a9919061447b565b60405180910390a15f8490508073ffffffffffffffffffffffffffffffffffffffff1663f242432a303387876040518563ffffffff1660e01b81526004016127f494939291906144d6565b5f604051808303815f87803b15801561280b575f80fd5b505af115801561281d573d5f803e3d5ffd5b50505050505050505050505050565b600260048111156128405761283f613ce0565b5b60015f8981526020019081526020015f205f01601c9054906101000a900460ff16600481111561287357612872613ce0565b5b146128b3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016128aa9061503c565b60405180910390fd5b5f600387873388886002896040516020016128ce9190614244565b6040516020818303038152906040526040516128ea91906142ca565b602060405180830381855afa158015612905573d5f803e3d5ffd5b5050506040513d601f19601f8201168201806040525081019061292891906142f4565b8860405160200161293f979695949392919061465e565b60405160208183030381529060405260405161295b91906142ca565b602060405180830381855afa158015612976573d5f803e3d5ffd5b5050506040515160601b905060015f8981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614612a01576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016129f89061444e565b60405180910390fd5b600360015f8a81526020019081526020015f205f01601c6101000a81548160ff02191690836004811115612a3857612a37613ce0565b5b02179055507f0d0da0df275f85bed3a5fe7ae79f3559341a3f9ccd8e010133438135bda00a878884604051612a6e929190614aee565b60405180910390a15f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603612b56573373ffffffffffffffffffffffffffffffffffffffff166108fc8890811502906040515f60405180830381858888f19350505050158015612aec573d5f803e3d5ffd5b507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166108fc8790811502906040515f60405180830381858888f19350505050158015612b50573d5f803e3d5ffd5b50612bd2565b5f829050612b8533898373ffffffffffffffffffffffffffffffffffffffff166134c49092919063ffffffff16565b612bd07f0000000000000000000000000000000000000000000000000000000000000000888373ffffffffffffffffffffffffffffffffffffffff166134c49092919063ffffffff16565b505b5050505050505050565b5f6004811115612bef57612bee613ce0565b5b60015f8b81526020019081526020015f205f01601c9054906101000a900460ff166004811115612c2257612c21613ce0565b5b14612c62576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612c59906150ca565b60405180910390fd5b5f8811612ca4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612c9b90615132565b60405180910390fd5b5f8711612ce6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612cdd9061519a565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1603612d54576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612d4b90614d8f565b60405180910390fd5b5f60038989883389898d604051602001612d74979695949392919061465e565b604051602081830303815290604052604051612d9091906142ca565b602060405180830381855afa158015612dab573d5f803e3d5ffd5b5050506040515160601b90506040518060800160405280826bffffffffffffffffffffffff191681526020018463ffffffff1681526020018363ffffffff16815260200160016004811115612e0357612e02613ce0565b5b81525060015f8c81526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548163ffffffff021916908363ffffffff1602179055506040820151815f0160186101000a81548163ffffffff021916908363ffffffff1602179055506060820151815f01601c6101000a81548160ff02191690836004811115612ebe57612ebd613ce0565b5b02179055509050507ffc6cdccd1d98ded12074a9ebc7f6ab74fed1814ff57f4fb5202464d8938bd9358a604051612ef5919061447b565b60405180910390a15f879050612f3933308b8d612f129190614b42565b8473ffffffffffffffffffffffffffffffffffffffff16613543909392919063ffffffff16565b5050505050505050505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b5f602052805f5260405f205f91509050805f015f9054906101000a900460601b90805f0160149054906101000a900463ffffffff1690805f0160189054906101000a900460ff16905083565b5f808383810190612fc79190614807565b90505f6003811115612fdc57612fdb613ce0565b5b5f80835f015181526020019081526020015f205f0160189054906101000a900460ff16600381111561301157613010613ce0565b5b14613051576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161304890615228565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff16816020015173ffffffffffffffffffffffffffffffffffffffff16036130c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016130ba9061490a565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff16816040015173ffffffffffffffffffffffffffffffffffffffff1603613135576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161312c90614972565b60405180910390fd5b806040015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146131a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161319e90614a00565b60405180910390fd5b8673ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614613215576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161320c90614a68565b60405180910390fd5b5f8511613257576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161324e90615290565b60405180910390fd5b61326481602001516134b3565b156132a4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161329b90614ad0565b60405180910390fd5b5f60038260200151898460600151856080015186604001518c8c6040516020016132d49796959493929190614384565b6040516020818303038152906040526040516132f091906142ca565b602060405180830381855afa15801561330b573d5f803e3d5ffd5b5050506040515160601b90506040518060600160405280826bffffffffffffffffffffffff191681526020018360a0015163ffffffff1681526020016001600381111561335b5761335a613ce0565b5b8152505f80845f015181526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548163ffffffff021916908363ffffffff1602179055506040820151815f0160186101000a81548160ff021916908360038111156133f2576133f1613ce0565b5b02179055509050507ff1dc11bbb6d7542c4267ecf1d370ff4c7092518633ecae9939e8488f4e53d2ad825f015160405161342c919061447b565b60405180910390a163f23a6e6160e01b925050509695505050505050565b5f7f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b5f80823b90505f8111915050919050565b61353e838473ffffffffffffffffffffffffffffffffffffffff1663a9059cbb85856040516024016134f79291906152ae565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506135c5565b505050565b6135bf848573ffffffffffffffffffffffffffffffffffffffff166323b872dd86868660405160240161357893929190614629565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506135c5565b50505050565b5f6135ef828473ffffffffffffffffffffffffffffffffffffffff1661365a90919063ffffffff16565b90505f81511415801561361357508080602001905181019061361191906152ff565b155b1561365557826040517f5274afe700000000000000000000000000000000000000000000000000000000815260040161364c919061405c565b60405180910390fd5b505050565b606061366783835f61366f565b905092915050565b6060814710156136b657306040517fcd7860590000000000000000000000000000000000000000000000000000000081526004016136ad919061405c565b60405180910390fd5b5f808573ffffffffffffffffffffffffffffffffffffffff1684866040516136de91906142ca565b5f6040518083038185875af1925050503d805f8114613718576040519150601f19603f3d011682016040523d82523d5f602084013e61371d565b606091505b509150915061372d868383613738565b925050509392505050565b60608261374d57613748826137c5565b6137bd565b5f825114801561377357505f8473ffffffffffffffffffffffffffffffffffffffff163b145b156137b557836040517f9996b3150000000000000000000000000000000000000000000000000000000081526004016137ac919061405c565b60405180910390fd5b8190506137be565b5b9392505050565b5f815111156137d75780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f604051905090565b5f80fd5b5f80fd5b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61384e8161381a565b8114613858575f80fd5b50565b5f8135905061386981613845565b92915050565b5f6020828403121561388457613883613812565b5b5f6138918482850161385b565b91505092915050565b5f8115159050919050565b6138ae8161389a565b82525050565b5f6020820190506138c75f8301846138a5565b92915050565b5f819050919050565b6138df816138cd565b81146138e9575f80fd5b50565b5f813590506138fa816138d6565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61392982613900565b9050919050565b6139398161391f565b8114613943575f80fd5b50565b5f8135905061395481613930565b92915050565b5f819050919050565b61396c8161395a565b8114613976575f80fd5b50565b5f8135905061398781613963565b92915050565b5f805f805f805f60e0888a0312156139a8576139a7613812565b5b5f6139b58a828b016138ec565b97505060206139c68a828b01613946565b96505060406139d78a828b016138ec565b95505060606139e88a828b016138ec565b94505060806139f98a828b01613946565b93505060a0613a0a8a828b01613979565b92505060c0613a1b8a828b01613979565b91505092959891949750929550565b5f805f805f8060c08789031215613a4457613a43613812565b5b5f613a5189828a016138ec565b9650506020613a6289828a01613946565b9550506040613a7389828a016138ec565b9450506060613a8489828a016138ec565b9350506080613a9589828a01613946565b92505060a0613aa689828a01613979565b9150509295509295509295565b5f805f805f805f60e0888a031215613ace57613acd613812565b5b5f613adb8a828b016138ec565b9750506020613aec8a828b01613979565b9650506040613afd8a828b01613979565b9550506060613b0e8a828b01613946565b9450506080613b1f8a828b016138ec565b93505060a0613b308a828b016138ec565b92505060c0613b418a828b01613946565b91505092959891949750929550565b5f80fd5b5f80fd5b5f80fd5b5f8083601f840112613b7157613b70613b50565b5b8235905067ffffffffffffffff811115613b8e57613b8d613b54565b5b602083019150836001820283011115613baa57613ba9613b58565b5b9250929050565b5f805f805f60808688031215613bca57613bc9613812565b5b5f613bd788828901613946565b9550506020613be888828901613946565b9450506040613bf988828901613979565b935050606086013567ffffffffffffffff811115613c1a57613c19613816565b5b613c2688828901613b5c565b92509250509295509295909350565b613c3e8161381a565b82525050565b5f602082019050613c575f830184613c35565b92915050565b5f60208284031215613c7257613c71613812565b5b5f613c7f848285016138ec565b91505092915050565b5f7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000082169050919050565b613cbc81613c88565b82525050565b5f63ffffffff82169050919050565b613cda81613cc2565b82525050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b60058110613d1e57613d1d613ce0565b5b50565b5f819050613d2e82613d0d565b919050565b5f613d3d82613d21565b9050919050565b613d4d81613d33565b82525050565b5f608082019050613d665f830187613cb3565b613d736020830186613cd1565b613d806040830185613cd1565b613d8d6060830184613d44565b95945050505050565b613d9f81613cc2565b8114613da9575f80fd5b50565b5f81359050613dba81613d96565b92915050565b5f805f805f805f60e0888a031215613ddb57613dda613812565b5b5f613de88a828b016138ec565b9750506020613df98a828b01613979565b9650506040613e0a8a828b01613946565b9550506060613e1b8a828b016138ec565b9450506080613e2c8a828b016138ec565b93505060a0613e3d8a828b01613dac565b92505060c0613e4e8a828b01613dac565b91505092959891949750929550565b5f8083601f840112613e7257613e71613b50565b5b8235905067ffffffffffffffff811115613e8f57613e8e613b54565b5b602083019150836020820283011115613eab57613eaa613b58565b5b9250929050565b5f805f805f805f8060a0898b031215613ece57613ecd613812565b5b5f613edb8b828c01613946565b9850506020613eec8b828c01613946565b975050604089013567ffffffffffffffff811115613f0d57613f0c613816565b5b613f198b828c01613e5d565b9650965050606089013567ffffffffffffffff811115613f3c57613f3b613816565b5b613f488b828c01613e5d565b9450945050608089013567ffffffffffffffff811115613f6b57613f6a613816565b5b613f778b828c01613b5c565b92509250509295985092959890939650565b5f805f805f805f805f6101208a8c031215613fa757613fa6613812565b5b5f613fb48c828d016138ec565b9950506020613fc58c828d01613979565b9850506040613fd68c828d01613979565b9750506060613fe78c828d01613946565b9650506080613ff88c828d01613946565b95505060a06140098c828d016138ec565b94505060c061401a8c828d016138ec565b93505060e061402b8c828d01613dac565b92505061010061403d8c828d01613dac565b9150509295985092959850929598565b6140568161391f565b82525050565b5f60208201905061406f5f83018461404d565b92915050565b6004811061408657614085613ce0565b5b50565b5f81905061409682614075565b919050565b5f6140a582614089565b9050919050565b6140b58161409b565b82525050565b5f6060820190506140ce5f830186613cb3565b6140db6020830185613cd1565b6140e860408301846140ac565b949350505050565b5f805f805f8060a0878903121561410a57614109613812565b5b5f61411789828a01613946565b965050602061412889828a01613946565b955050604061413989828a01613979565b945050606061414a89828a01613979565b935050608087013567ffffffffffffffff81111561416b5761416a613816565b5b61417789828a01613b5c565b92509250509295509295509295565b5f82825260208201905092915050565b7f496e76616c6964207061796d656e742073746174652e204d75737420626520505f8201527f61796d656e7453656e7400000000000000000000000000000000000000000000602082015250565b5f6141f0602a83614186565b91506141fb82614196565b604082019050919050565b5f6020820190508181035f83015261421d816141e4565b9050919050565b5f819050919050565b61423e614239826138cd565b614224565b82525050565b5f61424f828461422d565b60208201915081905092915050565b5f81519050919050565b5f81905092915050565b5f5b8381101561428f578082015181840152602081019050614274565b5f8484015250505050565b5f6142a48261425e565b6142ae8185614268565b93506142be818560208601614272565b80840191505092915050565b5f6142d5828461429a565b915081905092915050565b5f815190506142ee816138d6565b92915050565b5f6020828403121561430957614308613812565b5b5f614316848285016142e0565b91505092915050565b5f8160601b9050919050565b5f6143358261431f565b9050919050565b5f6143468261432b565b9050919050565b61435e6143598261391f565b61433c565b82525050565b5f819050919050565b61437e6143798261395a565b614364565b82525050565b5f61438f828a61434d565b60148201915061439f828961434d565b6014820191506143af828861422d565b6020820191506143bf828761422d565b6020820191506143cf828661434d565b6014820191506143df828561436d565b6020820191506143ef828461436d565b60208201915081905098975050505050505050565b7f496e76616c6964207061796d656e7448617368000000000000000000000000005f82015250565b5f614438601383614186565b915061444382614404565b602082019050919050565b5f6020820190508181035f8301526144658161442c565b9050919050565b614475816138cd565b82525050565b5f60208201905061448e5f83018461446c565b92915050565b61449d8161395a565b82525050565b5f82825260208201905092915050565b50565b5f6144c15f836144a3565b91506144cc826144b3565b5f82019050919050565b5f60a0820190506144e95f83018761404d565b6144f6602083018661404d565b6145036040830185614494565b6145106060830184614494565b8181036080830152614521816144b6565b905095945050505050565b5f614537828961434d565b601482019150614547828861434d565b601482019150614557828761422d565b602082019150614567828661422d565b602082019150614577828561434d565b601482019150614587828461436d565b602082019150819050979650505050505050565b7f43757272656e742074696d657374616d70206469646e277420657863656564205f8201527f7061796d656e7420726566756e64206c6f636b2074696d650000000000000000602082015250565b5f6145f5603883614186565b91506146008261459b565b604082019050919050565b5f6020820190508181035f830152614622816145e9565b9050919050565b5f60608201905061463c5f83018661404d565b614649602083018561404d565b6146566040830184614494565b949350505050565b5f614669828a61436d565b602082019150614679828961436d565b602082019150614689828861434d565b601482019150614699828761434d565b6014820191506146a9828661422d565b6020820191506146b9828561422d565b6020820191506146c9828461434d565b60148201915081905098975050505050505050565b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b614728826146e2565b810181811067ffffffffffffffff82111715614747576147466146f2565b5b80604052505050565b5f614759613809565b9050614765828261471f565b919050565b5f60c0828403121561477f5761477e6146de565b5b61478960c0614750565b90505f614798848285016138ec565b5f8301525060206147ab84828501613946565b60208301525060406147bf84828501613946565b60408301525060606147d3848285016138ec565b60608301525060806147e7848285016138ec565b60808301525060a06147fb84828501613dac565b60a08301525092915050565b5f60c0828403121561481c5761481b613812565b5b5f6148298482850161476a565b91505092915050565b7f4d616b657220455243373231207061796d656e74206d75737420626520556e695f8201527f6e697469616c697a656400000000000000000000000000000000000000000000602082015250565b5f61488c602a83614186565b915061489782614832565b604082019050919050565b5f6020820190508181035f8301526148b981614880565b9050919050565b7f54616b6572206d757374206e6f74206265207a65726f206164647265737300005f82015250565b5f6148f4601e83614186565b91506148ff826148c0565b602082019050919050565b5f6020820190508181035f830152614921816148e8565b9050919050565b7f546f6b656e206d757374206e6f74206265207a65726f206164647265737300005f82015250565b5f61495c601e83614186565b915061496782614928565b602082019050919050565b5f6020820190508181035f83015261498981614950565b9050919050565b7f546f6b656e206164647265737320646f6573206e6f74206d617463682073656e5f8201527f6465720000000000000000000000000000000000000000000000000000000000602082015250565b5f6149ea602383614186565b91506149f582614990565b604082019050919050565b5f6020820190508181035f830152614a17816149de565b9050919050565b7f4f70657261746f72206d757374206265207468652073656e64657200000000005f82015250565b5f614a52601b83614186565b9150614a5d82614a1e565b602082019050919050565b5f6020820190508181035f830152614a7f81614a46565b9050919050565b7f54616b65722063616e6e6f74206265206120636f6e74726163740000000000005f82015250565b5f614aba601a83614186565b9150614ac582614a86565b602082019050919050565b5f6020820190508181035f830152614ae781614aae565b9050919050565b5f604082019050614b015f83018561446c565b614b0e602083018461446c565b9392505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f614b4c8261395a565b9150614b578361395a565b9250828201905080821115614b6f57614b6e614b15565b5b92915050565b7f496e76616c6964207061796d656e742073746174652e204d75737420626520505f8201527f61796d656e7453656e74206f722054616b6572417070726f7665640000000000602082015250565b5f614bcf603b83614186565b9150614bda82614b75565b604082019050919050565b5f6020820190508181035f830152614bfc81614bc3565b9050919050565b7f43757272656e742074696d657374616d70206469646e277420657863656564205f8201527f7061796d656e74207072652d617070726f7665206c6f636b2074696d65000000602082015250565b5f614c5d603d83614186565b9150614c6882614c03565b604082019050919050565b5f6020820190508181035f830152614c8a81614c51565b9050919050565b7f54616b6572207061796d656e7420697320616c726561647920696e697469616c5f8201527f697a656400000000000000000000000000000000000000000000000000000000602082015250565b5f614ceb602483614186565b9150614cf682614c91565b604082019050919050565b5f6020820190508181035f830152614d1881614cdf565b9050919050565b7f5265636569766572206d757374206e6f74206265207a65726f206164647265735f8201527f7300000000000000000000000000000000000000000000000000000000000000602082015250565b5f614d79602183614186565b9150614d8482614d1f565b604082019050919050565b5f6020820190508181035f830152614da681614d6d565b9050919050565b7f4554482076616c7565206d7573742062652067726561746572207468616e207a5f8201527f65726f0000000000000000000000000000000000000000000000000000000000602082015250565b5f614e07602383614186565b9150614e1282614dad565b604082019050919050565b5f6020820190508181035f830152614e3481614dfb565b9050919050565b7f4554482076616c7565206d7573742062652067726561746572207468616e20645f8201527f6578206665650000000000000000000000000000000000000000000000000000602082015250565b5f614e95602683614186565b9150614ea082614e3b565b604082019050919050565b5f6020820190508181035f830152614ec281614e89565b9050919050565b5f614ed38261395a565b9150614ede8361395a565b9250828203905081811115614ef657614ef5614b15565b5b92915050565b7f4261746368207472616e7366657273206e6f7420737570706f727465640000005f82015250565b5f614f30601d83614186565b9150614f3b82614efc565b602082019050919050565b5f6020820190508181035f830152614f5d81614f24565b9050919050565b7f43616c6c6572206d75737420626520616e20454f4100000000000000000000005f82015250565b5f614f98601583614186565b9150614fa382614f64565b602082019050919050565b5f6020820190508181035f830152614fc581614f8c565b9050919050565b7f496e76616c6964207061796d656e742073746174652e204d75737420626520545f8201527f616b6572417070726f7665640000000000000000000000000000000000000000602082015250565b5f615026602c83614186565b915061503182614fcc565b604082019050919050565b5f6020820190508181035f8301526150538161501a565b9050919050565b7f4552433230207632207061796d656e7420697320616c726561647920696e69745f8201527f69616c697a656400000000000000000000000000000000000000000000000000602082015250565b5f6150b4602783614186565b91506150bf8261505a565b604082019050919050565b5f6020820190508181035f8301526150e1816150a8565b9050919050565b7f416d6f756e74206d757374206e6f74206265207a65726f0000000000000000005f82015250565b5f61511c601783614186565b9150615127826150e8565b602082019050919050565b5f6020820190508181035f83015261514981615110565b9050919050565b7f44657820666565206d757374206e6f74206265207a65726f00000000000000005f82015250565b5f615184601883614186565b915061518f82615150565b602082019050919050565b5f6020820190508181035f8301526151b181615178565b9050919050565b7f4d616b65722045524331313535207061796d656e74206d75737420626520556e5f8201527f696e697469616c697a6564000000000000000000000000000000000000000000602082015250565b5f615212602b83614186565b915061521d826151b8565b604082019050919050565b5f6020820190508181035f83015261523f81615206565b9050919050565b7f56616c7565206d7573742062652067726561746572207468616e2030000000005f82015250565b5f61527a601c83614186565b915061528582615246565b602082019050919050565b5f6020820190508181035f8301526152a78161526e565b9050919050565b5f6040820190506152c15f83018561404d565b6152ce6020830184614494565b9392505050565b6152de8161389a565b81146152e8575f80fd5b50565b5f815190506152f9816152d5565b92915050565b5f6020828403121561531457615313613812565b5b5f615321848285016152eb565b9150509291505056fea26469706673582212200d86b0f6898fb823c55626c3b02a7098bc8622606b092e0f458df6c86ce2967864736f6c63430008180033 \ No newline at end of file diff --git a/mm2src/mm2_test_helpers/contract_bytes/taker_swap_v2_bytes b/mm2src/mm2_test_helpers/contract_bytes/taker_swap_v2_bytes index 5394d75e8a..ea4cd8c817 100644 --- a/mm2src/mm2_test_helpers/contract_bytes/taker_swap_v2_bytes +++ b/mm2src/mm2_test_helpers/contract_bytes/taker_swap_v2_bytes @@ -1 +1 @@ -60a060405234801561000f575f80fd5b50604051612a1b380380612a1b83398181016040528101906100319190610137565b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361009f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610096906101e2565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff168152505050610200565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610106826100dd565b9050919050565b610116816100fc565b8114610120575f80fd5b50565b5f815190506101318161010d565b92915050565b5f6020828403121561014c5761014b6100d9565b5b5f61015984828501610123565b91505092915050565b5f82825260208201905092915050565b7f66656541646472657373206d757374206e6f74206265207a65726f20616464725f8201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b5f6101cc602383610162565b91506101d782610172565b604082019050919050565b5f6020820190508181035f8301526101f9816101c0565b9050919050565b6080516127f56102265f395f818161116d0152818161120801526115c401526127f55ff3fe60806040526004361061007a575f3560e01c80639b4603f21161004d5780639b4603f214610135578063cc90c19914610151578063d6a71eb414610179578063e06cf966146101a15761007a565b8063146e5b241461007e5780633e6af5f2146100a657806346b95ac7146100ce57806365e266171461010d575b5f80fd5b348015610089575f80fd5b506100a4600480360381019061009f91906119ef565b6101cb565b005b3480156100b1575f80fd5b506100cc60048036038101906100c791906119ef565b6103ae565b005b3480156100d9575f80fd5b506100f460048036038101906100ef9190611a8c565b6106bc565b6040516101049493929190611b82565b60405180910390f35b348015610118575f80fd5b50610133600480360381019061012e91906119ef565b61071d565b005b61014f600480360381019061014a9190611bef565b610b79565b005b34801561015c575f80fd5b50610177600480360381019061017291906119ef565b610ead565b005b348015610184575f80fd5b5061019f600480360381019061019a9190611c8c565b61125a565b005b3480156101ac575f80fd5b506101b56115c2565b6040516101c29190611d5f565b60405180910390f35b600160048111156101df576101de611b0f565b5b5f808981526020019081526020015f205f01601c9054906101000a900460ff16600481111561021157610210611b0f565b5b14610251576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161024890611df8565b60405180910390fd5b5f6003878787338888886040516020016102719796959493929190611e9b565b60405160208183030381529060405260405161028d9190611f6d565b602060405180830381855afa1580156102a8573d5f803e3d5ffd5b5050506040515160601b90505f808981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614610332576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161032990611fcd565b60405180910390fd5b60025f808a81526020019081526020015f205f01601c6101000a81548160ff0219169083600481111561036857610367611b0f565b5b02179055507f9c45e43e2ef051f70491ffd5221bf02ab37e1324128714ef9610df5f24fc9fb58860405161039c9190611ffa565b60405180910390a15050505050505050565b600160048111156103c2576103c1611b0f565b5b5f808981526020019081526020015f205f01601c9054906101000a900460ff1660048111156103f4576103f3611b0f565b5b14610434576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161042b90611df8565b60405180910390fd5b5f60038787873360028960405160200161044e9190612013565b60405160208183030381529060405260405161046a9190611f6d565b602060405180830381855afa158015610485573d5f803e3d5ffd5b5050506040513d601f19601f820116820180604052508101906104a89190612041565b88886040516020016104c09796959493929190611e9b565b6040516020818303038152906040526040516104dc9190611f6d565b602060405180830381855afa1580156104f7573d5f803e3d5ffd5b5050506040515160601b90505f808981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614610581576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161057890611fcd565b60405180910390fd5b60045f808a81526020019081526020015f205f01601c6101000a81548160ff021916908360048111156105b7576105b6611b0f565b5b02179055507f45169a52eef651b20a81474b50b8a5d83225225fcd097ef3cf7952d9ab304f2788856040516105ed92919061206c565b60405180910390a15f868861060291906120c0565b90505f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610680573373ffffffffffffffffffffffffffffffffffffffff166108fc8290811502906040515f60405180830381858888f1935050505015801561067a573d5f803e3d5ffd5b506106b1565b5f8390506106af33838373ffffffffffffffffffffffffffffffffffffffff166115e69092919063ffffffff16565b505b505050505050505050565b5f602052805f5260405f205f91509050805f015f9054906101000a900460601b90805f0160149054906101000a900463ffffffff1690805f0160189054906101000a900463ffffffff1690805f01601c9054906101000a900460ff16905084565b6001600481111561073157610730611b0f565b5b5f808981526020019081526020015f205f01601c9054906101000a900460ff16600481111561076357610762611b0f565b5b14806107b257506002600481111561077e5761077d611b0f565b5b5f808981526020019081526020015f205f01601c9054906101000a900460ff1660048111156107b0576107af611b0f565b5b145b6107f1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107e890612163565b60405180910390fd5b5f6003878787338888886040516020016108119796959493929190611e9b565b60405160208183030381529060405260405161082d9190611f6d565b602060405180830381855afa158015610848573d5f803e3d5ffd5b5050506040515160601b90505f808981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff1916146108d2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108c990611fcd565b60405180910390fd5b600260048111156108e6576108e5611b0f565b5b5f808a81526020019081526020015f205f01601c9054906101000a900460ff16600481111561091857610917611b0f565b5b03610989575f808981526020019081526020015f205f0160189054906101000a900463ffffffff1663ffffffff16421015610988576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161097f906121f1565b60405180910390fd5b5b6001600481111561099d5761099c611b0f565b5b5f808a81526020019081526020015f205f01601c9054906101000a900460ff1660048111156109cf576109ce611b0f565b5b03610a40575f808981526020019081526020015f205f0160149054906101000a900463ffffffff1663ffffffff16421015610a3f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a369061227f565b60405180910390fd5b5b60045f808a81526020019081526020015f205f01601c6101000a81548160ff02191690836004811115610a7657610a75611b0f565b5b02179055507fbdd7a4be6d82798a500b59077706b12d3f45acf5504828919f92501307b2b95388604051610aaa9190611ffa565b60405180910390a15f8688610abf91906120c0565b90505f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610b3d573373ffffffffffffffffffffffffffffffffffffffff166108fc8290811502906040515f60405180830381858888f19350505050158015610b37573d5f803e3d5ffd5b50610b6e565b5f839050610b6c33838373ffffffffffffffffffffffffffffffffffffffff166115e69092919063ffffffff16565b505b505050505050505050565b5f6004811115610b8c57610b8b611b0f565b5b5f808981526020019081526020015f205f01601c9054906101000a900460ff166004811115610bbe57610bbd611b0f565b5b14610bfe576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bf59061230d565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1603610c6c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c639061239b565b60405180910390fd5b5f3411610cae576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ca590612429565b60405180910390fd5b853411610cf0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ce7906124b7565b60405180910390fd5b5f60038734610cff91906124d5565b88883389895f604051602001610d1b9796959493929190611e9b565b604051602081830303815290604052604051610d379190611f6d565b602060405180830381855afa158015610d52573d5f803e3d5ffd5b5050506040515160601b90506040518060800160405280826bffffffffffffffffffffffff191681526020018463ffffffff1681526020018363ffffffff16815260200160016004811115610daa57610da9611b0f565b5b8152505f808a81526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548163ffffffff021916908363ffffffff1602179055506040820151815f0160186101000a81548163ffffffff021916908363ffffffff1602179055506060820151815f01601c6101000a81548160ff02191690836004811115610e6457610e63611b0f565b5b02179055509050507ffc6cdccd1d98ded12074a9ebc7f6ab74fed1814ff57f4fb5202464d8938bd93588604051610e9b9190611ffa565b60405180910390a15050505050505050565b60026004811115610ec157610ec0611b0f565b5b5f808981526020019081526020015f205f01601c9054906101000a900460ff166004811115610ef357610ef2611b0f565b5b14610f33576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f2a90612578565b60405180910390fd5b5f60038787338888600289604051602001610f4e9190612013565b604051602081830303815290604052604051610f6a9190611f6d565b602060405180830381855afa158015610f85573d5f803e3d5ffd5b5050506040513d601f19601f82011682018060405250810190610fa89190612041565b88604051602001610fbf9796959493929190611e9b565b604051602081830303815290604052604051610fdb9190611f6d565b602060405180830381855afa158015610ff6573d5f803e3d5ffd5b5050506040515160601b90505f808981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614611080576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161107790611fcd565b60405180910390fd5b60035f808a81526020019081526020015f205f01601c6101000a81548160ff021916908360048111156110b6576110b5611b0f565b5b02179055507f0d0da0df275f85bed3a5fe7ae79f3559341a3f9ccd8e010133438135bda00a8788846040516110ec92919061206c565b60405180910390a15f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036111d4573373ffffffffffffffffffffffffffffffffffffffff166108fc8890811502906040515f60405180830381858888f1935050505015801561116a573d5f803e3d5ffd5b507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166108fc8790811502906040515f60405180830381858888f193505050501580156111ce573d5f803e3d5ffd5b50611250565b5f82905061120333898373ffffffffffffffffffffffffffffffffffffffff166115e69092919063ffffffff16565b61124e7f0000000000000000000000000000000000000000000000000000000000000000888373ffffffffffffffffffffffffffffffffffffffff166115e69092919063ffffffff16565b505b5050505050505050565b5f600481111561126d5761126c611b0f565b5b5f808b81526020019081526020015f205f01601c9054906101000a900460ff16600481111561129f5761129e611b0f565b5b146112df576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112d690612606565b60405180910390fd5b5f8811611321576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113189061266e565b60405180910390fd5b5f8711611363576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161135a906126d6565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16036113d1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113c89061239b565b60405180910390fd5b5f60038989883389898d6040516020016113f19796959493929190611e9b565b60405160208183030381529060405260405161140d9190611f6d565b602060405180830381855afa158015611428573d5f803e3d5ffd5b5050506040515160601b90506040518060800160405280826bffffffffffffffffffffffff191681526020018463ffffffff1681526020018363ffffffff168152602001600160048111156114805761147f611b0f565b5b8152505f808c81526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548163ffffffff021916908363ffffffff1602179055506040820151815f0160186101000a81548163ffffffff021916908363ffffffff1602179055506060820151815f01601c6101000a81548160ff0219169083600481111561153a57611539611b0f565b5b02179055509050507ffc6cdccd1d98ded12074a9ebc7f6ab74fed1814ff57f4fb5202464d8938bd9358a6040516115719190611ffa565b60405180910390a15f8790506115b533308b8d61158e91906120c0565b8473ffffffffffffffffffffffffffffffffffffffff16611665909392919063ffffffff16565b5050505050505050505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b611660838473ffffffffffffffffffffffffffffffffffffffff1663a9059cbb8585604051602401611619929190612703565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506116e7565b505050565b6116e1848573ffffffffffffffffffffffffffffffffffffffff166323b872dd86868660405160240161169a9392919061272a565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506116e7565b50505050565b5f611711828473ffffffffffffffffffffffffffffffffffffffff1661177c90919063ffffffff16565b90505f8151141580156117355750808060200190518101906117339190612794565b155b1561177757826040517f5274afe700000000000000000000000000000000000000000000000000000000815260040161176e9190611d5f565b60405180910390fd5b505050565b606061178983835f611791565b905092915050565b6060814710156117d857306040517fcd7860590000000000000000000000000000000000000000000000000000000081526004016117cf9190611d5f565b60405180910390fd5b5f808573ffffffffffffffffffffffffffffffffffffffff1684866040516118009190611f6d565b5f6040518083038185875af1925050503d805f811461183a576040519150601f19603f3d011682016040523d82523d5f602084013e61183f565b606091505b509150915061184f86838361185a565b925050509392505050565b60608261186f5761186a826118e7565b6118df565b5f825114801561189557505f8473ffffffffffffffffffffffffffffffffffffffff163b145b156118d757836040517f9996b3150000000000000000000000000000000000000000000000000000000081526004016118ce9190611d5f565b60405180910390fd5b8190506118e0565b5b9392505050565b5f815111156118f95780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f80fd5b5f819050919050565b6119418161192f565b811461194b575f80fd5b50565b5f8135905061195c81611938565b92915050565b5f819050919050565b61197481611962565b811461197e575f80fd5b50565b5f8135905061198f8161196b565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6119be82611995565b9050919050565b6119ce816119b4565b81146119d8575f80fd5b50565b5f813590506119e9816119c5565b92915050565b5f805f805f805f60e0888a031215611a0a57611a0961192b565b5b5f611a178a828b0161194e565b9750506020611a288a828b01611981565b9650506040611a398a828b01611981565b9550506060611a4a8a828b016119db565b9450506080611a5b8a828b0161194e565b93505060a0611a6c8a828b0161194e565b92505060c0611a7d8a828b016119db565b91505092959891949750929550565b5f60208284031215611aa157611aa061192b565b5b5f611aae8482850161194e565b91505092915050565b5f7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000082169050919050565b611aeb81611ab7565b82525050565b5f63ffffffff82169050919050565b611b0981611af1565b82525050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b60058110611b4d57611b4c611b0f565b5b50565b5f819050611b5d82611b3c565b919050565b5f611b6c82611b50565b9050919050565b611b7c81611b62565b82525050565b5f608082019050611b955f830187611ae2565b611ba26020830186611b00565b611baf6040830185611b00565b611bbc6060830184611b73565b95945050505050565b611bce81611af1565b8114611bd8575f80fd5b50565b5f81359050611be981611bc5565b92915050565b5f805f805f805f60e0888a031215611c0a57611c0961192b565b5b5f611c178a828b0161194e565b9750506020611c288a828b01611981565b9650506040611c398a828b016119db565b9550506060611c4a8a828b0161194e565b9450506080611c5b8a828b0161194e565b93505060a0611c6c8a828b01611bdb565b92505060c0611c7d8a828b01611bdb565b91505092959891949750929550565b5f805f805f805f805f6101208a8c031215611caa57611ca961192b565b5b5f611cb78c828d0161194e565b9950506020611cc88c828d01611981565b9850506040611cd98c828d01611981565b9750506060611cea8c828d016119db565b9650506080611cfb8c828d016119db565b95505060a0611d0c8c828d0161194e565b94505060c0611d1d8c828d0161194e565b93505060e0611d2e8c828d01611bdb565b925050610100611d408c828d01611bdb565b9150509295985092959850929598565b611d59816119b4565b82525050565b5f602082019050611d725f830184611d50565b92915050565b5f82825260208201905092915050565b7f496e76616c6964207061796d656e742073746174652e204d75737420626520505f8201527f61796d656e7453656e7400000000000000000000000000000000000000000000602082015250565b5f611de2602a83611d78565b9150611ded82611d88565b604082019050919050565b5f6020820190508181035f830152611e0f81611dd6565b9050919050565b5f819050919050565b611e30611e2b82611962565b611e16565b82525050565b5f8160601b9050919050565b5f611e4c82611e36565b9050919050565b5f611e5d82611e42565b9050919050565b611e75611e70826119b4565b611e53565b82525050565b5f819050919050565b611e95611e908261192f565b611e7b565b82525050565b5f611ea6828a611e1f565b602082019150611eb68289611e1f565b602082019150611ec68288611e64565b601482019150611ed68287611e64565b601482019150611ee68286611e84565b602082019150611ef68285611e84565b602082019150611f068284611e64565b60148201915081905098975050505050505050565b5f81519050919050565b5f81905092915050565b8281835e5f83830152505050565b5f611f4782611f1b565b611f518185611f25565b9350611f61818560208601611f2f565b80840191505092915050565b5f611f788284611f3d565b915081905092915050565b7f496e76616c6964207061796d656e7448617368000000000000000000000000005f82015250565b5f611fb7601383611d78565b9150611fc282611f83565b602082019050919050565b5f6020820190508181035f830152611fe481611fab565b9050919050565b611ff48161192f565b82525050565b5f60208201905061200d5f830184611feb565b92915050565b5f61201e8284611e84565b60208201915081905092915050565b5f8151905061203b81611938565b92915050565b5f602082840312156120565761205561192b565b5b5f6120638482850161202d565b91505092915050565b5f60408201905061207f5f830185611feb565b61208c6020830184611feb565b9392505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6120ca82611962565b91506120d583611962565b92508282019050808211156120ed576120ec612093565b5b92915050565b7f496e76616c6964207061796d656e742073746174652e204d75737420626520505f8201527f61796d656e7453656e74206f722054616b6572417070726f7665640000000000602082015250565b5f61214d603b83611d78565b9150612158826120f3565b604082019050919050565b5f6020820190508181035f83015261217a81612141565b9050919050565b7f43757272656e742074696d657374616d70206469646e277420657863656564205f8201527f7061796d656e7420726566756e64206c6f636b2074696d650000000000000000602082015250565b5f6121db603883611d78565b91506121e682612181565b604082019050919050565b5f6020820190508181035f830152612208816121cf565b9050919050565b7f43757272656e742074696d657374616d70206469646e277420657863656564205f8201527f7061796d656e74207072652d617070726f7665206c6f636b2074696d65000000602082015250565b5f612269603d83611d78565b91506122748261220f565b604082019050919050565b5f6020820190508181035f8301526122968161225d565b9050919050565b7f54616b6572207061796d656e7420697320616c726561647920696e697469616c5f8201527f697a656400000000000000000000000000000000000000000000000000000000602082015250565b5f6122f7602483611d78565b91506123028261229d565b604082019050919050565b5f6020820190508181035f830152612324816122eb565b9050919050565b7f5265636569766572206d757374206e6f74206265207a65726f206164647265735f8201527f7300000000000000000000000000000000000000000000000000000000000000602082015250565b5f612385602183611d78565b91506123908261232b565b604082019050919050565b5f6020820190508181035f8301526123b281612379565b9050919050565b7f4554482076616c7565206d7573742062652067726561746572207468616e207a5f8201527f65726f0000000000000000000000000000000000000000000000000000000000602082015250565b5f612413602383611d78565b915061241e826123b9565b604082019050919050565b5f6020820190508181035f83015261244081612407565b9050919050565b7f4554482076616c7565206d7573742062652067726561746572207468616e20645f8201527f6578206665650000000000000000000000000000000000000000000000000000602082015250565b5f6124a1602683611d78565b91506124ac82612447565b604082019050919050565b5f6020820190508181035f8301526124ce81612495565b9050919050565b5f6124df82611962565b91506124ea83611962565b925082820390508181111561250257612501612093565b5b92915050565b7f496e76616c6964207061796d656e742073746174652e204d75737420626520545f8201527f616b6572417070726f7665640000000000000000000000000000000000000000602082015250565b5f612562602c83611d78565b915061256d82612508565b604082019050919050565b5f6020820190508181035f83015261258f81612556565b9050919050565b7f4552433230207632207061796d656e7420697320616c726561647920696e69745f8201527f69616c697a656400000000000000000000000000000000000000000000000000602082015250565b5f6125f0602783611d78565b91506125fb82612596565b604082019050919050565b5f6020820190508181035f83015261261d816125e4565b9050919050565b7f416d6f756e74206d757374206e6f74206265207a65726f0000000000000000005f82015250565b5f612658601783611d78565b915061266382612624565b602082019050919050565b5f6020820190508181035f8301526126858161264c565b9050919050565b7f44657820666565206d757374206e6f74206265207a65726f00000000000000005f82015250565b5f6126c0601883611d78565b91506126cb8261268c565b602082019050919050565b5f6020820190508181035f8301526126ed816126b4565b9050919050565b6126fd81611962565b82525050565b5f6040820190506127165f830185611d50565b61272360208301846126f4565b9392505050565b5f60608201905061273d5f830186611d50565b61274a6020830185611d50565b61275760408301846126f4565b949350505050565b5f8115159050919050565b6127738161275f565b811461277d575f80fd5b50565b5f8151905061278e8161276a565b92915050565b5f602082840312156127a9576127a861192b565b5b5f6127b684828501612780565b9150509291505056fea2646970667358221220fd2a736cddd6ebbd2831b0e65fb5470ffebd789dc32c1d3782ce69362e16fae564736f6c63430008190033 \ No newline at end of file +6080604052348015600e575f80fd5b50612ffd8061001c5f395ff3fe608060405234801561000f575f80fd5b50600436106100a7575f3560e01c8063b27e46fb1161006f578063b27e46fb1461015f578063bc197c811461017b578063c8d9009b146101ab578063c92cd12d146101c7578063efccb9eb146101e3578063f23a6e6114610215576100a7565b806301ffc9a7146100ab57806305ec158d146100db5780630f235fce146100f7578063150b7a02146101135780636e6bf6d214610143575b5f80fd5b6100c560048036038101906100c09190611ebc565b610245565b6040516100d29190611f01565b60405180910390f35b6100f560048036038101906100f09190611fda565b610326565b005b610111600480360381019061010c9190612077565b6105e6565b005b61012d60048036038101906101289190612161565b6108a0565b60405161013a91906121f4565b60405180910390f35b61015d60048036038101906101589190612077565b610cef565b005b61017960048036038101906101749190611fda565b610faa565b005b61019560048036038101906101909190612262565b611269565b6040516101a291906121f4565b60405180910390f35b6101c560048036038101906101c09190612077565b6112a5565b005b6101e160048036038101906101dc9190611fda565b6115ce565b005b6101fd60048036038101906101f89190612339565b6118fc565b60405161020c9392919061242f565b60405180910390f35b61022f600480360381019061022a9190612464565b611948565b60405161023c91906121f4565b60405180910390f35b5f7f4e2312e0000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061030f57507f150b7a02000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061031f575061031e82611ddc565b5b9050919050565b6001600381111561033a576103396123bc565b5b5f808981526020019081526020015f205f0160189054906101000a900460ff16600381111561036c5761036b6123bc565b5b146103ac576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103a39061257a565b60405180910390fd5b5f600387336002896040516020016103c491906125b8565b6040516020818303038152906040526040516103e09190612624565b602060405180830381855afa1580156103fb573d5f803e3d5ffd5b5050506040513d601f19601f8201168201806040525081019061041e919061264e565b8888888860405160200161043897969594939291906126de565b6040516020818303038152906040526040516104549190612624565b602060405180830381855afa15801561046f573d5f803e3d5ffd5b5050506040515160601b90505f808981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff1916146104f9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104f0906127a8565b60405180910390fd5b60035f808a81526020019081526020015f205f0160186101000a81548160ff0219169083600381111561052f5761052e6123bc565b5b02179055507fac509cdcc7ddb189f81fff6f4824f5c95076e64c3bdce542c50feaa6779afd738860405161056391906127d5565b60405180910390a15f8490508073ffffffffffffffffffffffffffffffffffffffff1663f242432a303387876040518563ffffffff1660e01b81526004016105ae949392919061283f565b5f604051808303815f87803b1580156105c5575f80fd5b505af11580156105d7573d5f803e3d5ffd5b50505050505050505050505050565b600160038111156105fa576105f96123bc565b5b5f808881526020019081526020015f205f0160189054906101000a900460ff16600381111561062c5761062b6123bc565b5b1461066c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106639061257a565b60405180910390fd5b5f600386338787878760405160200161068a96959493929190612895565b6040516020818303038152906040526040516106a69190612624565b602060405180830381855afa1580156106c1573d5f803e3d5ffd5b5050506040515160601b90505f808881526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff19161461074b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610742906127a8565b60405180910390fd5b5f808881526020019081526020015f205f0160149054906101000a900463ffffffff1663ffffffff164210156107b6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107ad90612974565b60405180910390fd5b60035f808981526020019081526020015f205f0160186101000a81548160ff021916908360038111156107ec576107eb6123bc565b5b02179055507f5dedc4f52b757d9112d09ca0b2f022927104d54e3f54da091587e8ad192190728760405161082091906127d5565b60405180910390a15f8390508073ffffffffffffffffffffffffffffffffffffffff166342842e0e3033866040518463ffffffff1660e01b815260040161086993929190612992565b5f604051808303815f87803b158015610880575f80fd5b505af1158015610892573d5f803e3d5ffd5b505050505050505050505050565b5f8083838101906108b19190612b1a565b90505f60038111156108c6576108c56123bc565b5b5f80835f015181526020019081526020015f205f0160189054906101000a900460ff1660038111156108fb576108fa6123bc565b5b1461093b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161093290612bb5565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff16816020015173ffffffffffffffffffffffffffffffffffffffff16036109ad576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109a490612c1d565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff16816040015173ffffffffffffffffffffffffffffffffffffffff1603610a1f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a1690612c85565b60405180910390fd5b806040015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610a91576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a8890612d13565b60405180910390fd5b8573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1614610aff576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610af690612d7b565b60405180910390fd5b610b0c8160200151611e45565b15610b4c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b4390612de3565b60405180910390fd5b5f60038260200151888460600151856080015186604001518b604051602001610b7a96959493929190612895565b604051602081830303815290604052604051610b969190612624565b602060405180830381855afa158015610bb1573d5f803e3d5ffd5b5050506040515160601b90506040518060600160405280826bffffffffffffffffffffffff191681526020018360a0015163ffffffff16815260200160016003811115610c0157610c006123bc565b5b8152505f80845f015181526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548163ffffffff021916908363ffffffff1602179055506040820151815f0160186101000a81548160ff02191690836003811115610c9857610c976123bc565b5b02179055509050507ff1dc11bbb6d7542c4267ecf1d370ff4c7092518633ecae9939e8488f4e53d2ad825f0151604051610cd291906127d5565b60405180910390a163150b7a0260e01b9250505095945050505050565b60016003811115610d0357610d026123bc565b5b5f808881526020019081526020015f205f0160189054906101000a900460ff166003811115610d3557610d346123bc565b5b14610d75576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d6c9061257a565b60405180910390fd5b5f60038633600288604051602001610d8d91906125b8565b604051602081830303815290604052604051610da99190612624565b602060405180830381855afa158015610dc4573d5f803e3d5ffd5b5050506040513d601f19601f82011682018060405250810190610de7919061264e565b878787604051602001610dff96959493929190612895565b604051602081830303815290604052604051610e1b9190612624565b602060405180830381855afa158015610e36573d5f803e3d5ffd5b5050506040515160601b90505f808881526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614610ec0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610eb7906127a8565b60405180910390fd5b60035f808981526020019081526020015f205f0160186101000a81548160ff02191690836003811115610ef657610ef56123bc565b5b02179055507fac509cdcc7ddb189f81fff6f4824f5c95076e64c3bdce542c50feaa6779afd7387604051610f2a91906127d5565b60405180910390a15f8390508073ffffffffffffffffffffffffffffffffffffffff166342842e0e3033866040518463ffffffff1660e01b8152600401610f7393929190612992565b5f604051808303815f87803b158015610f8a575f80fd5b505af1158015610f9c573d5f803e3d5ffd5b505050505050505050505050565b60016003811115610fbe57610fbd6123bc565b5b5f808981526020019081526020015f205f0160189054906101000a900460ff166003811115610ff057610fef6123bc565b5b14611030576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110279061257a565b60405180910390fd5b5f60038733888888888860405160200161105097969594939291906126de565b60405160208183030381529060405260405161106c9190612624565b602060405180830381855afa158015611087573d5f803e3d5ffd5b5050506040515160601b90505f808981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff191614611111576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611108906127a8565b60405180910390fd5b5f808981526020019081526020015f205f0160149054906101000a900463ffffffff1663ffffffff1642101561117c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161117390612974565b60405180910390fd5b60035f808a81526020019081526020015f205f0160186101000a81548160ff021916908360038111156111b2576111b16123bc565b5b02179055507f5dedc4f52b757d9112d09ca0b2f022927104d54e3f54da091587e8ad19219072886040516111e691906127d5565b60405180910390a15f8490508073ffffffffffffffffffffffffffffffffffffffff1663f242432a303387876040518563ffffffff1660e01b8152600401611231949392919061283f565b5f604051808303815f87803b158015611248575f80fd5b505af115801561125a573d5f803e3d5ffd5b50505050505050505050505050565b5f6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161129c90612e4b565b60405180910390fd5b600160038111156112b9576112b86123bc565b5b5f808881526020019081526020015f205f0160189054906101000a900460ff1660038111156112eb576112ea6123bc565b5b1461132b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113229061257a565b60405180910390fd5b3273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611399576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161139090612eb3565b60405180910390fd5b5f60033387876002886040516020016113b291906125b8565b6040516020818303038152906040526040516113ce9190612624565b602060405180830381855afa1580156113e9573d5f803e3d5ffd5b5050506040513d601f19601f8201168201806040525081019061140c919061264e565b878760405160200161142396959493929190612895565b60405160208183030381529060405260405161143f9190612624565b602060405180830381855afa15801561145a573d5f803e3d5ffd5b5050506040515160601b90505f808881526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff1916146114e4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114db906127a8565b60405180910390fd5b60025f808981526020019081526020015f205f0160186101000a81548160ff0219169083600381111561151a576115196123bc565b5b02179055507fad62ed075fe8969df63026f45152d6e996a0697a736a8de92ee85ae9c9958cf08760405161154e91906127d5565b60405180910390a15f8390508073ffffffffffffffffffffffffffffffffffffffff166342842e0e3033866040518463ffffffff1660e01b815260040161159793929190612992565b5f604051808303815f87803b1580156115ae575f80fd5b505af11580156115c0573d5f803e3d5ffd5b505050505050505050505050565b600160038111156115e2576115e16123bc565b5b5f808981526020019081526020015f205f0160189054906101000a900460ff166003811115611614576116136123bc565b5b14611654576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161164b9061257a565b60405180910390fd5b3273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146116c2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116b990612eb3565b60405180910390fd5b5f60033388886002896040516020016116db91906125b8565b6040516020818303038152906040526040516116f79190612624565b602060405180830381855afa158015611712573d5f803e3d5ffd5b5050506040513d601f19601f82011682018060405250810190611735919061264e565b88888860405160200161174e97969594939291906126de565b60405160208183030381529060405260405161176a9190612624565b602060405180830381855afa158015611785573d5f803e3d5ffd5b5050506040515160601b90505f808981526020019081526020015f205f015f9054906101000a900460601b6bffffffffffffffffffffffff1916816bffffffffffffffffffffffff19161461180f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611806906127a8565b60405180910390fd5b60025f808a81526020019081526020015f205f0160186101000a81548160ff02191690836003811115611845576118446123bc565b5b02179055507fad62ed075fe8969df63026f45152d6e996a0697a736a8de92ee85ae9c9958cf08860405161187991906127d5565b60405180910390a15f8490508073ffffffffffffffffffffffffffffffffffffffff1663f242432a303387876040518563ffffffff1660e01b81526004016118c4949392919061283f565b5f604051808303815f87803b1580156118db575f80fd5b505af11580156118ed573d5f803e3d5ffd5b50505050505050505050505050565b5f602052805f5260405f205f91509050805f015f9054906101000a900460601b90805f0160149054906101000a900463ffffffff1690805f0160189054906101000a900460ff16905083565b5f8083838101906119599190612b1a565b90505f600381111561196e5761196d6123bc565b5b5f80835f015181526020019081526020015f205f0160189054906101000a900460ff1660038111156119a3576119a26123bc565b5b146119e3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119da90612f41565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff16816020015173ffffffffffffffffffffffffffffffffffffffff1603611a55576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611a4c90612c1d565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff16816040015173ffffffffffffffffffffffffffffffffffffffff1603611ac7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611abe90612c85565b60405180910390fd5b806040015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611b39576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b3090612d13565b60405180910390fd5b8673ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614611ba7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b9e90612d7b565b60405180910390fd5b5f8511611be9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611be090612fa9565b60405180910390fd5b611bf68160200151611e45565b15611c36576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c2d90612de3565b60405180910390fd5b5f60038260200151898460600151856080015186604001518c8c604051602001611c6697969594939291906126de565b604051602081830303815290604052604051611c829190612624565b602060405180830381855afa158015611c9d573d5f803e3d5ffd5b5050506040515160601b90506040518060600160405280826bffffffffffffffffffffffff191681526020018360a0015163ffffffff16815260200160016003811115611ced57611cec6123bc565b5b8152505f80845f015181526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908360601c02179055506020820151815f0160146101000a81548163ffffffff021916908363ffffffff1602179055506040820151815f0160186101000a81548160ff02191690836003811115611d8457611d836123bc565b5b02179055509050507ff1dc11bbb6d7542c4267ecf1d370ff4c7092518633ecae9939e8488f4e53d2ad825f0151604051611dbe91906127d5565b60405180910390a163f23a6e6160e01b925050509695505050505050565b5f7f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b5f80823b90505f8111915050919050565b5f604051905090565b5f80fd5b5f80fd5b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b611e9b81611e67565b8114611ea5575f80fd5b50565b5f81359050611eb681611e92565b92915050565b5f60208284031215611ed157611ed0611e5f565b5b5f611ede84828501611ea8565b91505092915050565b5f8115159050919050565b611efb81611ee7565b82525050565b5f602082019050611f145f830184611ef2565b92915050565b5f819050919050565b611f2c81611f1a565b8114611f36575f80fd5b50565b5f81359050611f4781611f23565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f611f7682611f4d565b9050919050565b611f8681611f6c565b8114611f90575f80fd5b50565b5f81359050611fa181611f7d565b92915050565b5f819050919050565b611fb981611fa7565b8114611fc3575f80fd5b50565b5f81359050611fd481611fb0565b92915050565b5f805f805f805f60e0888a031215611ff557611ff4611e5f565b5b5f6120028a828b01611f39565b97505060206120138a828b01611f93565b96505060406120248a828b01611f39565b95505060606120358a828b01611f39565b94505060806120468a828b01611f93565b93505060a06120578a828b01611fc6565b92505060c06120688a828b01611fc6565b91505092959891949750929550565b5f805f805f8060c0878903121561209157612090611e5f565b5b5f61209e89828a01611f39565b96505060206120af89828a01611f93565b95505060406120c089828a01611f39565b94505060606120d189828a01611f39565b93505060806120e289828a01611f93565b92505060a06120f389828a01611fc6565b9150509295509295509295565b5f80fd5b5f80fd5b5f80fd5b5f8083601f84011261212157612120612100565b5b8235905067ffffffffffffffff81111561213e5761213d612104565b5b60208301915083600182028301111561215a57612159612108565b5b9250929050565b5f805f805f6080868803121561217a57612179611e5f565b5b5f61218788828901611f93565b955050602061219888828901611f93565b94505060406121a988828901611fc6565b935050606086013567ffffffffffffffff8111156121ca576121c9611e63565b5b6121d68882890161210c565b92509250509295509295909350565b6121ee81611e67565b82525050565b5f6020820190506122075f8301846121e5565b92915050565b5f8083601f84011261222257612221612100565b5b8235905067ffffffffffffffff81111561223f5761223e612104565b5b60208301915083602082028301111561225b5761225a612108565b5b9250929050565b5f805f805f805f8060a0898b03121561227e5761227d611e5f565b5b5f61228b8b828c01611f93565b985050602061229c8b828c01611f93565b975050604089013567ffffffffffffffff8111156122bd576122bc611e63565b5b6122c98b828c0161220d565b9650965050606089013567ffffffffffffffff8111156122ec576122eb611e63565b5b6122f88b828c0161220d565b9450945050608089013567ffffffffffffffff81111561231b5761231a611e63565b5b6123278b828c0161210c565b92509250509295985092959890939650565b5f6020828403121561234e5761234d611e5f565b5b5f61235b84828501611f39565b91505092915050565b5f7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000082169050919050565b61239881612364565b82525050565b5f63ffffffff82169050919050565b6123b68161239e565b82525050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b600481106123fa576123f96123bc565b5b50565b5f81905061240a826123e9565b919050565b5f612419826123fd565b9050919050565b6124298161240f565b82525050565b5f6060820190506124425f83018661238f565b61244f60208301856123ad565b61245c6040830184612420565b949350505050565b5f805f805f8060a0878903121561247e5761247d611e5f565b5b5f61248b89828a01611f93565b965050602061249c89828a01611f93565b95505060406124ad89828a01611fc6565b94505060606124be89828a01611fc6565b935050608087013567ffffffffffffffff8111156124df576124de611e63565b5b6124eb89828a0161210c565b92509250509295509295509295565b5f82825260208201905092915050565b7f496e76616c6964207061796d656e742073746174652e204d75737420626520505f8201527f61796d656e7453656e7400000000000000000000000000000000000000000000602082015250565b5f612564602a836124fa565b915061256f8261250a565b604082019050919050565b5f6020820190508181035f83015261259181612558565b9050919050565b5f819050919050565b6125b26125ad82611f1a565b612598565b82525050565b5f6125c382846125a1565b60208201915081905092915050565b5f81519050919050565b5f81905092915050565b8281835e5f83830152505050565b5f6125fe826125d2565b61260881856125dc565b93506126188185602086016125e6565b80840191505092915050565b5f61262f82846125f4565b915081905092915050565b5f8151905061264881611f23565b92915050565b5f6020828403121561266357612662611e5f565b5b5f6126708482850161263a565b91505092915050565b5f8160601b9050919050565b5f61268f82612679565b9050919050565b5f6126a082612685565b9050919050565b6126b86126b382611f6c565b612696565b82525050565b5f819050919050565b6126d86126d382611fa7565b6126be565b82525050565b5f6126e9828a6126a7565b6014820191506126f982896126a7565b60148201915061270982886125a1565b60208201915061271982876125a1565b60208201915061272982866126a7565b60148201915061273982856126c7565b60208201915061274982846126c7565b60208201915081905098975050505050505050565b7f496e76616c6964207061796d656e7448617368000000000000000000000000005f82015250565b5f6127926013836124fa565b915061279d8261275e565b602082019050919050565b5f6020820190508181035f8301526127bf81612786565b9050919050565b6127cf81611f1a565b82525050565b5f6020820190506127e85f8301846127c6565b92915050565b6127f781611f6c565b82525050565b61280681611fa7565b82525050565b5f82825260208201905092915050565b50565b5f61282a5f8361280c565b91506128358261281c565b5f82019050919050565b5f60a0820190506128525f8301876127ee565b61285f60208301866127ee565b61286c60408301856127fd565b61287960608301846127fd565b818103608083015261288a8161281f565b905095945050505050565b5f6128a082896126a7565b6014820191506128b082886126a7565b6014820191506128c082876125a1565b6020820191506128d082866125a1565b6020820191506128e082856126a7565b6014820191506128f082846126c7565b602082019150819050979650505050505050565b7f43757272656e742074696d657374616d70206469646e277420657863656564205f8201527f7061796d656e7420726566756e64206c6f636b2074696d650000000000000000602082015250565b5f61295e6038836124fa565b915061296982612904565b604082019050919050565b5f6020820190508181035f83015261298b81612952565b9050919050565b5f6060820190506129a55f8301866127ee565b6129b260208301856127ee565b6129bf60408301846127fd565b949350505050565b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b612a11826129cb565b810181811067ffffffffffffffff82111715612a3057612a2f6129db565b5b80604052505050565b5f612a42611e56565b9050612a4e8282612a08565b919050565b612a5c8161239e565b8114612a66575f80fd5b50565b5f81359050612a7781612a53565b92915050565b5f60c08284031215612a9257612a916129c7565b5b612a9c60c0612a39565b90505f612aab84828501611f39565b5f830152506020612abe84828501611f93565b6020830152506040612ad284828501611f93565b6040830152506060612ae684828501611f39565b6060830152506080612afa84828501611f39565b60808301525060a0612b0e84828501612a69565b60a08301525092915050565b5f60c08284031215612b2f57612b2e611e5f565b5b5f612b3c84828501612a7d565b91505092915050565b7f4d616b657220455243373231207061796d656e74206d75737420626520556e695f8201527f6e697469616c697a656400000000000000000000000000000000000000000000602082015250565b5f612b9f602a836124fa565b9150612baa82612b45565b604082019050919050565b5f6020820190508181035f830152612bcc81612b93565b9050919050565b7f54616b6572206d757374206e6f74206265207a65726f206164647265737300005f82015250565b5f612c07601e836124fa565b9150612c1282612bd3565b602082019050919050565b5f6020820190508181035f830152612c3481612bfb565b9050919050565b7f546f6b656e206d757374206e6f74206265207a65726f206164647265737300005f82015250565b5f612c6f601e836124fa565b9150612c7a82612c3b565b602082019050919050565b5f6020820190508181035f830152612c9c81612c63565b9050919050565b7f546f6b656e206164647265737320646f6573206e6f74206d617463682073656e5f8201527f6465720000000000000000000000000000000000000000000000000000000000602082015250565b5f612cfd6023836124fa565b9150612d0882612ca3565b604082019050919050565b5f6020820190508181035f830152612d2a81612cf1565b9050919050565b7f4f70657261746f72206d757374206265207468652073656e64657200000000005f82015250565b5f612d65601b836124fa565b9150612d7082612d31565b602082019050919050565b5f6020820190508181035f830152612d9281612d59565b9050919050565b7f54616b65722063616e6e6f74206265206120636f6e74726163740000000000005f82015250565b5f612dcd601a836124fa565b9150612dd882612d99565b602082019050919050565b5f6020820190508181035f830152612dfa81612dc1565b9050919050565b7f4261746368207472616e7366657273206e6f7420737570706f727465640000005f82015250565b5f612e35601d836124fa565b9150612e4082612e01565b602082019050919050565b5f6020820190508181035f830152612e6281612e29565b9050919050565b7f43616c6c6572206d75737420626520616e20454f4100000000000000000000005f82015250565b5f612e9d6015836124fa565b9150612ea882612e69565b602082019050919050565b5f6020820190508181035f830152612eca81612e91565b9050919050565b7f4d616b65722045524331313535207061796d656e74206d75737420626520556e5f8201527f696e697469616c697a6564000000000000000000000000000000000000000000602082015250565b5f612f2b602b836124fa565b9150612f3682612ed1565b604082019050919050565b5f6020820190508181035f830152612f5881612f1f565b9050919050565b7f56616c7565206d7573742062652067726561746572207468616e2030000000005f82015250565b5f612f93601c836124fa565b9150612f9e82612f5f565b602082019050919050565b5f6020820190508181035f830152612fc081612f87565b905091905056fea26469706673582212208adfd9bc3010e8e9bf1d503f3e439ea5632ee575d696af59d80e0a2268d48d7e64736f6c634300081a0033 \ No newline at end of file diff --git a/mm2src/mm2_test_helpers/src/for_tests.rs b/mm2src/mm2_test_helpers/src/for_tests.rs index e650ef4293..57084a72e2 100644 --- a/mm2src/mm2_test_helpers/src/for_tests.rs +++ b/mm2src/mm2_test_helpers/src/for_tests.rs @@ -828,6 +828,13 @@ pub fn erc20_dev_conf(contract_address: &str) -> Json { }) } +/// ERC20 token configuration used for dockerized tests on Sepolia +pub fn sepolia_erc20_dev_conf(contract_address: &str) -> Json { + let mut conf = erc20_dev_conf(contract_address); + set_chain_id(&mut conf, ETH_SEPOLIA_CHAIN_ID); + conf +} + /// global NFT configuration used for dockerized Geth dev node pub fn nft_dev_conf() -> Json { json!({ @@ -846,21 +853,8 @@ pub fn nft_dev_conf() -> Json { }) } -/// global NFT configuration used for Sepolia testnet -pub fn nft_sepolia_conf() -> Json { - json!({ - "coin": "NFT_ETH", - "name": "nftdev", - "chain_id": 11155111, - "mm2": 1, - "derivation_path": "m/44'/60'", - "protocol": { - "type": "NFT", - "protocol_data": { - "platform": "ETH" - } - } - }) +fn set_chain_id(conf: &mut Json, chain_id: u64) { + conf["chain_id"] = json!(chain_id); } pub fn eth_sepolia_conf() -> Json { @@ -868,7 +862,7 @@ pub fn eth_sepolia_conf() -> Json { "coin": "ETH", "name": "ethereum", "derivation_path": "m/44'/60'", - "chain_id": 11155111, + "chain_id": ETH_SEPOLIA_CHAIN_ID, "protocol": { "type": "ETH" }, @@ -882,7 +876,7 @@ pub fn eth_sepolia_trezor_firmware_compat_conf() -> Json { "coin": "tETH", "name": "ethereum", "derivation_path": "m/44'/1'", // Note: trezor uses coin type 1' for eth for testnet (SLIP44_TESTNET) - "chain_id": 11155111, + "chain_id": ETH_SEPOLIA_CHAIN_ID, "protocol": { "type": "ETH" }, @@ -912,12 +906,12 @@ pub fn jst_sepolia_conf() -> Json { json!({ "coin": "JST", "name": "jst", - "chain_id": 11155111, + "chain_id": ETH_SEPOLIA_CHAIN_ID, "protocol": { "type": "ERC20", "protocol_data": { "platform": "ETH", - "chain_id": 11155111, + "chain_id": ETH_SEPOLIA_CHAIN_ID, "contract_address": ETH_SEPOLIA_TOKEN_CONTRACT } }, @@ -929,14 +923,14 @@ pub fn jst_sepolia_trezor_conf() -> Json { json!({ "coin": "tJST", "name": "tjst", - "chain_id": 11155111, + "chain_id": ETH_SEPOLIA_CHAIN_ID, "derivation_path": "m/44'/1'", // Note: Trezor uses 1' coin type for all testnets "trezor_coin": "tETH", "protocol": { "type": "ERC20", "protocol_data": { "platform": "ETH", - "chain_id": 11155111, + "chain_id": ETH_SEPOLIA_CHAIN_ID, "contract_address": ETH_SEPOLIA_TOKEN_CONTRACT } } diff --git a/mm2src/proxy_signature/src/lib.rs b/mm2src/proxy_signature/src/lib.rs index 86f57a3431..f4ae2393a0 100644 --- a/mm2src/proxy_signature/src/lib.rs +++ b/mm2src/proxy_signature/src/lib.rs @@ -74,7 +74,9 @@ impl ProxySign { return false; } - let Ok(public_key) = PublicKey::try_decode_protobuf(&self.raw_message.public_key_encoded) else { return false }; + let Ok(public_key) = PublicKey::try_decode_protobuf(&self.raw_message.public_key_encoded) else { + return false; + }; if self.address != public_key.to_peer_id().to_string() { return false; From 805ed93f797390d19343446f2e7abea6d9c3f6d3 Mon Sep 17 00:00:00 2001 From: Alrighttt <36680730+Alrighttt@users.noreply.github.com> Date: Fri, 30 Aug 2024 07:09:19 -0400 Subject: [PATCH 06/29] feat(sia): extract sia lib to external repo (#2167) This comit removes any sia lib related code from kdf and uses sia-rust repo as a dependency instead. `my_balance` is also implemented for siacoin in this commit. --- Cargo.lock | 28 ++ mm2src/coins/Cargo.toml | 9 +- mm2src/coins/hd_wallet/pubkey.rs | 9 + mm2src/coins/lp_coins.rs | 8 +- mm2src/coins/sia/address.rs | 167 --------- mm2src/coins/sia/blake2b_internal.rs | 326 ------------------ mm2src/coins/sia/encoding.rs | 119 ------- mm2src/coins/sia/http_client.rs | 208 ----------- mm2src/coins/sia/spend_policy.rs | 322 ----------------- mm2src/coins/{sia.rs => siacoin.rs} | 109 ++++-- mm2src/coins/siacoin/sia_hd_wallet.rs | 44 +++ mm2src/coins_activation/src/prelude.rs | 2 +- .../src/sia_coin_activation.rs | 4 +- mm2src/mm2_main/Cargo.toml | 6 +- .../mm2_main/src/rpc/dispatcher/dispatcher.rs | 2 +- .../tests/docker_tests/docker_tests_common.rs | 20 ++ mm2src/mm2_main/tests/docker_tests/mod.rs | 1 + .../tests/docker_tests/sia_docker_tests.rs | 118 +++++++ mm2src/mm2_main/tests/docker_tests_main.rs | 1 + .../mm2_main/tests/docker_tests_sia_unique.rs | 106 ++++++ 20 files changed, 424 insertions(+), 1185 deletions(-) delete mode 100644 mm2src/coins/sia/address.rs delete mode 100644 mm2src/coins/sia/blake2b_internal.rs delete mode 100644 mm2src/coins/sia/encoding.rs delete mode 100644 mm2src/coins/sia/http_client.rs delete mode 100644 mm2src/coins/sia/spend_policy.rs rename mm2src/coins/{sia.rs => siacoin.rs} (84%) create mode 100644 mm2src/coins/siacoin/sia_hd_wallet.rs create mode 100644 mm2src/mm2_main/tests/docker_tests/sia_docker_tests.rs create mode 100644 mm2src/mm2_main/tests/docker_tests_sia_unique.rs diff --git a/Cargo.lock b/Cargo.lock index 681184de65..431959e5c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1026,6 +1026,7 @@ dependencies = [ "bytes 0.4.12", "cfg-if 1.0.0", "chain", + "chrono", "common", "cosmrs", "crossbeam 0.8.2", @@ -1077,6 +1078,7 @@ dependencies = [ "mm2_state_machine", "mm2_test_helpers", "mocktopus", + "nom", "num-traits", "parking_lot 0.12.0", "primitives", @@ -1102,10 +1104,12 @@ dependencies = [ "serde", "serde_derive", "serde_json", + "serde_with", "serialization", "serialization_derive", "sha2 0.10.7", "sha3 0.9.1", + "sia-rust", "solana-client", "solana-sdk", "solana-transaction-status", @@ -2019,6 +2023,7 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369" dependencies = [ + "serde", "signature 1.4.0", ] @@ -2055,6 +2060,7 @@ dependencies = [ "ed25519 1.5.2", "rand 0.7.3", "serde", + "serde_bytes", "sha2 0.9.9", "zeroize", ] @@ -4617,6 +4623,7 @@ dependencies = [ "serde_json", "serialization", "serialization_derive", + "sia-rust", "sp-runtime-interface", "sp-trie", "spv_validation", @@ -4624,6 +4631,7 @@ dependencies = [ "tokio", "trie-db", "trie-root 0.16.0", + "url", "uuid 1.2.2", "wasm-bindgen", "wasm-bindgen-futures", @@ -7011,6 +7019,26 @@ dependencies = [ "log", ] +[[package]] +name = "sia-rust" +version = "0.1.0" +source = "git+https://github.com/KomodoPlatform/sia-rust?rev=9f188b80b3213bcb604e7619275251ce08fae808#9f188b80b3213bcb604e7619275251ce08fae808" +dependencies = [ + "base64 0.21.7", + "blake2b_simd", + "chrono", + "derive_more", + "ed25519-dalek", + "hex", + "nom", + "reqwest", + "rustc-hex", + "serde", + "serde_json", + "serde_with", + "url", +] + [[package]] name = "signal-hook-registry" version = "1.4.0" diff --git a/mm2src/coins/Cargo.toml b/mm2src/coins/Cargo.toml index 3f37c3d74e..c5eeb570ea 100644 --- a/mm2src/coins/Cargo.toml +++ b/mm2src/coins/Cargo.toml @@ -18,7 +18,8 @@ enable-solana = [ ] enable-sia = [ "dep:reqwest", - "blake2b_simd" + "dep:blake2b_simd", + "dep:sia-rust" ] default = [] run-docker-tests = [] @@ -41,13 +42,14 @@ byteorder = "1.3" bytes = "0.4" cfg-if = "1.0" chain = { path = "../mm2_bitcoin/chain" } +chrono = { version = "0.4.23", "features" = ["serde"] } common = { path = "../common" } cosmrs = { version = "0.14.0", default-features = false } crossbeam = "0.8" crypto = { path = "../crypto" } db_common = { path = "../db_common" } derive_more = "0.99" -ed25519-dalek = "1.0.1" +ed25519-dalek = { version = "1.0.1", features = ["serde"] } enum_derives = { path = "../derives/enum_derives" } ethabi = { version = "17.0.0" } ethcore-transaction = { git = "https://github.com/KomodoPlatform/mm2-parity-ethereum.git", rev = "mm2-v2.1.1" } @@ -69,6 +71,7 @@ jsonrpc-core = "18.0.0" keys = { path = "../mm2_bitcoin/keys" } lazy_static = "1.4" libc = "0.2" +nom = "6.1.2" mm2_core = { path = "../mm2_core" } mm2_err_handle = { path = "../mm2_err_handle" } mm2_event_stream = { path = "../mm2_event_stream" } @@ -101,8 +104,10 @@ ser_error_derive = { path = "../derives/ser_error_derive" } serde = "1.0" serde_derive = "1.0" serde_json = { version = "1", features = ["preserve_order", "raw_value"] } +serde_with = "1.14.0" serialization = { path = "../mm2_bitcoin/serialization" } serialization_derive = { path = "../mm2_bitcoin/serialization_derive" } +sia-rust = { git = "https://github.com/KomodoPlatform/sia-rust", rev = "9f188b80b3213bcb604e7619275251ce08fae808", optional = true } spv_validation = { path = "../mm2_bitcoin/spv_validation" } sha2 = "0.10" sha3 = "0.9" diff --git a/mm2src/coins/hd_wallet/pubkey.rs b/mm2src/coins/hd_wallet/pubkey.rs index 7732819295..7babb12bd5 100644 --- a/mm2src/coins/hd_wallet/pubkey.rs +++ b/mm2src/coins/hd_wallet/pubkey.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "enable-sia")] +use crate::siacoin::sia_hd_wallet::Ed25519ExtendedPublicKey; use crate::CoinProtocol; use super::*; @@ -30,6 +32,13 @@ impl ExtendedPublicKeyOps for Secp256k1ExtendedPublicKey { fn to_string(&self, prefix: Prefix) -> String { self.to_string(prefix) } } +#[cfg(feature = "enable-sia")] +impl ExtendedPublicKeyOps for Ed25519ExtendedPublicKey { + fn derive_child(&self, child_number: ChildNumber) -> Result { self.derive_child(child_number) } + + fn to_string(&self, prefix: Prefix) -> String { self.to_string(prefix) } +} + /// This trait should be implemented for coins /// to support extracting extended public keys from any depth. /// The extraction can be from either an internal or external wallet. diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 8ca714a34f..1af6027e30 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -240,7 +240,7 @@ use coin_errors::{MyAddressError, ValidatePaymentError, ValidatePaymentFut, Vali pub mod coins_tests; pub mod eth; -use eth::eth_swap_v2::{PaymentStatusErr, ValidatePaymentV2Err}; +use eth::eth_swap_v2::{PaymentStatusErr, PrepareTxDataError, ValidatePaymentV2Err}; use eth::GetValidEthWithdrawAddError; use eth::{eth_coin_from_conf_and_request, get_eth_address, EthCoin, EthGasDetailsErr, EthTxFeeDetails, GetEthAddressError, SignedEthTx}; @@ -277,6 +277,9 @@ pub use test_coin::TestCoin; pub mod tx_history_storage; +#[cfg(feature = "enable-sia")] pub mod siacoin; +#[cfg(feature = "enable-sia")] use siacoin::SiaCoin; + #[doc(hidden)] #[allow(unused_variables)] #[cfg(all( @@ -319,9 +322,6 @@ use script::Script; pub mod z_coin; use crate::coin_balance::{BalanceObjectOps, HDWalletBalanceObject}; use z_coin::{ZCoin, ZcoinProtocolInfo}; -#[cfg(feature = "enable-sia")] pub mod sia; -use crate::eth::eth_swap_v2::PrepareTxDataError; -#[cfg(feature = "enable-sia")] use sia::SiaCoin; pub type TransactionFut = Box + Send>; pub type TransactionResult = Result; diff --git a/mm2src/coins/sia/address.rs b/mm2src/coins/sia/address.rs deleted file mode 100644 index 5218a08bc8..0000000000 --- a/mm2src/coins/sia/address.rs +++ /dev/null @@ -1,167 +0,0 @@ -use crate::sia::blake2b_internal::standard_unlock_hash; -use blake2b_simd::Params; -use ed25519_dalek::PublicKey; -use hex::FromHexError; -use rpc::v1::types::H256; -use serde::{Deserialize, Serialize}; -use std::convert::TryInto; -use std::fmt; -use std::str::FromStr; - -// TODO this could probably include the checksum within the data type -// generating the checksum on the fly is how Sia Go does this however -#[derive(Debug, Clone, PartialEq)] -pub struct Address(pub H256); - -impl Address { - pub fn str_without_prefix(&self) -> String { - let bytes = self.0 .0.as_ref(); - let checksum = blake2b_checksum(bytes); - format!("{}{}", hex::encode(bytes), hex::encode(checksum)) - } -} - -impl fmt::Display for Address { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "addr:{}", self.str_without_prefix()) } -} - -impl fmt::Display for ParseAddressError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Failed to parse Address: {:?}", self) } -} - -#[derive(Debug, Deserialize, Serialize)] -pub enum ParseAddressError { - #[serde(rename = "Address must begin with addr: prefix")] - MissingPrefix, - InvalidHexEncoding(String), - InvalidChecksum, - InvalidLength, - // Add other error kinds as needed -} - -impl From for ParseAddressError { - fn from(e: FromHexError) -> Self { ParseAddressError::InvalidHexEncoding(e.to_string()) } -} - -impl FromStr for Address { - type Err = ParseAddressError; - - fn from_str(s: &str) -> Result { - if !s.starts_with("addr:") { - return Err(ParseAddressError::MissingPrefix); - } - - let without_prefix = &s[5..]; - if without_prefix.len() != (32 + 6) * 2 { - return Err(ParseAddressError::InvalidLength); - } - - let (address_hex, checksum_hex) = without_prefix.split_at(32 * 2); - - let address_bytes: [u8; 32] = hex::decode(address_hex) - .map_err(ParseAddressError::from)? - .try_into() - .expect("length is 32 bytes"); - - let checksum = hex::decode(checksum_hex).map_err(ParseAddressError::from)?; - let checksum_bytes: [u8; 6] = checksum.try_into().expect("length is 6 bytes"); - - if checksum_bytes != blake2b_checksum(&address_bytes) { - return Err(ParseAddressError::InvalidChecksum); - } - - Ok(Address(H256::from(address_bytes))) - } -} - -// Sia uses the first 6 bytes of blake2b(preimage) appended -// to address as checksum -fn blake2b_checksum(preimage: &[u8]) -> [u8; 6] { - let hash = Params::new().hash_length(32).to_state().update(preimage).finalize(); - hash.as_array()[0..6].try_into().expect("array is 64 bytes long") -} - -pub fn v1_standard_address_from_pubkey(pubkey: &PublicKey) -> Address { - let hash = standard_unlock_hash(pubkey); - Address(hash) -} - -#[test] -fn test_v1_standard_address_from_pubkey() { - let pubkey = PublicKey::from_bytes( - &hex::decode("8a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c").unwrap(), - ) - .unwrap(); - let address = v1_standard_address_from_pubkey(&pubkey); - assert_eq!( - format!("{}", address), - "addr:c959f9b423b662c36ee58057b8157acedb4095cfeb7926e4ba44cd9ee1f49a5b7803c7501a7b" - ) -} - -#[test] -fn test_blake2b_checksum() { - let checksum = - blake2b_checksum(&hex::decode("591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a884").unwrap()); - let expected: [u8; 6] = hex::decode("0be0653e411f").unwrap().try_into().unwrap(); - assert_eq!(checksum, expected); -} - -#[test] -fn test_address_display() { - let address = Address("591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a884".into()); - let address_str = "addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f"; - assert_eq!(format!("{}", address), address_str); -} - -#[test] -fn test_address_fromstr() { - let address1 = Address("591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a884".into()); - - let address2 = - Address::from_str("addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f").unwrap(); - assert_eq!(address1, address2); -} - -#[test] -fn test_address_fromstr_bad_length() { - let address = Address::from_str("addr:dead"); - assert!(matches!(address, Err(ParseAddressError::InvalidLength))); -} - -#[test] -fn test_address_fromstr_odd_length() { - let address = Address::from_str("addr:f00"); - assert!(matches!(address, Err(ParseAddressError::InvalidLength))); -} - -#[test] -fn test_address_fromstr_invalid_hex() { - let address = - Address::from_str("addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e41gg"); - assert!(matches!(address, Err(ParseAddressError::InvalidHexEncoding(_)))); -} - -#[test] -fn test_address_fromstr_missing_prefix() { - let address = Address::from_str("591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e41gg"); - assert!(matches!(address, Err(ParseAddressError::MissingPrefix))); -} - -#[test] -fn test_address_fromstr_invalid_checksum() { - let address = - Address::from_str("addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a884ffffffffffff"); - assert!(matches!(address, Err(ParseAddressError::InvalidChecksum))); -} - -#[test] -fn test_address_str_without_prefix() { - let address = - Address::from_str("addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f").unwrap(); - - assert_eq!( - address.str_without_prefix(), - "591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f" - ); -} diff --git a/mm2src/coins/sia/blake2b_internal.rs b/mm2src/coins/sia/blake2b_internal.rs deleted file mode 100644 index 39c4f7c82b..0000000000 --- a/mm2src/coins/sia/blake2b_internal.rs +++ /dev/null @@ -1,326 +0,0 @@ -use blake2b_simd::Params; -use ed25519_dalek::PublicKey; -use rpc::v1::types::H256; -use std::default::Default; - -#[cfg(test)] use hex; -#[cfg(test)] use std::convert::TryInto; - -const LEAF_HASH_PREFIX: [u8; 1] = [0u8]; -const NODE_HASH_PREFIX: [u8; 1] = [1u8]; - -pub const ED25519_IDENTIFIER: [u8; 16] = [ - 0x65, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, -]; - -// Precomputed hash values used for all standard v1 addresses -// a standard address has 1 ed25519 public key, requires 1 signature and has a timelock of 0 -// https://github.com/SiaFoundation/core/blob/b5b08cde6b7d0f1b3a6f09b8aa9d0b817e769efb/types/hash.go#L94 -const STANDARD_TIMELOCK_BLAKE2B_HASH: [u8; 32] = [ - 0x51, 0x87, 0xb7, 0xa8, 0x02, 0x1b, 0xf4, 0xf2, 0xc0, 0x04, 0xea, 0x3a, 0x54, 0xcf, 0xec, 0xe1, 0x75, 0x4f, 0x11, - 0xc7, 0x62, 0x4d, 0x23, 0x63, 0xc7, 0xf4, 0xcf, 0x4f, 0xdd, 0xd1, 0x44, 0x1e, -]; - -const STANDARD_SIGS_REQUIRED_BLAKE2B_HASH: [u8; 32] = [ - 0xb3, 0x60, 0x10, 0xeb, 0x28, 0x5c, 0x15, 0x4a, 0x8c, 0xd6, 0x30, 0x84, 0xac, 0xbe, 0x7e, 0xac, 0x0c, 0x4d, 0x62, - 0x5a, 0xb4, 0xe1, 0xa7, 0x6e, 0x62, 0x4a, 0x87, 0x98, 0xcb, 0x63, 0x49, 0x7b, -]; - -#[derive(Debug, PartialEq)] -pub struct Accumulator { - trees: [H256; 64], - num_leaves: u64, -} - -impl Default for Accumulator { - fn default() -> Self { - Accumulator { - trees: [H256::default(); 64], // Initialize all bytes to zero - num_leaves: 0, - } - } -} - -impl Accumulator { - // Check if there is a tree at the given height - fn has_tree_at_height(&self, height: u64) -> bool { self.num_leaves & (1 << height) != 0 } - - // Add a leaf to the accumulator - pub fn add_leaf(&mut self, h: H256) { - let mut i = 0; - let mut new_hash = h; - while self.has_tree_at_height(i) { - new_hash = hash_blake2b_pair(&NODE_HASH_PREFIX, &self.trees[i as usize].0, &new_hash.0); - i += 1; - } - self.trees[i as usize] = new_hash; - self.num_leaves += 1; - } - - // Calulate the root hash of the Merkle tree - pub fn root(&self) -> H256 { - // trailing_zeros determines the height Merkle tree accumulator where the current lowest single leaf is located - let i = self.num_leaves.trailing_zeros() as u64; - if i == 64 { - return H256::default(); // Return all zeros if no leaves - } - let mut root = self.trees[i as usize]; - for j in i + 1..64 { - if self.has_tree_at_height(j) { - root = hash_blake2b_pair(&NODE_HASH_PREFIX, &self.trees[j as usize].0, &root.0); - } - } - root - } -} - -pub fn sigs_required_leaf(sigs_required: u64) -> H256 { - let sigs_required_array: [u8; 8] = sigs_required.to_le_bytes(); - let mut combined = Vec::new(); - combined.extend_from_slice(&LEAF_HASH_PREFIX); - combined.extend_from_slice(&sigs_required_array); - - hash_blake2b_single(&combined) -} - -// public key leaf is -// blake2b(leafHashPrefix + 16_byte_ascii_algorithm_identifier + public_key_length_u64 + public_key) -pub fn public_key_leaf(pubkey: &PublicKey) -> H256 { - let mut combined = Vec::new(); - combined.extend_from_slice(&LEAF_HASH_PREFIX); - combined.extend_from_slice(&ED25519_IDENTIFIER); - combined.extend_from_slice(&32u64.to_le_bytes()); - combined.extend_from_slice(pubkey.as_bytes()); - hash_blake2b_single(&combined) -} - -pub fn timelock_leaf(timelock: u64) -> H256 { - let timelock: [u8; 8] = timelock.to_le_bytes(); - let mut combined = Vec::new(); - combined.extend_from_slice(&LEAF_HASH_PREFIX); - combined.extend_from_slice(&timelock); - - hash_blake2b_single(&combined) -} - -// https://github.com/SiaFoundation/core/blob/b5b08cde6b7d0f1b3a6f09b8aa9d0b817e769efb/types/hash.go#L96 -// An UnlockHash is the Merkle root of UnlockConditions. Since the standard -// UnlockConditions use a single public key, the Merkle tree is: -// -// ┌─────────┴──────────┐ -// ┌─────┴─────┐ │ -// timelock pubkey sigsrequired -pub fn standard_unlock_hash(pubkey: &PublicKey) -> H256 { - let pubkey_leaf = public_key_leaf(pubkey); - let timelock_pubkey_node = hash_blake2b_pair(&NODE_HASH_PREFIX, &STANDARD_TIMELOCK_BLAKE2B_HASH, &pubkey_leaf.0); - hash_blake2b_pair( - &NODE_HASH_PREFIX, - &timelock_pubkey_node.0, - &STANDARD_SIGS_REQUIRED_BLAKE2B_HASH, - ) -} - -pub fn hash_blake2b_single(preimage: &[u8]) -> H256 { - let hash = Params::new().hash_length(32).to_state().update(preimage).finalize(); - let ret_array = hash.as_array(); - ret_array[0..32].into() -} - -fn hash_blake2b_pair(prefix: &[u8], leaf1: &[u8], leaf2: &[u8]) -> H256 { - let hash = Params::new() - .hash_length(32) - .to_state() - .update(prefix) - .update(leaf1) - .update(leaf2) - .finalize(); - let ret_array = hash.as_array(); - ret_array[0..32].into() -} - -#[test] -fn test_accumulator_new() { - let default_accumulator = Accumulator::default(); - - let expected = Accumulator { - trees: [H256::from("0000000000000000000000000000000000000000000000000000000000000000"); 64], - num_leaves: 0, - }; - assert_eq!(default_accumulator, expected) -} - -#[test] -fn test_accumulator_root_default() { assert_eq!(Accumulator::default().root(), H256::default()) } - -#[test] -fn test_accumulator_root() { - let mut accumulator = Accumulator::default(); - - let timelock_leaf = timelock_leaf(0u64); - accumulator.add_leaf(timelock_leaf); - - let pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let pubkey_leaf = public_key_leaf(&pubkey); - accumulator.add_leaf(pubkey_leaf); - - let sigs_required_leaf = sigs_required_leaf(1u64); - accumulator.add_leaf(sigs_required_leaf); - - let expected = H256::from("72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d"); - assert_eq!(accumulator.root(), expected); -} - -#[test] -fn test_accumulator_add_leaf_standard_unlock_hash() { - let mut accumulator = Accumulator::default(); - - let pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - - let pubkey_leaf = public_key_leaf(&pubkey); - let timelock_leaf = timelock_leaf(0u64); - let sigs_required_leaf = sigs_required_leaf(1u64); - - accumulator.add_leaf(timelock_leaf); - accumulator.add_leaf(pubkey_leaf); - accumulator.add_leaf(sigs_required_leaf); - - let root = accumulator.root(); - let expected = H256::from("72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d"); - assert_eq!(root, expected) -} - -#[test] -fn test_accumulator_add_leaf_2of2_multisig_unlock_hash() { - let mut accumulator = Accumulator::default(); - - let pubkey1 = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let pubkey2 = PublicKey::from_bytes( - &hex::decode("0101010000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - - let pubkey1_leaf = public_key_leaf(&pubkey1); - let pubkey2_leaf = public_key_leaf(&pubkey2); - - let timelock_leaf = timelock_leaf(0u64); - let sigs_required_leaf = sigs_required_leaf(2u64); - - accumulator.add_leaf(timelock_leaf); - accumulator.add_leaf(pubkey1_leaf); - accumulator.add_leaf(pubkey2_leaf); - accumulator.add_leaf(sigs_required_leaf); - - let root = accumulator.root(); - let expected = H256::from("1e94357817d236167e54970a8c08bbd41b37bfceeeb52f6c1ce6dd01d50ea1e7"); - assert_eq!(root, expected) -} - -#[test] -fn test_accumulator_add_leaf_1of2_multisig_unlock_hash() { - let mut accumulator = Accumulator::default(); - - let pubkey1 = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let pubkey2 = PublicKey::from_bytes( - &hex::decode("0101010000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - - let pubkey1_leaf = public_key_leaf(&pubkey1); - let pubkey2_leaf = public_key_leaf(&pubkey2); - - let timelock_leaf = timelock_leaf(0u64); - let sigs_required_leaf = sigs_required_leaf(1u64); - - accumulator.add_leaf(timelock_leaf); - accumulator.add_leaf(pubkey1_leaf); - accumulator.add_leaf(pubkey2_leaf); - accumulator.add_leaf(sigs_required_leaf); - - let root = accumulator.root(); - let expected = H256::from("d7f84e3423da09d111a17f64290c8d05e1cbe4cab2b6bed49e3a4d2f659f0585"); - assert_eq!(root, expected) -} - -#[test] -fn test_standard_unlock_hash() { - let pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - - let hash = standard_unlock_hash(&pubkey); - let expected = H256::from("72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d"); - assert_eq!(hash, expected) -} - -#[test] -fn test_hash_blake2b_pair() { - let left: [u8; 32] = hex::decode("cdcce3978a58ceb6c8480d218646db4eae85eb9ea9c2f5138fbacb4ce2c701e3") - .unwrap() - .try_into() - .unwrap(); - let right: [u8; 32] = hex::decode("b36010eb285c154a8cd63084acbe7eac0c4d625ab4e1a76e624a8798cb63497b") - .unwrap() - .try_into() - .unwrap(); - - let hash = hash_blake2b_pair(&NODE_HASH_PREFIX, &left, &right); - let expected = H256::from("72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d"); - assert_eq!(hash, expected) -} - -#[test] -fn test_create_ed25519_identifier() { - let mut ed25519_identifier: [u8; 16] = [0; 16]; - - let bytes = "ed25519".as_bytes(); - for (i, &byte) in bytes.iter().enumerate() { - ed25519_identifier[i] = byte; - } - assert_eq!(ed25519_identifier, ED25519_IDENTIFIER); -} - -#[test] -fn test_timelock_leaf() { - let hash = timelock_leaf(0); - let expected = H256::from(STANDARD_TIMELOCK_BLAKE2B_HASH); - assert_eq!(hash, expected) -} - -#[test] -fn test_sigs_required_leaf() { - let hash = sigs_required_leaf(1u64); - let expected = H256::from(STANDARD_SIGS_REQUIRED_BLAKE2B_HASH); - assert_eq!(hash, expected) -} - -#[test] -fn test_hash_blake2b_single() { - let hash = hash_blake2b_single(&hex::decode("006564323535313900000000000000000020000000000000000102030000000000000000000000000000000000000000000000000000000000").unwrap()); - let expected = H256::from("21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b"); - assert_eq!(hash, expected) -} - -#[test] -fn test_public_key_leaf() { - let pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - - let hash = public_key_leaf(&pubkey); - let expected = H256::from("21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b"); - assert_eq!(hash, expected) -} diff --git a/mm2src/coins/sia/encoding.rs b/mm2src/coins/sia/encoding.rs deleted file mode 100644 index 5b6516101a..0000000000 --- a/mm2src/coins/sia/encoding.rs +++ /dev/null @@ -1,119 +0,0 @@ -use crate::sia::blake2b_internal::hash_blake2b_single; -use rpc::v1::types::H256; - -// https://github.com/SiaFoundation/core/blob/092850cc52d3d981b19c66cd327b5d945b3c18d3/types/encoding.go#L16 -// TODO go implementation limits this to 1024 bytes, should we? -#[derive(Default)] -pub struct Encoder { - pub buffer: Vec, -} - -impl Encoder { - pub fn reset(&mut self) { self.buffer.clear(); } - - /// writes a length-prefixed []byte to the underlying stream. - pub fn write_len_prefixed_bytes(&mut self, data: &[u8]) { - self.buffer.extend_from_slice(&data.len().to_le_bytes()); - self.buffer.extend_from_slice(data); - } - - pub fn write_slice(&mut self, data: &[u8]) { self.buffer.extend_from_slice(data); } - - pub fn write_u8(&mut self, u: u8) { self.buffer.extend_from_slice(&[u]) } - - pub fn write_u64(&mut self, u: u64) { self.buffer.extend_from_slice(&u.to_le_bytes()); } - - pub fn write_distinguisher(&mut self, p: &str) { self.buffer.extend_from_slice(format!("sia/{}|", p).as_bytes()); } - - pub fn write_bool(&mut self, b: bool) { self.buffer.push(b as u8) } - - pub fn hash(&self) -> H256 { hash_blake2b_single(&self.buffer) } -} - -#[test] -fn test_encoder_default_hash() { - assert_eq!( - Encoder::default().hash(), - H256::from("0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8") - ) -} - -#[test] -fn test_encoder_write_bytes() { - let mut encoder = Encoder::default(); - encoder.write_len_prefixed_bytes(&[1, 2, 3, 4]); - assert_eq!( - encoder.hash(), - H256::from("d4a72b52e2e1f40e20ee40ea6d5080a1b1f76164786defbb7691a4427f3388f5") - ); -} - -#[test] -fn test_encoder_write_u8() { - let mut encoder = Encoder::default(); - encoder.write_u8(1); - assert_eq!( - encoder.hash(), - H256::from("ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25") - ); -} - -#[test] -fn test_encoder_write_u64() { - let mut encoder = Encoder::default(); - encoder.write_u64(1); - assert_eq!( - encoder.hash(), - H256::from("1dbd7d0b561a41d23c2a469ad42fbd70d5438bae826f6fd607413190c37c363b") - ); -} - -#[test] -fn test_encoder_write_distiguisher() { - let mut encoder = Encoder::default(); - encoder.write_distinguisher("test"); - assert_eq!( - encoder.hash(), - H256::from("25fb524721bf98a9a1233a53c40e7e198971b003bf23c24f59d547a1bb837f9c") - ); -} - -#[test] -fn test_encoder_write_bool() { - let mut encoder = Encoder::default(); - encoder.write_bool(true); - assert_eq!( - encoder.hash(), - H256::from("ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25") - ); -} - -#[test] -fn test_encoder_reset() { - let mut encoder = Encoder::default(); - encoder.write_bool(true); - assert_eq!( - encoder.hash(), - H256::from("ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25") - ); - - encoder.reset(); - encoder.write_bool(false); - assert_eq!( - encoder.hash(), - H256::from("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314") - ); -} - -#[test] -fn test_encoder_complex() { - let mut encoder = Encoder::default(); - encoder.write_distinguisher("test"); - encoder.write_bool(true); - encoder.write_u8(1); - encoder.write_len_prefixed_bytes(&[1, 2, 3, 4]); - assert_eq!( - encoder.hash(), - H256::from("b66d7a9bef9fb303fe0e41f6b5c5af410303e428c4ff9231f6eb381248693221") - ); -} diff --git a/mm2src/coins/sia/http_client.rs b/mm2src/coins/sia/http_client.rs deleted file mode 100644 index 774dcd08e1..0000000000 --- a/mm2src/coins/sia/http_client.rs +++ /dev/null @@ -1,208 +0,0 @@ -use crate::sia::address::Address; -use crate::sia::SiaHttpConf; -use base64::engine::general_purpose::STANDARD as BASE64; -use base64::Engine as _; // required for .encode() method -use core::fmt::Display; -use core::time::Duration; -use mm2_number::MmNumber; -use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION}; -use reqwest::{Client, Error, Url}; -use serde::de::DeserializeOwned; -use std::ops::Deref; -use std::sync::Arc; - -#[cfg(test)] use std::str::FromStr; - -const ENDPOINT_CONSENSUS_TIP: &str = "api/consensus/tip"; - -/// HTTP(s) client for Sia-protocol coins -#[derive(Debug)] -pub struct SiaHttpClientImpl { - /// Name of coin the http client is intended to work with - pub coin_ticker: String, - /// The uri to send requests to - pub uri: String, - /// Value of Authorization header password, e.g. "Basic base64(:password)" - pub auth: String, -} - -#[derive(Clone, Debug)] -pub struct SiaApiClient(pub Arc); - -impl Deref for SiaApiClient { - type Target = SiaApiClientImpl; - fn deref(&self) -> &SiaApiClientImpl { &self.0 } -} - -impl SiaApiClient { - pub fn new(_coin_ticker: &str, http_conf: SiaHttpConf) -> Result { - let new_arc = SiaApiClientImpl::new(http_conf.url, &http_conf.auth)?; - Ok(SiaApiClient(Arc::new(new_arc))) - } -} - -#[derive(Debug)] -pub struct SiaApiClientImpl { - client: Client, - base_url: Url, -} - -// this is neccesary to show the URL in error messages returned to the user -// this can be removed in favor of using ".with_url()" once reqwest is updated to v0.11.23 -#[derive(Debug)] -pub struct ReqwestErrorWithUrl { - error: Error, - url: Url, -} - -impl Display for ReqwestErrorWithUrl { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "Error: {}, URL: {}", self.error, self.url) - } -} - -#[derive(Debug, Display)] -pub enum SiaApiClientError { - Timeout(String), - BuildError(String), - ApiUnreachable(String), - ReqwestError(ReqwestErrorWithUrl), - UrlParse(url::ParseError), -} - -impl From for String { - fn from(e: SiaApiClientError) -> Self { format!("{:?}", e) } -} - -async fn fetch_and_parse(client: &Client, url: Url) -> Result { - client - .get(url.clone()) - .send() - .await - .map_err(|e| { - SiaApiClientError::ReqwestError(ReqwestErrorWithUrl { - error: e, - url: url.clone(), - }) - })? - .json::() - .await - .map_err(|e| SiaApiClientError::ReqwestError(ReqwestErrorWithUrl { error: e, url })) -} - -// https://github.com/SiaFoundation/core/blob/4e46803f702891e7a83a415b7fcd7543b13e715e/types/types.go#L181 -#[derive(Deserialize, Serialize, Debug)] -pub struct GetConsensusTipResponse { - pub height: u64, - pub id: String, // TODO this can match "BlockID" type -} - -// https://github.com/SiaFoundation/walletd/blob/9574e69ff0bf84de1235b68e78db2a41d5e27516/api/api.go#L36 -// https://github.com/SiaFoundation/walletd/blob/9574e69ff0bf84de1235b68e78db2a41d5e27516/wallet/wallet.go#L25 -#[derive(Deserialize, Serialize, Debug)] -pub struct GetAddressesBalanceResponse { - pub siacoins: MmNumber, - #[serde(rename = "immatureSiacoins")] - pub immature_siacoins: MmNumber, - pub siafunds: u64, -} - -impl SiaApiClientImpl { - fn new(base_url: Url, password: &str) -> Result { - let mut headers = HeaderMap::new(); - let auth_value = format!("Basic {}", BASE64.encode(format!(":{}", password))); - headers.insert( - AUTHORIZATION, - HeaderValue::from_str(&auth_value).map_err(|e| SiaApiClientError::BuildError(e.to_string()))?, - ); - - let client = Client::builder() - .default_headers(headers) - .timeout(Duration::from_secs(10)) // TODO make this configurable - .build() - .map_err(|e| { - SiaApiClientError::ReqwestError(ReqwestErrorWithUrl { - error: e, - url: base_url.clone(), - }) - })?; - Ok(SiaApiClientImpl { client, base_url }) - } - - pub async fn get_consensus_tip(&self) -> Result { - let base_url = self.base_url.clone(); - let endpoint_url = base_url - .join(ENDPOINT_CONSENSUS_TIP) - .map_err(SiaApiClientError::UrlParse)?; - - fetch_and_parse::(&self.client, endpoint_url).await - } - - pub async fn get_addresses_balance( - &self, - address: &Address, - ) -> Result { - self.get_addresses_balance_str(&address.str_without_prefix()).await - } - - // use get_addresses_balance whenever possible to rely on Address deserialization - pub async fn get_addresses_balance_str( - &self, - address: &str, - ) -> Result { - let base_url = self.base_url.clone(); - - let endpoint_path = format!("api/addresses/{}/balance", address); - let endpoint_url = base_url.join(&endpoint_path).map_err(SiaApiClientError::UrlParse)?; - - fetch_and_parse::(&self.client, endpoint_url).await - } - - pub async fn get_height(&self) -> Result { - let resp = self.get_consensus_tip().await?; - Ok(resp.height) - } -} - -#[tokio::test] -#[ignore] -async fn test_api_client_timeout() { - let api_client = SiaApiClientImpl::new(Url::parse("http://foo").unwrap(), "password").unwrap(); - let result = api_client.get_consensus_tip().await; - assert!(matches!(result, Err(SiaApiClientError::Timeout(_)))); -} - -// TODO all of the following must be adapted to use Docker Sia node -#[tokio::test] -#[ignore] -async fn test_api_client_invalid_auth() { - let api_client = SiaApiClientImpl::new(Url::parse("http://127.0.0.1:9980").unwrap(), "password").unwrap(); - let result = api_client.get_consensus_tip().await; - assert!(matches!(result, Err(SiaApiClientError::BuildError(_)))); -} - -// TODO must be adapted to use Docker Sia node -#[tokio::test] -#[ignore] -async fn test_api_client() { - let api_client = SiaApiClientImpl::new(Url::parse("http://127.0.0.1:9980").unwrap(), "password").unwrap(); - let _result = api_client.get_consensus_tip().await.unwrap(); -} - -#[tokio::test] -#[ignore] -async fn test_api_get_addresses_balance() { - let api_client = SiaApiClientImpl::new(Url::parse("http://127.0.0.1:9980").unwrap(), "password").unwrap(); - let address = - Address::from_str("addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f").unwrap(); - let result = api_client.get_addresses_balance(&address).await.unwrap(); - println!("ret {:?}", result); -} - -#[tokio::test] -#[ignore] -async fn test_api_get_addresses_balance_invalid() { - let api_client = SiaApiClientImpl::new(Url::parse("http://127.0.0.1:9980").unwrap(), "password").unwrap(); - let result = api_client.get_addresses_balance_str("what").await.unwrap(); - println!("ret {:?}", result); -} diff --git a/mm2src/coins/sia/spend_policy.rs b/mm2src/coins/sia/spend_policy.rs deleted file mode 100644 index 6c28f7250a..0000000000 --- a/mm2src/coins/sia/spend_policy.rs +++ /dev/null @@ -1,322 +0,0 @@ -use crate::sia::address::Address; -use crate::sia::blake2b_internal::{public_key_leaf, sigs_required_leaf, standard_unlock_hash, timelock_leaf, - Accumulator, ED25519_IDENTIFIER}; -use crate::sia::encoding::Encoder; -use ed25519_dalek::PublicKey; -use rpc::v1::types::H256; - -#[cfg(test)] use std::str::FromStr; - -const POLICY_VERSION: u8 = 1u8; - -#[derive(Debug, Clone)] -pub enum SpendPolicy { - Above(u64), - After(u64), - PublicKey(PublicKey), - Hash(H256), - Threshold(PolicyTypeThreshold), - Opaque(Address), - UnlockConditions(PolicyTypeUnlockConditions), // For v1 compatibility -} - -impl SpendPolicy { - pub fn to_u8(&self) -> u8 { - match self { - SpendPolicy::Above(_) => 1, - SpendPolicy::After(_) => 2, - SpendPolicy::PublicKey(_) => 3, - SpendPolicy::Hash(_) => 4, - SpendPolicy::Threshold(_) => 5, - SpendPolicy::Opaque(_) => 6, - SpendPolicy::UnlockConditions(_) => 7, - } - } - - pub fn encode(&self) -> Encoder { - let mut encoder = Encoder::default(); - encoder.write_u8(POLICY_VERSION); - encoder.write_slice(&self.encode_wo_prefix().buffer); - encoder - } - - pub fn encode_wo_prefix(&self) -> Encoder { - let mut encoder = Encoder::default(); - let opcode = self.to_u8(); - match self { - SpendPolicy::Above(height) => { - encoder.write_u8(opcode); - encoder.write_u64(*height); - }, - SpendPolicy::After(time) => { - encoder.write_u8(opcode); - encoder.write_u64(*time); - }, - SpendPolicy::PublicKey(pubkey) => { - encoder.write_u8(opcode); - encoder.write_slice(&pubkey.to_bytes()); - }, - SpendPolicy::Hash(hash) => { - encoder.write_u8(opcode); - encoder.write_slice(&hash.0); - }, - SpendPolicy::Threshold(PolicyTypeThreshold { n, of }) => { - encoder.write_u8(opcode); - encoder.write_u8(*n); - encoder.write_u8(of.len() as u8); - for policy in of { - encoder.write_slice(&policy.encode_wo_prefix().buffer); - } - }, - SpendPolicy::Opaque(p) => { - encoder.write_u8(opcode); - encoder.write_slice(&p.0 .0); - }, - SpendPolicy::UnlockConditions(PolicyTypeUnlockConditions(unlock_condition)) => { - encoder.write_u8(opcode); - encoder.write_u64(unlock_condition.timelock); - encoder.write_u64(unlock_condition.pubkeys.len() as u64); - for pubkey in &unlock_condition.pubkeys { - encoder.write_slice(&ED25519_IDENTIFIER); - encoder.write_slice(&pubkey.to_bytes()); - } - encoder.write_u64(unlock_condition.sigs_required); - }, - } - encoder - } - - fn address(&self) -> Address { - if let SpendPolicy::UnlockConditions(PolicyTypeUnlockConditions(unlock_condition)) = self { - return unlock_condition.address(); - } - let mut encoder = Encoder::default(); - encoder.write_distinguisher("address"); - - // if self is a threshold policy, we need to convert all of its subpolicies to opaque - let mut new_policy = self.clone(); - if let SpendPolicy::Threshold(ref mut p) = new_policy { - p.of = p.of.iter().map(SpendPolicy::opaque).collect(); - } - - let encoded_policy = new_policy.encode(); - encoder.write_slice(&encoded_policy.buffer); - Address(encoder.hash()) - } - - pub fn above(height: u64) -> Self { SpendPolicy::Above(height) } - - pub fn after(time: u64) -> Self { SpendPolicy::After(time) } - - pub fn public_key(pk: PublicKey) -> Self { SpendPolicy::PublicKey(pk) } - - pub fn hash(h: H256) -> Self { SpendPolicy::Hash(h) } - - pub fn threshold(n: u8, of: Vec) -> Self { SpendPolicy::Threshold(PolicyTypeThreshold { n, of }) } - - pub fn opaque(p: &SpendPolicy) -> Self { SpendPolicy::Opaque(p.address()) } - - pub fn anyone_can_spend() -> Self { SpendPolicy::threshold(0, vec![]) } -} - -#[derive(Debug, Clone)] -pub struct PolicyTypeThreshold { - pub n: u8, - pub of: Vec, -} - -// Compatibility with Sia's "UnlockConditions" -#[derive(Debug, Clone)] -pub struct PolicyTypeUnlockConditions(UnlockCondition); - -#[derive(Debug, Clone)] -pub struct UnlockCondition { - pubkeys: Vec, - timelock: u64, - sigs_required: u64, -} - -impl UnlockCondition { - pub fn new(pubkeys: Vec, timelock: u64, sigs_required: u64) -> Self { - // TODO check go implementation to see if there should be limitations or checks imposed here - UnlockCondition { - pubkeys, - timelock, - sigs_required, - } - } - - pub fn unlock_hash(&self) -> H256 { - // almost all UnlockConditions are standard, so optimize for that case - if self.timelock == 0 && self.pubkeys.len() == 1 && self.sigs_required == 1 { - return standard_unlock_hash(&self.pubkeys[0]); - } - - let mut accumulator = Accumulator::default(); - - accumulator.add_leaf(timelock_leaf(self.timelock)); - - for pubkey in &self.pubkeys { - accumulator.add_leaf(public_key_leaf(pubkey)); - } - - accumulator.add_leaf(sigs_required_leaf(self.sigs_required)); - accumulator.root() - } - - pub fn address(&self) -> Address { Address(self.unlock_hash()) } -} - -#[test] -fn test_unlock_condition_unlock_hash_standard() { - let pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let unlock_condition = UnlockCondition::new(vec![pubkey], 0, 1); - - let hash = unlock_condition.unlock_hash(); - let expected = H256::from("72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d"); - assert_eq!(hash, expected); - - let hash = standard_unlock_hash(&pubkey); - assert_eq!(hash, expected); -} - -#[test] -fn test_unlock_condition_unlock_hash_2of2_multisig() { - let pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let pubkey2 = PublicKey::from_bytes( - &hex::decode("0101010000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let unlock_condition = UnlockCondition::new(vec![pubkey, pubkey2], 0, 2); - - let hash = unlock_condition.unlock_hash(); - let expected = H256::from("1e94357817d236167e54970a8c08bbd41b37bfceeeb52f6c1ce6dd01d50ea1e7"); - assert_eq!(hash, expected); -} - -#[test] -fn test_unlock_condition_unlock_hash_1of2_multisig() { - let pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let pubkey2 = PublicKey::from_bytes( - &hex::decode("0101010000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let unlock_condition = UnlockCondition::new(vec![pubkey, pubkey2], 0, 1); - - let hash = unlock_condition.unlock_hash(); - let expected = H256::from("d7f84e3423da09d111a17f64290c8d05e1cbe4cab2b6bed49e3a4d2f659f0585"); - assert_eq!(hash, expected); -} - -#[test] -fn test_spend_policy_encode_above() { - let policy = SpendPolicy::above(1); - - let hash = policy.encode().hash(); - let expected = H256::from("bebf6cbdfb440a92e3e5d832ac30fe5d226ff6b352ed3a9398b7d35f086a8ab6"); - assert_eq!(hash, expected); - - let address = policy.address(); - let expected = - Address::from_str("addr:188b997bb99dee13e95f92c3ea150bd76b3ec72e5ba57b0d57439a1a6e2865e9b25ea5d1825e").unwrap(); - assert_eq!(address, expected); -} - -#[test] -fn test_spend_policy_encode_after() { - let policy = SpendPolicy::after(1); - - let hash = policy.encode().hash(); - let expected = H256::from("07b0f28eafd87a082ad11dc4724e1c491821260821a30bec68254444f97d9311"); - assert_eq!(hash, expected); - - let address = policy.address(); - let expected = - Address::from_str("addr:60c74e0ce5cede0f13f83b0132cb195c995bc7688c9fac34bbf2b14e14394b8bbe2991bc017f").unwrap(); - assert_eq!(address, expected); -} - -#[test] -fn test_spend_policy_encode_pubkey() { - let pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let policy = SpendPolicy::PublicKey(pubkey); - - let hash = policy.encode().hash(); - let expected = H256::from("4355c8f80f6e5a98b70c9c2f9a22f17747989b4744783c90439b2b034f698bfe"); - assert_eq!(hash, expected); - - let address = policy.address(); - let expected = - Address::from_str("addr:55a7793237722c6df8222fd512063cb74228085ef1805c5184713648c159b919ac792fbad0e1").unwrap(); - assert_eq!(address, expected); -} - -#[test] -fn test_spend_policy_encode_hash() { - let hash = H256::from("0102030000000000000000000000000000000000000000000000000000000000"); - let policy = SpendPolicy::Hash(hash); - - let encoded = policy.encode(); - let hash = encoded.hash(); - let expected = H256::from("9938967aefa6cbecc1f1620d2df5170d6811d4b2f47a879b621c1099a3b0628a"); - assert_eq!(hash, expected); - - let address = policy.address(); - let expected = - Address::from_str("addr:a4d5a06d8d3c2e45aa26627858ce8e881505ae3c9d122a1d282c7824163751936cffb347e435").unwrap(); - assert_eq!(address, expected); -} - -#[test] -fn test_spend_policy_encode_threshold() { - let policy = SpendPolicy::Threshold(PolicyTypeThreshold { - n: 1, - of: vec![SpendPolicy::above(1), SpendPolicy::after(1)], - }); - - let encoded = policy.encode(); - let hash = encoded.hash(); - let expected = H256::from("7d792df6cd0b5e0f795287b3bf4087bbcc4c1bd0c52880a552cdda3e5e33d802"); - assert_eq!(hash, expected); - - let address = policy.address(); - let expected = - Address::from_str("addr:4179b53aba165e46e4c85b3c8766bb758fb6f0bfa5721550b81981a3ec38efc460557dc1ded4").unwrap(); - assert_eq!(address, expected); -} - -#[test] -fn test_spend_policy_encode_unlock_condition() { - let pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let unlock_condition = UnlockCondition::new(vec![pubkey], 0, 1); - - let sub_policy = SpendPolicy::UnlockConditions(PolicyTypeUnlockConditions(unlock_condition)); - let base_address = sub_policy.address(); - let expected = - Address::from_str("addr:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515dd64b9a56043a").unwrap(); - assert_eq!(base_address, expected); - - let policy = SpendPolicy::Threshold(PolicyTypeThreshold { - n: 1, - of: vec![sub_policy], - }); - let address = policy.address(); - let expected = - Address::from_str("addr:1498a58c843ce66740e52421632d67a0f6991ea96db1fc97c29e46f89ae56e3534078876331d").unwrap(); - assert_eq!(address, expected); -} diff --git a/mm2src/coins/sia.rs b/mm2src/coins/siacoin.rs similarity index 84% rename from mm2src/coins/sia.rs rename to mm2src/coins/siacoin.rs index 446a507070..4e77a74731 100644 --- a/mm2src/coins/sia.rs +++ b/mm2src/coins/siacoin.rs @@ -1,5 +1,5 @@ -use super::{CoinBalance, HistorySyncState, MarketCoinOps, MmCoin, RawTransactionFut, RawTransactionRequest, SwapOps, - TradeFee, TransactionEnum, TransactionFut}; +use super::{BalanceError, CoinBalance, HistorySyncState, MarketCoinOps, MmCoin, RawTransactionFut, + RawTransactionRequest, SwapOps, TradeFee, TransactionEnum, TransactionFut}; use crate::{coin_errors::MyAddressError, BalanceFut, CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinFutSpawner, ConfirmPaymentInput, DexFee, FeeApproxStage, FoundSwapTxSpend, MakerSwapTakerCoin, MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, @@ -14,25 +14,22 @@ use crate::{coin_errors::MyAddressError, BalanceFut, CanRefundHtlc, CheckIfMyPay WithdrawRequest}; use async_trait::async_trait; use common::executor::AbortedError; +pub use ed25519_dalek::{Keypair, PublicKey, SecretKey, Signature}; use futures::{FutureExt, TryFutureExt}; use futures01::Future; use keys::KeyPair; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; -use mm2_number::{BigDecimal, MmNumber}; +use mm2_number::{BigDecimal, BigInt, MmNumber}; use rpc::v1::types::Bytes as BytesJson; use serde_json::Value as Json; use std::ops::Deref; use std::sync::Arc; -use url::Url; -pub mod address; -use address::v1_standard_address_from_pubkey; -pub mod blake2b_internal; -pub mod encoding; -pub mod http_client; -use http_client::{SiaApiClient, SiaApiClientError}; -pub mod spend_policy; +use sia_rust::http_client::{SiaApiClient, SiaApiClientError, SiaHttpConf}; +use sia_rust::spend_policy::SpendPolicy; + +pub mod sia_hd_wallet; #[derive(Clone)] pub struct SiaCoin(SiaArc); @@ -54,12 +51,6 @@ pub struct SiaCoinConf { pub foo: u32, } -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct SiaHttpConf { - pub url: Url, - pub auth: String, -} - // TODO see https://github.com/KomodoPlatform/komodo-defi-framework/pull/2086#discussion_r1521660384 // for additional fields needed #[derive(Clone, Debug, Deserialize, Serialize)] @@ -93,7 +84,7 @@ impl<'a> SiaConfBuilder<'a> { pub struct SiaCoinFields { /// SIA coin config pub conf: SiaCoinConf, - pub priv_key_policy: PrivKeyPolicy, + pub priv_key_policy: PrivKeyPolicy, /// HTTP(s) client pub http_client: SiaApiClient, } @@ -118,7 +109,7 @@ pub struct SiaCoinBuilder<'a> { ctx: &'a MmArc, ticker: &'a str, conf: &'a Json, - key_pair: ed25519_dalek::Keypair, + key_pair: Keypair, params: &'a SiaCoinActivationParams, } @@ -127,7 +118,7 @@ impl<'a> SiaCoinBuilder<'a> { ctx: &'a MmArc, ticker: &'a str, conf: &'a Json, - key_pair: ed25519_dalek::Keypair, + key_pair: Keypair, params: &'a SiaCoinActivationParams, ) -> Self { SiaCoinBuilder { @@ -140,15 +131,22 @@ impl<'a> SiaCoinBuilder<'a> { } } -fn generate_keypair_from_slice(priv_key: &[u8]) -> Result { - let secret_key = ed25519_dalek::SecretKey::from_bytes(priv_key).map_err(SiaCoinBuildError::EllipticCurveError)?; - let public_key = ed25519_dalek::PublicKey::from(&secret_key); - Ok(ed25519_dalek::Keypair { +fn generate_keypair_from_slice(priv_key: &[u8]) -> Result { + let secret_key = SecretKey::from_bytes(priv_key).map_err(SiaCoinBuildError::EllipticCurveError)?; + let public_key = PublicKey::from(&secret_key); + Ok(Keypair { secret: secret_key, public: public_key, }) } +/// Convert hastings amount to siacoin amount +fn siacoin_from_hastings(hastings: u128) -> BigDecimal { + let hastings = BigInt::from(hastings); + let decimals = BigInt::from(10u128.pow(24)); + BigDecimal::from(hastings) / BigDecimal::from(decimals) +} + impl From for SiaCoinBuildError { fn from(e: SiaConfError) -> Self { SiaCoinBuildError::ConfError(e) } } @@ -174,8 +172,9 @@ impl<'a> SiaCoinBuilder<'a> { let conf = SiaConfBuilder::new(self.conf, self.ticker()).build()?; let sia_fields = SiaCoinFields { conf, - http_client: SiaApiClient::new(self.ticker(), self.params.http_conf.clone()) - .map_err(SiaCoinBuildError::ClientError)?, + http_client: SiaApiClient::new(self.params.http_conf.clone()) + .map_err(SiaCoinBuildError::ClientError) + .await?, priv_key_policy: PrivKeyPolicy::Iguana(self.key_pair), }; let sia_arc = SiaArc::new(sia_fields); @@ -306,9 +305,15 @@ impl MarketCoinOps for SiaCoin { ) .into()); }, + #[cfg(target_arch = "wasm32")] + PrivKeyPolicy::Metamask(_) => { + return Err(MyAddressError::UnexpectedDerivationMethod( + "Metamask not supported. Must use iguana seed.".to_string(), + ) + .into()); + }, }; - - let address = v1_standard_address_from_pubkey(&key_pair.public); + let address = SpendPolicy::PublicKey(key_pair.public).address(); Ok(address.to_string()) } @@ -323,14 +328,30 @@ impl MarketCoinOps for SiaCoin { } fn my_balance(&self) -> BalanceFut { + let coin = self.clone(); let fut = async move { + let my_address = match &coin.0.priv_key_policy { + PrivKeyPolicy::Iguana(key_pair) => SpendPolicy::PublicKey(key_pair.public).address(), + _ => { + return MmError::err(BalanceError::UnexpectedDerivationMethod( + UnexpectedDerivationMethod::ExpectedSingleAddress, + )) + }, + }; + let balance = coin + .0 + .http_client + .address_balance(my_address) + .await + .map_to_mm(|e| BalanceError::Transport(e.to_string()))?; Ok(CoinBalance { - spendable: BigDecimal::default(), - unspendable: BigDecimal::default(), + spendable: siacoin_from_hastings(balance.siacoins.to_u128()), + unspendable: siacoin_from_hastings(balance.immature_siacoins.to_u128()), }) }; Box::new(fut.boxed().compat()) } + fn base_coin_balance(&self) -> BalanceFut { unimplemented!() } fn platform_ticker(&self) -> &str { "FOO" } // TODO Alright @@ -360,7 +381,7 @@ impl MarketCoinOps for SiaCoin { fn current_block(&self) -> Box + Send> { let http_client = self.0.http_client.clone(); // Clone the client - let height_fut = async move { http_client.get_height().await.map_err(|e| e.to_string()) } + let height_fut = async move { http_client.current_height().await.map_err(|e| e.to_string()) } .boxed() // Make the future 'static by boxing .compat(); // Convert to a futures 0.1-compatible future @@ -596,3 +617,29 @@ impl WatcherOps for SiaCoin { unimplemented!() } } + +#[cfg(test)] +mod tests { + use super::*; + use mm2_number::BigDecimal; + use std::str::FromStr; + + #[test] + fn test_siacoin_from_hastings() { + let hastings = u128::MAX; + let siacoin = siacoin_from_hastings(hastings); + assert_eq!( + siacoin, + BigDecimal::from_str("340282366920938.463463374607431768211455").unwrap() + ); + + let hastings = 0; + let siacoin = siacoin_from_hastings(hastings); + assert_eq!(siacoin, BigDecimal::from_str("0").unwrap()); + + // Total supply of Siacoin + let hastings = 57769875000000000000000000000000000; + let siacoin = siacoin_from_hastings(hastings); + assert_eq!(siacoin, BigDecimal::from_str("57769875000").unwrap()); + } +} diff --git a/mm2src/coins/siacoin/sia_hd_wallet.rs b/mm2src/coins/siacoin/sia_hd_wallet.rs new file mode 100644 index 0000000000..4c6a288ef5 --- /dev/null +++ b/mm2src/coins/siacoin/sia_hd_wallet.rs @@ -0,0 +1,44 @@ +use crate::hd_wallet::{HDAccount, HDAddress, HDWallet}; +use bip32::{ExtendedPublicKey, PrivateKeyBytes, PublicKey as bip32PublicKey, PublicKeyBytes, Result as bip32Result}; +use sia_rust::types::Address; +use sia_rust::PublicKey; + +pub struct SiaPublicKey(pub PublicKey); + +pub type SiaHDAddress = HDAddress; +pub type SiaHDAccount = HDAccount; +pub type SiaHDWallet = HDWallet; +pub type Ed25519ExtendedPublicKey = ExtendedPublicKey; + +impl bip32PublicKey for SiaPublicKey { + fn from_bytes(_bytes: PublicKeyBytes) -> bip32Result { + todo!() + //Ok(secp256k1_ffi::PublicKey::from_slice(&bytes)?) + } + + fn to_bytes(&self) -> PublicKeyBytes { + todo!() + // self.serialize() + } + + fn derive_child(&self, _other: PrivateKeyBytes) -> bip32Result { + todo!() + // use secp256k1_ffi::{Secp256k1, VerifyOnly}; + // let engine = Secp256k1::::verification_only(); + + // let mut child_key = *self; + // child_key + // .add_exp_assign(&engine, &other) + // .map_err(|_| Error::Crypto)?; + + // Ok(child_key) + } +} + +// coin type 1991 +// path component 0x800007c7 + +#[test] +fn test_something() { + println!("This is a test"); +} diff --git a/mm2src/coins_activation/src/prelude.rs b/mm2src/coins_activation/src/prelude.rs index 236d19b1c1..d000170fa3 100644 --- a/mm2src/coins_activation/src/prelude.rs +++ b/mm2src/coins_activation/src/prelude.rs @@ -1,5 +1,5 @@ #[cfg(feature = "enable-sia")] -use coins::sia::SiaCoinActivationParams; +use coins::siacoin::SiaCoinActivationParams; use coins::utxo::UtxoActivationParams; use coins::z_coin::ZcoinActivationParams; use coins::{coin_conf, CoinBalance, CoinProtocol, DerivationMethodResponse, MmCoinEnum}; diff --git a/mm2src/coins_activation/src/sia_coin_activation.rs b/mm2src/coins_activation/src/sia_coin_activation.rs index 7dd7539f66..11c72955ab 100644 --- a/mm2src/coins_activation/src/sia_coin_activation.rs +++ b/mm2src/coins_activation/src/sia_coin_activation.rs @@ -7,8 +7,8 @@ use async_trait::async_trait; use coins::coin_balance::{CoinBalanceReport, IguanaWalletBalance}; use coins::coin_errors::MyAddressError; use coins::my_tx_history_v2::TxHistoryStorage; -use coins::sia::{sia_coin_from_conf_and_params, SiaCoin, SiaCoinActivationParams, SiaCoinBuildError, - SiaCoinProtocolInfo}; +use coins::siacoin::{sia_coin_from_conf_and_params, SiaCoin, SiaCoinActivationParams, SiaCoinBuildError, + SiaCoinProtocolInfo}; use coins::tx_history_storage::CreateTxHistoryStorageError; use coins::{BalanceError, CoinBalance, CoinProtocol, MarketCoinOps, PrivKeyBuildPolicy, RegisterCoinError}; use crypto::hw_rpc_task::{HwRpcTaskAwaitingStatus, HwRpcTaskUserAction}; diff --git a/mm2src/mm2_main/Cargo.toml b/mm2src/mm2_main/Cargo.toml index f3232ac91e..60c3e9aa62 100644 --- a/mm2src/mm2_main/Cargo.toml +++ b/mm2src/mm2_main/Cargo.toml @@ -19,11 +19,11 @@ track-ctx-pointer = ["common/track-ctx-pointer"] zhtlc-native-tests = ["coins/zhtlc-native-tests"] run-docker-tests = ["coins/run-docker-tests"] # TODO -enable-solana = [] +enable-solana = ["coins/enable-solana", "coins_activation/enable-solana"] default = [] trezor-udp = ["crypto/trezor-udp"] # use for tests to connect to trezor emulator over udp run-device-tests = [] -enable-sia = [] +enable-sia = ["coins/enable-sia", "coins_activation/enable-sia"] [dependencies] async-std = { version = "1.5", features = ["unstable"] } @@ -132,6 +132,8 @@ ethabi = { version = "17.0.0" } rlp = { version = "0.5" } ethcore-transaction = { git = "https://github.com/KomodoPlatform/mm2-parity-ethereum.git", rev = "mm2-v2.1.1" } rustc-hex = "2" +sia-rust = { git = "https://github.com/KomodoPlatform/sia-rust", rev = "9f188b80b3213bcb604e7619275251ce08fae808" } +url = { version = "2.2.2", features = ["serde"] } [build-dependencies] chrono = "0.4" diff --git a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs index 5403392240..b9066bf540 100644 --- a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs +++ b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs @@ -28,7 +28,7 @@ use coins::rpc_command::{account_balance::account_balance, init_scan_for_new_addresses::{cancel_scan_for_new_addresses, init_scan_for_new_addresses, init_scan_for_new_addresses_status}, init_withdraw::{cancel_withdraw, init_withdraw, withdraw_status, withdraw_user_action}}; -#[cfg(feature = "enable-sia")] use coins::sia::SiaCoin; +#[cfg(feature = "enable-sia")] use coins::siacoin::SiaCoin; use coins::tendermint::{TendermintCoin, TendermintToken}; use coins::utxo::bch::BchCoin; use coins::utxo::qtum::QtumCoin; diff --git a/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs b/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs index 4e294e6031..fe5d7f96d4 100644 --- a/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs +++ b/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs @@ -105,6 +105,10 @@ pub const UTXO_ASSET_DOCKER_IMAGE: &str = "docker.io/artempikulin/testblockchain pub const UTXO_ASSET_DOCKER_IMAGE_WITH_TAG: &str = "docker.io/artempikulin/testblockchain:multiarch"; pub const GETH_DOCKER_IMAGE: &str = "docker.io/ethereum/client-go"; pub const GETH_DOCKER_IMAGE_WITH_TAG: &str = "docker.io/ethereum/client-go:stable"; +#[allow(dead_code)] +pub const SIA_DOCKER_IMAGE: &str = "docker.io/alrighttt/walletd-komodo"; +#[allow(dead_code)] +pub const SIA_DOCKER_IMAGE_WITH_TAG: &str = "docker.io/alrighttt/walletd-komodo:latest"; pub const NUCLEUS_IMAGE: &str = "docker.io/komodoofficial/nucleusd"; pub const ATOM_IMAGE: &str = "docker.io/komodoofficial/gaiad"; @@ -371,6 +375,22 @@ pub fn geth_docker_node<'a>(docker: &'a Cli, ticker: &'static str, port: u16) -> } } +#[allow(dead_code)] +pub fn sia_docker_node<'a>(docker: &'a Cli, ticker: &'static str, port: u16) -> DockerNode<'a> { + let image = + GenericImage::new(SIA_DOCKER_IMAGE, "latest").with_env_var("WALLETD_API_PASSWORD", "password".to_string()); + let args = vec![]; + let image = RunnableImage::from((image, args)) + .with_mapped_port((port, port)) + .with_container_name("sia-docker"); + let container = docker.run(image); + DockerNode { + container, + ticker: ticker.into(), + port, + } +} + pub fn nucleus_node(docker: &'_ Cli, runtime_dir: PathBuf) -> DockerNode<'_> { let nucleus_node_runtime_dir = runtime_dir.join("nucleus-testnet-data"); assert!(nucleus_node_runtime_dir.exists()); diff --git a/mm2src/mm2_main/tests/docker_tests/mod.rs b/mm2src/mm2_main/tests/docker_tests/mod.rs index 2adfe3b2c9..685f1bcad8 100644 --- a/mm2src/mm2_main/tests/docker_tests/mod.rs +++ b/mm2src/mm2_main/tests/docker_tests/mod.rs @@ -4,6 +4,7 @@ mod docker_ordermatch_tests; mod docker_tests_inner; mod eth_docker_tests; pub mod qrc20_tests; +#[cfg(feature = "enable-sia")] mod sia_docker_tests; mod slp_tests; #[cfg(feature = "enable-solana")] mod solana_tests; mod swap_proto_v2_tests; diff --git a/mm2src/mm2_main/tests/docker_tests/sia_docker_tests.rs b/mm2src/mm2_main/tests/docker_tests/sia_docker_tests.rs new file mode 100644 index 0000000000..b8546aa218 --- /dev/null +++ b/mm2src/mm2_main/tests/docker_tests/sia_docker_tests.rs @@ -0,0 +1,118 @@ +use common::block_on; +use sia_rust::http_client::{SiaApiClient, SiaApiClientError, SiaHttpConf}; +use sia_rust::http_endpoints::{AddressBalanceRequest, AddressUtxosRequest, ConsensusTipRequest, TxpoolBroadcastRequest}; +use sia_rust::spend_policy::SpendPolicy; +use sia_rust::transaction::{SiacoinOutput, V2TransactionBuilder}; +use sia_rust::types::{Address, Currency}; +use sia_rust::{Keypair, PublicKey, SecretKey}; +use std::process::Command; +use std::str::FromStr; +use url::Url; + +#[cfg(test)] +fn mine_blocks(n: u64, addr: &Address) { + Command::new("docker") + .arg("exec") + .arg("sia-docker") + .arg("walletd") + .arg("mine") + .arg(format!("-addr={}", addr)) + .arg(format!("-n={}", n)) + .status() + .expect("Failed to execute docker command"); +} + +#[test] +fn test_sia_new_client() { + let conf = SiaHttpConf { + url: Url::parse("http://localhost:9980/").unwrap(), + password: "password".to_string(), + }; + let _api_client = block_on(SiaApiClient::new(conf)).unwrap(); +} + +#[test] +fn test_sia_client_bad_auth() { + let conf = SiaHttpConf { + url: Url::parse("http://localhost:9980/").unwrap(), + password: "foo".to_string(), + }; + let result = block_on(SiaApiClient::new(conf)); + assert!(matches!(result, Err(SiaApiClientError::UnexpectedHttpStatus(401)))); +} + +#[test] +fn test_sia_client_consensus_tip() { + let conf = SiaHttpConf { + url: Url::parse("http://localhost:9980/").unwrap(), + password: "password".to_string(), + }; + let api_client = block_on(SiaApiClient::new(conf)).unwrap(); + let _response = block_on(api_client.dispatcher(ConsensusTipRequest)).unwrap(); +} + +// This test likely needs to be removed because mine_blocks has possibility of interfering with other async tests +// related to block height +#[test] +fn test_sia_client_address_balance() { + let conf = SiaHttpConf { + url: Url::parse("http://localhost:9980/").unwrap(), + password: "password".to_string(), + }; + let api_client = block_on(SiaApiClient::new(conf)).unwrap(); + + let address = + Address::from_str("addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f").unwrap(); + mine_blocks(10, &address); + + let request = AddressBalanceRequest { address }; + let response = block_on(api_client.dispatcher(request)).unwrap(); + + let expected = Currency::new(12919594847110692864, 54210108624275221); + assert_eq!(response.siacoins, expected); + assert_eq!(expected.to_u128(), 1000000000000000000000000000000000000); +} + +#[test] +fn test_sia_client_build_tx() { + let conf = SiaHttpConf { + url: Url::parse("http://localhost:9980/").unwrap(), + password: "password".to_string(), + }; + let api_client = block_on(SiaApiClient::new(conf)).unwrap(); + let sk: SecretKey = SecretKey::from_bytes( + &hex::decode("0100000000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + let pk: PublicKey = (&sk).into(); + let keypair = Keypair { public: pk, secret: sk }; + let spend_policy = SpendPolicy::PublicKey(pk); + + let address = spend_policy.address(); + + mine_blocks(201, &address); + + let utxos = block_on(api_client.dispatcher(AddressUtxosRequest { + address: address.clone(), + })) + .unwrap(); + let spend_this = utxos[0].clone(); + let vin = spend_this.clone(); + println!("utxo[0]: {:?}", spend_this); + let vout = SiacoinOutput { + value: spend_this.siacoin_output.value, + address, + }; + let tx = V2TransactionBuilder::new(0u64.into()) + .add_siacoin_input(vin, spend_policy) + .add_siacoin_output(vout) + .sign_simple(vec![&keypair]) + .unwrap() + .build(); + + let req = TxpoolBroadcastRequest { + transactions: vec![], + v2transactions: vec![tx], + }; + let _response = block_on(api_client.dispatcher(req)).unwrap(); +} diff --git a/mm2src/mm2_main/tests/docker_tests_main.rs b/mm2src/mm2_main/tests/docker_tests_main.rs index 5225f1b029..5f0045ea33 100644 --- a/mm2src/mm2_main/tests/docker_tests_main.rs +++ b/mm2src/mm2_main/tests/docker_tests_main.rs @@ -173,6 +173,7 @@ fn remove_docker_containers(name: &str) { .expect("Failed to execute docker command"); } } + fn prepare_runtime_dir() -> std::io::Result { let project_root = { let mut current_dir = std::env::current_dir().unwrap(); diff --git a/mm2src/mm2_main/tests/docker_tests_sia_unique.rs b/mm2src/mm2_main/tests/docker_tests_sia_unique.rs new file mode 100644 index 0000000000..521da60e01 --- /dev/null +++ b/mm2src/mm2_main/tests/docker_tests_sia_unique.rs @@ -0,0 +1,106 @@ +#![allow(unused_imports, dead_code)] +#![cfg(feature = "enable-sia")] +#![feature(async_closure)] +#![feature(custom_test_frameworks)] +#![feature(test)] +#![test_runner(docker_tests_runner)] +#![feature(drain_filter)] +#![feature(hash_raw_entry)] +#![cfg(not(target_arch = "wasm32"))] + +#[cfg(test)] +#[macro_use] +extern crate common; +#[cfg(test)] +#[macro_use] +extern crate gstuff; +#[cfg(test)] +#[macro_use] +extern crate lazy_static; +#[cfg(test)] +#[macro_use] +extern crate serde_json; +#[cfg(test)] extern crate ser_error_derive; +#[cfg(test)] extern crate test; + +use std::env; +use std::io::{BufRead, BufReader}; +use std::path::PathBuf; +use std::process::Command; +use test::{test_main, StaticBenchFn, StaticTestFn, TestDescAndFn}; +use testcontainers::clients::Cli; + +mod docker_tests; +use docker_tests::docker_tests_common::*; + +#[allow(dead_code)] mod integration_tests_common; + +/// Custom test runner intended to initialize the SIA coin daemon in a Docker container. +pub fn docker_tests_runner(tests: &[&TestDescAndFn]) { + let docker = Cli::default(); + let mut containers = vec![]; + + let skip_docker_tests_runner = std::env::var("SKIP_DOCKER_TESTS_RUNNER") + .map(|v| v == "1") + .unwrap_or(false); + + if !skip_docker_tests_runner { + const IMAGES: &[&str] = &[SIA_DOCKER_IMAGE_WITH_TAG]; + + for image in IMAGES { + pull_docker_image(image); + remove_docker_containers(image); + } + + let sia_node = sia_docker_node(&docker, "SIA", 9980); + println!("ran container?"); + containers.push(sia_node); + } + // detect if docker is installed + // skip the tests that use docker if not installed + let owned_tests: Vec<_> = tests + .iter() + .map(|t| match t.testfn { + StaticTestFn(f) => TestDescAndFn { + testfn: StaticTestFn(f), + desc: t.desc.clone(), + }, + StaticBenchFn(f) => TestDescAndFn { + testfn: StaticBenchFn(f), + desc: t.desc.clone(), + }, + _ => panic!("non-static tests passed to lp_coins test runner"), + }) + .collect(); + let args: Vec = env::args().collect(); + test_main(&args, owned_tests, None); +} + +fn pull_docker_image(name: &str) { + Command::new("docker") + .arg("pull") + .arg(name) + .status() + .expect("Failed to execute docker command"); +} + +fn remove_docker_containers(name: &str) { + let stdout = Command::new("docker") + .arg("ps") + .arg("-f") + .arg(format!("ancestor={}", name)) + .arg("-q") + .output() + .expect("Failed to execute docker command"); + + let reader = BufReader::new(stdout.stdout.as_slice()); + let ids: Vec<_> = reader.lines().map(|line| line.unwrap()).collect(); + if !ids.is_empty() { + Command::new("docker") + .arg("rm") + .arg("-f") + .args(ids) + .status() + .expect("Failed to execute docker command"); + } +} From 7ca140a18957034be9b28adc53deda38760ee07d Mon Sep 17 00:00:00 2001 From: Samuel Onoja Date: Fri, 30 Aug 2024 12:20:45 +0100 Subject: [PATCH 07/29] chore(test): turn on debug assertion (#2204) This commit removes zcoin additional chain validation to turn on debug assertion. --- Cargo.toml | 6 +----- mm2src/coins/z_coin/storage.rs | 23 ++--------------------- 2 files changed, 3 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 758587e557..deec6b843f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,10 +61,6 @@ codegen-units = 1 # lto = true panic = "abort" -[profile.test] -# required to avoid a long running process of librustcash additional chain validation that is enabled with debug assertions -debug-assertions = false - [profile.dev] opt-level = 0 debug = 1 @@ -74,4 +70,4 @@ incremental = true codegen-units = 256 [profile.release.package.mocktopus] -opt-level = 1 # TODO: MIR fails on optimizing this dependency, remove that.. \ No newline at end of file +opt-level = 1 # TODO: MIR fails on optimizing this dependency, remove that.. diff --git a/mm2src/coins/z_coin/storage.rs b/mm2src/coins/z_coin/storage.rs index 08e478f27a..b3c2c108c4 100644 --- a/mm2src/coins/z_coin/storage.rs +++ b/mm2src/coins/z_coin/storage.rs @@ -157,27 +157,8 @@ pub async fn scan_cached_block( ) }; - // Enforce that all roots match. This is slow, so only include in debug builds. - #[cfg(debug_assertions)] - { - let cur_root = tree.root(); - if witnesses.iter().any(|row| row.1.root() != cur_root) { - return Err(Error::InvalidWitnessAnchor(row.0, current_height).into()); - } - for tx in &txs { - for output in tx.shielded_outputs.iter() { - if output.witness.root() != cur_root { - return Err(Error::InvalidNewWitnessAnchor( - output.index, - tx.txid, - current_height, - output.witness.root(), - ) - .into()); - } - } - } - } + // To enforce that all roots match, + // see -> https://github.com/KomodoPlatform/librustzcash/blob/e92443a7bbd1c5e92e00e6deb45b5a33af14cea4/zcash_client_backend/src/data_api/chain.rs#L304-L326 let new_witnesses = data_guard .advance_by_block( From ffed8606ae5e74076b295018c4fec04ab2da42db Mon Sep 17 00:00:00 2001 From: Omer Yacine Date: Thu, 5 Sep 2024 03:18:59 +0300 Subject: [PATCH 08/29] fix(coins): add p2p feature to mm2_net dependency (#2210) This makes coins a separate crate that can be tested and checked separately. --- mm2src/coins/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm2src/coins/Cargo.toml b/mm2src/coins/Cargo.toml index c5eeb570ea..c870e9baf0 100644 --- a/mm2src/coins/Cargo.toml +++ b/mm2src/coins/Cargo.toml @@ -78,7 +78,7 @@ mm2_event_stream = { path = "../mm2_event_stream" } mm2_git = { path = "../mm2_git" } mm2_io = { path = "../mm2_io" } mm2_metrics = { path = "../mm2_metrics" } -mm2_net = { path = "../mm2_net" } +mm2_net = { path = "../mm2_net", features = ["p2p"] } mm2_number = { path = "../mm2_number"} mm2_rpc = { path = "../mm2_rpc" } mm2_state_machine = { path = "../mm2_state_machine" } From c3666303c3401efe533aa06c9f1d38834e3f6026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C3=96zkan?= Date: Fri, 6 Sep 2024 23:50:04 +0300 Subject: [PATCH 09/29] chore(WASM-builds): remove `wasm-opt` overriding (#2200) --- mm2src/mm2_bin_lib/Cargo.toml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/mm2src/mm2_bin_lib/Cargo.toml b/mm2src/mm2_bin_lib/Cargo.toml index 94e4e6d88e..7415f21b4f 100644 --- a/mm2src/mm2_bin_lib/Cargo.toml +++ b/mm2src/mm2_bin_lib/Cargo.toml @@ -10,11 +10,6 @@ authors = ["James Lee", "Artem Pikulin", "Artem Grinblat", "Omar S.", "Onur Ozka edition = "2018" default-run = "kdf" -# wasm-opt reduces the size from 17 Mb to 14. But it runs for few minutes, which is not good for CI. -# For production builds, it's recommended to run wasm-opt separately. -[package.metadata.wasm-pack.profile.release] -wasm-opt = false - [features] custom-swap-locktime = ["mm2_main/custom-swap-locktime"] # only for testing purposes, should never be activated on release builds. native = ["mm2_main/native"] # Deprecated From d1a8ea7a17d6199c1a2460ab8a3d360a968ea36c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C3=96zkan?= Date: Tue, 10 Sep 2024 09:26:03 +0300 Subject: [PATCH 10/29] chore(RPCs): rename `get_peers_info` RPC to `get_directly_connected_peers` (#2195) `get_peers_info` RPC had a misleading name, it only returns the directly connected peers, not all network peers. This commit renames the RPC to `get_directly_connected_peers` which causes a breaking change. --- mm2src/mm2_libp2p/src/atomicdex_behaviour.rs | 4 ++-- mm2src/mm2_main/src/rpc/dispatcher/dispatcher_legacy.rs | 2 +- .../mm2_main/src/rpc/lp_commands/lp_commands_legacy.rs | 4 ++-- mm2src/mm2_net/src/network_event.rs | 4 ++-- mm2src/mm2_p2p/src/behaviours/atomicdex.rs | 4 ++-- mm2src/mm2_p2p/src/lib.rs | 9 +++++---- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/mm2src/mm2_libp2p/src/atomicdex_behaviour.rs b/mm2src/mm2_libp2p/src/atomicdex_behaviour.rs index 482251fb93..c0f2c47725 100644 --- a/mm2src/mm2_libp2p/src/atomicdex_behaviour.rs +++ b/mm2src/mm2_libp2p/src/atomicdex_behaviour.rs @@ -46,8 +46,8 @@ const ANNOUNCE_INTERVAL: Duration = Duration::from_secs(600); const ANNOUNCE_INITIAL_DELAY: Duration = Duration::from_secs(60); const CHANNEL_BUF_SIZE: usize = 1024 * 8; -/// Returns info about connected peers -pub async fn get_peers_info(mut cmd_tx: AdexCmdTx) -> BTreeMap> { +/// Returns info about directly connected peers. +pub async fn get_directly_connected_peers(mut cmd_tx: AdexCmdTx) -> BTreeMap> { let (result_tx, rx) = oneshot::channel(); let cmd = AdexBehaviourCmd::GetPeersInfo { result_tx }; cmd_tx.send(cmd).await.expect("Rx should be present"); diff --git a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher_legacy.rs b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher_legacy.rs index bcb09c4d51..2415bc31ef 100644 --- a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher_legacy.rs +++ b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher_legacy.rs @@ -73,11 +73,11 @@ pub fn dispatcher(req: Json, ctx: MmArc) -> DispatcherRes { "electrum" => hyres(electrum(ctx, req)), "enable" => hyres(enable(ctx, req)), "get_enabled_coins" => hyres(get_enabled_coins(ctx)), + "get_directly_connected_peers" => hyres(get_directly_connected_peers(ctx)), "get_gossip_mesh" => hyres(get_gossip_mesh(ctx)), "get_gossip_peer_topics" => hyres(get_gossip_peer_topics(ctx)), "get_gossip_topic_peers" => hyres(get_gossip_topic_peers(ctx)), "get_my_peer_id" => hyres(get_my_peer_id(ctx)), - "get_peers_info" => hyres(get_peers_info(ctx)), "get_relay_mesh" => hyres(get_relay_mesh(ctx)), "get_trade_fee" => hyres(get_trade_fee(ctx, req)), // "fundvalue" => lp_fundvalue (ctx, req, false), 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 2769539d9b..7db7c5b02b 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 @@ -316,10 +316,10 @@ pub fn version(ctx: MmArc) -> HyRes { } } -pub async fn get_peers_info(ctx: MmArc) -> Result>, String> { +pub async fn get_directly_connected_peers(ctx: MmArc) -> Result>, String> { let ctx = P2PContext::fetch_from_mm_arc(&ctx); let cmd_tx = ctx.cmd_tx.lock().clone(); - let result = mm2_libp2p::get_peers_info(cmd_tx).await; + let result = mm2_libp2p::get_directly_connected_peers(cmd_tx).await; let result = json!({ "result": result, }); diff --git a/mm2src/mm2_net/src/network_event.rs b/mm2src/mm2_net/src/network_event.rs index b88655f383..9ff7ae15f7 100644 --- a/mm2src/mm2_net/src/network_event.rs +++ b/mm2src/mm2_net/src/network_event.rs @@ -30,14 +30,14 @@ impl EventBehaviour for NetworkEvent { loop { let p2p_cmd_tx = p2p_ctx.cmd_tx.lock().clone(); - let peers_info = atomicdex::get_peers_info(p2p_cmd_tx.clone()).await; + let directly_connected_peers = atomicdex::get_directly_connected_peers(p2p_cmd_tx.clone()).await; let gossip_mesh = atomicdex::get_gossip_mesh(p2p_cmd_tx.clone()).await; let gossip_peer_topics = atomicdex::get_gossip_peer_topics(p2p_cmd_tx.clone()).await; let gossip_topic_peers = atomicdex::get_gossip_topic_peers(p2p_cmd_tx.clone()).await; let relay_mesh = atomicdex::get_relay_mesh(p2p_cmd_tx).await; let event_data = json!({ - "peers_info": peers_info, + "directly_connected_peers": directly_connected_peers, "gossip_mesh": gossip_mesh, "gossip_peer_topics": gossip_peer_topics, "gossip_topic_peers": gossip_topic_peers, diff --git a/mm2src/mm2_p2p/src/behaviours/atomicdex.rs b/mm2src/mm2_p2p/src/behaviours/atomicdex.rs index db907711ba..9d58da4e1e 100644 --- a/mm2src/mm2_p2p/src/behaviours/atomicdex.rs +++ b/mm2src/mm2_p2p/src/behaviours/atomicdex.rs @@ -162,8 +162,8 @@ pub enum AdexBehaviourCmd { }, } -/// Returns info about connected peers -pub async fn get_peers_info(mut cmd_tx: AdexCmdTx) -> HashMap> { +/// Returns info about directly connected peers. +pub async fn get_directly_connected_peers(mut cmd_tx: AdexCmdTx) -> HashMap> { let (result_tx, rx) = oneshot::channel(); let cmd = AdexBehaviourCmd::GetPeersInfo { result_tx }; cmd_tx.send(cmd).await.expect("Rx should be present"); diff --git a/mm2src/mm2_p2p/src/lib.rs b/mm2src/mm2_p2p/src/lib.rs index e9a8f78ad4..8e6e6db159 100644 --- a/mm2src/mm2_p2p/src/lib.rs +++ b/mm2src/mm2_p2p/src/lib.rs @@ -16,10 +16,11 @@ use sha2::{Digest, Sha256}; pub use crate::swarm_runtime::SwarmRuntime; // atomicdex related re-exports -pub use behaviours::atomicdex::{get_gossip_mesh, get_gossip_peer_topics, get_gossip_topic_peers, get_peers_info, - get_relay_mesh, spawn_gossipsub, AdexBehaviourCmd, AdexBehaviourError, - AdexBehaviourEvent, AdexCmdTx, AdexEventRx, AdexResponse, AdexResponseChannel, - GossipsubEvent, GossipsubMessage, MessageId, NodeType, TopicHash, WssCerts}; +pub use behaviours::atomicdex::{get_directly_connected_peers, get_gossip_mesh, get_gossip_peer_topics, + get_gossip_topic_peers, get_relay_mesh, spawn_gossipsub, AdexBehaviourCmd, + AdexBehaviourError, AdexBehaviourEvent, AdexCmdTx, AdexEventRx, AdexResponse, + AdexResponseChannel, GossipsubEvent, GossipsubMessage, MessageId, NodeType, TopicHash, + WssCerts}; // peers-exchange re-exports pub use behaviours::peers_exchange::PeerAddresses; From 079ea5e4fd9929a73a6f3a7f62240933d7537a43 Mon Sep 17 00:00:00 2001 From: shamardy <39480341+shamardy@users.noreply.github.com> Date: Fri, 13 Sep 2024 15:11:15 +0300 Subject: [PATCH 11/29] fix(db): stop creating the all-zeroes dir on KDF start (#2218) KDF was creating the default all zeroes db dir on start which is not used, this commit fixes this. --- mm2src/mm2_core/src/mm_ctx.rs | 7 +++++-- mm2src/mm2_main/src/lp_native_dex.rs | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/mm2src/mm2_core/src/mm_ctx.rs b/mm2src/mm2_core/src/mm_ctx.rs index e891f0b649..ec7ceb036a 100644 --- a/mm2src/mm2_core/src/mm_ctx.rs +++ b/mm2src/mm2_core/src/mm_ctx.rs @@ -287,10 +287,13 @@ impl MmCtx { }) } + /// Returns the path to the MM databases root. + #[cfg(not(target_arch = "wasm32"))] + pub fn db_root(&self) -> PathBuf { path_to_db_root(self.conf["dbdir"].as_str()) } + #[cfg(not(target_arch = "wasm32"))] pub fn wallet_file_path(&self, wallet_name: &str) -> PathBuf { - let db_root = path_to_db_root(self.conf["dbdir"].as_str()); - db_root.join(wallet_name.to_string() + ".dat") + self.db_root().join(wallet_name.to_string() + ".dat") } /// MM database path. diff --git a/mm2src/mm2_main/src/lp_native_dex.rs b/mm2src/mm2_main/src/lp_native_dex.rs index 3b73947ffb..bd875511b0 100644 --- a/mm2src/mm2_main/src/lp_native_dex.rs +++ b/mm2src/mm2_main/src/lp_native_dex.rs @@ -498,9 +498,11 @@ pub async fn lp_init_continue(ctx: MmArc) -> MmInitResult<()> { pub async fn lp_init(ctx: MmArc, version: String, datetime: String) -> MmInitResult<()> { info!("Version: {} DT {}", version, datetime); + // Ensure the database root directory exists before initializing the wallet passphrase. + // This is necessary to store the encrypted wallet passphrase if needed. #[cfg(not(target_arch = "wasm32"))] { - let dbdir = ctx.dbdir(); + let dbdir = ctx.db_root(); fs::create_dir_all(&dbdir).map_to_mm(|e| MmInitError::ErrorCreatingDbDir { path: dbdir.clone(), error: e.to_string(), From d6bee9ddd9b9d08db24a827c54699c5dec0f1b69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C3=96zkan?= Date: Wed, 18 Sep 2024 12:57:35 +0300 Subject: [PATCH 12/29] remove the non-sense arguments (#2216) Signed-off-by: onur-ozkan --- mm2src/common/common.rs | 13 ------------- mm2src/mm2_main/src/mm2.rs | 11 ----------- 2 files changed, 24 deletions(-) diff --git a/mm2src/common/common.rs b/mm2src/common/common.rs index c98fe610d2..0eeb726ad4 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_main/src/mm2.rs b/mm2src/mm2_main/src/mm2.rs index 7dcc5572cb..4ecfb5706d 100644 --- a/mm2src/mm2_main/src/mm2.rs +++ b/mm2src/mm2_main/src/mm2.rs @@ -42,7 +42,6 @@ #[cfg(not(target_arch = "wasm32"))] use common::block_on; use common::crash_reports::init_crash_reports; -use common::double_panic_crash; use common::log::LogLevel; use common::password_policy::password_policy; use mm2_core::mm_ctx::MmCtxBuilder; @@ -248,16 +247,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"), From 3b271721ac7db39f5c99e8ac660f8cea4bfb1eea Mon Sep 17 00:00:00 2001 From: shamardy <39480341+shamardy@users.noreply.github.com> Date: Thu, 19 Sep 2024 03:40:16 +0300 Subject: [PATCH 13/29] docs(README): fix typos (#2212) --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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! ** From baa72a74fef4b0e22f766186a04a6252ed785b45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C3=96zkan?= Date: Thu, 19 Sep 2024 04:35:10 +0300 Subject: [PATCH 14/29] feat(core): handling CTRL-C signal with graceful shutdown (#2213) --- Cargo.toml | 2 +- mm2src/mm2_bin_lib/src/lib.rs | 2 +- mm2src/mm2_core/src/mm_ctx.rs | 15 ++++++++++- mm2src/mm2_main/Cargo.toml | 2 +- mm2src/mm2_main/src/mm2.rs | 27 +++++++++++++++++-- .../src/rpc/lp_commands/lp_commands_legacy.rs | 19 +------------ 6 files changed, 43 insertions(+), 24 deletions(-) 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/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..3013bce994 100644 --- a/mm2src/mm2_main/Cargo.toml +++ b/mm2src/mm2_main/Cargo.toml @@ -116,7 +116,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 4ecfb5706d..734e71becf 100644 --- a/mm2src/mm2_main/src/mm2.rs +++ b/mm2src/mm2_main/src/mm2.rs @@ -42,6 +42,7 @@ #[cfg(not(target_arch = "wasm32"))] use common::block_on; use common::crash_reports::init_crash_reports; +use common::log; use common::log::LogLevel; use common::password_policy::password_policy; use mm2_core::mm_ctx::MmCtxBuilder; @@ -53,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}; @@ -63,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; @@ -74,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; @@ -159,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. 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, From cde7f365265a93ab2d8718a146eac498c2a46bce Mon Sep 17 00:00:00 2001 From: Samuel Onoja Date: Mon, 23 Sep 2024 10:28:05 +0000 Subject: [PATCH 15/29] fix(clippy): fix coins mod clippy warnings in wasm (#2224) --- mm2src/coins/eth/eth_tests.rs | 58 +++++++--- mm2src/coins/lp_coins.rs | 4 +- .../coins/z_coin/storage/walletdb/wasm/mod.rs | 105 ++++++++++++++---- 3 files changed, 127 insertions(+), 40 deletions(-) diff --git a/mm2src/coins/eth/eth_tests.rs b/mm2src/coins/eth/eth_tests.rs index 9332594931..c20c12046e 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, now_sec}; -#[cfg(not(target_arch = "wasm32"))] -use ethkey::{Generator, Random}; +use crate::IguanaPrivKey; +use common::block_on; 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)))); @@ -306,6 +313,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. @@ -361,6 +369,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; @@ -463,6 +472,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())))); @@ -483,6 +493,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; @@ -533,6 +544,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() { @@ -562,6 +574,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); @@ -589,6 +602,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); @@ -662,6 +678,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( @@ -700,6 +717,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); @@ -727,6 +745,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(); @@ -824,6 +843,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(); @@ -842,6 +862,7 @@ fn test_message_hash() { ); } +#[cfg(not(target_arch = "wasm32"))] #[test] fn test_sign_verify_message() { let key_pair = KeyPair::from_secret_slice( @@ -866,6 +887,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(); // From 4fc838838ee9aa5878bcedebc8be4dd168f4f3b5 Mon Sep 17 00:00:00 2001 From: Samuel Onoja Date: Mon, 23 Sep 2024 14:32:59 +0000 Subject: [PATCH 16/29] fix(native-rpc): remove escaped response body (#2219) --- mm2src/mm2_main/Cargo.toml | 3 --- mm2src/mm2_main/src/rpc.rs | 50 ++------------------------------------ 2 files changed, 2 insertions(+), 51 deletions(-) diff --git a/mm2src/mm2_main/Cargo.toml b/mm2src/mm2_main/Cargo.toml index 3013bce994..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" } 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 From 5c324f2cc5122bbc4a2a527941e908ccffcf6f2c Mon Sep 17 00:00:00 2001 From: Omer Yacine Date: Tue, 24 Sep 2024 11:56:02 +0300 Subject: [PATCH 17/29] chore(tests): don't use `.wait()` and use `block_on` instead (#2220) Fixes an issue regarding a tokio tcp stream that breaks because it was spawned in a non-tokio runtime. the `.wait` methods were using a different runtime - from futures - to run the future. This commit uses `block_on(fut.compat())` from KDF's tokio runtime instead. --- clippy.toml | 4 + mm2src/coins/eth/eth_tests.rs | 54 +- mm2src/coins/lightning/ln_platform.rs | 22 +- mm2src/coins/qrc20/qrc20_tests.rs | 201 +-- mm2src/coins/solana/solana_tests.rs | 6 +- mm2src/coins/tendermint/tendermint_coin.rs | 112 +- mm2src/coins/utxo/slp.rs | 5 +- mm2src/coins/utxo/utxo_tests.rs | 660 +++---- mm2src/coins/z_coin/z_coin_native_tests.rs | 25 +- mm2src/common/common.rs | 13 + mm2src/mm2_err_handle/src/map_to_mm_fut.rs | 2 +- mm2src/mm2_err_handle/src/mm_error.rs | 7 +- .../tests/docker_tests/docker_tests_common.rs | 75 +- .../tests/docker_tests/docker_tests_inner.rs | 51 +- .../tests/docker_tests/eth_docker_tests.rs | 35 +- .../tests/docker_tests/qrc20_tests.rs | 236 +-- .../tests/docker_tests/swap_proto_v2_tests.rs | 9 +- .../tests/docker_tests/swap_watcher_tests.rs | 1580 ++++++++--------- 18 files changed, 1446 insertions(+), 1651 deletions(-) create mode 100644 clippy.toml diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 0000000000..068d7e886b --- /dev/null +++ b/clippy.toml @@ -0,0 +1,4 @@ +[[disallowed-methods]] +path = "futures::future::Future::wait" +replacement = "common::block_on_f01" +reason = "Use the default KDF async executor." \ No newline at end of file diff --git a/mm2src/coins/eth/eth_tests.rs b/mm2src/coins/eth/eth_tests.rs index c20c12046e..03705ff4ca 100644 --- a/mm2src/coins/eth/eth_tests.rs +++ b/mm2src/coins/eth/eth_tests.rs @@ -1,6 +1,6 @@ use super::*; use crate::IguanaPrivKey; -use common::block_on; +use common::{block_on, block_on_f01}; use mm2_core::mm_ctx::MmCtxBuilder; cfg_native!( @@ -191,18 +191,16 @@ fn test_wait_for_payment_spend_timeout() { 184, 42, 106, ]; - assert!(coin - .wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { - tx_bytes: &tx_bytes, - secret_hash: &[], - wait_until, - from_block, - swap_contract_address: &coin.swap_contract_address(), - check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, - watcher_reward: false - }) - .wait() - .is_err()); + assert!(block_on_f01(coin.wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { + tx_bytes: &tx_bytes, + secret_hash: &[], + wait_until, + from_block, + swap_contract_address: &coin.swap_contract_address(), + check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, + watcher_reward: false + })) + .is_err()); } #[cfg(not(target_arch = "wasm32"))] @@ -229,7 +227,7 @@ fn test_withdraw_impl_manual_fee() { memo: None, ibc_source_channel: None, }; - coin.get_balance().wait().unwrap(); + block_on_f01(coin.get_balance()).unwrap(); let tx_details = block_on(withdraw_impl(coin, withdraw_req)).unwrap(); let expected = Some( @@ -278,7 +276,7 @@ fn test_withdraw_impl_fee_details() { memo: None, ibc_source_channel: None, }; - coin.get_balance().wait().unwrap(); + block_on_f01(coin.get_balance()).unwrap(); let tx_details = block_on(withdraw_impl(coin, withdraw_req)).unwrap(); let expected = Some( @@ -486,10 +484,8 @@ fn get_receiver_trade_preimage() { paid_from_trading_vol: false, }; - let actual = coin - .get_receiver_trade_fee(FeeApproxStage::WithoutApprox) - .wait() - .expect("!get_sender_trade_fee"); + let actual = + block_on_f01(coin.get_receiver_trade_fee(FeeApproxStage::WithoutApprox)).expect("!get_sender_trade_fee"); assert_eq!(actual, expected_fee); } @@ -595,7 +591,9 @@ fn validate_dex_fee_invalid_sender_eth() { min_block_number: 0, uuid: &[], }; - let error = coin.validate_fee(validate_fee_args).wait().unwrap_err().into_inner(); + let error = block_on_f01(coin.validate_fee(validate_fee_args)) + .unwrap_err() + .into_inner(); match error { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("was sent from wrong address")), _ => panic!("Expected `WrongPaymentTx` wrong sender address, found {:?}", error), @@ -631,7 +629,9 @@ fn validate_dex_fee_invalid_sender_erc() { min_block_number: 0, uuid: &[], }; - let error = coin.validate_fee(validate_fee_args).wait().unwrap_err().into_inner(); + let error = block_on_f01(coin.validate_fee(validate_fee_args)) + .unwrap_err() + .into_inner(); match error { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("was sent from wrong address")), _ => panic!("Expected `WrongPaymentTx` wrong sender address, found {:?}", error), @@ -671,7 +671,9 @@ fn validate_dex_fee_eth_confirmed_before_min_block() { min_block_number: 11784793, uuid: &[], }; - let error = coin.validate_fee(validate_fee_args).wait().unwrap_err().into_inner(); + let error = block_on_f01(coin.validate_fee(validate_fee_args)) + .unwrap_err() + .into_inner(); match error { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("confirmed before min_block")), _ => panic!("Expected `WrongPaymentTx` early confirmation, found {:?}", error), @@ -710,7 +712,9 @@ fn validate_dex_fee_erc_confirmed_before_min_block() { min_block_number: 11823975, uuid: &[], }; - let error = coin.validate_fee(validate_fee_args).wait().unwrap_err().into_inner(); + let error = block_on_f01(coin.validate_fee(validate_fee_args)) + .unwrap_err() + .into_inner(); match error { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("confirmed before min_block")), _ => panic!("Expected `WrongPaymentTx` early confirmation, found {:?}", error), @@ -834,9 +838,7 @@ fn polygon_check_if_my_payment_sent() { amount: &BigDecimal::default(), payment_instructions: &None, }; - let my_payment = coin - .check_if_my_payment_sent(if_my_payment_sent_args) - .wait() + let my_payment = block_on_f01(coin.check_if_my_payment_sent(if_my_payment_sent_args)) .unwrap() .unwrap(); let expected_hash = BytesJson::from("69a20008cea0c15ee483b5bbdff942752634aa072dfd2ff715fe87eec302de11"); diff --git a/mm2src/coins/lightning/ln_platform.rs b/mm2src/coins/lightning/ln_platform.rs index 4d0573af95..0a5fae0a4f 100644 --- a/mm2src/coins/lightning/ln_platform.rs +++ b/mm2src/coins/lightning/ln_platform.rs @@ -15,7 +15,7 @@ use bitcoin::hash_types::{BlockHash, TxMerkleNode, Txid}; use bitcoin_hashes::{sha256d, Hash}; use common::executor::{abortable_queue::AbortableQueue, AbortableSystem, SpawnFuture, Timer}; use common::log::{debug, error, info}; -use common::wait_until_sec; +use common::{block_on_f01, wait_until_sec}; use futures::compat::Future01CompatExt; use futures::future::join_all; use keys::hash::H256; @@ -570,17 +570,15 @@ impl FeeEstimator for Platform { ConfirmationTarget::HighPriority => self.confirmations_targets.high_priority, }; let fee_per_kb = tokio::task::block_in_place(move || { - self.rpc_client() - .estimate_fee_sat( - platform_coin.decimals(), - // Todo: when implementing Native client detect_fee_method should be used for Native and - // EstimateFeeMethod::Standard for Electrum - &EstimateFeeMethod::Standard, - &conf.estimate_fee_mode, - n_blocks, - ) - .wait() - .unwrap_or(latest_fees) + block_on_f01(self.rpc_client().estimate_fee_sat( + platform_coin.decimals(), + // Todo: when implementing Native client detect_fee_method should be used for Native and + // EstimateFeeMethod::Standard for Electrum + &EstimateFeeMethod::Standard, + &conf.estimate_fee_mode, + n_blocks, + )) + .unwrap_or(latest_fees) }); // Set default fee to last known fee for the corresponding confirmation target diff --git a/mm2src/coins/qrc20/qrc20_tests.rs b/mm2src/coins/qrc20/qrc20_tests.rs index 2caf87c3bf..d77afa2f9b 100644 --- a/mm2src/coins/qrc20/qrc20_tests.rs +++ b/mm2src/coins/qrc20/qrc20_tests.rs @@ -1,6 +1,6 @@ use super::*; use crate::{DexFee, TxFeeDetails, WaitForHTLCTxSpendArgs}; -use common::{block_on, wait_until_sec, DEX_FEE_ADDR_RAW_PUBKEY}; +use common::{block_on, block_on_f01, wait_until_sec, DEX_FEE_ADDR_RAW_PUBKEY}; use crypto::Secp256k1Secret; use itertools::Itertools; use keys::Address; @@ -96,7 +96,7 @@ fn test_withdraw_to_p2sh_address_should_fail() { memo: None, ibc_source_channel: None, }; - let err = coin.withdraw(req).wait().unwrap_err().into_inner(); + let err = block_on_f01(coin.withdraw(req)).unwrap_err().into_inner(); let expect = WithdrawError::InvalidAddress("QRC20 can be sent to P2PKH addresses only".to_owned()); assert_eq!(err, expect); } @@ -112,19 +112,22 @@ fn test_withdraw_impl_fee_details() { let (_ctx, coin) = qrc20_coin_for_test(priv_key, None); Qrc20Coin::get_unspent_ordered_list.mock_safe(|coin, _| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 1000000000, - height: Default::default(), - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1000000000, + height: Default::default(), + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); let withdraw_req = WithdrawRequest { @@ -140,7 +143,7 @@ fn test_withdraw_impl_fee_details() { memo: None, ibc_source_channel: None, }; - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); let expected: Qrc20FeeDetails = json::from_value(json!({ "coin": "QTUM", @@ -287,7 +290,7 @@ fn test_wait_for_confirmations_excepted() { wait_until, check_every, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); // tx_hash: ed53b97deb2ad76974c972cb084f6ba63bd9f16c91c4a39106a20c6d14599b2a // `erc20Payment` contract call excepted @@ -299,7 +302,7 @@ fn test_wait_for_confirmations_excepted() { wait_until, check_every, }; - let error = coin.wait_for_confirmations(confirm_payment_input).wait().unwrap_err(); + let error = block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap_err(); log!("error: {:?}", error); assert!(error.contains("Contract call failed with an error: Revert")); @@ -313,7 +316,7 @@ fn test_wait_for_confirmations_excepted() { wait_until, check_every, }; - let error = coin.wait_for_confirmations(confirm_payment_input).wait().unwrap_err(); + let error = block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap_err(); log!("error: {:?}", error); assert!(error.contains("Contract call failed with an error: Revert")); } @@ -333,67 +336,59 @@ fn test_validate_fee() { let amount = BigDecimal::from_str("0.01").unwrap(); - let result = coin - .validate_fee(ValidateFeeArgs { - fee_tx: &tx, - expected_sender: &sender_pub, - fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - dex_fee: &DexFee::Standard(amount.clone().into()), - min_block_number: 0, - uuid: &[], - }) - .wait(); + let result = block_on_f01(coin.validate_fee(ValidateFeeArgs { + fee_tx: &tx, + expected_sender: &sender_pub, + fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, + dex_fee: &DexFee::Standard(amount.clone().into()), + min_block_number: 0, + uuid: &[], + })); assert!(result.is_ok()); let fee_addr_dif = hex::decode("03bc2c7ba671bae4a6fc835244c9762b41647b9827d4780a89a949b984a8ddcc05").unwrap(); - let err = coin - .validate_fee(ValidateFeeArgs { - fee_tx: &tx, - expected_sender: &sender_pub, - fee_addr: &fee_addr_dif, - dex_fee: &DexFee::Standard(amount.clone().into()), - min_block_number: 0, - uuid: &[], - }) - .wait() - .expect_err("Expected an error") - .into_inner(); + let err = block_on_f01(coin.validate_fee(ValidateFeeArgs { + fee_tx: &tx, + expected_sender: &sender_pub, + fee_addr: &fee_addr_dif, + dex_fee: &DexFee::Standard(amount.clone().into()), + min_block_number: 0, + uuid: &[], + })) + .expect_err("Expected an error") + .into_inner(); log!("error: {:?}", err); match err { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("QRC20 Fee tx was sent to wrong address")), _ => panic!("Expected `WrongPaymentTx` wrong receiver address, found {:?}", err), } - let err = coin - .validate_fee(ValidateFeeArgs { - fee_tx: &tx, - expected_sender: &DEX_FEE_ADDR_RAW_PUBKEY, - fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - dex_fee: &DexFee::Standard(amount.clone().into()), - min_block_number: 0, - uuid: &[], - }) - .wait() - .expect_err("Expected an error") - .into_inner(); + let err = block_on_f01(coin.validate_fee(ValidateFeeArgs { + fee_tx: &tx, + expected_sender: &DEX_FEE_ADDR_RAW_PUBKEY, + fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, + dex_fee: &DexFee::Standard(amount.clone().into()), + min_block_number: 0, + uuid: &[], + })) + .expect_err("Expected an error") + .into_inner(); log!("error: {:?}", err); match err { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("was sent from wrong address")), _ => panic!("Expected `WrongPaymentTx` wrong sender address, found {:?}", err), } - let err = coin - .validate_fee(ValidateFeeArgs { - fee_tx: &tx, - expected_sender: &sender_pub, - fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - dex_fee: &DexFee::Standard(amount.clone().into()), - min_block_number: 2000000, - uuid: &[], - }) - .wait() - .expect_err("Expected an error") - .into_inner(); + let err = block_on_f01(coin.validate_fee(ValidateFeeArgs { + fee_tx: &tx, + expected_sender: &sender_pub, + fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, + dex_fee: &DexFee::Standard(amount.clone().into()), + min_block_number: 2000000, + uuid: &[], + })) + .expect_err("Expected an error") + .into_inner(); log!("error: {:?}", err); match err { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("confirmed before min_block")), @@ -401,18 +396,16 @@ fn test_validate_fee() { } let amount_dif = BigDecimal::from_str("0.02").unwrap(); - let err = coin - .validate_fee(ValidateFeeArgs { - fee_tx: &tx, - expected_sender: &sender_pub, - fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - dex_fee: &DexFee::Standard(amount_dif.into()), - min_block_number: 0, - uuid: &[], - }) - .wait() - .expect_err("Expected an error") - .into_inner(); + let err = block_on_f01(coin.validate_fee(ValidateFeeArgs { + fee_tx: &tx, + expected_sender: &sender_pub, + fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, + dex_fee: &DexFee::Standard(amount_dif.into()), + min_block_number: 0, + uuid: &[], + })) + .expect_err("Expected an error") + .into_inner(); log!("error: {:?}", err); match err { ValidatePaymentError::WrongPaymentTx(err) => { @@ -424,18 +417,16 @@ fn test_validate_fee() { // QTUM tx "8a51f0ffd45f34974de50f07c5bf2f0949da4e88433f8f75191953a442cf9310" let tx = TransactionEnum::UtxoTx("020000000113640281c9332caeddd02a8dd0d784809e1ad87bda3c972d89d5ae41f5494b85010000006a47304402207c5c904a93310b8672f4ecdbab356b65dd869a426e92f1064a567be7ccfc61ff02203e4173b9467127f7de4682513a21efb5980e66dbed4da91dff46534b8e77c7ef012102baefe72b3591de2070c0da3853226b00f082d72daa417688b61cb18c1d543d1afeffffff020001b2c4000000001976a9149e032d4b0090a11dc40fe6c47601499a35d55fbb88acbc4dd20c2f0000001976a9144208fa7be80dcf972f767194ad365950495064a488ac76e70800".into()); let sender_pub = hex::decode("02baefe72b3591de2070c0da3853226b00f082d72daa417688b61cb18c1d543d1a").unwrap(); - let err = coin - .validate_fee(ValidateFeeArgs { - fee_tx: &tx, - expected_sender: &sender_pub, - fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - dex_fee: &DexFee::Standard(amount.into()), - min_block_number: 0, - uuid: &[], - }) - .wait() - .expect_err("Expected an error") - .into_inner(); + let err = block_on_f01(coin.validate_fee(ValidateFeeArgs { + fee_tx: &tx, + expected_sender: &sender_pub, + fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, + dex_fee: &DexFee::Standard(amount.into()), + min_block_number: 0, + uuid: &[], + })) + .expect_err("Expected an error") + .into_inner(); log!("error: {:?}", err); match err { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("Expected 'transfer' contract call")), @@ -462,18 +453,16 @@ fn test_wait_for_tx_spend_malicious() { let payment_tx = hex::decode("01000000016601daa208531d20532c460d0c86b74a275f4a126bbffcf4eafdf33835af2859010000006a47304402205825657548bc1b5acf3f4bb2f89635a02b04f3228cd08126e63c5834888e7ac402207ca05fa0a629a31908a97a508e15076e925f8e621b155312b7526a6666b06a76012103693bff1b39e8b5a306810023c29b95397eb395530b106b1820ea235fd81d9ce9ffffffff020000000000000000e35403a0860101284cc49b415b2a8620ad3b72361a5aeba5dffd333fb64750089d935a1ec974d6a91ef4f24ff6ba0000000000000000000000000000000000000000000000000000000001312d00000000000000000000000000d362e096e873eb7907e205fadc6175c6fec7bc44000000000000000000000000783cf0be521101942da509846ea476e683aad8324b6b2e5444c2639cc0fb7bcea5afba3f3cdce239000000000000000000000000000000000000000000000000000000000000000000000000000000005f855c7614ba8b71f3544b93e2f681f996da519a98ace0107ac2203de400000000001976a9149e032d4b0090a11dc40fe6c47601499a35d55fbb88ac415d855f").unwrap(); let wait_until = now_sec() + 1; let from_block = 696245; - let found = coin - .wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { - tx_bytes: &payment_tx, - secret_hash: &[], - wait_until, - from_block, - swap_contract_address: &coin.swap_contract_address(), - check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, - watcher_reward: false, - }) - .wait() - .unwrap(); + let found = block_on_f01(coin.wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { + tx_bytes: &payment_tx, + secret_hash: &[], + wait_until, + from_block, + swap_contract_address: &coin.swap_contract_address(), + check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, + watcher_reward: false, + })) + .unwrap(); let spend_tx = match found { TransactionEnum::UtxoTx(tx) => tx, @@ -731,7 +720,7 @@ fn test_get_trade_fee() { // check if the coin's tx fee is expected check_tx_fee(&coin, ActualTxFee::FixedPerKb(EXPECTED_TX_FEE as u64)); - let actual_trade_fee = coin.get_trade_fee().wait().unwrap(); + let actual_trade_fee = block_on_f01(coin.get_trade_fee()).unwrap(); let expected_trade_fee_amount = big_decimal_from_sat( 2 * CONTRACT_CALL_GAS_FEE + SWAP_PAYMENT_GAS_FEE + EXPECTED_TX_FEE, coin.utxo.decimals, @@ -905,10 +894,8 @@ fn test_receiver_trade_preimage() { // check if the coin's tx fee is expected check_tx_fee(&coin, ActualTxFee::FixedPerKb(EXPECTED_TX_FEE as u64)); - let actual = coin - .get_receiver_trade_fee(FeeApproxStage::WithoutApprox) - .wait() - .expect("!get_receiver_trade_fee"); + let actual = + block_on_f01(coin.get_receiver_trade_fee(FeeApproxStage::WithoutApprox)).expect("!get_receiver_trade_fee"); // only one contract call should be included into the expected trade fee let expected_receiver_fee = big_decimal_from_sat(CONTRACT_CALL_GAS_FEE + EXPECTED_TX_FEE, coin.utxo.decimals); let expected = TradeFee { @@ -935,7 +922,7 @@ fn test_taker_fee_tx_fee() { spendable: BigDecimal::from(5u32), unspendable: BigDecimal::from(0u32), }; - assert_eq!(coin.my_balance().wait().expect("!my_balance"), expected_balance); + assert_eq!(block_on_f01(coin.my_balance()).expect("!my_balance"), expected_balance); let dex_fee_amount = BigDecimal::from(5u32); let actual = block_on(coin.get_fee_to_send_taker_fee( diff --git a/mm2src/coins/solana/solana_tests.rs b/mm2src/coins/solana/solana_tests.rs index fe2b104293..d4126f35c0 100644 --- a/mm2src/coins/solana/solana_tests.rs +++ b/mm2src/coins/solana/solana_tests.rs @@ -1,5 +1,5 @@ use base58::ToBase58; -use common::{block_on, Future01CompatExt}; +use common::{block_on, block_on_f01, Future01CompatExt}; use rpc::v1::types::Bytes; use solana_client::rpc_request::TokenAccountsFilter; use solana_sdk::{bs58, @@ -367,7 +367,7 @@ fn solana_coin_send_and_refund_maker_payment() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let tx = coin.send_maker_payment(args).wait().unwrap(); + let tx = block_on_f01(coin.send_maker_payment(args)).unwrap(); log!("swap tx {:?}", tx); let refund_args = RefundPaymentArgs { @@ -415,7 +415,7 @@ fn solana_coin_send_and_spend_maker_payment() { wait_for_confirmation_until: 0, }; - let tx = coin.send_maker_payment(maker_payment_args).wait().unwrap(); + let tx = block_on_f01(coin.send_maker_payment(maker_payment_args)).unwrap(); log!("swap tx {:?}", tx); let maker_pub = taker_pub; diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index bc5e12f6ff..9c7a1c1d96 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -3313,7 +3313,7 @@ fn parse_expected_sequence_number(e: &str) -> MmResult { @@ -3725,18 +3723,16 @@ pub mod tendermint_coin_tests { data: TxRaw::decode(random_transfer_tx_bytes.as_slice()).unwrap(), }); - let error = coin - .validate_fee(ValidateFeeArgs { - fee_tx: &random_transfer_tx, - expected_sender: &[], - fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - dex_fee: &DexFee::Standard(invalid_amount.clone()), - min_block_number: 0, - uuid: &[1; 16], - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(coin.validate_fee(ValidateFeeArgs { + fee_tx: &random_transfer_tx, + expected_sender: &[], + fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, + dex_fee: &DexFee::Standard(invalid_amount.clone()), + min_block_number: 0, + uuid: &[1; 16], + })) + .unwrap_err() + .into_inner(); println!("{}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("sent to wrong address")), @@ -3758,18 +3754,16 @@ pub mod tendermint_coin_tests { data: TxRaw::decode(dex_fee_tx.encode_to_vec().as_slice()).unwrap(), }); - let error = coin - .validate_fee(ValidateFeeArgs { - fee_tx: &dex_fee_tx, - expected_sender: &[], - fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - dex_fee: &DexFee::Standard(invalid_amount), - min_block_number: 0, - uuid: &[1; 16], - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(coin.validate_fee(ValidateFeeArgs { + fee_tx: &dex_fee_tx, + expected_sender: &[], + fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, + dex_fee: &DexFee::Standard(invalid_amount), + min_block_number: 0, + uuid: &[1; 16], + })) + .unwrap_err() + .into_inner(); println!("{}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("Invalid amount")), @@ -3778,18 +3772,16 @@ pub mod tendermint_coin_tests { let valid_amount: BigDecimal = "0.0001".parse().unwrap(); // valid amount but invalid sender - let error = coin - .validate_fee(ValidateFeeArgs { - fee_tx: &dex_fee_tx, - expected_sender: &DEX_FEE_ADDR_RAW_PUBKEY, - fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - dex_fee: &DexFee::Standard(valid_amount.clone().into()), - min_block_number: 0, - uuid: &[1; 16], - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(coin.validate_fee(ValidateFeeArgs { + fee_tx: &dex_fee_tx, + expected_sender: &DEX_FEE_ADDR_RAW_PUBKEY, + fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, + dex_fee: &DexFee::Standard(valid_amount.clone().into()), + min_block_number: 0, + uuid: &[1; 16], + })) + .unwrap_err() + .into_inner(); println!("{}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("Invalid sender")), @@ -3797,18 +3789,16 @@ pub mod tendermint_coin_tests { } // invalid memo - let error = coin - .validate_fee(ValidateFeeArgs { - fee_tx: &dex_fee_tx, - expected_sender: &pubkey, - fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - dex_fee: &DexFee::Standard(valid_amount.into()), - min_block_number: 0, - uuid: &[1; 16], - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(coin.validate_fee(ValidateFeeArgs { + fee_tx: &dex_fee_tx, + expected_sender: &pubkey, + fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, + dex_fee: &DexFee::Standard(valid_amount.into()), + min_block_number: 0, + uuid: &[1; 16], + })) + .unwrap_err() + .into_inner(); println!("{}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("Invalid memo")), diff --git a/mm2src/coins/utxo/slp.rs b/mm2src/coins/utxo/slp.rs index c559449c22..503611445e 100644 --- a/mm2src/coins/utxo/slp.rs +++ b/mm2src/coins/utxo/slp.rs @@ -1945,6 +1945,7 @@ mod slp_tests { use crate::utxo::GetUtxoListOps; use crate::{utxo::bch::tbch_coin_for_test, TransactionErr}; use common::block_on; + use common::block_on_f01; use mocktopus::mocking::{MockResult, Mockable}; use std::mem::discriminant; @@ -2206,11 +2207,11 @@ mod slp_tests { ]; let tx_bytes_str = hex::encode(tx_bytes); - let err = fusd.send_raw_tx(&tx_bytes_str).wait().unwrap_err(); + let err = block_on_f01(fusd.send_raw_tx(&tx_bytes_str)).unwrap_err(); println!("{:?}", err); assert!(err.contains("is not valid with reason outputs greater than inputs")); - let err2 = fusd.send_raw_tx_bytes(tx_bytes).wait().unwrap_err(); + let err2 = block_on_f01(fusd.send_raw_tx_bytes(tx_bytes)).unwrap_err(); println!("{:?}", err2); assert!(err2.contains("is not valid with reason outputs greater than inputs")); assert_eq!(err, err2); diff --git a/mm2src/coins/utxo/utxo_tests.rs b/mm2src/coins/utxo/utxo_tests.rs index 868f558f1f..9456361c64 100644 --- a/mm2src/coins/utxo/utxo_tests.rs +++ b/mm2src/coins/utxo/utxo_tests.rs @@ -34,13 +34,12 @@ use crate::{BlockHeightAndTime, CoinBalance, ConfirmPaymentInput, DexFee, Iguana use crate::{WaitForHTLCTxSpendArgs, WithdrawFee}; use chain::{BlockHeader, BlockHeaderBits, OutPoint}; use common::executor::Timer; -use common::{block_on, wait_until_sec, OrdRange, PagingOptionsEnum, DEX_FEE_ADDR_RAW_PUBKEY}; +use common::{block_on, block_on_f01, wait_until_sec, OrdRange, PagingOptionsEnum, DEX_FEE_ADDR_RAW_PUBKEY}; use crypto::{privkey::key_pair_from_seed, Bip44Chain, HDPathToAccount, RpcDerivationPath, Secp256k1Secret}; #[cfg(not(target_arch = "wasm32"))] use db_common::sqlite::rusqlite::Connection; use futures::channel::mpsc::channel; -use futures::future::join_all; -use futures::TryFutureExt; +use futures::future::{join_all, Either, FutureExt, TryFutureExt}; use keys::prefixes::*; use mm2_core::mm_ctx::MmCtxBuilder; use mm2_number::bigdecimal::{BigDecimal, Signed}; @@ -438,18 +437,16 @@ fn test_wait_for_payment_spend_timeout_native() { let wait_until = now_sec() - 1; let from_block = 1000; - assert!(coin - .wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { - tx_bytes: &transaction, - secret_hash: &[], - wait_until, - from_block, - swap_contract_address: &None, - check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, - watcher_reward: false - }) - .wait() - .is_err()); + assert!(block_on_f01(coin.wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { + tx_bytes: &transaction, + secret_hash: &[], + wait_until, + from_block, + swap_contract_address: &None, + check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, + watcher_reward: false + })) + .is_err()); assert!(unsafe { OUTPUT_SPEND_CALLED }); } @@ -486,18 +483,16 @@ fn test_wait_for_payment_spend_timeout_electrum() { let wait_until = now_sec() - 1; let from_block = 1000; - assert!(coin - .wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { - tx_bytes: &transaction, - secret_hash: &[], - wait_until, - from_block, - swap_contract_address: &None, - check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, - watcher_reward: false - }) - .wait() - .is_err()); + assert!(block_on_f01(coin.wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { + tx_bytes: &transaction, + secret_hash: &[], + wait_until, + from_block, + swap_contract_address: &None, + check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, + watcher_reward: false + })) + .is_err()); assert!(unsafe { OUTPUT_SPEND_CALLED }); } @@ -575,19 +570,22 @@ fn test_search_for_swap_tx_spend_electrum_was_refunded() { #[cfg(not(target_arch = "wasm32"))] fn test_withdraw_impl_set_fixed_fee() { UtxoStandardCoin::get_unspent_ordered_list.mock_safe(|coin, _| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 1000000000, - height: Default::default(), - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1000000000, + height: Default::default(), + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); let client = NativeClient(Arc::new(NativeClientImpl::default())); @@ -613,7 +611,7 @@ fn test_withdraw_impl_set_fixed_fee() { } .into(), ); - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); assert_eq!(expected, tx_details.fee_details); } @@ -621,19 +619,22 @@ fn test_withdraw_impl_set_fixed_fee() { #[cfg(not(target_arch = "wasm32"))] fn test_withdraw_impl_sat_per_kb_fee() { UtxoStandardCoin::get_unspent_ordered_list.mock_safe(|coin, _| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 1000000000, - height: Default::default(), - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1000000000, + height: Default::default(), + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); let client = NativeClient(Arc::new(NativeClientImpl::default())); @@ -662,7 +663,7 @@ fn test_withdraw_impl_sat_per_kb_fee() { } .into(), ); - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); assert_eq!(expected, tx_details.fee_details); } @@ -670,19 +671,22 @@ fn test_withdraw_impl_sat_per_kb_fee() { #[cfg(not(target_arch = "wasm32"))] fn test_withdraw_impl_sat_per_kb_fee_amount_equal_to_max() { UtxoStandardCoin::get_unspent_ordered_list.mock_safe(|coin, _| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 1000000000, - height: Default::default(), - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1000000000, + height: Default::default(), + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); let client = NativeClient(Arc::new(NativeClientImpl::default())); @@ -701,7 +705,7 @@ fn test_withdraw_impl_sat_per_kb_fee_amount_equal_to_max() { memo: None, ibc_source_channel: None, }; - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); // The resulting transaction size might be 210 or 211 bytes depending on signature size // MM2 always expects the worst case during fee calculation // 0.1 * 211 / 1000 = 0.0211 @@ -721,19 +725,22 @@ fn test_withdraw_impl_sat_per_kb_fee_amount_equal_to_max() { #[cfg(not(target_arch = "wasm32"))] fn test_withdraw_impl_sat_per_kb_fee_amount_equal_to_max_dust_included_to_fee() { UtxoStandardCoin::get_unspent_ordered_list.mock_safe(|coin, _| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 1000000000, - height: Default::default(), - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1000000000, + height: Default::default(), + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); let client = NativeClient(Arc::new(NativeClientImpl::default())); @@ -752,7 +759,7 @@ fn test_withdraw_impl_sat_per_kb_fee_amount_equal_to_max_dust_included_to_fee() memo: None, ibc_source_channel: None, }; - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); // The resulting transaction size might be 210 or 211 bytes depending on signature size // MM2 always expects the worst case during fee calculation // 0.1 * 211 / 1000 = 0.0211 @@ -772,19 +779,22 @@ fn test_withdraw_impl_sat_per_kb_fee_amount_equal_to_max_dust_included_to_fee() #[cfg(not(target_arch = "wasm32"))] fn test_withdraw_impl_sat_per_kb_fee_amount_over_max() { UtxoStandardCoin::get_unspent_ordered_list.mock_safe(|coin, _| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 1000000000, - height: Default::default(), - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1000000000, + height: Default::default(), + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); let client = NativeClient(Arc::new(NativeClientImpl::default())); @@ -803,26 +813,29 @@ fn test_withdraw_impl_sat_per_kb_fee_amount_over_max() { memo: None, ibc_source_channel: None, }; - coin.withdraw(withdraw_req).wait().unwrap_err(); + block_on_f01(coin.withdraw(withdraw_req)).unwrap_err(); } #[test] #[cfg(not(target_arch = "wasm32"))] fn test_withdraw_impl_sat_per_kb_fee_max() { UtxoStandardCoin::get_unspent_ordered_list.mock_safe(|coin, _| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 1000000000, - height: Default::default(), - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1000000000, + height: Default::default(), + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); let client = NativeClient(Arc::new(NativeClientImpl::default())); @@ -851,7 +864,7 @@ fn test_withdraw_impl_sat_per_kb_fee_max() { } .into(), ); - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); assert_eq!(expected, tx_details.fee_details); } @@ -866,20 +879,23 @@ fn test_withdraw_kmd_rewards_impl( let verbose: RpcTransaction = json::from_str(verbose_serialized).unwrap(); let unspent_height = verbose.height; UtxoStandardCoin::get_unspent_ordered_list.mock_safe(move |coin: &UtxoStandardCoin, _| { - let tx: UtxoTx = tx_hex.into(); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: tx.hash(), - index: 0, - }, - value: tx.outputs[0].value, - height: unspent_height, - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let tx: UtxoTx = tx_hex.into(); + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: tx.hash(), + index: 0, + }, + value: tx.outputs[0].value, + height: unspent_height, + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); UtxoStandardCoin::get_current_mtp .mock_safe(move |_fields| MockResult::Return(Box::pin(futures::future::ok(current_mtp)))); @@ -909,7 +925,7 @@ fn test_withdraw_kmd_rewards_impl( coin: Some("KMD".into()), amount: "0.00001".parse().unwrap(), }); - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); assert_eq!(tx_details.fee_details, Some(expected_fee)); let expected_rewards = expected_rewards.map(|amount| KmdRewardsDetails { @@ -958,20 +974,23 @@ fn test_withdraw_rick_rewards_none() { const TX_HEX: &str = "0400008085202f8901df8119c507aa61d32332cd246dbfeb3818a4f96e76492454c1fbba5aa097977e000000004847304402205a7e229ea6929c97fd6dde254c19e4eb890a90353249721701ae7a1c477d99c402206a8b7c5bf42b5095585731d6b4c589ce557f63c20aed69ff242eca22ecfcdc7a01feffffff02d04d1bffbc050000232102afdbba3e3c90db5f0f4064118f79cf308f926c68afd64ea7afc930975663e4c4ac402dd913000000001976a9143e17014eca06281ee600adffa34b4afb0922a22288ac2bdab86035a00e000000000000000000000000"; UtxoStandardCoin::get_unspent_ordered_list.mock_safe(move |coin, _| { - let tx: UtxoTx = TX_HEX.into(); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: tx.hash(), - index: 0, - }, - value: tx.outputs[0].value, - height: Some(1431628), - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let tx: UtxoTx = TX_HEX.into(); + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: tx.hash(), + index: 0, + }, + value: tx.outputs[0].value, + height: Some(1431628), + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); let withdraw_req = WithdrawRequest { @@ -988,7 +1007,7 @@ fn test_withdraw_rick_rewards_none() { coin: Some(TEST_COIN_NAME.into()), amount: "0.00001".parse().unwrap(), }); - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); assert_eq!(tx_details.fee_details, Some(expected_fee)); assert_eq!(tx_details.kmd_rewards, None); } @@ -1066,7 +1085,7 @@ fn test_electrum_rpc_client_error() { let client = electrum_client_for_test(&["electrum1.cipig.net:10060"]); let empty_hash = H256Json::default(); - let err = client.get_verbose_transaction(&empty_hash).wait().unwrap_err(); + let err = block_on_f01(client.get_verbose_transaction(&empty_hash)).unwrap_err(); // use the static string instead because the actual error message cannot be obtain // by serde_json serialization @@ -1305,10 +1324,7 @@ fn test_get_median_time_past_from_electrum_kmd() { "electrum3.cipig.net:10001", ]); - let mtp = client - .get_median_time_past(1773390, KMD_MTP_BLOCK_COUNT, CoinVariant::Standard) - .wait() - .unwrap(); + let mtp = block_on_f01(client.get_median_time_past(1773390, KMD_MTP_BLOCK_COUNT, CoinVariant::Standard)).unwrap(); // the MTP is block time of 1773385 in this case assert_eq!(1583159915, mtp); } @@ -1321,10 +1337,7 @@ fn test_get_median_time_past_from_electrum_btc() { "electrum3.cipig.net:10000", ]); - let mtp = client - .get_median_time_past(632858, KMD_MTP_BLOCK_COUNT, CoinVariant::Standard) - .wait() - .unwrap(); + let mtp = block_on_f01(client.get_median_time_past(632858, KMD_MTP_BLOCK_COUNT, CoinVariant::Standard)).unwrap(); assert_eq!(1591173041, mtp); } @@ -1348,10 +1361,7 @@ fn test_get_median_time_past_from_native_has_median_in_get_block() { ) }); - let mtp = client - .get_median_time_past(632858, KMD_MTP_BLOCK_COUNT, CoinVariant::Standard) - .wait() - .unwrap(); + let mtp = block_on_f01(client.get_median_time_past(632858, KMD_MTP_BLOCK_COUNT, CoinVariant::Standard)).unwrap(); assert_eq!(1591173041, mtp); } @@ -1394,10 +1404,7 @@ fn test_get_median_time_past_from_native_does_not_have_median_in_get_block() { MockResult::Return(Box::new(futures01::future::ok(block))) }); - let mtp = client - .get_median_time_past(632858, KMD_MTP_BLOCK_COUNT, CoinVariant::Standard) - .wait() - .unwrap(); + let mtp = block_on_f01(client.get_median_time_past(632858, KMD_MTP_BLOCK_COUNT, CoinVariant::Standard)).unwrap(); assert_eq!(1591173041, mtp); } @@ -1597,13 +1604,11 @@ fn test_spam_rick() { fn test_one_unavailable_electrum_proto_version() { // check if the electrum-mona.bitbank.cc:50001 doesn't support the protocol version 1.4 let client = electrum_client_for_test(&["electrum-mona.bitbank.cc:50001"]); - let result = client - .server_version( - "electrum-mona.bitbank.cc:50001", - "AtomicDEX", - &OrdRange::new(1.4, 1.4).unwrap(), - ) - .wait(); + let result = block_on_f01(client.server_version( + "electrum-mona.bitbank.cc:50001", + "AtomicDEX", + &OrdRange::new(1.4, 1.4).unwrap(), + )); assert!(result .err() .unwrap() @@ -1628,7 +1633,7 @@ fn test_one_unavailable_electrum_proto_version() { block_on(async { Timer::sleep(0.5).await }); - assert!(coin.as_ref().rpc_client.get_block_count().wait().is_ok()); + assert!(block_on_f01(coin.as_ref().rpc_client.get_block_count()).is_ok()); } #[test] @@ -1685,7 +1690,7 @@ fn test_qtum_add_delegation() { address: address.to_string(), fee: Some(10), }; - let res = coin.add_delegation(request).wait().unwrap(); + let res = block_on_f01(coin.add_delegation(request)).unwrap(); // Eligible for delegation assert!(res.my_balance_change.is_negative()); assert_eq!(res.total_amount, res.spent_by_me); @@ -1695,7 +1700,7 @@ fn test_qtum_add_delegation() { address: "fake_address".to_string(), fee: Some(10), }; - let res = coin.add_delegation(request).wait(); + let res = block_on_f01(coin.add_delegation(request)); // Wrong address assert!(res.is_err()); } @@ -1728,7 +1733,7 @@ fn test_qtum_add_delegation_on_already_delegating() { address: address.to_string(), fee: Some(10), }; - let res = coin.add_delegation(request).wait(); + let res = block_on_f01(coin.add_delegation(request)); // Already Delegating assert!(res.is_err()); } @@ -1754,7 +1759,7 @@ fn test_qtum_get_delegation_infos() { keypair.private().secret, )) .unwrap(); - let staking_infos = coin.get_delegation_infos().wait().unwrap(); + let staking_infos = block_on_f01(coin.get_delegation_infos()).unwrap(); match staking_infos.staking_infos_details { StakingInfosDetails::Qtum(staking_details) => { assert!(staking_details.am_i_staking); @@ -1784,49 +1789,49 @@ fn test_qtum_remove_delegation() { keypair.private().secret, )) .unwrap(); - let res = coin.remove_delegation().wait(); + let res = block_on_f01(coin.remove_delegation()); assert!(res.is_ok()); } #[test] fn test_qtum_my_balance() { QtumCoin::get_mature_unspent_ordered_list.mock_safe(move |coin, _address| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - // spendable balance (66.0) - let mature = vec![ - UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + // spendable balance (66.0) + let mature = vec![ + UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 5000000000, + height: Default::default(), + script: Vec::new().into(), }, - value: 5000000000, - height: Default::default(), - script: Vec::new().into(), - }, - UnspentInfo { + UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1600000000, + height: Default::default(), + script: Vec::new().into(), + }, + ]; + // unspendable (2.0) + let immature = vec![UnspentInfo { outpoint: OutPoint { hash: 1.into(), index: 0, }, - value: 1600000000, + value: 200000000, height: Default::default(), script: Vec::new().into(), - }, - ]; - // unspendable (2.0) - let immature = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 200000000, - height: Default::default(), - script: Vec::new().into(), - }]; - MockResult::Return(Box::pin(futures::future::ok(( - MatureUnspentList { mature, immature }, - cache, - )))) + }]; + Ok((MatureUnspentList { mature, immature }, cache)) + }; + MockResult::Return(fut.boxed()) }); let conf = json!({"coin":"tQTUM","rpcport":13889,"pubtype":120,"p2shtype":110}); @@ -1845,7 +1850,7 @@ fn test_qtum_my_balance() { let params = UtxoActivationParams::from_legacy_req(&req).unwrap(); let coin = block_on(qtum_coin_with_priv_key(&ctx, "tQTUM", &conf, ¶ms, priv_key)).unwrap(); - let CoinBalance { spendable, unspendable } = coin.my_balance().wait().unwrap(); + let CoinBalance { spendable, unspendable } = block_on_f01(coin.my_balance()).unwrap(); let expected_spendable = BigDecimal::from(66); let expected_unspendable = BigDecimal::from(2); assert_eq!(spendable, expected_spendable); @@ -1881,7 +1886,7 @@ fn test_qtum_my_balance_with_check_utxo_maturity_false() { let params = UtxoActivationParams::from_legacy_req(&req).unwrap(); let coin = block_on(qtum_coin_with_priv_key(&ctx, "tQTUM", &conf, ¶ms, priv_key)).unwrap(); - let CoinBalance { spendable, unspendable } = coin.my_balance().wait().unwrap(); + let CoinBalance { spendable, unspendable } = block_on_f01(coin.my_balance()).unwrap(); let expected_spendable = BigDecimal::from(DISPLAY_BALANCE); let expected_unspendable = BigDecimal::from(0); assert_eq!(spendable, expected_spendable); @@ -1899,7 +1904,7 @@ fn test_get_mature_unspent_ordered_map_from_cache_impl( const TX_HASH: &str = "b43f9ed47f7b97d4766b6f1614136fa0c55b9a52c97342428333521fa13ad714"; let tx_hash: H256Json = hex::decode(TX_HASH).unwrap().as_slice().into(); let client = electrum_client_for_test(DOC_ELECTRUM_ADDRS); - let mut verbose = client.get_verbose_transaction(&tx_hash).wait().unwrap(); + let mut verbose = block_on_f01(client.get_verbose_transaction(&tx_hash)).unwrap(); verbose.confirmations = cached_confs; verbose.height = cached_height; @@ -2517,15 +2522,13 @@ fn test_find_output_spend_skips_conflicting_transactions() { let tx: UtxoTx = "0400008085202f89027f57730fcbbc2c72fb18bcc3766a713044831a117bb1cade3ed88644864f7333020000006a47304402206e3737b2fcf078b61b16fa67340cc3e79c5d5e2dc9ffda09608371552a3887450220460a332aa1b8ad8f2de92d319666f70751078b221199951f80265b4f7cef8543012102d8c948c6af848c588517288168faa397d6ba3ea924596d03d1d84f224b5123c2ffffffff42b916a80430b80a77e114445b08cf120735447a524de10742fac8f6a9d4170f000000006a473044022004aa053edafb9d161ea8146e0c21ed1593aa6b9404dd44294bcdf920a1695fd902202365eac15dbcc5e9f83e2eed56a8f2f0e5aded36206f9c3fabc668fd4665fa2d012102d8c948c6af848c588517288168faa397d6ba3ea924596d03d1d84f224b5123c2ffffffff03547b16000000000017a9143e8ad0e2bf573d32cb0b3d3a304d9ebcd0c2023b870000000000000000166a144e2b3c0323ab3c2dc6f86dc5ec0729f11e42f56103970400000000001976a91450f4f098306f988d8843004689fae28c83ef16e888ac89c5925f000000000000000000000000000000".into(); let vout = 0; let from_block = 0; - let actual = client - .find_output_spend( - tx.hash(), - &tx.outputs[vout].script_pubkey, - vout, - BlockHashOrHeight::Height(from_block), - TxHashAlgo::DSHA256, - ) - .wait(); + let actual = block_on_f01(client.find_output_spend( + tx.hash(), + &tx.outputs[vout].script_pubkey, + vout, + BlockHashOrHeight::Height(from_block), + TxHashAlgo::DSHA256, + )); assert_eq!(actual, Ok(None)); assert_eq!(unsafe { GET_RAW_TRANSACTION_BYTES_CALLED }, 1); } @@ -2609,7 +2612,7 @@ fn test_get_sender_trade_fee_dynamic_tx_fee() { ); coin_fields.tx_fee = TxFee::Dynamic(EstimateFeeMethod::Standard); let coin = utxo_coin_from_fields(coin_fields); - let my_balance = coin.my_spendable_balance().wait().expect("!my_balance"); + let my_balance = block_on_f01(coin.my_spendable_balance()).expect("!my_balance"); let expected_balance = BigDecimal::from_str("2.22222").expect("!BigDecimal::from_str"); assert_eq!(my_balance, expected_balance); @@ -2657,7 +2660,9 @@ fn test_validate_fee_wrong_sender() { min_block_number: 0, uuid: &[], }; - let error = coin.validate_fee(validate_fee_args).wait().unwrap_err().into_inner(); + let error = block_on_f01(coin.validate_fee(validate_fee_args)) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains(INVALID_SENDER_ERR_LOG)), @@ -2682,7 +2687,9 @@ fn test_validate_fee_min_block() { min_block_number: 278455, uuid: &[], }; - let error = coin.validate_fee(validate_fee_args).wait().unwrap_err().into_inner(); + let error = block_on_f01(coin.validate_fee(validate_fee_args)) + .unwrap_err() + .into_inner(); match error { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("confirmed before min_block")), _ => panic!("Expected `WrongPaymentTx` early confirmation, found {:?}", error), @@ -2711,7 +2718,7 @@ fn test_validate_fee_bch_70_bytes_signature() { min_block_number: 0, uuid: &[], }; - coin.validate_fee(validate_fee_args).wait().unwrap(); + block_on_f01(coin.validate_fee(validate_fee_args)).unwrap(); } #[test] @@ -2766,7 +2773,7 @@ fn firo_lelantus_tx() { "electrumx02.firo.org:50001", "electrumx03.firo.org:50001", ]); - let _tx = electrum.get_verbose_transaction(&tx_hash).wait().unwrap(); + let _tx = block_on_f01(electrum.get_verbose_transaction(&tx_hash)).unwrap(); } #[test] @@ -2903,9 +2910,7 @@ fn doge_mtp() { "electrum2.cipig.net:10060", "electrum3.cipig.net:10060", ]); - let mtp = electrum - .get_median_time_past(3631820, NonZeroU64::new(11).unwrap(), CoinVariant::Standard) - .wait() + let mtp = block_on_f01(electrum.get_median_time_past(3631820, NonZeroU64::new(11).unwrap(), CoinVariant::Standard)) .unwrap(); assert_eq!(mtp, 1614849084); } @@ -2917,9 +2922,7 @@ fn firo_mtp() { "electrumx02.firo.org:50001", "electrumx03.firo.org:50001", ]); - let mtp = electrum - .get_median_time_past(356730, NonZeroU64::new(11).unwrap(), CoinVariant::Standard) - .wait() + let mtp = block_on_f01(electrum.get_median_time_past(356730, NonZeroU64::new(11).unwrap(), CoinVariant::Standard)) .unwrap(); assert_eq!(mtp, 1616492629); } @@ -2927,9 +2930,7 @@ fn firo_mtp() { #[test] fn verus_mtp() { let electrum = electrum_client_for_test(&["el0.verus.io:17485", "el1.verus.io:17485", "el2.verus.io:17485"]); - let mtp = electrum - .get_median_time_past(1480113, NonZeroU64::new(11).unwrap(), CoinVariant::Standard) - .wait() + let mtp = block_on_f01(electrum.get_median_time_past(1480113, NonZeroU64::new(11).unwrap(), CoinVariant::Standard)) .unwrap(); assert_eq!(mtp, 1618579909); } @@ -2941,9 +2942,7 @@ fn sys_mtp() { "electrum2.cipig.net:10064", "electrum3.cipig.net:10064", ]); - let mtp = electrum - .get_median_time_past(1006678, NonZeroU64::new(11).unwrap(), CoinVariant::Standard) - .wait() + let mtp = block_on_f01(electrum.get_median_time_past(1006678, NonZeroU64::new(11).unwrap(), CoinVariant::Standard)) .unwrap(); assert_eq!(mtp, 1620019628); } @@ -2955,9 +2954,7 @@ fn btc_mtp() { "electrum2.cipig.net:10000", "electrum3.cipig.net:10000", ]); - let mtp = electrum - .get_median_time_past(681659, NonZeroU64::new(11).unwrap(), CoinVariant::Standard) - .wait() + let mtp = block_on_f01(electrum.get_median_time_past(681659, NonZeroU64::new(11).unwrap(), CoinVariant::Standard)) .unwrap(); assert_eq!(mtp, 1620019527); } @@ -2969,9 +2966,7 @@ fn rvn_mtp() { "electrum2.cipig.net:10051", "electrum3.cipig.net:10051", ]); - let mtp = electrum - .get_median_time_past(1968120, NonZeroU64::new(11).unwrap(), CoinVariant::Standard) - .wait() + let mtp = block_on_f01(electrum.get_median_time_past(1968120, NonZeroU64::new(11).unwrap(), CoinVariant::Standard)) .unwrap(); assert_eq!(mtp, 1633946264); } @@ -2983,10 +2978,8 @@ fn qtum_mtp() { "electrum2.cipig.net:10050", "electrum3.cipig.net:10050", ]); - let mtp = electrum - .get_median_time_past(681659, NonZeroU64::new(11).unwrap(), CoinVariant::Qtum) - .wait() - .unwrap(); + let mtp = + block_on_f01(electrum.get_median_time_past(681659, NonZeroU64::new(11).unwrap(), CoinVariant::Qtum)).unwrap(); assert_eq!(mtp, 1598854128); } @@ -2997,9 +2990,7 @@ fn zer_mtp() { "electrum2.cipig.net:10065", "electrum3.cipig.net:10065", ]); - let mtp = electrum - .get_median_time_past(1130915, NonZeroU64::new(11).unwrap(), CoinVariant::Standard) - .wait() + let mtp = block_on_f01(electrum.get_median_time_past(1130915, NonZeroU64::new(11).unwrap(), CoinVariant::Standard)) .unwrap(); assert_eq!(mtp, 1623240214); } @@ -3196,7 +3187,7 @@ fn test_withdraw_to_p2pk_fails() { }; assert!(matches!( - coin.withdraw(withdraw_req).wait().unwrap_err().into_inner(), + block_on_f01(coin.withdraw(withdraw_req)).unwrap_err().into_inner(), WithdrawError::InvalidAddress(..) )) } @@ -3209,19 +3200,22 @@ fn test_withdraw_to_p2pkh() { let coin = utxo_coin_for_test(UtxoRpcClientEnum::Native(client), None, false); UtxoStandardCoin::get_unspent_ordered_list.mock_safe(|coin, _| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 1000000000, - height: Default::default(), - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1000000000, + height: Default::default(), + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); // Create a p2pkh address for the test coin @@ -3249,7 +3243,7 @@ fn test_withdraw_to_p2pkh() { memo: None, ibc_source_channel: None, }; - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); let transaction: UtxoTx = deserialize(tx_details.tx.tx_hex().unwrap().as_slice()).unwrap(); let output_script: Script = transaction.outputs[0].script_pubkey.clone().into(); @@ -3266,19 +3260,22 @@ fn test_withdraw_to_p2sh() { let coin = utxo_coin_for_test(UtxoRpcClientEnum::Native(client), None, false); UtxoStandardCoin::get_unspent_ordered_list.mock_safe(|coin, _| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 1000000000, - height: Default::default(), - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1000000000, + height: Default::default(), + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); // Create a p2sh address for the test coin @@ -3306,7 +3303,7 @@ fn test_withdraw_to_p2sh() { memo: None, ibc_source_channel: None, }; - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); let transaction: UtxoTx = deserialize(tx_details.tx.tx_hex().unwrap().as_slice()).unwrap(); let output_script: Script = transaction.outputs[0].script_pubkey.clone().into(); @@ -3323,19 +3320,22 @@ fn test_withdraw_to_p2wpkh() { let coin = utxo_coin_for_test(UtxoRpcClientEnum::Native(client), None, true); UtxoStandardCoin::get_unspent_ordered_list.mock_safe(|coin, _| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 1000000000, - height: Default::default(), - script: coin - .script_for_address(&block_on(coin.as_ref().derivation_method.unwrap_single_addr())) - .unwrap(), - }]; - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1000000000, + height: Default::default(), + script: coin + .script_for_address(&coin.as_ref().derivation_method.unwrap_single_addr().await) + .unwrap(), + }]; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); // Create a p2wpkh address for the test coin @@ -3363,7 +3363,7 @@ fn test_withdraw_to_p2wpkh() { memo: None, ibc_source_channel: None, }; - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); let transaction: UtxoTx = deserialize(tx_details.tx.tx_hex().unwrap().as_slice()).unwrap(); let output_script: Script = transaction.outputs[0].script_pubkey.clone().into(); @@ -3380,22 +3380,29 @@ fn test_withdraw_p2pk_balance() { let coin = utxo_coin_for_test(UtxoRpcClientEnum::Native(client), None, false); UtxoStandardCoin::get_unspent_ordered_list.mock_safe(|coin, _| { - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = vec![UnspentInfo { - outpoint: OutPoint { - hash: 1.into(), - index: 0, - }, - value: 1000000000, - height: Default::default(), - // Use a p2pk output script for this UTXO - script: output_script_p2pk( - &block_on(coin.as_ref().derivation_method.unwrap_single_addr()) - .pubkey() - .unwrap(), - ), - }]; - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = vec![UnspentInfo { + outpoint: OutPoint { + hash: 1.into(), + index: 0, + }, + value: 1000000000, + height: Default::default(), + // Use a p2pk output script for this UTXO + script: output_script_p2pk( + &coin + .as_ref() + .derivation_method + .unwrap_single_addr() + .await + .pubkey() + .unwrap(), + ), + }]; + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); // Create a dummy p2pkh address to withdraw the coins to. @@ -3411,7 +3418,7 @@ fn test_withdraw_p2pk_balance() { memo: None, ibc_source_channel: None, }; - let tx_details = coin.withdraw(withdraw_req).wait().unwrap(); + let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); let transaction: UtxoTx = deserialize(tx_details.tx.tx_hex().unwrap().as_slice()).unwrap(); // The change should be in a p2pkh script. @@ -3432,8 +3439,11 @@ fn test_utxo_standard_with_check_utxo_maturity_true() { UtxoStandardCoin::get_mature_unspent_ordered_list.mock_safe(|coin, _| { unsafe { GET_MATURE_UNSPENT_ORDERED_LIST_CALLED = true }; - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - MockResult::Return(Box::pin(futures::future::ok((MatureUnspentList::default(), cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + Ok((MatureUnspentList::default(), cache)) + }; + MockResult::Return(fut.boxed()) }); let conf = json!({"coin":"RICK","asset":"RICK","rpcport":25435,"txversion":4,"overwintered":1,"mm2":1,"protocol":{"type":"UTXO"}}); @@ -3451,7 +3461,7 @@ fn test_utxo_standard_with_check_utxo_maturity_true() { let address = Address::from_legacyaddress("R9o9xTocqr6CeEDGDH6mEYpwLoMz6jNjMW", &KMD_PREFIXES).unwrap(); // Don't use `block_on` here because it's used within a mock of [`GetUtxoListOps::get_mature_unspent_ordered_list`]. - coin.get_unspent_ordered_list(&address).compat().wait().unwrap(); + block_on_f01(coin.get_unspent_ordered_list(&address).compat()).unwrap(); assert!(unsafe { GET_MATURE_UNSPENT_ORDERED_LIST_CALLED }); } @@ -3464,9 +3474,11 @@ fn test_utxo_standard_without_check_utxo_maturity() { UtxoStandardCoin::get_all_unspent_ordered_list.mock_safe(|coin, _| { unsafe { GET_ALL_UNSPENT_ORDERED_LIST_CALLED = true }; - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = Vec::new(); - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + Ok((Vec::new(), cache)) + }; + MockResult::Return(fut.boxed()) }); UtxoStandardCoin::get_mature_unspent_ordered_list.mock_safe(|_, _| { @@ -3487,7 +3499,7 @@ fn test_utxo_standard_without_check_utxo_maturity() { let address = Address::from_legacyaddress("R9o9xTocqr6CeEDGDH6mEYpwLoMz6jNjMW", &KMD_PREFIXES).unwrap(); // Don't use `block_on` here because it's used within a mock of [`UtxoStandardCoin::get_all_unspent_ordered_list`]. - coin.get_unspent_ordered_list(&address).compat().wait().unwrap(); + block_on_f01(coin.get_unspent_ordered_list(&address).compat()).unwrap(); assert!(unsafe { GET_ALL_UNSPENT_ORDERED_LIST_CALLED }); } @@ -3500,8 +3512,11 @@ fn test_qtum_without_check_utxo_maturity() { QtumCoin::get_mature_unspent_ordered_list.mock_safe(|coin, _| { unsafe { GET_MATURE_UNSPENT_ORDERED_LIST_CALLED = true }; - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - MockResult::Return(Box::pin(futures::future::ok((MatureUnspentList::default(), cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + Ok((MatureUnspentList::default(), cache)) + }; + MockResult::Return(fut.boxed()) }); let conf = json!({"coin":"tQTUM","rpcport":13889,"pubtype":120,"p2shtype":110}); @@ -3526,7 +3541,7 @@ fn test_qtum_without_check_utxo_maturity() { ) .unwrap(); // Don't use `block_on` here because it's used within a mock of [`QtumCoin::get_mature_unspent_ordered_list`]. - coin.get_unspent_ordered_list(&address).compat().wait().unwrap(); + block_on_f01(coin.get_unspent_ordered_list(&address).compat()).unwrap(); assert!(unsafe { GET_MATURE_UNSPENT_ORDERED_LIST_CALLED }); } @@ -3604,9 +3619,12 @@ fn test_qtum_with_check_utxo_maturity_false() { QtumCoin::get_all_unspent_ordered_list.mock_safe(|coin, _address| { unsafe { GET_ALL_UNSPENT_ORDERED_LIST_CALLED = true }; - let cache = block_on(coin.as_ref().recently_spent_outpoints.lock()); - let unspents = Vec::new(); - MockResult::Return(Box::pin(futures::future::ok((unspents, cache)))) + let fut = async move { + let cache = coin.as_ref().recently_spent_outpoints.lock().await; + let unspents = Vec::new(); + Ok((unspents, cache)) + }; + MockResult::Return(fut.boxed()) }); QtumCoin::get_mature_unspent_ordered_list.mock_safe(|_, _| { panic!( @@ -3637,7 +3655,7 @@ fn test_qtum_with_check_utxo_maturity_false() { ) .unwrap(); // Don't use `block_on` here because it's used within a mock of [`QtumCoin::get_all_unspent_ordered_list`]. - coin.get_unspent_ordered_list(&address).compat().wait().unwrap(); + block_on_f01(coin.get_unspent_ordered_list(&address).compat()).unwrap(); assert!(unsafe { GET_ALL_UNSPENT_ORDERED_LIST_CALLED }); } @@ -4365,7 +4383,9 @@ fn test_for_non_existent_tx_hex_utxo_electrum() { wait_until: timeout, check_every: 1, }; - let actual = coin.wait_for_confirmations(confirm_payment_input).wait().err().unwrap(); + let actual = block_on_f01(coin.wait_for_confirmations(confirm_payment_input)) + .err() + .unwrap(); assert!(actual.contains( "Tx d342ff9da528a2e262bddf2b6f9a27d1beb7aeb03f0fc8d9eac2987266447e44 was not found on chain after 10 tries" )); @@ -4408,10 +4428,7 @@ fn test_native_display_balances() { Address::from_legacyaddress("RJeDDtDRtKUoL8BCKdH7TNCHqUKr7kQRsi", &KMD_PREFIXES).unwrap(), Address::from_legacyaddress("RQHn9VPHBqNjYwyKfJbZCiaxVrWPKGQjeF", &KMD_PREFIXES).unwrap(), ]; - let actual = rpc_client - .display_balances(addresses, TEST_COIN_DECIMALS) - .wait() - .unwrap(); + let actual = block_on_f01(rpc_client.display_balances(addresses, TEST_COIN_DECIMALS)).unwrap(); let expected: Vec<(Address, BigDecimal)> = vec![ ( @@ -4549,7 +4566,6 @@ fn test_utxo_validate_valid_and_invalid_pubkey() { #[test] fn test_block_header_utxo_loop() { use crate::utxo::utxo_builder::{block_header_utxo_loop, BlockHeaderUtxoLoopExtraArgs}; - use futures::future::{Either, FutureExt}; use keys::hash::H256 as H256Json; static mut CURRENT_BLOCK_COUNT: u64 = 13; diff --git a/mm2src/coins/z_coin/z_coin_native_tests.rs b/mm2src/coins/z_coin/z_coin_native_tests.rs index 9e4358727d..790f42818f 100644 --- a/mm2src/coins/z_coin/z_coin_native_tests.rs +++ b/mm2src/coins/z_coin/z_coin_native_tests.rs @@ -1,14 +1,13 @@ use bitcrypto::dhash160; -use common::{block_on, now_sec}; +use common::{block_on, block_on_f01, now_sec}; use mm2_core::mm_ctx::MmCtxBuilder; use mm2_test_helpers::for_tests::zombie_conf; use std::path::PathBuf; use std::time::Duration; use zcash_client_backend::encoding::decode_extended_spending_key; -use super::{z_coin_from_conf_and_params_with_z_key, z_mainnet_constants, Future, PrivKeyBuildPolicy, - RefundPaymentArgs, SendPaymentArgs, SpendPaymentArgs, SwapOps, ValidateFeeArgs, ValidatePaymentError, - ZTransaction}; +use super::{z_coin_from_conf_and_params_with_z_key, z_mainnet_constants, PrivKeyBuildPolicy, RefundPaymentArgs, + SendPaymentArgs, SpendPaymentArgs, SwapOps, ValidateFeeArgs, ValidatePaymentError, ZTransaction}; use crate::z_coin::{z_htlc::z_send_dex_fee, ZcoinActivationParams, ZcoinRpcMode}; use crate::DexFee; use crate::{CoinProtocol, SwapTxTypeWithSecretHash}; @@ -55,7 +54,7 @@ fn zombie_coin_send_and_refund_maker_payment() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let tx = coin.send_maker_payment(args).wait().unwrap(); + let tx = block_on_f01(coin.send_maker_payment(args)).unwrap(); log!("swap tx {}", hex::encode(tx.tx_hash_as_bytes().0)); let refund_args = RefundPaymentArgs { @@ -116,7 +115,7 @@ fn zombie_coin_send_and_spend_maker_payment() { wait_for_confirmation_until: 0, }; - let tx = coin.send_maker_payment(maker_payment_args).wait().unwrap(); + let tx = block_on_f01(coin.send_maker_payment(maker_payment_args)).unwrap(); log!("swap tx {}", hex::encode(tx.tx_hash_as_bytes().0)); let maker_pub = taker_pub; @@ -234,7 +233,9 @@ fn zombie_coin_validate_dex_fee() { uuid: &[1; 16], }; // Invalid amount should return an error - let err = coin.validate_fee(validate_fee_args).wait().unwrap_err().into_inner(); + let err = block_on_f01(coin.validate_fee(validate_fee_args)) + .unwrap_err() + .into_inner(); match err { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("Dex fee has invalid amount")), _ => panic!("Expected `WrongPaymentTx`: {:?}", err), @@ -249,7 +250,9 @@ fn zombie_coin_validate_dex_fee() { min_block_number: 12000, uuid: &[2; 16], }; - let err = coin.validate_fee(validate_fee_args).wait().unwrap_err().into_inner(); + let err = block_on_f01(coin.validate_fee(validate_fee_args)) + .unwrap_err() + .into_inner(); match err { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("Dex fee has invalid memo")), _ => panic!("Expected `WrongPaymentTx`: {:?}", err), @@ -264,7 +267,9 @@ fn zombie_coin_validate_dex_fee() { min_block_number: 14000, uuid: &[1; 16], }; - let err = coin.validate_fee(validate_fee_args).wait().unwrap_err().into_inner(); + let err = block_on_f01(coin.validate_fee(validate_fee_args)) + .unwrap_err() + .into_inner(); match err { ValidatePaymentError::WrongPaymentTx(err) => assert!(err.contains("confirmed before min block")), _ => panic!("Expected `WrongPaymentTx`: {:?}", err), @@ -279,7 +284,7 @@ fn zombie_coin_validate_dex_fee() { min_block_number: 12000, uuid: &[1; 16], }; - coin.validate_fee(validate_fee_args).wait().unwrap(); + block_on_f01(coin.validate_fee(validate_fee_args)).unwrap(); } fn default_zcoin_activation_params() -> ZcoinActivationParams { diff --git a/mm2src/common/common.rs b/mm2src/common/common.rs index 0eeb726ad4..f665a9a8e0 100644 --- a/mm2src/common/common.rs +++ b/mm2src/common/common.rs @@ -621,7 +621,20 @@ pub fn var(name: &str) -> Result { #[cfg(target_arch = "wasm32")] pub fn var(_name: &str) -> Result { ERR!("Environment variable not supported in WASM") } +/// Runs the given future on MM2's executor and waits for the result. +/// +/// This is compatible with futures 0.1. +pub fn block_on_f01(f: F) -> Result +where + F: Future, +{ + block_on(f.compat()) +} + #[cfg(not(target_arch = "wasm32"))] +/// Runs the given future on MM2's executor and waits for the result. +/// +/// This is compatible with futures 0.3. pub fn block_on(f: F) -> F::Output where F: Future03, diff --git a/mm2src/mm2_err_handle/src/map_to_mm_fut.rs b/mm2src/mm2_err_handle/src/map_to_mm_fut.rs index 01de1b7d7e..0cfeb9cf13 100644 --- a/mm2src/mm2_err_handle/src/map_to_mm_fut.rs +++ b/mm2src/mm2_err_handle/src/map_to_mm_fut.rs @@ -21,7 +21,7 @@ where /// /// ```rust /// let fut = futures01::future::err("An error".to_owned()); - /// let mapped_res: Result<(), MmError> = fut.map_to_mm_fut(|e| e.len()).wait(); + /// let mapped_res: Result<(), MmError> = block_on_f01(fut.map_to_mm_fut(|e| e.len())); /// ``` #[track_caller] fn map_to_mm_fut(self, f: F) -> MapToMmFuture<'a, T, E1, E2> diff --git a/mm2src/mm2_err_handle/src/mm_error.rs b/mm2src/mm2_err_handle/src/mm_error.rs index 56e7f29f50..1ea7bf1555 100644 --- a/mm2src/mm2_err_handle/src/mm_error.rs +++ b/mm2src/mm2_err_handle/src/mm_error.rs @@ -332,6 +332,7 @@ impl FormattedTrace for Vec { mod tests { use super::*; use crate::prelude::*; + use common::block_on_f01; use futures01::Future; use ser_error_derive::SerializeErrorType; use serde_json::{self as json, json}; @@ -425,10 +426,8 @@ mod tests { } let into_mm_line = line!() + 2; - let mm_err = generate_error("An error") - .map_to_mm_fut(|error| error.len()) - .wait() - .expect_err("Expected an error"); + let mm_err = + block_on_f01(generate_error("An error").map_to_mm_fut(|error| error.len())).expect_err("Expected an error"); assert_eq!(mm_err.etype, 8); assert_eq!(mm_err.trace, vec![TraceLocation::new("mm_error", into_mm_line)]); } diff --git a/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs b/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs index fe5d7f96d4..49150fab55 100644 --- a/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs +++ b/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs @@ -1,4 +1,4 @@ -pub use common::{block_on, now_ms, now_sec, wait_until_ms, wait_until_sec, DEX_FEE_ADDR_RAW_PUBKEY}; +pub use common::{block_on, block_on_f01, now_ms, now_sec, wait_until_ms, wait_until_sec, DEX_FEE_ADDR_RAW_PUBKEY}; pub use mm2_number::MmNumber; use mm2_rpc::data::legacy::BalanceResponse; pub use mm2_test_helpers::for_tests::{check_my_swap_status, check_recent_swaps, enable_eth_coin, enable_native, @@ -27,7 +27,6 @@ use crypto::Secp256k1Secret; use ethabi::Token; use ethereum_types::{H160 as H160Eth, U256}; use futures::TryFutureExt; -use futures01::Future; use http::StatusCode; use keys::{Address, AddressBuilder, AddressHashEnum, AddressPrefix, KeyPair, NetworkAddressPrefixes, NetworkPrefix as CashAddrPrefix}; @@ -155,13 +154,13 @@ pub trait CoinDockerOps { fn wait_ready(&self, expected_tx_version: i32) { let timeout = wait_until_ms(120000); loop { - match self.rpc_client().get_block_count().wait() { + match block_on_f01(self.rpc_client().get_block_count()) { Ok(n) => { if n > 1 { if let UtxoRpcClientEnum::Native(client) = self.rpc_client() { - let hash = client.get_block_hash(n).wait().unwrap(); - let block = client.get_block(hash).wait().unwrap(); - let coinbase = client.get_verbose_transaction(&block.tx[0]).wait().unwrap(); + let hash = block_on_f01(client.get_block_hash(n)).unwrap(); + let block = block_on_f01(client.get_block(hash)).unwrap(); + let coinbase = block_on_f01(client.get_verbose_transaction(&block.tx[0])).unwrap(); log!("Coinbase tx {:?} in block {}", coinbase, n); if coinbase.version == expected_tx_version { break; @@ -251,10 +250,11 @@ impl BchDockerOps { .build() .expect("valid address props"); - self.native_client() - .import_address(&address.to_string(), &address.to_string(), false) - .wait() - .unwrap(); + block_on_f01( + self.native_client() + .import_address(&address.to_string(), &address.to_string(), false), + ) + .unwrap(); let script_pubkey = Builder::build_p2pkh(&key_pair.public().address_hash().into()); @@ -270,9 +270,7 @@ impl BchDockerOps { slp_privkeys.push(*key_pair.private_ref()); } - let slp_genesis_tx = send_outputs_from_my_address(self.coin.clone(), bch_outputs) - .wait() - .unwrap(); + let slp_genesis_tx = block_on_f01(send_outputs_from_my_address(self.coin.clone(), bch_outputs)).unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: slp_genesis_tx.tx_hex(), confirmations: 1, @@ -280,7 +278,7 @@ impl BchDockerOps { wait_until: wait_until_sec(30), check_every: 1, }; - self.coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(self.coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let adex_slp = SlpToken::new( 8, @@ -299,7 +297,7 @@ impl BchDockerOps { wait_until: wait_until_sec(30), check_every: 1, }; - self.coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(self.coin.wait_for_confirmations(confirm_payment_input)).unwrap(); *SLP_TOKEN_OWNERS.lock().unwrap() = slp_privkeys; *SLP_TOKEN_ID.lock().unwrap() = slp_genesis_tx.tx_hash_as_bytes().as_slice().into(); } @@ -466,7 +464,7 @@ where match coin.as_ref().rpc_client { UtxoRpcClientEnum::Native(ref native) => { let my_address = coin.my_address().unwrap(); - native.import_address(&my_address, &my_address, false).wait().unwrap() + block_on_f01(native.import_address(&my_address, &my_address, false)).unwrap() }, UtxoRpcClientEnum::Electrum(_) => panic!("Expected NativeClient"), } @@ -586,9 +584,7 @@ where UtxoRpcClientEnum::Native(ref native) => native, UtxoRpcClientEnum::Electrum(_) => panic!("NativeClient expected"), }; - let mut addresses = native - .get_addresses_by_label(label) - .wait() + let mut addresses = block_on_f01(native.get_addresses_by_label(label)) .expect("!getaddressesbylabel") .into_iter(); match addresses.next() { @@ -610,22 +606,20 @@ pub fn fill_qrc20_address(coin: &Qrc20Coin, amount: BigDecimal, timeout: u64) { }; let from_addr = get_address_by_label(coin, QTUM_ADDRESS_LABEL); - let to_addr = coin.my_addr_as_contract_addr().compat().wait().unwrap(); + let to_addr = block_on_f01(coin.my_addr_as_contract_addr().compat()).unwrap(); let satoshis = sat_from_big_decimal(&amount, coin.as_ref().decimals).expect("!sat_from_big_decimal"); - let hash = client - .transfer_tokens( - &coin.contract_address, - &from_addr, - to_addr, - satoshis.into(), - coin.as_ref().decimals, - ) - .wait() - .expect("!transfer_tokens") - .txid; + let hash = block_on_f01(client.transfer_tokens( + &coin.contract_address, + &from_addr, + to_addr, + satoshis.into(), + coin.as_ref().decimals, + )) + .expect("!transfer_tokens") + .txid; - let tx_bytes = client.get_transaction_bytes(&hash).wait().unwrap(); + let tx_bytes = block_on_f01(client.get_transaction_bytes(&hash)).unwrap(); log!("{:02x}", tx_bytes); let confirm_payment_input = ConfirmPaymentInput { payment_tx: tx_bytes.0, @@ -634,7 +628,7 @@ pub fn fill_qrc20_address(coin: &Qrc20Coin, amount: BigDecimal, timeout: u64) { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); } /// Generate random privkey, create a QRC20 coin and fill it's address with the specified balance. @@ -742,9 +736,9 @@ where let timeout = wait_until_sec(timeout); if let UtxoRpcClientEnum::Native(client) = &coin.as_ref().rpc_client { - client.import_address(address, address, false).wait().unwrap(); - let hash = client.send_to_address(address, &amount).wait().unwrap(); - let tx_bytes = client.get_transaction_bytes(&hash).wait().unwrap(); + block_on_f01(client.import_address(address, address, false)).unwrap(); + let hash = block_on_f01(client.send_to_address(address, &amount)).unwrap(); + let tx_bytes = block_on_f01(client.get_transaction_bytes(&hash)).unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: tx_bytes.clone().0, confirmations: 1, @@ -752,13 +746,10 @@ where wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); log!("{:02x}", tx_bytes); loop { - let unspents = client - .list_unspent_impl(0, std::i32::MAX, vec![address.to_string()]) - .wait() - .unwrap(); + let unspents = block_on_f01(client.list_unspent_impl(0, std::i32::MAX, vec![address.to_string()])).unwrap(); if !unspents.is_empty() { break; } @@ -794,7 +785,7 @@ pub fn wait_for_estimate_smart_fee(timeout: u64) -> Result<(), String> { UtxoRpcClientEnum::Electrum(_) => panic!("Expected NativeClient"), }; while now_sec() < timeout { - if let Ok(res) = client.estimate_smart_fee(&None, 1).wait() { + if let Ok(res) = block_on_f01(client.estimate_smart_fee(&None, 1)) { if res.errors.is_empty() { *state = EstimateSmartFeeState::Ok; return Ok(()); diff --git a/mm2src/mm2_main/tests/docker_tests/docker_tests_inner.rs b/mm2src/mm2_main/tests/docker_tests/docker_tests_inner.rs index 330dec30de..fd9e4eefb6 100644 --- a/mm2src/mm2_main/tests/docker_tests/docker_tests_inner.rs +++ b/mm2src/mm2_main/tests/docker_tests/docker_tests_inner.rs @@ -12,10 +12,9 @@ use coins::TxFeeDetails; use coins::{ConfirmPaymentInput, FoundSwapTxSpend, MarketCoinOps, MmCoin, RefundPaymentArgs, SearchForSwapTxSpendInput, SendPaymentArgs, SpendPaymentArgs, SwapOps, SwapTxTypeWithSecretHash, TransactionEnum, WithdrawRequest}; -use common::{block_on, executor::Timer, get_utc_timestamp, now_sec, wait_until_sec}; +use common::{block_on, block_on_f01, executor::Timer, get_utc_timestamp, now_sec, wait_until_sec}; use crypto::privkey::key_pair_from_seed; use crypto::{CryptoCtx, DerivationPath, KeyPairPolicy}; -use futures01::Future; use http::StatusCode; use mm2_number::{BigDecimal, BigRational, MmNumber}; use mm2_test_helpers::for_tests::{check_my_swap_status_amounts, disable_coin, disable_coin_err, enable_eth_coin, @@ -51,7 +50,7 @@ fn test_search_for_swap_tx_spend_native_was_refunded_taker() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let tx = coin.send_taker_payment(taker_payment_args).wait().unwrap(); + let tx = block_on_f01(coin.send_taker_payment(taker_payment_args)).unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: tx.tx_hex(), @@ -60,7 +59,7 @@ fn test_search_for_swap_tx_spend_native_was_refunded_taker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let maker_refunds_payment_args = RefundPaymentArgs { payment_tx: &tx.tx_hex(), time_lock, @@ -81,7 +80,7 @@ fn test_search_for_swap_tx_spend_native_was_refunded_taker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock, @@ -113,7 +112,9 @@ fn test_for_non_existent_tx_hex_utxo() { wait_until: timeout, check_every: 1, }; - let actual = coin.wait_for_confirmations(confirm_payment_input).wait().err().unwrap(); + let actual = block_on_f01(coin.wait_for_confirmations(confirm_payment_input)) + .err() + .unwrap(); assert!(actual.contains( "Tx d342ff9da528a2e262bddf2b6f9a27d1beb7aeb03f0fc8d9eac2987266447e44 was not found on chain after 10 tries" )); @@ -138,7 +139,7 @@ fn test_search_for_swap_tx_spend_native_was_refunded_maker() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let tx = coin.send_maker_payment(maker_payment_args).wait().unwrap(); + let tx = block_on_f01(coin.send_maker_payment(maker_payment_args)).unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: tx.tx_hex(), @@ -147,7 +148,7 @@ fn test_search_for_swap_tx_spend_native_was_refunded_maker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let maker_refunds_payment_args = RefundPaymentArgs { payment_tx: &tx.tx_hex(), time_lock, @@ -168,7 +169,7 @@ fn test_search_for_swap_tx_spend_native_was_refunded_maker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock, @@ -207,7 +208,7 @@ fn test_search_for_taker_swap_tx_spend_native_was_spent_by_maker() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let tx = coin.send_taker_payment(taker_payment_args).wait().unwrap(); + let tx = block_on_f01(coin.send_taker_payment(taker_payment_args)).unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: tx.tx_hex(), @@ -216,7 +217,7 @@ fn test_search_for_taker_swap_tx_spend_native_was_spent_by_maker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let maker_spends_payment_args = SpendPaymentArgs { other_payment_tx: &tx.tx_hex(), time_lock, @@ -236,7 +237,7 @@ fn test_search_for_taker_swap_tx_spend_native_was_spent_by_maker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock, @@ -275,7 +276,7 @@ fn test_search_for_maker_swap_tx_spend_native_was_spent_by_taker() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let tx = coin.send_maker_payment(maker_payment_args).wait().unwrap(); + let tx = block_on_f01(coin.send_maker_payment(maker_payment_args)).unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: tx.tx_hex(), @@ -284,7 +285,7 @@ fn test_search_for_maker_swap_tx_spend_native_was_spent_by_taker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let taker_spends_payment_args = SpendPaymentArgs { other_payment_tx: &tx.tx_hex(), time_lock, @@ -304,7 +305,7 @@ fn test_search_for_maker_swap_tx_spend_native_was_spent_by_taker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock, @@ -346,7 +347,7 @@ fn test_one_hundred_maker_payments_in_a_row_native() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let tx = coin.send_maker_payment(maker_payment_args).wait().unwrap(); + let tx = block_on_f01(coin.send_maker_payment(maker_payment_args)).unwrap(); if let TransactionEnum::UtxoTx(tx) = tx { unspents.push(UnspentInfo { outpoint: OutPoint { @@ -2472,16 +2473,12 @@ fn test_maker_order_should_not_kick_start_and_appear_in_orderbook_if_balance_is_ bob_conf["log"] = mm_bob.folder.join("mm2_dup.log").to_str().unwrap().into(); block_on(mm_bob.stop()).unwrap(); - let withdraw = coin - .withdraw(WithdrawRequest::new_max( - "MYCOIN".to_string(), - "RRYmiZSDo3UdHHqj1rLKf8cbJroyv9NxXw".to_string(), - )) - .wait() - .unwrap(); - coin.send_raw_tx(&hex::encode(&withdraw.tx.tx_hex().unwrap().0)) - .wait() - .unwrap(); + let withdraw = block_on_f01(coin.withdraw(WithdrawRequest::new_max( + "MYCOIN".to_string(), + "RRYmiZSDo3UdHHqj1rLKf8cbJroyv9NxXw".to_string(), + ))) + .unwrap(); + block_on_f01(coin.send_raw_tx(&hex::encode(&withdraw.tx.tx_hex().unwrap().0))).unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: withdraw.tx.tx_hex().unwrap().0.to_owned(), confirmations: 1, @@ -2489,7 +2486,7 @@ fn test_maker_order_should_not_kick_start_and_appear_in_orderbook_if_balance_is_ wait_until: wait_until_sec(10), check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let mm_bob_dup = MarketMakerIt::start(bob_conf, "pass".to_string(), None).unwrap(); let (_bob_dup_dump_log, _bob_dup_dump_dashboard) = mm_dump(&mm_bob_dup.log_path); diff --git a/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs b/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs index 3a898b0f2f..cc44cb5af1 100644 --- a/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs @@ -19,10 +19,9 @@ use coins::{lp_coinfind, CoinProtocol, CoinWithDerivationMethod, CoinsContext, C SendNftMakerPaymentArgs, SendPaymentArgs, SendTakerFundingArgs, SpendNftMakerPaymentArgs, SpendPaymentArgs, SwapOps, SwapTxFeePolicy, SwapTxTypeWithSecretHash, TakerCoinSwapOpsV2, ToBytes, Transaction, TxPreimageWithSig, ValidateNftMakerPaymentArgs, ValidateTakerFundingArgs}; -use common::{block_on, now_sec}; +use common::{block_on, block_on_f01, now_sec}; use crypto::Secp256k1Secret; use ethereum_types::U256; -use futures01::Future; use mm2_core::mm_ctx::MmArc; use mm2_number::{BigDecimal, BigUint}; use mm2_test_helpers::for_tests::{erc20_dev_conf, eth_dev_conf, eth_sepolia_conf, nft_dev_conf, sepolia_erc20_dev_conf}; @@ -540,7 +539,7 @@ fn send_and_refund_eth_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) { watcher_reward: None, wait_for_confirmation_until: 0, }; - let eth_maker_payment = eth_coin.send_maker_payment(send_payment_args).wait().unwrap(); + let eth_maker_payment = block_on_f01(eth_coin.send_maker_payment(send_payment_args)).unwrap(); let confirm_input = ConfirmPaymentInput { payment_tx: eth_maker_payment.tx_hex(), @@ -549,7 +548,7 @@ fn send_and_refund_eth_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) { wait_until: now_sec() + 60, check_every: 1, }; - eth_coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(eth_coin.wait_for_confirmations(confirm_input)).unwrap(); let refund_args = RefundPaymentArgs { payment_tx: ð_maker_payment.tx_hex(), @@ -572,7 +571,7 @@ fn send_and_refund_eth_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) { wait_until: now_sec() + 60, check_every: 1, }; - eth_coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(eth_coin.wait_for_confirmations(confirm_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock, @@ -626,7 +625,7 @@ fn send_and_spend_eth_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) { watcher_reward: None, wait_for_confirmation_until: 0, }; - let eth_maker_payment = maker_eth_coin.send_maker_payment(send_payment_args).wait().unwrap(); + let eth_maker_payment = block_on_f01(maker_eth_coin.send_maker_payment(send_payment_args)).unwrap(); let confirm_input = ConfirmPaymentInput { payment_tx: eth_maker_payment.tx_hex(), @@ -635,7 +634,7 @@ fn send_and_spend_eth_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) { wait_until: now_sec() + 60, check_every: 1, }; - taker_eth_coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(taker_eth_coin.wait_for_confirmations(confirm_input)).unwrap(); let spend_args = SpendPaymentArgs { other_payment_tx: ð_maker_payment.tx_hex(), @@ -657,7 +656,7 @@ fn send_and_spend_eth_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) { wait_until: now_sec() + 60, check_every: 1, }; - taker_eth_coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(taker_eth_coin.wait_for_confirmations(confirm_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock, @@ -709,7 +708,7 @@ fn send_and_refund_erc20_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) watcher_reward: None, wait_for_confirmation_until: now_sec() + 60, }; - let eth_maker_payment = erc20_coin.send_maker_payment(send_payment_args).wait().unwrap(); + let eth_maker_payment = block_on_f01(erc20_coin.send_maker_payment(send_payment_args)).unwrap(); let confirm_input = ConfirmPaymentInput { payment_tx: eth_maker_payment.tx_hex(), @@ -718,7 +717,7 @@ fn send_and_refund_erc20_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) wait_until: now_sec() + 60, check_every: 1, }; - erc20_coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(erc20_coin.wait_for_confirmations(confirm_input)).unwrap(); let refund_args = RefundPaymentArgs { payment_tx: ð_maker_payment.tx_hex(), @@ -741,7 +740,7 @@ fn send_and_refund_erc20_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) wait_until: now_sec() + 60, check_every: 1, }; - erc20_coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(erc20_coin.wait_for_confirmations(confirm_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock, @@ -798,7 +797,7 @@ fn send_and_spend_erc20_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) { watcher_reward: None, wait_for_confirmation_until: now_sec() + 60, }; - let eth_maker_payment = maker_erc20_coin.send_maker_payment(send_payment_args).wait().unwrap(); + let eth_maker_payment = block_on_f01(maker_erc20_coin.send_maker_payment(send_payment_args)).unwrap(); let confirm_input = ConfirmPaymentInput { payment_tx: eth_maker_payment.tx_hex(), @@ -807,7 +806,7 @@ fn send_and_spend_erc20_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) { wait_until: now_sec() + 60, check_every: 1, }; - taker_erc20_coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(taker_erc20_coin.wait_for_confirmations(confirm_input)).unwrap(); let spend_args = SpendPaymentArgs { other_payment_tx: ð_maker_payment.tx_hex(), @@ -829,7 +828,7 @@ fn send_and_spend_erc20_maker_payment_impl(swap_txfee_policy: SwapTxFeePolicy) { wait_until: now_sec() + 60, check_every: 1, }; - taker_erc20_coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(taker_erc20_coin.wait_for_confirmations(confirm_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock, @@ -971,12 +970,12 @@ fn test_nonce_several_urls() { // Use one working and one failing URL. let coin = eth_coin_with_random_privkey_using_urls(swap_contract(), &[GETH_RPC_URL, "http://127.0.0.1:0"]); let my_address = block_on(coin.derivation_method().single_addr_or_err()).unwrap(); - let (old_nonce, _) = coin.clone().get_addr_nonce(my_address).wait().unwrap(); + let (old_nonce, _) = block_on_f01(coin.clone().get_addr_nonce(my_address)).unwrap(); // Send a payment to increase the nonce. - coin.send_to_address(my_address, 200000000.into()).wait().unwrap(); + block_on_f01(coin.send_to_address(my_address, 200000000.into())).unwrap(); - let (new_nonce, _) = coin.get_addr_nonce(my_address).wait().unwrap(); + let (new_nonce, _) = block_on_f01(coin.get_addr_nonce(my_address)).unwrap(); assert_eq!(old_nonce + 1, new_nonce); } @@ -1308,7 +1307,7 @@ fn wait_for_confirmations(coin: &EthCoin, tx: &SignedEthTx, wait_seconds: u64) { wait_until: now_sec() + wait_seconds, check_every: 1, }; - coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_input)).unwrap(); } fn validate_nft_maker_payment(setup: &NftTestSetup, maker_payment: &SignedEthTx, amount: BigDecimal) { diff --git a/mm2src/mm2_main/tests/docker_tests/qrc20_tests.rs b/mm2src/mm2_main/tests/docker_tests/qrc20_tests.rs index e66a3a5852..6d8d03d96c 100644 --- a/mm2src/mm2_main/tests/docker_tests/qrc20_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/qrc20_tests.rs @@ -11,10 +11,9 @@ use coins::{CheckIfMyPaymentSentArgs, ConfirmPaymentInput, DexFee, FeeApproxStag SwapTxTypeWithSecretHash, TradePreimageValue, TransactionEnum, ValidateFeeArgs, ValidatePaymentInput, WaitForHTLCTxSpendArgs}; use common::log::debug; -use common::{temp_dir, DEX_FEE_ADDR_RAW_PUBKEY}; +use common::{block_on_f01, temp_dir, DEX_FEE_ADDR_RAW_PUBKEY}; use crypto::Secp256k1Secret; use ethereum_types::H160; -use futures01::Future; use http::StatusCode; use mm2_core::mm_ctx::{MmArc, MmCtxBuilder}; use mm2_main::lp_swap::{dex_fee_amount, max_taker_vol_from_available}; @@ -78,9 +77,7 @@ impl QtumDockerOps { match self.coin.as_ref().rpc_client { UtxoRpcClientEnum::Native(ref native) => { - let result = native - .create_contract(&bytecode.into(), gas_limit, gas_price, sender) - .wait() + let result = block_on_f01(native.create_contract(&bytecode.into(), gas_limit, gas_price, sender)) .expect("!createcontract"); result.address.0.into() }, @@ -165,14 +162,8 @@ fn withdraw_and_send(mm: &MarketMakerIt, coin: &str, to: &str, amount: f64) { fn test_taker_spends_maker_payment() { let (_ctx, maker_coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 10.into()); let (_ctx, taker_coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 1.into()); - let maker_old_balance = maker_coin - .my_spendable_balance() - .wait() - .expect("Error on get maker balance"); - let taker_old_balance = taker_coin - .my_spendable_balance() - .wait() - .expect("Error on get taker balance"); + let maker_old_balance = block_on_f01(maker_coin.my_spendable_balance()).expect("Error on get maker balance"); + let taker_old_balance = block_on_f01(taker_coin.my_spendable_balance()).expect("Error on get taker balance"); assert_eq!(maker_old_balance, BigDecimal::from(10)); assert_eq!(taker_old_balance, BigDecimal::from(1)); @@ -194,7 +185,7 @@ fn test_taker_spends_maker_payment() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let payment = maker_coin.send_maker_payment(maker_payment_args).wait().unwrap(); + let payment = block_on_f01(maker_coin.send_maker_payment(maker_payment_args)).unwrap(); let payment_tx_hash = payment.tx_hash_as_bytes(); let payment_tx_hex = payment.tx_hex(); log!("Maker payment: {:?}", payment_tx_hash); @@ -210,7 +201,7 @@ fn test_taker_spends_maker_payment() { wait_until, check_every, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let input = ValidatePaymentInput { payment_tx: payment_tx_hex.clone(), @@ -249,16 +240,10 @@ fn test_taker_spends_maker_payment() { wait_until, check_every, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); - - let maker_balance = maker_coin - .my_spendable_balance() - .wait() - .expect("Error on get maker balance"); - let taker_balance = taker_coin - .my_spendable_balance() - .wait() - .expect("Error on get taker balance"); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); + + let maker_balance = block_on_f01(maker_coin.my_spendable_balance()).expect("Error on get maker balance"); + let taker_balance = block_on_f01(taker_coin.my_spendable_balance()).expect("Error on get taker balance"); assert_eq!(maker_old_balance - amount.clone(), maker_balance); assert_eq!(taker_old_balance + amount, taker_balance); } @@ -267,14 +252,8 @@ fn test_taker_spends_maker_payment() { fn test_maker_spends_taker_payment() { let (_ctx, maker_coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 10.into()); let (_ctx, taker_coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 10.into()); - let maker_old_balance = maker_coin - .my_spendable_balance() - .wait() - .expect("Error on get maker balance"); - let taker_old_balance = taker_coin - .my_spendable_balance() - .wait() - .expect("Error on get taker balance"); + let maker_old_balance = block_on_f01(maker_coin.my_spendable_balance()).expect("Error on get maker balance"); + let taker_old_balance = block_on_f01(taker_coin.my_spendable_balance()).expect("Error on get taker balance"); assert_eq!(maker_old_balance, BigDecimal::from(10)); assert_eq!(taker_old_balance, BigDecimal::from(10)); @@ -296,7 +275,7 @@ fn test_maker_spends_taker_payment() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let payment = taker_coin.send_taker_payment(taker_payment_args).wait().unwrap(); + let payment = block_on_f01(taker_coin.send_taker_payment(taker_payment_args)).unwrap(); let payment_tx_hash = payment.tx_hash_as_bytes(); let payment_tx_hex = payment.tx_hex(); log!("Taker payment: {:?}", payment_tx_hash); @@ -312,7 +291,7 @@ fn test_maker_spends_taker_payment() { wait_until, check_every, }; - maker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(maker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let input = ValidatePaymentInput { payment_tx: payment_tx_hex.clone(), @@ -351,16 +330,10 @@ fn test_maker_spends_taker_payment() { wait_until, check_every, }; - maker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); - - let maker_balance = maker_coin - .my_spendable_balance() - .wait() - .expect("Error on get maker balance"); - let taker_balance = taker_coin - .my_spendable_balance() - .wait() - .expect("Error on get taker balance"); + block_on_f01(maker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); + + let maker_balance = block_on_f01(maker_coin.my_spendable_balance()).expect("Error on get maker balance"); + let taker_balance = block_on_f01(taker_coin.my_spendable_balance()).expect("Error on get taker balance"); assert_eq!(maker_old_balance + amount.clone(), maker_balance); assert_eq!(taker_old_balance - amount, taker_balance); } @@ -368,7 +341,7 @@ fn test_maker_spends_taker_payment() { #[test] fn test_maker_refunds_payment() { let (_ctx, coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 10.into()); - let expected_balance = coin.my_spendable_balance().wait().unwrap(); + let expected_balance = block_on_f01(coin.my_spendable_balance()).unwrap(); assert_eq!(expected_balance, BigDecimal::from(10)); let timelock = now_sec() - 200; @@ -387,7 +360,7 @@ fn test_maker_refunds_payment() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let payment = coin.send_maker_payment(maker_payment).wait().unwrap(); + let payment = block_on_f01(coin.send_maker_payment(maker_payment)).unwrap(); let payment_tx_hash = payment.tx_hash_as_bytes(); let payment_tx_hex = payment.tx_hex(); log!("Maker payment: {:?}", payment_tx_hash); @@ -403,9 +376,9 @@ fn test_maker_refunds_payment() { wait_until, check_every, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let balance_after_payment = coin.my_spendable_balance().wait().unwrap(); + let balance_after_payment = block_on_f01(coin.my_spendable_balance()).unwrap(); assert_eq!(expected_balance.clone() - amount, balance_after_payment); let maker_refunds_payment_args = RefundPaymentArgs { payment_tx: &payment_tx_hex, @@ -431,16 +404,16 @@ fn test_maker_refunds_payment() { wait_until, check_every, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let balance_after_refund = coin.my_spendable_balance().wait().unwrap(); + let balance_after_refund = block_on_f01(coin.my_spendable_balance()).unwrap(); assert_eq!(expected_balance, balance_after_refund); } #[test] fn test_taker_refunds_payment() { let (_ctx, coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 10.into()); - let expected_balance = coin.my_spendable_balance().wait().unwrap(); + let expected_balance = block_on_f01(coin.my_spendable_balance()).unwrap(); assert_eq!(expected_balance, BigDecimal::from(10)); let timelock = now_sec() - 200; @@ -459,7 +432,7 @@ fn test_taker_refunds_payment() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let payment = coin.send_taker_payment(taker_payment_args).wait().unwrap(); + let payment = block_on_f01(coin.send_taker_payment(taker_payment_args)).unwrap(); let payment_tx_hash = payment.tx_hash_as_bytes(); let payment_tx_hex = payment.tx_hex(); log!("Taker payment: {:?}", payment_tx_hash); @@ -475,9 +448,9 @@ fn test_taker_refunds_payment() { wait_until, check_every, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let balance_after_payment = coin.my_spendable_balance().wait().unwrap(); + let balance_after_payment = block_on_f01(coin.my_spendable_balance()).unwrap(); assert_eq!(expected_balance.clone() - amount, balance_after_payment); let taker_refunds_payment_args = RefundPaymentArgs { payment_tx: &payment_tx_hex, @@ -503,9 +476,9 @@ fn test_taker_refunds_payment() { wait_until, check_every, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let balance_after_refund = coin.my_spendable_balance().wait().unwrap(); + let balance_after_refund = block_on_f01(coin.my_spendable_balance()).unwrap(); assert_eq!(expected_balance, balance_after_refund); } @@ -528,7 +501,7 @@ fn test_check_if_my_payment_sent() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let payment = coin.send_maker_payment(maker_payment_args).wait().unwrap(); + let payment = block_on_f01(coin.send_maker_payment(maker_payment_args)).unwrap(); let payment_tx_hash = payment.tx_hash_as_bytes(); let payment_tx_hex = payment.tx_hex(); log!("Maker payment: {:?}", payment_tx_hash); @@ -544,9 +517,9 @@ fn test_check_if_my_payment_sent() { wait_until, check_every, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let search_from_block = coin.current_block().wait().expect("!current_block") - 10; + let search_from_block = block_on_f01(coin.current_block()).expect("!current_block") - 10; let if_my_payment_sent_args = CheckIfMyPaymentSentArgs { time_lock: timelock, other_pub: &taker_pub, @@ -557,7 +530,7 @@ fn test_check_if_my_payment_sent() { amount: &amount, payment_instructions: &None, }; - let found = coin.check_if_my_payment_sent(if_my_payment_sent_args).wait().unwrap(); + let found = block_on_f01(coin.check_if_my_payment_sent(if_my_payment_sent_args)).unwrap(); assert_eq!(found, Some(payment)); } @@ -565,7 +538,7 @@ fn test_check_if_my_payment_sent() { fn test_search_for_swap_tx_spend_taker_spent() { let (_ctx, maker_coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 10.into()); let (_ctx, taker_coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 1.into()); - let search_from_block = maker_coin.current_block().wait().expect("!current_block"); + let search_from_block = block_on_f01(maker_coin.current_block()).expect("!current_block"); let timelock = now_sec() - 200; let maker_pub = maker_coin.my_public_key().unwrap(); @@ -585,7 +558,7 @@ fn test_search_for_swap_tx_spend_taker_spent() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let payment = maker_coin.send_maker_payment(maker_payment_args).wait().unwrap(); + let payment = block_on_f01(maker_coin.send_maker_payment(maker_payment_args)).unwrap(); let payment_tx_hash = payment.tx_hash_as_bytes(); let payment_tx_hex = payment.tx_hex(); log!("Maker payment: {:?}", payment_tx_hash); @@ -601,7 +574,7 @@ fn test_search_for_swap_tx_spend_taker_spent() { wait_until, check_every, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let taker_spends_payment_args = SpendPaymentArgs { other_payment_tx: &payment_tx_hex, time_lock: timelock, @@ -625,7 +598,7 @@ fn test_search_for_swap_tx_spend_taker_spent() { wait_until, check_every, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock: timelock, @@ -645,7 +618,7 @@ fn test_search_for_swap_tx_spend_taker_spent() { #[test] fn test_search_for_swap_tx_spend_maker_refunded() { let (_ctx, maker_coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 10.into()); - let search_from_block = maker_coin.current_block().wait().expect("!current_block"); + let search_from_block = block_on_f01(maker_coin.current_block()).expect("!current_block"); let timelock = now_sec() - 200; let taker_pub = hex::decode("022b00078841f37b5d30a6a1defb82b3af4d4e2d24dd4204d41f0c9ce1e875de1a").unwrap(); @@ -664,7 +637,7 @@ fn test_search_for_swap_tx_spend_maker_refunded() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let payment = maker_coin.send_maker_payment(maker_payment_args).wait().unwrap(); + let payment = block_on_f01(maker_coin.send_maker_payment(maker_payment_args)).unwrap(); let payment_tx_hash = payment.tx_hash_as_bytes(); let payment_tx_hex = payment.tx_hex(); log!("Maker payment: {:?}", payment_tx_hash); @@ -680,7 +653,7 @@ fn test_search_for_swap_tx_spend_maker_refunded() { wait_until, check_every, }; - maker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(maker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let maker_refunds_payment_args = RefundPaymentArgs { payment_tx: &payment_tx_hex, time_lock: timelock, @@ -705,7 +678,7 @@ fn test_search_for_swap_tx_spend_maker_refunded() { wait_until, check_every, }; - maker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(maker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock: timelock, @@ -725,7 +698,7 @@ fn test_search_for_swap_tx_spend_maker_refunded() { #[test] fn test_search_for_swap_tx_spend_not_spent() { let (_ctx, maker_coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 10.into()); - let search_from_block = maker_coin.current_block().wait().expect("!current_block"); + let search_from_block = block_on_f01(maker_coin.current_block()).expect("!current_block"); let timelock = now_sec() - 200; let taker_pub = hex::decode("022b00078841f37b5d30a6a1defb82b3af4d4e2d24dd4204d41f0c9ce1e875de1a").unwrap(); @@ -744,7 +717,7 @@ fn test_search_for_swap_tx_spend_not_spent() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let payment = maker_coin.send_maker_payment(maker_payment_args).wait().unwrap(); + let payment = block_on_f01(maker_coin.send_maker_payment(maker_payment_args)).unwrap(); let payment_tx_hash = payment.tx_hash_as_bytes(); let payment_tx_hex = payment.tx_hex(); log!("Maker payment: {:?}", payment_tx_hash); @@ -760,7 +733,7 @@ fn test_search_for_swap_tx_spend_not_spent() { wait_until, check_every, }; - maker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(maker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock: timelock, @@ -781,7 +754,7 @@ fn test_search_for_swap_tx_spend_not_spent() { fn test_wait_for_tx_spend() { let (_ctx, maker_coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 10.into()); let (_ctx, taker_coin, _priv_key) = generate_qrc20_coin_with_random_privkey("QICK", 20.into(), 1.into()); - let from_block = maker_coin.current_block().wait().expect("!current_block"); + let from_block = block_on_f01(maker_coin.current_block()).expect("!current_block"); let timelock = now_sec() - 200; let maker_pub = maker_coin.my_public_key().unwrap(); @@ -801,7 +774,7 @@ fn test_wait_for_tx_spend() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let payment = maker_coin.send_maker_payment(maker_payment_args).wait().unwrap(); + let payment = block_on_f01(maker_coin.send_maker_payment(maker_payment_args)).unwrap(); let payment_tx_hash = payment.tx_hash_as_bytes(); let payment_tx_hex = payment.tx_hex(); log!("Maker payment: {:?}", payment_tx_hash); @@ -817,22 +790,20 @@ fn test_wait_for_tx_spend() { wait_until, check_every, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); // first try to check if the wait_for_htlc_tx_spend() returns an error correctly let wait_until = wait_until_sec(5); - let tx_err = maker_coin - .wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { - tx_bytes: &payment_tx_hex, - secret_hash: &[], - wait_until, - from_block, - swap_contract_address: &maker_coin.swap_contract_address(), - check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, - watcher_reward: false, - }) - .wait() - .expect_err("Expected 'Waited too long' error"); + let tx_err = block_on_f01(maker_coin.wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { + tx_bytes: &payment_tx_hex, + secret_hash: &[], + wait_until, + from_block, + swap_contract_address: &maker_coin.swap_contract_address(), + check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, + watcher_reward: false, + })) + .expect_err("Expected 'Waited too long' error"); let err = tx_err.get_plain_text_format(); log!("error: {:?}", err); @@ -860,18 +831,16 @@ fn test_wait_for_tx_spend() { }); let wait_until = wait_until_sec(120); - let found = maker_coin - .wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { - tx_bytes: &payment_tx_hex, - secret_hash: &[], - wait_until, - from_block, - swap_contract_address: &maker_coin.swap_contract_address(), - check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, - watcher_reward: false, - }) - .wait() - .unwrap(); + let found = block_on_f01(maker_coin.wait_for_htlc_tx_spend(WaitForHTLCTxSpendArgs { + tx_bytes: &payment_tx_hex, + secret_hash: &[], + wait_until, + from_block, + swap_contract_address: &maker_coin.swap_contract_address(), + check_every: TAKER_PAYMENT_SPEND_SEARCH_INTERVAL, + watcher_reward: false, + })) + .unwrap(); unsafe { assert_eq!(Some(found), SPEND_TX) } } @@ -1030,7 +999,7 @@ fn test_get_max_taker_vol_and_trade_with_dynamic_trade_fee(coin: QtumCoin, priv_ log!("{:?}", block_on(enable_native(&mm, "MYCOIN", &[], None))); log!("{:?}", block_on(enable_native(&mm, "QTUM", &[], None))); - let qtum_balance = coin.my_spendable_balance().wait().expect("!my_balance"); + let qtum_balance = block_on_f01(coin.my_spendable_balance()).expect("!my_balance"); let qtum_min_tx_amount = MmNumber::from("0.000728"); // - `max_possible = balance - locked_amount`, where `locked_amount = 0` @@ -1103,10 +1072,8 @@ fn test_get_max_taker_vol_and_trade_with_dynamic_trade_fee(coin: QtumCoin, priv_ let secret_hash = &[0; 20]; let dex_fee = dex_fee_amount("QTUM", "MYCOIN", &expected_max_taker_vol, &qtum_min_tx_amount); - let _taker_fee_tx = coin - .send_taker_fee(&DEX_FEE_ADDR_RAW_PUBKEY, dex_fee, &[], timelock) - .wait() - .expect("!send_taker_fee"); + let _taker_fee_tx = + block_on_f01(coin.send_taker_fee(&DEX_FEE_ADDR_RAW_PUBKEY, dex_fee, &[], timelock)).expect("!send_taker_fee"); let taker_payment_args = SendPaymentArgs { time_lock_duration: 0, time_lock: timelock, @@ -1120,12 +1087,9 @@ fn test_get_max_taker_vol_and_trade_with_dynamic_trade_fee(coin: QtumCoin, priv_ wait_for_confirmation_until: 0, }; - let _taker_payment_tx = coin - .send_taker_payment(taker_payment_args) - .wait() - .expect("!send_taker_payment"); + let _taker_payment_tx = block_on_f01(coin.send_taker_payment(taker_payment_args)).expect("!send_taker_payment"); - let my_balance = coin.my_spendable_balance().wait().expect("!my_balance"); + let my_balance = block_on_f01(coin.my_spendable_balance()).expect("!my_balance"); assert_eq!( my_balance, BigDecimal::from(0u32), @@ -1519,7 +1483,7 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_maker() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let tx = coin.send_maker_payment(maker_payment).wait().unwrap(); + let tx = block_on_f01(coin.send_maker_payment(maker_payment)).unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: tx.tx_hex(), @@ -1528,7 +1492,7 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_maker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let maker_refunds_payment_args = RefundPaymentArgs { payment_tx: &tx.tx_hex(), time_lock, @@ -1549,7 +1513,7 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_maker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock, @@ -1587,7 +1551,7 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_taker() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let tx = coin.send_taker_payment(taker_payment).wait().unwrap(); + let tx = block_on_f01(coin.send_taker_payment(taker_payment)).unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: tx.tx_hex(), @@ -1596,7 +1560,7 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_taker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let maker_refunds_payment_args = RefundPaymentArgs { payment_tx: &tx.tx_hex(), time_lock, @@ -1617,7 +1581,7 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_taker() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock, @@ -1741,26 +1705,23 @@ fn test_send_taker_fee_qtum() { generate_segwit_qtum_coin_with_random_privkey("QTUM", BigDecimal::try_from(0.5).unwrap(), Some(0)); let amount = BigDecimal::from_str("0.01").unwrap(); - let tx = coin - .send_taker_fee( - &DEX_FEE_ADDR_RAW_PUBKEY, - DexFee::Standard(amount.clone().into()), - &[], - 0, - ) - .wait() - .expect("!send_taker_fee"); + let tx = block_on_f01(coin.send_taker_fee( + &DEX_FEE_ADDR_RAW_PUBKEY, + DexFee::Standard(amount.clone().into()), + &[], + 0, + )) + .expect("!send_taker_fee"); assert!(matches!(tx, TransactionEnum::UtxoTx(_)), "Expected UtxoTx"); - coin.validate_fee(ValidateFeeArgs { + block_on_f01(coin.validate_fee(ValidateFeeArgs { fee_tx: &tx, expected_sender: coin.my_public_key().unwrap(), fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, dex_fee: &DexFee::Standard(amount.into()), min_block_number: 0, uuid: &[], - }) - .wait() + })) .expect("!validate_fee"); } @@ -1773,25 +1734,22 @@ fn test_send_taker_fee_qrc20() { ); let amount = BigDecimal::from_str("0.01").unwrap(); - let tx = coin - .send_taker_fee( - &DEX_FEE_ADDR_RAW_PUBKEY, - DexFee::Standard(amount.clone().into()), - &[], - 0, - ) - .wait() - .expect("!send_taker_fee"); + let tx = block_on_f01(coin.send_taker_fee( + &DEX_FEE_ADDR_RAW_PUBKEY, + DexFee::Standard(amount.clone().into()), + &[], + 0, + )) + .expect("!send_taker_fee"); assert!(matches!(tx, TransactionEnum::UtxoTx(_)), "Expected UtxoTx"); - coin.validate_fee(ValidateFeeArgs { + block_on_f01(coin.validate_fee(ValidateFeeArgs { fee_tx: &tx, expected_sender: coin.my_public_key().unwrap(), fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, dex_fee: &DexFee::Standard(amount.into()), min_block_number: 0, uuid: &[], - }) - .wait() + })) .expect("!validate_fee"); } diff --git a/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs b/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs index 672ef88aa5..46d950242d 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs @@ -6,8 +6,7 @@ use coins::{ConfirmPaymentInput, DexFee, FundingTxSpend, GenTakerFundingSpendArg RefundMakerPaymentSecretArgs, RefundMakerPaymentTimelockArgs, RefundTakerPaymentArgs, SendMakerPaymentArgs, SendTakerFundingArgs, SwapTxTypeWithSecretHash, TakerCoinSwapOpsV2, Transaction, ValidateMakerPaymentArgs, ValidateTakerFundingArgs}; -use common::{block_on, now_sec, DEX_FEE_ADDR_RAW_PUBKEY}; -use futures01::Future; +use common::{block_on, block_on_f01, now_sec, DEX_FEE_ADDR_RAW_PUBKEY}; use mm2_number::MmNumber; use mm2_test_helpers::for_tests::{active_swaps, check_recent_swaps, coins_needed_for_kickstart, disable_coin, disable_coin_err, enable_native, get_locked_amount, mm_dump, my_swap_status, @@ -93,7 +92,7 @@ fn send_and_refund_taker_funding_timelock() { wait_until: now_sec() + 20, check_every: 1, }; - coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_input)).unwrap(); let found_refund_tx = block_on(coin.search_for_taker_funding_spend(&taker_funding_utxo_tx, 1, taker_secret_hash)).unwrap(); @@ -180,7 +179,7 @@ fn send_and_refund_taker_funding_secret() { wait_until: now_sec() + 20, check_every: 1, }; - coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_input)).unwrap(); let found_refund_tx = block_on(coin.search_for_taker_funding_spend(&taker_funding_utxo_tx, 1, taker_secret_hash)).unwrap(); @@ -268,7 +267,7 @@ fn send_and_spend_taker_funding() { wait_until: now_sec() + 20, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_input)).unwrap(); let found_spend_tx = block_on(taker_coin.search_for_taker_funding_spend(&taker_funding_utxo_tx, 1, taker_secret_hash)).unwrap(); diff --git a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs index 22c17c1444..aadda99709 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -13,9 +13,8 @@ use coins::{ConfirmPaymentInput, FoundSwapTxSpend, MarketCoinOps, MmCoin, MmCoin INVALID_CONTRACT_ADDRESS_ERR_LOG, INVALID_PAYMENT_STATE_ERR_LOG, INVALID_RECEIVER_ERR_LOG, INVALID_REFUND_TX_ERR_LOG, INVALID_SCRIPT_ERR_LOG, INVALID_SENDER_ERR_LOG, INVALID_SWAP_ID_ERR_LOG, OLD_TRANSACTION_ERR_LOG}; -use common::{block_on, now_sec, wait_until_sec, DEX_FEE_ADDR_RAW_PUBKEY}; +use common::{block_on, block_on_f01, now_sec, wait_until_sec, DEX_FEE_ADDR_RAW_PUBKEY}; use crypto::privkey::{key_pair_from_secret, key_pair_from_seed}; -use futures01::Future; use mm2_main::lp_swap::{dex_fee_amount, dex_fee_amount_from_taker_coin, generate_secret, get_payment_locktime, MAKER_PAYMENT_SENT_LOG, MAKER_PAYMENT_SPEND_FOUND_LOG, MAKER_PAYMENT_SPEND_SENT_LOG, REFUND_TEST_FAILURE_LOG, TAKER_PAYMENT_REFUND_SENT_LOG, WATCHER_MESSAGE_SENT_LOG}; @@ -1208,15 +1207,13 @@ fn test_watcher_validate_taker_fee_utxo() { let taker_amount = MmNumber::from((10, 1)); let fee_amount = dex_fee_amount_from_taker_coin(&taker_coin, maker_coin.ticker(), &taker_amount); - let taker_fee = taker_coin - .send_taker_fee( - &DEX_FEE_ADDR_RAW_PUBKEY, - fee_amount, - Uuid::new_v4().as_bytes(), - lock_duration, - ) - .wait() - .unwrap(); + let taker_fee = block_on_f01(taker_coin.send_taker_fee( + &DEX_FEE_ADDR_RAW_PUBKEY, + fee_amount, + Uuid::new_v4().as_bytes(), + lock_duration, + )) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_fee.tx_hex(), @@ -1226,30 +1223,26 @@ fn test_watcher_validate_taker_fee_utxo() { check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let validate_taker_fee_res = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: taker_pubkey.to_vec(), - min_block_number: 0, - fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), - lock_duration, - }) - .wait(); + let validate_taker_fee_res = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: taker_pubkey.to_vec(), + min_block_number: 0, + fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), + lock_duration, + })); assert!(validate_taker_fee_res.is_ok()); - let error = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: maker_coin.my_public_key().unwrap().to_vec(), - min_block_number: 0, - fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), - lock_duration, - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: maker_coin.my_public_key().unwrap().to_vec(), + min_block_number: 0, + fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), + lock_duration, + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { @@ -1259,17 +1252,15 @@ fn test_watcher_validate_taker_fee_utxo() { _ => panic!("Expected `WrongPaymentTx` invalid public key, found {:?}", error), } - let error = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: taker_pubkey.to_vec(), - min_block_number: std::u64::MAX, - fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), - lock_duration, - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: taker_pubkey.to_vec(), + min_block_number: std::u64::MAX, + fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), + lock_duration, + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -1281,17 +1272,15 @@ fn test_watcher_validate_taker_fee_utxo() { ), } - let error = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: taker_pubkey.to_vec(), - min_block_number: 0, - fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), - lock_duration: 0, - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: taker_pubkey.to_vec(), + min_block_number: 0, + fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), + lock_duration: 0, + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -1300,17 +1289,15 @@ fn test_watcher_validate_taker_fee_utxo() { _ => panic!("Expected `WrongPaymentTx` transaction too old, found {:?}", error), } - let error = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: taker_pubkey.to_vec(), - min_block_number: 0, - fee_addr: taker_pubkey.to_vec(), - lock_duration, - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: taker_pubkey.to_vec(), + min_block_number: 0, + fee_addr: taker_pubkey.to_vec(), + lock_duration, + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -1334,15 +1321,13 @@ fn test_watcher_validate_taker_fee_eth() { let taker_amount = MmNumber::from((1, 1)); let fee_amount = dex_fee_amount_from_taker_coin(&taker_coin, "ETH", &taker_amount); - let taker_fee = taker_coin - .send_taker_fee( - &DEX_FEE_ADDR_RAW_PUBKEY, - fee_amount, - Uuid::new_v4().as_bytes(), - lock_duration, - ) - .wait() - .unwrap(); + let taker_fee = block_on_f01(taker_coin.send_taker_fee( + &DEX_FEE_ADDR_RAW_PUBKEY, + fee_amount, + Uuid::new_v4().as_bytes(), + lock_duration, + )) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_fee.tx_hex(), @@ -1351,31 +1336,27 @@ fn test_watcher_validate_taker_fee_eth() { wait_until: timeout, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); - - let validate_taker_fee_res = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: taker_pubkey.to_vec(), - min_block_number: 0, - fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), - lock_duration, - }) - .wait(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); + + let validate_taker_fee_res = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: taker_pubkey.to_vec(), + min_block_number: 0, + fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), + lock_duration, + })); assert!(validate_taker_fee_res.is_ok()); let wrong_keypair = key_pair_from_secret(random_secp256k1_secret().as_slice()).unwrap(); - let error = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: wrong_keypair.public().to_vec(), - min_block_number: 0, - fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), - lock_duration, - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: wrong_keypair.public().to_vec(), + min_block_number: 0, + fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), + lock_duration, + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { @@ -1385,17 +1366,15 @@ fn test_watcher_validate_taker_fee_eth() { _ => panic!("Expected `WrongPaymentTx` invalid public key, found {:?}", error), } - let error = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: taker_pubkey.to_vec(), - min_block_number: std::u64::MAX, - fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), - lock_duration, - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: taker_pubkey.to_vec(), + min_block_number: std::u64::MAX, + fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), + lock_duration, + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -1407,17 +1386,15 @@ fn test_watcher_validate_taker_fee_eth() { ), } - let error = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: taker_pubkey.to_vec(), - min_block_number: 0, - fee_addr: taker_pubkey.to_vec(), - lock_duration, - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: taker_pubkey.to_vec(), + min_block_number: 0, + fee_addr: taker_pubkey.to_vec(), + lock_duration, + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -1441,15 +1418,13 @@ fn test_watcher_validate_taker_fee_erc20() { let taker_amount = MmNumber::from((1, 1)); let fee_amount = dex_fee_amount_from_taker_coin(&taker_coin, "ETH", &taker_amount); - let taker_fee = taker_coin - .send_taker_fee( - &DEX_FEE_ADDR_RAW_PUBKEY, - fee_amount, - Uuid::new_v4().as_bytes(), - lock_duration, - ) - .wait() - .unwrap(); + let taker_fee = block_on_f01(taker_coin.send_taker_fee( + &DEX_FEE_ADDR_RAW_PUBKEY, + fee_amount, + Uuid::new_v4().as_bytes(), + lock_duration, + )) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_fee.tx_hex(), @@ -1458,31 +1433,27 @@ fn test_watcher_validate_taker_fee_erc20() { wait_until: timeout, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); - - let validate_taker_fee_res = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: taker_pubkey.to_vec(), - min_block_number: 0, - fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), - lock_duration, - }) - .wait(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); + + let validate_taker_fee_res = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: taker_pubkey.to_vec(), + min_block_number: 0, + fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), + lock_duration, + })); assert!(validate_taker_fee_res.is_ok()); let wrong_keypair = key_pair_from_secret(random_secp256k1_secret().as_slice()).unwrap(); - let error = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: wrong_keypair.public().to_vec(), - min_block_number: 0, - fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), - lock_duration, - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: wrong_keypair.public().to_vec(), + min_block_number: 0, + fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), + lock_duration, + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { @@ -1492,17 +1463,15 @@ fn test_watcher_validate_taker_fee_erc20() { _ => panic!("Expected `WrongPaymentTx` invalid public key, found {:?}", error), } - let error = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: taker_pubkey.to_vec(), - min_block_number: std::u64::MAX, - fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), - lock_duration, - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: taker_pubkey.to_vec(), + min_block_number: std::u64::MAX, + fee_addr: DEX_FEE_ADDR_RAW_PUBKEY.to_vec(), + lock_duration, + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -1514,17 +1483,15 @@ fn test_watcher_validate_taker_fee_erc20() { ), } - let error = taker_coin - .watcher_validate_taker_fee(WatcherValidateTakerFeeInput { - taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), - sender_pubkey: taker_pubkey.to_vec(), - min_block_number: 0, - fee_addr: taker_pubkey.to_vec(), - lock_duration, - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { + taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), + sender_pubkey: taker_pubkey.to_vec(), + min_block_number: 0, + fee_addr: taker_pubkey.to_vec(), + lock_duration, + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -1552,21 +1519,19 @@ fn test_watcher_validate_taker_payment_utxo() { let secret_hash = dhash160(&generate_secret().unwrap()); - let taker_payment = taker_coin - .send_taker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: maker_pubkey, - secret_hash: secret_hash.as_slice(), - amount: BigDecimal::from(10), - swap_contract_address: &None, - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: None, - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let taker_payment = block_on_f01(taker_coin.send_taker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: maker_pubkey, + secret_hash: secret_hash.as_slice(), + amount: BigDecimal::from(10), + swap_contract_address: &None, + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: None, + wait_for_confirmation_until, + })) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_payment.tx_hex(), @@ -1575,21 +1540,19 @@ fn test_watcher_validate_taker_payment_utxo() { wait_until: timeout, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let taker_payment_refund_preimage = taker_coin - .create_taker_payment_refund_preimage( - &taker_payment.tx_hex(), - time_lock, - maker_pubkey, - secret_hash.as_slice(), - &None, - &[], - ) - .wait() - .unwrap(); - let validate_taker_payment_res = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { + let taker_payment_refund_preimage = block_on_f01(taker_coin.create_taker_payment_refund_preimage( + &taker_payment.tx_hex(), + time_lock, + maker_pubkey, + secret_hash.as_slice(), + &None, + &[], + )) + .unwrap(); + let validate_taker_payment_res = + block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { payment_tx: taker_payment.tx_hex(), taker_payment_refund_preimage: taker_payment_refund_preimage.tx_hex(), time_lock, @@ -1599,25 +1562,22 @@ fn test_watcher_validate_taker_payment_utxo() { wait_until: timeout, confirmations: 1, maker_coin: MmCoinEnum::UtxoCoin(maker_coin.clone()), - }) - .wait(); + })); assert!(validate_taker_payment_res.is_ok()); - let error = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { - payment_tx: taker_payment.tx_hex(), - taker_payment_refund_preimage: taker_payment_refund_preimage.tx_hex(), - time_lock, - taker_pub: maker_pubkey.to_vec(), - maker_pub: maker_pubkey.to_vec(), - secret_hash: secret_hash.to_vec(), - wait_until: timeout, - confirmations: 1, - maker_coin: MmCoinEnum::UtxoCoin(maker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { + payment_tx: taker_payment.tx_hex(), + taker_payment_refund_preimage: taker_payment_refund_preimage.tx_hex(), + time_lock, + taker_pub: maker_pubkey.to_vec(), + maker_pub: maker_pubkey.to_vec(), + secret_hash: secret_hash.to_vec(), + wait_until: timeout, + confirmations: 1, + maker_coin: MmCoinEnum::UtxoCoin(maker_coin.clone()), + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { @@ -1629,21 +1589,19 @@ fn test_watcher_validate_taker_payment_utxo() { // Used to get wrong swap id let wrong_secret_hash = dhash160(&generate_secret().unwrap()); - let error = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { - payment_tx: taker_payment.tx_hex(), - taker_payment_refund_preimage: taker_payment_refund_preimage.tx_hex(), - time_lock, - taker_pub: taker_pubkey.to_vec(), - maker_pub: maker_pubkey.to_vec(), - secret_hash: wrong_secret_hash.to_vec(), - wait_until: timeout, - confirmations: 1, - maker_coin: MmCoinEnum::UtxoCoin(maker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { + payment_tx: taker_payment.tx_hex(), + taker_payment_refund_preimage: taker_payment_refund_preimage.tx_hex(), + time_lock, + taker_pub: taker_pubkey.to_vec(), + maker_pub: maker_pubkey.to_vec(), + secret_hash: wrong_secret_hash.to_vec(), + wait_until: timeout, + confirmations: 1, + maker_coin: MmCoinEnum::UtxoCoin(maker_coin.clone()), + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { @@ -1656,21 +1614,19 @@ fn test_watcher_validate_taker_payment_utxo() { ), } - let taker_payment_wrong_secret = taker_coin - .send_taker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: maker_pubkey, - secret_hash: wrong_secret_hash.as_slice(), - amount: BigDecimal::from(10), - swap_contract_address: &taker_coin.swap_contract_address(), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: None, - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let taker_payment_wrong_secret = block_on_f01(taker_coin.send_taker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: maker_pubkey, + secret_hash: wrong_secret_hash.as_slice(), + amount: BigDecimal::from(10), + swap_contract_address: &taker_coin.swap_contract_address(), + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: None, + wait_for_confirmation_until, + })) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_payment_wrong_secret.tx_hex(), @@ -1679,23 +1635,21 @@ fn test_watcher_validate_taker_payment_utxo() { wait_until: timeout, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let error = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { - payment_tx: taker_payment.tx_hex(), - taker_payment_refund_preimage: taker_payment_refund_preimage.tx_hex(), - time_lock: 500, - taker_pub: taker_pubkey.to_vec(), - maker_pub: maker_pubkey.to_vec(), - secret_hash: wrong_secret_hash.to_vec(), - wait_until: timeout, - confirmations: 1, - maker_coin: MmCoinEnum::UtxoCoin(maker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { + payment_tx: taker_payment.tx_hex(), + taker_payment_refund_preimage: taker_payment_refund_preimage.tx_hex(), + time_lock: 500, + taker_pub: taker_pubkey.to_vec(), + maker_pub: maker_pubkey.to_vec(), + secret_hash: wrong_secret_hash.to_vec(), + wait_until: timeout, + confirmations: 1, + maker_coin: MmCoinEnum::UtxoCoin(maker_coin.clone()), + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { @@ -1708,33 +1662,29 @@ fn test_watcher_validate_taker_payment_utxo() { ), } - let wrong_taker_payment_refund_preimage = taker_coin - .create_taker_payment_refund_preimage( - &taker_payment.tx_hex(), - time_lock, - maker_pubkey, - wrong_secret_hash.as_slice(), - &None, - &[], - ) - .wait() - .unwrap(); + let wrong_taker_payment_refund_preimage = block_on_f01(taker_coin.create_taker_payment_refund_preimage( + &taker_payment.tx_hex(), + time_lock, + maker_pubkey, + wrong_secret_hash.as_slice(), + &None, + &[], + )) + .unwrap(); - let error = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { - payment_tx: taker_payment.tx_hex(), - taker_payment_refund_preimage: wrong_taker_payment_refund_preimage.tx_hex(), - time_lock, - taker_pub: taker_pubkey.to_vec(), - maker_pub: maker_pubkey.to_vec(), - secret_hash: secret_hash.to_vec(), - wait_until: timeout, - confirmations: 1, - maker_coin: MmCoinEnum::UtxoCoin(maker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { + payment_tx: taker_payment.tx_hex(), + taker_payment_refund_preimage: wrong_taker_payment_refund_preimage.tx_hex(), + time_lock, + taker_pub: taker_pubkey.to_vec(), + maker_pub: maker_pubkey.to_vec(), + secret_hash: secret_hash.to_vec(), + wait_until: timeout, + confirmations: 1, + maker_coin: MmCoinEnum::UtxoCoin(maker_coin.clone()), + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { @@ -1777,21 +1727,19 @@ fn test_watcher_validate_taker_payment_eth() { .unwrap(), ); - let taker_payment = taker_coin - .send_taker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: maker_pub, - secret_hash: secret_hash.as_slice(), - amount: taker_amount.clone(), - swap_contract_address: &taker_coin.swap_contract_address(), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: watcher_reward.clone(), - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let taker_payment = block_on_f01(taker_coin.send_taker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: maker_pub, + secret_hash: secret_hash.as_slice(), + amount: taker_amount.clone(), + swap_contract_address: &taker_coin.swap_contract_address(), + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: watcher_reward.clone(), + wait_for_confirmation_until, + })) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_payment.tx_hex(), @@ -1800,10 +1748,10 @@ fn test_watcher_validate_taker_payment_eth() { wait_until: timeout, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let validate_taker_payment_res = taker_coin - .watcher_validate_taker_payment(coins::WatcherValidatePaymentInput { + let validate_taker_payment_res = block_on_f01(taker_coin.watcher_validate_taker_payment( + coins::WatcherValidatePaymentInput { payment_tx: taker_payment.tx_hex(), taker_payment_refund_preimage: Vec::new(), time_lock, @@ -1813,12 +1761,12 @@ fn test_watcher_validate_taker_payment_eth() { wait_until: timeout, confirmations: 1, maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait(); + }, + )); assert!(validate_taker_payment_res.is_ok()); - let error = taker_coin - .watcher_validate_taker_payment(coins::WatcherValidatePaymentInput { + let error = block_on_f01( + taker_coin.watcher_validate_taker_payment(coins::WatcherValidatePaymentInput { payment_tx: taker_payment.tx_hex(), taker_payment_refund_preimage: Vec::new(), time_lock, @@ -1828,10 +1776,10 @@ fn test_watcher_validate_taker_payment_eth() { wait_until: timeout, confirmations: 1, maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + }), + ) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -1843,24 +1791,22 @@ fn test_watcher_validate_taker_payment_eth() { ), } - let taker_payment_wrong_contract = taker_coin - .send_taker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: maker_pub, - secret_hash: secret_hash.as_slice(), - amount: taker_amount.clone(), - swap_contract_address: &Some("9130b257d37a52e52f21054c4da3450c72f595ce".into()), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: watcher_reward.clone(), - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let taker_payment_wrong_contract = block_on_f01(taker_coin.send_taker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: maker_pub, + secret_hash: secret_hash.as_slice(), + amount: taker_amount.clone(), + swap_contract_address: &Some("9130b257d37a52e52f21054c4da3450c72f595ce".into()), + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: watcher_reward.clone(), + wait_for_confirmation_until, + })) + .unwrap(); - let error = taker_coin - .watcher_validate_taker_payment(coins::WatcherValidatePaymentInput { + let error = block_on_f01( + taker_coin.watcher_validate_taker_payment(coins::WatcherValidatePaymentInput { payment_tx: taker_payment_wrong_contract.tx_hex(), taker_payment_refund_preimage: Vec::new(), time_lock, @@ -1870,10 +1816,10 @@ fn test_watcher_validate_taker_payment_eth() { wait_until: timeout, confirmations: 1, maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + }), + ) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -1887,8 +1833,8 @@ fn test_watcher_validate_taker_payment_eth() { // Used to get wrong swap id let wrong_secret_hash = dhash160(&generate_secret().unwrap()); - let error = taker_coin - .watcher_validate_taker_payment(coins::WatcherValidatePaymentInput { + let error = block_on_f01( + taker_coin.watcher_validate_taker_payment(coins::WatcherValidatePaymentInput { payment_tx: taker_payment.tx_hex(), taker_payment_refund_preimage: Vec::new(), time_lock, @@ -1898,10 +1844,10 @@ fn test_watcher_validate_taker_payment_eth() { wait_until: timeout, confirmations: 1, maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + }), + ) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::UnexpectedPaymentState(err) => { @@ -1913,21 +1859,19 @@ fn test_watcher_validate_taker_payment_eth() { ), } - let taker_payment_wrong_secret = taker_coin - .send_taker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: maker_pub, - secret_hash: wrong_secret_hash.as_slice(), - amount: taker_amount, - swap_contract_address: &taker_coin.swap_contract_address(), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward, - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let taker_payment_wrong_secret = block_on_f01(taker_coin.send_taker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: maker_pub, + secret_hash: wrong_secret_hash.as_slice(), + amount: taker_amount, + swap_contract_address: &taker_coin.swap_contract_address(), + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward, + wait_for_confirmation_until, + })) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_payment_wrong_secret.tx_hex(), @@ -1936,23 +1880,21 @@ fn test_watcher_validate_taker_payment_eth() { wait_until: timeout, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let error = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { - payment_tx: taker_payment.tx_hex(), - taker_payment_refund_preimage: Vec::new(), - time_lock, - taker_pub: taker_pub.to_vec(), - maker_pub: maker_pub.to_vec(), - secret_hash: wrong_secret_hash.to_vec(), - wait_until: timeout, - confirmations: 1, - maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { + payment_tx: taker_payment.tx_hex(), + taker_payment_refund_preimage: Vec::new(), + time_lock, + taker_pub: taker_pub.to_vec(), + maker_pub: maker_pub.to_vec(), + secret_hash: wrong_secret_hash.to_vec(), + wait_until: timeout, + confirmations: 1, + maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -1964,21 +1906,19 @@ fn test_watcher_validate_taker_payment_eth() { ), } - let error = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { - payment_tx: taker_payment.tx_hex(), - taker_payment_refund_preimage: Vec::new(), - time_lock, - taker_pub: taker_pub.to_vec(), - maker_pub: taker_pub.to_vec(), - secret_hash: secret_hash.to_vec(), - wait_until: timeout, - confirmations: 1, - maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { + payment_tx: taker_payment.tx_hex(), + taker_payment_refund_preimage: Vec::new(), + time_lock, + taker_pub: taker_pub.to_vec(), + maker_pub: taker_pub.to_vec(), + secret_hash: secret_hash.to_vec(), + wait_until: timeout, + confirmations: 1, + maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -2023,21 +1963,19 @@ fn test_watcher_validate_taker_payment_erc20() { .unwrap(), ); - let taker_payment = taker_coin - .send_taker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: maker_pub, - secret_hash: secret_hash.as_slice(), - amount: taker_amount.clone(), - swap_contract_address: &taker_coin.swap_contract_address(), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: watcher_reward.clone(), - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let taker_payment = block_on_f01(taker_coin.send_taker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: maker_pub, + secret_hash: secret_hash.as_slice(), + amount: taker_amount.clone(), + swap_contract_address: &taker_coin.swap_contract_address(), + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: watcher_reward.clone(), + wait_for_confirmation_until, + })) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_payment.tx_hex(), @@ -2046,10 +1984,10 @@ fn test_watcher_validate_taker_payment_erc20() { wait_until: timeout, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let validate_taker_payment_res = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { + let validate_taker_payment_res = + block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { payment_tx: taker_payment.tx_hex(), taker_payment_refund_preimage: Vec::new(), time_lock, @@ -2059,25 +1997,22 @@ fn test_watcher_validate_taker_payment_erc20() { wait_until: timeout, confirmations: 1, maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait(); + })); assert!(validate_taker_payment_res.is_ok()); - let error = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { - payment_tx: taker_payment.tx_hex(), - taker_payment_refund_preimage: Vec::new(), - time_lock, - taker_pub: maker_pub.to_vec(), - maker_pub: maker_pub.to_vec(), - secret_hash: secret_hash.to_vec(), - wait_until: timeout, - confirmations: 1, - maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { + payment_tx: taker_payment.tx_hex(), + taker_payment_refund_preimage: Vec::new(), + time_lock, + taker_pub: maker_pub.to_vec(), + maker_pub: maker_pub.to_vec(), + secret_hash: secret_hash.to_vec(), + wait_until: timeout, + confirmations: 1, + maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -2089,37 +2024,33 @@ fn test_watcher_validate_taker_payment_erc20() { ), } - let taker_payment_wrong_contract = taker_coin - .send_taker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: maker_pub, - secret_hash: secret_hash.as_slice(), - amount: taker_amount.clone(), - swap_contract_address: &Some("9130b257d37a52e52f21054c4da3450c72f595ce".into()), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: watcher_reward.clone(), - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let taker_payment_wrong_contract = block_on_f01(taker_coin.send_taker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: maker_pub, + secret_hash: secret_hash.as_slice(), + amount: taker_amount.clone(), + swap_contract_address: &Some("9130b257d37a52e52f21054c4da3450c72f595ce".into()), + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: watcher_reward.clone(), + wait_for_confirmation_until, + })) + .unwrap(); - let error = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { - payment_tx: taker_payment_wrong_contract.tx_hex(), - taker_payment_refund_preimage: Vec::new(), - time_lock, - taker_pub: taker_pub.to_vec(), - maker_pub: maker_pub.to_vec(), - secret_hash: secret_hash.to_vec(), - wait_until: timeout, - confirmations: 1, - maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { + payment_tx: taker_payment_wrong_contract.tx_hex(), + taker_payment_refund_preimage: Vec::new(), + time_lock, + taker_pub: taker_pub.to_vec(), + maker_pub: maker_pub.to_vec(), + secret_hash: secret_hash.to_vec(), + wait_until: timeout, + confirmations: 1, + maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -2133,21 +2064,19 @@ fn test_watcher_validate_taker_payment_erc20() { // Used to get wrong swap id let wrong_secret_hash = dhash160(&generate_secret().unwrap()); - let error = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { - payment_tx: taker_payment.tx_hex(), - taker_payment_refund_preimage: Vec::new(), - time_lock, - taker_pub: taker_pub.to_vec(), - maker_pub: maker_pub.to_vec(), - secret_hash: wrong_secret_hash.to_vec(), - wait_until: timeout, - confirmations: 1, - maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { + payment_tx: taker_payment.tx_hex(), + taker_payment_refund_preimage: Vec::new(), + time_lock, + taker_pub: taker_pub.to_vec(), + maker_pub: maker_pub.to_vec(), + secret_hash: wrong_secret_hash.to_vec(), + wait_until: timeout, + confirmations: 1, + maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::UnexpectedPaymentState(err) => { @@ -2159,21 +2088,19 @@ fn test_watcher_validate_taker_payment_erc20() { ), } - let taker_payment_wrong_secret = taker_coin - .send_taker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: maker_pub, - secret_hash: wrong_secret_hash.as_slice(), - amount: taker_amount, - swap_contract_address: &taker_coin.swap_contract_address(), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward, - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let taker_payment_wrong_secret = block_on_f01(taker_coin.send_taker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: maker_pub, + secret_hash: wrong_secret_hash.as_slice(), + amount: taker_amount, + swap_contract_address: &taker_coin.swap_contract_address(), + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward, + wait_for_confirmation_until, + })) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_payment_wrong_secret.tx_hex(), @@ -2182,23 +2109,21 @@ fn test_watcher_validate_taker_payment_erc20() { wait_until: timeout, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let error = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { - payment_tx: taker_payment.tx_hex(), - taker_payment_refund_preimage: Vec::new(), - time_lock, - taker_pub: taker_pub.to_vec(), - maker_pub: maker_pub.to_vec(), - secret_hash: wrong_secret_hash.to_vec(), - wait_until: timeout, - confirmations: 1, - maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { + payment_tx: taker_payment.tx_hex(), + taker_payment_refund_preimage: Vec::new(), + time_lock, + taker_pub: taker_pub.to_vec(), + maker_pub: maker_pub.to_vec(), + secret_hash: wrong_secret_hash.to_vec(), + wait_until: timeout, + confirmations: 1, + maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -2210,21 +2135,19 @@ fn test_watcher_validate_taker_payment_erc20() { ), } - let error = taker_coin - .watcher_validate_taker_payment(WatcherValidatePaymentInput { - payment_tx: taker_payment.tx_hex(), - taker_payment_refund_preimage: Vec::new(), - time_lock, - taker_pub: taker_pub.to_vec(), - maker_pub: taker_pub.to_vec(), - secret_hash: secret_hash.to_vec(), - wait_until: timeout, - confirmations: 1, - maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), - }) - .wait() - .unwrap_err() - .into_inner(); + let error = block_on_f01(taker_coin.watcher_validate_taker_payment(WatcherValidatePaymentInput { + payment_tx: taker_payment.tx_hex(), + taker_payment_refund_preimage: Vec::new(), + time_lock, + taker_pub: taker_pub.to_vec(), + maker_pub: taker_pub.to_vec(), + secret_hash: secret_hash.to_vec(), + wait_until: timeout, + confirmations: 1, + maker_coin: MmCoinEnum::EthCoin(taker_coin.clone()), + })) + .unwrap_err() + .into_inner(); log!("error: {:?}", error); match error { ValidatePaymentError::WrongPaymentTx(err) => { @@ -2250,21 +2173,19 @@ fn test_taker_validates_taker_payment_refund_utxo() { let secret_hash = dhash160(&generate_secret().unwrap()); - let taker_payment = taker_coin - .send_taker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: maker_pubkey, - secret_hash: secret_hash.as_slice(), - amount: BigDecimal::from(10), - swap_contract_address: &None, - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: None, - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let taker_payment = block_on_f01(taker_coin.send_taker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: maker_pubkey, + secret_hash: secret_hash.as_slice(), + amount: BigDecimal::from(10), + swap_contract_address: &None, + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: None, + wait_for_confirmation_until, + })) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_payment.tx_hex(), @@ -2273,34 +2194,30 @@ fn test_taker_validates_taker_payment_refund_utxo() { wait_until: timeout, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let taker_payment_refund_preimage = taker_coin - .create_taker_payment_refund_preimage( - &taker_payment.tx_hex(), - time_lock, - maker_pubkey, - secret_hash.as_slice(), - &None, - &[], - ) - .wait() - .unwrap(); + let taker_payment_refund_preimage = block_on_f01(taker_coin.create_taker_payment_refund_preimage( + &taker_payment.tx_hex(), + time_lock, + maker_pubkey, + secret_hash.as_slice(), + &None, + &[], + )) + .unwrap(); - let taker_payment_refund = taker_coin - .send_taker_payment_refund_preimage(RefundPaymentArgs { - payment_tx: &taker_payment_refund_preimage.tx_hex(), - other_pubkey: maker_pubkey, - tx_type_with_secret_hash: SwapTxTypeWithSecretHash::TakerOrMakerPayment { - maker_secret_hash: secret_hash.as_slice(), - }, - time_lock, - swap_contract_address: &None, - swap_unique_data: &[], - watcher_reward: false, - }) - .wait() - .unwrap(); + let taker_payment_refund = block_on_f01(taker_coin.send_taker_payment_refund_preimage(RefundPaymentArgs { + payment_tx: &taker_payment_refund_preimage.tx_hex(), + other_pubkey: maker_pubkey, + tx_type_with_secret_hash: SwapTxTypeWithSecretHash::TakerOrMakerPayment { + maker_secret_hash: secret_hash.as_slice(), + }, + time_lock, + swap_contract_address: &None, + swap_unique_data: &[], + watcher_reward: false, + })) + .unwrap(); let validate_input = ValidateWatcherSpendInput { payment_tx: taker_payment_refund.tx_hex(), @@ -2313,9 +2230,7 @@ fn test_taker_validates_taker_payment_refund_utxo() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let validate_watcher_refund = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait(); + let validate_watcher_refund = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)); assert!(validate_watcher_refund.is_ok()); } @@ -2347,21 +2262,19 @@ fn test_taker_validates_taker_payment_refund_eth() { )) .unwrap(); - let taker_payment = taker_coin - .send_taker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: maker_pub, - secret_hash: secret_hash.as_slice(), - amount: taker_amount.clone(), - swap_contract_address: &taker_coin.swap_contract_address(), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: Some(watcher_reward.clone()), - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let taker_payment = block_on_f01(taker_coin.send_taker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: maker_pub, + secret_hash: secret_hash.as_slice(), + amount: taker_amount.clone(), + swap_contract_address: &taker_coin.swap_contract_address(), + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: Some(watcher_reward.clone()), + wait_for_confirmation_until, + })) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_payment.tx_hex(), @@ -2370,19 +2283,17 @@ fn test_taker_validates_taker_payment_refund_eth() { wait_until: timeout, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let taker_payment_refund_preimage = taker_coin - .create_taker_payment_refund_preimage( - &taker_payment.tx_hex(), - time_lock, - taker_pub, - secret_hash.as_slice(), - &taker_coin.swap_contract_address(), - &[], - ) - .wait() - .unwrap(); + let taker_payment_refund_preimage = block_on_f01(taker_coin.create_taker_payment_refund_preimage( + &taker_payment.tx_hex(), + time_lock, + taker_pub, + secret_hash.as_slice(), + &taker_coin.swap_contract_address(), + &[], + )) + .unwrap(); let validate_input = ValidateWatcherSpendInput { payment_tx: taker_payment_refund_preimage.tx_hex(), @@ -2395,9 +2306,7 @@ fn test_taker_validates_taker_payment_refund_eth() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -2411,20 +2320,18 @@ fn test_taker_validates_taker_payment_refund_eth() { ), } - let taker_payment_refund = taker_coin - .send_taker_payment_refund_preimage(RefundPaymentArgs { - payment_tx: &taker_payment_refund_preimage.tx_hex(), - other_pubkey: taker_pub, - tx_type_with_secret_hash: SwapTxTypeWithSecretHash::TakerOrMakerPayment { - maker_secret_hash: secret_hash.as_slice(), - }, - time_lock, - swap_contract_address: &taker_coin.swap_contract_address(), - swap_unique_data: &[], - watcher_reward: true, - }) - .wait() - .unwrap(); + let taker_payment_refund = block_on_f01(taker_coin.send_taker_payment_refund_preimage(RefundPaymentArgs { + payment_tx: &taker_payment_refund_preimage.tx_hex(), + other_pubkey: taker_pub, + tx_type_with_secret_hash: SwapTxTypeWithSecretHash::TakerOrMakerPayment { + maker_secret_hash: secret_hash.as_slice(), + }, + time_lock, + swap_contract_address: &taker_coin.swap_contract_address(), + swap_unique_data: &[], + watcher_reward: true, + })) + .unwrap(); let validate_input = ValidateWatcherSpendInput { payment_tx: taker_payment_refund.tx_hex(), @@ -2437,9 +2344,7 @@ fn test_taker_validates_taker_payment_refund_eth() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let validate_watcher_refund = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait(); + let validate_watcher_refund = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)); assert!(validate_watcher_refund.is_ok()); let validate_input = ValidateWatcherSpendInput { @@ -2452,9 +2357,7 @@ fn test_taker_validates_taker_payment_refund_eth() { watcher_reward: Some(watcher_reward.clone()), spend_type: WatcherSpendType::TakerPaymentRefund, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -2479,9 +2382,7 @@ fn test_taker_validates_taker_payment_refund_eth() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let error = maker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(maker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -2506,9 +2407,7 @@ fn test_taker_validates_taker_payment_refund_eth() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -2536,9 +2435,7 @@ fn test_taker_validates_taker_payment_refund_eth() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -2566,9 +2463,7 @@ fn test_taker_validates_taker_payment_refund_eth() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -2596,9 +2491,7 @@ fn test_taker_validates_taker_payment_refund_eth() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -2623,9 +2516,7 @@ fn test_taker_validates_taker_payment_refund_eth() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -2672,21 +2563,19 @@ fn test_taker_validates_taker_payment_refund_erc20() { .unwrap(), ); - let taker_payment = taker_coin - .send_taker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: maker_pub, - secret_hash: secret_hash.as_slice(), - amount: taker_amount.clone(), - swap_contract_address: &taker_coin.swap_contract_address(), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: watcher_reward.clone(), - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let taker_payment = block_on_f01(taker_coin.send_taker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: maker_pub, + secret_hash: secret_hash.as_slice(), + amount: taker_amount.clone(), + swap_contract_address: &taker_coin.swap_contract_address(), + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: watcher_reward.clone(), + wait_for_confirmation_until, + })) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: taker_payment.tx_hex(), @@ -2695,34 +2584,30 @@ fn test_taker_validates_taker_payment_refund_erc20() { wait_until: timeout, check_every: 1, }; - taker_coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(taker_coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let taker_payment_refund_preimage = taker_coin - .create_taker_payment_refund_preimage( - &taker_payment.tx_hex(), - time_lock, - taker_pub, - secret_hash.as_slice(), - &taker_coin.swap_contract_address(), - &[], - ) - .wait() - .unwrap(); + let taker_payment_refund_preimage = block_on_f01(taker_coin.create_taker_payment_refund_preimage( + &taker_payment.tx_hex(), + time_lock, + taker_pub, + secret_hash.as_slice(), + &taker_coin.swap_contract_address(), + &[], + )) + .unwrap(); - let taker_payment_refund = taker_coin - .send_taker_payment_refund_preimage(RefundPaymentArgs { - payment_tx: &taker_payment_refund_preimage.tx_hex(), - other_pubkey: taker_pub, - tx_type_with_secret_hash: SwapTxTypeWithSecretHash::TakerOrMakerPayment { - maker_secret_hash: secret_hash.as_slice(), - }, - time_lock, - swap_contract_address: &taker_coin.swap_contract_address(), - swap_unique_data: &[], - watcher_reward: true, - }) - .wait() - .unwrap(); + let taker_payment_refund = block_on_f01(taker_coin.send_taker_payment_refund_preimage(RefundPaymentArgs { + payment_tx: &taker_payment_refund_preimage.tx_hex(), + other_pubkey: taker_pub, + tx_type_with_secret_hash: SwapTxTypeWithSecretHash::TakerOrMakerPayment { + maker_secret_hash: secret_hash.as_slice(), + }, + time_lock, + swap_contract_address: &taker_coin.swap_contract_address(), + swap_unique_data: &[], + watcher_reward: true, + })) + .unwrap(); let validate_input = ValidateWatcherSpendInput { payment_tx: taker_payment_refund.tx_hex(), @@ -2735,9 +2620,7 @@ fn test_taker_validates_taker_payment_refund_erc20() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let validate_watcher_refund = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait(); + let validate_watcher_refund = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)); assert!(validate_watcher_refund.is_ok()); let validate_input = ValidateWatcherSpendInput { @@ -2751,9 +2634,7 @@ fn test_taker_validates_taker_payment_refund_erc20() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -2783,54 +2664,48 @@ fn test_taker_validates_maker_payment_spend_utxo() { let secret = generate_secret().unwrap(); let secret_hash = dhash160(&secret); - let maker_payment = maker_coin - .send_maker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: taker_pubkey, - secret_hash: secret_hash.as_slice(), - amount: BigDecimal::from(10), - swap_contract_address: &None, - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: None, - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let maker_payment = block_on_f01(maker_coin.send_maker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: taker_pubkey, + secret_hash: secret_hash.as_slice(), + amount: BigDecimal::from(10), + swap_contract_address: &None, + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: None, + wait_for_confirmation_until, + })) + .unwrap(); - maker_coin - .wait_for_confirmations(ConfirmPaymentInput { - payment_tx: maker_payment.tx_hex(), - confirmations: 1, - requires_nota: false, - wait_until: timeout, - check_every: 1, - }) - .wait() - .unwrap(); + block_on_f01(maker_coin.wait_for_confirmations(ConfirmPaymentInput { + payment_tx: maker_payment.tx_hex(), + confirmations: 1, + requires_nota: false, + wait_until: timeout, + check_every: 1, + })) + .unwrap(); - let maker_payment_spend_preimage = taker_coin - .create_maker_payment_spend_preimage( - &maker_payment.tx_hex(), - time_lock, - maker_pubkey, - secret_hash.as_slice(), - &[], - ) - .wait() - .unwrap(); + let maker_payment_spend_preimage = block_on_f01(taker_coin.create_maker_payment_spend_preimage( + &maker_payment.tx_hex(), + time_lock, + maker_pubkey, + secret_hash.as_slice(), + &[], + )) + .unwrap(); - let maker_payment_spend = taker_coin - .send_maker_payment_spend_preimage(SendMakerPaymentSpendPreimageInput { + let maker_payment_spend = block_on_f01(taker_coin.send_maker_payment_spend_preimage( + SendMakerPaymentSpendPreimageInput { preimage: &maker_payment_spend_preimage.tx_hex(), secret_hash: secret_hash.as_slice(), secret: secret.as_slice(), taker_pub: taker_pubkey, watcher_reward: false, - }) - .wait() - .unwrap(); + }, + )) + .unwrap(); let validate_input = ValidateWatcherSpendInput { payment_tx: maker_payment_spend.tx_hex(), @@ -2843,9 +2718,7 @@ fn test_taker_validates_maker_payment_spend_utxo() { spend_type: WatcherSpendType::TakerPaymentRefund, }; - let validate_watcher_spend = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait(); + let validate_watcher_spend = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)); assert!(validate_watcher_spend.is_ok()); } @@ -2877,43 +2750,37 @@ fn test_taker_validates_maker_payment_spend_eth() { .unwrap() .unwrap(); - let maker_payment = maker_coin - .send_maker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: taker_pub, - secret_hash: secret_hash.as_slice(), - amount: maker_amount.clone(), - swap_contract_address: &maker_coin.swap_contract_address(), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: Some(watcher_reward.clone()), - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let maker_payment = block_on_f01(maker_coin.send_maker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: taker_pub, + secret_hash: secret_hash.as_slice(), + amount: maker_amount.clone(), + swap_contract_address: &maker_coin.swap_contract_address(), + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: Some(watcher_reward.clone()), + wait_for_confirmation_until, + })) + .unwrap(); - maker_coin - .wait_for_confirmations(ConfirmPaymentInput { - payment_tx: maker_payment.tx_hex(), - confirmations: 1, - requires_nota: false, - wait_until: timeout, - check_every: 1, - }) - .wait() - .unwrap(); + block_on_f01(maker_coin.wait_for_confirmations(ConfirmPaymentInput { + payment_tx: maker_payment.tx_hex(), + confirmations: 1, + requires_nota: false, + wait_until: timeout, + check_every: 1, + })) + .unwrap(); - let maker_payment_spend_preimage = taker_coin - .create_maker_payment_spend_preimage( - &maker_payment.tx_hex(), - time_lock, - maker_pub, - secret_hash.as_slice(), - &[], - ) - .wait() - .unwrap(); + let maker_payment_spend_preimage = block_on_f01(taker_coin.create_maker_payment_spend_preimage( + &maker_payment.tx_hex(), + time_lock, + maker_pub, + secret_hash.as_slice(), + &[], + )) + .unwrap(); let validate_input = ValidateWatcherSpendInput { payment_tx: maker_payment_spend_preimage.tx_hex(), @@ -2926,9 +2793,7 @@ fn test_taker_validates_maker_payment_spend_eth() { spend_type: WatcherSpendType::MakerPaymentSpend, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -2942,27 +2807,25 @@ fn test_taker_validates_maker_payment_spend_eth() { ), } - let maker_payment_spend = taker_coin - .send_maker_payment_spend_preimage(SendMakerPaymentSpendPreimageInput { + let maker_payment_spend = block_on_f01(taker_coin.send_maker_payment_spend_preimage( + SendMakerPaymentSpendPreimageInput { preimage: &maker_payment_spend_preimage.tx_hex(), secret_hash: secret_hash.as_slice(), secret: secret.as_slice(), taker_pub, watcher_reward: true, - }) - .wait() - .unwrap(); + }, + )) + .unwrap(); - maker_coin - .wait_for_confirmations(ConfirmPaymentInput { - payment_tx: maker_payment_spend.tx_hex(), - confirmations: 1, - requires_nota: false, - wait_until: timeout, - check_every: 1, - }) - .wait() - .unwrap(); + block_on_f01(maker_coin.wait_for_confirmations(ConfirmPaymentInput { + payment_tx: maker_payment_spend.tx_hex(), + confirmations: 1, + requires_nota: false, + wait_until: timeout, + check_every: 1, + })) + .unwrap(); let validate_input = ValidateWatcherSpendInput { payment_tx: maker_payment_spend.tx_hex(), @@ -2975,10 +2838,7 @@ fn test_taker_validates_maker_payment_spend_eth() { spend_type: WatcherSpendType::MakerPaymentSpend, }; - taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() - .unwrap(); + block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)).unwrap(); let validate_input = ValidateWatcherSpendInput { payment_tx: maker_payment_spend.tx_hex(), @@ -2991,9 +2851,7 @@ fn test_taker_validates_maker_payment_spend_eth() { spend_type: WatcherSpendType::MakerPaymentSpend, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -3018,9 +2876,7 @@ fn test_taker_validates_maker_payment_spend_eth() { spend_type: WatcherSpendType::MakerPaymentSpend, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -3045,9 +2901,7 @@ fn test_taker_validates_maker_payment_spend_eth() { spend_type: WatcherSpendType::MakerPaymentSpend, }; - let error = maker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(maker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -3075,9 +2929,7 @@ fn test_taker_validates_maker_payment_spend_eth() { spend_type: WatcherSpendType::MakerPaymentSpend, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -3105,9 +2957,7 @@ fn test_taker_validates_maker_payment_spend_eth() { spend_type: WatcherSpendType::MakerPaymentSpend, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -3135,9 +2985,7 @@ fn test_taker_validates_maker_payment_spend_eth() { spend_type: WatcherSpendType::MakerPaymentSpend, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -3162,9 +3010,7 @@ fn test_taker_validates_maker_payment_spend_eth() { spend_type: WatcherSpendType::MakerPaymentSpend, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -3206,65 +3052,57 @@ fn test_taker_validates_maker_payment_spend_erc20() { )) .unwrap(); - let maker_payment = maker_coin - .send_maker_payment(SendPaymentArgs { - time_lock_duration, - time_lock, - other_pubkey: taker_pub, - secret_hash: secret_hash.as_slice(), - amount: maker_amount.clone(), - swap_contract_address: &maker_coin.swap_contract_address(), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: watcher_reward.clone(), - wait_for_confirmation_until, - }) - .wait() - .unwrap(); + let maker_payment = block_on_f01(maker_coin.send_maker_payment(SendPaymentArgs { + time_lock_duration, + time_lock, + other_pubkey: taker_pub, + secret_hash: secret_hash.as_slice(), + amount: maker_amount.clone(), + swap_contract_address: &maker_coin.swap_contract_address(), + swap_unique_data: &[], + payment_instructions: &None, + watcher_reward: watcher_reward.clone(), + wait_for_confirmation_until, + })) + .unwrap(); - maker_coin - .wait_for_confirmations(ConfirmPaymentInput { - payment_tx: maker_payment.tx_hex(), - confirmations: 1, - requires_nota: false, - wait_until: timeout, - check_every: 1, - }) - .wait() - .unwrap(); + block_on_f01(maker_coin.wait_for_confirmations(ConfirmPaymentInput { + payment_tx: maker_payment.tx_hex(), + confirmations: 1, + requires_nota: false, + wait_until: timeout, + check_every: 1, + })) + .unwrap(); - let maker_payment_spend_preimage = taker_coin - .create_maker_payment_spend_preimage( - &maker_payment.tx_hex(), - time_lock, - maker_pub, - secret_hash.as_slice(), - &[], - ) - .wait() - .unwrap(); + let maker_payment_spend_preimage = block_on_f01(taker_coin.create_maker_payment_spend_preimage( + &maker_payment.tx_hex(), + time_lock, + maker_pub, + secret_hash.as_slice(), + &[], + )) + .unwrap(); - let maker_payment_spend = taker_coin - .send_maker_payment_spend_preimage(SendMakerPaymentSpendPreimageInput { + let maker_payment_spend = block_on_f01(taker_coin.send_maker_payment_spend_preimage( + SendMakerPaymentSpendPreimageInput { preimage: &maker_payment_spend_preimage.tx_hex(), secret_hash: secret_hash.as_slice(), secret: secret.as_slice(), taker_pub, watcher_reward: true, - }) - .wait() - .unwrap(); + }, + )) + .unwrap(); - maker_coin - .wait_for_confirmations(ConfirmPaymentInput { - payment_tx: maker_payment_spend.tx_hex(), - confirmations: 1, - requires_nota: false, - wait_until: timeout, - check_every: 1, - }) - .wait() - .unwrap(); + block_on_f01(maker_coin.wait_for_confirmations(ConfirmPaymentInput { + payment_tx: maker_payment_spend.tx_hex(), + confirmations: 1, + requires_nota: false, + wait_until: timeout, + check_every: 1, + })) + .unwrap(); let validate_input = ValidateWatcherSpendInput { payment_tx: maker_payment_spend.tx_hex(), @@ -3277,10 +3115,7 @@ fn test_taker_validates_maker_payment_spend_erc20() { spend_type: WatcherSpendType::MakerPaymentSpend, }; - taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() - .unwrap(); + block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)).unwrap(); let validate_input = ValidateWatcherSpendInput { payment_tx: maker_payment_spend.tx_hex(), @@ -3293,9 +3128,7 @@ fn test_taker_validates_maker_payment_spend_erc20() { spend_type: WatcherSpendType::MakerPaymentSpend, }; - let error = taker_coin - .taker_validates_payment_spend_or_refund(validate_input) - .wait() + let error = block_on_f01(taker_coin.taker_validates_payment_spend_or_refund(validate_input)) .unwrap_err() .into_inner(); log!("error: {:?}", error); @@ -3329,7 +3162,7 @@ fn test_send_taker_payment_refund_preimage_utxo() { watcher_reward: None, wait_for_confirmation_until: 0, }; - let tx = coin.send_taker_payment(taker_payment_args).wait().unwrap(); + let tx = block_on_f01(coin.send_taker_payment(taker_payment_args)).unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: tx.tx_hex(), @@ -3338,27 +3171,30 @@ fn test_send_taker_payment_refund_preimage_utxo() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); - let refund_tx = coin - .create_taker_payment_refund_preimage(&tx.tx_hex(), time_lock, my_public_key, &[0; 20], &None, &[]) - .wait() - .unwrap(); + let refund_tx = block_on_f01(coin.create_taker_payment_refund_preimage( + &tx.tx_hex(), + time_lock, + my_public_key, + &[0; 20], + &None, + &[], + )) + .unwrap(); - let refund_tx = coin - .send_taker_payment_refund_preimage(RefundPaymentArgs { - payment_tx: &refund_tx.tx_hex(), - swap_contract_address: &None, - tx_type_with_secret_hash: SwapTxTypeWithSecretHash::TakerOrMakerPayment { - maker_secret_hash: &[0; 20], - }, - other_pubkey: my_public_key, - time_lock, - swap_unique_data: &[], - watcher_reward: false, - }) - .wait() - .unwrap(); + let refund_tx = block_on_f01(coin.send_taker_payment_refund_preimage(RefundPaymentArgs { + payment_tx: &refund_tx.tx_hex(), + swap_contract_address: &None, + tx_type_with_secret_hash: SwapTxTypeWithSecretHash::TakerOrMakerPayment { + maker_secret_hash: &[0; 20], + }, + other_pubkey: my_public_key, + time_lock, + swap_unique_data: &[], + watcher_reward: false, + })) + .unwrap(); let confirm_payment_input = ConfirmPaymentInput { payment_tx: refund_tx.tx_hex(), @@ -3367,7 +3203,7 @@ fn test_send_taker_payment_refund_preimage_utxo() { wait_until: timeout, check_every: 1, }; - coin.wait_for_confirmations(confirm_payment_input).wait().unwrap(); + block_on_f01(coin.wait_for_confirmations(confirm_payment_input)).unwrap(); let search_input = SearchForSwapTxSpendInput { time_lock, From ab23c11955a15e1bcc1347a660d514e8e856dfe9 Mon Sep 17 00:00:00 2001 From: shamardy <39480341+shamardy@users.noreply.github.com> Date: Wed, 25 Sep 2024 20:05:13 +0300 Subject: [PATCH 18/29] feat(wallets): add `get_wallet_names` rpc (#2202) This commit introduces the `get_wallet_names` RPC method, which allows clients to retrieve information about all wallet names and the currently active one. --- mm2src/mm2_core/src/lib.rs | 9 +- mm2src/mm2_core/src/mm_ctx.rs | 9 ++ mm2src/mm2_io/src/fs.rs | 59 +++++++++++-- mm2src/mm2_main/src/lp_wallet.rs | 57 +++++++++++- .../src/lp_wallet/mnemonics_storage.rs | 9 +- .../src/lp_wallet/mnemonics_wasm_db.rs | 13 +++ .../mm2_main/src/rpc/dispatcher/dispatcher.rs | 3 +- mm2src/mm2_main/src/wasm_tests.rs | 70 +++++++++++---- .../tests/mm2_tests/mm2_tests_inner.rs | 80 +++++++++++++---- mm2src/mm2_test_helpers/src/for_tests.rs | 86 +++++++++++++++++-- mm2src/mm2_test_helpers/src/structs.rs | 7 ++ 11 files changed, 343 insertions(+), 59 deletions(-) diff --git a/mm2src/mm2_core/src/lib.rs b/mm2src/mm2_core/src/lib.rs index 62e4bef09c..88b0fd5c81 100644 --- a/mm2src/mm2_core/src/lib.rs +++ b/mm2src/mm2_core/src/lib.rs @@ -1,10 +1,11 @@ -use derive_more::Display; -use rand::{thread_rng, Rng}; +#[cfg(target_arch = "wasm32")] use derive_more::Display; +#[cfg(target_arch = "wasm32")] use rand::{thread_rng, Rng}; pub mod data_asker; pub mod event_dispatcher; pub mod mm_ctx; +#[cfg(target_arch = "wasm32")] #[derive(Clone, Copy, Display, PartialEq, Default)] pub enum DbNamespaceId { #[display(fmt = "MAIN")] @@ -14,9 +15,13 @@ pub enum DbNamespaceId { Test(u64), } +#[cfg(target_arch = "wasm32")] impl DbNamespaceId { pub fn for_test() -> DbNamespaceId { let mut rng = thread_rng(); DbNamespaceId::Test(rng.gen()) } + + #[inline(always)] + pub fn for_test_with_id(id: u64) -> DbNamespaceId { DbNamespaceId::Test(id) } } diff --git a/mm2src/mm2_core/src/mm_ctx.rs b/mm2src/mm2_core/src/mm_ctx.rs index 0be8e66734..54d5f71004 100644 --- a/mm2src/mm2_core/src/mm_ctx.rs +++ b/mm2src/mm2_core/src/mm_ctx.rs @@ -291,6 +291,9 @@ impl MmCtx { #[cfg(not(target_arch = "wasm32"))] pub fn db_root(&self) -> PathBuf { path_to_db_root(self.conf["dbdir"].as_str()) } + #[cfg(not(target_arch = "wasm32"))] + pub fn db_root(&self) -> PathBuf { path_to_db_root(self.conf["dbdir"].as_str()) } + #[cfg(not(target_arch = "wasm32"))] pub fn wallet_file_path(&self, wallet_name: &str) -> PathBuf { self.db_root().join(wallet_name.to_string() + ".dat") @@ -753,6 +756,12 @@ impl MmCtxBuilder { self } + #[cfg(target_arch = "wasm32")] + pub fn with_test_db_namespace_with_id(mut self, id: u64) -> Self { + self.db_namespace = DbNamespaceId::for_test_with_id(id); + self + } + pub fn into_mm_arc(self) -> MmArc { // NB: We avoid recreating LogState // in order not to interfere with the integration tests checking LogState drop on shutdown. diff --git a/mm2src/mm2_io/src/fs.rs b/mm2src/mm2_io/src/fs.rs index 94a323c0b8..489b3bad8b 100644 --- a/mm2src/mm2_io/src/fs.rs +++ b/mm2src/mm2_io/src/fs.rs @@ -192,19 +192,52 @@ where json::from_slice(&content).map_to_mm(FsJsonError::Deserializing) } -/// Read the `dir_path` entries trying to deserialize each as the `T` type. +async fn filter_files_by_extension(dir_path: &Path, extension: &str) -> IoResult> { + let ext = Some(OsStr::new(extension).to_ascii_lowercase()); + let entries = read_dir_async(dir_path) + .await? + .into_iter() + .filter(|path| path.extension().map(|ext| ext.to_ascii_lowercase()) == ext) + .collect(); + Ok(entries) +} + +/// Helper function to extract file names or stems based on the provided extraction function. +fn extract_file_identifiers<'a, F>(entries: Vec, extractor: F) -> impl Iterator + 'a +where + F: Fn(&Path) -> Option<&OsStr> + 'a, +{ + entries + .into_iter() + .filter_map(move |path| extractor(&path).and_then(OsStr::to_str).map(ToOwned::to_owned)) +} + +/// Lists files by the specified extension from the given directory path. +/// If include_extension is true, returns full file names; otherwise, returns file stems. +pub async fn list_files_by_extension( + dir_path: &Path, + extension: &str, + include_extension: bool, +) -> IoResult> { + let entries = filter_files_by_extension(dir_path, extension).await?; + let extractor = if include_extension { + Path::file_name + } else { + Path::file_stem + }; + Ok(extract_file_identifiers(entries, extractor)) +} + +/// Read the `dir_path` entries trying to deserialize each as the `T` type, +/// filtering by the specified extension. /// Please note that files that couldn't be deserialized are skipped. -pub async fn read_dir_json(dir_path: &Path) -> FsJsonResult> +pub async fn read_files_with_extension(dir_path: &Path, extension: &str) -> FsJsonResult> where T: DeserializeOwned, { - let json_ext = Some(OsStr::new("json")); - let entries: Vec<_> = read_dir_async(dir_path) + let entries = filter_files_by_extension(dir_path, extension) .await - .mm_err(FsJsonError::IoReading)? - .into_iter() - .filter(|path| path.extension() == json_ext) - .collect(); + .mm_err(FsJsonError::IoReading)?; let type_name = std::any::type_name::(); let mut result = Vec::new(); @@ -233,6 +266,16 @@ where Ok(result) } +/// Read the `dir_path` entries trying to deserialize each as the `T` type from JSON files. +/// Please note that files that couldn't be deserialized are skipped. +#[inline(always)] +pub async fn read_dir_json(dir_path: &Path) -> FsJsonResult> +where + T: DeserializeOwned, +{ + read_files_with_extension(dir_path, "json").await +} + pub async fn write_json(t: &T, path: &Path, use_tmp_file: bool) -> FsJsonResult<()> where T: Serialize, diff --git a/mm2src/mm2_main/src/lp_wallet.rs b/mm2src/mm2_main/src/lp_wallet.rs index 19ac357cab..559821a26a 100644 --- a/mm2src/mm2_main/src/lp_wallet.rs +++ b/mm2src/mm2_main/src/lp_wallet.rs @@ -2,23 +2,24 @@ use common::HttpStatusCode; use crypto::{decrypt_mnemonic, encrypt_mnemonic, generate_mnemonic, CryptoCtx, CryptoInitError, EncryptedData, MnemonicError}; use http::StatusCode; +use itertools::Itertools; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use serde::de::DeserializeOwned; -use serde_json::{self as json}; +use serde_json::{self as json, Value as Json}; cfg_wasm32! { use crate::lp_wallet::mnemonics_wasm_db::{WalletsDb, WalletsDBError}; use mm2_core::mm_ctx::from_ctx; use mm2_db::indexed_db::{ConstructibleDb, DbLocked, InitDbResult}; - use mnemonics_wasm_db::{read_encrypted_passphrase_if_available, save_encrypted_passphrase}; + use mnemonics_wasm_db::{read_all_wallet_names, read_encrypted_passphrase_if_available, save_encrypted_passphrase}; use std::sync::Arc; type WalletsDbLocked<'a> = DbLocked<'a, WalletsDb>; } cfg_native! { - use mnemonics_storage::{read_encrypted_passphrase_if_available, save_encrypted_passphrase, WalletsStorageError}; + use mnemonics_storage::{read_all_wallet_names, read_encrypted_passphrase_if_available, save_encrypted_passphrase, WalletsStorageError}; } #[cfg(not(target_arch = "wasm32"))] mod mnemonics_storage; @@ -499,3 +500,53 @@ pub async fn get_mnemonic_rpc(ctx: MmArc, req: GetMnemonicRequest) -> MmResult, + activated_wallet: Option, +} + +#[derive(Debug, Display, Serialize, SerializeErrorType)] +#[serde(tag = "error_type", content = "error_data")] +pub enum GetWalletsError { + #[display(fmt = "Wallets storage error: {}", _0)] + WalletsStorageError(String), + #[display(fmt = "Internal error: {}", _0)] + Internal(String), +} + +impl HttpStatusCode for GetWalletsError { + fn status_code(&self) -> StatusCode { + match self { + GetWalletsError::WalletsStorageError(_) | GetWalletsError::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR, + } + } +} + +#[cfg(not(target_arch = "wasm32"))] +impl From for GetWalletsError { + fn from(e: WalletsStorageError) -> Self { GetWalletsError::WalletsStorageError(e.to_string()) } +} + +#[cfg(target_arch = "wasm32")] +impl From for GetWalletsError { + fn from(e: WalletsDBError) -> Self { GetWalletsError::WalletsStorageError(e.to_string()) } +} + +/// Retrieves all created wallets and the currently activated wallet. +pub async fn get_wallet_names_rpc(ctx: MmArc, _req: Json) -> MmResult { + // We want to return wallet names in the same order for both native and wasm32 targets. + let wallets = read_all_wallet_names(&ctx).await?.sorted().collect(); + // Note: `ok_or` is used here on `Constructible>` to handle the case where the wallet name is not set. + // `wallet_name` can be `None` in the case of no-login mode. + let activated_wallet = ctx.wallet_name.ok_or(GetWalletsError::Internal( + "`wallet_name` not initialized yet!".to_string(), + ))?; + + Ok(GetWalletNamesResponse { + wallet_names: wallets, + activated_wallet: activated_wallet.clone(), + }) +} diff --git a/mm2src/mm2_main/src/lp_wallet/mnemonics_storage.rs b/mm2src/mm2_main/src/lp_wallet/mnemonics_storage.rs index 3cf40e61fb..c873b2d5ff 100644 --- a/mm2src/mm2_main/src/lp_wallet/mnemonics_storage.rs +++ b/mm2src/mm2_main/src/lp_wallet/mnemonics_storage.rs @@ -1,7 +1,7 @@ use crypto::EncryptedData; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; -use mm2_io::fs::ensure_file_is_writable; +use mm2_io::fs::{ensure_file_is_writable, list_files_by_extension}; type WalletsStorageResult = Result>; @@ -61,3 +61,10 @@ pub(super) async fn read_encrypted_passphrase_if_available(ctx: &MmArc) -> Walle )) }) } + +pub(super) async fn read_all_wallet_names(ctx: &MmArc) -> WalletsStorageResult> { + let wallet_names = list_files_by_extension(&ctx.db_root(), "dat", false) + .await + .mm_err(|e| WalletsStorageError::FsReadError(format!("Error reading wallets directory: {}", e)))?; + Ok(wallet_names) +} diff --git a/mm2src/mm2_main/src/lp_wallet/mnemonics_wasm_db.rs b/mm2src/mm2_main/src/lp_wallet/mnemonics_wasm_db.rs index 40daa62029..a815bfcca1 100644 --- a/mm2src/mm2_main/src/lp_wallet/mnemonics_wasm_db.rs +++ b/mm2src/mm2_main/src/lp_wallet/mnemonics_wasm_db.rs @@ -144,3 +144,16 @@ pub(super) async fn read_encrypted_passphrase_if_available(ctx: &MmArc) -> Walle }) .transpose() } + +pub(super) async fn read_all_wallet_names(ctx: &MmArc) -> WalletsDBResult> { + let wallets_ctx = WalletsContext::from_ctx(ctx).map_to_mm(WalletsDBError::Internal)?; + + let db = wallets_ctx.wallets_db().await?; + let transaction = db.transaction().await?; + let table = transaction.table::().await?; + + let all_items = table.get_all_items().await?; + let wallet_names = all_items.into_iter().map(|(_, item)| item.wallet_name); + + Ok(wallet_names) +} diff --git a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs index b9066bf540..fec08d106f 100644 --- a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs +++ b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs @@ -5,7 +5,7 @@ use crate::lp_native_dex::init_metamask::{cancel_connect_metamask, connect_metam use crate::lp_ordermatch::{best_orders_rpc_v2, orderbook_rpc_v2, start_simple_market_maker_bot, stop_simple_market_maker_bot}; use crate::lp_swap::swap_v2_rpcs::{active_swaps_rpc, my_recent_swaps_rpc, my_swap_status_rpc}; -use crate::lp_wallet::get_mnemonic_rpc; +use crate::lp_wallet::{get_mnemonic_rpc, get_wallet_names_rpc}; use crate::rpc::rate_limiter::{process_rate_limit, RateLimitContext}; use crate::{lp_stats::{add_node_to_version_stat, remove_node_from_version_stat, start_version_stat_collection, stop_version_stat_collection, update_version_stat_collection}, @@ -190,6 +190,7 @@ async fn dispatcher_v2(request: MmRpcRequest, ctx: MmArc) -> DispatcherResult handle_mmrpc(ctx, request, get_raw_transaction).await, "get_shared_db_id" => handle_mmrpc(ctx, request, get_shared_db_id).await, "get_staking_infos" => handle_mmrpc(ctx, request, get_staking_infos).await, + "get_wallet_names" => handle_mmrpc(ctx, request, get_wallet_names_rpc).await, "max_maker_vol" => handle_mmrpc(ctx, request, max_maker_vol).await, "my_recent_swaps" => handle_mmrpc(ctx, request, my_recent_swaps_rpc).await, "my_swap_status" => handle_mmrpc(ctx, request, my_swap_status_rpc).await, diff --git a/mm2src/mm2_main/src/wasm_tests.rs b/mm2src/mm2_main/src/wasm_tests.rs index fbf37ca545..bd24bf4a4c 100644 --- a/mm2src/mm2_main/src/wasm_tests.rs +++ b/mm2src/mm2_main/src/wasm_tests.rs @@ -1,14 +1,14 @@ use crate::lp_init; -use common::executor::{spawn, Timer}; +use common::executor::{spawn, spawn_abortable, spawn_local_abortable, AbortOnDropHandle, Timer}; use common::log::wasm_log::register_wasm_log; use mm2_core::mm_ctx::MmArc; use mm2_number::BigDecimal; use mm2_rpc::data::legacy::OrderbookResponse; use mm2_test_helpers::electrums::{doc_electrums, marty_electrums}; use mm2_test_helpers::for_tests::{check_recent_swaps, enable_electrum_json, enable_utxo_v2_electrum, - enable_z_coin_light, morty_conf, pirate_conf, rick_conf, start_swaps, - test_qrc20_history_impl, wait_for_swaps_finish_and_check_status, MarketMakerIt, - Mm2InitPrivKeyPolicy, Mm2TestConf, Mm2TestConfForSwap, ARRR, MORTY, + enable_z_coin_light, get_wallet_names, morty_conf, pirate_conf, rick_conf, + start_swaps, test_qrc20_history_impl, wait_for_swaps_finish_and_check_status, + MarketMakerIt, Mm2InitPrivKeyPolicy, Mm2TestConf, Mm2TestConfForSwap, ARRR, MORTY, PIRATE_ELECTRUMS, PIRATE_LIGHTWALLETD_URLS, RICK}; use mm2_test_helpers::get_passphrase; use mm2_test_helpers::structs::{Bip44Chain, EnableCoinBalance, HDAccountAddressId}; @@ -16,6 +16,7 @@ use serde_json::json; use wasm_bindgen_test::wasm_bindgen_test; const PIRATE_TEST_BALANCE_SEED: &str = "pirate test seed"; +const STOP_TIMEOUT_MS: u64 = 1000; /// Starts the WASM version of MM. fn wasm_start(ctx: MmArc) { @@ -26,13 +27,7 @@ fn wasm_start(ctx: MmArc) { /// This function runs Alice and Bob nodes, activates coins, starts swaps, /// and then immediately stops the nodes to check if `MmArc` is dropped in a short period. -async fn test_mm2_stops_impl( - pairs: &[(&'static str, &'static str)], - maker_price: f64, - taker_price: f64, - volume: f64, - stop_timeout_ms: u64, -) { +async fn test_mm2_stops_impl(pairs: &[(&'static str, &'static str)], maker_price: f64, taker_price: f64, volume: f64) { let coins = json!([rick_conf(), morty_conf()]); let bob_passphrase = get_passphrase!(".env.seed", "BOB_PASSPHRASE").unwrap(); @@ -69,20 +64,18 @@ async fn test_mm2_stops_impl( start_swaps(&mut mm_bob, &mut mm_alice, pairs, maker_price, taker_price, volume).await; mm_alice - .stop_and_wait_for_ctx_is_dropped(stop_timeout_ms) + .stop_and_wait_for_ctx_is_dropped(STOP_TIMEOUT_MS) .await .unwrap(); - mm_bob.stop_and_wait_for_ctx_is_dropped(stop_timeout_ms).await.unwrap(); + mm_bob.stop_and_wait_for_ctx_is_dropped(STOP_TIMEOUT_MS).await.unwrap(); } #[wasm_bindgen_test] async fn test_mm2_stops_immediately() { - const STOP_TIMEOUT_MS: u64 = 1000; - register_wasm_log(); let pairs: &[_] = &[("RICK", "MORTY")]; - test_mm2_stops_impl(pairs, 1., 1., 0.0001, STOP_TIMEOUT_MS).await; + test_mm2_stops_impl(pairs, 1., 1., 0.0001).await; } #[wasm_bindgen_test] @@ -147,8 +140,6 @@ async fn trade_base_rel_electrum( assert_eq!(0, bob_orderbook.asks.len(), "{} {} asks must be empty", base, rel); } - const STOP_TIMEOUT_MS: u64 = 1000; - mm_bob.stop_and_wait_for_ctx_is_dropped(STOP_TIMEOUT_MS).await.unwrap(); mm_alice .stop_and_wait_for_ctx_is_dropped(STOP_TIMEOUT_MS) @@ -266,3 +257,46 @@ async fn activate_z_coin_light() { }; assert_eq!(balance.balance.spendable, BigDecimal::default()); } + +#[wasm_bindgen_test] +async fn test_get_wallet_names() { + const DB_NAMESPACE_NUM: u64 = 1; + + let coins = json!([]); + + // Initialize the first wallet with a specific name + let wallet_1 = Mm2TestConf::seednode_with_wallet_name(&coins, "wallet_1", "pass"); + let mm_wallet_1 = + MarketMakerIt::start_with_db(wallet_1.conf, wallet_1.rpc_password, Some(wasm_start), DB_NAMESPACE_NUM) + .await + .unwrap(); + + // Retrieve and verify the wallet names for the first wallet + let get_wallet_names_1 = get_wallet_names(&mm_wallet_1).await; + assert_eq!(get_wallet_names_1.wallet_names, vec!["wallet_1"]); + assert_eq!(get_wallet_names_1.activated_wallet.unwrap(), "wallet_1"); + + // Stop the first wallet before starting the second one + mm_wallet_1 + .stop_and_wait_for_ctx_is_dropped(STOP_TIMEOUT_MS) + .await + .unwrap(); + + // Initialize the second wallet with a different name + let wallet_2 = Mm2TestConf::seednode_with_wallet_name(&coins, "wallet_2", "pass"); + let mm_wallet_2 = + MarketMakerIt::start_with_db(wallet_2.conf, wallet_2.rpc_password, Some(wasm_start), DB_NAMESPACE_NUM) + .await + .unwrap(); + + // Retrieve and verify the wallet names for the second wallet + let get_wallet_names_2 = get_wallet_names(&mm_wallet_2).await; + assert_eq!(get_wallet_names_2.wallet_names, vec!["wallet_1", "wallet_2"]); + assert_eq!(get_wallet_names_2.activated_wallet.unwrap(), "wallet_2"); + + // Stop the second wallet + mm_wallet_2 + .stop_and_wait_for_ctx_is_dropped(STOP_TIMEOUT_MS) + .await + .unwrap(); +} diff --git a/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs b/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs index 137eb6ef09..57a4709758 100644 --- a/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs +++ b/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs @@ -14,15 +14,15 @@ use mm2_test_helpers::electrums::*; use mm2_test_helpers::for_tests::wait_check_stats_swap_status; use mm2_test_helpers::for_tests::{account_balance, btc_segwit_conf, btc_with_spv_conf, btc_with_sync_starting_header, check_recent_swaps, enable_qrc20, enable_utxo_v2_electrum, eth_dev_conf, - find_metrics_in_json, from_env_file, get_new_address, get_shared_db_id, mm_spat, - morty_conf, my_balance, rick_conf, sign_message, start_swaps, tbtc_conf, - tbtc_segwit_conf, tbtc_with_spv_conf, test_qrc20_history_impl, tqrc20_conf, - verify_message, wait_for_swaps_finish_and_check_status, - wait_till_history_has_records, MarketMakerIt, Mm2InitPrivKeyPolicy, Mm2TestConf, - Mm2TestConfForSwap, RaiiDump, DOC_ELECTRUM_ADDRS, ETH_MAINNET_NODE, - ETH_MAINNET_SWAP_CONTRACT, ETH_SEPOLIA_NODES, ETH_SEPOLIA_SWAP_CONTRACT, - MARTY_ELECTRUM_ADDRS, MORTY, QRC20_ELECTRUMS, RICK, RICK_ELECTRUM_ADDRS, - TBTC_ELECTRUMS, T_BCH_ELECTRUMS}; + find_metrics_in_json, from_env_file, get_new_address, get_shared_db_id, + get_wallet_names, mm_spat, morty_conf, my_balance, rick_conf, sign_message, + start_swaps, tbtc_conf, tbtc_segwit_conf, tbtc_with_spv_conf, + test_qrc20_history_impl, tqrc20_conf, verify_message, + wait_for_swaps_finish_and_check_status, wait_till_history_has_records, + MarketMakerIt, Mm2InitPrivKeyPolicy, Mm2TestConf, Mm2TestConfForSwap, RaiiDump, + DOC_ELECTRUM_ADDRS, ETH_MAINNET_NODE, ETH_MAINNET_SWAP_CONTRACT, ETH_SEPOLIA_NODES, + ETH_SEPOLIA_SWAP_CONTRACT, MARTY_ELECTRUM_ADDRS, MORTY, QRC20_ELECTRUMS, RICK, + RICK_ELECTRUM_ADDRS, TBTC_ELECTRUMS, T_BCH_ELECTRUMS}; use mm2_test_helpers::get_passphrase; use mm2_test_helpers::structs::*; use serde_json::{self as json, json, Value as Json}; @@ -2351,7 +2351,7 @@ fn test_electrum_tx_history() { {"coin":"RICK","asset":"RICK","rpcport":8923,"txversion":4,"overwintered":1,"protocol":{"type":"UTXO"}}, ]); - let mut mm = MarketMakerIt::start( + let mm_bob = MarketMakerIt::start( json! ({ "gui": "nogui", "netid": 9998, @@ -2367,14 +2367,26 @@ fn test_electrum_tx_history() { None, ) .unwrap(); + let (_dump_log, _dump_dashboard) = mm_bob.mm_dump(); + log!("log path: {}", mm_bob.log_path.display()); + + let bob_electrum = block_on(enable_electrum(&mm_bob, "RICK", false, DOC_ELECTRUM_ADDRS)); + let mut enable_res_bob = HashMap::new(); + enable_res_bob.insert("RICK", bob_electrum); + log!("enable_coins_bob: {:?}", enable_res_bob); + + let mmconf = Mm2TestConf::seednode_with_wallet_name(&coins, "wallet", "pass"); + let mut mm = MarketMakerIt::start(mmconf.conf, mmconf.rpc_password, None).unwrap(); let (_dump_log, _dump_dashboard) = mm.mm_dump(); log!("log path: {}", mm.log_path.display()); // Enable RICK electrum client with tx_history loop. let electrum = block_on(enable_electrum(&mm, "RICK", true, DOC_ELECTRUM_ADDRS)); + log!("enable_coins: {:?}", electrum); + let receiving_address = electrum.address; // Wait till tx_history will not be loaded - block_on(mm.wait_for_log(500., |log| log.contains("history has been loaded successfully"))).unwrap(); + block_on(mm.wait_for_log(5., |log| log.contains("history has been loaded successfully"))).unwrap(); // tx_history is requested every 30 seconds, wait another iteration thread::sleep(Duration::from_secs(31)); @@ -2384,16 +2396,13 @@ fn test_electrum_tx_history() { assert_eq!(get_tx_history_request_count(&mm), 1); // make a transaction to change balance - let mut enable_res = HashMap::new(); - enable_res.insert("RICK", electrum); - log!("enable_coins: {:?}", enable_res); withdraw_and_send( - &mm, + &mm_bob, "RICK", None, - "RRYmiZSDo3UdHHqj1rLKf8cbJroyv9NxXw", - &enable_res, - "-0.00001", + &receiving_address, + &enable_res_bob, + "-0.00101", 0.001, ); @@ -5809,6 +5818,41 @@ fn test_get_shared_db_id() { ); } +#[test] +#[cfg(not(target_arch = "wasm32"))] +fn test_get_wallet_names() { + let coins = json!([]); + + // Initialize the first wallet with a specific name + let wallet_1 = Mm2TestConf::seednode_with_wallet_name(&coins, "wallet_1", "pass"); + let mm_wallet_1 = MarketMakerIt::start(wallet_1.conf, wallet_1.rpc_password, None).unwrap(); + + // Retrieve and verify the wallet names for the first wallet + let get_wallet_names_1 = block_on(get_wallet_names(&mm_wallet_1)); + assert_eq!(get_wallet_names_1.wallet_names, vec!["wallet_1"]); + assert_eq!(get_wallet_names_1.activated_wallet.unwrap(), "wallet_1"); + + // Initialize the second wallet with a different name + let mut wallet_2 = Mm2TestConf::seednode_with_wallet_name(&coins, "wallet_2", "pass"); + + // Set the database directory for the second wallet to the same as the first wallet + wallet_2.conf["dbdir"] = mm_wallet_1.folder.join("DB").to_str().unwrap().into(); + + // Stop the first wallet before starting the second one + block_on(mm_wallet_1.stop()).unwrap(); + + // Start the second wallet + let mm_wallet_2 = MarketMakerIt::start(wallet_2.conf, wallet_2.rpc_password, None).unwrap(); + + // Retrieve and verify the wallet names for the second wallet + let get_wallet_names_2 = block_on(get_wallet_names(&mm_wallet_2)); + assert_eq!(get_wallet_names_2.wallet_names, vec!["wallet_1", "wallet_2"]); + assert_eq!(get_wallet_names_2.activated_wallet.unwrap(), "wallet_2"); + + // Stop the second wallet + block_on(mm_wallet_2.stop()).unwrap(); +} + #[test] #[cfg(not(target_arch = "wasm32"))] fn test_sign_raw_transaction_rick() { diff --git a/mm2src/mm2_test_helpers/src/for_tests.rs b/mm2src/mm2_test_helpers/src/for_tests.rs index 57084a72e2..df5a488cf9 100644 --- a/mm2src/mm2_test_helpers/src/for_tests.rs +++ b/mm2src/mm2_test_helpers/src/for_tests.rs @@ -310,6 +310,21 @@ impl Mm2TestConf { } } + pub fn seednode_with_wallet_name(coins: &Json, wallet_name: &str, wallet_password: &str) -> Self { + Mm2TestConf { + conf: json!({ + "gui": "nogui", + "netid": 9998, + "coins": coins, + "rpc_password": DEFAULT_RPC_PASSWORD, + "i_am_seed": true, + "wallet_name": wallet_name, + "wallet_password": wallet_password, + }), + rpc_password: DEFAULT_RPC_PASSWORD.into(), + } + } + pub fn light_node(passphrase: &str, coins: &Json, seednodes: &[&str]) -> Self { Mm2TestConf { conf: json!({ @@ -1351,17 +1366,49 @@ impl MarketMakerIt { /// Start a new MarketMaker locally. /// /// * `conf` - The command-line configuration passed to the MarketMaker. + /// * `userpass` - RPC API key. + /// * `local` - Function to start the MarketMaker locally. + /// * `envs` - The environment variables passed to the process. + /// The argument is ignored for nodes running in a browser. + #[cfg(target_arch = "wasm32")] + pub async fn start_with_envs( + conf: Json, + userpass: String, + local: Option, + _envs: &[(&str, &str)], + ) -> Result { + MarketMakerIt::start_market_maker(conf, userpass, local, None).await + } + + /// Start a new MarketMaker locally with a specific database namespace. + /// + /// * `conf` - The command-line configuration passed to the MarketMaker. + /// * `userpass` - RPC API key. + /// * `local` - Function to start the MarketMaker locally. + /// * `db_namespace_id` - The test database namespace identifier. + #[cfg(target_arch = "wasm32")] + pub async fn start_with_db( + conf: Json, + userpass: String, + local: Option, + db_namespace_id: u64, + ) -> Result { + MarketMakerIt::start_market_maker(conf, userpass, local, Some(db_namespace_id)).await + } + + /// Common helper function to start the MarketMaker. + /// + /// * `conf` - The command-line configuration passed to the MarketMaker. /// Unique P2P in-memory port is injected as `p2p_in_memory_port` unless this field is already present. /// * `userpass` - RPC API key. We should probably extract it automatically from the MM log. /// * `local` - Function to start the MarketMaker locally. Required for nodes running in a browser. - /// * `envs` - The enviroment variables passed to the process. - /// The argument is ignore for nodes running in a browser. + /// * `db_namespace_id` - Optional test database namespace identifier. #[cfg(target_arch = "wasm32")] - pub async fn start_with_envs( + async fn start_market_maker( mut conf: Json, userpass: String, local: Option, - _envs: &[(&str, &str)], + db_namespace_id: Option, ) -> Result { if conf["p2p_in_memory"].is_null() { conf["p2p_in_memory"] = Json::Bool(true); @@ -1377,10 +1424,19 @@ impl MarketMakerIt { conf["p2p_in_memory_port"] = Json::Number(new_p2p_port.into()); } - let ctx = mm2_core::mm_ctx::MmCtxBuilder::new() - .with_conf(conf.clone()) - .with_test_db_namespace() - .into_mm_arc(); + let ctx = { + let builder = MmCtxBuilder::new() + .with_conf(conf.clone()); + + let builder = if let Some(ns) = db_namespace_id { + builder.with_test_db_namespace_with_id(ns) + } else { + builder.with_test_db_namespace() + }; + + builder.into_mm_arc() + }; + let local = try_s!(local.ok_or("!local")); local(ctx.clone()); @@ -2838,6 +2894,20 @@ pub async fn get_shared_db_id(mm: &MarketMakerIt) -> GetSharedDbIdResult { res.result } +pub async fn get_wallet_names(mm: &MarketMakerIt) -> GetWalletNamesResult { + let request = mm + .rpc(&json!({ + "userpass": mm.userpass, + "method": "get_wallet_names", + "mmrpc": "2.0", + })) + .await + .unwrap(); + assert_eq!(request.0, StatusCode::OK, "'get_wallet_names' failed: {}", request.1); + let res: RpcSuccessResponse<_> = json::from_str(&request.1).unwrap(); + res.result +} + pub async fn max_maker_vol(mm: &MarketMakerIt, coin: &str) -> RpcResponse { let rc = mm .rpc(&json!({ diff --git a/mm2src/mm2_test_helpers/src/structs.rs b/mm2src/mm2_test_helpers/src/structs.rs index aa2246186c..8bdcfec722 100644 --- a/mm2src/mm2_test_helpers/src/structs.rs +++ b/mm2src/mm2_test_helpers/src/structs.rs @@ -827,6 +827,13 @@ pub struct GetSharedDbIdResult { pub shared_db_id: String, } +#[derive(Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct GetWalletNamesResult { + pub wallet_names: Vec, + pub activated_wallet: Option, +} + #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] pub struct RpcV2Response { From 8e19bbd3d236131cc7daba0baaf968d141351971 Mon Sep 17 00:00:00 2001 From: shamardy <39480341+shamardy@users.noreply.github.com> Date: Wed, 25 Sep 2024 20:39:44 +0300 Subject: [PATCH 19/29] fix(merge): remove duplicated db_root function (#2229) --- mm2src/mm2_core/src/mm_ctx.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mm2src/mm2_core/src/mm_ctx.rs b/mm2src/mm2_core/src/mm_ctx.rs index 54d5f71004..c61b4e1aba 100644 --- a/mm2src/mm2_core/src/mm_ctx.rs +++ b/mm2src/mm2_core/src/mm_ctx.rs @@ -290,10 +290,6 @@ impl MmCtx { /// Returns the path to the MM databases root. #[cfg(not(target_arch = "wasm32"))] pub fn db_root(&self) -> PathBuf { path_to_db_root(self.conf["dbdir"].as_str()) } - - #[cfg(not(target_arch = "wasm32"))] - pub fn db_root(&self) -> PathBuf { path_to_db_root(self.conf["dbdir"].as_str()) } - #[cfg(not(target_arch = "wasm32"))] pub fn wallet_file_path(&self, wallet_name: &str) -> PathBuf { self.db_root().join(wallet_name.to_string() + ".dat") From 7ef08e47667c9cf20b900733619107d47ef0aa1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C3=96zkan?= Date: Thu, 26 Sep 2024 05:00:34 +0300 Subject: [PATCH 20/29] chore(doc): update issue address in README (#2227) --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bec7bd1166..441c23ba8c 100755 --- a/README.md +++ b/README.md @@ -193,4 +193,5 @@ The current state can be considered as an alpha version. ## Help and troubleshooting -If you have any question/want to report a bug/suggest an improvement feel free to [open an issue](https://github.com/artemii235/SuperNET/issues/new) or join the [Komodo Platform Discord](https://discord.gg/PGxVm2y) `dev-marketmaker` channel. +If you have any question/want to report a bug/suggest an improvement feel free to [open an issue](https://github.com/KomodoPlatform/komodo-defi-framework/issues/new/choose) or join the [Komodo Platform Discord](https://discord.gg/PGxVm2y) `dev-marketmaker` channel. + From 6dc3c7da28af9e539c0cd632c7030f2c1d57d86e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C3=96zkan?= Date: Fri, 27 Sep 2024 12:33:34 +0300 Subject: [PATCH 21/29] feat(CI): handle remote files in a safer way (#2217) This commit updates GHA runners to use locked scripts rather than always using the latest one from the master branch. It also adds a new GHA helper/plugin to easily download files and verify their checksums. --- .github/actions/deps-install/action.yml | 29 ++++++++++-- .../actions/download-and-verify/action.yml | 46 +++++++++++++++++++ .github/workflows/test.yml | 24 +++++++--- 3 files changed, 89 insertions(+), 10 deletions(-) create mode 100644 .github/actions/download-and-verify/action.yml diff --git a/.github/actions/deps-install/action.yml b/.github/actions/deps-install/action.yml index 25ed15bf50..02b0ffbec4 100644 --- a/.github/actions/deps-install/action.yml +++ b/.github/actions/deps-install/action.yml @@ -12,33 +12,54 @@ inputs: runs: using: 'composite' steps: + - name: Download protoc (Linux) + if: runner.os == 'Linux' && contains(inputs.deps, 'protoc') + uses: ./.github/actions/download-and-verify + with: + url: "https://github.com/protocolbuffers/protobuf/releases/download/v25.3/protoc-25.3-linux-x86_64.zip" + output_file: "protoc-25.3-linux-x86_64.zip" + checksum: "f853e691868d0557425ea290bf7ba6384eef2fa9b04c323afab49a770ba9da80" + - name: Install protoc (Linux) env: TMP: ${{ inputs.temp || runner.temp }} if: runner.os == 'Linux' && contains(inputs.deps, 'protoc') shell: bash run: | - wget https://github.com/protocolbuffers/protobuf/releases/download/v25.3/protoc-25.3-linux-x86_64.zip - unzip protoc-25.3-linux-x86_64 -d "$TMP/protobuf" + unzip protoc-25.3-linux-x86_64.zip -d "$TMP/protobuf" echo "$TMP/protobuf/bin" >> $GITHUB_PATH + - name: Download protoc (MacOS) + if: runner.os == 'macOS' && contains(inputs.deps, 'protoc') + uses: ./.github/actions/download-and-verify + with: + url: "https://github.com/protocolbuffers/protobuf/releases/download/v25.3/protoc-25.3-osx-x86_64.zip" + output_file: "protoc-25.3-osx-x86_64.zip" + checksum: "247e003b8e115405172eacc50bd19825209d85940728e766f0848eee7c80e2a1" + - name: Install protoc (MacOS) env: TMP: ${{ inputs.temp || runner.temp }} if: runner.os == 'macOS' && contains(inputs.deps, 'protoc') shell: bash run: | - wget https://github.com/protocolbuffers/protobuf/releases/download/v25.3/protoc-25.3-osx-x86_64.zip unzip protoc-25.3-osx-x86_64.zip -d "$TMP/protobuf" echo "$TMP/protobuf/bin" >> $GITHUB_PATH + + - name: Download protoc (Windows) + uses: ./.github/actions/download-and-verify + with: + url: "https://github.com/protocolbuffers/protobuf/releases/download/v25.3/protoc-25.3-win64.zip" + output_file: "protoc-25.3-win64.zip" + checksum: "d6b336b852726364313330631656b7f395dde5b1141b169f5c4b8d43cdf01482" + - name: Install protoc (Windows) env: TMP: ${{ inputs.temp || runner.temp }} if: runner.os == 'Windows' && contains(inputs.deps, 'protoc') shell: powershell run: | - Invoke-WebRequest -Uri https://github.com/protocolbuffers/protobuf/releases/download/v25.3/protoc-25.3-win64.zip -OutFile protoc-25.3-win64.zip 7z x protoc-25.3-win64.zip -o"$TMP\protobuf" echo "$TMP\protobuf\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append diff --git a/.github/actions/download-and-verify/action.yml b/.github/actions/download-and-verify/action.yml new file mode 100644 index 0000000000..198601f486 --- /dev/null +++ b/.github/actions/download-and-verify/action.yml @@ -0,0 +1,46 @@ +name: "Download and verify remote files" + +runs: + using: "composite" + steps: + - name: Download (Unix) + if: runner.os != 'Windows' + shell: bash + run: curl -L -o ${{ inputs.output_file }} ${{ inputs.url }} + + - name: Download (Windows) + if: runner.os == 'Windows' + shell: powershell + run: Invoke-WebRequest -Uri ${{ inputs.url }} -OutFile ${{ inputs.output_file }} + + - name: Verify (Unix) + if: runner.os != 'Windows' + shell: bash + run: | + if [[ "$RUNNER_OS" == "macOS" ]]; then + echo "${{ inputs.checksum }} *${{ inputs.output_file }}" | shasum -a 256 -c + else + echo "${{ inputs.checksum }} ${{ inputs.output_file }}" | sha256sum -c + fi + + - name: Verify (Windows) + if: runner.os == 'Windows' + shell: powershell + run: | + $expectedChecksum = "${{ inputs.checksum }}" + $actualChecksum = (Get-FileHash -Path "${{ inputs.output_file }}" -Algorithm SHA256).Hash + if ($expectedChecksum -ne $actualChecksum) { + Write-Output "Checksum did not match! Expected: $expectedChecksum, Found: $actualChecksum" + exit 1 + } + +inputs: + url: + description: "URL of the remote file." + required: true + output_file: + description: "Output path." + required: true + checksum: + description: "Expected checksum of the downloaded file." + required: true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a9790f9b13..12a60bbc3c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -122,7 +122,7 @@ jobs: - name: Test run: | - wget -O - https://raw.githubusercontent.com/KomodoPlatform/komodo/master/zcutil/fetch-params-alt.sh | bash + wget -O - https://raw.githubusercontent.com/KomodoPlatform/komodo/0adeeabdd484ef40539d1275c6a765f5c530ea79/zcutil/fetch-params-alt.sh | bash cargo test --test 'mm2_tests_main' --no-fail-fast mac-x86-64-kdf-integration: @@ -154,7 +154,7 @@ jobs: - name: Test run: | - wget -O - https://raw.githubusercontent.com/KomodoPlatform/komodo/master/zcutil/fetch-params-alt.sh | bash + wget -O - https://raw.githubusercontent.com/KomodoPlatform/komodo/0adeeabdd484ef40539d1275c6a765f5c530ea79/zcutil/fetch-params-alt.sh | bash cargo test --test 'mm2_tests_main' --no-fail-fast win-x86-64-kdf-integration: @@ -181,10 +181,16 @@ jobs: - name: Cargo cache uses: ./.github/actions/cargo-cache + - name: Download wget64 + uses: ./.github/actions/download-and-verify + with: + url: "https://github.com/KomodoPlatform/komodo/raw/d456be35acd1f8584e1e4f971aea27bd0644d5c5/zcutil/wget64.exe" + output_file: "/wget64.exe" + checksum: "d80719431dc22b0e4a070f61fab982b113a4ed9a6d4cf25e64b5be390dcadb94" + - name: Test run: | - Invoke-WebRequest -Uri https://github.com/KomodoPlatform/komodo/raw/d456be35acd1f8584e1e4f971aea27bd0644d5c5/zcutil/wget64.exe -OutFile \wget64.exe - Invoke-WebRequest -Uri https://raw.githubusercontent.com/KomodoPlatform/komodo/master/zcutil/fetch-params-alt.bat -OutFile \cmd.bat && \cmd.bat + Invoke-WebRequest -Uri https://raw.githubusercontent.com/KomodoPlatform/komodo/0adeeabdd484ef40539d1275c6a765f5c530ea79/zcutil/fetch-params-alt.bat -OutFile \cmd.bat && \cmd.bat cargo test --test 'mm2_tests_main' --no-fail-fast docker-tests: @@ -213,7 +219,7 @@ jobs: - name: Test run: | - wget -O - https://raw.githubusercontent.com/KomodoPlatform/komodo/v0.8.1/zcutil/fetch-params-alt.sh | bash + wget -O - https://raw.githubusercontent.com/KomodoPlatform/komodo/v0.8.1//zcutil/fetch-params-alt.sh | bash cargo test --test 'docker_tests_main' --features run-docker-tests --no-fail-fast wasm: @@ -241,11 +247,17 @@ jobs: - name: Install wasm-pack run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + - name: Download geckodriver + uses: ./.github/actions/download-and-verify + with: + url: "https://github.com/mozilla/geckodriver/releases/download/v0.32.2/geckodriver-v0.32.2-linux64.tar.gz" + output_file: "geckodriver-v0.32.2-linux64.tar.gz" + checksum: "1eab226bf009599f5aa1d77d9ed4c374e10a03fd848b500be1b32cefd2cbec64" + - name: Install firefox and geckodriver run: | sudo apt-get update -y sudo apt-get install -y firefox - wget https://github.com/mozilla/geckodriver/releases/download/v0.32.2/geckodriver-v0.32.2-linux64.tar.gz sudo tar -xzvf geckodriver-v0.32.2-linux64.tar.gz -C /bin sudo chmod +x /bin/geckodriver From 76d43423ed379b9c2db1338f61505cf2c8bf6779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C3=96zkan?= Date: Tue, 1 Oct 2024 11:27:55 +0300 Subject: [PATCH 22/29] fix(proxy-signature): add message lifetime overflows (#2233) This commit prevents creating messages for proxy with too long lifetimes. --- mm2src/proxy_signature/src/lib.rs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/mm2src/proxy_signature/src/lib.rs b/mm2src/proxy_signature/src/lib.rs index f4ae2393a0..15cf596c89 100644 --- a/mm2src/proxy_signature/src/lib.rs +++ b/mm2src/proxy_signature/src/lib.rs @@ -2,6 +2,7 @@ use chrono::Utc; use http::Uri; use libp2p::identity::{Keypair, PublicKey, SigningError}; use serde::{Deserialize, Serialize}; +use std::convert::TryFrom; /// Represents a message and its corresponding signature. #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] @@ -69,8 +70,11 @@ impl RawMessage { impl ProxySign { /// Validates if the message is still valid based on its expiration time and signature verification. - pub fn is_valid_message(&self) -> bool { - if Utc::now().timestamp() > self.raw_message.expires_at { + pub fn is_valid_message(&self, max_message_exp_secs: u64) -> bool { + let now = Utc::now().timestamp(); + let remaining_expiration_seconds = u64::try_from(self.raw_message.expires_at - now).unwrap_or(0); + + if remaining_expiration_seconds == 0 || remaining_expiration_seconds > max_message_exp_secs { return false; } @@ -111,14 +115,14 @@ pub mod proxy_signature_tests { fn sign_and_verify() { let keypair = random_keypair(); let signed_proxy_message = RawMessage::sign(&keypair, &Uri::from_static("http://example.com"), 0, 5).unwrap(); - assert!(signed_proxy_message.is_valid_message()); + assert!(signed_proxy_message.is_valid_message(10)); } #[test] fn expired_signature() { let keypair = random_keypair(); let signed_proxy_message = RawMessage::sign(&keypair, &Uri::from_static("http://example.com"), 0, -1).unwrap(); - assert!(!signed_proxy_message.is_valid_message()); + assert!(!signed_proxy_message.is_valid_message(10)); } #[test] @@ -127,17 +131,24 @@ pub mod proxy_signature_tests { let mut signed_proxy_message = RawMessage::sign(&keypair, &Uri::from_static("http://example.com"), 0, 5).unwrap(); signed_proxy_message.raw_message.uri = "http://demo.com".to_string(); - assert!(!signed_proxy_message.is_valid_message()); + assert!(!signed_proxy_message.is_valid_message(10)); let mut signed_proxy_message = RawMessage::sign(&keypair, &Uri::from_static("http://example.com"), 0, 5).unwrap(); signed_proxy_message.raw_message.body_size += 1; - assert!(!signed_proxy_message.is_valid_message()); + assert!(!signed_proxy_message.is_valid_message(10)); let mut signed_proxy_message = RawMessage::sign(&keypair, &Uri::from_static("http://example.com"), 0, 5).unwrap(); signed_proxy_message.raw_message.expires_at += 1; - assert!(!signed_proxy_message.is_valid_message()); + assert!(!signed_proxy_message.is_valid_message(10)); + } + + #[test] + fn message_lifetime_overflow() { + let keypair = random_keypair(); + let signed_proxy_message = RawMessage::sign(&keypair, &Uri::from_static("http://example.com"), 0, 5).unwrap(); + assert!(!signed_proxy_message.is_valid_message(4)); } #[test] From 1f3dffa167cbf088870ad697205a982d9f735256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C3=96zkan?= Date: Thu, 3 Oct 2024 21:11:17 +0300 Subject: [PATCH 23/29] feat(new-RPC): connection healthcheck implementation for peers (#2194) --- mm2src/common/expirable_map.rs | 22 +- mm2src/mm2_core/src/mm_ctx.rs | 11 +- mm2src/mm2_main/Cargo.toml | 1 + mm2src/mm2_main/src/lp_healthcheck.rs | 513 ++++++++++++++++++ mm2src/mm2_main/src/lp_native_dex.rs | 11 +- mm2src/mm2_main/src/lp_network.rs | 6 +- mm2src/mm2_main/src/mm2.rs | 1 + .../mm2_main/src/rpc/dispatcher/dispatcher.rs | 2 + .../tests/mm2_tests/mm2_tests_inner.rs | 42 +- mm2src/mm2_test_helpers/src/for_tests.rs | 38 +- 10 files changed, 629 insertions(+), 18 deletions(-) create mode 100644 mm2src/mm2_main/src/lp_healthcheck.rs diff --git a/mm2src/common/expirable_map.rs b/mm2src/common/expirable_map.rs index 9b85dea84c..e9fe7f0b4f 100644 --- a/mm2src/common/expirable_map.rs +++ b/mm2src/common/expirable_map.rs @@ -14,9 +14,26 @@ pub struct ExpirableEntry { } impl ExpirableEntry { + #[inline(always)] + pub fn new(v: V, exp: Duration) -> Self { + Self { + expires_at: Instant::now() + exp, + value: v, + } + } + + #[inline(always)] pub fn get_element(&self) -> &V { &self.value } + #[inline(always)] + pub fn update_value(&mut self, v: V) { self.value = v } + + #[inline(always)] pub fn update_expiration(&mut self, expires_at: Instant) { self.expires_at = expires_at } + + /// Checks whether entry has longer ttl than the given one. + #[inline(always)] + pub fn has_longer_life_than(&self, min_ttl: Duration) -> bool { self.expires_at > Instant::now() + min_ttl } } impl Default for ExpirableMap { @@ -47,10 +64,7 @@ impl ExpirableMap { /// If a value already exists for the given key, it will be updated and then /// the old one will be returned. pub fn insert(&mut self, k: K, v: V, exp: Duration) -> Option { - let entry = ExpirableEntry { - expires_at: Instant::now() + exp, - value: v, - }; + let entry = ExpirableEntry::new(v, exp); self.0.insert(k, entry).map(|v| v.value) } diff --git a/mm2src/mm2_core/src/mm_ctx.rs b/mm2src/mm2_core/src/mm_ctx.rs index c61b4e1aba..6e060d91c3 100644 --- a/mm2src/mm2_core/src/mm_ctx.rs +++ b/mm2src/mm2_core/src/mm_ctx.rs @@ -1,9 +1,12 @@ #[cfg(feature = "track-ctx-pointer")] use common::executor::Timer; -use common::executor::{abortable_queue::{AbortableQueue, WeakSpawner}, - graceful_shutdown, AbortSettings, AbortableSystem, SpawnAbortable, SpawnFuture}; use common::log::{self, LogLevel, LogOnError, LogState}; use common::{cfg_native, cfg_wasm32, small_rng}; +use common::{executor::{abortable_queue::{AbortableQueue, WeakSpawner}, + graceful_shutdown, AbortSettings, AbortableSystem, SpawnAbortable, SpawnFuture}, + expirable_map::ExpirableMap}; +use futures::channel::oneshot; +use futures::lock::Mutex as AsyncMutex; use gstuff::{try_s, Constructible, ERR, ERRL}; use lazy_static::lazy_static; use mm2_event_stream::{controller::Controller, Event, EventStreamConfiguration}; @@ -30,7 +33,6 @@ cfg_wasm32! { cfg_native! { use db_common::async_sql_conn::AsyncConnection; use db_common::sqlite::rusqlite::Connection; - use futures::lock::Mutex as AsyncMutex; use rustls::ServerName; use mm2_metrics::prometheus; use mm2_metrics::MmMetricsError; @@ -142,6 +144,8 @@ pub struct MmCtx { /// asynchronous handle for rusqlite connection. #[cfg(not(target_arch = "wasm32"))] pub async_sqlite_connection: Constructible>>, + /// Links the RPC context to the P2P context to handle health check responses. + pub healthcheck_response_handler: AsyncMutex>>, } impl MmCtx { @@ -191,6 +195,7 @@ impl MmCtx { nft_ctx: Mutex::new(None), #[cfg(not(target_arch = "wasm32"))] async_sqlite_connection: Constructible::default(), + healthcheck_response_handler: AsyncMutex::new(ExpirableMap::default()), } } diff --git a/mm2src/mm2_main/Cargo.toml b/mm2src/mm2_main/Cargo.toml index 8f84ebb90a..53154efa05 100644 --- a/mm2src/mm2_main/Cargo.toml +++ b/mm2src/mm2_main/Cargo.toml @@ -32,6 +32,7 @@ bitcrypto = { path = "../mm2_bitcoin/crypto" } blake2 = "0.10.6" bytes = "0.4" chain = { path = "../mm2_bitcoin/chain" } +chrono = "0.4" cfg-if = "1.0" coins = { path = "../coins" } coins_activation = { path = "../coins_activation" } diff --git a/mm2src/mm2_main/src/lp_healthcheck.rs b/mm2src/mm2_main/src/lp_healthcheck.rs new file mode 100644 index 0000000000..722bc21402 --- /dev/null +++ b/mm2src/mm2_main/src/lp_healthcheck.rs @@ -0,0 +1,513 @@ +use async_std::prelude::FutureExt; +use chrono::Utc; +use common::executor::SpawnFuture; +use common::expirable_map::ExpirableEntry; +use common::{log, HttpStatusCode, StatusCode}; +use derive_more::Display; +use futures::channel::oneshot::{self, Receiver, Sender}; +use instant::{Duration, Instant}; +use lazy_static::lazy_static; +use mm2_core::mm_ctx::MmArc; +use mm2_err_handle::prelude::MmError; +use mm2_libp2p::{decode_message, encode_message, pub_sub_topic, Libp2pPublic, TopicPrefix}; +use mm2_net::p2p::P2PContext; +use ser_error_derive::SerializeErrorType; +use serde::{Deserialize, Serialize}; +use std::convert::TryFrom; +use std::str::FromStr; +use std::sync::Mutex; + +use crate::lp_network::broadcast_p2p_msg; + +pub(crate) const PEER_HEALTHCHECK_PREFIX: TopicPrefix = "hcheck"; + +const fn healthcheck_message_exp_secs() -> u64 { + #[cfg(test)] + return 3; + + #[cfg(not(test))] + 10 +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[cfg_attr(any(test, target_arch = "wasm32"), derive(PartialEq))] +pub(crate) struct HealthcheckMessage { + #[serde(deserialize_with = "deserialize_bytes")] + signature: Vec, + data: HealthcheckData, +} + +/// Wrapper of `libp2p::PeerId` with trait additional implementations. +/// +/// TODO: This should be used as a replacement of `libp2p::PeerId` in the entire project. +#[derive(Clone, Copy, Debug, Display, PartialEq)] +pub struct PeerAddress(mm2_libp2p::PeerId); + +impl From for PeerAddress { + fn from(value: mm2_libp2p::PeerId) -> Self { Self(value) } +} + +impl From for mm2_libp2p::PeerId { + fn from(value: PeerAddress) -> Self { value.0 } +} + +impl Serialize for PeerAddress { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.0.to_string()) + } +} + +impl<'de> Deserialize<'de> for PeerAddress { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct PeerAddressVisitor; + + impl<'de> serde::de::Visitor<'de> for PeerAddressVisitor { + type Value = PeerAddress; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a string representation of peer id.") + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + if value.len() > 100 { + return Err(serde::de::Error::invalid_length( + value.len(), + &"peer id cannot exceed 100 characters.", + )); + } + + Ok(mm2_libp2p::PeerId::from_str(value) + .map_err(serde::de::Error::custom)? + .into()) + } + + fn visit_string(self, value: String) -> Result + where + E: serde::de::Error, + { + self.visit_str(&value) + } + } + + deserializer.deserialize_str(PeerAddressVisitor) + } +} + +#[derive(Debug, Display)] +enum SignValidationError { + #[display( + fmt = "Healthcheck message is expired. Current time in UTC: {now_secs}, healthcheck `expires_at` in UTC: {expires_at_secs}" + )] + Expired { now_secs: u64, expires_at_secs: u64 }, + #[display( + fmt = "Healthcheck message have too high expiration time. Max allowed expiration seconds: {max_allowed_expiration_secs}, received message expiration seconds: {remaining_expiration_secs}" + )] + LifetimeOverflow { + max_allowed_expiration_secs: u64, + remaining_expiration_secs: u64, + }, + #[display(fmt = "Public key is not valid.")] + InvalidPublicKey, + #[display(fmt = "Signature integrity doesn't match with the public key.")] + FakeSignature, + #[display(fmt = "Process failed unexpectedly due to this reason: {reason}")] + Internal { reason: String }, +} + +impl HealthcheckMessage { + pub(crate) fn generate_message(ctx: &MmArc, is_a_reply: bool) -> Result { + let p2p_ctx = P2PContext::fetch_from_mm_arc(ctx); + let keypair = p2p_ctx.keypair(); + let sender_public_key = keypair.public().encode_protobuf(); + + let data = HealthcheckData { + sender_public_key, + expires_at_secs: u64::try_from(Utc::now().timestamp()).map_err(|e| e.to_string())? + + healthcheck_message_exp_secs(), + is_a_reply, + }; + + let signature = try_s!(keypair.sign(&try_s!(data.encode()))); + + Ok(Self { signature, data }) + } + + fn generate_or_use_cached_message(ctx: &MmArc) -> Result { + const MIN_DURATION_FOR_REUSABLE_MSG: Duration = Duration::from_secs(5); + + lazy_static! { + static ref RECENTLY_GENERATED_MESSAGE: Mutex> = + Mutex::new(ExpirableEntry::new( + // Using dummy values in order to initialize `HealthcheckMessage` context. + HealthcheckMessage { + signature: vec![], + data: HealthcheckData { + sender_public_key: vec![], + expires_at_secs: 0, + is_a_reply: false, + }, + }, + Duration::from_secs(0) + )); + } + + // If recently generated message has longer life than `MIN_DURATION_FOR_REUSABLE_MSG`, we can reuse it to + // reduce the message generation overhead under high pressure. + let mut mutexed_msg = RECENTLY_GENERATED_MESSAGE.lock().unwrap(); + + if mutexed_msg.has_longer_life_than(MIN_DURATION_FOR_REUSABLE_MSG) { + Ok(mutexed_msg.get_element().clone()) + } else { + let new_msg = HealthcheckMessage::generate_message(ctx, true)?; + + mutexed_msg.update_value(new_msg.clone()); + mutexed_msg.update_expiration(Instant::now() + Duration::from_secs(healthcheck_message_exp_secs())); + + Ok(new_msg) + } + } + + fn is_received_message_valid(&self) -> Result { + let now_secs = u64::try_from(Utc::now().timestamp()) + .map_err(|e| SignValidationError::Internal { reason: e.to_string() })?; + + let remaining_expiration_secs = self.data.expires_at_secs - now_secs; + + if remaining_expiration_secs == 0 { + return Err(SignValidationError::Expired { + now_secs, + expires_at_secs: self.data.expires_at_secs, + }); + } else if remaining_expiration_secs > healthcheck_message_exp_secs() { + return Err(SignValidationError::LifetimeOverflow { + max_allowed_expiration_secs: healthcheck_message_exp_secs(), + remaining_expiration_secs, + }); + } + + let Ok(public_key) = Libp2pPublic::try_decode_protobuf(&self.data.sender_public_key) else { + log::debug!("Couldn't decode public key from the healthcheck message."); + + return Err(SignValidationError::InvalidPublicKey); + }; + + let encoded_message = self + .data + .encode() + .map_err(|e| SignValidationError::Internal { reason: e.to_string() })?; + + if public_key.verify(&encoded_message, &self.signature) { + Ok(public_key.to_peer_id().into()) + } else { + Err(SignValidationError::FakeSignature) + } + } + + #[inline] + pub(crate) fn encode(&self) -> Result, rmp_serde::encode::Error> { encode_message(self) } + + #[inline] + pub(crate) fn decode(bytes: &[u8]) -> Result { decode_message(bytes) } + + #[inline] + pub(crate) fn should_reply(&self) -> bool { !self.data.is_a_reply } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[cfg_attr(any(test, target_arch = "wasm32"), derive(PartialEq))] +struct HealthcheckData { + #[serde(deserialize_with = "deserialize_bytes")] + sender_public_key: Vec, + expires_at_secs: u64, + is_a_reply: bool, +} + +impl HealthcheckData { + #[inline] + fn encode(&self) -> Result, rmp_serde::encode::Error> { encode_message(self) } +} + +#[inline] +pub fn peer_healthcheck_topic(peer_address: &PeerAddress) -> String { + pub_sub_topic(PEER_HEALTHCHECK_PREFIX, &peer_address.to_string()) +} + +#[derive(Deserialize)] +pub struct RequestPayload { + peer_address: PeerAddress, +} + +fn deserialize_bytes<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + struct ByteVisitor; + + impl<'de> serde::de::Visitor<'de> for ByteVisitor { + type Value = Vec; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a non-empty byte array up to 512 bytes") + } + + fn visit_seq(self, mut seq: A) -> Result, A::Error> + where + A: serde::de::SeqAccess<'de>, + { + let mut buffer = vec![]; + while let Some(byte) = seq.next_element()? { + if buffer.len() >= 512 { + return Err(serde::de::Error::invalid_length( + buffer.len(), + &"longest possible length allowed for this field is 512 bytes (with RSA algorithm).", + )); + } + + buffer.push(byte); + } + + if buffer.is_empty() { + return Err(serde::de::Error::custom("Can't be empty.")); + } + + Ok(buffer) + } + } + + deserializer.deserialize_seq(ByteVisitor) +} + +#[derive(Debug, Display, Serialize, SerializeErrorType)] +#[serde(tag = "error_type", content = "error_data")] +pub enum HealthcheckRpcError { + MessageGenerationFailed { reason: String }, + MessageEncodingFailed { reason: String }, + Internal { reason: String }, +} + +impl HttpStatusCode for HealthcheckRpcError { + fn status_code(&self) -> common::StatusCode { + match self { + HealthcheckRpcError::MessageGenerationFailed { .. } + | HealthcheckRpcError::Internal { .. } + | HealthcheckRpcError::MessageEncodingFailed { .. } => StatusCode::INTERNAL_SERVER_ERROR, + } + } +} + +pub async fn peer_connection_healthcheck_rpc( + ctx: MmArc, + req: RequestPayload, +) -> Result> { + // When things go awry, we want records to clear themselves to keep the memory clean of unused data. + // This is unrelated to the timeout logic. + let address_record_exp = Duration::from_secs(healthcheck_message_exp_secs()); + + let target_peer_address = req.peer_address; + + let p2p_ctx = P2PContext::fetch_from_mm_arc(&ctx); + if target_peer_address == p2p_ctx.peer_id().into() { + // That's us, so return true. + return Ok(true); + } + + let message = HealthcheckMessage::generate_message(&ctx, false) + .map_err(|reason| HealthcheckRpcError::MessageGenerationFailed { reason })?; + + let encoded_message = message + .encode() + .map_err(|e| HealthcheckRpcError::MessageEncodingFailed { reason: e.to_string() })?; + + let (tx, rx): (Sender<()>, Receiver<()>) = oneshot::channel(); + + { + let mut book = ctx.healthcheck_response_handler.lock().await; + book.clear_expired_entries(); + book.insert(target_peer_address.to_string(), tx, address_record_exp); + } + + broadcast_p2p_msg( + &ctx, + peer_healthcheck_topic(&target_peer_address), + encoded_message, + None, + ); + + let timeout_duration = Duration::from_secs(healthcheck_message_exp_secs()); + Ok(rx.timeout(timeout_duration).await == Ok(Ok(()))) +} + +pub(crate) async fn process_p2p_healthcheck_message(ctx: &MmArc, message: mm2_libp2p::GossipsubMessage) { + macro_rules! try_or_return { + ($exp:expr, $msg: expr) => { + match $exp { + Ok(t) => t, + Err(e) => { + log::error!("{}, error: {e:?}", $msg); + return; + }, + } + }; + } + + let data = try_or_return!( + HealthcheckMessage::decode(&message.data), + "Couldn't decode healthcheck message" + ); + + let ctx = ctx.clone(); + + // Pass the remaining work to another thread to free up this one as soon as possible, + // so KDF can handle a high amount of healthcheck messages more efficiently. + ctx.spawner().spawn(async move { + let sender_peer = match data.is_received_message_valid() { + Ok(t) => t, + Err(e) => { + log::error!("Received an invalid healthcheck message. Error: {e}"); + return; + }, + }; + + if data.should_reply() { + // Reply the message so they know we are healthy. + + let msg = try_or_return!( + HealthcheckMessage::generate_or_use_cached_message(&ctx), + "Couldn't generate the healthcheck message, this is very unusual!" + ); + + let encoded_msg = try_or_return!( + msg.encode(), + "Couldn't encode healthcheck message, this is very unusual!" + ); + + let topic = peer_healthcheck_topic(&sender_peer); + broadcast_p2p_msg(&ctx, topic, encoded_msg, None); + } else { + // The requested peer is healthy; signal the response channel. + let mut response_handler = ctx.healthcheck_response_handler.lock().await; + if let Some(tx) = response_handler.remove(&sender_peer.to_string()) { + if tx.send(()).is_err() { + log::error!("Result channel isn't present for peer '{sender_peer}'."); + }; + } else { + log::info!("Peer '{sender_peer}' isn't recorded in the healthcheck response handler."); + }; + } + }); +} + +#[cfg(any(test, target_arch = "wasm32"))] +mod tests { + use std::mem::discriminant; + + use super::*; + use common::cross_test; + use crypto::CryptoCtx; + use mm2_libp2p::behaviours::atomicdex::generate_ed25519_keypair; + use mm2_test_helpers::for_tests::mm_ctx_with_iguana; + + common::cfg_wasm32! { + use wasm_bindgen_test::*; + wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + } + + fn ctx() -> MmArc { + let ctx = mm_ctx_with_iguana(Some("dummy-value")); + let p2p_key = { + let crypto_ctx = CryptoCtx::from_ctx(&ctx).unwrap(); + let key = bitcrypto::sha256(crypto_ctx.mm2_internal_privkey_slice()); + key.take() + }; + + let (cmd_tx, _) = futures::channel::mpsc::channel(0); + + let p2p_context = P2PContext::new(cmd_tx, generate_ed25519_keypair(p2p_key)); + p2p_context.store_to_mm_arc(&ctx); + + ctx + } + + cross_test!(test_peer_address, { + #[derive(Deserialize, Serialize)] + struct PeerAddressTest { + peer_address: PeerAddress, + } + + let address_str = "12D3KooWEtuv7kmgGCC7oAQ31hB7AR5KkhT3eEWB2bP2roo3M7rY"; + let json_content = format!("{{\"peer_address\": \"{address_str}\"}}"); + let address_struct: PeerAddressTest = serde_json::from_str(&json_content).unwrap(); + + let actual_peer_id = mm2_libp2p::PeerId::from_str(address_str).unwrap(); + let deserialized_peer_id: mm2_libp2p::PeerId = address_struct.peer_address.into(); + + assert_eq!(deserialized_peer_id, actual_peer_id); + }); + + cross_test!(test_valid_message, { + let ctx = ctx(); + let message = HealthcheckMessage::generate_message(&ctx, false).unwrap(); + message.is_received_message_valid().unwrap(); + }); + + cross_test!(test_corrupted_messages, { + let ctx = ctx(); + + let mut message = HealthcheckMessage::generate_message(&ctx, false).unwrap(); + message.data.expires_at_secs += healthcheck_message_exp_secs() * 3; + assert_eq!( + discriminant(&message.is_received_message_valid().err().unwrap()), + discriminant(&SignValidationError::LifetimeOverflow { + max_allowed_expiration_secs: 0, + remaining_expiration_secs: 0 + }) + ); + + let mut message = HealthcheckMessage::generate_message(&ctx, false).unwrap(); + message.data.is_a_reply = !message.data.is_a_reply; + assert_eq!( + discriminant(&message.is_received_message_valid().err().unwrap()), + discriminant(&SignValidationError::FakeSignature) + ); + + let mut message = HealthcheckMessage::generate_message(&ctx, false).unwrap(); + message.data.sender_public_key.push(0); + assert_eq!( + discriminant(&message.is_received_message_valid().err().unwrap()), + discriminant(&SignValidationError::InvalidPublicKey) + ); + }); + + cross_test!(test_expired_message, { + let ctx = ctx(); + let message = HealthcheckMessage::generate_message(&ctx, false).unwrap(); + common::executor::Timer::sleep(3.).await; + assert_eq!( + discriminant(&message.is_received_message_valid().err().unwrap()), + discriminant(&SignValidationError::Expired { + now_secs: 0, + expires_at_secs: 0 + }) + ); + }); + + cross_test!(test_encode_decode, { + let ctx = ctx(); + let original = HealthcheckMessage::generate_message(&ctx, false).unwrap(); + + let encoded = original.encode().unwrap(); + assert!(!encoded.is_empty()); + + let decoded = HealthcheckMessage::decode(&encoded).unwrap(); + assert_eq!(original, decoded); + }); +} diff --git a/mm2src/mm2_main/src/lp_native_dex.rs b/mm2src/mm2_main/src/lp_native_dex.rs index bd875511b0..cd055132a5 100644 --- a/mm2src/mm2_main/src/lp_native_dex.rs +++ b/mm2src/mm2_main/src/lp_native_dex.rs @@ -36,7 +36,7 @@ use mm2_metrics::mm_gauge; use mm2_net::network_event::NetworkEvent; use mm2_net::p2p::P2PContext; use rpc_task::RpcTaskError; -use serde_json::{self as json}; +use serde_json as json; use std::convert::TryInto; use std::io; use std::path::PathBuf; @@ -47,8 +47,9 @@ use std::{fs, usize}; #[cfg(not(target_arch = "wasm32"))] use crate::database::init_and_migrate_sql_db; use crate::heartbeat_event::HeartbeatEvent; +use crate::lp_healthcheck::peer_healthcheck_topic; use crate::lp_message_service::{init_message_service, InitMessageServiceError}; -use crate::lp_network::{lp_network_ports, p2p_event_process_loop, NetIdError}; +use crate::lp_network::{lp_network_ports, p2p_event_process_loop, subscribe_to_topic, NetIdError}; use crate::lp_ordermatch::{broadcast_maker_orders_keep_alive_loop, clean_memory_loop, init_ordermatch_context, lp_ordermatch_loop, orders_kick_start, BalanceUpdateOrdermatchHandler, OrdermatchInitError}; use crate::lp_swap::{running_swaps_num, swap_kick_starts}; @@ -635,7 +636,8 @@ pub async fn init_p2p(ctx: MmArc) -> P2PResult<()> { ); }) .await; - let (cmd_tx, event_rx, _peer_id) = spawn_result?; + + let (cmd_tx, event_rx, peer_id) = spawn_result?; let p2p_context = P2PContext::new(cmd_tx, generate_ed25519_keypair(p2p_key)); p2p_context.store_to_mm_arc(&ctx); @@ -643,6 +645,9 @@ pub async fn init_p2p(ctx: MmArc) -> P2PResult<()> { let fut = p2p_event_process_loop(ctx.weak(), event_rx, i_am_seed); ctx.spawner().spawn(fut); + // Listen for health check messages. + subscribe_to_topic(&ctx, peer_healthcheck_topic(&peer_id.into())); + Ok(()) } diff --git a/mm2src/mm2_main/src/lp_network.rs b/mm2src/mm2_main/src/lp_network.rs index eb84f390d1..8e5195e93a 100644 --- a/mm2src/mm2_main/src/lp_network.rs +++ b/mm2src/mm2_main/src/lp_network.rs @@ -38,8 +38,7 @@ use mm2_net::p2p::P2PContext; use serde::de; use std::net::ToSocketAddrs; -use crate::lp_ordermatch; -use crate::{lp_stats, lp_swap}; +use crate::{lp_healthcheck, lp_ordermatch, lp_stats, lp_swap}; pub type P2PRequestResult = Result>; pub type P2PProcessResult = Result>; @@ -216,6 +215,9 @@ async fn process_p2p_message( } } }, + Some(lp_healthcheck::PEER_HEALTHCHECK_PREFIX) => { + lp_healthcheck::process_p2p_healthcheck_message(&ctx, message).await + }, None | Some(_) => (), } diff --git a/mm2src/mm2_main/src/mm2.rs b/mm2src/mm2_main/src/mm2.rs index 734e71becf..5224ed1b92 100644 --- a/mm2src/mm2_main/src/mm2.rs +++ b/mm2src/mm2_main/src/mm2.rs @@ -72,6 +72,7 @@ use mm2_err_handle::prelude::*; pub mod heartbeat_event; pub mod lp_dispatcher; +pub mod lp_healthcheck; pub mod lp_message_service; mod lp_native_dex; pub mod lp_network; diff --git a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs index fec08d106f..5a480a0a2e 100644 --- a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs +++ b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs @@ -1,4 +1,5 @@ use super::{DispatcherError, DispatcherResult, PUBLIC_METHODS}; +use crate::lp_healthcheck::peer_connection_healthcheck_rpc; use crate::lp_native_dex::init_hw::{cancel_init_trezor, init_trezor, init_trezor_status, init_trezor_user_action}; #[cfg(target_arch = "wasm32")] use crate::lp_native_dex::init_metamask::{cancel_connect_metamask, connect_metamask, connect_metamask_status}; @@ -214,6 +215,7 @@ async fn dispatcher_v2(request: MmRpcRequest, ctx: MmArc) -> DispatcherResult handle_mmrpc(ctx, request, withdraw).await, "ibc_chains" => handle_mmrpc(ctx, request, ibc_chains).await, "ibc_transfer_channels" => handle_mmrpc(ctx, request, ibc_transfer_channels).await, + "peer_connection_healthcheck" => handle_mmrpc(ctx, request, peer_connection_healthcheck_rpc).await, "withdraw_nft" => handle_mmrpc(ctx, request, withdraw_nft).await, "start_eth_fee_estimator" => handle_mmrpc(ctx, request, start_eth_fee_estimator).await, "stop_eth_fee_estimator" => handle_mmrpc(ctx, request, stop_eth_fee_estimator).await, diff --git a/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs b/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs index 57a4709758..906af55887 100644 --- a/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs +++ b/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs @@ -35,7 +35,7 @@ use uuid::Uuid; cfg_native! { use common::block_on; - use mm2_test_helpers::for_tests::{get_passphrase, new_mm2_temp_folder_path}; + use mm2_test_helpers::for_tests::{get_passphrase, new_mm2_temp_folder_path, peer_connection_healthcheck}; use mm2_io::fs::slurp; use hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN; } @@ -5963,6 +5963,46 @@ fn test_sign_raw_transaction_p2wpkh() { assert!(response["error"].as_str().unwrap().contains("Signing error")); } +#[test] +#[cfg(not(target_arch = "wasm32"))] +fn test_connection_healthcheck_rpc() { + const BOB_ADDRESS: &str = "12D3KooWEtuv7kmgGCC7oAQ31hB7AR5KkhT3eEWB2bP2roo3M7rY"; + const BOB_SEED: &str = "dummy-value-bob"; + + const ALICE_ADDRESS: &str = "12D3KooWHnoKd2Lr7BoxHCCeBhcnfAZsdiCdojbEMLE7DDSbMo1g"; + const ALICE_SEED: &str = "dummy-value-alice"; + + let bob_conf = Mm2TestConf::seednode(BOB_SEED, &json!([])); + let bob_mm = MarketMakerIt::start(bob_conf.conf, bob_conf.rpc_password, None).unwrap(); + + thread::sleep(Duration::from_secs(2)); + + let mut alice_conf = Mm2TestConf::seednode(ALICE_SEED, &json!([])); + alice_conf.conf["seednodes"] = json!([bob_mm.my_seed_addr()]); + alice_conf.conf["skip_startup_checks"] = json!(true); + let alice_mm = MarketMakerIt::start(alice_conf.conf, alice_conf.rpc_password, None).unwrap(); + + thread::sleep(Duration::from_secs(2)); + + // Self-address check for Bob + let response = block_on(peer_connection_healthcheck(&bob_mm, BOB_ADDRESS)); + assert_eq!(response["result"], json!(true)); + + // Check address of Alice + let response = block_on(peer_connection_healthcheck(&bob_mm, ALICE_ADDRESS)); + assert_eq!(response["result"], json!(true)); + + thread::sleep(Duration::from_secs(1)); + + // Self-address check for Alice + let response = block_on(peer_connection_healthcheck(&alice_mm, ALICE_ADDRESS)); + assert_eq!(response["result"], json!(true)); + + // Check address of Bob + let response = block_on(peer_connection_healthcheck(&alice_mm, BOB_ADDRESS)); + assert_eq!(response["result"], json!(true)); +} + #[cfg(all(feature = "run-device-tests", not(target_arch = "wasm32")))] mod trezor_tests { use coins::eth::{eth_coin_from_conf_and_request, gas_limit, EthCoin}; diff --git a/mm2src/mm2_test_helpers/src/for_tests.rs b/mm2src/mm2_test_helpers/src/for_tests.rs index df5a488cf9..196112cc0b 100644 --- a/mm2src/mm2_test_helpers/src/for_tests.rs +++ b/mm2src/mm2_test_helpers/src/for_tests.rs @@ -868,9 +868,7 @@ pub fn nft_dev_conf() -> Json { }) } -fn set_chain_id(conf: &mut Json, chain_id: u64) { - conf["chain_id"] = json!(chain_id); -} +fn set_chain_id(conf: &mut Json, chain_id: u64) { conf["chain_id"] = json!(chain_id); } pub fn eth_sepolia_conf() -> Json { json!({ @@ -1896,6 +1894,30 @@ pub async fn enable_qrc20( json::from_str(&electrum.1).unwrap() } +pub async fn peer_connection_healthcheck(mm: &MarketMakerIt, peer_address: &str) -> Json { + let response = mm + .rpc(&json!({ + "userpass": mm.userpass, + "method": "peer_connection_healthcheck", + "mmrpc": "2.0", + "params": { + "peer_address": peer_address + } + })) + .await + .unwrap(); + + assert_eq!( + response.0, + StatusCode::OK, + "RPC «peer_connection_healthcheck» failed with {} {}", + response.0, + response.1 + ); + + json::from_str(&response.1).unwrap() +} + /// Reads passphrase and userpass from .env file pub fn from_env_file(env: Vec) -> (Option, Option) { use regex::bytes::Regex; @@ -2973,7 +2995,10 @@ pub async fn enable_tendermint( tx_history: bool, ) -> Json { let ibc_requests: Vec<_> = ibc_assets.iter().map(|ticker| json!({ "ticker": ticker })).collect(); - let nodes: Vec = rpc_urls.iter().map(|u| json!({"url": u, "komodo_proxy": false })).collect(); + let nodes: Vec = rpc_urls + .iter() + .map(|u| json!({"url": u, "komodo_proxy": false })) + .collect(); let request = json!({ "userpass": mm.userpass, @@ -3010,7 +3035,10 @@ pub async fn enable_tendermint_without_balance( tx_history: bool, ) -> Json { let ibc_requests: Vec<_> = ibc_assets.iter().map(|ticker| json!({ "ticker": ticker })).collect(); - let nodes: Vec = rpc_urls.iter().map(|u| json!({"url": u, "komodo_proxy": false })).collect(); + let nodes: Vec = rpc_urls + .iter() + .map(|u| json!({"url": u, "komodo_proxy": false })) + .collect(); let request = json!({ "userpass": mm.userpass, From 122112aca622838585ef9aaaa8f9fc98ad1bca87 Mon Sep 17 00:00:00 2001 From: shamardy <39480341+shamardy@users.noreply.github.com> Date: Fri, 4 Oct 2024 13:46:04 +0300 Subject: [PATCH 24/29] chore(adex-cli): deprecate adex-cli (#2234) --- .github/workflows/adex-cli.yml | 37 ---------------------------------- mm2src/adex_cli/README.md | 3 +++ 2 files changed, 3 insertions(+), 37 deletions(-) delete mode 100644 .github/workflows/adex-cli.yml create mode 100644 mm2src/adex_cli/README.md diff --git a/.github/workflows/adex-cli.yml b/.github/workflows/adex-cli.yml deleted file mode 100644 index 32b62f04ed..0000000000 --- a/.github/workflows/adex-cli.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Adex CLI -on: [push] - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -env: - BRANCH_NAME: ${{ github.head_ref || github.ref_name }} - -jobs: - code-check: - name: Code Checks - timeout-minutes: 60 - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - steps: - - uses: actions/checkout@v3 - - - name: Cargo cache - uses: ./.github/actions/cargo-cache - - - name: Start checking code format and lint - continue-on-error: true - run: | - cargo fmt --manifest-path ./mm2src/adex_cli/Cargo.toml --all -- --check - cargo clippy --manifest-path ./mm2src/adex_cli/Cargo.toml --all-targets --all-features -- --D warnings - - - name: Start building - run: | - cargo build --manifest-path ./mm2src/adex_cli/Cargo.toml - - - name: Start testing - run: | - cargo test --manifest-path ./mm2src/adex_cli/Cargo.toml --no-fail-fast diff --git a/mm2src/adex_cli/README.md b/mm2src/adex_cli/README.md new file mode 100644 index 0000000000..6066b2c0bc --- /dev/null +++ b/mm2src/adex_cli/README.md @@ -0,0 +1,3 @@ +## ⚠️ Deprecated + +**The `adex-cli` tool is no longer maintained and has been deprecated.** \ No newline at end of file From 359cb5c83dcd6914225025d9831864d64f0e04c4 Mon Sep 17 00:00:00 2001 From: Alina Sharon <52405288+laruh@users.noreply.github.com> Date: Fri, 4 Oct 2024 17:56:10 +0700 Subject: [PATCH 25/29] fix(legacy-swap): taker failed spend maker payment marked as failed (#2199) This fixes the issue where taker doesn't wait for maker payment spend confirmation. Two new events were added: - "MakerPaymentSpendConfirmed": Success event, fired after "MakerPaymentSpent". - "MakerPaymentSpendConfirmFailed": Error event, means that the taker spend maker payment transaction confirmation was failed. --- .../for_tests/iris_nimda_rick_taker_swap.json | 8 +- ...yment_wait_confirm_failed_taker_saved.json | 2 +- .../recreate_maker_swap_taker_saved.json | 2 +- .../recreate_taker_swap_taker_expected.json | 4 +- ...nt_wait_confirm_failed_taker_expected.json | 4 +- mm2src/mm2_main/src/lp_swap/maker_swap.rs | 8 +- .../src/lp_swap/recreate_swap_data.rs | 2 + mm2src/mm2_main/src/lp_swap/taker_restart.rs | 3 +- mm2src/mm2_main/src/lp_swap/taker_swap.rs | 101 ++++++++++++++---- .../tests/docker_tests/swap_watcher_tests.rs | 2 + .../docker_tests/swaps_file_lock_tests.rs | 4 +- mm2src/mm2_test_helpers/src/for_tests.rs | 15 ++- 12 files changed, 115 insertions(+), 40 deletions(-) diff --git a/mm2src/mm2_main/src/for_tests/iris_nimda_rick_taker_swap.json b/mm2src/mm2_main/src/for_tests/iris_nimda_rick_taker_swap.json index b040229f93..93cab41558 100644 --- a/mm2src/mm2_main/src/for_tests/iris_nimda_rick_taker_swap.json +++ b/mm2src/mm2_main/src/for_tests/iris_nimda_rick_taker_swap.json @@ -145,12 +145,14 @@ "Started", "Negotiated", "TakerFeeSent", + "TakerPaymentInstructionsReceived", "MakerPaymentReceived", "MakerPaymentWaitConfirmStarted", "MakerPaymentValidatedAndConfirmed", "TakerPaymentSent", "TakerPaymentSpent", "MakerPaymentSpent", + "MakerPaymentSpendConfirmed", "Finished" ], "error_events": [ @@ -164,8 +166,12 @@ "TakerPaymentDataSendFailed", "TakerPaymentWaitForSpendFailed", "MakerPaymentSpendFailed", + "MakerPaymentSpendConfirmFailed", "TakerPaymentWaitRefundStarted", + "TakerPaymentRefundStarted", "TakerPaymentRefunded", - "TakerPaymentRefundFailed" + "TakerPaymentRefundedByWatcher", + "TakerPaymentRefundFailed", + "TakerPaymentRefundFinished" ] } \ No newline at end of file diff --git a/mm2src/mm2_main/src/for_tests/recreate_maker_swap_maker_payment_wait_confirm_failed_taker_saved.json b/mm2src/mm2_main/src/for_tests/recreate_maker_swap_maker_payment_wait_confirm_failed_taker_saved.json index 07ebe0d6b9..73261f641e 100644 --- a/mm2src/mm2_main/src/for_tests/recreate_maker_swap_maker_payment_wait_confirm_failed_taker_saved.json +++ b/mm2src/mm2_main/src/for_tests/recreate_maker_swap_maker_payment_wait_confirm_failed_taker_saved.json @@ -49,6 +49,6 @@ "taker_coin":"MORTY", "gui":"atomicDEX 0.5.1 iOS", "mm_version":"1b065636a", - "success_events":["Started","Negotiated","TakerFeeSent","TakerPaymentInstructionsReceived","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"], + "success_events":["Started","Negotiated","TakerFeeSent","TakerPaymentInstructionsReceived","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"], "error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"] } \ No newline at end of file diff --git a/mm2src/mm2_main/src/for_tests/recreate_maker_swap_taker_saved.json b/mm2src/mm2_main/src/for_tests/recreate_maker_swap_taker_saved.json index 8fe8622f74..a3158581c6 100644 --- a/mm2src/mm2_main/src/for_tests/recreate_maker_swap_taker_saved.json +++ b/mm2src/mm2_main/src/for_tests/recreate_maker_swap_taker_saved.json @@ -61,6 +61,6 @@ "taker_coin":"MORTY", "gui":"atomicDEX 0.5.1 iOS", "mm_version":"1b065636a", - "success_events":["Started","Negotiated","TakerFeeSent","TakerPaymentInstructionsReceived","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"], + "success_events":["Started","Negotiated","TakerFeeSent","TakerPaymentInstructionsReceived","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"], "error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"] } \ No newline at end of file diff --git a/mm2src/mm2_main/src/for_tests/recreate_taker_swap_taker_expected.json b/mm2src/mm2_main/src/for_tests/recreate_taker_swap_taker_expected.json index cfb9ab66f8..1c1cc39acf 100644 --- a/mm2src/mm2_main/src/for_tests/recreate_taker_swap_taker_expected.json +++ b/mm2src/mm2_main/src/for_tests/recreate_taker_swap_taker_expected.json @@ -57,6 +57,6 @@ "taker_coin":"MORTY", "gui":null, "mm_version":"", - "success_events":["Started","Negotiated","TakerFeeSent","TakerPaymentInstructionsReceived","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"], - "error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefundStarted","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed","TakerPaymentRefundFinished"] + "success_events":["Started","Negotiated","TakerFeeSent","TakerPaymentInstructionsReceived","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"], + "error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","MakerPaymentSpendConfirmFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefundStarted","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed","TakerPaymentRefundFinished"] } \ No newline at end of file diff --git a/mm2src/mm2_main/src/for_tests/recreate_taker_swap_taker_payment_wait_confirm_failed_taker_expected.json b/mm2src/mm2_main/src/for_tests/recreate_taker_swap_taker_payment_wait_confirm_failed_taker_expected.json index cb3146b2a3..e743669805 100644 --- a/mm2src/mm2_main/src/for_tests/recreate_taker_swap_taker_payment_wait_confirm_failed_taker_expected.json +++ b/mm2src/mm2_main/src/for_tests/recreate_taker_swap_taker_payment_wait_confirm_failed_taker_expected.json @@ -53,6 +53,6 @@ "taker_coin":"MORTY", "gui":null, "mm_version":"", - "success_events":["Started","Negotiated","TakerFeeSent","TakerPaymentInstructionsReceived","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"], - "error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefundStarted","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed","TakerPaymentRefundFinished"] + "success_events":["Started","Negotiated","TakerFeeSent","TakerPaymentInstructionsReceived","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"], + "error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","MakerPaymentSpendConfirmFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefundStarted","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed","TakerPaymentRefundFinished"] } \ No newline at end of file diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index 82e3809c6c..812e7c366a 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -1141,12 +1141,12 @@ impl MakerSwap { } async fn confirm_taker_payment_spend(&self) -> Result<(Option, Vec), String> { - // we should wait for only one confirmation to make sure our spend transaction is not failed - let confirmations = std::cmp::min(1, self.r().data.taker_payment_confirmations); + // We should wait for only one confirmation to make sure our spend transaction is not failed. + // However, we allow the user to use 0 confirmations if specified. let requires_nota = false; let confirm_taker_payment_spend_input = ConfirmPaymentInput { payment_tx: self.r().taker_payment_spend.clone().unwrap().tx_hex.0, - confirmations, + confirmations: std::cmp::min(1, self.r().data.taker_payment_confirmations), requires_nota, wait_until: self.wait_refund_until(), check_every: WAIT_CONFIRM_INTERVAL_SEC, @@ -1679,7 +1679,7 @@ impl MakerSwapEvent { }, MakerSwapEvent::TakerPaymentSpent(_) => "Taker payment spent...".to_owned(), MakerSwapEvent::TakerPaymentSpendFailed(_) => "Taker payment spend failed...".to_owned(), - MakerSwapEvent::TakerPaymentSpendConfirmStarted => "Taker payment send wait confirm started...".to_owned(), + MakerSwapEvent::TakerPaymentSpendConfirmStarted => "Taker payment spend confirm started...".to_owned(), MakerSwapEvent::TakerPaymentSpendConfirmed => "Taker payment spend confirmed...".to_owned(), MakerSwapEvent::TakerPaymentSpendConfirmFailed(_) => "Taker payment spend confirm failed...".to_owned(), MakerSwapEvent::MakerPaymentWaitRefundStarted { wait_until } => { diff --git a/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs b/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs index 0d139df62f..e4e430c71c 100644 --- a/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs +++ b/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs @@ -264,6 +264,8 @@ fn convert_taker_to_maker_events( | TakerSwapEvent::MakerPaymentWaitConfirmStarted | TakerSwapEvent::MakerPaymentValidatedAndConfirmed | TakerSwapEvent::MakerPaymentSpent(_) + | TakerSwapEvent::MakerPaymentSpendConfirmed + | TakerSwapEvent::MakerPaymentSpendConfirmFailed(_) | TakerSwapEvent::MakerPaymentSpentByWatcher(_) | TakerSwapEvent::MakerPaymentSpendFailed(_) // We don't know the reason at the moment, so we rely on the errors handling above. diff --git a/mm2src/mm2_main/src/lp_swap/taker_restart.rs b/mm2src/mm2_main/src/lp_swap/taker_restart.rs index 9ab1b48092..d934b6b11e 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_restart.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_restart.rs @@ -49,6 +49,7 @@ pub async fn get_command_based_on_maker_or_watcher_activity( Err(e) => ERR!("Error {} when trying to find taker payment spend", e), }, TakerSwapCommand::SpendMakerPayment => check_maker_payment_spend_and_add_event(ctx, swap, saved).await, + TakerSwapCommand::ConfirmMakerPaymentSpend => Ok(command), TakerSwapCommand::PrepareForTakerPaymentRefund | TakerSwapCommand::RefundTakerPayment => { #[cfg(not(any(test, feature = "run-docker-tests")))] { @@ -141,7 +142,7 @@ pub async fn check_maker_payment_spend_and_add_event( let new_swap = SavedSwap::Taker(saved); try_s!(new_swap.save_to_db(ctx).await); info!("{}", MAKER_PAYMENT_SPENT_BY_WATCHER_LOG); - Ok(TakerSwapCommand::Finish) + Ok(TakerSwapCommand::ConfirmMakerPaymentSpend) } pub async fn check_taker_payment_spend(swap: &TakerSwap) -> Result, String> { diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 1b72199244..3bedf837e4 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -44,7 +44,7 @@ use uuid::Uuid; const TAKER_PAYMENT_SPEND_SEARCH_INTERVAL: f64 = 10.; -pub const TAKER_SUCCESS_EVENTS: [&str; 11] = [ +pub const TAKER_SUCCESS_EVENTS: [&str; 12] = [ "Started", "Negotiated", "TakerFeeSent", @@ -55,10 +55,11 @@ pub const TAKER_SUCCESS_EVENTS: [&str; 11] = [ "TakerPaymentSent", "TakerPaymentSpent", "MakerPaymentSpent", + "MakerPaymentSpendConfirmed", "Finished", ]; -pub const TAKER_USING_WATCHERS_SUCCESS_EVENTS: [&str; 13] = [ +pub const TAKER_USING_WATCHERS_SUCCESS_EVENTS: [&str; 14] = [ "Started", "Negotiated", "TakerFeeSent", @@ -71,10 +72,11 @@ pub const TAKER_USING_WATCHERS_SUCCESS_EVENTS: [&str; 13] = [ "TakerPaymentSpent", "MakerPaymentSpent", "MakerPaymentSpentByWatcher", + "MakerPaymentSpendConfirmed", "Finished", ]; -pub const TAKER_ERROR_EVENTS: [&str; 16] = [ +pub const TAKER_ERROR_EVENTS: [&str; 17] = [ "StartFailed", "NegotiateFailed", "TakerFeeSendFailed", @@ -85,6 +87,7 @@ pub const TAKER_ERROR_EVENTS: [&str; 16] = [ "TakerPaymentDataSendFailed", "TakerPaymentWaitForSpendFailed", "MakerPaymentSpendFailed", + "MakerPaymentSpendConfirmFailed", "TakerPaymentWaitRefundStarted", "TakerPaymentRefundStarted", "TakerPaymentRefunded", @@ -178,8 +181,10 @@ impl TakerSavedEvent { TakerSwapEvent::TakerPaymentSpent(_) => Some(TakerSwapCommand::SpendMakerPayment), TakerSwapEvent::TakerPaymentWaitForSpendFailed(_) => Some(TakerSwapCommand::PrepareForTakerPaymentRefund), TakerSwapEvent::TakerPaymentWaitConfirmFailed(_) => Some(TakerSwapCommand::PrepareForTakerPaymentRefund), - TakerSwapEvent::MakerPaymentSpent(_) => Some(TakerSwapCommand::Finish), - TakerSwapEvent::MakerPaymentSpentByWatcher(_) => Some(TakerSwapCommand::Finish), + TakerSwapEvent::MakerPaymentSpent(_) => Some(TakerSwapCommand::ConfirmMakerPaymentSpend), + TakerSwapEvent::MakerPaymentSpendConfirmed => Some(TakerSwapCommand::Finish), + TakerSwapEvent::MakerPaymentSpendConfirmFailed(_) => Some(TakerSwapCommand::PrepareForTakerPaymentRefund), + TakerSwapEvent::MakerPaymentSpentByWatcher(_) => Some(TakerSwapCommand::ConfirmMakerPaymentSpend), TakerSwapEvent::MakerPaymentSpendFailed(_) => Some(TakerSwapCommand::PrepareForTakerPaymentRefund), TakerSwapEvent::TakerPaymentWaitRefundStarted { .. } => { Some(TakerSwapCommand::PrepareForTakerPaymentRefund) @@ -259,6 +264,9 @@ impl TakerSavedSwap { if !self.is_finished() { return false; }; + let mut maker_payment_spent = false; + let mut maker_payment_spent_by_watcher = false; + let mut maker_payment_spend_confirmed_failed = false; for event in self.events.iter() { match event.event { TakerSwapEvent::StartFailed(_) @@ -267,15 +275,26 @@ impl TakerSavedSwap { | TakerSwapEvent::MakerPaymentValidateFailed(_) | TakerSwapEvent::TakerPaymentRefunded(_) | TakerSwapEvent::TakerPaymentRefundedByWatcher(_) - | TakerSwapEvent::MakerPaymentSpent(_) - | TakerSwapEvent::MakerPaymentSpentByWatcher(_) + | TakerSwapEvent::MakerPaymentSpendConfirmed | TakerSwapEvent::MakerPaymentWaitConfirmFailed(_) => { return false; }, + TakerSwapEvent::MakerPaymentSpent(_) => { + maker_payment_spent = true; + }, + TakerSwapEvent::MakerPaymentSpentByWatcher(_) => { + maker_payment_spent_by_watcher = true; + }, + TakerSwapEvent::MakerPaymentSpendConfirmFailed(_) => { + maker_payment_spend_confirmed_failed = true; + }, _ => (), } } - true + // MakerPaymentSpent or MakerPaymentSpentByWatcher were the last success events but a new step `MakerPaymentSpendConfirmed` was added after them. + // For backward compatibility (old saved swaps) we need to check for MakerPaymentSpent or MakerPaymentSpentByWatcher + // and if there is no MakerPaymentSpendConfirmFailed. + maker_payment_spend_confirmed_failed || (!maker_payment_spent && !maker_payment_spent_by_watcher) } pub fn swap_data(&self) -> Result<&TakerSwapData, String> { @@ -554,6 +573,7 @@ pub struct TakerSwapMut { pub maker_payment: Option, pub taker_payment: Option, maker_payment_spend: Option, + maker_payment_spend_confirmed: bool, taker_payment_spend: Option, maker_payment_spend_preimage: Option>, taker_payment_refund_preimage: Option>, @@ -660,6 +680,8 @@ pub enum TakerSwapEvent { TakerPaymentSpent(TakerPaymentSpentData), TakerPaymentWaitForSpendFailed(SwapError), MakerPaymentSpent(TransactionIdentifier), + MakerPaymentSpendConfirmed, + MakerPaymentSpendConfirmFailed(SwapError), MakerPaymentSpentByWatcher(TransactionIdentifier), MakerPaymentSpendFailed(SwapError), TakerPaymentWaitRefundStarted { wait_until: u64 }, @@ -698,6 +720,8 @@ impl TakerSwapEvent { TakerSwapEvent::TakerPaymentSpent(_) => "Taker payment spent...".to_owned(), TakerSwapEvent::TakerPaymentWaitForSpendFailed(_) => "Taker payment wait for spend failed...".to_owned(), TakerSwapEvent::MakerPaymentSpent(_) => "Maker payment spent...".to_owned(), + TakerSwapEvent::MakerPaymentSpendConfirmed => "Maker payment spent confirmed...".to_owned(), + TakerSwapEvent::MakerPaymentSpendConfirmFailed(_) => "Maker payment spend confirm failed...".to_owned(), TakerSwapEvent::MakerPaymentSpentByWatcher(_) => "Maker payment spent by watcher...".to_owned(), TakerSwapEvent::MakerPaymentSpendFailed(_) => "Maker payment spend failed...".to_owned(), TakerSwapEvent::TakerPaymentWaitRefundStarted { wait_until } => { @@ -733,6 +757,7 @@ impl TakerSwapEvent { | TakerSwapEvent::TakerPaymentSent(_) | TakerSwapEvent::TakerPaymentSpent(_) | TakerSwapEvent::MakerPaymentSpent(_) + | TakerSwapEvent::MakerPaymentSpendConfirmed | TakerSwapEvent::MakerPaymentSpentByWatcher(_) | TakerSwapEvent::Finished ) @@ -751,6 +776,7 @@ pub enum TakerSwapCommand { SendTakerPayment, WaitForTakerPaymentSpend, SpendMakerPayment, + ConfirmMakerPaymentSpend, PrepareForTakerPaymentRefund, RefundTakerPayment, FinalizeTakerPaymentRefund, @@ -837,6 +863,8 @@ impl TakerSwap { }, TakerSwapEvent::TakerPaymentWaitForSpendFailed(err) => self.errors.lock().push(err), TakerSwapEvent::MakerPaymentSpent(tx) => self.w().maker_payment_spend = Some(tx), + TakerSwapEvent::MakerPaymentSpendConfirmed => self.w().maker_payment_spend_confirmed = true, + TakerSwapEvent::MakerPaymentSpendConfirmFailed(err) => self.errors.lock().push(err), TakerSwapEvent::MakerPaymentSpentByWatcher(tx) => self.w().maker_payment_spend = Some(tx), TakerSwapEvent::MakerPaymentSpendFailed(err) => self.errors.lock().push(err), TakerSwapEvent::TakerPaymentWaitRefundStarted { .. } => (), @@ -862,6 +890,7 @@ impl TakerSwap { TakerSwapCommand::SendTakerPayment => self.send_taker_payment().await, TakerSwapCommand::WaitForTakerPaymentSpend => self.wait_for_taker_payment_spend().await, TakerSwapCommand::SpendMakerPayment => self.spend_maker_payment().await, + TakerSwapCommand::ConfirmMakerPaymentSpend => self.confirm_maker_payment_spend().await, TakerSwapCommand::PrepareForTakerPaymentRefund => self.prepare_for_taker_payment_refund().await, TakerSwapCommand::RefundTakerPayment => self.refund_taker_payment().await, TakerSwapCommand::FinalizeTakerPaymentRefund => self.finalize_taker_payment_refund().await, @@ -907,6 +936,7 @@ impl TakerSwap { other_taker_coin_htlc_pub: H264::default(), taker_fee: None, maker_payment: None, + maker_payment_spend_confirmed: false, taker_payment: None, taker_payment_spend: None, maker_payment_spend_preimage: None, @@ -1829,9 +1859,38 @@ impl TakerSwap { tx_hash, }; - Ok((Some(TakerSwapCommand::Finish), vec![TakerSwapEvent::MakerPaymentSpent( - tx_ident, - )])) + Ok((Some(TakerSwapCommand::ConfirmMakerPaymentSpend), vec![ + TakerSwapEvent::MakerPaymentSpent(tx_ident), + ])) + } + + async fn confirm_maker_payment_spend(&self) -> Result<(Option, Vec), String> { + // We should wait for only one confirmation to ensure our spend transaction does not fail. + // However, we allow the user to use 0 confirmations if specified. + let confirm_maker_payment_spend_input = ConfirmPaymentInput { + payment_tx: self.r().maker_payment_spend.clone().unwrap().tx_hex.0, + confirmations: std::cmp::min(1, self.r().data.maker_payment_confirmations), + requires_nota: false, + wait_until: self.wait_refund_until(), + check_every: WAIT_CONFIRM_INTERVAL_SEC, + }; + let wait_fut = self + .maker_coin + .wait_for_confirmations(confirm_maker_payment_spend_input); + if let Err(err) = wait_fut.compat().await { + return Ok((Some(TakerSwapCommand::PrepareForTakerPaymentRefund), vec![ + TakerSwapEvent::MakerPaymentSpendConfirmFailed( + ERRL!("!wait for maker payment spend confirmations: {}", err).into(), + ), + TakerSwapEvent::TakerPaymentWaitRefundStarted { + wait_until: self.wait_refund_until(), + }, + ])); + } + info!("Maker payment spend confirmed"); + Ok((Some(TakerSwapCommand::Finish), vec![ + TakerSwapEvent::MakerPaymentSpendConfirmed, + ])) } async fn prepare_for_taker_payment_refund( @@ -2708,7 +2767,7 @@ mod taker_swap_tests { fn test_recover_funds_taker_swap_maker_payment_spend_errored() { let ctx = mm_ctx_with_iguana(PASSPHRASE); - let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.12596566232185483","maker_coin":"KMD","maker_coin_start_block":1458035,"maker_payment_confirmations":1,"maker_payment_wait":1564053079,"my_persistent_pub":"0326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0a","started_at":1564050479,"taker_amount":"50.000000000000001504212457800000","taker_coin":"DOGE","taker_coin_start_block":2823448,"taker_payment_confirmations":1,"taker_payment_lock":1564058279,"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"},"type":"Started"},"timestamp":1564050480269},{"event":{"data":{"maker_payment_locktime":1564066080,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"3669eb83a007a3c507448d79f45a9f06ec2f36a8"},"type":"Negotiated"},"timestamp":1564050540991},{"event":{"data":{"tx_hash":"bdde828b492d6d1cc25cd2322fd592dafd722fcc7d8b0fedce4d3bb4a1a8c8ff","tx_hex":"0100000002c7efa995c8b7be0a8b6c2d526c6c444c1634d65584e9ee89904e9d8675eac88c010000006a473044022051f34d5e3b7d0b9098d5e35333f3550f9cb9e57df83d5e4635b7a8d2986d6d5602200288c98da05de6950e01229a637110a1800ba643e75cfec59d4eb1021ad9b40801210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffffae6c233989efa7c7d2aa6534adc96078917ff395b7f09f734a147b2f44ade164000000006a4730440220393a784c2da74d0e2a28ec4f7df6c8f9d8b2af6ae6957f1e68346d744223a8fd02201b7a96954ac06815a43a6c7668d829ae9cbb5de76fa77189ddfd9e3038df662c01210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffff02115f5800000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac41a84641020000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac6d84395d"},"type":"TakerFeeSent"},"timestamp":1564050545296},{"event":{"data":{"tx_hash":"0a0f11fa82802c2c30862c50ab2162185dae8de7f7235f32c506f814c142b382","tx_hex":"0400008085202f8902ace337db2dd4c56b0697f58fb8cfb6bd1cd6f469d925fc0376d1dcfb7581bf82000000006b483045022100d1f95be235c5c8880f5d703ace287e2768548792c58c5dbd27f5578881b30ea70220030596106e21c7e0057ee0dab283f9a1fe273f15208cba80870c447bd559ef0d0121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff9f339752567c404427fd77f2b35cecdb4c21489edc64e25e729fdb281785e423000000006a47304402203179e95877dbc107123a417f1e648e3ff13d384890f1e4a67b6dd5087235152e0220102a8ab799fadb26b5d89ceb9c7bc721a7e0c2a0d0d7e46bbe0cf3d130010d430121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff025635c0000000000017a91480a95d366d65e34a465ab17b0c9eb1d5a33bae08876cbfce05000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac8d7c395d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1564050588176},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1564050588178},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1564050693585},{"event":{"data":{"tx_hash":"539cb6dbdc25465bbccc575554f05d1bb04c70efce4316e41194e747375c3659","tx_hex":"0100000001ffc8a8a1b43b4dceed0f8b7dcc2f72fdda92d52f32d25cc21c6d2d498b82debd010000006a47304402203967b7f9f5532fa47116585c7d1bcba51861ea2059cca00409f34660db18e33a0220640991911852533a12fdfeb039fb9c8ca2c45482c6993bd84636af3670d49c1501210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffff0200f2052a0100000017a914f2fa08ae416b576779ae5da975e5442663215fce87415173f9000000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac0585395d"},"type":"TakerPaymentSent"},"timestamp":1564050695611},{"event":{"data":{"secret":"1b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093","transaction":{"tx_hash":"cc5af1cf68d246419fee49c3d74c0cd173599d115b86efe274368a614951bc47","tx_hex":"010000000159365c3747e79411e41643ceef704cb01b5df0545557ccbc5b4625dcdbb69c5300000000d747304402200e78e27d2f1c18676f98ca3dfa4e4a9eeaa8209b55f57b4dd5d9e1abdf034cfa0220623b5c22b62234cec230342aa306c497e43494b44ec2425b84e236b1bf01257001201b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093004c6b6304a7a2395db175210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0aac6782012088a9143669eb83a007a3c507448d79f45a9f06ec2f36a88821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68ffffffff01008d380c010000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac8c77395d"}},"type":"TakerPaymentSpent"},"timestamp":1564051092890},{"event":{"data":{"error":"lp_swap:1981] utxo:891] rpc_clients:738] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"67\", method: \"blockchain.transaction.broadcast\", params: [String(\"0400008085202f890182b342c114f806c5325f23f7e78dae5d186221ab502c86302c2c8082fa110f0a00000000d7473044022035791ea5548f87484065c9e1f0bdca9ebc699f2c7f51182c84f360102e32dc3d02200612ed53bca52d9c2568437f087598531534badf26229fe0f652ea72ddf03ca501201b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093004c6b630420c1395db17521031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac6782012088a9143669eb83a007a3c507448d79f45a9f06ec2f36a888210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0aac68ffffffff01460ec000000000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac967e395d000000000000000000000000000000\")] }, error: Transport(\"rpc_clients:668] All electrums are currently disconnected\") }"},"type":"MakerPaymentSpendFailed"},"timestamp":1564051092897},{"event":{"type":"Finished"},"timestamp":1564051092900}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"}"#; + let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","MakerPaymentSpendConfirmFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.12596566232185483","maker_coin":"KMD","maker_coin_start_block":1458035,"maker_payment_confirmations":1,"maker_payment_wait":1564053079,"my_persistent_pub":"0326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0a","started_at":1564050479,"taker_amount":"50.000000000000001504212457800000","taker_coin":"DOGE","taker_coin_start_block":2823448,"taker_payment_confirmations":1,"taker_payment_lock":1564058279,"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"},"type":"Started"},"timestamp":1564050480269},{"event":{"data":{"maker_payment_locktime":1564066080,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"3669eb83a007a3c507448d79f45a9f06ec2f36a8"},"type":"Negotiated"},"timestamp":1564050540991},{"event":{"data":{"tx_hash":"bdde828b492d6d1cc25cd2322fd592dafd722fcc7d8b0fedce4d3bb4a1a8c8ff","tx_hex":"0100000002c7efa995c8b7be0a8b6c2d526c6c444c1634d65584e9ee89904e9d8675eac88c010000006a473044022051f34d5e3b7d0b9098d5e35333f3550f9cb9e57df83d5e4635b7a8d2986d6d5602200288c98da05de6950e01229a637110a1800ba643e75cfec59d4eb1021ad9b40801210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffffae6c233989efa7c7d2aa6534adc96078917ff395b7f09f734a147b2f44ade164000000006a4730440220393a784c2da74d0e2a28ec4f7df6c8f9d8b2af6ae6957f1e68346d744223a8fd02201b7a96954ac06815a43a6c7668d829ae9cbb5de76fa77189ddfd9e3038df662c01210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffff02115f5800000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac41a84641020000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac6d84395d"},"type":"TakerFeeSent"},"timestamp":1564050545296},{"event":{"data":{"tx_hash":"0a0f11fa82802c2c30862c50ab2162185dae8de7f7235f32c506f814c142b382","tx_hex":"0400008085202f8902ace337db2dd4c56b0697f58fb8cfb6bd1cd6f469d925fc0376d1dcfb7581bf82000000006b483045022100d1f95be235c5c8880f5d703ace287e2768548792c58c5dbd27f5578881b30ea70220030596106e21c7e0057ee0dab283f9a1fe273f15208cba80870c447bd559ef0d0121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff9f339752567c404427fd77f2b35cecdb4c21489edc64e25e729fdb281785e423000000006a47304402203179e95877dbc107123a417f1e648e3ff13d384890f1e4a67b6dd5087235152e0220102a8ab799fadb26b5d89ceb9c7bc721a7e0c2a0d0d7e46bbe0cf3d130010d430121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff025635c0000000000017a91480a95d366d65e34a465ab17b0c9eb1d5a33bae08876cbfce05000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac8d7c395d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1564050588176},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1564050588178},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1564050693585},{"event":{"data":{"tx_hash":"539cb6dbdc25465bbccc575554f05d1bb04c70efce4316e41194e747375c3659","tx_hex":"0100000001ffc8a8a1b43b4dceed0f8b7dcc2f72fdda92d52f32d25cc21c6d2d498b82debd010000006a47304402203967b7f9f5532fa47116585c7d1bcba51861ea2059cca00409f34660db18e33a0220640991911852533a12fdfeb039fb9c8ca2c45482c6993bd84636af3670d49c1501210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffff0200f2052a0100000017a914f2fa08ae416b576779ae5da975e5442663215fce87415173f9000000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac0585395d"},"type":"TakerPaymentSent"},"timestamp":1564050695611},{"event":{"data":{"secret":"1b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093","transaction":{"tx_hash":"cc5af1cf68d246419fee49c3d74c0cd173599d115b86efe274368a614951bc47","tx_hex":"010000000159365c3747e79411e41643ceef704cb01b5df0545557ccbc5b4625dcdbb69c5300000000d747304402200e78e27d2f1c18676f98ca3dfa4e4a9eeaa8209b55f57b4dd5d9e1abdf034cfa0220623b5c22b62234cec230342aa306c497e43494b44ec2425b84e236b1bf01257001201b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093004c6b6304a7a2395db175210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0aac6782012088a9143669eb83a007a3c507448d79f45a9f06ec2f36a88821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68ffffffff01008d380c010000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac8c77395d"}},"type":"TakerPaymentSpent"},"timestamp":1564051092890},{"event":{"data":{"error":"lp_swap:1981] utxo:891] rpc_clients:738] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"67\", method: \"blockchain.transaction.broadcast\", params: [String(\"0400008085202f890182b342c114f806c5325f23f7e78dae5d186221ab502c86302c2c8082fa110f0a00000000d7473044022035791ea5548f87484065c9e1f0bdca9ebc699f2c7f51182c84f360102e32dc3d02200612ed53bca52d9c2568437f087598531534badf26229fe0f652ea72ddf03ca501201b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093004c6b630420c1395db17521031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac6782012088a9143669eb83a007a3c507448d79f45a9f06ec2f36a888210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0aac68ffffffff01460ec000000000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac967e395d000000000000000000000000000000\")] }, error: Transport(\"rpc_clients:668] All electrums are currently disconnected\") }"},"type":"MakerPaymentSpendFailed"},"timestamp":1564051092897},{"event":{"type":"Finished"},"timestamp":1564051092900}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"}"#; let taker_saved_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -2744,7 +2803,7 @@ mod taker_swap_tests { fn test_recover_funds_taker_swap_taker_payment_errored_but_sent_not_spent() { let ctx = mm_ctx_with_iguana(PASSPHRASE); - let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"3.54932734","maker_coin":"KMD","maker_coin_start_block":1452970,"maker_payment_confirmations":1,"maker_payment_wait":1563746537,"my_persistent_pub":"03101ace6b08605b9424b0582b5cce044b70a3c8d8d10cb2965e039b0967ae92b9","started_at":1563743937,"taker_amount":"0.02004833998671660000000000","taker_coin":"ETH","taker_coin_start_block":8196380,"taker_payment_confirmations":1,"taker_payment_lock":1563751737,"uuid":"3447b727-fe93-4357-8e5a-8cf2699b7e86"},"type":"Started"},"timestamp":1563743937741},{"event":{"data":{"maker_payment_locktime":1563759539,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"432c8272ac59b47dea2d299b5cf1ee64ea1917b9"},"type":"Negotiated"},"timestamp":1563744003530},{"event":{"data":{"tx_hash":"a59203eb2328827de00bed699a29389792906e4f39fdea145eb40dc6b3821bd6","tx_hex":"f8690284ee6b280082520894d8997941dd1346e9231118d5685d866294f59e5b865af3107a4000801ca0743d2b7c9fad65805d882179062012261be328d7628ae12ee08eff8d7657d993a07eecbd051f49d35279416778faa4664962726d516ce65e18755c9b9406a9c2fd"},"type":"TakerFeeSent"},"timestamp":1563744020598},{"event":{"data":{"tx_hash":"0cf4acbcefde53645851c5c6053ea61fe0cbb5f828a906d69eb809e0b071a03b","tx_hex":"0400008085202f89025d5ae3e8c87418c9b735f8f2f7d29e26820c33c9f30d53f2d31f8b99ea9b1490010000006a47304402201185c06ca575261c539b287175751b7de642eb7466c59128639a19b4c2dd2f9b02201c8c4167d581864bedd4d1deb5596472e6e3ce29fe9e7996907a7b59c905d5490121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff06dbf9971c8dfd4a0c8c49f4f15c51de59ba13b2efa702682e26869843af9a87000000006a473044022012b47c12c7f6ad7d8b778fc4b5dcfd56a39325daf302f56e7b84753ba5216cfa022076bf571cf9e20facf70d2f134e8ed2de67aa08581a27ff3128bf93a9b594ac770121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff02fed727150000000017a914d5268b31131a652f9b6ddf57db62f02285cdfad1874e1d7835000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac37cf345d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563744071778},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563744071781},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563744118073},{"event":{"data":{"error":"lp_swap:1888] eth:654] RPC error: Error { code: ServerError(-32010), message: \"Transaction with the same hash was already imported.\", data: None }"},"type":"TakerPaymentTransactionFailed"},"timestamp":1563744118577},{"event":{"type":"Finished"},"timestamp":1563744118580}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"uuid":"3447b727-fe93-4357-8e5a-8cf2699b7e86"}"#; + let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","MakerPaymentSpendConfirmFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"3.54932734","maker_coin":"KMD","maker_coin_start_block":1452970,"maker_payment_confirmations":1,"maker_payment_wait":1563746537,"my_persistent_pub":"03101ace6b08605b9424b0582b5cce044b70a3c8d8d10cb2965e039b0967ae92b9","started_at":1563743937,"taker_amount":"0.02004833998671660000000000","taker_coin":"ETH","taker_coin_start_block":8196380,"taker_payment_confirmations":1,"taker_payment_lock":1563751737,"uuid":"3447b727-fe93-4357-8e5a-8cf2699b7e86"},"type":"Started"},"timestamp":1563743937741},{"event":{"data":{"maker_payment_locktime":1563759539,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"432c8272ac59b47dea2d299b5cf1ee64ea1917b9"},"type":"Negotiated"},"timestamp":1563744003530},{"event":{"data":{"tx_hash":"a59203eb2328827de00bed699a29389792906e4f39fdea145eb40dc6b3821bd6","tx_hex":"f8690284ee6b280082520894d8997941dd1346e9231118d5685d866294f59e5b865af3107a4000801ca0743d2b7c9fad65805d882179062012261be328d7628ae12ee08eff8d7657d993a07eecbd051f49d35279416778faa4664962726d516ce65e18755c9b9406a9c2fd"},"type":"TakerFeeSent"},"timestamp":1563744020598},{"event":{"data":{"tx_hash":"0cf4acbcefde53645851c5c6053ea61fe0cbb5f828a906d69eb809e0b071a03b","tx_hex":"0400008085202f89025d5ae3e8c87418c9b735f8f2f7d29e26820c33c9f30d53f2d31f8b99ea9b1490010000006a47304402201185c06ca575261c539b287175751b7de642eb7466c59128639a19b4c2dd2f9b02201c8c4167d581864bedd4d1deb5596472e6e3ce29fe9e7996907a7b59c905d5490121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff06dbf9971c8dfd4a0c8c49f4f15c51de59ba13b2efa702682e26869843af9a87000000006a473044022012b47c12c7f6ad7d8b778fc4b5dcfd56a39325daf302f56e7b84753ba5216cfa022076bf571cf9e20facf70d2f134e8ed2de67aa08581a27ff3128bf93a9b594ac770121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff02fed727150000000017a914d5268b31131a652f9b6ddf57db62f02285cdfad1874e1d7835000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac37cf345d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563744071778},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563744071781},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563744118073},{"event":{"data":{"error":"lp_swap:1888] eth:654] RPC error: Error { code: ServerError(-32010), message: \"Transaction with the same hash was already imported.\", data: None }"},"type":"TakerPaymentTransactionFailed"},"timestamp":1563744118577},{"event":{"type":"Finished"},"timestamp":1563744118580}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"uuid":"3447b727-fe93-4357-8e5a-8cf2699b7e86"}"#; let taker_saved_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -2794,7 +2853,7 @@ mod taker_swap_tests { fn test_recover_funds_taker_swap_taker_payment_errored_but_sent_and_spent_by_maker() { let ctx = mm_ctx_with_iguana(PASSPHRASE); - let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"3.54932734","maker_coin":"KMD","maker_coin_start_block":1452970,"maker_payment_confirmations":1,"maker_payment_wait":1563746537,"my_persistent_pub":"03101ace6b08605b9424b0582b5cce044b70a3c8d8d10cb2965e039b0967ae92b9","started_at":1563743937,"taker_amount":"0.02004833998671660000000000","taker_coin":"ETH","taker_coin_start_block":8196380,"taker_payment_confirmations":1,"taker_payment_lock":1563751737,"uuid":"3447b727-fe93-4357-8e5a-8cf2699b7e86"},"type":"Started"},"timestamp":1563743937741},{"event":{"data":{"maker_payment_locktime":1563759539,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"432c8272ac59b47dea2d299b5cf1ee64ea1917b9"},"type":"Negotiated"},"timestamp":1563744003530},{"event":{"data":{"tx_hash":"a59203eb2328827de00bed699a29389792906e4f39fdea145eb40dc6b3821bd6","tx_hex":"f8690284ee6b280082520894d8997941dd1346e9231118d5685d866294f59e5b865af3107a4000801ca0743d2b7c9fad65805d882179062012261be328d7628ae12ee08eff8d7657d993a07eecbd051f49d35279416778faa4664962726d516ce65e18755c9b9406a9c2fd"},"type":"TakerFeeSent"},"timestamp":1563744020598},{"event":{"data":{"tx_hash":"0cf4acbcefde53645851c5c6053ea61fe0cbb5f828a906d69eb809e0b071a03b","tx_hex":"0400008085202f89025d5ae3e8c87418c9b735f8f2f7d29e26820c33c9f30d53f2d31f8b99ea9b1490010000006a47304402201185c06ca575261c539b287175751b7de642eb7466c59128639a19b4c2dd2f9b02201c8c4167d581864bedd4d1deb5596472e6e3ce29fe9e7996907a7b59c905d5490121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff06dbf9971c8dfd4a0c8c49f4f15c51de59ba13b2efa702682e26869843af9a87000000006a473044022012b47c12c7f6ad7d8b778fc4b5dcfd56a39325daf302f56e7b84753ba5216cfa022076bf571cf9e20facf70d2f134e8ed2de67aa08581a27ff3128bf93a9b594ac770121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff02fed727150000000017a914d5268b31131a652f9b6ddf57db62f02285cdfad1874e1d7835000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac37cf345d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563744071778},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563744071781},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563744118073},{"event":{"data":{"error":"lp_swap:1888] eth:654] RPC error: Error { code: ServerError(-32010), message: \"Transaction with the same hash was already imported.\", data: None }"},"type":"TakerPaymentTransactionFailed"},"timestamp":1563744118577},{"event":{"type":"Finished"},"timestamp":1563744118580}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"uuid":"3447b727-fe93-4357-8e5a-8cf2699b7e86"}"#; + let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"3.54932734","maker_coin":"KMD","maker_coin_start_block":1452970,"maker_payment_confirmations":1,"maker_payment_wait":1563746537,"my_persistent_pub":"03101ace6b08605b9424b0582b5cce044b70a3c8d8d10cb2965e039b0967ae92b9","started_at":1563743937,"taker_amount":"0.02004833998671660000000000","taker_coin":"ETH","taker_coin_start_block":8196380,"taker_payment_confirmations":1,"taker_payment_lock":1563751737,"uuid":"3447b727-fe93-4357-8e5a-8cf2699b7e86"},"type":"Started"},"timestamp":1563743937741},{"event":{"data":{"maker_payment_locktime":1563759539,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"432c8272ac59b47dea2d299b5cf1ee64ea1917b9"},"type":"Negotiated"},"timestamp":1563744003530},{"event":{"data":{"tx_hash":"a59203eb2328827de00bed699a29389792906e4f39fdea145eb40dc6b3821bd6","tx_hex":"f8690284ee6b280082520894d8997941dd1346e9231118d5685d866294f59e5b865af3107a4000801ca0743d2b7c9fad65805d882179062012261be328d7628ae12ee08eff8d7657d993a07eecbd051f49d35279416778faa4664962726d516ce65e18755c9b9406a9c2fd"},"type":"TakerFeeSent"},"timestamp":1563744020598},{"event":{"data":{"tx_hash":"0cf4acbcefde53645851c5c6053ea61fe0cbb5f828a906d69eb809e0b071a03b","tx_hex":"0400008085202f89025d5ae3e8c87418c9b735f8f2f7d29e26820c33c9f30d53f2d31f8b99ea9b1490010000006a47304402201185c06ca575261c539b287175751b7de642eb7466c59128639a19b4c2dd2f9b02201c8c4167d581864bedd4d1deb5596472e6e3ce29fe9e7996907a7b59c905d5490121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff06dbf9971c8dfd4a0c8c49f4f15c51de59ba13b2efa702682e26869843af9a87000000006a473044022012b47c12c7f6ad7d8b778fc4b5dcfd56a39325daf302f56e7b84753ba5216cfa022076bf571cf9e20facf70d2f134e8ed2de67aa08581a27ff3128bf93a9b594ac770121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff02fed727150000000017a914d5268b31131a652f9b6ddf57db62f02285cdfad1874e1d7835000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac37cf345d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563744071778},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563744071781},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563744118073},{"event":{"data":{"error":"lp_swap:1888] eth:654] RPC error: Error { code: ServerError(-32010), message: \"Transaction with the same hash was already imported.\", data: None }"},"type":"TakerPaymentTransactionFailed"},"timestamp":1563744118577},{"event":{"type":"Finished"},"timestamp":1563744118580}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"uuid":"3447b727-fe93-4357-8e5a-8cf2699b7e86"}"#; let taker_saved_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -2847,7 +2906,7 @@ mod taker_swap_tests { fn test_recover_funds_taker_swap_taker_payment_refund_failed_not_spent() { let ctx = mm_ctx_with_iguana(PASSPHRASE); - let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.58610590","maker_coin":"KMD","maker_coin_start_block":1450923,"maker_payment_confirmations":1,"maker_payment_wait":1563623475,"my_persistent_pub":"02713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91","started_at":1563620875,"taker_amount":"0.0077700000552410000000000","taker_coin":"LTC","taker_coin_start_block":1670837,"taker_payment_confirmations":1,"taker_payment_lock":1563628675,"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"},"type":"Started"},"timestamp":1563620875766},{"event":{"data":{"maker_payment_locktime":1563636475,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"7ed38daab6085c1a1e4426e61dc87a3c2c081a95"},"type":"Negotiated"},"timestamp":1563620955014},{"event":{"data":{"tx_hash":"6740136eaaa615d9d231969e3a9599d0fc59e53989237a8d31cd6fc86c160013","tx_hex":"0100000001a2586ea8294cedc55741bef625ba72c646399903391a7f6c604a58c6263135f2000000006b4830450221009c78c8ba4a7accab6b09f9a95da5bc59c81f4fc1e60b288ec3c5462b4d02ef01022056b63be1629cf17751d3cc5ffec51bcb1d7f9396e9ce9ca254d0f34104f7263a012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0210270000000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac78aa1900000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac5bf6325d"},"type":"TakerFeeSent"},"timestamp":1563620958220},{"event":{"data":{"tx_hash":"d0f6e664cea9d89fe7b5cf8005fdca070d1ab1d05a482aaef95c08cdaecddf0a","tx_hex":"0400008085202f89019f1cbda354342cdf982046b331bbd3791f53b692efc6e4becc36be495b2977d9000000006b483045022100fa9d4557394141f6a8b9bfb8cd594a521fd8bcd1965dbf8bc4e04abc849ac66e0220589f521814c10a7561abfd5e432f7a2ee60d4875fe4604618af3207dae531ac00121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff029e537e030000000017a9145534898009f1467191065f6890b96914b39a1c018791857702000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac72ee325d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563620999307},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563620999310},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563621244153},{"event":{"data":{"tx_hash":"1e883eb2f3991e84ba27f53651f89b7dda708678a5b9813d043577f222b9ca30","tx_hex":"01000000011300166cc86fcd318d7a238939e559fcd099953a9e9631d2d915a6aa6e134067010000006a47304402206781d5f2db2ff13d2ec7e266f774ea5630cc2dba4019e18e9716131b8b026051022006ebb33857b6d180f13aa6be2fc532f9734abde9d00ae14757e7d7ba3741c08c012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0228db0b000000000017a91483818667161bf94adda3964a81a231cbf6f5338187b0480c00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac7cf7325d"},"type":"TakerPaymentSent"},"timestamp":1563621246370},{"event":{"data":{"error":"utxo:1145] rpc_clients:782] Waited too long until 1563628675 for output TransactionOutput { value: 777000, script_pubkey: a91483818667161bf94adda3964a81a231cbf6f5338187 } to be spent "},"type":"TakerPaymentWaitForSpendFailed"},"timestamp":1563638060370},{"event":{"data":{"error":"lp_swap:2025] utxo:938] rpc_clients:719] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"9\", method: \"blockchain.transaction.broadcast\", params: [String(\"010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d\")] }, error: Response(Object({\"code\": Number(1), \"message\": String(\"the transaction was rejected by network rules.\\n\\nMissing inputs\\n[010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d]\")})) }"},"type":"TakerPaymentRefundFailed"},"timestamp":1563638060583},{"event":{"type":"Finished"},"timestamp":1563638060585}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"}"#; + let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","MakerPaymentSpendConfirmFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.58610590","maker_coin":"KMD","maker_coin_start_block":1450923,"maker_payment_confirmations":1,"maker_payment_wait":1563623475,"my_persistent_pub":"02713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91","started_at":1563620875,"taker_amount":"0.0077700000552410000000000","taker_coin":"LTC","taker_coin_start_block":1670837,"taker_payment_confirmations":1,"taker_payment_lock":1563628675,"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"},"type":"Started"},"timestamp":1563620875766},{"event":{"data":{"maker_payment_locktime":1563636475,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"7ed38daab6085c1a1e4426e61dc87a3c2c081a95"},"type":"Negotiated"},"timestamp":1563620955014},{"event":{"data":{"tx_hash":"6740136eaaa615d9d231969e3a9599d0fc59e53989237a8d31cd6fc86c160013","tx_hex":"0100000001a2586ea8294cedc55741bef625ba72c646399903391a7f6c604a58c6263135f2000000006b4830450221009c78c8ba4a7accab6b09f9a95da5bc59c81f4fc1e60b288ec3c5462b4d02ef01022056b63be1629cf17751d3cc5ffec51bcb1d7f9396e9ce9ca254d0f34104f7263a012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0210270000000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac78aa1900000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac5bf6325d"},"type":"TakerFeeSent"},"timestamp":1563620958220},{"event":{"data":{"tx_hash":"d0f6e664cea9d89fe7b5cf8005fdca070d1ab1d05a482aaef95c08cdaecddf0a","tx_hex":"0400008085202f89019f1cbda354342cdf982046b331bbd3791f53b692efc6e4becc36be495b2977d9000000006b483045022100fa9d4557394141f6a8b9bfb8cd594a521fd8bcd1965dbf8bc4e04abc849ac66e0220589f521814c10a7561abfd5e432f7a2ee60d4875fe4604618af3207dae531ac00121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff029e537e030000000017a9145534898009f1467191065f6890b96914b39a1c018791857702000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac72ee325d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563620999307},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563620999310},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563621244153},{"event":{"data":{"tx_hash":"1e883eb2f3991e84ba27f53651f89b7dda708678a5b9813d043577f222b9ca30","tx_hex":"01000000011300166cc86fcd318d7a238939e559fcd099953a9e9631d2d915a6aa6e134067010000006a47304402206781d5f2db2ff13d2ec7e266f774ea5630cc2dba4019e18e9716131b8b026051022006ebb33857b6d180f13aa6be2fc532f9734abde9d00ae14757e7d7ba3741c08c012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0228db0b000000000017a91483818667161bf94adda3964a81a231cbf6f5338187b0480c00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac7cf7325d"},"type":"TakerPaymentSent"},"timestamp":1563621246370},{"event":{"data":{"error":"utxo:1145] rpc_clients:782] Waited too long until 1563628675 for output TransactionOutput { value: 777000, script_pubkey: a91483818667161bf94adda3964a81a231cbf6f5338187 } to be spent "},"type":"TakerPaymentWaitForSpendFailed"},"timestamp":1563638060370},{"event":{"data":{"error":"lp_swap:2025] utxo:938] rpc_clients:719] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"9\", method: \"blockchain.transaction.broadcast\", params: [String(\"010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d\")] }, error: Response(Object({\"code\": Number(1), \"message\": String(\"the transaction was rejected by network rules.\\n\\nMissing inputs\\n[010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d]\")})) }"},"type":"TakerPaymentRefundFailed"},"timestamp":1563638060583},{"event":{"type":"Finished"},"timestamp":1563638060585}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"}"#; let taker_saved_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -2890,7 +2949,7 @@ mod taker_swap_tests { fn test_recover_funds_taker_swap_taker_payment_refund_failed_not_spent_too_early_to_refund() { let ctx = mm_ctx_with_iguana(PASSPHRASE); - let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.58610590","maker_coin":"KMD","maker_coin_start_block":1450923,"maker_payment_confirmations":1,"maker_payment_wait":1563623475,"my_persistent_pub":"02713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91","started_at":1563620875,"taker_amount":"0.0077700000552410000000000","taker_coin":"LTC","taker_coin_start_block":1670837,"taker_payment_confirmations":1,"taker_payment_lock":1563628675,"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"},"type":"Started"},"timestamp":1563620875766},{"event":{"data":{"maker_payment_locktime":1563636475,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"7ed38daab6085c1a1e4426e61dc87a3c2c081a95"},"type":"Negotiated"},"timestamp":1563620955014},{"event":{"data":{"tx_hash":"6740136eaaa615d9d231969e3a9599d0fc59e53989237a8d31cd6fc86c160013","tx_hex":"0100000001a2586ea8294cedc55741bef625ba72c646399903391a7f6c604a58c6263135f2000000006b4830450221009c78c8ba4a7accab6b09f9a95da5bc59c81f4fc1e60b288ec3c5462b4d02ef01022056b63be1629cf17751d3cc5ffec51bcb1d7f9396e9ce9ca254d0f34104f7263a012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0210270000000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac78aa1900000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac5bf6325d"},"type":"TakerFeeSent"},"timestamp":1563620958220},{"event":{"data":{"tx_hash":"d0f6e664cea9d89fe7b5cf8005fdca070d1ab1d05a482aaef95c08cdaecddf0a","tx_hex":"0400008085202f89019f1cbda354342cdf982046b331bbd3791f53b692efc6e4becc36be495b2977d9000000006b483045022100fa9d4557394141f6a8b9bfb8cd594a521fd8bcd1965dbf8bc4e04abc849ac66e0220589f521814c10a7561abfd5e432f7a2ee60d4875fe4604618af3207dae531ac00121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff029e537e030000000017a9145534898009f1467191065f6890b96914b39a1c018791857702000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac72ee325d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563620999307},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563620999310},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563621244153},{"event":{"data":{"tx_hash":"1e883eb2f3991e84ba27f53651f89b7dda708678a5b9813d043577f222b9ca30","tx_hex":"01000000011300166cc86fcd318d7a238939e559fcd099953a9e9631d2d915a6aa6e134067010000006a47304402206781d5f2db2ff13d2ec7e266f774ea5630cc2dba4019e18e9716131b8b026051022006ebb33857b6d180f13aa6be2fc532f9734abde9d00ae14757e7d7ba3741c08c012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0228db0b000000000017a91483818667161bf94adda3964a81a231cbf6f5338187b0480c00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac7cf7325d"},"type":"TakerPaymentSent"},"timestamp":1563621246370},{"event":{"data":{"error":"utxo:1145] rpc_clients:782] Waited too long until 1563628675 for output TransactionOutput { value: 777000, script_pubkey: a91483818667161bf94adda3964a81a231cbf6f5338187 } to be spent "},"type":"TakerPaymentWaitForSpendFailed"},"timestamp":1563638060370},{"event":{"data":{"error":"lp_swap:2025] utxo:938] rpc_clients:719] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"9\", method: \"blockchain.transaction.broadcast\", params: [String(\"010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d\")] }, error: Response(Object({\"code\": Number(1), \"message\": String(\"the transaction was rejected by network rules.\\n\\nMissing inputs\\n[010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d]\")})) }"},"type":"TakerPaymentRefundFailed"},"timestamp":1563638060583},{"event":{"type":"Finished"},"timestamp":1563638060585}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"}"#; + let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","MakerPaymentSpendConfirmFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.58610590","maker_coin":"KMD","maker_coin_start_block":1450923,"maker_payment_confirmations":1,"maker_payment_wait":1563623475,"my_persistent_pub":"02713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91","started_at":1563620875,"taker_amount":"0.0077700000552410000000000","taker_coin":"LTC","taker_coin_start_block":1670837,"taker_payment_confirmations":1,"taker_payment_lock":1563628675,"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"},"type":"Started"},"timestamp":1563620875766},{"event":{"data":{"maker_payment_locktime":1563636475,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"7ed38daab6085c1a1e4426e61dc87a3c2c081a95"},"type":"Negotiated"},"timestamp":1563620955014},{"event":{"data":{"tx_hash":"6740136eaaa615d9d231969e3a9599d0fc59e53989237a8d31cd6fc86c160013","tx_hex":"0100000001a2586ea8294cedc55741bef625ba72c646399903391a7f6c604a58c6263135f2000000006b4830450221009c78c8ba4a7accab6b09f9a95da5bc59c81f4fc1e60b288ec3c5462b4d02ef01022056b63be1629cf17751d3cc5ffec51bcb1d7f9396e9ce9ca254d0f34104f7263a012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0210270000000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac78aa1900000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac5bf6325d"},"type":"TakerFeeSent"},"timestamp":1563620958220},{"event":{"data":{"tx_hash":"d0f6e664cea9d89fe7b5cf8005fdca070d1ab1d05a482aaef95c08cdaecddf0a","tx_hex":"0400008085202f89019f1cbda354342cdf982046b331bbd3791f53b692efc6e4becc36be495b2977d9000000006b483045022100fa9d4557394141f6a8b9bfb8cd594a521fd8bcd1965dbf8bc4e04abc849ac66e0220589f521814c10a7561abfd5e432f7a2ee60d4875fe4604618af3207dae531ac00121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff029e537e030000000017a9145534898009f1467191065f6890b96914b39a1c018791857702000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac72ee325d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563620999307},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563620999310},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563621244153},{"event":{"data":{"tx_hash":"1e883eb2f3991e84ba27f53651f89b7dda708678a5b9813d043577f222b9ca30","tx_hex":"01000000011300166cc86fcd318d7a238939e559fcd099953a9e9631d2d915a6aa6e134067010000006a47304402206781d5f2db2ff13d2ec7e266f774ea5630cc2dba4019e18e9716131b8b026051022006ebb33857b6d180f13aa6be2fc532f9734abde9d00ae14757e7d7ba3741c08c012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0228db0b000000000017a91483818667161bf94adda3964a81a231cbf6f5338187b0480c00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac7cf7325d"},"type":"TakerPaymentSent"},"timestamp":1563621246370},{"event":{"data":{"error":"utxo:1145] rpc_clients:782] Waited too long until 1563628675 for output TransactionOutput { value: 777000, script_pubkey: a91483818667161bf94adda3964a81a231cbf6f5338187 } to be spent "},"type":"TakerPaymentWaitForSpendFailed"},"timestamp":1563638060370},{"event":{"data":{"error":"lp_swap:2025] utxo:938] rpc_clients:719] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"9\", method: \"blockchain.transaction.broadcast\", params: [String(\"010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d\")] }, error: Response(Object({\"code\": Number(1), \"message\": String(\"the transaction was rejected by network rules.\\n\\nMissing inputs\\n[010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d]\")})) }"},"type":"TakerPaymentRefundFailed"},"timestamp":1563638060583},{"event":{"type":"Finished"},"timestamp":1563638060585}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"}"#; let taker_saved_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -2921,7 +2980,7 @@ mod taker_swap_tests { fn test_recover_funds_taker_swap_taker_payment_refund_failed_spent_by_maker() { let ctx = mm_ctx_with_iguana(PASSPHRASE); - let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.58610590","maker_coin":"KMD","maker_coin_start_block":1450923,"maker_payment_confirmations":1,"maker_payment_wait":1563623475,"my_persistent_pub":"02713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91","started_at":1563620875,"taker_amount":"0.0077700000552410000000000","taker_coin":"LTC","taker_coin_start_block":1670837,"taker_payment_confirmations":1,"taker_payment_lock":1563628675,"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"},"type":"Started"},"timestamp":1563620875766},{"event":{"data":{"maker_payment_locktime":1563636475,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"7ed38daab6085c1a1e4426e61dc87a3c2c081a95"},"type":"Negotiated"},"timestamp":1563620955014},{"event":{"data":{"tx_hash":"6740136eaaa615d9d231969e3a9599d0fc59e53989237a8d31cd6fc86c160013","tx_hex":"0100000001a2586ea8294cedc55741bef625ba72c646399903391a7f6c604a58c6263135f2000000006b4830450221009c78c8ba4a7accab6b09f9a95da5bc59c81f4fc1e60b288ec3c5462b4d02ef01022056b63be1629cf17751d3cc5ffec51bcb1d7f9396e9ce9ca254d0f34104f7263a012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0210270000000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac78aa1900000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac5bf6325d"},"type":"TakerFeeSent"},"timestamp":1563620958220},{"event":{"data":{"tx_hash":"d0f6e664cea9d89fe7b5cf8005fdca070d1ab1d05a482aaef95c08cdaecddf0a","tx_hex":"0400008085202f89019f1cbda354342cdf982046b331bbd3791f53b692efc6e4becc36be495b2977d9000000006b483045022100fa9d4557394141f6a8b9bfb8cd594a521fd8bcd1965dbf8bc4e04abc849ac66e0220589f521814c10a7561abfd5e432f7a2ee60d4875fe4604618af3207dae531ac00121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff029e537e030000000017a9145534898009f1467191065f6890b96914b39a1c018791857702000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac72ee325d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563620999307},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563620999310},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563621244153},{"event":{"data":{"tx_hash":"1e883eb2f3991e84ba27f53651f89b7dda708678a5b9813d043577f222b9ca30","tx_hex":"01000000011300166cc86fcd318d7a238939e559fcd099953a9e9631d2d915a6aa6e134067010000006a47304402206781d5f2db2ff13d2ec7e266f774ea5630cc2dba4019e18e9716131b8b026051022006ebb33857b6d180f13aa6be2fc532f9734abde9d00ae14757e7d7ba3741c08c012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0228db0b000000000017a91483818667161bf94adda3964a81a231cbf6f5338187b0480c00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac7cf7325d"},"type":"TakerPaymentSent"},"timestamp":1563621246370},{"event":{"data":{"error":"utxo:1145] rpc_clients:782] Waited too long until 1563628675 for output TransactionOutput { value: 777000, script_pubkey: a91483818667161bf94adda3964a81a231cbf6f5338187 } to be spent "},"type":"TakerPaymentWaitForSpendFailed"},"timestamp":1563638060370},{"event":{"data":{"error":"lp_swap:2025] utxo:938] rpc_clients:719] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"9\", method: \"blockchain.transaction.broadcast\", params: [String(\"010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d\")] }, error: Response(Object({\"code\": Number(1), \"message\": String(\"the transaction was rejected by network rules.\\n\\nMissing inputs\\n[010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d]\")})) }"},"type":"TakerPaymentRefundFailed"},"timestamp":1563638060583},{"event":{"type":"Finished"},"timestamp":1563638060585}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"}"#; + let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","MakerPaymentSpendConfirmFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.58610590","maker_coin":"KMD","maker_coin_start_block":1450923,"maker_payment_confirmations":1,"maker_payment_wait":1563623475,"my_persistent_pub":"02713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91","started_at":1563620875,"taker_amount":"0.0077700000552410000000000","taker_coin":"LTC","taker_coin_start_block":1670837,"taker_payment_confirmations":1,"taker_payment_lock":1563628675,"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"},"type":"Started"},"timestamp":1563620875766},{"event":{"data":{"maker_payment_locktime":1563636475,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"7ed38daab6085c1a1e4426e61dc87a3c2c081a95"},"type":"Negotiated"},"timestamp":1563620955014},{"event":{"data":{"tx_hash":"6740136eaaa615d9d231969e3a9599d0fc59e53989237a8d31cd6fc86c160013","tx_hex":"0100000001a2586ea8294cedc55741bef625ba72c646399903391a7f6c604a58c6263135f2000000006b4830450221009c78c8ba4a7accab6b09f9a95da5bc59c81f4fc1e60b288ec3c5462b4d02ef01022056b63be1629cf17751d3cc5ffec51bcb1d7f9396e9ce9ca254d0f34104f7263a012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0210270000000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac78aa1900000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac5bf6325d"},"type":"TakerFeeSent"},"timestamp":1563620958220},{"event":{"data":{"tx_hash":"d0f6e664cea9d89fe7b5cf8005fdca070d1ab1d05a482aaef95c08cdaecddf0a","tx_hex":"0400008085202f89019f1cbda354342cdf982046b331bbd3791f53b692efc6e4becc36be495b2977d9000000006b483045022100fa9d4557394141f6a8b9bfb8cd594a521fd8bcd1965dbf8bc4e04abc849ac66e0220589f521814c10a7561abfd5e432f7a2ee60d4875fe4604618af3207dae531ac00121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff029e537e030000000017a9145534898009f1467191065f6890b96914b39a1c018791857702000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac72ee325d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563620999307},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563620999310},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563621244153},{"event":{"data":{"tx_hash":"1e883eb2f3991e84ba27f53651f89b7dda708678a5b9813d043577f222b9ca30","tx_hex":"01000000011300166cc86fcd318d7a238939e559fcd099953a9e9631d2d915a6aa6e134067010000006a47304402206781d5f2db2ff13d2ec7e266f774ea5630cc2dba4019e18e9716131b8b026051022006ebb33857b6d180f13aa6be2fc532f9734abde9d00ae14757e7d7ba3741c08c012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0228db0b000000000017a91483818667161bf94adda3964a81a231cbf6f5338187b0480c00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac7cf7325d"},"type":"TakerPaymentSent"},"timestamp":1563621246370},{"event":{"data":{"error":"utxo:1145] rpc_clients:782] Waited too long until 1563628675 for output TransactionOutput { value: 777000, script_pubkey: a91483818667161bf94adda3964a81a231cbf6f5338187 } to be spent "},"type":"TakerPaymentWaitForSpendFailed"},"timestamp":1563638060370},{"event":{"data":{"error":"lp_swap:2025] utxo:938] rpc_clients:719] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"9\", method: \"blockchain.transaction.broadcast\", params: [String(\"010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d\")] }, error: Response(Object({\"code\": Number(1), \"message\": String(\"the transaction was rejected by network rules.\\n\\nMissing inputs\\n[010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d]\")})) }"},"type":"TakerPaymentRefundFailed"},"timestamp":1563638060583},{"event":{"type":"Finished"},"timestamp":1563638060585}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"}"#; let taker_saved_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -2968,7 +3027,7 @@ mod taker_swap_tests { let ctx = mm_ctx_with_iguana(PASSPHRASE); // the json doesn't have Finished event at the end - let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.12596566232185483","maker_coin":"KMD","maker_coin_start_block":1458035,"maker_payment_confirmations":1,"maker_payment_wait":1564053079,"my_persistent_pub":"0326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0a","started_at":1564050479,"taker_amount":"50.000000000000001504212457800000","taker_coin":"DOGE","taker_coin_start_block":2823448,"taker_payment_confirmations":1,"taker_payment_lock":1564058279,"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"},"type":"Started"},"timestamp":1564050480269},{"event":{"data":{"maker_payment_locktime":1564066080,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"3669eb83a007a3c507448d79f45a9f06ec2f36a8"},"type":"Negotiated"},"timestamp":1564050540991},{"event":{"data":{"tx_hash":"bdde828b492d6d1cc25cd2322fd592dafd722fcc7d8b0fedce4d3bb4a1a8c8ff","tx_hex":"0100000002c7efa995c8b7be0a8b6c2d526c6c444c1634d65584e9ee89904e9d8675eac88c010000006a473044022051f34d5e3b7d0b9098d5e35333f3550f9cb9e57df83d5e4635b7a8d2986d6d5602200288c98da05de6950e01229a637110a1800ba643e75cfec59d4eb1021ad9b40801210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffffae6c233989efa7c7d2aa6534adc96078917ff395b7f09f734a147b2f44ade164000000006a4730440220393a784c2da74d0e2a28ec4f7df6c8f9d8b2af6ae6957f1e68346d744223a8fd02201b7a96954ac06815a43a6c7668d829ae9cbb5de76fa77189ddfd9e3038df662c01210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffff02115f5800000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac41a84641020000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac6d84395d"},"type":"TakerFeeSent"},"timestamp":1564050545296},{"event":{"data":{"tx_hash":"0a0f11fa82802c2c30862c50ab2162185dae8de7f7235f32c506f814c142b382","tx_hex":"0400008085202f8902ace337db2dd4c56b0697f58fb8cfb6bd1cd6f469d925fc0376d1dcfb7581bf82000000006b483045022100d1f95be235c5c8880f5d703ace287e2768548792c58c5dbd27f5578881b30ea70220030596106e21c7e0057ee0dab283f9a1fe273f15208cba80870c447bd559ef0d0121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff9f339752567c404427fd77f2b35cecdb4c21489edc64e25e729fdb281785e423000000006a47304402203179e95877dbc107123a417f1e648e3ff13d384890f1e4a67b6dd5087235152e0220102a8ab799fadb26b5d89ceb9c7bc721a7e0c2a0d0d7e46bbe0cf3d130010d430121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff025635c0000000000017a91480a95d366d65e34a465ab17b0c9eb1d5a33bae08876cbfce05000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac8d7c395d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1564050588176},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1564050588178},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1564050693585},{"event":{"data":{"tx_hash":"539cb6dbdc25465bbccc575554f05d1bb04c70efce4316e41194e747375c3659","tx_hex":"0100000001ffc8a8a1b43b4dceed0f8b7dcc2f72fdda92d52f32d25cc21c6d2d498b82debd010000006a47304402203967b7f9f5532fa47116585c7d1bcba51861ea2059cca00409f34660db18e33a0220640991911852533a12fdfeb039fb9c8ca2c45482c6993bd84636af3670d49c1501210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffff0200f2052a0100000017a914f2fa08ae416b576779ae5da975e5442663215fce87415173f9000000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac0585395d"},"type":"TakerPaymentSent"},"timestamp":1564050695611},{"event":{"data":{"secret":"1b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093","transaction":{"tx_hash":"cc5af1cf68d246419fee49c3d74c0cd173599d115b86efe274368a614951bc47","tx_hex":"010000000159365c3747e79411e41643ceef704cb01b5df0545557ccbc5b4625dcdbb69c5300000000d747304402200e78e27d2f1c18676f98ca3dfa4e4a9eeaa8209b55f57b4dd5d9e1abdf034cfa0220623b5c22b62234cec230342aa306c497e43494b44ec2425b84e236b1bf01257001201b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093004c6b6304a7a2395db175210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0aac6782012088a9143669eb83a007a3c507448d79f45a9f06ec2f36a88821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68ffffffff01008d380c010000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac8c77395d"}},"type":"TakerPaymentSpent"},"timestamp":1564051092890},{"event":{"data":{"error":"lp_swap:1981] utxo:891] rpc_clients:738] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"67\", method: \"blockchain.transaction.broadcast\", params: [String(\"0400008085202f890182b342c114f806c5325f23f7e78dae5d186221ab502c86302c2c8082fa110f0a00000000d7473044022035791ea5548f87484065c9e1f0bdca9ebc699f2c7f51182c84f360102e32dc3d02200612ed53bca52d9c2568437f087598531534badf26229fe0f652ea72ddf03ca501201b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093004c6b630420c1395db17521031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac6782012088a9143669eb83a007a3c507448d79f45a9f06ec2f36a888210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0aac68ffffffff01460ec000000000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac967e395d000000000000000000000000000000\")] }, error: Transport(\"rpc_clients:668] All electrums are currently disconnected\") }"},"type":"MakerPaymentSpendFailed"},"timestamp":1564051092897}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"}"#; + let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","MakerPaymentSpendConfirmFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.12596566232185483","maker_coin":"KMD","maker_coin_start_block":1458035,"maker_payment_confirmations":1,"maker_payment_wait":1564053079,"my_persistent_pub":"0326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0a","started_at":1564050479,"taker_amount":"50.000000000000001504212457800000","taker_coin":"DOGE","taker_coin_start_block":2823448,"taker_payment_confirmations":1,"taker_payment_lock":1564058279,"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"},"type":"Started"},"timestamp":1564050480269},{"event":{"data":{"maker_payment_locktime":1564066080,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"3669eb83a007a3c507448d79f45a9f06ec2f36a8"},"type":"Negotiated"},"timestamp":1564050540991},{"event":{"data":{"tx_hash":"bdde828b492d6d1cc25cd2322fd592dafd722fcc7d8b0fedce4d3bb4a1a8c8ff","tx_hex":"0100000002c7efa995c8b7be0a8b6c2d526c6c444c1634d65584e9ee89904e9d8675eac88c010000006a473044022051f34d5e3b7d0b9098d5e35333f3550f9cb9e57df83d5e4635b7a8d2986d6d5602200288c98da05de6950e01229a637110a1800ba643e75cfec59d4eb1021ad9b40801210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffffae6c233989efa7c7d2aa6534adc96078917ff395b7f09f734a147b2f44ade164000000006a4730440220393a784c2da74d0e2a28ec4f7df6c8f9d8b2af6ae6957f1e68346d744223a8fd02201b7a96954ac06815a43a6c7668d829ae9cbb5de76fa77189ddfd9e3038df662c01210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffff02115f5800000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac41a84641020000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac6d84395d"},"type":"TakerFeeSent"},"timestamp":1564050545296},{"event":{"data":{"tx_hash":"0a0f11fa82802c2c30862c50ab2162185dae8de7f7235f32c506f814c142b382","tx_hex":"0400008085202f8902ace337db2dd4c56b0697f58fb8cfb6bd1cd6f469d925fc0376d1dcfb7581bf82000000006b483045022100d1f95be235c5c8880f5d703ace287e2768548792c58c5dbd27f5578881b30ea70220030596106e21c7e0057ee0dab283f9a1fe273f15208cba80870c447bd559ef0d0121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff9f339752567c404427fd77f2b35cecdb4c21489edc64e25e729fdb281785e423000000006a47304402203179e95877dbc107123a417f1e648e3ff13d384890f1e4a67b6dd5087235152e0220102a8ab799fadb26b5d89ceb9c7bc721a7e0c2a0d0d7e46bbe0cf3d130010d430121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff025635c0000000000017a91480a95d366d65e34a465ab17b0c9eb1d5a33bae08876cbfce05000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac8d7c395d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1564050588176},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1564050588178},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1564050693585},{"event":{"data":{"tx_hash":"539cb6dbdc25465bbccc575554f05d1bb04c70efce4316e41194e747375c3659","tx_hex":"0100000001ffc8a8a1b43b4dceed0f8b7dcc2f72fdda92d52f32d25cc21c6d2d498b82debd010000006a47304402203967b7f9f5532fa47116585c7d1bcba51861ea2059cca00409f34660db18e33a0220640991911852533a12fdfeb039fb9c8ca2c45482c6993bd84636af3670d49c1501210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffff0200f2052a0100000017a914f2fa08ae416b576779ae5da975e5442663215fce87415173f9000000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac0585395d"},"type":"TakerPaymentSent"},"timestamp":1564050695611},{"event":{"data":{"secret":"1b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093","transaction":{"tx_hash":"cc5af1cf68d246419fee49c3d74c0cd173599d115b86efe274368a614951bc47","tx_hex":"010000000159365c3747e79411e41643ceef704cb01b5df0545557ccbc5b4625dcdbb69c5300000000d747304402200e78e27d2f1c18676f98ca3dfa4e4a9eeaa8209b55f57b4dd5d9e1abdf034cfa0220623b5c22b62234cec230342aa306c497e43494b44ec2425b84e236b1bf01257001201b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093004c6b6304a7a2395db175210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0aac6782012088a9143669eb83a007a3c507448d79f45a9f06ec2f36a88821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68ffffffff01008d380c010000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac8c77395d"}},"type":"TakerPaymentSpent"},"timestamp":1564051092890},{"event":{"data":{"error":"lp_swap:1981] utxo:891] rpc_clients:738] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"67\", method: \"blockchain.transaction.broadcast\", params: [String(\"0400008085202f890182b342c114f806c5325f23f7e78dae5d186221ab502c86302c2c8082fa110f0a00000000d7473044022035791ea5548f87484065c9e1f0bdca9ebc699f2c7f51182c84f360102e32dc3d02200612ed53bca52d9c2568437f087598531534badf26229fe0f652ea72ddf03ca501201b8886b8a2cdb62505699400b694ac20f04d7bd4abd80e1ab154aa8d861fc093004c6b630420c1395db17521031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac6782012088a9143669eb83a007a3c507448d79f45a9f06ec2f36a888210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0aac68ffffffff01460ec000000000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac967e395d000000000000000000000000000000\")] }, error: Transport(\"rpc_clients:668] All electrums are currently disconnected\") }"},"type":"MakerPaymentSpendFailed"},"timestamp":1564051092897}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"}"#; let taker_saved_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -3005,7 +3064,7 @@ mod taker_swap_tests { let ctx = mm_ctx_with_iguana(PASSPHRASE); // swap file contains neither maker_coin_swap_contract_address nor taker_coin_swap_contract_address - let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.58610590","maker_coin":"KMD","maker_coin_start_block":1450923,"maker_payment_confirmations":1,"maker_payment_wait":1563623475,"my_persistent_pub":"02713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91","started_at":1563620875,"taker_amount":"0.0077700000552410000000000","taker_coin":"LTC","taker_coin_start_block":1670837,"taker_payment_confirmations":1,"taker_payment_lock":1563628675,"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"},"type":"Started"},"timestamp":1563620875766},{"event":{"data":{"maker_payment_locktime":1563636475,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"7ed38daab6085c1a1e4426e61dc87a3c2c081a95"},"type":"Negotiated"},"timestamp":1563620955014},{"event":{"data":{"tx_hash":"6740136eaaa615d9d231969e3a9599d0fc59e53989237a8d31cd6fc86c160013","tx_hex":"0100000001a2586ea8294cedc55741bef625ba72c646399903391a7f6c604a58c6263135f2000000006b4830450221009c78c8ba4a7accab6b09f9a95da5bc59c81f4fc1e60b288ec3c5462b4d02ef01022056b63be1629cf17751d3cc5ffec51bcb1d7f9396e9ce9ca254d0f34104f7263a012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0210270000000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac78aa1900000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac5bf6325d"},"type":"TakerFeeSent"},"timestamp":1563620958220},{"event":{"data":{"tx_hash":"d0f6e664cea9d89fe7b5cf8005fdca070d1ab1d05a482aaef95c08cdaecddf0a","tx_hex":"0400008085202f89019f1cbda354342cdf982046b331bbd3791f53b692efc6e4becc36be495b2977d9000000006b483045022100fa9d4557394141f6a8b9bfb8cd594a521fd8bcd1965dbf8bc4e04abc849ac66e0220589f521814c10a7561abfd5e432f7a2ee60d4875fe4604618af3207dae531ac00121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff029e537e030000000017a9145534898009f1467191065f6890b96914b39a1c018791857702000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac72ee325d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563620999307},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563620999310},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563621244153},{"event":{"data":{"tx_hash":"1e883eb2f3991e84ba27f53651f89b7dda708678a5b9813d043577f222b9ca30","tx_hex":"01000000011300166cc86fcd318d7a238939e559fcd099953a9e9631d2d915a6aa6e134067010000006a47304402206781d5f2db2ff13d2ec7e266f774ea5630cc2dba4019e18e9716131b8b026051022006ebb33857b6d180f13aa6be2fc532f9734abde9d00ae14757e7d7ba3741c08c012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0228db0b000000000017a91483818667161bf94adda3964a81a231cbf6f5338187b0480c00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac7cf7325d"},"type":"TakerPaymentSent"},"timestamp":1563621246370},{"event":{"data":{"error":"utxo:1145] rpc_clients:782] Waited too long until 1563628675 for output TransactionOutput { value: 777000, script_pubkey: a91483818667161bf94adda3964a81a231cbf6f5338187 } to be spent "},"type":"TakerPaymentWaitForSpendFailed"},"timestamp":1563638060370},{"event":{"data":{"error":"lp_swap:2025] utxo:938] rpc_clients:719] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"9\", method: \"blockchain.transaction.broadcast\", params: [String(\"010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d\")] }, error: Response(Object({\"code\": Number(1), \"message\": String(\"the transaction was rejected by network rules.\\n\\nMissing inputs\\n[010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d]\")})) }"},"type":"TakerPaymentRefundFailed"},"timestamp":1563638060583},{"event":{"type":"Finished"},"timestamp":1563638060585}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"}"#; + let taker_saved_json = r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","TakerPaymentTransactionFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","MakerPaymentSpendConfirmFailed","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.58610590","maker_coin":"KMD","maker_coin_start_block":1450923,"maker_payment_confirmations":1,"maker_payment_wait":1563623475,"my_persistent_pub":"02713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91","started_at":1563620875,"taker_amount":"0.0077700000552410000000000","taker_coin":"LTC","taker_coin_start_block":1670837,"taker_payment_confirmations":1,"taker_payment_lock":1563628675,"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"},"type":"Started"},"timestamp":1563620875766},{"event":{"data":{"maker_payment_locktime":1563636475,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"7ed38daab6085c1a1e4426e61dc87a3c2c081a95"},"type":"Negotiated"},"timestamp":1563620955014},{"event":{"data":{"tx_hash":"6740136eaaa615d9d231969e3a9599d0fc59e53989237a8d31cd6fc86c160013","tx_hex":"0100000001a2586ea8294cedc55741bef625ba72c646399903391a7f6c604a58c6263135f2000000006b4830450221009c78c8ba4a7accab6b09f9a95da5bc59c81f4fc1e60b288ec3c5462b4d02ef01022056b63be1629cf17751d3cc5ffec51bcb1d7f9396e9ce9ca254d0f34104f7263a012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0210270000000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac78aa1900000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac5bf6325d"},"type":"TakerFeeSent"},"timestamp":1563620958220},{"event":{"data":{"tx_hash":"d0f6e664cea9d89fe7b5cf8005fdca070d1ab1d05a482aaef95c08cdaecddf0a","tx_hex":"0400008085202f89019f1cbda354342cdf982046b331bbd3791f53b692efc6e4becc36be495b2977d9000000006b483045022100fa9d4557394141f6a8b9bfb8cd594a521fd8bcd1965dbf8bc4e04abc849ac66e0220589f521814c10a7561abfd5e432f7a2ee60d4875fe4604618af3207dae531ac00121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff029e537e030000000017a9145534898009f1467191065f6890b96914b39a1c018791857702000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac72ee325d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1563620999307},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1563620999310},{"event":{"type":"MakerPaymentValidatedAndConfirmed"},"timestamp":1563621244153},{"event":{"data":{"tx_hash":"1e883eb2f3991e84ba27f53651f89b7dda708678a5b9813d043577f222b9ca30","tx_hex":"01000000011300166cc86fcd318d7a238939e559fcd099953a9e9631d2d915a6aa6e134067010000006a47304402206781d5f2db2ff13d2ec7e266f774ea5630cc2dba4019e18e9716131b8b026051022006ebb33857b6d180f13aa6be2fc532f9734abde9d00ae14757e7d7ba3741c08c012102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ffffffff0228db0b000000000017a91483818667161bf94adda3964a81a231cbf6f5338187b0480c00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac7cf7325d"},"type":"TakerPaymentSent"},"timestamp":1563621246370},{"event":{"data":{"error":"utxo:1145] rpc_clients:782] Waited too long until 1563628675 for output TransactionOutput { value: 777000, script_pubkey: a91483818667161bf94adda3964a81a231cbf6f5338187 } to be spent "},"type":"TakerPaymentWaitForSpendFailed"},"timestamp":1563638060370},{"event":{"data":{"error":"lp_swap:2025] utxo:938] rpc_clients:719] JsonRpcError { request: JsonRpcRequest { jsonrpc: \"2.0\", id: \"9\", method: \"blockchain.transaction.broadcast\", params: [String(\"010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d\")] }, error: Response(Object({\"code\": Number(1), \"message\": String(\"the transaction was rejected by network rules.\\n\\nMissing inputs\\n[010000000130cab922f27735043d81b9a5788670da7d9bf85136f527ba841e99f3b23e881e00000000b6473044022058a0c1da6bcf8c1418899ff8475f3ab6dddbff918528451c1fe71c2f7dad176302204c2e0bcf8f9b5f09e02ccfeb9256e9b34fb355ea655a5704a8a3fa920079b91501514c6b63048314335db1752102713015d3fa4d30259e90be5f131beb593bf0131f3af2dcdb304e3322d8d52b91ac6782012088a9147ed38daab6085c1a1e4426e61dc87a3c2c081a958821031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ac68feffffff0188540a00000000001976a91406ccabfd5f9075ecd5e8d0d31c0e973a54d51e8288ac1c2b335d]\")})) }"},"type":"TakerPaymentRefundFailed"},"timestamp":1563638060583},{"event":{"type":"Finished"},"timestamp":1563638060585}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"uuid":"9db641f5-4300-4527-9fa6-f1c391d42c35"}"#; let taker_saved_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -3040,7 +3099,7 @@ mod taker_swap_tests { let ctx = mm_ctx_with_iguana(PASSPHRASE); // swap file contains only maker_coin_swap_contract_address - let taker_saved_json = r#"{"type":"Taker","uuid":"49c79ea4-e1eb-4fb2-a0ef-265bded0b77f","events":[{"timestamp":1608542326909,"event":{"type":"Started","data":{"taker_coin":"RICK","maker_coin":"ETH","maker":"c6a78589e18b482aea046975e6d0acbdea7bf7dbf04d9d5bd67fda917815e3ed","my_persistent_pub":"02031d4256c4bc9f99ac88bf3dba21773132281f65f9bf23a59928bce08961e2f3","lock_duration":7800,"maker_amount":"0.1","taker_amount":"0.1","maker_payment_confirmations":1,"maker_payment_requires_nota":false,"taker_payment_confirmations":0,"taker_payment_requires_nota":false,"taker_payment_lock":1608550126,"uuid":"49c79ea4-e1eb-4fb2-a0ef-265bded0b77f","started_at":1608542326,"maker_payment_wait":1608545446,"maker_coin_start_block":14360,"taker_coin_start_block":723123,"maker_coin_swap_contract_address":"eA6D65434A15377081495a9E7C5893543E7c32cB"}}},{"timestamp":1608542327416,"event":{"type":"Negotiated","data":{"maker_payment_locktime":1608557926,"maker_pubkey":"03c6a78589e18b482aea046975e6d0acbdea7bf7dbf04d9d5bd67fda917815e3ed","secret_hash":"8b0221f3b977c1c65dddf17c1c28e2bbced9e7b4"}}},{"timestamp":1608542332604,"event":{"type":"TakerFeeSent","data":{"tx_hex":"0400008085202f89011ca964f77200b73d64b481f47de84098041d3470d6256e44f2741f080e2b11cf020000006b4830450221008a064f5e51ef8281d43eb7bcd016fed7e560ea1eb7b0713ec977602c96d8f79b02205bfaa6655b849b9922c03276b938273f2edb8fb9ffcaa2a9212d7220560f6060012102031d4256c4bc9f99ac88bf3dba21773132281f65f9bf23a59928bce08961e2f3ffffffff0246320000000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac62752e27000000001976a91405aab5342166f8594baf17a7d9bef5d56744332788ac7768e05f000000000000000000000000000000","tx_hash":"3793df28ed2aac6188d2c48ec65eff12eea301089d60da655fc96f598326d708"}}},{"timestamp":1608542334018,"event":{"type":"MakerPaymentReceived","data":{"tx_hex":"f8ef82021c80830249f094a09ad3cd7e96586ebd05a2607ee56b56fb2db8fd88016345785d8a0000b884152cf3af50aebafeaf827c62c2eed09e265fa5aa9e013c0f27f0a88259f1aaa1279f0c32000000000000000000000000bab36286672fbdc7b250804bf6d14be0df69fa298b0221f3b977c1c65dddf17c1c28e2bbced9e7b4000000000000000000000000000000000000000000000000000000000000000000000000000000005fe0a5661ba0f18a0c5c349462b51dacd1a0761e4997d4572a01e48480c4e310d69a40308ad3a04510513f01a79c59f22c9cb79952547c8dfc4c74785b630f512d64369323e0c1","tx_hash":"6782323490584a2bc768cd5199506bfa1ed91e7515b35bb72fa269604b7dc0aa"}}},{"timestamp":1608542334019,"event":{"type":"MakerPaymentWaitConfirmStarted"}},{"timestamp":1608542334825,"event":{"type":"MakerPaymentValidatedAndConfirmed"}},{"timestamp":1608542337671,"event":{"type":"TakerPaymentSent","data":{"tx_hex":"0400008085202f890108d72683596fc95f65da609d0801a3ee12ff5ec68ec4d28861ac2aed28df9337010000006b48304502210086a03db599438b243bee2b02af56e23447f85d09854416b51305536b9ca5890e02204b288acdea4cdc7ab1ffbd9766a7bdf95f5bd02d2917dfb7089dbf29032591b0012102031d4256c4bc9f99ac88bf3dba21773132281f65f9bf23a59928bce08961e2f3ffffffff03809698000000000017a914888e9e1816214c3960eac7b55e35521ca4426b0c870000000000000000166a148b0221f3b977c1c65dddf17c1c28e2bbced9e7b4fada9526000000001976a91405aab5342166f8594baf17a7d9bef5d56744332788ac7f68e05f000000000000000000000000000000","tx_hash":"44fa493757df5fdca823bbac05a8b8feb5862d799d4947fd544abcd129feceea"}}},{"timestamp":1608542348271,"event":{"type":"TakerPaymentSpent","data":{"transaction":{"tx_hex":"0400008085202f8901eacefe29d1bc4a54fd47499d792d86b5feb8a805acbb23a8dc5fdf573749fa4400000000d74730440220508c853cc4f1fcb9e6aa00e704eef99adaee9a4ea63a1fd6393bb7ff18da02c802200396bb5d52157bd77ff26ac521ed75aca388d3ec1e5e3ebb7b3aed73c3d33ec50120df871242dcbcc4fe9ed4d3413e21b2f8ce606a3ee7128c9b2d2e31fcedc1848e004c6b6304ee86e05fb1752102031d4256c4bc9f99ac88bf3dba21773132281f65f9bf23a59928bce08961e2f3ac6782012088a9148b0221f3b977c1c65dddf17c1c28e2bbced9e7b4882103c6a78589e18b482aea046975e6d0acbdea7bf7dbf04d9d5bd67fda917815e3edac68ffffffff0198929800000000001976a9146d9d2b554d768232320587df75c4338ecc8bf37d88ac725ae05f000000000000000000000000000000","tx_hash":"9376dde62249802a0aba8259f51def9bb2e509af85a5ec7df04b479a9da28a29"},"secret":"df871242dcbcc4fe9ed4d3413e21b2f8ce606a3ee7128c9b2d2e31fcedc1848e"}}},{"timestamp":1608542349372,"event":{"type":"MakerPaymentSpent","data":{"tx_hex":"f90107821fb980830249f094a09ad3cd7e96586ebd05a2607ee56b56fb2db8fd80b8a402ed292b50aebafeaf827c62c2eed09e265fa5aa9e013c0f27f0a88259f1aaa1279f0c32000000000000000000000000000000000000000000000000016345785d8a0000df871242dcbcc4fe9ed4d3413e21b2f8ce606a3ee7128c9b2d2e31fcedc1848e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000004b2d0d6c2c785217457b69b922a2a9cea98f71e91ca0ed6a4942a78c7ae6eb3c9dec496459a9ef68b34cb389acd939d13d3ecaf7e4aca021bb77e80fc60acf25a7a01cc1272b1b76594a521fb1abe1322d650e58a672c2","tx_hash":"c2d206e665aee159a5ab9aff60f76444e97bdad8f9152eccb6ca07d9204974ca"}}},{"timestamp":1608542349373,"event":{"type":"Finished"}}],"maker_amount":"0.1","maker_coin":"ETH","taker_amount":"0.1","taker_coin":"RICK","gui":"nogui","mm_version":"1a6082121","success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"]}"#; + let taker_saved_json = r#"{"type":"Taker","uuid":"49c79ea4-e1eb-4fb2-a0ef-265bded0b77f","events":[{"timestamp":1608542326909,"event":{"type":"Started","data":{"taker_coin":"RICK","maker_coin":"ETH","maker":"c6a78589e18b482aea046975e6d0acbdea7bf7dbf04d9d5bd67fda917815e3ed","my_persistent_pub":"02031d4256c4bc9f99ac88bf3dba21773132281f65f9bf23a59928bce08961e2f3","lock_duration":7800,"maker_amount":"0.1","taker_amount":"0.1","maker_payment_confirmations":1,"maker_payment_requires_nota":false,"taker_payment_confirmations":0,"taker_payment_requires_nota":false,"taker_payment_lock":1608550126,"uuid":"49c79ea4-e1eb-4fb2-a0ef-265bded0b77f","started_at":1608542326,"maker_payment_wait":1608545446,"maker_coin_start_block":14360,"taker_coin_start_block":723123,"maker_coin_swap_contract_address":"eA6D65434A15377081495a9E7C5893543E7c32cB"}}},{"timestamp":1608542327416,"event":{"type":"Negotiated","data":{"maker_payment_locktime":1608557926,"maker_pubkey":"03c6a78589e18b482aea046975e6d0acbdea7bf7dbf04d9d5bd67fda917815e3ed","secret_hash":"8b0221f3b977c1c65dddf17c1c28e2bbced9e7b4"}}},{"timestamp":1608542332604,"event":{"type":"TakerFeeSent","data":{"tx_hex":"0400008085202f89011ca964f77200b73d64b481f47de84098041d3470d6256e44f2741f080e2b11cf020000006b4830450221008a064f5e51ef8281d43eb7bcd016fed7e560ea1eb7b0713ec977602c96d8f79b02205bfaa6655b849b9922c03276b938273f2edb8fb9ffcaa2a9212d7220560f6060012102031d4256c4bc9f99ac88bf3dba21773132281f65f9bf23a59928bce08961e2f3ffffffff0246320000000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac62752e27000000001976a91405aab5342166f8594baf17a7d9bef5d56744332788ac7768e05f000000000000000000000000000000","tx_hash":"3793df28ed2aac6188d2c48ec65eff12eea301089d60da655fc96f598326d708"}}},{"timestamp":1608542334018,"event":{"type":"MakerPaymentReceived","data":{"tx_hex":"f8ef82021c80830249f094a09ad3cd7e96586ebd05a2607ee56b56fb2db8fd88016345785d8a0000b884152cf3af50aebafeaf827c62c2eed09e265fa5aa9e013c0f27f0a88259f1aaa1279f0c32000000000000000000000000bab36286672fbdc7b250804bf6d14be0df69fa298b0221f3b977c1c65dddf17c1c28e2bbced9e7b4000000000000000000000000000000000000000000000000000000000000000000000000000000005fe0a5661ba0f18a0c5c349462b51dacd1a0761e4997d4572a01e48480c4e310d69a40308ad3a04510513f01a79c59f22c9cb79952547c8dfc4c74785b630f512d64369323e0c1","tx_hash":"6782323490584a2bc768cd5199506bfa1ed91e7515b35bb72fa269604b7dc0aa"}}},{"timestamp":1608542334019,"event":{"type":"MakerPaymentWaitConfirmStarted"}},{"timestamp":1608542334825,"event":{"type":"MakerPaymentValidatedAndConfirmed"}},{"timestamp":1608542337671,"event":{"type":"TakerPaymentSent","data":{"tx_hex":"0400008085202f890108d72683596fc95f65da609d0801a3ee12ff5ec68ec4d28861ac2aed28df9337010000006b48304502210086a03db599438b243bee2b02af56e23447f85d09854416b51305536b9ca5890e02204b288acdea4cdc7ab1ffbd9766a7bdf95f5bd02d2917dfb7089dbf29032591b0012102031d4256c4bc9f99ac88bf3dba21773132281f65f9bf23a59928bce08961e2f3ffffffff03809698000000000017a914888e9e1816214c3960eac7b55e35521ca4426b0c870000000000000000166a148b0221f3b977c1c65dddf17c1c28e2bbced9e7b4fada9526000000001976a91405aab5342166f8594baf17a7d9bef5d56744332788ac7f68e05f000000000000000000000000000000","tx_hash":"44fa493757df5fdca823bbac05a8b8feb5862d799d4947fd544abcd129feceea"}}},{"timestamp":1608542348271,"event":{"type":"TakerPaymentSpent","data":{"transaction":{"tx_hex":"0400008085202f8901eacefe29d1bc4a54fd47499d792d86b5feb8a805acbb23a8dc5fdf573749fa4400000000d74730440220508c853cc4f1fcb9e6aa00e704eef99adaee9a4ea63a1fd6393bb7ff18da02c802200396bb5d52157bd77ff26ac521ed75aca388d3ec1e5e3ebb7b3aed73c3d33ec50120df871242dcbcc4fe9ed4d3413e21b2f8ce606a3ee7128c9b2d2e31fcedc1848e004c6b6304ee86e05fb1752102031d4256c4bc9f99ac88bf3dba21773132281f65f9bf23a59928bce08961e2f3ac6782012088a9148b0221f3b977c1c65dddf17c1c28e2bbced9e7b4882103c6a78589e18b482aea046975e6d0acbdea7bf7dbf04d9d5bd67fda917815e3edac68ffffffff0198929800000000001976a9146d9d2b554d768232320587df75c4338ecc8bf37d88ac725ae05f000000000000000000000000000000","tx_hash":"9376dde62249802a0aba8259f51def9bb2e509af85a5ec7df04b479a9da28a29"},"secret":"df871242dcbcc4fe9ed4d3413e21b2f8ce606a3ee7128c9b2d2e31fcedc1848e"}}},{"timestamp":1608542349372,"event":{"type":"MakerPaymentSpent","data":{"tx_hex":"f90107821fb980830249f094a09ad3cd7e96586ebd05a2607ee56b56fb2db8fd80b8a402ed292b50aebafeaf827c62c2eed09e265fa5aa9e013c0f27f0a88259f1aaa1279f0c32000000000000000000000000000000000000000000000000016345785d8a0000df871242dcbcc4fe9ed4d3413e21b2f8ce606a3ee7128c9b2d2e31fcedc1848e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000004b2d0d6c2c785217457b69b922a2a9cea98f71e91ca0ed6a4942a78c7ae6eb3c9dec496459a9ef68b34cb389acd939d13d3ecaf7e4aca021bb77e80fc60acf25a7a01cc1272b1b76594a521fb1abe1322d650e58a672c2","tx_hash":"c2d206e665aee159a5ab9aff60f76444e97bdad8f9152eccb6ca07d9204974ca"}}},{"timestamp":1608542349373,"event":{"type":"Finished"}}],"maker_amount":"0.1","maker_coin":"ETH","taker_amount":"0.1","taker_coin":"RICK","gui":"nogui","mm_version":"1a6082121","success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","MakerPaymentSpendConfirmFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"]}"#; let taker_saved_swap: TakerSavedSwap = json::from_str(taker_saved_json).unwrap(); TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); @@ -3074,7 +3133,7 @@ mod taker_swap_tests { fn test_recoverable() { // Swap ended with MakerPaymentWaitConfirmFailed event. // MM2 did not attempt to send the payment in this case so swap is not recoverable. - let swap: TakerSavedSwap = json::from_str(r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.12596566232185483","maker_coin":"KMD","maker_coin_start_block":1458035,"maker_payment_confirmations":1,"maker_payment_wait":1564053079,"my_persistent_pub":"0326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0a","started_at":1564050479,"taker_amount":"50.000000000000001504212457800000","taker_coin":"DOGE","taker_coin_start_block":2823448,"taker_payment_confirmations":1,"taker_payment_lock":1564058279,"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"},"type":"Started"},"timestamp":1564050480269},{"event":{"data":{"maker_payment_locktime":1564066080,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"3669eb83a007a3c507448d79f45a9f06ec2f36a8"},"type":"Negotiated"},"timestamp":1564050540991},{"event":{"data":{"tx_hash":"bdde828b492d6d1cc25cd2322fd592dafd722fcc7d8b0fedce4d3bb4a1a8c8ff","tx_hex":"0100000002c7efa995c8b7be0a8b6c2d526c6c444c1634d65584e9ee89904e9d8675eac88c010000006a473044022051f34d5e3b7d0b9098d5e35333f3550f9cb9e57df83d5e4635b7a8d2986d6d5602200288c98da05de6950e01229a637110a1800ba643e75cfec59d4eb1021ad9b40801210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffffae6c233989efa7c7d2aa6534adc96078917ff395b7f09f734a147b2f44ade164000000006a4730440220393a784c2da74d0e2a28ec4f7df6c8f9d8b2af6ae6957f1e68346d744223a8fd02201b7a96954ac06815a43a6c7668d829ae9cbb5de76fa77189ddfd9e3038df662c01210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffff02115f5800000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac41a84641020000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac6d84395d"},"type":"TakerFeeSent"},"timestamp":1564050545296},{"event":{"data":{"tx_hash":"0a0f11fa82802c2c30862c50ab2162185dae8de7f7235f32c506f814c142b382","tx_hex":"0400008085202f8902ace337db2dd4c56b0697f58fb8cfb6bd1cd6f469d925fc0376d1dcfb7581bf82000000006b483045022100d1f95be235c5c8880f5d703ace287e2768548792c58c5dbd27f5578881b30ea70220030596106e21c7e0057ee0dab283f9a1fe273f15208cba80870c447bd559ef0d0121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff9f339752567c404427fd77f2b35cecdb4c21489edc64e25e729fdb281785e423000000006a47304402203179e95877dbc107123a417f1e648e3ff13d384890f1e4a67b6dd5087235152e0220102a8ab799fadb26b5d89ceb9c7bc721a7e0c2a0d0d7e46bbe0cf3d130010d430121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff025635c0000000000017a91480a95d366d65e34a465ab17b0c9eb1d5a33bae08876cbfce05000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac8d7c395d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1564050588176},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1564050588178},{"event":{"data":{"error":"error"},"type":"MakerPaymentWaitConfirmFailed"},"timestamp":1564051092897},{"event":{"type":"Finished"},"timestamp":1564051092900}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"}"#).unwrap(); + let swap: TakerSavedSwap = json::from_str(r#"{"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","MakerPaymentSpendConfirmFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefunded","TakerPaymentRefundedByWatcher","TakerPaymentRefundFailed"],"events":[{"event":{"data":{"lock_duration":7800,"maker":"1bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","maker_amount":"0.12596566232185483","maker_coin":"KMD","maker_coin_start_block":1458035,"maker_payment_confirmations":1,"maker_payment_wait":1564053079,"my_persistent_pub":"0326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0a","started_at":1564050479,"taker_amount":"50.000000000000001504212457800000","taker_coin":"DOGE","taker_coin_start_block":2823448,"taker_payment_confirmations":1,"taker_payment_lock":1564058279,"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"},"type":"Started"},"timestamp":1564050480269},{"event":{"data":{"maker_payment_locktime":1564066080,"maker_pubkey":"031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8","secret_hash":"3669eb83a007a3c507448d79f45a9f06ec2f36a8"},"type":"Negotiated"},"timestamp":1564050540991},{"event":{"data":{"tx_hash":"bdde828b492d6d1cc25cd2322fd592dafd722fcc7d8b0fedce4d3bb4a1a8c8ff","tx_hex":"0100000002c7efa995c8b7be0a8b6c2d526c6c444c1634d65584e9ee89904e9d8675eac88c010000006a473044022051f34d5e3b7d0b9098d5e35333f3550f9cb9e57df83d5e4635b7a8d2986d6d5602200288c98da05de6950e01229a637110a1800ba643e75cfec59d4eb1021ad9b40801210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffffae6c233989efa7c7d2aa6534adc96078917ff395b7f09f734a147b2f44ade164000000006a4730440220393a784c2da74d0e2a28ec4f7df6c8f9d8b2af6ae6957f1e68346d744223a8fd02201b7a96954ac06815a43a6c7668d829ae9cbb5de76fa77189ddfd9e3038df662c01210326846707a52a233cfc49a61ef51b1698bbe6aa78fa8b8d411c02743c09688f0affffffff02115f5800000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac41a84641020000001976a914444f0e1099709ba4d742454a7d98a5c9c162ceab88ac6d84395d"},"type":"TakerFeeSent"},"timestamp":1564050545296},{"event":{"data":{"tx_hash":"0a0f11fa82802c2c30862c50ab2162185dae8de7f7235f32c506f814c142b382","tx_hex":"0400008085202f8902ace337db2dd4c56b0697f58fb8cfb6bd1cd6f469d925fc0376d1dcfb7581bf82000000006b483045022100d1f95be235c5c8880f5d703ace287e2768548792c58c5dbd27f5578881b30ea70220030596106e21c7e0057ee0dab283f9a1fe273f15208cba80870c447bd559ef0d0121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff9f339752567c404427fd77f2b35cecdb4c21489edc64e25e729fdb281785e423000000006a47304402203179e95877dbc107123a417f1e648e3ff13d384890f1e4a67b6dd5087235152e0220102a8ab799fadb26b5d89ceb9c7bc721a7e0c2a0d0d7e46bbe0cf3d130010d430121031bb83b58ec130e28e0a6d5d2acf2eb01b0d3f1670e021d47d31db8a858219da8ffffffff025635c0000000000017a91480a95d366d65e34a465ab17b0c9eb1d5a33bae08876cbfce05000000001976a914c3f710deb7320b0efa6edb14e3ebeeb9155fa90d88ac8d7c395d000000000000000000000000000000"},"type":"MakerPaymentReceived"},"timestamp":1564050588176},{"event":{"type":"MakerPaymentWaitConfirmStarted"},"timestamp":1564050588178},{"event":{"data":{"error":"error"},"type":"MakerPaymentWaitConfirmFailed"},"timestamp":1564051092897},{"event":{"type":"Finished"},"timestamp":1564051092900}],"success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"uuid":"41383f43-46a5-478c-9386-3b2cce0aca20"}"#).unwrap(); assert!(!swap.is_recoverable()); } diff --git a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs index aadda99709..dc6f8cb5ba 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -409,6 +409,7 @@ fn test_taker_saves_the_swap_as_successful_after_restart_panic_at_wait_for_taker "WatcherMessageSent", "TakerPaymentSpent", "MakerPaymentSpentByWatcher", + "MakerPaymentSpendConfirmed", "Finished", ]; check_actual_events(&mm_alice, &uuids[0], &expected_events); @@ -467,6 +468,7 @@ fn test_taker_saves_the_swap_as_successful_after_restart_panic_at_maker_payment_ "WatcherMessageSent", "TakerPaymentSpent", "MakerPaymentSpentByWatcher", + "MakerPaymentSpendConfirmed", "Finished", ]; check_actual_events(&mm_alice, &uuids[0], &expected_events); diff --git a/mm2src/mm2_main/tests/docker_tests/swaps_file_lock_tests.rs b/mm2src/mm2_main/tests/docker_tests/swaps_file_lock_tests.rs index 8ca965e784..785eb0a849 100644 --- a/mm2src/mm2_main/tests/docker_tests/swaps_file_lock_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swaps_file_lock_tests.rs @@ -13,8 +13,8 @@ use std::time::Duration; const UNFINISHED_MAKER_SWAP: &str = r#"{"type":"Maker","uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","events":[{"timestamp":1588244036250,"event":{"type":"Started","data":{"taker_coin":"MYCOIN1","maker_coin":"MYCOIN","taker":"859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521","secret":"dedf3c8dcfff9ee2787b4bf211f960fd044fdab7fa8e922ef613a0115848a498","secret_hash":"9304bce3196f344b2a22dc99db406e95ab6f3107","my_persistent_pub":"02987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4d","lock_duration":7800,"maker_amount":"999.99999","taker_amount":"999.99999","maker_payment_confirmations":1,"maker_payment_requires_nota":false,"taker_payment_confirmations":1,"taker_payment_requires_nota":false,"maker_payment_lock":1588259636,"uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","started_at":1588244036,"maker_coin_start_block":12,"taker_coin_start_block":11}}},{"timestamp":1588244038035,"event":{"type":"Negotiated","data":{"taker_payment_locktime":1588251836,"taker_pubkey":"02859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521"}}}],"maker_amount":"999.99999","maker_coin":"MYCOIN","taker_amount":"999.99999","taker_coin":"MYCOIN1","gui":"nogui","mm_version":"UNKNOWN","success_events":["Started","Negotiated","TakerFeeValidated","MakerPaymentSent","TakerPaymentReceived","TakerPaymentWaitConfirmStarted","TakerPaymentValidatedAndConfirmed","TakerPaymentSpent","Finished"],"error_events":["StartFailed","NegotiateFailed","TakerFeeValidateFailed","MakerPaymentTransactionFailed","MakerPaymentDataSendFailed","MakerPaymentWaitConfirmFailed","TakerPaymentValidateFailed","TakerPaymentWaitConfirmFailed","TakerPaymentSpendFailed","MakerPaymentWaitRefundStarted","MakerPaymentRefunded","MakerPaymentRefundFailed"]}"#; const FINISHED_MAKER_SWAP: &str = r#"{"type":"Maker","uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","events":[{"timestamp":1588244036250,"event":{"type":"Started","data":{"taker_coin":"MYCOIN1","maker_coin":"MYCOIN","taker":"859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521","secret":"dedf3c8dcfff9ee2787b4bf211f960fd044fdab7fa8e922ef613a0115848a498","secret_hash":"9304bce3196f344b2a22dc99db406e95ab6f3107","my_persistent_pub":"02987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4d","lock_duration":7800,"maker_amount":"999.99999","taker_amount":"999.99999","maker_payment_confirmations":1,"maker_payment_requires_nota":false,"taker_payment_confirmations":1,"taker_payment_requires_nota":false,"maker_payment_lock":1588259636,"uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","started_at":1588244036,"maker_coin_start_block":12,"taker_coin_start_block":11}}},{"timestamp":1588244038035,"event":{"type":"Negotiated","data":{"taker_payment_locktime":1588251836,"taker_pubkey":"02859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521"}}},{"timestamp":1588244038463,"event":{"type":"TakerFeeValidated","data":{"tx_hex":"0400008085202f8901bdde9bca02870787441f6068e4c2a869a3aac3d1d0925f6a6e27874343544d0a010000006a47304402206694a794693b55fbe8205cb1cbb992d92fa2a9a851763ad1bf1628c16deaf73e02203cfff465504bdd6c51e8fbd45dd2e1187142fdc29e82f36f577616d9d6097d7a012102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ffffffff02dfceab07000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac39fd41892e0000001976a914d24e799df360da3ca3158d63b89ffaff27722c1588ac46aeaa5e000000000000000000000000000000","tx_hash":"7118d7484d1cbfdd6126673a848a228856f8748d946f6a9a440c90f0d62e27c6","from":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"to":["RThtXup6Zo7LZAi8kRWgjAyi1s4u6U9Cpf","RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"total_amount":"2000","spent_by_me":"0","received_by_me":"0","my_balance_change":"0","block_height":13,"timestamp":1588244038,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN1","internal_id":"7118d7484d1cbfdd6126673a848a228856f8748d946f6a9a440c90f0d62e27c6"}}},{"timestamp":1588244038505,"event":{"type":"MakerPaymentSent","data":{"tx_hex":"0400008085202f890163cc0aceb3b432f84c1407991a0389b74b7842f030aa261203aa0b4b9a9a15fd010000006b483045022100f3239794b7b0e1c75aae084a65535270f154231ce86a4739976ff69eeff1ebd402206c0aeb28b7b4b2e77e98b3c3efd9a20d85c2cd0627acc3f45d577c0e88c34fb4012102987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4dffffffff0118e476481700000017a914dc4ed4686174503b85374bfb0aefe07a9fb37bcf8746aeaa5e000000000000000000000000000000","tx_hash":"9a4c0b3f85ed0bb24dc9575ce7c2fd6bc50ad9d37c91c478946f9c33d15abfdf","from":["RAzSdYQhjCFdyhjrBz1AZQDVg3Hu8DrzYc"],"to":["bYp9ncp3V7FYsymipriVbdd3QL72hK9hio"],"total_amount":"1000","spent_by_me":"1000","received_by_me":"0","my_balance_change":"-1000","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN","internal_id":"9a4c0b3f85ed0bb24dc9575ce7c2fd6bc50ad9d37c91c478946f9c33d15abfdf"}}},{"timestamp":1588244053938,"event":{"type":"TakerPaymentReceived","data":{"tx_hex":"0400008085202f8901c6272ed6f0900c449a6a6f948d74f85688228a843a672661ddbf1c4d48d71871010000006b483045022100c37627385c66b7bdf466b4dd4e7095b7551e6f5ea35f9bcc6344eb629f2edcb202203280eaba64b4d72010500166fab62cf34a687a516b2fe83d4eceaf8572cb37a7012102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ffffffff0218e476481700000017a91431ff75ed72cd135a7ce50d121e71efc37066b9f9873915cb40170000001976a914d24e799df360da3ca3158d63b89ffaff27722c1588ac55aeaa5e000000000000000000000000000000","tx_hash":"b4718ce94aa43f9073ab0b70c18ab4ea4b587338fb7110f20ff7d1bb452df08f","from":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"to":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC","bHHdqM8XWDHee2oyzydwUJKEE2BjofEtZH"],"total_amount":"1998.71298873","spent_by_me":"0","received_by_me":"0","my_balance_change":"0","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN1","internal_id":"b4718ce94aa43f9073ab0b70c18ab4ea4b587338fb7110f20ff7d1bb452df08f"}}},{"timestamp":1588244053939,"event":{"type":"TakerPaymentWaitConfirmStarted"}},{"timestamp":1588244068951,"event":{"type":"TakerPaymentValidatedAndConfirmed"}},{"timestamp":1588244068959,"event":{"type":"TakerPaymentSpent","data":{"tx_hex":"0400008085202f89018ff02d45bbd1f70ff21071fb3873584beab48ac1700bab73903fa44ae98c71b400000000d747304402204f0d641a3916e54d6788744c3229110a431eff18634c66fbd1741f9ca7dba99d02202315ee1d9317cc4d5d75d01f066f2c8a59876f790106d310144cdc03c25f985e0120dedf3c8dcfff9ee2787b4bf211f960fd044fdab7fa8e922ef613a0115848a498004c6b6304bcccaa5eb1752102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ac6782012088a9149304bce3196f344b2a22dc99db406e95ab6f3107882102987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4dac68ffffffff0130e07648170000001976a91412c553e8469363f2d30268c475af1e9186cc90af88ac54a0aa5e000000000000000000000000000000","tx_hash":"e7aed7a77e47b44dc9d12166589bbade70faea10b64888f73ed4be04bcc9f9a9","from":["bHHdqM8XWDHee2oyzydwUJKEE2BjofEtZH"],"to":["RAzSdYQhjCFdyhjrBz1AZQDVg3Hu8DrzYc"],"total_amount":"999.99999","spent_by_me":"0","received_by_me":"999.99998","my_balance_change":"999.99998","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN1","internal_id":"e7aed7a77e47b44dc9d12166589bbade70faea10b64888f73ed4be04bcc9f9a9"}}},{"timestamp":1588244068960,"event":{"type":"Finished"}}],"maker_amount":"999.99999","maker_coin":"MYCOIN","taker_amount":"999.99999","taker_coin":"MYCOIN1","gui":"nogui","mm_version":"UNKNOWN","success_events":["Started","Negotiated","TakerFeeValidated","MakerPaymentSent","TakerPaymentReceived","TakerPaymentWaitConfirmStarted","TakerPaymentValidatedAndConfirmed","TakerPaymentSpent","Finished"],"error_events":["StartFailed","NegotiateFailed","TakerFeeValidateFailed","MakerPaymentTransactionFailed","MakerPaymentDataSendFailed","MakerPaymentWaitConfirmFailed","TakerPaymentValidateFailed","TakerPaymentWaitConfirmFailed","TakerPaymentSpendFailed","MakerPaymentWaitRefundStarted","MakerPaymentRefunded","MakerPaymentRefundFailed"]}"#; -const UNFINISHED_TAKER_SWAP: &str = r#"{"type":"Taker","uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","events":[{"timestamp":1588244036253,"event":{"type":"Started","data":{"taker_coin":"MYCOIN1","maker_coin":"MYCOIN","maker":"987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4d","my_persistent_pub":"02859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521","lock_duration":7800,"maker_amount":"999.99999","taker_amount":"999.99999","maker_payment_confirmations":1,"maker_payment_requires_nota":false,"taker_payment_confirmations":1,"taker_payment_requires_nota":false,"taker_payment_lock":1588251836,"uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","started_at":1588244036,"maker_payment_wait":1588247156,"maker_coin_start_block":12,"taker_coin_start_block":11}}},{"timestamp":1588244038239,"event":{"type":"Negotiated","data":{"maker_payment_locktime":1588259636,"maker_pubkey":"02987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4d","secret_hash":"9304bce3196f344b2a22dc99db406e95ab6f3107"}}},{"timestamp":1588244038271,"event":{"type":"TakerFeeSent","data":{"tx_hex":"0400008085202f8901bdde9bca02870787441f6068e4c2a869a3aac3d1d0925f6a6e27874343544d0a010000006a47304402206694a794693b55fbe8205cb1cbb992d92fa2a9a851763ad1bf1628c16deaf73e02203cfff465504bdd6c51e8fbd45dd2e1187142fdc29e82f36f577616d9d6097d7a012102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ffffffff02dfceab07000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac39fd41892e0000001976a914d24e799df360da3ca3158d63b89ffaff27722c1588ac46aeaa5e000000000000000000000000000000","tx_hash":"7118d7484d1cbfdd6126673a848a228856f8748d946f6a9a440c90f0d62e27c6","from":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"to":["RThtXup6Zo7LZAi8kRWgjAyi1s4u6U9Cpf","RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"total_amount":"2000","spent_by_me":"2000","received_by_me":"1998.71298873","my_balance_change":"-1.28701127","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN1","internal_id":"7118d7484d1cbfdd6126673a848a228856f8748d946f6a9a440c90f0d62e27c6"}}}],"maker_amount":"999.99999","maker_coin":"MYCOIN","taker_amount":"999.99999","taker_coin":"MYCOIN1","gui":"nogui","mm_version":"UNKNOWN","success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefunded","TakerPaymentRefundFailed"]}"#; -const FINISHED_TAKER_SWAP: &str = r#"{"type":"Taker","uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","events":[{"timestamp":1588244036253,"event":{"type":"Started","data":{"taker_coin":"MYCOIN1","maker_coin":"MYCOIN","maker":"987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4d","my_persistent_pub":"02859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521","lock_duration":7800,"maker_amount":"999.99999","taker_amount":"999.99999","maker_payment_confirmations":1,"maker_payment_requires_nota":false,"taker_payment_confirmations":1,"taker_payment_requires_nota":false,"taker_payment_lock":1588251836,"uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","started_at":1588244036,"maker_payment_wait":1588247156,"maker_coin_start_block":12,"taker_coin_start_block":11}}},{"timestamp":1588244038239,"event":{"type":"Negotiated","data":{"maker_payment_locktime":1588259636,"maker_pubkey":"02987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4d","secret_hash":"9304bce3196f344b2a22dc99db406e95ab6f3107"}}},{"timestamp":1588244038271,"event":{"type":"TakerFeeSent","data":{"tx_hex":"0400008085202f8901bdde9bca02870787441f6068e4c2a869a3aac3d1d0925f6a6e27874343544d0a010000006a47304402206694a794693b55fbe8205cb1cbb992d92fa2a9a851763ad1bf1628c16deaf73e02203cfff465504bdd6c51e8fbd45dd2e1187142fdc29e82f36f577616d9d6097d7a012102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ffffffff02dfceab07000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac39fd41892e0000001976a914d24e799df360da3ca3158d63b89ffaff27722c1588ac46aeaa5e000000000000000000000000000000","tx_hash":"7118d7484d1cbfdd6126673a848a228856f8748d946f6a9a440c90f0d62e27c6","from":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"to":["RThtXup6Zo7LZAi8kRWgjAyi1s4u6U9Cpf","RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"total_amount":"2000","spent_by_me":"2000","received_by_me":"1998.71298873","my_balance_change":"-1.28701127","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN1","internal_id":"7118d7484d1cbfdd6126673a848a228856f8748d946f6a9a440c90f0d62e27c6"}}},{"timestamp":1588244038698,"event":{"type":"MakerPaymentReceived","data":{"tx_hex":"0400008085202f890163cc0aceb3b432f84c1407991a0389b74b7842f030aa261203aa0b4b9a9a15fd010000006b483045022100f3239794b7b0e1c75aae084a65535270f154231ce86a4739976ff69eeff1ebd402206c0aeb28b7b4b2e77e98b3c3efd9a20d85c2cd0627acc3f45d577c0e88c34fb4012102987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4dffffffff0118e476481700000017a914dc4ed4686174503b85374bfb0aefe07a9fb37bcf8746aeaa5e000000000000000000000000000000","tx_hash":"9a4c0b3f85ed0bb24dc9575ce7c2fd6bc50ad9d37c91c478946f9c33d15abfdf","from":["RAzSdYQhjCFdyhjrBz1AZQDVg3Hu8DrzYc"],"to":["bYp9ncp3V7FYsymipriVbdd3QL72hK9hio"],"total_amount":"1000","spent_by_me":"0","received_by_me":"0","my_balance_change":"0","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN","internal_id":"9a4c0b3f85ed0bb24dc9575ce7c2fd6bc50ad9d37c91c478946f9c33d15abfdf"}}},{"timestamp":1588244038699,"event":{"type":"MakerPaymentWaitConfirmStarted"}},{"timestamp":1588244053712,"event":{"type":"MakerPaymentValidatedAndConfirmed"}},{"timestamp":1588244053745,"event":{"type":"TakerPaymentSent","data":{"tx_hex":"0400008085202f8901c6272ed6f0900c449a6a6f948d74f85688228a843a672661ddbf1c4d48d71871010000006b483045022100c37627385c66b7bdf466b4dd4e7095b7551e6f5ea35f9bcc6344eb629f2edcb202203280eaba64b4d72010500166fab62cf34a687a516b2fe83d4eceaf8572cb37a7012102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ffffffff0218e476481700000017a91431ff75ed72cd135a7ce50d121e71efc37066b9f9873915cb40170000001976a914d24e799df360da3ca3158d63b89ffaff27722c1588ac55aeaa5e000000000000000000000000000000","tx_hash":"b4718ce94aa43f9073ab0b70c18ab4ea4b587338fb7110f20ff7d1bb452df08f","from":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"to":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC","bHHdqM8XWDHee2oyzydwUJKEE2BjofEtZH"],"total_amount":"1998.71298873","spent_by_me":"1998.71298873","received_by_me":"998.71298873","my_balance_change":"-1000","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN1","internal_id":"b4718ce94aa43f9073ab0b70c18ab4ea4b587338fb7110f20ff7d1bb452df08f"}}},{"timestamp":1588244078860,"event":{"type":"TakerPaymentSpent","data":{"transaction":{"tx_hex":"0400008085202f89018ff02d45bbd1f70ff21071fb3873584beab48ac1700bab73903fa44ae98c71b400000000d747304402204f0d641a3916e54d6788744c3229110a431eff18634c66fbd1741f9ca7dba99d02202315ee1d9317cc4d5d75d01f066f2c8a59876f790106d310144cdc03c25f985e0120dedf3c8dcfff9ee2787b4bf211f960fd044fdab7fa8e922ef613a0115848a498004c6b6304bcccaa5eb1752102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ac6782012088a9149304bce3196f344b2a22dc99db406e95ab6f3107882102987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4dac68ffffffff0130e07648170000001976a91412c553e8469363f2d30268c475af1e9186cc90af88ac54a0aa5e000000000000000000000000000000","tx_hash":"e7aed7a77e47b44dc9d12166589bbade70faea10b64888f73ed4be04bcc9f9a9","from":["bHHdqM8XWDHee2oyzydwUJKEE2BjofEtZH"],"to":["RAzSdYQhjCFdyhjrBz1AZQDVg3Hu8DrzYc"],"total_amount":"999.99999","spent_by_me":"0","received_by_me":"0","my_balance_change":"0","block_height":29,"timestamp":1588244070,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN1","internal_id":"e7aed7a77e47b44dc9d12166589bbade70faea10b64888f73ed4be04bcc9f9a9"},"secret":"dedf3c8dcfff9ee2787b4bf211f960fd044fdab7fa8e922ef613a0115848a498"}}},{"timestamp":1588244078870,"event":{"type":"MakerPaymentSpent","data":{"tx_hex":"0400008085202f8901dfbf5ad1339c6f9478c4917cd3d90ac56bfdc2e75c57c94db20bed853f0b4c9a00000000d848304502210092535c081325ba5261699d7cfd4c503fb6125dde86389b83f40f3e2c006039bb022063cfd72aa15558dee874cac08b22dbcf11d3f06c8e48b0ddaf75b86887d604410120dedf3c8dcfff9ee2787b4bf211f960fd044fdab7fa8e922ef613a0115848a498004c6b630434ebaa5eb1752102987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4dac6782012088a9149304bce3196f344b2a22dc99db406e95ab6f3107882102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ac68ffffffff0130e07648170000001976a914d24e799df360da3ca3158d63b89ffaff27722c1588ac5ea0aa5e000000000000000000000000000000","tx_hash":"caea128b1c85a88abd5924e512780ee18952dadc217b0c06f4b2820eb71d03bc","from":["bYp9ncp3V7FYsymipriVbdd3QL72hK9hio"],"to":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"total_amount":"999.99999","spent_by_me":"0","received_by_me":"999.99998","my_balance_change":"999.99998","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN","internal_id":"caea128b1c85a88abd5924e512780ee18952dadc217b0c06f4b2820eb71d03bc"}}},{"timestamp":1588244078871,"event":{"type":"Finished"}}],"maker_amount":"999.99999","maker_coin":"MYCOIN","taker_amount":"999.99999","taker_coin":"MYCOIN1","gui":"nogui","mm_version":"UNKNOWN","success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","Finished"],"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefunded","TakerPaymentRefundFailed"]}"#; +const UNFINISHED_TAKER_SWAP: &str = r#"{"type":"Taker","uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","events":[{"timestamp":1588244036253,"event":{"type":"Started","data":{"taker_coin":"MYCOIN1","maker_coin":"MYCOIN","maker":"987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4d","my_persistent_pub":"02859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521","lock_duration":7800,"maker_amount":"999.99999","taker_amount":"999.99999","maker_payment_confirmations":1,"maker_payment_requires_nota":false,"taker_payment_confirmations":1,"taker_payment_requires_nota":false,"taker_payment_lock":1588251836,"uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","started_at":1588244036,"maker_payment_wait":1588247156,"maker_coin_start_block":12,"taker_coin_start_block":11}}},{"timestamp":1588244038239,"event":{"type":"Negotiated","data":{"maker_payment_locktime":1588259636,"maker_pubkey":"02987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4d","secret_hash":"9304bce3196f344b2a22dc99db406e95ab6f3107"}}},{"timestamp":1588244038271,"event":{"type":"TakerFeeSent","data":{"tx_hex":"0400008085202f8901bdde9bca02870787441f6068e4c2a869a3aac3d1d0925f6a6e27874343544d0a010000006a47304402206694a794693b55fbe8205cb1cbb992d92fa2a9a851763ad1bf1628c16deaf73e02203cfff465504bdd6c51e8fbd45dd2e1187142fdc29e82f36f577616d9d6097d7a012102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ffffffff02dfceab07000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac39fd41892e0000001976a914d24e799df360da3ca3158d63b89ffaff27722c1588ac46aeaa5e000000000000000000000000000000","tx_hash":"7118d7484d1cbfdd6126673a848a228856f8748d946f6a9a440c90f0d62e27c6","from":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"to":["RThtXup6Zo7LZAi8kRWgjAyi1s4u6U9Cpf","RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"total_amount":"2000","spent_by_me":"2000","received_by_me":"1998.71298873","my_balance_change":"-1.28701127","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN1","internal_id":"7118d7484d1cbfdd6126673a848a228856f8748d946f6a9a440c90f0d62e27c6"}}}],"maker_amount":"999.99999","maker_coin":"MYCOIN","taker_amount":"999.99999","taker_coin":"MYCOIN1","gui":"nogui","mm_version":"UNKNOWN","success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefunded","TakerPaymentRefundFailed"]}"#; +const FINISHED_TAKER_SWAP: &str = r#"{"type":"Taker","uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","events":[{"timestamp":1588244036253,"event":{"type":"Started","data":{"taker_coin":"MYCOIN1","maker_coin":"MYCOIN","maker":"987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4d","my_persistent_pub":"02859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521","lock_duration":7800,"maker_amount":"999.99999","taker_amount":"999.99999","maker_payment_confirmations":1,"maker_payment_requires_nota":false,"taker_payment_confirmations":1,"taker_payment_requires_nota":false,"taker_payment_lock":1588251836,"uuid":"5acb0e63-8b26-469e-81df-7dd9e4a9ad15","started_at":1588244036,"maker_payment_wait":1588247156,"maker_coin_start_block":12,"taker_coin_start_block":11}}},{"timestamp":1588244038239,"event":{"type":"Negotiated","data":{"maker_payment_locktime":1588259636,"maker_pubkey":"02987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4d","secret_hash":"9304bce3196f344b2a22dc99db406e95ab6f3107"}}},{"timestamp":1588244038271,"event":{"type":"TakerFeeSent","data":{"tx_hex":"0400008085202f8901bdde9bca02870787441f6068e4c2a869a3aac3d1d0925f6a6e27874343544d0a010000006a47304402206694a794693b55fbe8205cb1cbb992d92fa2a9a851763ad1bf1628c16deaf73e02203cfff465504bdd6c51e8fbd45dd2e1187142fdc29e82f36f577616d9d6097d7a012102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ffffffff02dfceab07000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac39fd41892e0000001976a914d24e799df360da3ca3158d63b89ffaff27722c1588ac46aeaa5e000000000000000000000000000000","tx_hash":"7118d7484d1cbfdd6126673a848a228856f8748d946f6a9a440c90f0d62e27c6","from":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"to":["RThtXup6Zo7LZAi8kRWgjAyi1s4u6U9Cpf","RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"total_amount":"2000","spent_by_me":"2000","received_by_me":"1998.71298873","my_balance_change":"-1.28701127","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN1","internal_id":"7118d7484d1cbfdd6126673a848a228856f8748d946f6a9a440c90f0d62e27c6"}}},{"timestamp":1588244038698,"event":{"type":"MakerPaymentReceived","data":{"tx_hex":"0400008085202f890163cc0aceb3b432f84c1407991a0389b74b7842f030aa261203aa0b4b9a9a15fd010000006b483045022100f3239794b7b0e1c75aae084a65535270f154231ce86a4739976ff69eeff1ebd402206c0aeb28b7b4b2e77e98b3c3efd9a20d85c2cd0627acc3f45d577c0e88c34fb4012102987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4dffffffff0118e476481700000017a914dc4ed4686174503b85374bfb0aefe07a9fb37bcf8746aeaa5e000000000000000000000000000000","tx_hash":"9a4c0b3f85ed0bb24dc9575ce7c2fd6bc50ad9d37c91c478946f9c33d15abfdf","from":["RAzSdYQhjCFdyhjrBz1AZQDVg3Hu8DrzYc"],"to":["bYp9ncp3V7FYsymipriVbdd3QL72hK9hio"],"total_amount":"1000","spent_by_me":"0","received_by_me":"0","my_balance_change":"0","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN","internal_id":"9a4c0b3f85ed0bb24dc9575ce7c2fd6bc50ad9d37c91c478946f9c33d15abfdf"}}},{"timestamp":1588244038699,"event":{"type":"MakerPaymentWaitConfirmStarted"}},{"timestamp":1588244053712,"event":{"type":"MakerPaymentValidatedAndConfirmed"}},{"timestamp":1588244053745,"event":{"type":"TakerPaymentSent","data":{"tx_hex":"0400008085202f8901c6272ed6f0900c449a6a6f948d74f85688228a843a672661ddbf1c4d48d71871010000006b483045022100c37627385c66b7bdf466b4dd4e7095b7551e6f5ea35f9bcc6344eb629f2edcb202203280eaba64b4d72010500166fab62cf34a687a516b2fe83d4eceaf8572cb37a7012102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ffffffff0218e476481700000017a91431ff75ed72cd135a7ce50d121e71efc37066b9f9873915cb40170000001976a914d24e799df360da3ca3158d63b89ffaff27722c1588ac55aeaa5e000000000000000000000000000000","tx_hash":"b4718ce94aa43f9073ab0b70c18ab4ea4b587338fb7110f20ff7d1bb452df08f","from":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"to":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC","bHHdqM8XWDHee2oyzydwUJKEE2BjofEtZH"],"total_amount":"1998.71298873","spent_by_me":"1998.71298873","received_by_me":"998.71298873","my_balance_change":"-1000","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN1","internal_id":"b4718ce94aa43f9073ab0b70c18ab4ea4b587338fb7110f20ff7d1bb452df08f"}}},{"timestamp":1588244078860,"event":{"type":"TakerPaymentSpent","data":{"transaction":{"tx_hex":"0400008085202f89018ff02d45bbd1f70ff21071fb3873584beab48ac1700bab73903fa44ae98c71b400000000d747304402204f0d641a3916e54d6788744c3229110a431eff18634c66fbd1741f9ca7dba99d02202315ee1d9317cc4d5d75d01f066f2c8a59876f790106d310144cdc03c25f985e0120dedf3c8dcfff9ee2787b4bf211f960fd044fdab7fa8e922ef613a0115848a498004c6b6304bcccaa5eb1752102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ac6782012088a9149304bce3196f344b2a22dc99db406e95ab6f3107882102987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4dac68ffffffff0130e07648170000001976a91412c553e8469363f2d30268c475af1e9186cc90af88ac54a0aa5e000000000000000000000000000000","tx_hash":"e7aed7a77e47b44dc9d12166589bbade70faea10b64888f73ed4be04bcc9f9a9","from":["bHHdqM8XWDHee2oyzydwUJKEE2BjofEtZH"],"to":["RAzSdYQhjCFdyhjrBz1AZQDVg3Hu8DrzYc"],"total_amount":"999.99999","spent_by_me":"0","received_by_me":"0","my_balance_change":"0","block_height":29,"timestamp":1588244070,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN1","internal_id":"e7aed7a77e47b44dc9d12166589bbade70faea10b64888f73ed4be04bcc9f9a9"},"secret":"dedf3c8dcfff9ee2787b4bf211f960fd044fdab7fa8e922ef613a0115848a498"}}},{"timestamp":1588244078870,"event":{"type":"MakerPaymentSpent","data":{"tx_hex":"0400008085202f8901dfbf5ad1339c6f9478c4917cd3d90ac56bfdc2e75c57c94db20bed853f0b4c9a00000000d848304502210092535c081325ba5261699d7cfd4c503fb6125dde86389b83f40f3e2c006039bb022063cfd72aa15558dee874cac08b22dbcf11d3f06c8e48b0ddaf75b86887d604410120dedf3c8dcfff9ee2787b4bf211f960fd044fdab7fa8e922ef613a0115848a498004c6b630434ebaa5eb1752102987d5f82205a55d789616f470ae9df48537f050ee050d501aa316651642a0a4dac6782012088a9149304bce3196f344b2a22dc99db406e95ab6f3107882102859a80b83941e4e8ff2f511080e3ea5021db4ba95caec30eb37864e71ae73521ac68ffffffff0130e07648170000001976a914d24e799df360da3ca3158d63b89ffaff27722c1588ac5ea0aa5e000000000000000000000000000000","tx_hash":"caea128b1c85a88abd5924e512780ee18952dadc217b0c06f4b2820eb71d03bc","from":["bYp9ncp3V7FYsymipriVbdd3QL72hK9hio"],"to":["RUTBzLtJNTn89Wkb6oZocbesKrjBDTRMrC"],"total_amount":"999.99999","spent_by_me":"0","received_by_me":"999.99998","my_balance_change":"999.99998","block_height":0,"timestamp":0,"fee_details":{"amount":"0.00001"},"coin":"MYCOIN","internal_id":"caea128b1c85a88abd5924e512780ee18952dadc217b0c06f4b2820eb71d03bc"}}},{"timestamp":1588244078871,"event":{"type":"MakerPaymentSpendConfirmed"}},{"timestamp":1588244078872,"event":{"type":"Finished"}}],"maker_amount":"999.99999","maker_coin":"MYCOIN","taker_amount":"999.99999","taker_coin":"MYCOIN1","gui":"nogui","mm_version":"UNKNOWN","success_events":["Started","Negotiated","TakerFeeSent","MakerPaymentReceived","MakerPaymentWaitConfirmStarted","MakerPaymentValidatedAndConfirmed","TakerPaymentSent","TakerPaymentSpent","MakerPaymentSpent","MakerPaymentSpendConfirmed","Finished"],"error_events":["StartFailed","NegotiateFailed","TakerFeeSendFailed","MakerPaymentValidateFailed","MakerPaymentWaitConfirmFailed","TakerPaymentTransactionFailed","TakerPaymentWaitConfirmFailed","TakerPaymentDataSendFailed","TakerPaymentWaitForSpendFailed","MakerPaymentSpendFailed","TakerPaymentWaitRefundStarted","TakerPaymentRefunded","TakerPaymentRefundFailed"]}"#; fn swap_file_lock_prevents_double_swap_start_on_kick_start(swap_json: &str) { let (_ctx, _, bob_priv_key) = generate_utxo_coin_with_random_privkey("MYCOIN", 1000.into()); diff --git a/mm2src/mm2_test_helpers/src/for_tests.rs b/mm2src/mm2_test_helpers/src/for_tests.rs index 196112cc0b..ffe7659a43 100644 --- a/mm2src/mm2_test_helpers/src/for_tests.rs +++ b/mm2src/mm2_test_helpers/src/for_tests.rs @@ -81,7 +81,7 @@ pub const MAKER_ERROR_EVENTS: [&str; 15] = [ "MakerPaymentRefundFinished", ]; -pub const TAKER_SUCCESS_EVENTS: [&str; 11] = [ +pub const TAKER_SUCCESS_EVENTS: [&str; 12] = [ "Started", "Negotiated", "TakerFeeSent", @@ -92,10 +92,11 @@ pub const TAKER_SUCCESS_EVENTS: [&str; 11] = [ "TakerPaymentSent", "TakerPaymentSpent", "MakerPaymentSpent", + "MakerPaymentSpendConfirmed", "Finished", ]; -pub const TAKER_USING_WATCHERS_SUCCESS_EVENTS: [&str; 13] = [ +pub const TAKER_USING_WATCHERS_SUCCESS_EVENTS: [&str; 14] = [ "Started", "Negotiated", "TakerFeeSent", @@ -108,11 +109,12 @@ pub const TAKER_USING_WATCHERS_SUCCESS_EVENTS: [&str; 13] = [ "TakerPaymentSpent", "MakerPaymentSpent", "MakerPaymentSpentByWatcher", + "MakerPaymentSpendConfirmed", "Finished", ]; // Taker using watchers and watcher spends maker payment -pub const TAKER_ACTUAL_EVENTS_WATCHER_SPENDS_MAKER_PAYMENT: [&str; 12] = [ +pub const TAKER_ACTUAL_EVENTS_WATCHER_SPENDS_MAKER_PAYMENT: [&str; 13] = [ "Started", "Negotiated", "TakerFeeSent", @@ -124,11 +126,12 @@ pub const TAKER_ACTUAL_EVENTS_WATCHER_SPENDS_MAKER_PAYMENT: [&str; 12] = [ "WatcherMessageSent", "TakerPaymentSpent", "MakerPaymentSpentByWatcher", + "MakerPaymentSpendConfirmed", "Finished", ]; // Taker using watchers and spends maker payment instead of watcher -pub const TAKER_ACTUAL_EVENTS_TAKER_SPENDS_MAKER_PAYMENT: [&str; 12] = [ +pub const TAKER_ACTUAL_EVENTS_TAKER_SPENDS_MAKER_PAYMENT: [&str; 13] = [ "Started", "Negotiated", "TakerFeeSent", @@ -140,10 +143,11 @@ pub const TAKER_ACTUAL_EVENTS_TAKER_SPENDS_MAKER_PAYMENT: [&str; 12] = [ "WatcherMessageSent", "TakerPaymentSpent", "MakerPaymentSpent", + "MakerPaymentSpendConfirmed", "Finished", ]; -pub const TAKER_ERROR_EVENTS: [&str; 16] = [ +pub const TAKER_ERROR_EVENTS: [&str; 17] = [ "StartFailed", "NegotiateFailed", "TakerFeeSendFailed", @@ -154,6 +158,7 @@ pub const TAKER_ERROR_EVENTS: [&str; 16] = [ "TakerPaymentDataSendFailed", "TakerPaymentWaitForSpendFailed", "MakerPaymentSpendFailed", + "MakerPaymentSpendConfirmFailed", "TakerPaymentWaitRefundStarted", "TakerPaymentRefundStarted", "TakerPaymentRefunded", From ce183766480abafb7275a10f384f308c60711118 Mon Sep 17 00:00:00 2001 From: shamardy <39480341+shamardy@users.noreply.github.com> Date: Fri, 4 Oct 2024 14:20:52 +0300 Subject: [PATCH 26/29] fix(orders): fix cancel order race condition using time-based cache (#2232) This commit addresses the issue where cancelled orders can sometimes remain in the orderbook due to P2P message caching and out-of-order message delivery. --- Cargo.lock | 1 + .../eth/web3_transport/websocket_transport.rs | 17 +--- mm2src/common/expirable_map.rs | 90 +++++++++++++------ mm2src/mm2_core/Cargo.toml | 1 + mm2src/mm2_core/src/data_asker.rs | 2 - mm2src/mm2_core/src/mm_ctx.rs | 3 +- mm2src/mm2_main/Cargo.toml | 2 +- mm2src/mm2_main/src/lp_healthcheck.rs | 74 +-------------- mm2src/mm2_main/src/lp_ordermatch.rs | 86 +++++++++++++----- .../tests/mm2_tests/orderbook_sync_tests.rs | 75 +++++++++++++++- mm2src/mm2_p2p/src/lib.rs | 65 ++++++++++++++ 11 files changed, 278 insertions(+), 138 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 431959e5c5..08a985285b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4404,6 +4404,7 @@ dependencies = [ "mm2_err_handle", "mm2_event_stream", "mm2_metrics", + "mm2_p2p", "mm2_rpc", "primitives", "rand 0.7.3", diff --git a/mm2src/coins/eth/web3_transport/websocket_transport.rs b/mm2src/coins/eth/web3_transport/websocket_transport.rs index d17ef99d7e..fd1220e92e 100644 --- a/mm2src/coins/eth/web3_transport/websocket_transport.rs +++ b/mm2src/coins/eth/web3_transport/websocket_transport.rs @@ -97,12 +97,7 @@ impl WebsocketTransport { } } - async fn handle_keepalive( - &self, - wsocket: &mut WebSocketStream, - response_notifiers: &mut ExpirableMap>>, - expires_at: Option, - ) -> OuterAction { + async fn handle_keepalive(&self, wsocket: &mut WebSocketStream, expires_at: Option) -> OuterAction { const SIMPLE_REQUEST: &str = r#"{"jsonrpc":"2.0","method":"net_version","params":[],"id": 0 }"#; if let Some(expires_at) = expires_at { @@ -112,10 +107,7 @@ impl WebsocketTransport { } } - // Drop expired response notifier channels - response_notifiers.clear_expired_entries(); - - let mut should_continue = Default::default(); + let mut should_continue = false; for _ in 0..MAX_ATTEMPTS { match wsocket .send(tokio_tungstenite_wasm::Message::Text(SIMPLE_REQUEST.to_string())) @@ -206,9 +198,6 @@ impl WebsocketTransport { } if let Some(id) = inc_event.get("id") { - // just to ensure we don't have outdated entries - response_notifiers.clear_expired_entries(); - let request_id = id.as_u64().unwrap_or_default() as usize; if let Some(notifier) = response_notifiers.remove(&request_id) { @@ -279,7 +268,7 @@ impl WebsocketTransport { loop { futures_util::select! { _ = keepalive_interval.next().fuse() => { - match self.handle_keepalive(&mut wsocket, &mut response_notifiers, expires_at).await { + match self.handle_keepalive(&mut wsocket, expires_at).await { OuterAction::None => {}, OuterAction::Continue => continue, OuterAction::Break => break, diff --git a/mm2src/common/expirable_map.rs b/mm2src/common/expirable_map.rs index e9fe7f0b4f..996e2edfae 100644 --- a/mm2src/common/expirable_map.rs +++ b/mm2src/common/expirable_map.rs @@ -5,7 +5,7 @@ use instant::{Duration, Instant}; use rustc_hash::FxHashMap; -use std::hash::Hash; +use std::{collections::BTreeMap, hash::Hash}; #[derive(Clone, Debug)] pub struct ExpirableEntry { @@ -36,45 +36,83 @@ impl ExpirableEntry { pub fn has_longer_life_than(&self, min_ttl: Duration) -> bool { self.expires_at > Instant::now() + min_ttl } } -impl Default for ExpirableMap { +impl Default for ExpirableMap { fn default() -> Self { Self::new() } } /// A map that allows associating values with keys and expiring entries. -/// It is important to note that this implementation does not automatically -/// remove any entries; it is the caller's responsibility to invoke `clear_expired_entries` -/// at specified intervals. +/// It is important to note that this implementation does not have a background worker to +/// automatically clear expired entries. Outdated entries are only removed when the control flow +/// is handed back to the map mutably (i.e. some mutable method of the map is invoked). /// /// WARNING: This is designed for performance-oriented use-cases utilizing `FxHashMap` /// under the hood and is not suitable for cryptographic purposes. #[derive(Clone, Debug)] -pub struct ExpirableMap(FxHashMap>); +pub struct ExpirableMap { + map: FxHashMap>, + /// A sorted inverse map from expiration times to keys to speed up expired entries clearing. + expiries: BTreeMap, +} -impl ExpirableMap { +impl ExpirableMap { /// Creates a new empty `ExpirableMap` #[inline] - pub fn new() -> Self { Self(FxHashMap::default()) } + pub fn new() -> Self { + Self { + map: FxHashMap::default(), + expiries: BTreeMap::new(), + } + } + + /// Returns the associated value if present and not expired. + #[inline] + pub fn get(&self, k: &K) -> Option<&V> { + self.map + .get(k) + .filter(|v| v.expires_at > Instant::now()) + .map(|v| &v.value) + } - /// Returns the associated value if present. + /// Removes a key-value pair from the map and returns the associated value if present and not expired. #[inline] - pub fn get(&mut self, k: &K) -> Option<&V> { self.0.get(k).map(|v| &v.value) } + pub fn remove(&mut self, k: &K) -> Option { + self.map.remove(k).filter(|v| v.expires_at > Instant::now()).map(|v| { + self.expiries.remove(&v.expires_at); + v.value + }) + } /// Inserts a key-value pair with an expiration duration. /// /// If a value already exists for the given key, it will be updated and then /// the old one will be returned. pub fn insert(&mut self, k: K, v: V, exp: Duration) -> Option { + self.clear_expired_entries(); let entry = ExpirableEntry::new(v, exp); - - self.0.insert(k, entry).map(|v| v.value) + self.expiries.insert(entry.expires_at, k); + self.map.insert(k, entry).map(|v| v.value) } /// Removes expired entries from the map. - pub fn clear_expired_entries(&mut self) { self.0.retain(|_k, v| Instant::now() < v.expires_at); } - - /// Removes a key-value pair from the map and returns the associated value if present. - #[inline] - pub fn remove(&mut self, k: &K) -> Option { self.0.remove(k).map(|v| v.value) } + /// + /// Iterates through the `expiries` in order, removing entries that have expired. + /// Stops at the first non-expired entry, leveraging the sorted nature of `BTreeMap`. + fn clear_expired_entries(&mut self) { + let now = Instant::now(); + + // `pop_first()` is used here as it efficiently removes expired entries. + // `first_key_value()` was considered as it wouldn't need re-insertion for + // non-expired entries, but it would require an extra remove operation for + // each expired entry. `pop_first()` needs only one re-insertion per call, + // which is an acceptable trade-off compared to multiple remove operations. + while let Some((exp, key)) = self.expiries.pop_first() { + if exp > now { + self.expiries.insert(exp, key); + break; + } + self.map.remove(&key); + } + } } #[cfg(any(test, target_arch = "wasm32"))] @@ -94,8 +132,8 @@ mod tests { let exp = Duration::from_secs(1); // Insert 2 entries with 1 sec expiration time - expirable_map.insert("key1".to_string(), value.to_string(), exp); - expirable_map.insert("key2".to_string(), value.to_string(), exp); + expirable_map.insert("key1", value, exp); + expirable_map.insert("key2", value, exp); // Wait for entries to expire Timer::sleep(2.).await; @@ -104,14 +142,14 @@ mod tests { expirable_map.clear_expired_entries(); // We waited for 2 seconds, so we shouldn't have any entry accessible - assert_eq!(expirable_map.0.len(), 0); + assert_eq!(expirable_map.map.len(), 0); // Insert 5 entries - expirable_map.insert("key1".to_string(), value.to_string(), Duration::from_secs(5)); - expirable_map.insert("key2".to_string(), value.to_string(), Duration::from_secs(4)); - expirable_map.insert("key3".to_string(), value.to_string(), Duration::from_secs(7)); - expirable_map.insert("key4".to_string(), value.to_string(), Duration::from_secs(2)); - expirable_map.insert("key5".to_string(), value.to_string(), Duration::from_millis(3750)); + expirable_map.insert("key1", value, Duration::from_secs(5)); + expirable_map.insert("key2", value, Duration::from_secs(4)); + expirable_map.insert("key3", value, Duration::from_secs(7)); + expirable_map.insert("key4", value, Duration::from_secs(2)); + expirable_map.insert("key5", value, Duration::from_millis(3750)); // Wait 2 seconds to expire some entries Timer::sleep(2.).await; @@ -120,6 +158,6 @@ mod tests { expirable_map.clear_expired_entries(); // We waited for 2 seconds, only one entry should expire - assert_eq!(expirable_map.0.len(), 4); + assert_eq!(expirable_map.map.len(), 4); }); } diff --git a/mm2src/mm2_core/Cargo.toml b/mm2src/mm2_core/Cargo.toml index 943dcac280..a9d308be9a 100644 --- a/mm2src/mm2_core/Cargo.toml +++ b/mm2src/mm2_core/Cargo.toml @@ -20,6 +20,7 @@ lazy_static = "1.4" mm2_err_handle = { path = "../mm2_err_handle" } mm2_event_stream = { path = "../mm2_event_stream" } mm2_metrics = { path = "../mm2_metrics" } +mm2_libp2p = { path = "../mm2_p2p", package = "mm2_p2p" } primitives = { path = "../mm2_bitcoin/primitives" } rand = { version = "0.7", features = ["std", "small_rng", "wasm-bindgen"] } serde = "1" diff --git a/mm2src/mm2_core/src/data_asker.rs b/mm2src/mm2_core/src/data_asker.rs index 5d01310630..7f32f93365 100644 --- a/mm2src/mm2_core/src/data_asker.rs +++ b/mm2src/mm2_core/src/data_asker.rs @@ -113,8 +113,6 @@ pub async fn send_asked_data_rpc( asked_data: SendAskedDataRequest, ) -> Result> { let mut awaiting_asks = ctx.data_asker.awaiting_asks.lock().await; - awaiting_asks.clear_expired_entries(); - match awaiting_asks.remove(&asked_data.data_id) { Some(sender) => { sender.send(asked_data.data).map_to_mm(|_| { diff --git a/mm2src/mm2_core/src/mm_ctx.rs b/mm2src/mm2_core/src/mm_ctx.rs index 6e060d91c3..3f56970f3c 100644 --- a/mm2src/mm2_core/src/mm_ctx.rs +++ b/mm2src/mm2_core/src/mm_ctx.rs @@ -10,6 +10,7 @@ use futures::lock::Mutex as AsyncMutex; use gstuff::{try_s, Constructible, ERR, ERRL}; use lazy_static::lazy_static; use mm2_event_stream::{controller::Controller, Event, EventStreamConfiguration}; +use mm2_libp2p::PeerAddress; use mm2_metrics::{MetricsArc, MetricsOps}; use primitives::hash::H160; use rand::Rng; @@ -145,7 +146,7 @@ pub struct MmCtx { #[cfg(not(target_arch = "wasm32"))] pub async_sqlite_connection: Constructible>>, /// Links the RPC context to the P2P context to handle health check responses. - pub healthcheck_response_handler: AsyncMutex>>, + pub healthcheck_response_handler: AsyncMutex>>, } impl MmCtx { diff --git a/mm2src/mm2_main/Cargo.toml b/mm2src/mm2_main/Cargo.toml index 53154efa05..07091e2631 100644 --- a/mm2src/mm2_main/Cargo.toml +++ b/mm2src/mm2_main/Cargo.toml @@ -65,7 +65,7 @@ mm2_err_handle = { path = "../mm2_err_handle" } mm2_event_stream = { path = "../mm2_event_stream" } mm2_gui_storage = { path = "../mm2_gui_storage" } mm2_io = { path = "../mm2_io" } -mm2-libp2p = { path = "../mm2_p2p", package = "mm2_p2p" } +mm2_libp2p = { path = "../mm2_p2p", package = "mm2_p2p" } mm2_metrics = { path = "../mm2_metrics" } mm2_net = { path = "../mm2_net", features = ["event-stream", "p2p"] } mm2_number = { path = "../mm2_number" } diff --git a/mm2src/mm2_main/src/lp_healthcheck.rs b/mm2src/mm2_main/src/lp_healthcheck.rs index 722bc21402..849f478d5f 100644 --- a/mm2src/mm2_main/src/lp_healthcheck.rs +++ b/mm2src/mm2_main/src/lp_healthcheck.rs @@ -9,12 +9,11 @@ use instant::{Duration, Instant}; use lazy_static::lazy_static; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::MmError; -use mm2_libp2p::{decode_message, encode_message, pub_sub_topic, Libp2pPublic, TopicPrefix}; +use mm2_libp2p::{decode_message, encode_message, pub_sub_topic, Libp2pPublic, PeerAddress, TopicPrefix}; use mm2_net::p2p::P2PContext; use ser_error_derive::SerializeErrorType; use serde::{Deserialize, Serialize}; use std::convert::TryFrom; -use std::str::FromStr; use std::sync::Mutex; use crate::lp_network::broadcast_p2p_msg; @@ -37,71 +36,6 @@ pub(crate) struct HealthcheckMessage { data: HealthcheckData, } -/// Wrapper of `libp2p::PeerId` with trait additional implementations. -/// -/// TODO: This should be used as a replacement of `libp2p::PeerId` in the entire project. -#[derive(Clone, Copy, Debug, Display, PartialEq)] -pub struct PeerAddress(mm2_libp2p::PeerId); - -impl From for PeerAddress { - fn from(value: mm2_libp2p::PeerId) -> Self { Self(value) } -} - -impl From for mm2_libp2p::PeerId { - fn from(value: PeerAddress) -> Self { value.0 } -} - -impl Serialize for PeerAddress { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&self.0.to_string()) - } -} - -impl<'de> Deserialize<'de> for PeerAddress { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - struct PeerAddressVisitor; - - impl<'de> serde::de::Visitor<'de> for PeerAddressVisitor { - type Value = PeerAddress; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a string representation of peer id.") - } - - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - if value.len() > 100 { - return Err(serde::de::Error::invalid_length( - value.len(), - &"peer id cannot exceed 100 characters.", - )); - } - - Ok(mm2_libp2p::PeerId::from_str(value) - .map_err(serde::de::Error::custom)? - .into()) - } - - fn visit_string(self, value: String) -> Result - where - E: serde::de::Error, - { - self.visit_str(&value) - } - } - - deserializer.deserialize_str(PeerAddressVisitor) - } -} - #[derive(Debug, Display)] enum SignValidationError { #[display( @@ -331,8 +265,7 @@ pub async fn peer_connection_healthcheck_rpc( { let mut book = ctx.healthcheck_response_handler.lock().await; - book.clear_expired_entries(); - book.insert(target_peer_address.to_string(), tx, address_record_exp); + book.insert(target_peer_address, tx, address_record_exp); } broadcast_p2p_msg( @@ -395,7 +328,7 @@ pub(crate) async fn process_p2p_healthcheck_message(ctx: &MmArc, message: mm2_li } else { // The requested peer is healthy; signal the response channel. let mut response_handler = ctx.healthcheck_response_handler.lock().await; - if let Some(tx) = response_handler.remove(&sender_peer.to_string()) { + if let Some(tx) = response_handler.remove(&sender_peer) { if tx.send(()).is_err() { log::error!("Result channel isn't present for peer '{sender_peer}'."); }; @@ -409,6 +342,7 @@ pub(crate) async fn process_p2p_healthcheck_message(ctx: &MmArc, message: mm2_li #[cfg(any(test, target_arch = "wasm32"))] mod tests { use std::mem::discriminant; + use std::str::FromStr; use super::*; use common::cross_test; diff --git a/mm2src/mm2_main/src/lp_ordermatch.rs b/mm2src/mm2_main/src/lp_ordermatch.rs index 9829fed75a..dc9b85082b 100644 --- a/mm2src/mm2_main/src/lp_ordermatch.rs +++ b/mm2src/mm2_main/src/lp_ordermatch.rs @@ -122,6 +122,7 @@ const TAKER_ORDER_TIMEOUT: u64 = 30; const ORDER_MATCH_TIMEOUT: u64 = 30; const ORDERBOOK_REQUESTING_TIMEOUT: u64 = MIN_ORDER_KEEP_ALIVE_INTERVAL * 2; const MAX_ORDERS_NUMBER_IN_ORDERBOOK_RESPONSE: usize = 1000; +const RECENTLY_CANCELLED_TIMEOUT: Duration = Duration::from_secs(120); #[cfg(not(test))] const TRIE_STATE_HISTORY_TIMEOUT: u64 = 14400; #[cfg(test)] @@ -331,6 +332,12 @@ async fn process_orders_keep_alive( Ok(()) } +#[inline] +fn process_maker_order_created(ctx: &MmArc, from_pubkey: String, created_msg: new_protocol::MakerOrderCreated) { + let order: OrderbookItem = (created_msg, from_pubkey).into(); + insert_or_update_order(ctx, order); +} + fn process_maker_order_updated( ctx: MmArc, from_pubkey: String, @@ -350,6 +357,22 @@ fn process_maker_order_updated( Ok(()) } +fn process_maker_order_cancelled(ctx: &MmArc, from_pubkey: String, cancelled_msg: new_protocol::MakerOrderCancelled) { + let uuid = Uuid::from(cancelled_msg.uuid); + let ordermatch_ctx = OrdermatchContext::from_ctx(ctx).expect("from_ctx failed"); + let mut orderbook = ordermatch_ctx.orderbook.lock(); + // Add the order to the recently cancelled list to ignore it if a new order with the same uuid + // is received within the `RECENTLY_CANCELLED_TIMEOUT` timeframe. + // We do this even if the order is in the order_set, because it could have been added through + // means other than the order creation message. + orderbook.recently_cancelled.insert(uuid, from_pubkey.clone()); + if let Some(order) = orderbook.order_set.get(&uuid) { + if order.pubkey == from_pubkey { + orderbook.remove_order_trie_update(uuid); + } + } +} + // fn verify_pubkey_orderbook(orderbook: &GetOrderbookPubkeyItem) -> Result<(), String> { // let keys: Vec<(_, _)> = orderbook // .orders @@ -456,16 +479,6 @@ fn insert_or_update_my_order(ctx: &MmArc, item: OrderbookItem, my_order: &MakerO } } -fn delete_order(ctx: &MmArc, pubkey: &str, uuid: Uuid) { - let ordermatch_ctx = OrdermatchContext::from_ctx(ctx).expect("from_ctx failed"); - let mut orderbook = ordermatch_ctx.orderbook.lock(); - if let Some(order) = orderbook.order_set.get(&uuid) { - if order.pubkey == pubkey { - orderbook.remove_order_trie_update(uuid); - } - } -} - fn delete_my_order(ctx: &MmArc, uuid: Uuid, p2p_privkey: Option) { let ordermatch_ctx: Arc = OrdermatchContext::from_ctx(ctx).expect("from_ctx failed"); let mut orderbook = ordermatch_ctx.orderbook.lock(); @@ -548,8 +561,7 @@ pub async fn process_msg(ctx: MmArc, from_peer: String, msg: &[u8], i_am_relay: log::debug!("received ordermatch message {:?}", message); match message { new_protocol::OrdermatchMessage::MakerOrderCreated(created_msg) => { - let order: OrderbookItem = (created_msg, hex::encode(pubkey.to_bytes().as_slice())).into(); - insert_or_update_order(&ctx, order); + process_maker_order_created(&ctx, pubkey.to_hex(), created_msg); Ok(()) }, new_protocol::OrdermatchMessage::PubkeyKeepAlive(keep_alive) => { @@ -576,7 +588,7 @@ pub async fn process_msg(ctx: MmArc, from_peer: String, msg: &[u8], i_am_relay: Ok(()) }, new_protocol::OrdermatchMessage::MakerOrderCancelled(cancelled_msg) => { - delete_order(&ctx, &pubkey.to_hex(), cancelled_msg.uuid.into()); + process_maker_order_cancelled(&ctx, pubkey.to_hex(), cancelled_msg); Ok(()) }, new_protocol::OrdermatchMessage::MakerOrderUpdated(updated_msg) => { @@ -1083,15 +1095,15 @@ fn maker_order_updated_p2p_notify( broadcast_p2p_msg(&ctx, topic, encoded_msg, peer_id); } -fn maker_order_cancelled_p2p_notify(ctx: MmArc, order: &MakerOrder) { +fn maker_order_cancelled_p2p_notify(ctx: &MmArc, order: &MakerOrder) { let message = new_protocol::OrdermatchMessage::MakerOrderCancelled(new_protocol::MakerOrderCancelled { uuid: order.uuid.into(), timestamp: now_sec(), pair_trie_root: H64::default(), }); - delete_my_order(&ctx, order.uuid, order.p2p_privkey); + delete_my_order(ctx, order.uuid, order.p2p_privkey); log::debug!("maker_order_cancelled_p2p_notify called, message {:?}", message); - broadcast_ordermatch_message(&ctx, order.orderbook_topic(), message, order.p2p_keypair()); + broadcast_ordermatch_message(ctx, order.orderbook_topic(), message, order.p2p_keypair()); } pub struct BalanceUpdateOrdermatchHandler { @@ -1141,7 +1153,7 @@ impl BalanceTradeFeeUpdatedHandler for BalanceUpdateOrdermatchHandler { // This checks that the order hasn't been removed by another process if removed_order_mutex.is_some() { // cancel the order - maker_order_cancelled_p2p_notify(ctx.clone(), &order); + maker_order_cancelled_p2p_notify(&ctx, &order); delete_my_maker_order( ctx.clone(), order.clone(), @@ -2477,7 +2489,6 @@ fn collect_orderbook_metrics(ctx: &MmArc, orderbook: &Orderbook) { mm_gauge!(ctx.metrics, "orderbook.memory_db", memory_db_size as f64); } -#[derive(Default)] struct Orderbook { /// A map from (base, rel). ordered: HashMap<(String, String), BTreeSet>, @@ -2490,12 +2501,34 @@ struct Orderbook { order_set: HashMap, /// a map of orderbook states of known maker pubkeys pubkeys_state: HashMap, + /// The `TimeCache` of recently canceled orders, mapping `Uuid` to the maker pubkey as `String`, + /// used to avoid order recreation in case of out-of-order p2p messages, + /// e.g., when receiving the order cancellation message before the order is created. + /// Entries are kept for `RECENTLY_CANCELLED_TIMEOUT` seconds. + recently_cancelled: TimeCache, topics_subscribed_to: HashMap, /// MemoryDB instance to store Patricia Tries data memory_db: MemoryDB, my_p2p_pubkeys: HashSet, } +impl Default for Orderbook { + fn default() -> Self { + Orderbook { + ordered: HashMap::default(), + pairs_existing_for_base: HashMap::default(), + pairs_existing_for_rel: HashMap::default(), + unordered: HashMap::default(), + order_set: HashMap::default(), + pubkeys_state: HashMap::default(), + recently_cancelled: TimeCache::new(RECENTLY_CANCELLED_TIMEOUT), + topics_subscribed_to: HashMap::default(), + memory_db: MemoryDB::default(), + my_p2p_pubkeys: HashSet::default(), + } + } +} + fn hashed_null_node() -> TrieHash { ::hashed_null_node() } impl Orderbook { @@ -2512,6 +2545,12 @@ impl Orderbook { fn find_order_by_uuid(&self, uuid: &Uuid) -> Option { self.order_set.get(uuid).cloned() } fn insert_or_update_order_update_trie(&mut self, order: OrderbookItem) { + // Ignore the order if it was recently cancelled + if self.recently_cancelled.get(&order.uuid) == Some(&order.pubkey) { + warn!("Maker order {} was recently cancelled, ignoring", order.uuid); + return; + } + let zero = BigRational::from_integer(0.into()); if order.max_volume <= zero || order.price <= zero || order.min_volume < zero { self.remove_order_trie_update(order.uuid); @@ -3314,6 +3353,7 @@ pub async fn lp_ordermatch_loop(ctx: MmArc) { // This checks that the order hasn't been removed by another process if let Some(order_mutex) = removed_order_mutex { let order = order_mutex.lock().await; + maker_order_cancelled_p2p_notify(&ctx, &order); delete_my_maker_order( ctx.clone(), order.clone(), @@ -3430,7 +3470,7 @@ async fn check_balance_for_maker_orders(ctx: MmArc, ordermatch_ctx: &OrdermatchC let removed_order_mutex = ordermatch_ctx.maker_orders_ctx.lock().remove_order(&uuid); // This checks that the order hasn't been removed by another process if removed_order_mutex.is_some() { - maker_order_cancelled_p2p_notify(ctx.clone(), &order); + maker_order_cancelled_p2p_notify(&ctx, &order); delete_my_maker_order(ctx.clone(), order.clone(), reason) .compat() .await @@ -4739,7 +4779,7 @@ async fn cancel_previous_maker_orders( let removed_order_mutex = ordermatch_ctx.maker_orders_ctx.lock().remove_order(&uuid); // This checks that the uuid, &order.base hasn't been removed by another process if removed_order_mutex.is_some() { - maker_order_cancelled_p2p_notify(ctx.clone(), &order); + maker_order_cancelled_p2p_notify(ctx, &order); delete_my_maker_order(ctx.clone(), order.clone(), MakerOrderCancellationReason::Cancelled) .compat() .await @@ -5141,7 +5181,7 @@ pub async fn cancel_order(ctx: MmArc, req: CancelOrderReq) -> Result Result> let removed_order_mutex = ordermatch_ctx.maker_orders_ctx.lock().remove_order(&order.uuid); // This checks that the order hasn't been removed by another process if removed_order_mutex.is_some() { - maker_order_cancelled_p2p_notify(ctx.clone(), &order); + maker_order_cancelled_p2p_notify(&ctx, &order); delete_my_maker_order(ctx, order.clone(), MakerOrderCancellationReason::Cancelled) .compat() .await @@ -5539,7 +5579,7 @@ pub async fn cancel_orders_by(ctx: &MmArc, cancel_by: CancelBy) -> Result<(Vec = Secp256k1::signing_only(); } +/// Wrapper of `libp2p::PeerId` with trait additional implementations. +/// +/// TODO: This should be used as a replacement of `libp2p::PeerId` in the entire project. +#[derive(Clone, Copy, Debug, Display, Eq, Hash, PartialEq)] +pub struct PeerAddress(PeerId); + +impl From for PeerAddress { + fn from(value: PeerId) -> Self { Self(value) } +} + +impl From for PeerId { + fn from(value: PeerAddress) -> Self { value.0 } +} + +impl Serialize for PeerAddress { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.0.to_string()) + } +} + +impl<'de> Deserialize<'de> for PeerAddress { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct PeerAddressVisitor; + + impl<'de> serde::de::Visitor<'de> for PeerAddressVisitor { + type Value = PeerAddress; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a string representation of peer id.") + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + if value.len() > 100 { + return Err(serde::de::Error::invalid_length( + value.len(), + &"peer id cannot exceed 100 characters.", + )); + } + + Ok(PeerId::from_str(value).map_err(de::Error::custom)?.into()) + } + + fn visit_string(self, value: String) -> Result + where + E: de::Error, + { + self.visit_str(&value) + } + } + + deserializer.deserialize_str(PeerAddressVisitor) + } +} + #[derive(Clone, Copy, Debug)] pub enum NetworkInfo { /// The in-memory network. From e65fefe5adfdff6e3d1fb0d31b65e5ef78b2e437 Mon Sep 17 00:00:00 2001 From: Omer Yacine Date: Mon, 7 Oct 2024 09:40:35 +0300 Subject: [PATCH 27/29] chore(cli): remove leftover subcommands from help message (#2235) --- mm2src/mm2_main/src/mm2.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/mm2src/mm2_main/src/mm2.rs b/mm2src/mm2_main/src/mm2.rs index 5224ed1b92..dd7c7bed27 100644 --- a/mm2src/mm2_main/src/mm2.rs +++ b/mm2src/mm2_main/src/mm2.rs @@ -204,7 +204,6 @@ Some (but not all) of the JSON configuration parameters (* - required): {"coins": [{"name": "dash", "coin": "DASH", ...}, ...], ...}. coins .. Information about the currencies: their ticker symbols, names, ports, addresses, etc. If the field isn't present on the command line then we try loading it from the 'coins' file. - crash .. Simulate a crash to check how the crash handling works. dbdir .. MM database path. 'DB' by default. gui .. The information about GUI app using KDF instance. Included in swap statuses shared with network. .. It's recommended to put essential info to this field (application name, OS, version, etc). @@ -213,7 +212,6 @@ Some (but not all) of the JSON configuration parameters (* - required): netid .. Subnetwork. Affects ports and keys. passphrase * .. Wallet seed. Compressed WIFs and hexadecimal ECDSA keys (prefixed with 0x) are also accepted. - panic .. Simulate a panic to see if backtrace works. rpccors .. Access-Control-Allow-Origin header value to be used in all the RPC responses. Default is currently 'http://localhost:3000' rpcip .. IP address to bind to for RPC server. Overrides the 127.0.0.1 default @@ -226,7 +224,6 @@ Some (but not all) of the JSON configuration parameters (* - required): Defaults to `false`. seednodes .. Seednode IPs that node will use. At least one seed IP must be present if the node is not a seed itself. - stderr .. Print a message to stderr and exit. wif .. `1` to add WIFs to the information we provide about a coin. Environment variables: From 26b87c3949d324f33b0f7f881721f2107d82c725 Mon Sep 17 00:00:00 2001 From: shamardy <39480341+shamardy@users.noreply.github.com> Date: Thu, 10 Oct 2024 18:58:21 +0300 Subject: [PATCH 28/29] chore(solana): remove solana implementation (#2239) The solana implementation was removed until it can be redone using the latest solana sdk --- Cargo.lock | 1851 ++--------------- mm2src/coins/Cargo.toml | 21 - mm2src/coins/lp_coins.rs | 152 -- mm2src/coins/solana.rs | 1098 ---------- mm2src/coins/solana/solana_common.rs | 181 -- mm2src/coins/solana/solana_common_tests.rs | 96 - .../coins/solana/solana_decode_tx_helpers.rs | 222 -- mm2src/coins/solana/solana_tests.rs | 436 ---- mm2src/coins/solana/spl.rs | 600 ------ mm2src/coins/solana/spl_tests.rs | 138 -- mm2src/coins_activation/Cargo.toml | 1 - mm2src/coins_activation/src/lib.rs | 14 - .../src/solana_with_tokens_activation.rs | 327 --- .../src/spl_token_activation.rs | 117 -- mm2src/mm2_main/Cargo.toml | 2 - mm2src/mm2_main/src/lp_ordermatch.rs | 6 +- mm2src/mm2_main/src/lp_swap.rs | 4 - .../mm2_main/src/rpc/dispatcher/dispatcher.rs | 18 - .../tests/docker_tests/docker_tests_common.rs | 23 - mm2src/mm2_main/tests/docker_tests/mod.rs | 1 - .../tests/docker_tests/solana_tests.rs | 144 -- mm2src/mm2_test_helpers/src/for_tests.rs | 47 - mm2src/mm2_test_helpers/src/structs.rs | 8 - 23 files changed, 151 insertions(+), 5356 deletions(-) delete mode 100644 mm2src/coins/solana.rs delete mode 100644 mm2src/coins/solana/solana_common.rs delete mode 100644 mm2src/coins/solana/solana_common_tests.rs delete mode 100644 mm2src/coins/solana/solana_decode_tx_helpers.rs delete mode 100644 mm2src/coins/solana/solana_tests.rs delete mode 100644 mm2src/coins/solana/spl.rs delete mode 100644 mm2src/coins/solana/spl_tests.rs delete mode 100644 mm2src/coins_activation/src/solana_with_tokens_activation.rs delete mode 100644 mm2src/coins_activation/src/spl_token_activation.rs delete mode 100644 mm2src/mm2_main/tests/docker_tests/solana_tests.rs diff --git a/Cargo.lock b/Cargo.lock index 08a985285b..cf62d3f115 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -119,12 +119,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "aliasable" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -134,15 +128,6 @@ dependencies = [ "libc", ] -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -dependencies = [ - "winapi", -] - [[package]] name = "anyhow" version = "1.0.42" @@ -186,12 +171,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" -[[package]] -name = "assert_matches" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" - [[package]] name = "async-io" version = "1.13.0" @@ -261,7 +240,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 1.0.95", ] @@ -278,7 +257,7 @@ version = "0.1.76" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "531b97fb4cd3dfdce92c35dedbfdc1f0b9d8091c8ca943d6dae340ef5012d514" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 2.0.38", ] @@ -391,12 +370,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" -[[package]] -name = "base32" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa" - [[package]] name = "base58" version = "0.2.0" @@ -409,12 +382,6 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" -[[package]] -name = "base64" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" - [[package]] name = "base64" version = "0.13.0" @@ -471,15 +438,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - [[package]] name = "bip32" version = "0.2.2" @@ -610,20 +568,6 @@ dependencies = [ "constant_time_eq", ] -[[package]] -name = "blake3" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08e53fc5a564bb15bfe6fae56bd71522205f1f91893f9c0116edad6496c183f" -dependencies = [ - "arrayref", - "arrayvec 0.7.1", - "cc", - "cfg-if 1.0.0", - "constant_time_eq", - "digest 0.10.7", -] - [[package]] name = "block-buffer" version = "0.9.0" @@ -695,51 +639,6 @@ dependencies = [ "serde_with", ] -[[package]] -name = "borsh" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" -dependencies = [ - "borsh-derive", - "hashbrown 0.11.2", -] - -[[package]] -name = "borsh-derive" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" -dependencies = [ - "borsh-derive-internal", - "borsh-schema-derive-internal", - "proc-macro-crate 0.1.5", - "proc-macro2 1.0.69", - "syn 1.0.95", -] - -[[package]] -name = "borsh-derive-internal" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.95", -] - -[[package]] -name = "borsh-schema-derive-internal" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.95", -] - [[package]] name = "bs58" version = "0.4.0" @@ -764,42 +663,12 @@ version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" -[[package]] -name = "bv" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8834bb1d8ee5dc048ee3124f2c7c1afcc6bc9aed03f11e9dfd8c69470a5db340" -dependencies = [ - "feature-probe", - "serde", -] - [[package]] name = "byte-slice-cast" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65c1bf4a04a88c54f589125563643d773f3254b5c38571395e2b591c693bbc81" -[[package]] -name = "bytemuck" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e851ca7c24871e7336801608a4797d7376545b6928a10d32d75685687141ead" -dependencies = [ - "bytemuck_derive", -] - -[[package]] -name = "bytemuck_derive" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e215f8c2f9f79cb53c8335e687ffd07d5bfcb6fe5fc80723762d0be46e7cc54" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.95", -] - [[package]] name = "byteorder" version = "1.4.3" @@ -825,44 +694,12 @@ dependencies = [ "serde", ] -[[package]] -name = "bzip2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" -dependencies = [ - "bzip2-sys", - "libc", -] - -[[package]] -name = "bzip2-sys" -version = "0.1.11+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - [[package]] name = "cache-padded" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24508e28c677875c380c20f4d28124fab6f8ed4ef929a1397d7b1a31e92f1005" -[[package]] -name = "caps" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61bf7211aad104ce2769ec05efcdfabf85ee84ac92461d142f22cf8badd0e54c" -dependencies = [ - "errno 0.2.8", - "libc", - "thiserror", -] - [[package]] name = "cbc" version = "0.1.2" @@ -877,9 +714,6 @@ name = "cc" version = "1.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574" -dependencies = [ - "jobserver", -] [[package]] name = "cfg-if" @@ -965,21 +799,6 @@ dependencies = [ "inout", ] -[[package]] -name = "clap" -version = "2.33.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" -dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim 0.8.0", - "textwrap", - "unicode-width", - "vec_map", -] - [[package]] name = "cloudabi" version = "0.0.3" @@ -989,15 +808,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "cloudabi" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467" -dependencies = [ - "bitflags", -] - [[package]] name = "codespan-reporting" version = "0.11.1" @@ -1016,7 +826,6 @@ dependencies = [ "async-trait", "base58", "base64 0.21.7", - "bincode", "bip32", "bitcoin", "bitcoin_hashes", @@ -1035,7 +844,6 @@ dependencies = [ "derive_more", "dirs", "ed25519-dalek", - "ed25519-dalek-bip32 0.2.0", "enum_derives", "ethabi", "ethcore-transaction", @@ -1080,7 +888,7 @@ dependencies = [ "mocktopus", "nom", "num-traits", - "parking_lot 0.12.0", + "parking_lot", "primitives", "prost", "prost-build", @@ -1095,7 +903,6 @@ dependencies = [ "rpc_task", "rust-ini", "rustls 0.21.10", - "satomic-swap", "script", "secp256k1 0.20.3", "secp256k1 0.24.3", @@ -1110,11 +917,6 @@ dependencies = [ "sha2 0.10.7", "sha3 0.9.1", "sia-rust", - "solana-client", - "solana-sdk", - "solana-transaction-status", - "spl-associated-token-account", - "spl-token", "spv_validation", "tendermint-rpc", "time 0.3.20", @@ -1164,7 +966,7 @@ dependencies = [ "mm2_metamask", "mm2_metrics", "mm2_number", - "parking_lot 0.12.0", + "parking_lot", "rpc", "rpc_task", "ser_error", @@ -1208,7 +1010,7 @@ dependencies = [ "libc", "lightning", "log", - "parking_lot 0.12.0", + "parking_lot", "parking_lot_core 0.6.2", "primitive-types", "rand 0.7.3", @@ -1250,21 +1052,6 @@ dependencies = [ "crossbeam-utils 0.8.16", ] -[[package]] -name = "console" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28b32d32ca44b70c3e4acd7db1babf555fa026e385fb95f18028f88848b3c31" -dependencies = [ - "encode_unicode", - "libc", - "once_cell", - "regex", - "terminal_size", - "unicode-width", - "winapi", -] - [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -1275,16 +1062,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "console_log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501a375961cef1a0d44767200e66e4a559283097e91d0730b1d75dfb2f8a1494" -dependencies = [ - "log", - "web-sys", -] - [[package]] name = "const-oid" version = "0.9.6" @@ -1556,7 +1333,7 @@ dependencies = [ "mm2_eth", "mm2_metamask", "num-traits", - "parking_lot 0.12.0", + "parking_lot", "primitives", "rpc", "rpc_task", @@ -1607,16 +1384,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "crypto-mac" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bcd97a54c7ca5ce2f6eb16f6bede5b0ab5f0055fedc17d2f0b4466e21671ca" -dependencies = [ - "generic-array", - "subtle", -] - [[package]] name = "crypto-mac" version = "0.11.1" @@ -1723,7 +1490,7 @@ dependencies = [ "cc", "codespan-reporting", "lazy_static", - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "scratch", "syn 1.0.95", @@ -1741,7 +1508,7 @@ version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b846f081361125bfc8dc9d3940c84e1fd83ba54bbca7b17cd29483c828be0704" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 1.0.95", ] @@ -1764,9 +1531,9 @@ checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", - "strsim 0.10.0", + "strsim", "syn 1.0.95", ] @@ -1781,17 +1548,6 @@ dependencies = [ "syn 1.0.95", ] -[[package]] -name = "dashmap" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" -dependencies = [ - "cfg-if 1.0.0", - "num_cpus", - "rayon", -] - [[package]] name = "data-encoding" version = "2.4.0" @@ -1843,55 +1599,17 @@ dependencies = [ "zeroize", ] -[[package]] -name = "derivation-path" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193388a8c8c75a490b604ff61775e236541b8975e98e5ca1f6ea97d122b7e2db" -dependencies = [ - "failure", -] - -[[package]] -name = "derivation-path" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e5c37193a1db1d8ed868c03ec7b152175f26160a5b740e5e484143877e0adf0" - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.95", -] - [[package]] name = "derive_more" version = "0.99.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 1.0.95", ] -[[package]] -name = "dialoguer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61579ada4ec0c6031cfac3f86fdba0d195a7ebeb5e36693bd53cb5999a25beeb" -dependencies = [ - "console", - "lazy_static", - "tempfile", - "zeroize", -] - [[package]] name = "digest" version = "0.9.0" @@ -1913,15 +1631,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "dir-diff" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2860407d7d7e2e004bb2128510ad9e8d669e76fa005ccf567977b5d71b8b4a0b" -dependencies = [ - "walkdir", -] - [[package]] name = "directories" version = "3.0.2" @@ -1942,16 +1651,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if 1.0.0", - "dirs-sys-next", -] - [[package]] name = "dirs-sys" version = "0.3.6" @@ -1963,40 +1662,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users 0.4.0", - "winapi", -] - -[[package]] -name = "dlopen" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e80ad39f814a9abe68583cd50a2d45c8a67561c3361ab8da240587dda80937" -dependencies = [ - "dlopen_derive", - "lazy_static", - "libc", - "winapi", -] - -[[package]] -name = "dlopen_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f236d9e1b1fbd81cea0f9cbdc8dcc7e8ebcd80e6659cd7cb2ad5f6c05946c581" -dependencies = [ - "libc", - "quote 0.6.13", - "syn 0.15.44", -] - [[package]] name = "dtoa" version = "1.0.2" @@ -2065,31 +1730,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "ed25519-dalek-bip32" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057f328f31294b5ab432e6c39642f54afd1531677d6d4ba2905932844cc242f3" -dependencies = [ - "derivation-path 0.1.3", - "ed25519-dalek", - "failure", - "hmac 0.9.0", - "sha2 0.9.9", -] - -[[package]] -name = "ed25519-dalek-bip32" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d2be62a4061b872c8c0873ee4fc6f101ce7b889d039f019c5fa2af471a59908" -dependencies = [ - "derivation-path 0.2.0", - "ed25519-dalek", - "hmac 0.12.1", - "sha2 0.10.7", -] - [[package]] name = "edit-distance" version = "2.1.0" @@ -2121,12 +1761,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "encode_unicode" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" - [[package]] name = "encoding_rs" version = "0.8.28" @@ -2149,7 +1783,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" dependencies = [ "heck", - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 1.0.95", ] @@ -2170,7 +1804,7 @@ name = "enum_derives" version = "0.1.0" dependencies = [ "itertools", - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 1.0.95", ] @@ -2326,28 +1960,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "failure" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" -dependencies = [ - "backtrace", - "failure_derive", -] - -[[package]] -name = "failure_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.95", - "synstructure", -] - [[package]] name = "fallible-iterator" version = "0.2.0" @@ -2369,12 +1981,6 @@ dependencies = [ "instant", ] -[[package]] -name = "feature-probe" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" - [[package]] name = "ff" version = "0.8.0" @@ -2402,18 +2008,6 @@ version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" -[[package]] -name = "filetime" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "redox_syscall 0.2.10", - "winapi", -] - [[package]] name = "findshlibs" version = "0.5.0" @@ -2604,7 +2198,7 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 2.0.38", ] @@ -2688,22 +2282,11 @@ version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ - "serde", "typenum", "version_check", "zeroize", ] -[[package]] -name = "gethostname" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e692e296bfac1d2533ef168d0b60ff5897b8b70a4009276834014dd8924cc028" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "getrandom" version = "0.1.14" @@ -2929,17 +2512,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b07f60793ff0a4d9cef0f18e63b5357e06209987153a64648c972c1e5aff336f" -[[package]] -name = "hidapi" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b1717343691998deb81766bfcd1dce6df0d5d6c37070b5a3de2bb6d39f7822" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - [[package]] name = "hkd32" version = "0.6.0" @@ -2962,16 +2534,6 @@ dependencies = [ "digest 0.9.0", ] -[[package]] -name = "hmac" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deae6d9dbb35ec2c502d62b8f7b1c000a0822c3b0794ba36b3149c0a1c840dff" -dependencies = [ - "crypto-mac 0.9.1", - "digest 0.9.0", -] - [[package]] name = "hmac" version = "0.11.0" @@ -3264,7 +2826,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5dacb10c5b3bb92d46ba347505a9041e676bb20ad220101326bffb0c93031ee" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 1.0.95", ] @@ -3275,12 +2837,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" -[[package]] -name = "index_list" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9d968042a4902e08810946fc7cd5851eb75e80301342305af755ca06cb82ce" - [[package]] name = "indexmap" version = "1.9.3" @@ -3301,18 +2857,6 @@ dependencies = [ "hashbrown 0.14.3", ] -[[package]] -name = "indicatif" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d207dc617c7a380ab07ff572a6e52fa202a2a8f355860ac9c38e23f8196be1b" -dependencies = [ - "console", - "lazy_static", - "number_prefix", - "regex", -] - [[package]] name = "inout" version = "0.1.3" @@ -3414,15 +2958,6 @@ dependencies = [ "libc", ] -[[package]] -name = "jobserver" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" -dependencies = [ - "libc", -] - [[package]] name = "js-sys" version = "0.3.64" @@ -3542,16 +3077,6 @@ version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" -[[package]] -name = "libloading" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" -dependencies = [ - "cfg-if 1.0.0", - "winapi", -] - [[package]] name = "libm" version = "0.1.4" @@ -3634,7 +3159,7 @@ dependencies = [ "multihash", "multistream-select", "once_cell", - "parking_lot 0.12.0", + "parking_lot", "pin-project", "quick-protobuf", "rand 0.8.4", @@ -3654,7 +3179,7 @@ dependencies = [ "libp2p-core", "libp2p-identity", "log", - "parking_lot 0.12.0", + "parking_lot", "smallvec 1.6.1", "trust-dns-resolver", ] @@ -3740,7 +3265,7 @@ dependencies = [ "asn1_der", "bs58 0.5.0", "ed25519-dalek", - "libsecp256k1 0.7.0", + "libsecp256k1", "log", "multihash", "quick-protobuf", @@ -3873,7 +3398,7 @@ source = "git+https://github.com/KomodoPlatform/rust-libp2p.git?tag=k-0.52.4#6fc dependencies = [ "heck", "proc-macro-warning", - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 2.0.38", ] @@ -3918,7 +3443,7 @@ dependencies = [ "libp2p-core", "libp2p-identity", "log", - "parking_lot 0.12.0", + "parking_lot", "quicksink", "rw-stream-sink", "soketto", @@ -3940,25 +3465,6 @@ dependencies = [ "yamux 0.13.1", ] -[[package]] -name = "libsecp256k1" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9d220bc1feda2ac231cb78c3d26f27676b8cf82c96971f7aeef3d0cf2797c73" -dependencies = [ - "arrayref", - "base64 0.12.3", - "digest 0.9.0", - "hmac-drbg", - "libsecp256k1-core 0.2.2", - "libsecp256k1-gen-ecmult 0.2.1", - "libsecp256k1-gen-genmult 0.2.1", - "rand 0.7.3", - "serde", - "sha2 0.9.9", - "typenum", -] - [[package]] name = "libsecp256k1" version = "0.7.0" @@ -3969,26 +3475,15 @@ dependencies = [ "base64 0.13.0", "digest 0.9.0", "hmac-drbg", - "libsecp256k1-core 0.3.0", - "libsecp256k1-gen-ecmult 0.3.0", - "libsecp256k1-gen-genmult 0.3.0", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", "rand 0.8.4", "serde", "sha2 0.9.9", "typenum", ] -[[package]] -name = "libsecp256k1-core" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f6ab710cec28cef759c5f18671a27dae2a5f952cdaaee1d8e2908cb2478a80" -dependencies = [ - "crunchy", - "digest 0.9.0", - "subtle", -] - [[package]] name = "libsecp256k1-core" version = "0.3.0" @@ -4000,31 +3495,13 @@ dependencies = [ "subtle", ] -[[package]] -name = "libsecp256k1-gen-ecmult" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccab96b584d38fac86a83f07e659f0deafd0253dc096dab5a36d53efe653c5c3" -dependencies = [ - "libsecp256k1-core 0.2.2", -] - [[package]] name = "libsecp256k1-gen-ecmult" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" dependencies = [ - "libsecp256k1-core 0.3.0", -] - -[[package]] -name = "libsecp256k1-gen-genmult" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67abfe149395e3aa1c48a2beb32b068e2334402df8181f818d3aee2b304c4f5d" -dependencies = [ - "libsecp256k1-core 0.2.2", + "libsecp256k1-core", ] [[package]] @@ -4033,7 +3510,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" dependencies = [ - "libsecp256k1-core 0.3.0", + "libsecp256k1-core", ] [[package]] @@ -4230,15 +3707,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" -[[package]] -name = "memmap2" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057a3db23999c867821a7a59feb06a578fcb03685e983dff90daf9e7d24ac08f" -dependencies = [ - "libc", -] - [[package]] name = "memoffset" version = "0.5.6" @@ -4303,7 +3771,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddece26afd34c31585c74a4db0630c376df271c285d682d1e55012197830b6df" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 2.0.38", ] @@ -4485,7 +3953,7 @@ dependencies = [ "cfg-if 1.0.0", "common", "futures 0.3.28", - "parking_lot 0.12.0", + "parking_lot", "serde", "tokio", "wasm-bindgen-test", @@ -4600,7 +4068,7 @@ dependencies = [ "mocktopus", "num-traits", "parity-util-mem", - "parking_lot 0.12.0", + "parking_lot", "primitives", "prost", "prost-build", @@ -4656,7 +4124,7 @@ dependencies = [ "lazy_static", "mm2_err_handle", "mm2_eth", - "parking_lot 0.12.0", + "parking_lot", "serde", "serde_derive", "serde_json", @@ -4711,7 +4179,7 @@ dependencies = [ "mm2_event_stream", "mm2_p2p", "mm2_state_machine", - "parking_lot 0.12.0", + "parking_lot", "pin-project", "prost", "rand 0.7.3", @@ -4845,7 +4313,7 @@ version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3048ef3680533a27f9f8e7d6a0bce44dc61e4895ea0f42709337fa1c8616fefe" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 1.0.95", ] @@ -4984,19 +4452,6 @@ dependencies = [ "smallvec 1.6.1", ] -[[package]] -name = "nix" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" -dependencies = [ - "bitflags", - "cc", - "cfg-if 1.0.0", - "libc", - "memoffset 0.6.4", -] - [[package]] name = "nix" version = "0.24.3" @@ -5045,7 +4500,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 1.0.95", ] @@ -5092,34 +4547,6 @@ dependencies = [ "libc", ] -[[package]] -name = "num_enum" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9bd055fb730c4f8f4f57d45d35cd6b3f0980535b056dc7ff119cee6a66ed6f" -dependencies = [ - "derivative", - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "486ea01961c4a818096de679a8b740b26d9033146ac5291b1c98557658f8cdd9" -dependencies = [ - "proc-macro-crate 1.1.3", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.95", -] - -[[package]] -name = "number_prefix" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" - [[package]] name = "object" version = "0.29.0" @@ -5156,30 +4583,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "ouroboros" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f357ef82d1b4db66fbed0b8d542cbd3c22d0bf5b393b3c257b9ba4568e70c9c3" -dependencies = [ - "aliasable", - "ouroboros_macro", - "stable_deref_trait", -] - -[[package]] -name = "ouroboros_macro" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44a0b52c2cbaef7dffa5fec1a43274afe8bd2a644fa9fc50a9ef4ff0269b1257" -dependencies = [ - "Inflector", - "proc-macro-error", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.95", -] - [[package]] name = "packed_simd_2" version = "0.3.8" @@ -5220,8 +4623,8 @@ version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c45ed1f39709f5a89338fab50e59816b2e8815f5bb58276e7ddf9afd495f73f8" dependencies = [ - "proc-macro-crate 1.1.3", - "proc-macro2 1.0.69", + "proc-macro-crate", + "proc-macro2", "quote 1.0.33", "syn 1.0.95", ] @@ -5238,7 +4641,7 @@ dependencies = [ "impl-trait-for-tuples", "lru 0.7.5", "parity-util-mem-derive", - "parking_lot 0.12.0", + "parking_lot", "primitive-types", "smallvec 1.6.1", "winapi", @@ -5250,7 +4653,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "syn 1.0.95", "synstructure", ] @@ -5267,17 +4670,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" -[[package]] -name = "parking_lot" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.0", -] - [[package]] name = "parking_lot" version = "0.12.0" @@ -5295,7 +4687,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" dependencies = [ "cfg-if 0.1.10", - "cloudabi 0.0.3", + "cloudabi", "libc", "redox_syscall 0.1.56", "rustc_version 0.2.3", @@ -5303,21 +4695,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "parking_lot_core" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b" -dependencies = [ - "cfg-if 0.1.10", - "cloudabi 0.1.0", - "instant", - "libc", - "redox_syscall 0.1.56", - "smallvec 1.6.1", - "winapi", -] - [[package]] name = "parking_lot_core" version = "0.9.1" @@ -5348,24 +4725,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" -[[package]] -name = "pbkdf2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" -dependencies = [ - "crypto-mac 0.8.0", -] - -[[package]] -name = "pbkdf2" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739" -dependencies = [ - "crypto-mac 0.11.1", -] - [[package]] name = "peg" version = "0.7.0" @@ -5383,7 +4742,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aa52829b8decbef693af90202711348ab001456803ba2a98eb4ec8fb70844c" dependencies = [ "peg-runtime", - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", ] @@ -5433,7 +4792,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 2.0.38", ] @@ -5535,7 +4894,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f28f53e8b192565862cf99343194579a022eb9c7dd3a8d03134734803c7b3125" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "syn 1.0.95", ] @@ -5563,15 +4922,6 @@ dependencies = [ "uint", ] -[[package]] -name = "proc-macro-crate" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" -dependencies = [ - "toml", -] - [[package]] name = "proc-macro-crate" version = "1.1.3" @@ -5582,50 +4932,17 @@ dependencies = [ "toml", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2 1.0.69", - "quote 1.0.33", - "syn 1.0.95", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "version_check", -] - [[package]] name = "proc-macro-warning" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70550716265d1ec349c41f70dd4f964b4fd88394efe4405f0c1da679c4799a07" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 2.0.38", ] -[[package]] -name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -dependencies = [ - "unicode-xid 0.1.0", -] - [[package]] name = "proc-macro2" version = "1.0.69" @@ -5643,7 +4960,7 @@ checksum = "78c2f43e8969d51935d2a7284878ae053ba30034cd563f673cde37ba5205685e" dependencies = [ "dtoa", "itoa 1.0.10", - "parking_lot 0.12.0", + "parking_lot", "prometheus-client-derive-encode", ] @@ -5653,7 +4970,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b6a5217beb0ad503ee7fa752d451c905113d70721b937126158f3106a48cc1" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 1.0.95", ] @@ -5698,7 +5015,7 @@ checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools", - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 1.0.95", ] @@ -5749,15 +5066,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "qstring" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" -dependencies = [ - "percent-encoding", -] - [[package]] name = "quanta" version = "0.11.1" @@ -5818,22 +5126,13 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" -[[package]] -name = "quote" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -dependencies = [ - "proc-macro2 0.4.30", -] - [[package]] name = "quote" version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", ] [[package]] @@ -6025,7 +5324,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" dependencies = [ - "cloudabi 0.0.3", + "cloudabi", "fuchsia-cprng", "libc", "rand_core 0.4.2", @@ -6072,35 +5371,10 @@ dependencies = [ ] [[package]] -name = "rayon" -version = "1.5.1" +name = "rcgen" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" -dependencies = [ - "autocfg 1.1.0", - "crossbeam-deque 0.8.1", - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" -dependencies = [ - "crossbeam-channel 0.5.1", - "crossbeam-deque 0.8.1", - "crossbeam-utils 0.8.16", - "lazy_static", - "num_cpus", -] - -[[package]] -name = "rcgen" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" +checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" dependencies = [ "pem", "ring 0.16.20", @@ -6168,7 +5442,7 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c523ccaed8ac4b0288948849a350b37d3035827413c458b6a40ddb614bb4f72" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 1.0.95", ] @@ -6329,16 +5603,6 @@ dependencies = [ "serde", ] -[[package]] -name = "rpassword" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc936cf8a7ea60c58f030fd36a612a48f440610214dc54bc36431f9ea0c3efb" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "rpc" version = "0.1.0" @@ -6381,7 +5645,7 @@ dependencies = [ "log", "netlink-packet-route", "netlink-proto", - "nix 0.24.3", + "nix", "thiserror", "tokio", ] @@ -6598,14 +5862,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "satomic-swap" -version = "0.1.0" -source = "git+https://github.com/KomodoPlatform/satomic-swap.git?rev=413e472#413e4725a97f2c4d5d34101b3d2c49009c95cb28" -dependencies = [ - "solana-program", -] - [[package]] name = "scale-info" version = "2.1.2" @@ -6624,8 +5880,8 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50e334bb10a245e28e5fd755cabcafd96cfcd167c99ae63a46924ca8d8703a3c" dependencies = [ - "proc-macro-crate 1.1.3", - "proc-macro2 1.0.69", + "proc-macro-crate", + "proc-macro2", "quote 1.0.33", "syn 1.0.95", ] @@ -6809,7 +6065,7 @@ dependencies = [ name = "ser_error_derive" version = "0.1.0" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "ser_error", "syn 1.0.95", @@ -6850,7 +6106,7 @@ version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 2.0.38", ] @@ -6873,7 +6129,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dc6b7951b17b051f3210b063f12cc17320e2fe30ae05b0fe2a3abb068551c76" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 1.0.95", ] @@ -6907,23 +6163,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ "darling", - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 1.0.95", ] -[[package]] -name = "serde_yaml" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a521f2940385c165a24ee286aa8599633d162077a54bdcae2a6fd5a7bfa7a0" -dependencies = [ - "indexmap 1.9.3", - "ryu", - "serde", - "yaml-rust", -] - [[package]] name = "serialization" version = "0.1.0" @@ -7077,725 +6321,112 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68a406c1882ed7f29cd5e248c9848a80e7cb6ae0fea82346d2746f2f941c07e1" -[[package]] -name = "slab" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" -dependencies = [ - "autocfg 1.1.0", -] - -[[package]] -name = "smallvec" -version = "0.6.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" -dependencies = [ - "maybe-uninit", -] - -[[package]] -name = "smallvec" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" - -[[package]] -name = "smol" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "620cbb3c6e34da57d3a248cda0cd01cd5848164dc062e764e65d06fe3ea7aed5" -dependencies = [ - "async-task", - "blocking", - "concurrent-queue 1.1.1", - "fastrand", - "futures-io", - "futures-util", - "libc", - "once_cell", - "scoped-tls", - "slab", - "socket2 0.3.19", - "wepoll-sys-stjepang", - "winapi", -] - -[[package]] -name = "snow" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ccba027ba85743e09d15c03296797cad56395089b832b48b5a5217880f57733" -dependencies = [ - "aes-gcm", - "blake2", - "chacha20poly1305", - "curve25519-dalek 4.0.0-rc.1", - "rand_core 0.6.4", - "ring 0.16.20", - "rustc_version 0.4.0", - "sha2 0.10.7", - "subtle", -] - -[[package]] -name = "socket2" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "winapi", -] - -[[package]] -name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "socket2" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" -dependencies = [ - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "soketto" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "083624472e8817d44d02c0e55df043737ff11f279af924abdf93845717c2b75c" -dependencies = [ - "base64 0.13.0", - "bytes 1.4.0", - "futures 0.3.28", - "httparse", - "log", - "rand 0.8.4", - "sha-1", -] - -[[package]] -name = "solana-account-decoder" -version = "1.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ea8c1862fc46c6ab40d83d15ced24a7afb1f3422da5824f1e9260f5ac10141f" -dependencies = [ - "Inflector", - "base64 0.12.3", - "bincode", - "bs58 0.4.0", - "bv", - "lazy_static", - "serde", - "serde_derive", - "serde_json", - "solana-config-program", - "solana-sdk", - "solana-vote-program", - "spl-token", - "thiserror", - "zstd", -] - -[[package]] -name = "solana-address-lookup-table-program" -version = "1.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c60728aec35d772e6614319558cdccbe3f845102699b65ba5ac7497da0b626a" -dependencies = [ - "bincode", - "bytemuck", - "log", - "num-derive", - "num-traits", - "rustc_version 0.4.0", - "serde", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-program-runtime", - "solana-sdk", - "thiserror", -] - -[[package]] -name = "solana-bloom" -version = "1.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddcd7c6adb802bc812a5a80c8de06ba0f0e8df0cca296a8b4e67cd04c16218f" -dependencies = [ - "bv", - "fnv", - "log", - "rand 0.7.3", - "rayon", - "rustc_version 0.4.0", - "serde", - "serde_derive", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-sdk", -] - -[[package]] -name = "solana-bucket-map" -version = "1.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3435b145894971a58a08a7b6be997ec782239fdecd5edd9846cd1d6aa5986" -dependencies = [ - "fs_extra", - "log", - "memmap2", - "rand 0.7.3", - "rayon", - "solana-logger", - "solana-measure", - "solana-sdk", - "tempfile", -] - -[[package]] -name = "solana-clap-utils" -version = "1.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8417a89c377728dbfbf1966b6493544f6e5e168ebc5bb444f3526481fae94e31" -dependencies = [ - "chrono", - "clap", - "rpassword", - "solana-perf", - "solana-remote-wallet", - "solana-sdk", - "thiserror", - "tiny-bip39", - "uriparse", - "url", -] - -[[package]] -name = "solana-cli-config" -version = "1.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e8b011d36369ef2bc3dff63fee078bf2916a4fd21f3aa702ee731c7ddf83d28" -dependencies = [ - "dirs-next", - "lazy_static", - "serde", - "serde_derive", - "serde_yaml", - "url", -] - -[[package]] -name = "solana-client" -version = "1.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e20f4df8cee4a1819f1c5b0d3d85d50c30f27133b2ae68c2fd92655e4aede34a" -dependencies = [ - "base64 0.13.0", - "bincode", - "bs58 0.4.0", - "clap", - "indicatif", - "jsonrpc-core", - "log", - "rayon", - "reqwest", - "semver 1.0.6", - "serde", - "serde_derive", - "serde_json", - "solana-account-decoder", - "solana-clap-utils", - "solana-faucet", - "solana-measure", - "solana-net-utils", - "solana-sdk", - "solana-transaction-status", - "solana-version", - "solana-vote-program", - "thiserror", - "tokio", - "tungstenite", - "url", -] - -[[package]] -name = "solana-compute-budget-program" -version = "1.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685567c221f6bb5b64387f7b45d03036ad112b2ecbcd0f94b11204efab9f891e" -dependencies = [ - "solana-program-runtime", - "solana-sdk", -] - -[[package]] -name = "solana-config-program" -version = "1.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f4b04403ff77f09eba5cf94078c1178161e26d346245b06180866ab5286fe6b" -dependencies = [ - "bincode", - "chrono", - "serde", - "serde_derive", - "solana-program-runtime", - "solana-sdk", -] - -[[package]] -name = "solana-faucet" -version = "1.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a11e1b6d5ce435bb3df95f2a970cd80500a8abf94ea87558c35fe0cce8456ab" -dependencies = [ - "bincode", - "byteorder", - "clap", - "log", - "serde", - "serde_derive", - "solana-clap-utils", - "solana-cli-config", - "solana-logger", - "solana-metrics", - "solana-sdk", - "solana-version", - "spl-memo", - "thiserror", - "tokio", -] - -[[package]] -name = "solana-frozen-abi" -version = "1.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5f69a79200f5ba439eb8b790c5e00beab4d1fae4da69ce023c69c6ac1b55bf1" -dependencies = [ - "bs58 0.4.0", - "bv", - "generic-array", - "log", - "memmap2", - "rustc_version 0.4.0", - "serde", - "serde_derive", - "sha2 0.9.9", - "solana-frozen-abi-macro", - "solana-logger", - "thiserror", -] - -[[package]] -name = "solana-frozen-abi-macro" -version = "1.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402fffb54bf5d335e6df26fc1719feecfbd7a22fafdf6649fe78380de3c47384" -dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", - "rustc_version 0.4.0", - "syn 1.0.95", -] - -[[package]] -name = "solana-logger" -version = "1.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942dc59fc9da66d178362051b658646b65dc11cea0bc804e4ecd2528d3c1279f" -dependencies = [ - "env_logger", - "lazy_static", - "log", -] - -[[package]] -name = "solana-measure" -version = "1.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ccd5b1278b115249d6ca5a203fd852f7d856e048488c24442222ee86e682bd9" -dependencies = [ - "log", - "solana-sdk", -] - -[[package]] -name = "solana-metrics" -version = "1.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9774cd8309f599797b1612731fbc56df6c612879ab4923a3dc7234400eea419" -dependencies = [ - "env_logger", - "gethostname", - "lazy_static", - "log", - "reqwest", - "solana-sdk", -] - -[[package]] -name = "solana-net-utils" -version = "1.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cb530af085d8aab563530ed39703096aa526249d350082823882fdd59cdf839" -dependencies = [ - "bincode", - "clap", - "log", - "nix 0.23.1", - "rand 0.7.3", - "serde", - "serde_derive", - "socket2 0.4.9", - "solana-logger", - "solana-sdk", - "solana-version", - "tokio", - "url", -] - -[[package]] -name = "solana-perf" -version = "1.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4117c0cf7753bc18f3a09f4973175c3f2c7c5d8e3c9bc15cab09060b06f3434f" -dependencies = [ - "ahash 0.7.6", - "bincode", - "bv", - "caps", - "curve25519-dalek 3.2.0", - "dlopen", - "dlopen_derive", - "fnv", - "lazy_static", - "libc", - "log", - "nix 0.23.1", - "rand 0.7.3", - "rayon", - "serde", - "solana-bloom", - "solana-logger", - "solana-metrics", - "solana-rayon-threadlimit", - "solana-sdk", - "solana-vote-program", -] - -[[package]] -name = "solana-program" -version = "1.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a463f546a2f5842d35974bd4691ae5ceded6785ec24db440f773723f6ce4e11" -dependencies = [ - "base64 0.13.0", - "bincode", - "bitflags", - "blake3", - "borsh", - "borsh-derive", - "bs58 0.4.0", - "bv", - "bytemuck", - "console_error_panic_hook", - "console_log", - "curve25519-dalek 3.2.0", - "getrandom 0.1.14", - "itertools", - "js-sys", - "lazy_static", - "libsecp256k1 0.6.0", - "log", - "num-derive", - "num-traits", - "parking_lot 0.11.1", - "rand 0.7.3", - "rustc_version 0.4.0", - "rustversion", - "serde", - "serde_bytes", - "serde_derive", - "sha2 0.9.9", - "sha3 0.9.1", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-logger", - "solana-sdk-macro", - "thiserror", - "wasm-bindgen", -] - -[[package]] -name = "solana-program-runtime" -version = "1.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09841673334eab958d5bedab9c9d75ed2ff7a7ef70e7dfd6b239c6838a3d79ec" -dependencies = [ - "base64 0.13.0", - "bincode", - "itertools", - "libc", - "libloading", - "log", - "num-derive", - "num-traits", - "rustc_version 0.4.0", - "serde", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-logger", - "solana-measure", - "solana-sdk", - "thiserror", -] - -[[package]] -name = "solana-rayon-threadlimit" -version = "1.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92893e3129dfabb703cd045e1367f3ced91202a2d0b6179a3dcd62ad6bead3b" -dependencies = [ - "lazy_static", - "num_cpus", -] - -[[package]] -name = "solana-remote-wallet" -version = "1.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "315534baecaae3f804548ccc4738d73ae01bf6523219787ebe55ee66d8db9a85" -dependencies = [ - "base32", - "console", - "dialoguer", - "hidapi", - "log", - "num-derive", - "num-traits", - "parking_lot 0.11.1", - "qstring", - "semver 1.0.6", - "solana-sdk", - "thiserror", - "uriparse", -] - -[[package]] -name = "solana-runtime" -version = "1.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bd06e905260433f7e8d18bccb2e2eb2aa5cc53379d104d331ddeb12e13230a0" -dependencies = [ - "arrayref", - "bincode", - "blake3", - "bv", - "bytemuck", - "byteorder", - "bzip2", - "crossbeam-channel 0.5.1", - "dashmap", - "dir-diff", - "flate2", - "fnv", - "index_list", - "itertools", - "lazy_static", - "log", - "memmap2", - "num-derive", - "num-traits", - "num_cpus", - "ouroboros", - "rand 0.7.3", - "rayon", - "regex", - "rustc_version 0.4.0", - "serde", - "serde_derive", - "solana-address-lookup-table-program", - "solana-bloom", - "solana-bucket-map", - "solana-compute-budget-program", - "solana-config-program", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-logger", - "solana-measure", - "solana-metrics", - "solana-program-runtime", - "solana-rayon-threadlimit", - "solana-sdk", - "solana-stake-program", - "solana-vote-program", - "symlink", - "tar", - "tempfile", - "thiserror", - "zstd", +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg 1.1.0", ] [[package]] -name = "solana-sdk" -version = "1.9.20" +name = "smallvec" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6560e605c68fa1e3e66a9d3c8529d097d402e1183f80dd06a2c870d0ecb795c2" +checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" dependencies = [ - "assert_matches", - "base64 0.13.0", - "bincode", - "bitflags", - "borsh", - "bs58 0.4.0", - "bytemuck", - "byteorder", - "chrono", - "derivation-path 0.1.3", - "digest 0.9.0", - "ed25519-dalek", - "ed25519-dalek-bip32 0.1.1", - "generic-array", - "hmac 0.11.0", - "itertools", - "js-sys", - "lazy_static", - "libsecp256k1 0.6.0", - "log", - "memmap2", - "num-derive", - "num-traits", - "pbkdf2 0.9.0", - "qstring", - "rand 0.7.3", - "rand_chacha 0.2.2", - "rustc_version 0.4.0", - "rustversion", - "serde", - "serde_bytes", - "serde_derive", - "serde_json", - "sha2 0.9.9", - "sha3 0.9.1", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-logger", - "solana-program", - "solana-sdk-macro", - "thiserror", - "uriparse", - "wasm-bindgen", + "maybe-uninit", ] [[package]] -name = "solana-sdk-macro" -version = "1.9.20" +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + +[[package]] +name = "smol" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c834b4e02ac911b13c13aed08b3f847e722f6be79d31b1c660c1dbd2dee83cdb" +checksum = "620cbb3c6e34da57d3a248cda0cd01cd5848164dc062e764e65d06fe3ea7aed5" dependencies = [ - "bs58 0.4.0", - "proc-macro2 1.0.69", - "quote 1.0.33", - "rustversion", - "syn 1.0.95", + "async-task", + "blocking", + "concurrent-queue 1.1.1", + "fastrand", + "futures-io", + "futures-util", + "libc", + "once_cell", + "scoped-tls", + "slab", + "socket2 0.3.19", + "wepoll-sys-stjepang", + "winapi", ] [[package]] -name = "solana-stake-program" -version = "1.9.20" +name = "snow" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92597c0ed16d167d5ee48e5b13e92dfaed9c55b23a13ec261440136cd418649" +checksum = "5ccba027ba85743e09d15c03296797cad56395089b832b48b5a5217880f57733" dependencies = [ - "bincode", - "log", - "num-derive", - "num-traits", + "aes-gcm", + "blake2", + "chacha20poly1305", + "curve25519-dalek 4.0.0-rc.1", + "rand_core 0.6.4", + "ring 0.16.20", "rustc_version 0.4.0", - "serde", - "serde_derive", - "solana-config-program", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-metrics", - "solana-program-runtime", - "solana-sdk", - "solana-vote-program", - "thiserror", + "sha2 0.10.7", + "subtle", ] [[package]] -name = "solana-transaction-status" -version = "1.9.20" +name = "socket2" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612a51efa19380992e81fc64a2fb55d42aed32c67d795848d980cbe1f9693250" +checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" dependencies = [ - "Inflector", - "base64 0.12.3", - "bincode", - "bs58 0.4.0", - "lazy_static", - "log", - "serde", - "serde_derive", - "serde_json", - "solana-account-decoder", - "solana-measure", - "solana-metrics", - "solana-runtime", - "solana-sdk", - "solana-vote-program", - "spl-associated-token-account", - "spl-memo", - "spl-token", - "thiserror", + "cfg-if 1.0.0", + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", ] [[package]] -name = "solana-version" -version = "1.9.20" +name = "socket2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222e2c91640d45cd9617dfc07121555a9bdac10e6e105f6931b758f46db6faaa" +checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" dependencies = [ - "log", - "rustc_version 0.4.0", - "serde", - "serde_derive", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-sdk", + "libc", + "windows-sys 0.48.0", ] [[package]] -name = "solana-vote-program" -version = "1.9.20" +name = "soketto" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4cc64945010e9e76d368493ad091aa5cf43ee16f69296290ebb5c815e433232" +checksum = "083624472e8817d44d02c0e55df043737ff11f279af924abdf93845717c2b75c" dependencies = [ - "bincode", + "base64 0.13.0", + "bytes 1.4.0", + "futures 0.3.28", + "httparse", "log", - "num-derive", - "num-traits", - "rustc_version 0.4.0", - "serde", - "serde_derive", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-logger", - "solana-metrics", - "solana-program-runtime", - "solana-sdk", - "thiserror", + "rand 0.8.4", + "sha-1", ] [[package]] @@ -7829,7 +6460,7 @@ version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d676664972e22a0796176e81e7bec41df461d1edf52090955cdab55f2c956ff2" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 1.0.95", ] @@ -7858,8 +6489,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ecb916b9664ed9f90abef0ff5a3e61454c1efea5861b2997e03f39b59b955f" dependencies = [ "Inflector", - "proc-macro-crate 1.1.3", - "proc-macro2 1.0.69", + "proc-macro-crate", + "proc-macro2", "quote 1.0.33", "syn 1.0.95", ] @@ -7943,39 +6574,6 @@ dependencies = [ "der", ] -[[package]] -name = "spl-associated-token-account" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "393e2240d521c3dd770806bff25c2c00d761ac962be106e14e22dd912007f428" -dependencies = [ - "solana-program", - "spl-token", -] - -[[package]] -name = "spl-memo" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0dc6f70db6bacea7ff25870b016a65ba1d1b6013536f08e4fd79a8f9005325" -dependencies = [ - "solana-program", -] - -[[package]] -name = "spl-token" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93bfdd5bd7c869cb565c7d7635c4fafe189b988a0bdef81063cd9585c6b8dc01" -dependencies = [ - "arrayref", - "num-derive", - "num-traits", - "num_enum", - "solana-program", - "thiserror", -] - [[package]] name = "spv_validation" version = "0.1.0" @@ -8013,31 +6611,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f9799e6d412271cb2414597581128b03f3285f260ea49f5363d07df6a332b3e" dependencies = [ "Inflector", - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "serde", "serde_json", "unicode-xid 0.2.0", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - [[package]] name = "strsim" version = "0.10.0" @@ -8065,12 +6651,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" -[[package]] -name = "symlink" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a" - [[package]] name = "syn" version = "0.11.11" @@ -8082,24 +6662,13 @@ dependencies = [ "unicode-xid 0.0.4", ] -[[package]] -name = "syn" -version = "0.15.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "unicode-xid 0.1.0", -] - [[package]] name = "syn" version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "unicode-ident", ] @@ -8110,7 +6679,7 @@ version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "unicode-ident", ] @@ -8136,7 +6705,7 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 1.0.95", "unicode-xid 0.2.0", @@ -8169,17 +6738,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" -[[package]] -name = "tar" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" -dependencies = [ - "filetime", - "libc", - "xattr", -] - [[package]] name = "tempfile" version = "3.4.0" @@ -8293,16 +6851,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "terminal_size" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "test_helpers" version = "0.1.0" @@ -8327,15 +6875,6 @@ dependencies = [ "sha2 0.10.7", ] -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - [[package]] name = "thiserror" version = "1.0.40" @@ -8351,7 +6890,7 @@ version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 2.0.38", ] @@ -8394,25 +6933,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "tiny-bip39" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc59cb9dfc85bb312c3a78fd6aa8a8582e310b0fa885d5bb877f6dcc601839d" -dependencies = [ - "anyhow", - "hmac 0.8.1", - "once_cell", - "pbkdf2 0.4.0", - "rand 0.7.3", - "rustc-hash", - "sha2 0.9.9", - "thiserror", - "unicode-normalization", - "wasm-bindgen", - "zeroize", -] - [[package]] name = "tiny-keccak" version = "1.4.4" @@ -8457,7 +6977,6 @@ dependencies = [ "libc", "mio", "num_cpus", - "parking_lot 0.12.0", "pin-project-lite 0.2.9", "signal-hook-registry", "socket2 0.4.9", @@ -8491,7 +7010,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 2.0.38", ] @@ -8624,7 +7143,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6fdaae4c2c638bb70fe42803a26fbd6fc6ac8c72f5c59f67ecc2a2dcabf4b07" dependencies = [ "prettyplease", - "proc-macro2 1.0.69", + "proc-macro2", "prost-build", "quote 1.0.33", "syn 1.0.95", @@ -8680,7 +7199,7 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 2.0.38", ] @@ -8790,7 +7309,7 @@ dependencies = [ "ipconfig", "lazy_static", "lru-cache", - "parking_lot 0.12.0", + "parking_lot", "resolv-conf", "smallvec 1.6.1", "thiserror", @@ -8824,7 +7343,6 @@ dependencies = [ "url", "utf-8", "webpki", - "webpki-roots 0.22.3", ] [[package]] @@ -8886,12 +7404,6 @@ version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" - [[package]] name = "unicode-xid" version = "0.2.0" @@ -8930,16 +7442,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" -[[package]] -name = "uriparse" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e515b1ada404168e145ac55afba3c42f04cf972201a8552d42e2abb17c1b7221" -dependencies = [ - "fnv", - "lazy_static", -] - [[package]] name = "url" version = "2.2.2" @@ -9006,12 +7508,6 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.4" @@ -9132,7 +7628,7 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 2.0.38", "wasm-bindgen-shared", @@ -9166,7 +7662,7 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 2.0.38", "wasm-bindgen-backend", @@ -9199,7 +7695,7 @@ version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c2e18093f11c19ca4e188c177fecc7c372304c311189f12c2f9bea5b7324ac7" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", ] @@ -9233,7 +7729,7 @@ dependencies = [ "js-sys", "jsonrpc-core", "log", - "parking_lot 0.12.0", + "parking_lot", "pin-project", "rand 0.8.4", "reqwest", @@ -9605,24 +8101,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "xattr" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" -dependencies = [ - "libc", -] - -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - [[package]] name = "yamux" version = "0.12.1" @@ -9632,7 +8110,7 @@ dependencies = [ "futures 0.3.28", "log", "nohash-hasher", - "parking_lot 0.12.0", + "parking_lot", "pin-project", "rand 0.8.4", "static_assertions", @@ -9648,7 +8126,7 @@ dependencies = [ "instant", "log", "nohash-hasher", - "parking_lot 0.12.0", + "parking_lot", "pin-project", "rand 0.8.4", "static_assertions", @@ -9809,37 +8287,8 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", "quote 1.0.33", "syn 1.0.95", "synstructure", ] - -[[package]] -name = "zstd" -version = "0.9.2+zstd.1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2390ea1bf6c038c39674f22d95f0564725fc06034a47129179810b2fc58caa54" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "4.1.3+zstd.1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e99d81b99fb3c2c2c794e3fe56c305c63d5173a16a46b5850b07c935ffc7db79" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "1.6.2+zstd.1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2daf2f248d9ea44454bfcb2516534e8b8ad2fc91bf818a1885495fc42bc8ac9f" -dependencies = [ - "cc", - "libc", -] diff --git a/mm2src/coins/Cargo.toml b/mm2src/coins/Cargo.toml index c870e9baf0..62c0e24ea0 100644 --- a/mm2src/coins/Cargo.toml +++ b/mm2src/coins/Cargo.toml @@ -5,17 +5,6 @@ edition = "2018" [features] zhtlc-native-tests = [] -# TODO -enable-solana = [ - "dep:bincode", - "dep:ed25519-dalek-bip32", - "dep:solana-client", - "dep:solana-sdk", - "dep:solana-transaction-status", - "dep:spl-token", - "dep:spl-associated-token-account", - "dep:satomic-swap" -] enable-sia = [ "dep:reqwest", "dep:blake2b_simd", @@ -96,7 +85,6 @@ rlp = { version = "0.5" } rmp-serde = "0.14.3" rpc = { path = "../mm2_bitcoin/rpc" } rpc_task = { path = "../rpc_task" } -satomic-swap = { git = "https://github.com/KomodoPlatform/satomic-swap.git", rev = "413e472", optional = true } script = { path = "../mm2_bitcoin/script" } secp256k1 = { version = "0.20" } ser_error = { path = "../derives/ser_error" } @@ -125,15 +113,6 @@ zcash_client_backend = { git = "https://github.com/KomodoPlatform/librustzcash.g zcash_extras = { git = "https://github.com/KomodoPlatform/librustzcash.git", tag = "k-1.4.1" } zcash_primitives = {features = ["transparent-inputs"], git = "https://github.com/KomodoPlatform/librustzcash.git", tag = "k-1.4.1" } -[target.'cfg(all(not(target_os = "ios"), not(target_os = "android"), not(target_arch = "wasm32")))'.dependencies] -bincode = { version = "1.3.3", default-features = false, optional = true } -ed25519-dalek-bip32 = { version = "0.2.0", default-features = false, optional = true } -solana-client = { version = "1", default-features = false, optional = true } -solana-sdk = { version = "1", default-features = false, optional = true } -solana-transaction-status = { version = "1", optional = true } -spl-token = { version = "3", optional = true } -spl-associated-token-account = { version = "1", optional = true } - [target.'cfg(target_arch = "wasm32")'.dependencies] blake2b_simd = "0.5" ff = "0.8" diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 500963fba8..3e5f55ce41 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -124,29 +124,6 @@ macro_rules! try_f { }; } -#[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!( - "{:?}", $err - )))) - }; -} - -#[cfg(all(feature = "enable-solana", not(target_arch = "wasm32")))] -macro_rules! try_tx_fus_opt { - ($e: expr, $err: expr) => { - match $e { - Some(ok) => ok, - None => { - return Box::new(futures01::future::err(crate::TransactionErr::Plain(ERRL!( - "{:?}", $err - )))) - }, - } - }; -} - /// `TransactionErr` compatible `try_fus` macro. macro_rules! try_tx_fus { ($e: expr) => { @@ -280,30 +257,6 @@ pub mod tx_history_storage; #[cfg(feature = "enable-sia")] pub mod siacoin; #[cfg(feature = "enable-sia")] use siacoin::SiaCoin; -#[doc(hidden)] -#[allow(unused_variables)] -#[cfg(all( - feature = "enable-solana", - not(target_os = "ios"), - not(target_os = "android"), - not(target_arch = "wasm32") -))] -pub mod solana; -#[cfg(all( - feature = "enable-solana", - not(target_os = "ios"), - not(target_os = "android"), - not(target_arch = "wasm32") -))] -pub use solana::spl::SplToken; -#[cfg(all( - feature = "enable-solana", - not(target_os = "ios"), - not(target_os = "android"), - not(target_arch = "wasm32") -))] -pub use solana::{SolTransaction, SolanaActivationParams, SolanaCoin, SolanaFeeDetails}; - pub mod utxo; use utxo::bch::{bch_coin_with_policy, BchActivationRequest, BchCoin}; use utxo::qtum::{self, qtum_coin_with_policy, Qrc20AddressError, QtumCoin, QtumDelegationOps, QtumDelegationRequest, @@ -631,8 +584,6 @@ pub trait Transaction: fmt::Debug + 'static { pub enum TransactionEnum { UtxoTx(UtxoTx), SignedEthTx(SignedEthTx), - #[cfg(all(feature = "enable-solana", not(target_arch = "wasm32")))] - SolTransaction(SolTransaction), ZTransaction(ZTransaction), CosmosTransaction(CosmosTransaction), #[cfg(not(target_arch = "wasm32"))] @@ -641,8 +592,6 @@ pub enum TransactionEnum { ifrom!(TransactionEnum, UtxoTx); ifrom!(TransactionEnum, SignedEthTx); -#[cfg(all(feature = "enable-solana", not(target_arch = "wasm32")))] -ifrom!(TransactionEnum, SolTransaction); ifrom!(TransactionEnum, ZTransaction); #[cfg(not(target_arch = "wasm32"))] ifrom!(TransactionEnum, LightningPayment); @@ -666,8 +615,6 @@ impl Deref for TransactionEnum { TransactionEnum::CosmosTransaction(ref t) => t, #[cfg(not(target_arch = "wasm32"))] TransactionEnum::LightningPayment(ref p) => p, - #[cfg(all(feature = "enable-solana", not(target_arch = "wasm32")))] - TransactionEnum::SolTransaction(ref s) => s, } } } @@ -2271,13 +2218,6 @@ pub enum TxFeeDetails { Qrc20(Qrc20FeeDetails), Slp(SlpFeeDetails), Tendermint(TendermintFeeDetails), - #[cfg(all( - feature = "enable-solana", - not(target_os = "ios"), - not(target_os = "android"), - not(target_arch = "wasm32") - ))] - Solana(SolanaFeeDetails), } /// Deserialize the TxFeeDetails as an untagged enum. @@ -2292,13 +2232,6 @@ impl<'de> Deserialize<'de> for TxFeeDetails { Utxo(UtxoFeeDetails), Eth(EthTxFeeDetails), Qrc20(Qrc20FeeDetails), - #[cfg(all( - feature = "enable-solana", - not(target_os = "ios"), - not(target_os = "android"), - not(target_arch = "wasm32") - ))] - Solana(SolanaFeeDetails), Tendermint(TendermintFeeDetails), } @@ -2306,13 +2239,6 @@ impl<'de> Deserialize<'de> for TxFeeDetails { TxFeeDetailsUnTagged::Utxo(f) => Ok(TxFeeDetails::Utxo(f)), TxFeeDetailsUnTagged::Eth(f) => Ok(TxFeeDetails::Eth(f)), TxFeeDetailsUnTagged::Qrc20(f) => Ok(TxFeeDetails::Qrc20(f)), - #[cfg(all( - feature = "enable-solana", - not(target_os = "ios"), - not(target_os = "android"), - not(target_arch = "wasm32") - ))] - TxFeeDetailsUnTagged::Solana(f) => Ok(TxFeeDetails::Solana(f)), TxFeeDetailsUnTagged::Tendermint(f) => Ok(TxFeeDetails::Tendermint(f)), } } @@ -2330,16 +2256,6 @@ impl From for TxFeeDetails { fn from(qrc20_details: Qrc20FeeDetails) -> Self { TxFeeDetails::Qrc20(qrc20_details) } } -#[cfg(all( - feature = "enable-solana", - not(target_os = "ios"), - not(target_os = "android"), - not(target_arch = "wasm32") -))] -impl From for TxFeeDetails { - fn from(solana_details: SolanaFeeDetails) -> Self { TxFeeDetails::Solana(solana_details) } -} - impl From for TxFeeDetails { fn from(tendermint_details: TendermintFeeDetails) -> Self { TxFeeDetails::Tendermint(tendermint_details) } } @@ -3544,20 +3460,6 @@ pub enum MmCoinEnum { SlpToken(SlpToken), Tendermint(TendermintCoin), TendermintToken(TendermintToken), - #[cfg(all( - feature = "enable-solana", - not(target_os = "ios"), - not(target_os = "android"), - not(target_arch = "wasm32") - ))] - SolanaCoin(SolanaCoin), - #[cfg(all( - feature = "enable-solana", - not(target_os = "ios"), - not(target_os = "android"), - not(target_arch = "wasm32") - ))] - SplToken(SplToken), #[cfg(not(target_arch = "wasm32"))] LightningCoin(LightningCoin), #[cfg(feature = "enable-sia")] @@ -3577,26 +3479,6 @@ impl From for MmCoinEnum { fn from(c: TestCoin) -> MmCoinEnum { MmCoinEnum::Test(c) } } -#[cfg(all( - feature = "enable-solana", - not(target_os = "ios"), - not(target_os = "android"), - not(target_arch = "wasm32") -))] -impl From for MmCoinEnum { - fn from(c: SolanaCoin) -> MmCoinEnum { MmCoinEnum::SolanaCoin(c) } -} - -#[cfg(all( - feature = "enable-solana", - not(target_os = "ios"), - not(target_os = "android"), - not(target_arch = "wasm32") -))] -impl From for MmCoinEnum { - fn from(c: SplToken) -> MmCoinEnum { MmCoinEnum::SplToken(c) } -} - impl From for MmCoinEnum { fn from(coin: QtumCoin) -> Self { MmCoinEnum::QtumCoin(coin) } } @@ -3654,20 +3536,6 @@ impl Deref for MmCoinEnum { #[cfg(feature = "enable-sia")] MmCoinEnum::SiaCoin(ref c) => c, MmCoinEnum::Test(ref c) => c, - #[cfg(all( - feature = "enable-solana", - not(target_os = "ios"), - not(target_os = "android"), - not(target_arch = "wasm32") - ))] - MmCoinEnum::SolanaCoin(ref c) => c, - #[cfg(all( - feature = "enable-solana", - not(target_os = "ios"), - not(target_os = "android"), - not(target_arch = "wasm32") - ))] - MmCoinEnum::SplToken(ref c) => c, } } } @@ -4377,14 +4245,6 @@ pub enum CoinProtocol { network: BlockchainNetwork, confirmation_targets: PlatformCoinConfirmationTargets, }, - #[cfg(all(feature = "enable-solana", not(target_arch = "wasm32")))] - SOLANA, - #[cfg(all(feature = "enable-solana", not(target_arch = "wasm32")))] - SPLTOKEN { - platform: String, - token_contract_address: String, - decimals: u8, - }, ZHTLC(ZcoinProtocolInfo), #[cfg(feature = "enable-sia")] SIA, @@ -4646,14 +4506,6 @@ pub async fn lp_coininit(ctx: &MmArc, ticker: &str, req: &Json) -> Result return ERR!("NFT protocol is not supported by lp_coininit"), #[cfg(not(target_arch = "wasm32"))] CoinProtocol::LIGHTNING { .. } => return ERR!("Lightning protocol is not supported by lp_coininit"), - #[cfg(all(feature = "enable-solana", not(target_arch = "wasm32")))] - CoinProtocol::SOLANA => { - return ERR!("Solana protocol is not supported by lp_coininit - use enable_solana_with_tokens instead") - }, - #[cfg(all(feature = "enable-solana", not(target_arch = "wasm32")))] - CoinProtocol::SPLTOKEN { .. } => { - return ERR!("SplToken protocol is not supported by lp_coininit - use enable_spl instead") - }, #[cfg(feature = "enable-sia")] CoinProtocol::SIA { .. } => { return ERR!("SIA protocol is not supported by lp_coininit. Use task::enable_sia::init"); @@ -5238,10 +5090,6 @@ pub fn address_by_coin_conf_and_pubkey_str( CoinProtocol::LIGHTNING { .. } => { ERR!("address_by_coin_conf_and_pubkey_str is not implemented for lightning protocol yet!") }, - #[cfg(all(feature = "enable-solana", not(target_arch = "wasm32")))] - CoinProtocol::SOLANA | CoinProtocol::SPLTOKEN { .. } => { - ERR!("Solana pubkey is the public address - you do not need to use this rpc call.") - }, CoinProtocol::ZHTLC { .. } => ERR!("address_by_coin_conf_and_pubkey_str is not supported for ZHTLC protocol!"), #[cfg(feature = "enable-sia")] CoinProtocol::SIA { .. } => ERR!("address_by_coin_conf_and_pubkey_str is not supported for SIA protocol!"), // TODO Alright diff --git a/mm2src/coins/solana.rs b/mm2src/coins/solana.rs deleted file mode 100644 index 2b9abef1ca..0000000000 --- a/mm2src/coins/solana.rs +++ /dev/null @@ -1,1098 +0,0 @@ -use std::{collections::HashMap, - convert::{TryFrom, TryInto}, - fmt::Debug, - ops::Deref, - str::FromStr, - sync::{Arc, Mutex}}; - -use async_trait::async_trait; -use base58::ToBase58; -use bincode::{deserialize, serialize}; -use bitcrypto::sha256; -use common::{async_blocking, - executor::{abortable_queue::AbortableQueue, AbortableSystem, AbortedError}, - log::error, - now_sec}; -use crypto::HDPathToCoin; -use derive_more::Display; -use futures::{compat::Future01CompatExt, - {FutureExt, TryFutureExt}}; -use futures01::Future; -use keys::KeyPair; -use mm2_core::mm_ctx::MmArc; -use mm2_err_handle::prelude::*; -use mm2_number::{BigDecimal, MmNumber}; -use num_traits::ToPrimitive; -use rpc::v1::types::Bytes as BytesJson; -pub use satomic_swap::{instruction::AtomicSwapInstruction, STORAGE_SPACE_ALLOCATED}; -use serde_json::{self as json, Value as Json}; -use solana_client::{client_error::{ClientError, ClientErrorKind}, - rpc_client::RpcClient, - rpc_request::TokenAccountsFilter}; -pub use solana_sdk::transaction::Transaction as SolTransaction; -use solana_sdk::{commitment_config::{CommitmentConfig, CommitmentLevel}, - instruction::{AccountMeta, Instruction}, - native_token::sol_to_lamports, - program_error::ProgramError, - pubkey::{ParsePubkeyError, Pubkey}, - signature::{Keypair as SolKeypair, Signer}}; -use spl_token::solana_program; - -use super::{CoinBalance, HistorySyncState, MarketCoinOps, MmCoin, SwapOps, TradeFee, Transaction, TransactionEnum, - TransactionErr, WatcherOps}; -use crate::coin_errors::{MyAddressError, ValidatePaymentResult}; -use crate::hd_wallet::HDPathAccountToAddressId; -use crate::solana::{solana_common::{lamports_to_sol, PrepareTransferData, SufficientBalanceError}, - spl::SplTokenInfo}; -use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinFutSpawner, ConfirmPaymentInput, DexFee, - FeeApproxStage, FoundSwapTxSpend, MakerSwapTakerCoin, MmCoinEnum, NegotiateSwapContractAddrErr, - PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, PrivKeyBuildPolicy, - PrivKeyPolicyNotAllowed, RawTransactionError, RawTransactionFut, RawTransactionRequest, - RawTransactionResult, RefundError, RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput, - SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignRawTransactionRequest, SignatureResult, - SpendPaymentArgs, TakerSwapMakerCoin, TradePreimageFut, TradePreimageResult, TradePreimageValue, - TransactionData, TransactionDetails, TransactionFut, TransactionResult, TransactionType, TxMarshalingErr, - UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, - ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, - ValidateWatcherSpendInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherReward, WatcherRewardError, - WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, - WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult}; - -pub mod solana_common; -mod solana_decode_tx_helpers; -pub mod spl; - -#[cfg(test)] mod solana_common_tests; -#[cfg(test)] mod solana_tests; -#[cfg(test)] mod spl_tests; - -pub const SOLANA_DEFAULT_DECIMALS: u64 = 9; -pub const LAMPORTS_DUMMY_AMOUNT: u64 = 10; - -#[async_trait] -pub trait SolanaCommonOps { - fn rpc(&self) -> &RpcClient; - - fn is_token(&self) -> bool; - - async fn check_balance_and_prepare_transfer( - &self, - max: bool, - amount: BigDecimal, - fees: u64, - ) -> Result>; -} - -impl From for BalanceError { - fn from(e: ClientError) -> Self { - match e.kind { - ClientErrorKind::Io(e) => BalanceError::Transport(e.to_string()), - ClientErrorKind::Reqwest(e) => BalanceError::Transport(e.to_string()), - ClientErrorKind::RpcError(e) => BalanceError::Transport(format!("{:?}", e)), - ClientErrorKind::SerdeJson(e) => BalanceError::InvalidResponse(e.to_string()), - ClientErrorKind::Custom(e) => BalanceError::Internal(e), - ClientErrorKind::SigningError(_) - | ClientErrorKind::TransactionError(_) - | ClientErrorKind::FaucetError(_) => BalanceError::Internal("not_reacheable".to_string()), - } - } -} - -impl From for BalanceError { - fn from(e: ParsePubkeyError) -> Self { BalanceError::Internal(format!("{:?}", e)) } -} - -impl From for WithdrawError { - fn from(e: ClientError) -> Self { - match e.kind { - ClientErrorKind::Io(e) => WithdrawError::Transport(e.to_string()), - ClientErrorKind::Reqwest(e) => WithdrawError::Transport(e.to_string()), - ClientErrorKind::RpcError(e) => WithdrawError::Transport(format!("{:?}", e)), - ClientErrorKind::SerdeJson(e) => WithdrawError::InternalError(e.to_string()), - ClientErrorKind::Custom(e) => WithdrawError::InternalError(e), - ClientErrorKind::SigningError(_) - | ClientErrorKind::TransactionError(_) - | ClientErrorKind::FaucetError(_) => WithdrawError::InternalError("not_reacheable".to_string()), - } - } -} - -impl From for WithdrawError { - fn from(e: ParsePubkeyError) -> Self { WithdrawError::InvalidAddress(format!("{:?}", e)) } -} - -impl From for WithdrawError { - fn from(e: ProgramError) -> Self { WithdrawError::InternalError(format!("{:?}", e)) } -} - -#[derive(Debug)] -pub enum AccountError { - NotFundedError(String), - ParsePubKeyError(String), - ClientError(ClientErrorKind), -} - -impl From for AccountError { - fn from(e: ClientError) -> Self { AccountError::ClientError(e.kind) } -} - -impl From for AccountError { - fn from(e: ParsePubkeyError) -> Self { AccountError::ParsePubKeyError(format!("{:?}", e)) } -} - -impl From for WithdrawError { - fn from(e: AccountError) -> Self { - match e { - AccountError::NotFundedError(_) => WithdrawError::ZeroBalanceToWithdrawMax, - AccountError::ParsePubKeyError(err) => WithdrawError::InternalError(err), - AccountError::ClientError(e) => WithdrawError::Transport(format!("{:?}", e)), - } - } -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct SolanaActivationParams { - confirmation_commitment: CommitmentLevel, - client_url: String, - #[serde(default)] - path_to_address: HDPathAccountToAddressId, -} - -#[derive(Debug, Display)] -pub enum SolanaFromLegacyReqErr { - InvalidCommitmentLevel(String), - InvalidClientParsing(json::Error), - ClientNoAvailableNodes(String), -} - -#[derive(Debug, Display)] -pub enum KeyPairCreationError { - #[display(fmt = "Signature error: {}", _0)] - SignatureError(ed25519_dalek::SignatureError), - #[display(fmt = "KeyPairFromSeed error: {}", _0)] - KeyPairFromSeed(String), -} - -impl From for KeyPairCreationError { - fn from(e: ed25519_dalek::SignatureError) -> Self { KeyPairCreationError::SignatureError(e) } -} - -fn generate_keypair_from_slice(priv_key: &[u8]) -> Result> { - let secret_key = ed25519_dalek::SecretKey::from_bytes(priv_key)?; - let public_key = ed25519_dalek::PublicKey::from(&secret_key); - let key_pair = ed25519_dalek::Keypair { - secret: secret_key, - public: public_key, - }; - solana_sdk::signature::keypair_from_seed(key_pair.to_bytes().as_ref()) - .map_to_mm(|e| KeyPairCreationError::KeyPairFromSeed(e.to_string())) -} - -pub async fn solana_coin_with_policy( - ctx: &MmArc, - ticker: &str, - conf: &Json, - params: SolanaActivationParams, - priv_key_policy: PrivKeyBuildPolicy, -) -> Result { - let client = RpcClient::new_with_commitment(params.client_url.clone(), CommitmentConfig { - commitment: params.confirmation_commitment, - }); - let decimals = conf["decimals"].as_u64().unwrap_or(SOLANA_DEFAULT_DECIMALS) as u8; - - let priv_key = match priv_key_policy { - PrivKeyBuildPolicy::IguanaPrivKey(priv_key) => priv_key, - PrivKeyBuildPolicy::GlobalHDAccount(global_hd) => { - let path_to_coin: HDPathToCoin = try_s!(json::from_value(conf["derivation_path"].clone())); - let derivation_path = try_s!(params.path_to_address.to_derivation_path(&path_to_coin)); - try_s!(global_hd.derive_secp256k1_secret(&derivation_path)) - }, - PrivKeyBuildPolicy::Trezor => return ERR!("{}", PrivKeyPolicyNotAllowed::HardwareWalletNotSupported), - }; - - let key_pair = try_s!(generate_keypair_from_slice(priv_key.as_slice())); - let my_address = key_pair.pubkey().to_string(); - let spl_tokens_infos = Arc::new(Mutex::new(HashMap::new())); - - // Create an abortable system linked to the `MmCtx` so if the context is stopped via `MmArc::stop`, - // all spawned futures related to `SolanaCoin` will be aborted as well. - let abortable_system: AbortableQueue = try_s!(ctx.abortable_system.create_subsystem()); - - let solana_coin = SolanaCoin(Arc::new(SolanaCoinImpl { - my_address, - key_pair, - ticker: ticker.to_string(), - client, - decimals, - spl_tokens_infos, - abortable_system, - })); - Ok(solana_coin) -} - -/// pImpl idiom. -pub struct SolanaCoinImpl { - ticker: String, - key_pair: SolKeypair, - client: RpcClient, - decimals: u8, - my_address: String, - spl_tokens_infos: Arc>>, - /// This spawner is used to spawn coin's related futures that should be aborted on coin deactivation - /// and on [`MmArc::stop`]. - pub abortable_system: AbortableQueue, -} - -#[derive(Clone)] -pub struct SolanaCoin(Arc); -impl Deref for SolanaCoin { - type Target = SolanaCoinImpl; - fn deref(&self) -> &SolanaCoinImpl { &self.0 } -} - -#[async_trait] -impl SolanaCommonOps for SolanaCoin { - fn rpc(&self) -> &RpcClient { &self.client } - - fn is_token(&self) -> bool { false } - - async fn check_balance_and_prepare_transfer( - &self, - max: bool, - amount: BigDecimal, - fees: u64, - ) -> Result> { - solana_common::check_balance_and_prepare_transfer(self, max, amount, fees).await - } -} - -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] -pub struct SolanaFeeDetails { - pub amount: BigDecimal, -} - -async fn withdraw_base_coin_impl(coin: SolanaCoin, req: WithdrawRequest) -> WithdrawResult { - let (hash, fees) = coin.estimate_withdraw_fees().await?; - let res = coin - .check_balance_and_prepare_transfer(req.max, req.amount.clone(), fees) - .await?; - let to = solana_sdk::pubkey::Pubkey::try_from(&*req.to)?; - let tx = solana_sdk::system_transaction::transfer(&coin.key_pair, &to, res.lamports_to_send, hash); - let serialized_tx = serialize(&tx).map_to_mm(|e| WithdrawError::InternalError(e.to_string()))?; - let total_amount = lamports_to_sol(res.lamports_to_send); - let received_by_me = if req.to == coin.my_address { - total_amount.clone() - } else { - 0.into() - }; - let spent_by_me = &total_amount + &res.sol_required; - Ok(TransactionDetails { - tx: TransactionData::new_signed(serialized_tx.into(), tx.signatures[0].to_string()), - from: vec![coin.my_address.clone()], - to: vec![req.to], - total_amount: spent_by_me.clone(), - my_balance_change: &received_by_me - &spent_by_me, - spent_by_me, - received_by_me, - block_height: 0, - timestamp: now_sec(), - fee_details: Some( - SolanaFeeDetails { - amount: res.sol_required, - } - .into(), - ), - coin: coin.ticker.clone(), - internal_id: vec![].into(), - kmd_rewards: None, - transaction_type: TransactionType::StandardTransfer, - memo: None, - }) -} - -async fn withdraw_impl(coin: SolanaCoin, req: WithdrawRequest) -> WithdrawResult { - let validate_address_result = coin.validate_address(&req.to); - if !validate_address_result.is_valid { - return MmError::err(WithdrawError::InvalidAddress( - validate_address_result.reason.unwrap_or_else(|| "Unknown".to_string()), - )); - } - withdraw_base_coin_impl(coin, req).await -} - -type SolTxFut = Box + Send + 'static>; - -impl Transaction for SolTransaction { - fn tx_hex(&self) -> Vec { - serialize(self).unwrap_or_else(|e| { - error!("Error serializing SolTransaction: {}", e); - vec![] - }) - } - - fn tx_hash_as_bytes(&self) -> BytesJson { - let hash = match self.signatures.get(0) { - Some(signature) => signature, - None => { - error!("No signature found in SolTransaction"); - return BytesJson(Vec::new()); - }, - }; - BytesJson(Vec::from(hash.as_ref())) - } -} - -impl SolanaCoin { - pub async fn estimate_withdraw_fees(&self) -> Result<(solana_sdk::hash::Hash, u64), MmError> { - let hash = async_blocking({ - let coin = self.clone(); - move || coin.rpc().get_latest_blockhash() - }) - .await?; - let to = self.key_pair.pubkey(); - - let tx = solana_sdk::system_transaction::transfer(&self.key_pair, &to, LAMPORTS_DUMMY_AMOUNT, hash); - let fees = async_blocking({ - let coin = self.clone(); - move || coin.rpc().get_fee_for_message(tx.message()) - }) - .await?; - Ok((hash, fees)) - } - - pub async fn my_balance_spl(&self, infos: SplTokenInfo) -> Result> { - let token_accounts = async_blocking({ - let coin = self.clone(); - move || { - coin.rpc().get_token_accounts_by_owner( - &coin.key_pair.pubkey(), - TokenAccountsFilter::Mint(infos.token_contract_address), - ) - } - }) - .await?; - if token_accounts.is_empty() { - return Ok(CoinBalance { - spendable: Default::default(), - unspendable: Default::default(), - }); - } - let actual_token_pubkey = - Pubkey::from_str(&token_accounts[0].pubkey).map_err(|e| BalanceError::Internal(format!("{:?}", e)))?; - let amount = async_blocking({ - let coin = self.clone(); - move || coin.rpc().get_token_account_balance(&actual_token_pubkey) - }) - .await?; - let balance = - BigDecimal::from_str(&amount.ui_amount_string).map_to_mm(|e| BalanceError::Internal(e.to_string()))?; - Ok(CoinBalance { - spendable: balance, - unspendable: Default::default(), - }) - } - - fn my_balance_impl(&self) -> BalanceFut { - let coin = self.clone(); - let fut = async_blocking(move || { - // this is blocking IO - let res = coin.rpc().get_balance(&coin.key_pair.pubkey())?; - Ok(lamports_to_sol(res)) - }); - Box::new(fut.boxed().compat()) - } - - pub fn add_spl_token_info(&self, ticker: String, info: SplTokenInfo) { - self.spl_tokens_infos.lock().unwrap().insert(ticker, info); - } - - /// WARNING - /// Be very careful using this function since it returns dereferenced clone - /// of value behind the MutexGuard and makes it non-thread-safe. - pub fn get_spl_tokens_infos(&self) -> HashMap { - let guard = self.spl_tokens_infos.lock().unwrap(); - (*guard).clone() - } - - fn send_hash_time_locked_payment(&self, args: SendPaymentArgs<'_>) -> SolTxFut { - let receiver = Pubkey::new(args.other_pubkey); - let swap_program_id = Pubkey::new(try_tx_fus_opt!( - args.swap_contract_address, - format!( - "Unable to extract Bytes from args.swap_contract_address ( {:?} )", - args.swap_contract_address - ) - )); - let amount = sol_to_lamports(try_tx_fus_opt!( - args.amount.to_f64(), - format!("Unable to extract value from args.amount ( {:?} )", args.amount) - )); - let secret_hash: [u8; 32] = try_tx_fus!(<[u8; 32]>::try_from(args.secret_hash)); - let (vault_pda, vault_pda_data, vault_bump_seed, vault_bump_seed_data, rent_exemption_lamports) = - try_tx_fus!(self.create_vaults(args.time_lock, secret_hash, swap_program_id, STORAGE_SPACE_ALLOCATED)); - let swap_instruction = AtomicSwapInstruction::LamportsPayment { - secret_hash, - lock_time: args.time_lock, - amount, - receiver, - rent_exemption_lamports, - vault_bump_seed, - vault_bump_seed_data, - }; - - let accounts = vec![ - AccountMeta::new(self.key_pair.pubkey(), true), - AccountMeta::new(vault_pda_data, false), - AccountMeta::new(vault_pda, false), - AccountMeta::new(solana_program::system_program::id(), false), - ]; - self.sign_and_send_swap_transaction_fut(swap_program_id, accounts, swap_instruction.pack()) - } - - fn spend_hash_time_locked_payment(&self, args: SpendPaymentArgs) -> SolTxFut { - let sender = Pubkey::new(args.other_pubkey); - let swap_program_id = Pubkey::new(try_tx_fus_opt!( - args.swap_contract_address.as_ref(), - format!( - "Unable to extract Bytes from args.swap_contract_address ( {:?} )", - args.swap_contract_address - ) - )); - let secret: [u8; 32] = try_tx_fus!(<[u8; 32]>::try_from(args.secret)); - let secret_hash = sha256(secret.as_slice()).take(); - let (lock_time, tx_secret_hash, amount, token_program) = - try_tx_fus!(self.get_swap_transaction_details(args.other_payment_tx)); - if secret_hash != tx_secret_hash { - try_tx_fus_err!(format!( - "Provided secret_hash {:?} does not match transaction secret_hash {:?}", - secret_hash, tx_secret_hash - )); - } - let (vault_pda, vault_pda_data, vault_bump_seed, vault_bump_seed_data, _rent_exemption_lamports) = - try_tx_fus!(self.create_vaults(lock_time, secret_hash, swap_program_id, STORAGE_SPACE_ALLOCATED)); - let swap_instruction = AtomicSwapInstruction::ReceiverSpend { - secret, - lock_time, - amount, - sender, - token_program, - vault_bump_seed, - vault_bump_seed_data, - }; - let accounts = vec![ - AccountMeta::new(self.key_pair.pubkey(), true), - AccountMeta::new(vault_pda_data, false), - AccountMeta::new(vault_pda, false), - AccountMeta::new(solana_program::system_program::id(), false), - ]; - self.sign_and_send_swap_transaction_fut(swap_program_id, accounts, swap_instruction.pack()) - } - - fn refund_hash_time_locked_payment(&self, args: RefundPaymentArgs) -> SolTxFut { - let receiver = Pubkey::new(args.other_pubkey); - let swap_program_id = Pubkey::new(try_tx_fus_opt!( - args.swap_contract_address.as_ref(), - format!( - "Unable to extract Bytes from args.swap_contract_address ( {:?} )", - args.swap_contract_address - ) - )); - let (lock_time, secret_hash, amount, token_program) = - try_tx_fus!(self.get_swap_transaction_details(args.payment_tx)); - let (vault_pda, vault_pda_data, vault_bump_seed, vault_bump_seed_data, _rent_exemption_lamports) = - try_tx_fus!(self.create_vaults(lock_time, secret_hash, swap_program_id, STORAGE_SPACE_ALLOCATED)); - let swap_instruction = AtomicSwapInstruction::SenderRefund { - secret_hash, - lock_time, - amount, - receiver, - token_program, - vault_bump_seed, - vault_bump_seed_data, - }; - let accounts = vec![ - AccountMeta::new(self.key_pair.pubkey(), true), // Marked as signer - AccountMeta::new(vault_pda_data, false), // Not a signer - AccountMeta::new(vault_pda, false), // Not a signer - AccountMeta::new(solana_program::system_program::id(), false), //system_program must be included - ]; - self.sign_and_send_swap_transaction_fut(swap_program_id, accounts, swap_instruction.pack()) - } - - fn get_swap_transaction_details(&self, tx_hex: &[u8]) -> Result<(u64, [u8; 32], u64, Pubkey), Box> { - let transaction: SolTransaction = deserialize(tx_hex) - .map_err(|e| Box::new(TransactionErr::Plain(ERRL!("error deserializing tx_hex: {:?}", e))))?; - - let instruction = transaction - .message - .instructions - .get(0) - .ok_or_else(|| Box::new(TransactionErr::Plain(ERRL!("Instruction not found in message"))))?; - - let instruction_data = &instruction.data[..]; - let instruction = AtomicSwapInstruction::unpack(instruction_data[0], instruction_data) - .map_err(|e| Box::new(TransactionErr::Plain(ERRL!("error unpacking tx data: {:?}", e))))?; - - match instruction { - AtomicSwapInstruction::LamportsPayment { - secret_hash, - lock_time, - amount, - .. - } => Ok((lock_time, secret_hash, amount, Pubkey::new_from_array([0; 32]))), - AtomicSwapInstruction::SPLTokenPayment { - secret_hash, - lock_time, - amount, - token_program, - .. - } => Ok((lock_time, secret_hash, amount, token_program)), - AtomicSwapInstruction::ReceiverSpend { - secret, - lock_time, - amount, - token_program, - .. - } => Ok((lock_time, sha256(&secret).take(), amount, token_program)), - AtomicSwapInstruction::SenderRefund { - secret_hash, - lock_time, - amount, - token_program, - .. - } => Ok((lock_time, secret_hash, amount, token_program)), - } - } - - fn sign_and_send_swap_transaction_fut( - &self, - program_id: Pubkey, - accounts: Vec, - data: Vec, - ) -> SolTxFut { - let coin = self.clone(); - Box::new( - async move { coin.sign_and_send_swap_transaction(program_id, accounts, data).await } - .boxed() - .compat(), - ) - } - - pub async fn sign_and_send_swap_transaction( - &self, - program_id: Pubkey, - accounts: Vec, - data: Vec, - ) -> Result { - // Construct the instruction to send to the program - // The parameters here depend on your specific program's requirements - let instruction = Instruction { - program_id, - accounts, // Specify account metas here - data, // Pass data to the program here - }; - - // Create a transaction - let recent_blockhash = self - .client - .get_latest_blockhash() - .map_err(|e| TransactionErr::Plain(format!("Failed to get recent blockhash: {:?}", e)))?; - - let transaction: SolTransaction = SolTransaction::new_signed_with_payer( - &[instruction], - Some(&self.key_pair.pubkey()), //payer pubkey - &[&self.key_pair], //payer - recent_blockhash, - ); - - // Send the transaction - let tx = self - .client - .send_and_confirm_transaction(&transaction) - .map(|_signature| transaction) - .map_err(|e| TransactionErr::Plain(ERRL!("Solana ClientError: {:?}", e)))?; - - Ok(tx) - } - - fn create_vaults( - &self, - lock_time: u64, - secret_hash: [u8; 32], - program_id: Pubkey, - space: u64, - ) -> Result<(Pubkey, Pubkey, u8, u8, u64), Box> { - let seeds: &[&[u8]] = &[b"swap", &lock_time.to_le_bytes()[..], &secret_hash[..]]; - let (vault_pda, bump_seed) = Pubkey::find_program_address(seeds, &program_id); - - let seeds_data: &[&[u8]] = &[b"swap_data", &lock_time.to_le_bytes()[..], &secret_hash[..]]; - let (vault_pda_data, bump_seed_data) = Pubkey::find_program_address(seeds_data, &program_id); - - let rent_exemption_lamports = self - .client - .get_minimum_balance_for_rent_exemption( - space - .try_into() - .map_err(|e| Box::new(TransactionErr::Plain(ERRL!("unable to convert space: {:?}", e))))?, - ) - .map_err(|e| { - Box::new(TransactionErr::Plain(ERRL!( - "error get_minimum_balance_for_rent_exemption: {:?}", - e - ))) - })?; - - Ok(( - vault_pda, - vault_pda_data, - bump_seed, - bump_seed_data, - rent_exemption_lamports, - )) - } -} - -#[async_trait] -impl MarketCoinOps for SolanaCoin { - fn ticker(&self) -> &str { &self.ticker } - - fn my_address(&self) -> MmResult { Ok(self.my_address.clone()) } - - async fn get_public_key(&self) -> Result> { - Ok(self.key_pair.pubkey().to_string()) - } - - fn sign_message_hash(&self, _message: &str) -> Option<[u8; 32]> { unimplemented!() } - - fn sign_message(&self, message: &str) -> SignatureResult { solana_common::sign_message(self, message) } - - fn verify_message(&self, signature: &str, message: &str, pubkey_bs58: &str) -> VerificationResult { - solana_common::verify_message(self, signature, message, pubkey_bs58) - } - - fn my_balance(&self) -> BalanceFut { - let decimals = self.decimals as u64; - let fut = self.my_balance_impl().and_then(move |result| { - Ok(CoinBalance { - spendable: result.with_prec(decimals), - unspendable: 0.into(), - }) - }); - Box::new(fut) - } - - fn base_coin_balance(&self) -> BalanceFut { - let decimals = self.decimals as u64; - let fut = self - .my_balance_impl() - .and_then(move |result| Ok(result.with_prec(decimals))); - Box::new(fut) - } - - fn platform_ticker(&self) -> &str { self.ticker() } - - fn send_raw_tx(&self, tx: &str) -> Box + Send> { - let coin = self.clone(); - let tx = tx.to_owned(); - let fut = async_blocking(move || { - let bytes = hex::decode(tx).map_to_mm(|e| e).map_err(|e| format!("{:?}", e))?; - let tx: SolTransaction = deserialize(bytes.as_slice()) - .map_to_mm(|e| e) - .map_err(|e| format!("{:?}", e))?; - // this is blocking IO - let signature = coin.rpc().send_transaction(&tx).map_err(|e| format!("{:?}", e))?; - Ok(signature.to_string()) - }); - Box::new(fut.boxed().compat()) - } - - fn send_raw_tx_bytes(&self, tx: &[u8]) -> Box + Send> { - let coin = self.clone(); - let tx = tx.to_owned(); - let fut = async_blocking(move || { - let tx = try_s!(deserialize(tx.as_slice())); - // this is blocking IO - let signature = coin.rpc().send_transaction(&tx).map_err(|e| format!("{:?}", e))?; - Ok(signature.to_string()) - }); - Box::new(fut.boxed().compat()) - } - - #[inline(always)] - async fn sign_raw_tx(&self, _args: &SignRawTransactionRequest) -> RawTransactionResult { - MmError::err(RawTransactionError::NotImplemented { - coin: self.ticker().to_string(), - }) - } - - fn wait_for_confirmations(&self, _input: ConfirmPaymentInput) -> Box + Send> { - unimplemented!() - } - - fn wait_for_htlc_tx_spend(&self, args: WaitForHTLCTxSpendArgs<'_>) -> TransactionFut { unimplemented!() } - - fn tx_enum_from_bytes(&self, _bytes: &[u8]) -> Result> { - MmError::err(TxMarshalingErr::NotSupported( - "tx_enum_from_bytes is not supported for Solana yet.".to_string(), - )) - } - - fn current_block(&self) -> Box + Send> { - let coin = self.clone(); - let fut = async_blocking(move || coin.rpc().get_block_height().map_err(|e| format!("{:?}", e))); - Box::new(fut.boxed().compat()) - } - - fn display_priv_key(&self) -> Result { Ok(self.key_pair.secret().to_bytes()[..].to_base58()) } - - fn min_tx_amount(&self) -> BigDecimal { BigDecimal::from(0) } - - fn min_trading_vol(&self) -> MmNumber { MmNumber::from("0.00777") } - - fn is_trezor(&self) -> bool { unimplemented!() } -} - -#[async_trait] -impl SwapOps for SolanaCoin { - fn send_taker_fee(&self, _fee_addr: &[u8], dex_fee: DexFee, _uuid: &[u8], _expire_at: u64) -> TransactionFut { - unimplemented!() - } - - fn send_maker_payment(&self, maker_payment: SendPaymentArgs) -> TransactionFut { - Box::new( - self.send_hash_time_locked_payment(maker_payment) - .map(TransactionEnum::from), - ) - } - - fn send_taker_payment(&self, taker_payment: SendPaymentArgs) -> TransactionFut { - Box::new( - self.send_hash_time_locked_payment(taker_payment) - .map(TransactionEnum::from), - ) - } - - async fn send_maker_spends_taker_payment( - &self, - maker_spends_payment_args: SpendPaymentArgs<'_>, - ) -> TransactionResult { - self.spend_hash_time_locked_payment(maker_spends_payment_args) - .compat() - .await - .map(TransactionEnum::from) - } - - async fn send_taker_spends_maker_payment( - &self, - taker_spends_payment_args: SpendPaymentArgs<'_>, - ) -> TransactionResult { - self.spend_hash_time_locked_payment(taker_spends_payment_args) - .compat() - .await - .map(TransactionEnum::from) - } - - async fn send_taker_refunds_payment(&self, taker_refunds_payment_args: RefundPaymentArgs<'_>) -> TransactionResult { - self.refund_hash_time_locked_payment(taker_refunds_payment_args) - .map(TransactionEnum::from) - .compat() - .await - } - - async fn send_maker_refunds_payment(&self, maker_refunds_payment_args: RefundPaymentArgs<'_>) -> TransactionResult { - self.refund_hash_time_locked_payment(maker_refunds_payment_args) - .map(TransactionEnum::from) - .compat() - .await - } - - fn validate_fee(&self, _validate_fee_args: ValidateFeeArgs) -> ValidatePaymentFut<()> { unimplemented!() } - - async fn validate_maker_payment(&self, input: ValidatePaymentInput) -> ValidatePaymentResult<()> { - unimplemented!() - } - - async fn validate_taker_payment(&self, input: ValidatePaymentInput) -> ValidatePaymentResult<()> { - unimplemented!() - } - - fn check_if_my_payment_sent( - &self, - _if_my_payment_sent_args: CheckIfMyPaymentSentArgs, - ) -> Box, Error = String> + Send> { - unimplemented!() - } - - async fn search_for_swap_tx_spend_my( - &self, - _: SearchForSwapTxSpendInput<'_>, - ) -> Result, String> { - unimplemented!() - } - - async fn search_for_swap_tx_spend_other( - &self, - _: SearchForSwapTxSpendInput<'_>, - ) -> Result, String> { - unimplemented!() - } - - fn check_tx_signed_by_pub(&self, tx: &[u8], expected_pub: &[u8]) -> Result> { - unimplemented!(); - } - - async fn extract_secret( - &self, - secret_hash: &[u8], - spend_tx: &[u8], - watcher_reward: bool, - ) -> Result, String> { - unimplemented!() - } - - fn is_auto_refundable(&self) -> bool { false } - - async fn wait_for_htlc_refund(&self, _tx: &[u8], _locktime: u64) -> RefundResult<()> { - MmError::err(RefundError::Internal( - "wait_for_htlc_refund is not supported for this coin!".into(), - )) - } - - fn negotiate_swap_contract_addr( - &self, - _other_side_address: Option<&[u8]>, - ) -> Result, MmError> { - unimplemented!() - } - - #[inline] - fn derive_htlc_key_pair(&self, _swap_unique_data: &[u8]) -> KeyPair { todo!() } - - #[inline] - fn derive_htlc_pubkey(&self, swap_unique_data: &[u8]) -> Vec { - self.derive_htlc_key_pair(swap_unique_data).public_slice().to_vec() - } - - fn validate_other_pubkey(&self, _raw_pubkey: &[u8]) -> MmResult<(), ValidateOtherPubKeyErr> { unimplemented!() } - - async fn maker_payment_instructions( - &self, - _args: PaymentInstructionArgs<'_>, - ) -> Result>, MmError> { - unimplemented!() - } - - async fn taker_payment_instructions( - &self, - _args: PaymentInstructionArgs<'_>, - ) -> Result>, MmError> { - unimplemented!() - } - - fn validate_maker_payment_instructions( - &self, - _instructions: &[u8], - _args: PaymentInstructionArgs<'_>, - ) -> Result> { - unimplemented!() - } - - fn validate_taker_payment_instructions( - &self, - _instructions: &[u8], - _args: PaymentInstructionArgs<'_>, - ) -> Result> { - unimplemented!() - } -} - -#[async_trait] -impl TakerSwapMakerCoin for SolanaCoin { - async fn on_taker_payment_refund_start(&self, _maker_payment: &[u8]) -> RefundResult<()> { Ok(()) } - - async fn on_taker_payment_refund_success(&self, _maker_payment: &[u8]) -> RefundResult<()> { Ok(()) } -} - -#[async_trait] -impl MakerSwapTakerCoin for SolanaCoin { - async fn on_maker_payment_refund_start(&self, _taker_payment: &[u8]) -> RefundResult<()> { Ok(()) } - - async fn on_maker_payment_refund_success(&self, _taker_payment: &[u8]) -> RefundResult<()> { Ok(()) } -} - -#[async_trait] -impl WatcherOps for SolanaCoin { - fn create_maker_payment_spend_preimage( - &self, - _maker_payment_tx: &[u8], - _time_lock: u64, - _maker_pub: &[u8], - _secret_hash: &[u8], - _swap_unique_data: &[u8], - ) -> TransactionFut { - unimplemented!(); - } - - fn send_maker_payment_spend_preimage(&self, _input: SendMakerPaymentSpendPreimageInput) -> TransactionFut { - unimplemented!(); - } - - fn create_taker_payment_refund_preimage( - &self, - _taker_payment_tx: &[u8], - _time_lock: u64, - _maker_pub: &[u8], - _secret_hash: &[u8], - _swap_contract_address: &Option, - _swap_unique_data: &[u8], - ) -> TransactionFut { - unimplemented!(); - } - - fn send_taker_payment_refund_preimage(&self, _watcher_refunds_payment_args: RefundPaymentArgs) -> TransactionFut { - unimplemented!(); - } - - fn watcher_validate_taker_fee(&self, input: WatcherValidateTakerFeeInput) -> ValidatePaymentFut<()> { - unimplemented!(); - } - - fn watcher_validate_taker_payment(&self, _input: WatcherValidatePaymentInput) -> ValidatePaymentFut<()> { - unimplemented!(); - } - - fn taker_validates_payment_spend_or_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { - unimplemented!() - } - - async fn watcher_search_for_swap_tx_spend( - &self, - input: WatcherSearchForSwapTxSpendInput<'_>, - ) -> Result, String> { - unimplemented!(); - } - - async fn get_taker_watcher_reward( - &self, - other_coin: &MmCoinEnum, - coin_amount: Option, - other_coin_amount: Option, - reward_amount: Option, - wait_until: u64, - ) -> Result> { - unimplemented!(); - } - - async fn get_maker_watcher_reward( - &self, - other_coin: &MmCoinEnum, - reward_amount: Option, - wait_until: u64, - ) -> Result, MmError> { - unimplemented!(); - } -} - -#[async_trait] -impl MmCoin for SolanaCoin { - fn is_asset_chain(&self) -> bool { false } - - fn spawner(&self) -> CoinFutSpawner { CoinFutSpawner::new(&self.abortable_system) } - - fn withdraw(&self, req: WithdrawRequest) -> WithdrawFut { - Box::new(Box::pin(withdraw_impl(self.clone(), req)).compat()) - } - - fn get_raw_transaction(&self, _req: RawTransactionRequest) -> RawTransactionFut { unimplemented!() } - - fn get_tx_hex_by_hash(&self, tx_hash: Vec) -> RawTransactionFut { unimplemented!() } - - fn decimals(&self) -> u8 { self.decimals } - - fn convert_to_address(&self, _from: &str, _to_address_format: Json) -> Result { unimplemented!() } - - fn validate_address(&self, address: &str) -> ValidateAddressResult { - if address.len() != 44 { - return ValidateAddressResult { - is_valid: false, - reason: Some("Invalid address length".to_string()), - }; - } - let result = Pubkey::try_from(address); - match result { - Ok(pubkey) => { - if pubkey.is_on_curve() { - ValidateAddressResult { - is_valid: true, - reason: None, - } - } else { - ValidateAddressResult { - is_valid: false, - reason: Some("not_on_curve".to_string()), - } - } - }, - Err(err) => ValidateAddressResult { - is_valid: false, - reason: Some(format!("{:?}", err)), - }, - } - } - - fn process_history_loop(&self, _ctx: MmArc) -> Box + Send> { unimplemented!() } - - fn history_sync_status(&self) -> HistorySyncState { unimplemented!() } - - /// Get fee to be paid per 1 swap transaction - fn get_trade_fee(&self) -> Box + Send> { unimplemented!() } - - async fn get_sender_trade_fee( - &self, - _value: TradePreimageValue, - _stage: FeeApproxStage, - _include_refund_fee: bool, - ) -> TradePreimageResult { - unimplemented!() - } - - fn get_receiver_trade_fee(&self, _stage: FeeApproxStage) -> TradePreimageFut { unimplemented!() } - - async fn get_fee_to_send_taker_fee( - &self, - _dex_fee_amount: DexFee, - _stage: FeeApproxStage, - ) -> TradePreimageResult { - unimplemented!() - } - - fn required_confirmations(&self) -> u64 { 1 } - - fn requires_notarization(&self) -> bool { false } - - fn set_required_confirmations(&self, _confirmations: u64) { unimplemented!() } - - fn set_requires_notarization(&self, _requires_nota: bool) { unimplemented!() } - - fn swap_contract_address(&self) -> Option { unimplemented!() } - - fn fallback_swap_contract(&self) -> Option { unimplemented!() } - - fn mature_confirmations(&self) -> Option { None } - - fn coin_protocol_info(&self, _amount_to_receive: Option) -> Vec { Vec::new() } - - fn is_coin_protocol_supported( - &self, - _info: &Option>, - _amount_to_send: Option, - _locktime: u64, - _is_maker: bool, - ) -> bool { - true - } - - fn on_disabled(&self) -> Result<(), AbortedError> { AbortableSystem::abort_all(&self.abortable_system) } - - fn on_token_deactivated(&self, _ticker: &str) { unimplemented!() } -} diff --git a/mm2src/coins/solana/solana_common.rs b/mm2src/coins/solana/solana_common.rs deleted file mode 100644 index 3f74c1caff..0000000000 --- a/mm2src/coins/solana/solana_common.rs +++ /dev/null @@ -1,181 +0,0 @@ -use crate::solana::SolanaCommonOps; -use crate::{BalanceError, MarketCoinOps, NumConversError, SignatureError, SignatureResult, SolanaCoin, - UnexpectedDerivationMethod, VerificationError, VerificationResult, WithdrawError}; -use base58::FromBase58; -use derive_more::Display; -use futures::compat::Future01CompatExt; -use mm2_err_handle::prelude::*; -use mm2_number::bigdecimal::{BigDecimal, ToPrimitive}; -use solana_sdk::native_token::LAMPORTS_PER_SOL; -use solana_sdk::signature::{Signature, Signer}; -use std::str::FromStr; - -#[derive(Debug, Display)] -pub enum SufficientBalanceError { - #[display( - fmt = "Not enough {} to withdraw: available {}, required at least {}", - coin, - available, - required - )] - NotSufficientBalance { - coin: String, - available: BigDecimal, - required: BigDecimal, - }, - #[display(fmt = "The amount {} is too small, required at least {}", amount, threshold)] - AmountTooLow { amount: BigDecimal, threshold: BigDecimal }, - #[display(fmt = "{}", _0)] - UnexpectedDerivationMethod(UnexpectedDerivationMethod), - #[display(fmt = "Wallet storage error: {}", _0)] - WalletStorageError(String), - #[display(fmt = "Invalid response: {}", _0)] - InvalidResponse(String), - #[display(fmt = "Transport: {}", _0)] - Transport(String), - #[display(fmt = "Internal: {}", _0)] - Internal(String), -} - -impl From for SufficientBalanceError { - fn from(e: NumConversError) -> Self { SufficientBalanceError::Internal(e.to_string()) } -} - -impl From for SufficientBalanceError { - fn from(e: BalanceError) -> Self { - match e { - BalanceError::Transport(e) => SufficientBalanceError::Transport(e), - BalanceError::InvalidResponse(e) => SufficientBalanceError::InvalidResponse(e), - BalanceError::UnexpectedDerivationMethod(e) => SufficientBalanceError::UnexpectedDerivationMethod(e), - BalanceError::Internal(e) => SufficientBalanceError::Internal(e), - BalanceError::WalletStorageError(e) => SufficientBalanceError::WalletStorageError(e), - } - } -} -impl From for WithdrawError { - fn from(e: SufficientBalanceError) -> Self { - match e { - SufficientBalanceError::NotSufficientBalance { - coin, - available, - required, - } => WithdrawError::NotSufficientBalance { - coin, - available, - required, - }, - SufficientBalanceError::UnexpectedDerivationMethod(e) => WithdrawError::from(e), - SufficientBalanceError::InvalidResponse(e) | SufficientBalanceError::Transport(e) => { - WithdrawError::Transport(e) - }, - SufficientBalanceError::Internal(e) | SufficientBalanceError::WalletStorageError(e) => { - WithdrawError::InternalError(e) - }, - SufficientBalanceError::AmountTooLow { amount, threshold } => { - WithdrawError::AmountTooLow { amount, threshold } - }, - } - } -} - -pub struct PrepareTransferData { - pub to_send: BigDecimal, - pub my_balance: BigDecimal, - pub sol_required: BigDecimal, - pub lamports_to_send: u64, -} - -pub fn lamports_to_sol(lamports: u64) -> BigDecimal { BigDecimal::from(lamports) / BigDecimal::from(LAMPORTS_PER_SOL) } - -pub fn sol_to_lamports(sol: &BigDecimal) -> Result> { - let maybe_lamports = (sol * BigDecimal::from(LAMPORTS_PER_SOL)).to_u64(); - match maybe_lamports { - None => MmError::err(NumConversError("Error when converting sol to lamports".to_string())), - Some(lamports) => Ok(lamports), - } -} - -pub fn ui_amount_to_amount(ui_amount: BigDecimal, decimals: u8) -> Result> { - let maybe_amount = (ui_amount * BigDecimal::from(10_u64.pow(decimals as u32))).to_u64(); - match maybe_amount { - None => MmError::err(NumConversError("Error when converting ui amount to amount".to_string())), - Some(amount) => Ok(amount), - } -} - -pub fn amount_to_ui_amount(amount: u64, decimals: u8) -> BigDecimal { - BigDecimal::from(amount) / BigDecimal::from(10_u64.pow(decimals as u32)) -} - -pub fn sign_message(coin: &SolanaCoin, message: &str) -> SignatureResult { - let signature = coin - .key_pair - .try_sign_message(message.as_bytes()) - .map_err(|e| SignatureError::InternalError(e.to_string()))?; - Ok(signature.to_string()) -} - -pub fn verify_message( - coin: &SolanaCoin, - signature: &str, - message: &str, - pubkey_bs58: &str, -) -> VerificationResult { - let pubkey = pubkey_bs58.from_base58()?; - let signature = - Signature::from_str(signature).map_err(|e| VerificationError::SignatureDecodingError(e.to_string()))?; - let is_valid = signature.verify(&pubkey, message.as_bytes()); - Ok(is_valid) -} - -pub async fn check_balance_and_prepare_transfer( - coin: &T, - max: bool, - amount: BigDecimal, - fees: u64, -) -> Result> -where - T: SolanaCommonOps + MarketCoinOps, -{ - let base_balance = coin.base_coin_balance().compat().await?; - let sol_required = lamports_to_sol(fees); - if base_balance < sol_required { - return MmError::err(SufficientBalanceError::NotSufficientBalance { - coin: coin.platform_ticker().to_string(), - available: base_balance.clone(), - required: sol_required.clone(), - }); - } - - let my_balance = coin.my_balance().compat().await?.spendable; - let to_send = if max { my_balance.clone() } else { amount.clone() }; - let to_check = if max || coin.is_token() { - to_send.clone() - } else { - &to_send + &sol_required - }; - if to_check > my_balance { - return MmError::err(SufficientBalanceError::NotSufficientBalance { - coin: coin.ticker().to_string(), - available: my_balance, - required: to_check, - }); - } - - let lamports_to_send = if !coin.is_token() { - if max { - sol_to_lamports(&my_balance)? - sol_to_lamports(&sol_required)? - } else { - sol_to_lamports(&amount)? - } - } else { - 0_u64 - }; - - Ok(PrepareTransferData { - to_send, - my_balance, - sol_required, - lamports_to_send, - }) -} diff --git a/mm2src/coins/solana/solana_common_tests.rs b/mm2src/coins/solana/solana_common_tests.rs deleted file mode 100644 index 6bb5b833d1..0000000000 --- a/mm2src/coins/solana/solana_common_tests.rs +++ /dev/null @@ -1,96 +0,0 @@ -use super::*; -use crate::solana::spl::{SplToken, SplTokenFields}; -use crypto::privkey::{bip39_seed_from_passphrase, key_pair_from_seed}; -use ed25519_dalek_bip32::{DerivationPath, ExtendedSecretKey}; -use mm2_core::mm_ctx::MmCtxBuilder; -use solana_client::rpc_client::RpcClient; -use solana_sdk::commitment_config::{CommitmentConfig, CommitmentLevel}; -use std::str::FromStr; - -pub enum SolanaNet { - //Mainnet, - Testnet, - Devnet, -} - -pub fn solana_net_to_url(net_type: SolanaNet) -> String { - match net_type { - //SolanaNet::Mainnet => "https://api.mainnet-beta.solana.com".to_string(), - SolanaNet::Testnet => "https://api.testnet.solana.com/".to_string(), - SolanaNet::Devnet => "https://api.devnet.solana.com".to_string(), - } -} - -pub fn generate_key_pair_from_seed(seed: &str) -> SolKeypair { - let derivation_path = DerivationPath::from_str("m/44'/501'/0'").unwrap(); - let seed = bip39_seed_from_passphrase(seed).unwrap(); - - let ext = ExtendedSecretKey::from_seed(&seed.0) - .unwrap() - .derive(&derivation_path) - .unwrap(); - let pub_key = ext.public_key(); - let pair = ed25519_dalek::Keypair { - secret: ext.secret_key, - public: pub_key, - }; - - solana_sdk::signature::keypair_from_seed(pair.to_bytes().as_ref()).unwrap() -} - -pub fn generate_key_pair_from_iguana_seed(seed: String) -> SolKeypair { - let key_pair = key_pair_from_seed(seed.as_str()).unwrap(); - let secret_key = ed25519_dalek::SecretKey::from_bytes(key_pair.private().secret.as_slice()).unwrap(); - let public_key = ed25519_dalek::PublicKey::from(&secret_key); - let other_key_pair = ed25519_dalek::Keypair { - secret: secret_key, - public: public_key, - }; - solana_sdk::signature::keypair_from_seed(other_key_pair.to_bytes().as_ref()).unwrap() -} - -pub fn spl_coin_for_test( - solana_coin: SolanaCoin, - ticker: String, - decimals: u8, - token_contract_address: Pubkey, -) -> SplToken { - SplToken { - conf: Arc::new(SplTokenFields { - decimals, - ticker, - token_contract_address, - abortable_system: AbortableQueue::default(), - }), - platform_coin: solana_coin, - } -} - -pub fn solana_coin_for_test(seed: String, net_type: SolanaNet) -> (MmArc, SolanaCoin) { - let url = solana_net_to_url(net_type); - let client = RpcClient::new_with_commitment(url, CommitmentConfig { - commitment: CommitmentLevel::Finalized, - }); - let conf = json!({ - "coins":[ - {"coin":"SOL","name":"solana","protocol":{"type":"SOL"},"rpcport":80,"mm2":1} - ] - }); - let ctx = MmCtxBuilder::new().with_conf(conf).into_mm_arc(); - let (ticker, decimals) = ("SOL".to_string(), 8); - let key_pair = generate_key_pair_from_iguana_seed(seed); - let my_address = key_pair.pubkey().to_string(); - let spl_tokens_infos = Arc::new(Mutex::new(HashMap::new())); - let spawner = AbortableQueue::default(); - - let solana_coin = SolanaCoin(Arc::new(SolanaCoinImpl { - decimals, - my_address, - key_pair, - ticker, - client, - spl_tokens_infos, - abortable_system: spawner, - })); - (ctx, solana_coin) -} diff --git a/mm2src/coins/solana/solana_decode_tx_helpers.rs b/mm2src/coins/solana/solana_decode_tx_helpers.rs deleted file mode 100644 index bd22fc044e..0000000000 --- a/mm2src/coins/solana/solana_decode_tx_helpers.rs +++ /dev/null @@ -1,222 +0,0 @@ -extern crate serde_derive; - -use crate::{NumConversResult, SolanaCoin, SolanaFeeDetails, TransactionData, TransactionDetails, TransactionType}; -use mm2_number::BigDecimal; -use solana_sdk::native_token::lamports_to_sol; -use std::convert::TryFrom; - -#[derive(Debug, Serialize, Deserialize)] -pub struct SolanaConfirmedTransaction { - slot: u64, - transaction: Transaction, - meta: Meta, - #[serde(rename = "blockTime")] - block_time: u64, -} - -#[allow(dead_code)] -impl SolanaConfirmedTransaction { - pub fn extract_account_index(&self, address: String) -> usize { - // find the equivalent of index_of(needle) in rust, and return result later - let mut idx = 0_usize; - for account in self.transaction.message.account_keys.iter() { - if account.pubkey == address { - return idx; - } - idx += 1; - } - idx - } - - pub fn extract_solana_transactions(&self, solana_coin: &SolanaCoin) -> NumConversResult> { - let mut transactions = Vec::new(); - let account_idx = self.extract_account_index(solana_coin.my_address.clone()); - for instruction in self.transaction.message.instructions.iter() { - if instruction.is_solana_transfer() { - let lamports = instruction.parsed.info.lamports.unwrap_or_default(); - let amount = BigDecimal::try_from(lamports_to_sol(lamports))?; - let is_self_transfer = instruction.parsed.info.source == instruction.parsed.info.destination; - let am_i_sender = instruction.parsed.info.source == solana_coin.my_address; - let spent_by_me = if am_i_sender && !is_self_transfer { - amount.clone() - } else { - 0.into() - }; - let received_by_me = if is_self_transfer { amount.clone() } else { 0.into() }; - let my_balance_change = if am_i_sender { - BigDecimal::try_from(lamports_to_sol( - self.meta.pre_balances[account_idx] - self.meta.post_balances[account_idx], - ))? - } else { - BigDecimal::try_from(lamports_to_sol( - self.meta.post_balances[account_idx] - self.meta.pre_balances[account_idx], - ))? - }; - let fee = BigDecimal::try_from(lamports_to_sol(self.meta.fee))?; - let tx = TransactionDetails { - tx: TransactionData::new_signed(Default::default(), self.transaction.signatures[0].to_string()), - from: vec![instruction.parsed.info.source.clone()], - to: vec![instruction.parsed.info.destination.clone()], - total_amount: amount, - spent_by_me, - received_by_me, - my_balance_change, - block_height: self.slot, - timestamp: self.block_time, - fee_details: Some(SolanaFeeDetails { amount: fee }.into()), - coin: solana_coin.ticker.clone(), - internal_id: Default::default(), - kmd_rewards: None, - transaction_type: TransactionType::StandardTransfer, - memo: None, - }; - transactions.push(tx); - } - } - Ok(transactions) - } -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Meta { - err: Option, - status: Status, - fee: u64, - #[serde(rename = "preBalances")] - pre_balances: Vec, - #[serde(rename = "postBalances")] - post_balances: Vec, - #[serde(rename = "innerInstructions")] - inner_instructions: Vec>, - #[serde(rename = "logMessages")] - log_messages: Vec, - #[serde(rename = "preTokenBalances")] - pre_token_balances: Vec, - #[serde(rename = "postTokenBalances")] - post_token_balances: Vec, - rewards: Vec>, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct TokenBalance { - #[serde(rename = "accountIndex")] - account_index: u64, - mint: String, - #[serde(rename = "uiTokenAmount")] - ui_token_amount: TokenAmount, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct TokenAmount { - #[serde(rename = "uiAmount")] - ui_amount: f64, - decimals: u64, - amount: String, - #[serde(rename = "uiAmountString")] - ui_amount_string: String, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Status { - #[serde(rename = "Ok")] - ok: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Transaction { - signatures: Vec, - message: Message, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Message { - #[serde(rename = "accountKeys")] - account_keys: Vec, - #[serde(rename = "recentBlockhash")] - recent_blockhash: String, - instructions: Vec, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct AccountKey { - pubkey: String, - writable: bool, - signer: bool, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Instruction { - program: Program, - #[serde(rename = "programId")] - program_id: String, - parsed: Parsed, -} - -#[allow(dead_code)] -impl Instruction { - pub fn is_solana_transfer(&self) -> bool { - let is_system = match self.program { - Program::SplToken => return false, - Program::System => true, - }; - let is_transfer = match self.parsed.parsed_type { - Type::Transfer => true, - Type::TransferChecked => true, - Type::Unknown => false, - }; - is_system && is_transfer && self.parsed.info.lamports.is_some() - } - - // Will be used later - pub fn is_spl_transfer(&self) -> bool { - let is_spl_token = match self.program { - Program::SplToken => true, - Program::System => return false, - }; - let is_transfer = match self.parsed.parsed_type { - Type::Transfer => true, - Type::TransferChecked => true, - Type::Unknown => false, - }; - is_spl_token && is_transfer - } -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Parsed { - info: Info, - #[serde(rename = "type")] - parsed_type: Type, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Info { - destination: String, - lamports: Option, - source: String, - mint: Option, - #[serde(rename = "multisigAuthority")] - multisig_authority: Option, - signers: Option>, - #[serde(rename = "tokenAmount")] - token_amount: Option, - authority: Option, - amount: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub enum Type { - #[serde(rename = "transfer")] - Transfer, - #[serde(rename = "transferChecked")] - TransferChecked, - Unknown, -} - -#[derive(Debug, Serialize, Deserialize)] -pub enum Program { - #[serde(rename = "spl-token")] - SplToken, - #[serde(rename = "system")] - System, -} diff --git a/mm2src/coins/solana/solana_tests.rs b/mm2src/coins/solana/solana_tests.rs deleted file mode 100644 index d4126f35c0..0000000000 --- a/mm2src/coins/solana/solana_tests.rs +++ /dev/null @@ -1,436 +0,0 @@ -use base58::ToBase58; -use common::{block_on, block_on_f01, Future01CompatExt}; -use rpc::v1::types::Bytes; -use solana_client::rpc_request::TokenAccountsFilter; -use solana_sdk::{bs58, - signature::{Signature, Signer}}; -use solana_transaction_status::UiTransactionEncoding; -use std::{ops::Neg, str::FromStr}; - -use super::solana_common_tests::{generate_key_pair_from_iguana_seed, generate_key_pair_from_seed, - solana_coin_for_test, SolanaNet}; -use super::solana_decode_tx_helpers::SolanaConfirmedTransaction; -use super::*; -use crate::{MarketCoinOps, SwapTxTypeWithSecretHash}; - -#[test] -#[cfg(not(target_arch = "wasm32"))] -fn solana_keypair_from_secp() { - let solana_key_pair = generate_key_pair_from_iguana_seed("federal stay trigger hour exist success game vapor become comfort action phone bright ill target wild nasty crumble dune close rare fabric hen iron".to_string()); - assert_eq!( - "FJktmyjV9aBHEShT4hfnLpr9ELywdwVtEL1w1rSWgbVf", - solana_key_pair.pubkey().to_string() - ); - - let other_solana_keypair = generate_key_pair_from_iguana_seed("bob passphrase".to_string()); - assert_eq!( - "B7KMMHyc3eYguUMneXRznY1NWh91HoVA2muVJetstYKE", - other_solana_keypair.pubkey().to_string() - ); -} - -// Research tests -// TODO remove `ignore` attribute once the test is stable. -#[test] -#[ignore] -#[cfg(not(target_arch = "wasm32"))] -fn solana_prerequisites() { - // same test as trustwallet - { - let fin = - generate_key_pair_from_seed("hood vacant left trim hard mushroom device flavor ask better arrest again"); - let public_address = fin.pubkey().to_string(); - let priv_key = &fin.secret().to_bytes()[..].to_base58(); - assert_eq!(public_address.len(), 44); - assert_eq!(public_address, "4rmosKwMH7zeaXGbej1PFybZBUyuUNQLf8RfyzCcYvkx"); - assert_eq!(priv_key, "CZtxt17aTfDrJrzwBWdVqcmFwVVptW8EX7RRnth9tT3M"); - let client = solana_client::rpc_client::RpcClient::new("https://api.testnet.solana.com/".to_string()); - let balance = client.get_balance(&fin.pubkey()).expect("Expect to retrieve balance"); - assert_eq!(balance, 0); - } - - { - let key_pair = generate_key_pair_from_iguana_seed("passphrase not really secure".to_string()); - let public_address = key_pair.pubkey().to_string(); - assert_eq!(public_address.len(), 44); - assert_eq!(public_address, "2jTgfhf98GosnKSCXjL5YSiEa3MLrmR42yy9kZZq1i2c"); - let client = solana_client::rpc_client::RpcClient::new("https://api.testnet.solana.com/".to_string()); - let balance = client - .get_balance(&key_pair.pubkey()) - .expect("Expect to retrieve balance"); - assert_eq!(lamports_to_sol(balance), BigDecimal::from(0)); - assert_eq!(balance, 0); - - // This will fetch all the balance from all tokens - let token_accounts = client - .get_token_accounts_by_owner(&key_pair.pubkey(), TokenAccountsFilter::ProgramId(spl_token::id())) - .expect(""); - log!("{:?}", token_accounts); - let actual_token_pubkey = solana_sdk::pubkey::Pubkey::from_str(token_accounts[0].pubkey.as_str()).unwrap(); - let amount = client.get_token_account_balance(&actual_token_pubkey).unwrap(); - assert_ne!(amount.ui_amount_string.as_str(), "0"); - } -} - -#[test] -#[cfg(not(target_arch = "wasm32"))] -fn solana_coin_creation() { - let passphrase = "federal stay trigger hour exist success game vapor become comfort action phone bright ill target wild nasty crumble dune close rare fabric hen iron".to_string(); - let (_, sol_coin) = solana_coin_for_test(passphrase, SolanaNet::Testnet); - assert_eq!( - sol_coin.my_address().unwrap(), - "FJktmyjV9aBHEShT4hfnLpr9ELywdwVtEL1w1rSWgbVf" - ); -} - -#[test] -#[cfg(not(target_arch = "wasm32"))] -fn solana_my_balance() { - let passphrase = "federal stay trigger hour exist success game vapor become comfort action phone bright ill target wild nasty crumble dune close rare fabric hen iron".to_string(); - let (_, sol_coin) = solana_coin_for_test(passphrase, SolanaNet::Testnet); - let res = block_on(sol_coin.my_balance().compat()).unwrap(); - assert_ne!(res.spendable, BigDecimal::from(0)); -} - -#[test] -#[cfg(not(target_arch = "wasm32"))] -fn solana_block_height() { - let passphrase = "federal stay trigger hour exist success game vapor become comfort action phone bright ill target wild nasty crumble dune close rare fabric hen iron".to_string(); - let (_, sol_coin) = solana_coin_for_test(passphrase, SolanaNet::Testnet); - let res = block_on(sol_coin.current_block().compat()).unwrap(); - log!("block is : {}", res); - assert!(res > 0); -} - -#[test] -#[cfg(not(target_arch = "wasm32"))] -fn solana_validate_address() { - let passphrase = "federal stay trigger hour exist success game vapor become comfort action phone bright ill target wild nasty crumble dune close rare fabric hen iron".to_string(); - let (_, sol_coin) = solana_coin_for_test(passphrase, SolanaNet::Testnet); - - // invalid len - let res = sol_coin.validate_address("invalidaddressobviously"); - assert!(!res.is_valid); - let res = sol_coin.validate_address("GMtMFbuVgjDnzsBd3LLBfM4X8RyYcDGCM92tPq2PG6B2"); - assert!(res.is_valid); - - // Typo - let res = sol_coin.validate_address("Fr8fraJXAe1cFU81mF7NhHTrUzXjZAJkQE1gUQ11riH"); - assert!(!res.is_valid); - - // invalid len - let res = sol_coin.validate_address("r8fraJXAe1cFU81mF7NhHTrUzXjZAJkQE1gUQ11riHn"); - assert!(!res.is_valid); -} - -#[test] -#[cfg(not(target_arch = "wasm32"))] -fn test_sign_message() { - let passphrase = "spice describe gravity federal blast come thank unfair canal monkey style afraid".to_string(); - let (_, sol_coin) = solana_coin_for_test(passphrase, SolanaNet::Testnet); - let signature = sol_coin.sign_message("test").unwrap(); - assert_eq!( - signature, - "4dzKwEteN8nch76zPMEjPX19RsaQwGTxsbtfg2bwGTkGenLfrdm31zvn9GH5rvaJBwivp6ESXx1KYR672ngs3UfF" - ); -} - -#[test] -#[cfg(not(target_arch = "wasm32"))] -fn test_verify_message() { - let passphrase = "spice describe gravity federal blast come thank unfair canal monkey style afraid".to_string(); - let (_, sol_coin) = solana_coin_for_test(passphrase, SolanaNet::Testnet); - let is_valid = sol_coin - .verify_message( - "4dzKwEteN8nch76zPMEjPX19RsaQwGTxsbtfg2bwGTkGenLfrdm31zvn9GH5rvaJBwivp6ESXx1KYR672ngs3UfF", - "test", - "8UF6jSVE1jW8mSiGqt8Hft1rLwPjdKLaTfhkNozFwoAG", - ) - .unwrap(); - assert!(is_valid); -} - -#[test] -#[cfg(not(target_arch = "wasm32"))] -fn solana_transaction_simulations() { - let passphrase = "federal stay trigger hour exist success game vapor become comfort action phone bright ill target wild nasty crumble dune close rare fabric hen iron".to_string(); - let (_, sol_coin) = solana_coin_for_test(passphrase, SolanaNet::Devnet); - let request_amount = BigDecimal::try_from(0.0001).unwrap(); - let valid_tx_details = block_on( - sol_coin - .withdraw(WithdrawRequest { - coin: "SOL".to_string(), - from: None, - to: sol_coin.my_address.clone(), - amount: request_amount.clone(), - max: false, - fee: None, - memo: None, - ibc_source_channel: None, - }) - .compat(), - ) - .unwrap(); - let (_, fees) = block_on(sol_coin.estimate_withdraw_fees()).unwrap(); - let sol_required = lamports_to_sol(fees); - let expected_spent_by_me = &request_amount + &sol_required; - assert_eq!(valid_tx_details.spent_by_me, expected_spent_by_me); - assert_eq!(valid_tx_details.received_by_me, request_amount); - assert_eq!(valid_tx_details.total_amount, expected_spent_by_me); - assert_eq!(valid_tx_details.my_balance_change, sol_required.neg()); -} - -#[test] -#[cfg(not(target_arch = "wasm32"))] -fn solana_transaction_zero_balance() { - let passphrase = "fake passphrase".to_string(); - let (_, sol_coin) = solana_coin_for_test(passphrase, SolanaNet::Devnet); - let invalid_tx_details = block_on( - sol_coin - .withdraw(WithdrawRequest { - coin: "SOL".to_string(), - from: None, - to: sol_coin.my_address.clone(), - amount: BigDecimal::from_str("0.000001").unwrap(), - max: false, - fee: None, - memo: None, - ibc_source_channel: None, - }) - .compat(), - ); - let error = invalid_tx_details.unwrap_err(); - let (_, fees) = block_on(sol_coin.estimate_withdraw_fees()).unwrap(); - let sol_required = lamports_to_sol(fees); - match error.into_inner() { - WithdrawError::NotSufficientBalance { required, .. } => { - assert_eq!(required, sol_required); - }, - e => panic!("Unexpected err {:?}", e), - }; -} - -#[test] -#[cfg(not(target_arch = "wasm32"))] -fn solana_transaction_simulations_not_enough_for_fees() { - let passphrase = "non existent passphrase".to_string(); - let (_, sol_coin) = solana_coin_for_test(passphrase, SolanaNet::Devnet); - let invalid_tx_details = block_on( - sol_coin - .withdraw(WithdrawRequest { - coin: "SOL".to_string(), - from: None, - to: sol_coin.my_address.clone(), - amount: BigDecimal::from(1), - max: false, - fee: None, - memo: None, - ibc_source_channel: None, - }) - .compat(), - ); - let error = invalid_tx_details.unwrap_err(); - let (_, fees) = block_on(sol_coin.estimate_withdraw_fees()).unwrap(); - let sol_required = lamports_to_sol(fees); - match error.into_inner() { - WithdrawError::NotSufficientBalance { - coin: _, - available, - required, - } => { - assert_eq!(available, 0.into()); - assert_eq!(required, sol_required); - }, - e => panic!("Unexpected err {:?}", e), - }; -} - -#[test] -#[cfg(not(target_arch = "wasm32"))] -fn solana_transaction_simulations_max() { - let passphrase = "federal stay trigger hour exist success game vapor become comfort action phone bright ill target wild nasty crumble dune close rare fabric hen iron".to_string(); - let (_, sol_coin) = solana_coin_for_test(passphrase, SolanaNet::Devnet); - let valid_tx_details = block_on( - sol_coin - .withdraw(WithdrawRequest { - coin: "SOL".to_string(), - from: None, - to: sol_coin.my_address.clone(), - amount: BigDecimal::from(0), - max: true, - fee: None, - memo: None, - ibc_source_channel: None, - }) - .compat(), - ) - .unwrap(); - let balance = block_on(sol_coin.my_balance().compat()).unwrap().spendable; - let (_, fees) = block_on(sol_coin.estimate_withdraw_fees()).unwrap(); - let sol_required = lamports_to_sol(fees); - assert_eq!(valid_tx_details.my_balance_change, sol_required.clone().neg()); - assert_eq!(valid_tx_details.total_amount, balance); - assert_eq!(valid_tx_details.spent_by_me, balance); - assert_eq!(valid_tx_details.received_by_me, &balance - &sol_required); - log!("{:?}", valid_tx_details); -} - -#[test] -#[cfg(not(target_arch = "wasm32"))] -fn solana_test_transactions() { - let passphrase = "federal stay trigger hour exist success game vapor become comfort action phone bright ill target wild nasty crumble dune close rare fabric hen iron".to_string(); - let (_, sol_coin) = solana_coin_for_test(passphrase, SolanaNet::Devnet); - let valid_tx_details = block_on( - sol_coin - .withdraw(WithdrawRequest { - coin: "SOL".to_string(), - from: None, - to: sol_coin.my_address.clone(), - amount: BigDecimal::try_from(0.0001).unwrap(), - max: false, - fee: None, - memo: None, - ibc_source_channel: None, - }) - .compat(), - ) - .unwrap(); - log!("{:?}", valid_tx_details); - - let tx_str = hex::encode(&*valid_tx_details.tx.tx_hex().unwrap().0); - let res = block_on(sol_coin.send_raw_tx(&tx_str).compat()).unwrap(); - - let res2 = block_on( - sol_coin - .send_raw_tx_bytes(&valid_tx_details.tx.tx_hex().unwrap().0) - .compat(), - ) - .unwrap(); - assert_eq!(res, res2); - - //log!("{:?}", res); -} - -// This test is just a unit test for brainstorming around tx_history for base_coin. -#[test] -#[ignore] -#[cfg(not(target_arch = "wasm32"))] -fn solana_test_tx_history() { - let passphrase = "federal stay trigger hour exist success game vapor become comfort action phone bright ill target wild nasty crumble dune close rare fabric hen iron".to_string(); - let (_, sol_coin) = solana_coin_for_test(passphrase, SolanaNet::Testnet); - let res = sol_coin - .client - .get_signatures_for_address(&sol_coin.key_pair.pubkey()) - .unwrap(); - let mut history = Vec::new(); - for cur in res.iter() { - let signature = Signature::from_str(cur.signature.clone().as_str()).unwrap(); - let res = sol_coin - .client - .get_transaction(&signature, UiTransactionEncoding::JsonParsed) - .unwrap(); - log!("{}", serde_json::to_string(&res).unwrap()); - let parsed = serde_json::to_value(&res).unwrap(); - let tx_infos: SolanaConfirmedTransaction = serde_json::from_value(parsed).unwrap(); - let mut txs = tx_infos.extract_solana_transactions(&sol_coin).unwrap(); - history.append(&mut txs); - } - log!("{}", serde_json::to_string(&history).unwrap()); -} - -#[test] -fn solana_coin_send_and_refund_maker_payment() { - let passphrase = "federal stay trigger hour exist success game vapor become comfort action phone bright ill target wild nasty crumble dune close rare fabric hen iron".to_string(); - let (_, coin) = solana_coin_for_test(passphrase, SolanaNet::Devnet); - let solana_program_id = "3fystoi7pB1cnDEbRRpSjFJA4fG3W2vQQZ21jSrBc11B"; - let solana_program_id = bs58::decode(solana_program_id).into_vec().unwrap_or_else(|e| { - log!("Failed to decode program ID: {}", e); - Vec::new() - }); - - let pk_data = [1; 32]; - let time_lock = now_sec() - 3600; - let taker_pub = coin.key_pair.pubkey().to_string(); - let taker_pub = Pubkey::from_str(taker_pub.as_str()).unwrap(); - let secret = [0; 32]; - let secret_hash = sha256(&secret); - - let args = SendPaymentArgs { - time_lock_duration: 0, - time_lock, - other_pubkey: taker_pub.as_ref(), - secret_hash: secret_hash.as_slice(), - amount: "0.01".parse().unwrap(), - swap_contract_address: &Some(Bytes::from(solana_program_id.clone())), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: None, - wait_for_confirmation_until: 0, - }; - let tx = block_on_f01(coin.send_maker_payment(args)).unwrap(); - log!("swap tx {:?}", tx); - - let refund_args = RefundPaymentArgs { - payment_tx: &tx.tx_hex(), - time_lock, - other_pubkey: taker_pub.as_ref(), - tx_type_with_secret_hash: SwapTxTypeWithSecretHash::TakerOrMakerPayment { - maker_secret_hash: secret_hash.as_slice(), - }, - swap_contract_address: &Some(Bytes::from(solana_program_id)), - swap_unique_data: pk_data.as_slice(), - watcher_reward: false, - }; - let refund_tx = block_on(coin.send_maker_refunds_payment(refund_args)).unwrap(); - log!("refund tx {:?}", refund_tx); -} - -#[test] -fn solana_coin_send_and_spend_maker_payment() { - let passphrase = "federal stay trigger hour exist success game vapor become comfort action phone bright ill target wild nasty crumble dune close rare fabric hen iron".to_string(); - let (_, coin) = solana_coin_for_test(passphrase, SolanaNet::Devnet); - let solana_program_id = "3fystoi7pB1cnDEbRRpSjFJA4fG3W2vQQZ21jSrBc11B"; - let solana_program_id = bs58::decode(solana_program_id).into_vec().unwrap_or_else(|e| { - log!("Failed to decode program ID: {}", e); - Vec::new() - }); - - let pk_data = [1; 32]; - let lock_time = now_sec() - 1000; - let taker_pub = coin.key_pair.pubkey().to_string(); - let taker_pub = Pubkey::from_str(taker_pub.as_str()).unwrap(); - let secret = [0; 32]; - let secret_hash = sha256(&secret); - - let maker_payment_args = SendPaymentArgs { - time_lock_duration: 0, - time_lock: lock_time, - other_pubkey: taker_pub.as_ref(), - secret_hash: secret_hash.as_slice(), - amount: "0.01".parse().unwrap(), - swap_contract_address: &Some(Bytes::from(solana_program_id.clone())), - swap_unique_data: &[], - payment_instructions: &None, - watcher_reward: None, - wait_for_confirmation_until: 0, - }; - - let tx = block_on_f01(coin.send_maker_payment(maker_payment_args)).unwrap(); - log!("swap tx {:?}", tx); - - let maker_pub = taker_pub; - - let spends_payment_args = SpendPaymentArgs { - other_payment_tx: &tx.tx_hex(), - time_lock: lock_time, - other_pubkey: maker_pub.as_ref(), - secret: &secret, - secret_hash: secret_hash.as_slice(), - swap_contract_address: &Some(Bytes::from(solana_program_id)), - swap_unique_data: pk_data.as_slice(), - watcher_reward: false, - }; - - let spend_tx = block_on(coin.send_taker_spends_maker_payment(spends_payment_args)).unwrap(); - log!("spend tx {}", hex::encode(spend_tx.tx_hash_as_bytes().0)); -} diff --git a/mm2src/coins/solana/spl.rs b/mm2src/coins/solana/spl.rs deleted file mode 100644 index 7bc720e5e7..0000000000 --- a/mm2src/coins/solana/spl.rs +++ /dev/null @@ -1,600 +0,0 @@ -use super::{CoinBalance, HistorySyncState, MarketCoinOps, MmCoin, SwapOps, TradeFee, TransactionEnum, WatcherOps}; -use crate::coin_errors::{MyAddressError, ValidatePaymentResult}; -use crate::solana::solana_common::{ui_amount_to_amount, PrepareTransferData, SufficientBalanceError}; -use crate::solana::{solana_common, AccountError, SolanaCommonOps, SolanaFeeDetails}; -use crate::{BalanceFut, CheckIfMyPaymentSentArgs, CoinFutSpawner, ConfirmPaymentInput, DexFee, FeeApproxStage, - FoundSwapTxSpend, MakerSwapTakerCoin, MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs, - PaymentInstructions, PaymentInstructionsErr, RawTransactionError, RawTransactionFut, - RawTransactionRequest, RawTransactionResult, RefundError, RefundPaymentArgs, RefundResult, - SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignRawTransactionRequest, - SignatureResult, SolanaCoin, SpendPaymentArgs, TakerSwapMakerCoin, TradePreimageFut, TradePreimageResult, - TradePreimageValue, TransactionData, TransactionDetails, TransactionFut, TransactionResult, - TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, - ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, - ValidatePaymentInput, ValidateWatcherSpendInput, VerificationResult, WaitForHTLCTxSpendArgs, - WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, - WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult}; -use async_trait::async_trait; -use bincode::serialize; -use common::executor::{abortable_queue::AbortableQueue, AbortableSystem, AbortedError}; -use common::{async_blocking, now_sec}; -use futures::{FutureExt, TryFutureExt}; -use futures01::Future; -use keys::KeyPair; -use mm2_core::mm_ctx::MmArc; -use mm2_err_handle::prelude::*; -use mm2_number::{BigDecimal, MmNumber}; -use rpc::v1::types::Bytes as BytesJson; -use serde_json::Value as Json; -use solana_client::{rpc_client::RpcClient, rpc_request::TokenAccountsFilter}; -use solana_sdk::message::Message; -use solana_sdk::transaction::Transaction; -use solana_sdk::{pubkey::Pubkey, signature::Signer}; -use spl_associated_token_account::{create_associated_token_account, get_associated_token_address}; -use std::{convert::TryFrom, - fmt::{Debug, Formatter, Result as FmtResult}, - str::FromStr, - sync::Arc}; - -#[derive(Debug)] -pub enum SplTokenCreationError { - InvalidPubkey(String), - Internal(String), -} - -impl From for SplTokenCreationError { - fn from(e: AbortedError) -> Self { SplTokenCreationError::Internal(e.to_string()) } -} - -pub struct SplTokenFields { - pub decimals: u8, - pub ticker: String, - pub token_contract_address: Pubkey, - pub abortable_system: AbortableQueue, -} - -#[derive(Clone, Debug)] -pub struct SplTokenInfo { - pub token_contract_address: Pubkey, - pub decimals: u8, -} - -#[derive(Debug)] -pub struct SplProtocolConf { - pub platform_coin_ticker: String, - pub decimals: u8, - pub token_contract_address: String, -} - -#[derive(Clone)] -pub struct SplToken { - pub conf: Arc, - pub platform_coin: SolanaCoin, -} - -impl Debug for SplToken { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { f.write_str(&self.conf.ticker) } -} - -impl SplToken { - pub fn new( - decimals: u8, - ticker: String, - token_address: String, - platform_coin: SolanaCoin, - ) -> MmResult { - let token_contract_address = solana_sdk::pubkey::Pubkey::from_str(&token_address) - .map_err(|e| MmError::new(SplTokenCreationError::InvalidPubkey(format!("{:?}", e))))?; - - // Create an abortable system linked to the `platform_coin` so if the platform coin is disabled, - // all spawned futures related to `SplToken` will be aborted as well. - let abortable_system = platform_coin.abortable_system.create_subsystem()?; - - let conf = Arc::new(SplTokenFields { - decimals, - ticker, - token_contract_address, - abortable_system, - }); - Ok(SplToken { conf, platform_coin }) - } - - pub fn get_info(&self) -> SplTokenInfo { - SplTokenInfo { - token_contract_address: self.conf.token_contract_address, - decimals: self.decimals(), - } - } -} - -async fn withdraw_spl_token_impl(coin: SplToken, req: WithdrawRequest) -> WithdrawResult { - let (hash, fees) = coin.platform_coin.estimate_withdraw_fees().await?; - let res = coin - .check_balance_and_prepare_transfer(req.max, req.amount.clone(), fees) - .await?; - let system_destination_pubkey = solana_sdk::pubkey::Pubkey::try_from(&*req.to)?; - let contract_key = coin.get_underlying_contract_pubkey(); - let auth_key = coin.platform_coin.key_pair.pubkey(); - let funding_address = coin.get_pubkey().await?; - let dest_token_address = get_associated_token_address(&system_destination_pubkey, &contract_key); - let mut instructions = Vec::with_capacity(1); - let account_info = async_blocking({ - let coin = coin.clone(); - move || coin.rpc().get_account(&dest_token_address) - }) - .await; - if account_info.is_err() { - let instruction_creation = create_associated_token_account(&auth_key, &dest_token_address, &contract_key); - instructions.push(instruction_creation); - } - let amount = ui_amount_to_amount(req.amount, coin.conf.decimals)?; - let instruction_transfer_checked = spl_token::instruction::transfer_checked( - &spl_token::id(), - &funding_address, - &contract_key, - &dest_token_address, - &auth_key, - &[&auth_key], - amount, - coin.conf.decimals, - )?; - instructions.push(instruction_transfer_checked); - let msg = Message::new(&instructions, Some(&auth_key)); - let signers = vec![&coin.platform_coin.key_pair]; - let tx = Transaction::new(&signers, msg, hash); - let serialized_tx = serialize(&tx).map_to_mm(|e| WithdrawError::InternalError(e.to_string()))?; - let received_by_me = if req.to == coin.platform_coin.my_address { - res.to_send.clone() - } else { - 0.into() - }; - Ok(TransactionDetails { - tx: TransactionData::new_signed(serialized_tx.into(), tx.signatures[0].to_string()), - from: vec![coin.platform_coin.my_address.clone()], - to: vec![req.to], - total_amount: res.to_send.clone(), - spent_by_me: res.to_send.clone(), - my_balance_change: &received_by_me - &res.to_send, - received_by_me, - block_height: 0, - timestamp: now_sec(), - fee_details: Some( - SolanaFeeDetails { - amount: res.sol_required, - } - .into(), - ), - coin: coin.conf.ticker.clone(), - internal_id: vec![].into(), - kmd_rewards: None, - transaction_type: TransactionType::StandardTransfer, - memo: None, - }) -} - -async fn withdraw_impl(coin: SplToken, req: WithdrawRequest) -> WithdrawResult { - let validate_address_result = coin.validate_address(&req.to); - if !validate_address_result.is_valid { - return MmError::err(WithdrawError::InvalidAddress( - validate_address_result.reason.unwrap_or_else(|| "Unknown".to_string()), - )); - } - withdraw_spl_token_impl(coin, req).await -} - -#[async_trait] -impl SolanaCommonOps for SplToken { - fn rpc(&self) -> &RpcClient { &self.platform_coin.client } - - fn is_token(&self) -> bool { true } - - async fn check_balance_and_prepare_transfer( - &self, - max: bool, - amount: BigDecimal, - fees: u64, - ) -> Result> { - solana_common::check_balance_and_prepare_transfer(self, max, amount, fees).await - } -} - -impl SplToken { - fn get_underlying_contract_pubkey(&self) -> Pubkey { self.conf.token_contract_address } - - async fn get_pubkey(&self) -> Result> { - let coin = self.clone(); - let token_accounts = async_blocking(move || { - coin.rpc().get_token_accounts_by_owner( - &coin.platform_coin.key_pair.pubkey(), - TokenAccountsFilter::Mint(coin.get_underlying_contract_pubkey()), - ) - }) - .await?; - if token_accounts.is_empty() { - return MmError::err(AccountError::NotFundedError("account_not_funded".to_string())); - } - Ok(Pubkey::from_str(&token_accounts[0].pubkey)?) - } - - fn my_balance_impl(&self) -> BalanceFut { - let coin = self.clone(); - let fut = async move { - coin.platform_coin - .my_balance_spl(SplTokenInfo { - token_contract_address: coin.conf.token_contract_address, - decimals: coin.conf.decimals, - }) - .await - }; - Box::new(fut.boxed().compat()) - } -} - -#[async_trait] -impl MarketCoinOps for SplToken { - fn ticker(&self) -> &str { &self.conf.ticker } - - fn my_address(&self) -> MmResult { Ok(self.platform_coin.my_address.clone()) } - - async fn get_public_key(&self) -> Result> { unimplemented!() } - - fn sign_message_hash(&self, _message: &str) -> Option<[u8; 32]> { unimplemented!() } - - fn sign_message(&self, message: &str) -> SignatureResult { - solana_common::sign_message(&self.platform_coin, message) - } - - fn verify_message(&self, signature: &str, message: &str, pubkey_bs58: &str) -> VerificationResult { - solana_common::verify_message(&self.platform_coin, signature, message, pubkey_bs58) - } - - fn my_balance(&self) -> BalanceFut { - let fut = self.my_balance_impl().and_then(Ok); - Box::new(fut) - } - - fn base_coin_balance(&self) -> BalanceFut { self.platform_coin.base_coin_balance() } - - fn platform_ticker(&self) -> &str { self.platform_coin.ticker() } - - #[inline(always)] - fn send_raw_tx(&self, tx: &str) -> Box + Send> { - self.platform_coin.send_raw_tx(tx) - } - - #[inline(always)] - fn send_raw_tx_bytes(&self, tx: &[u8]) -> Box + Send> { - self.platform_coin.send_raw_tx_bytes(tx) - } - - #[inline(always)] - async fn sign_raw_tx(&self, _args: &SignRawTransactionRequest) -> RawTransactionResult { - MmError::err(RawTransactionError::NotImplemented { - coin: self.ticker().to_string(), - }) - } - - fn wait_for_confirmations(&self, _input: ConfirmPaymentInput) -> Box + Send> { - unimplemented!() - } - - fn wait_for_htlc_tx_spend(&self, args: WaitForHTLCTxSpendArgs<'_>) -> TransactionFut { unimplemented!() } - - fn tx_enum_from_bytes(&self, _bytes: &[u8]) -> Result> { - MmError::err(TxMarshalingErr::NotSupported( - "tx_enum_from_bytes is not supported for Spl yet.".to_string(), - )) - } - - fn current_block(&self) -> Box + Send> { self.platform_coin.current_block() } - - fn display_priv_key(&self) -> Result { self.platform_coin.display_priv_key() } - - fn min_tx_amount(&self) -> BigDecimal { BigDecimal::from(0) } - - fn min_trading_vol(&self) -> MmNumber { MmNumber::from("0.00777") } - - fn is_trezor(&self) -> bool { self.platform_coin.is_trezor() } -} - -#[async_trait] -impl SwapOps for SplToken { - fn send_taker_fee(&self, _fee_addr: &[u8], dex_fee: DexFee, _uuid: &[u8], _expire_at: u64) -> TransactionFut { - unimplemented!() - } - - fn send_maker_payment(&self, _maker_payment_args: SendPaymentArgs) -> TransactionFut { unimplemented!() } - - fn send_taker_payment(&self, _taker_payment_args: SendPaymentArgs) -> TransactionFut { unimplemented!() } - - async fn send_maker_spends_taker_payment( - &self, - _maker_spends_payment_args: SpendPaymentArgs<'_>, - ) -> TransactionResult { - unimplemented!() - } - - async fn send_taker_spends_maker_payment( - &self, - _taker_spends_payment_args: SpendPaymentArgs<'_>, - ) -> TransactionResult { - unimplemented!() - } - - async fn send_taker_refunds_payment( - &self, - _taker_refunds_payment_args: RefundPaymentArgs<'_>, - ) -> TransactionResult { - unimplemented!() - } - - async fn send_maker_refunds_payment( - &self, - _maker_refunds_payment_args: RefundPaymentArgs<'_>, - ) -> TransactionResult { - todo!() - } - - fn validate_fee(&self, _validate_fee_args: ValidateFeeArgs) -> ValidatePaymentFut<()> { unimplemented!() } - - async fn validate_maker_payment(&self, input: ValidatePaymentInput) -> ValidatePaymentResult<()> { - unimplemented!() - } - - async fn validate_taker_payment(&self, input: ValidatePaymentInput) -> ValidatePaymentResult<()> { - unimplemented!() - } - - fn check_if_my_payment_sent( - &self, - _if_my_payment_sent_args: CheckIfMyPaymentSentArgs, - ) -> Box, Error = String> + Send> { - unimplemented!() - } - - async fn search_for_swap_tx_spend_my( - &self, - _: SearchForSwapTxSpendInput<'_>, - ) -> Result, String> { - unimplemented!() - } - - async fn search_for_swap_tx_spend_other( - &self, - _: SearchForSwapTxSpendInput<'_>, - ) -> Result, String> { - unimplemented!() - } - - fn check_tx_signed_by_pub(&self, tx: &[u8], expected_pub: &[u8]) -> Result> { - unimplemented!(); - } - - async fn extract_secret( - &self, - secret_hash: &[u8], - spend_tx: &[u8], - watcher_reward: bool, - ) -> Result, String> { - unimplemented!() - } - - fn is_auto_refundable(&self) -> bool { false } - - async fn wait_for_htlc_refund(&self, _tx: &[u8], _locktime: u64) -> RefundResult<()> { - MmError::err(RefundError::Internal( - "wait_for_htlc_refund is not supported for this coin!".into(), - )) - } - - fn negotiate_swap_contract_addr( - &self, - _other_side_address: Option<&[u8]>, - ) -> Result, MmError> { - unimplemented!() - } - - #[inline] - fn derive_htlc_key_pair(&self, _swap_unique_data: &[u8]) -> KeyPair { todo!() } - - #[inline] - fn derive_htlc_pubkey(&self, swap_unique_data: &[u8]) -> Vec { - self.derive_htlc_key_pair(swap_unique_data).public_slice().to_vec() - } - - fn validate_other_pubkey(&self, _raw_pubkey: &[u8]) -> MmResult<(), ValidateOtherPubKeyErr> { unimplemented!() } - - async fn maker_payment_instructions( - &self, - _args: PaymentInstructionArgs<'_>, - ) -> Result>, MmError> { - unimplemented!() - } - - async fn taker_payment_instructions( - &self, - _args: PaymentInstructionArgs<'_>, - ) -> Result>, MmError> { - unimplemented!() - } - - fn validate_maker_payment_instructions( - &self, - _instructions: &[u8], - _args: PaymentInstructionArgs<'_>, - ) -> Result> { - unimplemented!() - } - - fn validate_taker_payment_instructions( - &self, - _instructions: &[u8], - _args: PaymentInstructionArgs<'_>, - ) -> Result> { - unimplemented!() - } -} - -#[async_trait] -impl TakerSwapMakerCoin for SplToken { - async fn on_taker_payment_refund_start(&self, _maker_payment: &[u8]) -> RefundResult<()> { Ok(()) } - - async fn on_taker_payment_refund_success(&self, _maker_payment: &[u8]) -> RefundResult<()> { Ok(()) } -} - -#[async_trait] -impl MakerSwapTakerCoin for SplToken { - async fn on_maker_payment_refund_start(&self, _taker_payment: &[u8]) -> RefundResult<()> { Ok(()) } - - async fn on_maker_payment_refund_success(&self, _taker_payment: &[u8]) -> RefundResult<()> { Ok(()) } -} - -#[async_trait] -impl WatcherOps for SplToken { - fn send_maker_payment_spend_preimage(&self, _input: SendMakerPaymentSpendPreimageInput) -> TransactionFut { - unimplemented!(); - } - - fn create_taker_payment_refund_preimage( - &self, - _taker_payment_tx: &[u8], - _time_lock: u64, - _maker_pub: &[u8], - _secret_hash: &[u8], - _swap_contract_address: &Option, - _swap_unique_data: &[u8], - ) -> TransactionFut { - unimplemented!(); - } - - fn create_maker_payment_spend_preimage( - &self, - _maker_payment_tx: &[u8], - _time_lock: u64, - _maker_pub: &[u8], - _secret_hash: &[u8], - _swap_unique_data: &[u8], - ) -> TransactionFut { - unimplemented!(); - } - - fn send_taker_payment_refund_preimage(&self, _watcher_refunds_payment_args: RefundPaymentArgs) -> TransactionFut { - unimplemented!(); - } - - fn watcher_validate_taker_fee(&self, _input: WatcherValidateTakerFeeInput) -> ValidatePaymentFut<()> { - unimplemented!(); - } - - fn watcher_validate_taker_payment(&self, _input: WatcherValidatePaymentInput) -> ValidatePaymentFut<()> { - unimplemented!(); - } - - fn taker_validates_payment_spend_or_refund(&self, _input: ValidateWatcherSpendInput) -> ValidatePaymentFut<()> { - unimplemented!() - } - - async fn watcher_search_for_swap_tx_spend( - &self, - input: WatcherSearchForSwapTxSpendInput<'_>, - ) -> Result, String> { - unimplemented!(); - } - - async fn get_taker_watcher_reward( - &self, - other_coin: &MmCoinEnum, - coin_amount: Option, - other_coin_amount: Option, - reward_amount: Option, - wait_until: u64, - ) -> Result> { - unimplemented!(); - } - - async fn get_maker_watcher_reward( - &self, - other_coin: &MmCoinEnum, - reward_amount: Option, - wait_until: u64, - ) -> Result, MmError> { - unimplemented!(); - } -} - -#[async_trait] -impl MmCoin for SplToken { - fn is_asset_chain(&self) -> bool { false } - - fn spawner(&self) -> CoinFutSpawner { CoinFutSpawner::new(&self.conf.abortable_system) } - - fn withdraw(&self, req: WithdrawRequest) -> WithdrawFut { - Box::new(Box::pin(withdraw_impl(self.clone(), req)).compat()) - } - - fn get_raw_transaction(&self, _req: RawTransactionRequest) -> RawTransactionFut { unimplemented!() } - - fn get_tx_hex_by_hash(&self, tx_hash: Vec) -> RawTransactionFut { unimplemented!() } - - fn decimals(&self) -> u8 { self.conf.decimals } - - fn convert_to_address(&self, _from: &str, _to_address_format: Json) -> Result { unimplemented!() } - - fn validate_address(&self, address: &str) -> ValidateAddressResult { self.platform_coin.validate_address(address) } - - fn process_history_loop(&self, _ctx: MmArc) -> Box + Send> { unimplemented!() } - - fn history_sync_status(&self) -> HistorySyncState { unimplemented!() } - - /// Get fee to be paid per 1 swap transaction - fn get_trade_fee(&self) -> Box + Send> { unimplemented!() } - - async fn get_sender_trade_fee( - &self, - _value: TradePreimageValue, - _stage: FeeApproxStage, - _include_refund_fee: bool, - ) -> TradePreimageResult { - unimplemented!() - } - - fn get_receiver_trade_fee(&self, _stage: FeeApproxStage) -> TradePreimageFut { unimplemented!() } - - async fn get_fee_to_send_taker_fee( - &self, - _dex_fee_amount: DexFee, - _stage: FeeApproxStage, - ) -> TradePreimageResult { - unimplemented!() - } - - fn required_confirmations(&self) -> u64 { 1 } - - fn requires_notarization(&self) -> bool { false } - - fn set_required_confirmations(&self, _confirmations: u64) { unimplemented!() } - - fn set_requires_notarization(&self, _requires_nota: bool) { unimplemented!() } - - fn swap_contract_address(&self) -> Option { unimplemented!() } - - fn fallback_swap_contract(&self) -> Option { unimplemented!() } - - fn mature_confirmations(&self) -> Option { Some(1) } - - fn coin_protocol_info(&self, _amount_to_receive: Option) -> Vec { Vec::new() } - - fn is_coin_protocol_supported( - &self, - _info: &Option>, - _amount_to_send: Option, - _locktime: u64, - _is_maker: bool, - ) -> bool { - true - } - - fn on_disabled(&self) -> Result<(), AbortedError> { self.conf.abortable_system.abort_all() } - - fn on_token_deactivated(&self, _ticker: &str) {} -} diff --git a/mm2src/coins/solana/spl_tests.rs b/mm2src/coins/solana/spl_tests.rs deleted file mode 100644 index 10943e6e33..0000000000 --- a/mm2src/coins/solana/spl_tests.rs +++ /dev/null @@ -1,138 +0,0 @@ -use super::*; -use crate::{solana::solana_common_tests::solana_coin_for_test, - solana::solana_common_tests::{spl_coin_for_test, SolanaNet}}; -use common::{block_on, Future01CompatExt}; -use std::ops::Neg; -use std::str::FromStr; - -#[test] -#[cfg(not(target_arch = "wasm32"))] -fn spl_coin_creation() { - let passphrase = "federal stay trigger hour exist success game vapor become comfort action phone bright ill target wild nasty crumble dune close rare fabric hen iron".to_string(); - let (_, sol_coin) = solana_coin_for_test(passphrase, SolanaNet::Testnet); - let sol_spl_usdc_coin = spl_coin_for_test( - sol_coin, - "USDC".to_string(), - 6, - solana_sdk::pubkey::Pubkey::from_str("CpMah17kQEL2wqyMKt3mZBdTnZbkbfx4nqmQMFDP5vwp").unwrap(), - ); - - log!("address: {}", sol_spl_usdc_coin.my_address().unwrap()); - assert_eq!( - sol_spl_usdc_coin.my_address().unwrap(), - "FJktmyjV9aBHEShT4hfnLpr9ELywdwVtEL1w1rSWgbVf" - ); -} - -#[test] -#[cfg(not(target_arch = "wasm32"))] -fn test_sign_message() { - let passphrase = "spice describe gravity federal blast come thank unfair canal monkey style afraid".to_string(); - let (_, sol_coin) = solana_coin_for_test(passphrase, SolanaNet::Testnet); - let sol_spl_usdc_coin = spl_coin_for_test( - sol_coin, - "USDC".to_string(), - 6, - solana_sdk::pubkey::Pubkey::from_str("CpMah17kQEL2wqyMKt3mZBdTnZbkbfx4nqmQMFDP5vwp").unwrap(), - ); - let signature = sol_spl_usdc_coin.sign_message("test").unwrap(); - assert_eq!( - signature, - "4dzKwEteN8nch76zPMEjPX19RsaQwGTxsbtfg2bwGTkGenLfrdm31zvn9GH5rvaJBwivp6ESXx1KYR672ngs3UfF" - ); -} - -#[test] -#[cfg(not(target_arch = "wasm32"))] -fn test_verify_message() { - let passphrase = "spice describe gravity federal blast come thank unfair canal monkey style afraid".to_string(); - let (_, sol_coin) = solana_coin_for_test(passphrase, SolanaNet::Testnet); - let sol_spl_usdc_coin = spl_coin_for_test( - sol_coin, - "USDC".to_string(), - 6, - solana_sdk::pubkey::Pubkey::from_str("CpMah17kQEL2wqyMKt3mZBdTnZbkbfx4nqmQMFDP5vwp").unwrap(), - ); - let is_valid = sol_spl_usdc_coin - .verify_message( - "4dzKwEteN8nch76zPMEjPX19RsaQwGTxsbtfg2bwGTkGenLfrdm31zvn9GH5rvaJBwivp6ESXx1KYR672ngs3UfF", - "test", - "8UF6jSVE1jW8mSiGqt8Hft1rLwPjdKLaTfhkNozFwoAG", - ) - .unwrap(); - assert!(is_valid); -} - -#[test] -#[cfg(not(target_arch = "wasm32"))] -fn spl_my_balance() { - let passphrase = "federal stay trigger hour exist success game vapor become comfort action phone bright ill target wild nasty crumble dune close rare fabric hen iron".to_string(); - let (_, sol_coin) = solana_coin_for_test(passphrase, SolanaNet::Testnet); - let sol_spl_usdc_coin = spl_coin_for_test( - sol_coin.clone(), - "USDC".to_string(), - 6, - solana_sdk::pubkey::Pubkey::from_str("CpMah17kQEL2wqyMKt3mZBdTnZbkbfx4nqmQMFDP5vwp").unwrap(), - ); - - let res = block_on(sol_spl_usdc_coin.my_balance().compat()).unwrap(); - assert_ne!(res.spendable, BigDecimal::from(0)); - assert!(res.spendable < BigDecimal::from(10)); - - let sol_spl_wsol_coin = spl_coin_for_test( - sol_coin, - "WSOL".to_string(), - 8, - solana_sdk::pubkey::Pubkey::from_str("So11111111111111111111111111111111111111112").unwrap(), - ); - let res = block_on(sol_spl_wsol_coin.my_balance().compat()).unwrap(); - assert_eq!(res.spendable, BigDecimal::from(0)); -} - -// Stop ignoring when Solana is released -#[test] -#[ignore] -#[cfg(not(target_arch = "wasm32"))] -fn test_spl_transactions() { - let passphrase = "federal stay trigger hour exist success game vapor become comfort action phone bright ill target wild nasty crumble dune close rare fabric hen iron".to_string(); - let (_, sol_coin) = solana_coin_for_test(passphrase, SolanaNet::Testnet); - let usdc_sol_coin = spl_coin_for_test( - sol_coin, - "USDC".to_string(), - 6, - solana_sdk::pubkey::Pubkey::from_str("CpMah17kQEL2wqyMKt3mZBdTnZbkbfx4nqmQMFDP5vwp").unwrap(), - ); - let withdraw_amount = BigDecimal::from_str("0.0001").unwrap(); - let valid_tx_details = block_on( - usdc_sol_coin - .withdraw(WithdrawRequest { - coin: "USDC".to_string(), - from: None, - to: "AYJmtzc9D4KU6xsDzhKShFyYKUNXY622j9QoQEo4LfpX".to_string(), - amount: withdraw_amount.clone(), - max: false, - fee: None, - memo: None, - ibc_source_channel: None, - }) - .compat(), - ) - .unwrap(); - log!("{:?}", valid_tx_details); - assert_eq!(valid_tx_details.total_amount, withdraw_amount); - assert_eq!(valid_tx_details.my_balance_change, withdraw_amount.neg()); - assert_eq!(valid_tx_details.coin, "USDC".to_string()); - assert_ne!(valid_tx_details.timestamp, 0); - - let tx_str = hex::encode(&*valid_tx_details.tx.tx_hex().unwrap().0); - let res = block_on(usdc_sol_coin.send_raw_tx(&tx_str).compat()).unwrap(); - log!("{:?}", res); - - let res2 = block_on( - usdc_sol_coin - .send_raw_tx_bytes(&valid_tx_details.tx.tx_hex().unwrap().0) - .compat(), - ) - .unwrap(); - assert_eq!(res, res2); -} diff --git a/mm2src/coins_activation/Cargo.toml b/mm2src/coins_activation/Cargo.toml index aef4b9b557..be951c67d4 100644 --- a/mm2src/coins_activation/Cargo.toml +++ b/mm2src/coins_activation/Cargo.toml @@ -8,7 +8,6 @@ doctest = false [features] enable-sia = [] -enable-solana = [] default = [] for-tests = [] diff --git a/mm2src/coins_activation/src/lib.rs b/mm2src/coins_activation/src/lib.rs index 8a779b80f4..8020f91f27 100644 --- a/mm2src/coins_activation/src/lib.rs +++ b/mm2src/coins_activation/src/lib.rs @@ -10,20 +10,6 @@ mod platform_coin_with_tokens; mod prelude; #[cfg(feature = "enable-sia")] mod sia_coin_activation; mod slp_token_activation; -#[cfg(all( - feature = "enable-solana", - not(target_os = "ios"), - not(target_os = "android"), - not(target_arch = "wasm32") -))] -mod solana_with_tokens_activation; -#[cfg(all( - feature = "enable-solana", - not(target_os = "ios"), - not(target_os = "android"), - not(target_arch = "wasm32") -))] -mod spl_token_activation; mod standalone_coin; mod tendermint_token_activation; mod tendermint_with_assets_activation; diff --git a/mm2src/coins_activation/src/solana_with_tokens_activation.rs b/mm2src/coins_activation/src/solana_with_tokens_activation.rs deleted file mode 100644 index f411995779..0000000000 --- a/mm2src/coins_activation/src/solana_with_tokens_activation.rs +++ /dev/null @@ -1,327 +0,0 @@ -use crate::context::CoinsActivationContext; -use crate::platform_coin_with_tokens::{EnablePlatformCoinWithTokensError, GetPlatformBalance, - InitPlatformCoinWithTokensAwaitingStatus, - InitPlatformCoinWithTokensInProgressStatus, InitPlatformCoinWithTokensTask, - InitPlatformCoinWithTokensTaskManagerShared, - InitPlatformCoinWithTokensUserAction, InitTokensAsMmCoinsError, - PlatformCoinWithTokensActivationOps, RegisterTokenInfo, TokenActivationParams, - TokenActivationRequest, TokenAsMmCoinInitializer, TokenInitializer, TokenOf}; -use crate::prelude::*; -use crate::prelude::{CoinAddressInfo, TokenBalances, TryFromCoinProtocol, TxHistory}; -use crate::spl_token_activation::SplActivationRequest; -use async_trait::async_trait; -use coins::coin_errors::MyAddressError; -use coins::my_tx_history_v2::TxHistoryStorage; -use coins::solana::solana_coin_with_policy; -use coins::solana::spl::{SplProtocolConf, SplTokenCreationError}; -use coins::{BalanceError, CoinBalance, CoinProtocol, DerivationMethodResponse, MarketCoinOps, MmCoinEnum, - PrivKeyBuildPolicy, SolanaActivationParams, SolanaCoin, SplToken}; -use common::Future01CompatExt; -use common::{drop_mutability, true_f}; -use crypto::CryptoCtxError; -use futures::future::try_join_all; -use mm2_core::mm_ctx::MmArc; -use mm2_err_handle::prelude::*; -use mm2_event_stream::EventStreamConfiguration; -use mm2_number::BigDecimal; -use rpc_task::RpcTaskHandleShared; -use serde_derive::{Deserialize, Serialize}; -use serde_json::Value as Json; -use std::collections::HashMap; - -pub struct SplTokenInitializer { - platform_coin: SolanaCoin, -} - -impl TokenOf for SplToken { - type PlatformCoin = SolanaCoin; -} - -pub struct SplTokenInitializerErr { - ticker: String, - inner: SplTokenCreationError, -} - -#[async_trait] -impl TokenInitializer for SplTokenInitializer { - type Token = SplToken; - type TokenActivationRequest = SplActivationRequest; - type TokenProtocol = SplProtocolConf; - type InitTokensError = SplTokenInitializerErr; - - fn tokens_requests_from_platform_request( - platform_params: &SolanaWithTokensActivationRequest, - ) -> Vec> { - platform_params.spl_tokens_requests.clone() - } - - async fn enable_tokens( - &self, - activation_params: Vec>, - ) -> Result, MmError> { - let tokens = activation_params - .into_iter() - .map(|param| { - let ticker = param.ticker.clone(); - SplToken::new( - param.protocol.decimals, - param.ticker, - param.protocol.token_contract_address, - self.platform_coin.clone(), - ) - .mm_err(|inner| SplTokenInitializerErr { ticker, inner }) - }) - .collect::, _>>()?; - Ok(tokens) - } - - fn platform_coin(&self) -> &SolanaCoin { &self.platform_coin } -} - -impl RegisterTokenInfo for SolanaCoin { - fn register_token_info(&self, token: &SplToken) { self.add_spl_token_info(token.ticker().into(), token.get_info()) } -} - -#[derive(Clone, Debug, Deserialize)] -pub struct SolanaWithTokensActivationRequest { - #[serde(flatten)] - platform_request: SolanaActivationParams, - spl_tokens_requests: Vec>, - #[serde(default = "true_f")] - pub get_balances: bool, -} - -impl TxHistory for SolanaWithTokensActivationRequest { - fn tx_history(&self) -> bool { false } -} - -impl ActivationRequestInfo for SolanaWithTokensActivationRequest { - fn is_hw_policy(&self) -> bool { false } // TODO: fix when device policy is added -} - -#[derive(Debug, Serialize, Clone)] -pub struct SolanaWithTokensActivationResult { - current_block: u64, - solana_addresses_infos: HashMap>, - spl_addresses_infos: HashMap>, -} - -impl GetPlatformBalance for SolanaWithTokensActivationResult { - fn get_platform_balance(&self) -> Option { - self.solana_addresses_infos - .iter() - .fold(Some(BigDecimal::from(0)), |total, (_, addr_info)| { - total.and_then(|t| addr_info.balances.as_ref().map(|b| t + b.get_total())) - }) - } -} - -impl CurrentBlock for SolanaWithTokensActivationResult { - fn current_block(&self) -> u64 { self.current_block } -} - -#[derive(Debug, Clone)] -pub enum SolanaWithTokensActivationError { - PlatformCoinCreationError { ticker: String, error: String }, - UnableToRetrieveMyAddress(String), - GetBalanceError(BalanceError), - Transport(String), - Internal(String), -} - -impl From for SolanaWithTokensActivationError { - fn from(err: MyAddressError) -> Self { Self::UnableToRetrieveMyAddress(err.to_string()) } -} - -impl From for EnablePlatformCoinWithTokensError { - fn from(e: SolanaWithTokensActivationError) -> Self { - match e { - SolanaWithTokensActivationError::PlatformCoinCreationError { ticker, error } => { - EnablePlatformCoinWithTokensError::PlatformCoinCreationError { ticker, error } - }, - SolanaWithTokensActivationError::UnableToRetrieveMyAddress(e) => { - EnablePlatformCoinWithTokensError::Internal(e) - }, - SolanaWithTokensActivationError::GetBalanceError(e) => { - EnablePlatformCoinWithTokensError::Internal(format!("{:?}", e)) - }, - SolanaWithTokensActivationError::Transport(e) => EnablePlatformCoinWithTokensError::Transport(e), - SolanaWithTokensActivationError::Internal(e) => EnablePlatformCoinWithTokensError::Internal(e), - } - } -} - -impl From for SolanaWithTokensActivationError { - fn from(e: BalanceError) -> Self { SolanaWithTokensActivationError::GetBalanceError(e) } -} - -impl From for SolanaWithTokensActivationError { - fn from(e: CryptoCtxError) -> Self { SolanaWithTokensActivationError::Internal(e.to_string()) } -} - -pub struct SolanaProtocolInfo {} - -impl TryFromCoinProtocol for SolanaProtocolInfo { - fn try_from_coin_protocol(proto: CoinProtocol) -> Result> - where - Self: Sized, - { - match proto { - CoinProtocol::SOLANA {} => Ok(SolanaProtocolInfo {}), - protocol => MmError::err(protocol), - } - } -} - -impl From for InitTokensAsMmCoinsError { - fn from(err: SplTokenInitializerErr) -> Self { - match err.inner { - SplTokenCreationError::InvalidPubkey(error) => InitTokensAsMmCoinsError::TokenProtocolParseError { - ticker: err.ticker, - error, - }, - SplTokenCreationError::Internal(internal) => InitTokensAsMmCoinsError::Internal(internal), - } - } -} - -#[async_trait] -impl PlatformCoinWithTokensActivationOps for SolanaCoin { - type ActivationRequest = SolanaWithTokensActivationRequest; - type PlatformProtocolInfo = SolanaProtocolInfo; - type ActivationResult = SolanaWithTokensActivationResult; - type ActivationError = SolanaWithTokensActivationError; - - type InProgressStatus = InitPlatformCoinWithTokensInProgressStatus; - type AwaitingStatus = InitPlatformCoinWithTokensAwaitingStatus; - type UserAction = InitPlatformCoinWithTokensUserAction; - - async fn enable_platform_coin( - ctx: MmArc, - ticker: String, - platform_conf: &Json, - activation_request: Self::ActivationRequest, - _protocol_conf: Self::PlatformProtocolInfo, - ) -> Result> { - let priv_key_policy = PrivKeyBuildPolicy::detect_priv_key_policy(&ctx)?; - solana_coin_with_policy( - &ctx, - &ticker, - platform_conf, - activation_request.platform_request, - priv_key_policy, - ) - .await - .map_to_mm(|error| SolanaWithTokensActivationError::PlatformCoinCreationError { ticker, error }) - } - - async fn enable_global_nft( - &self, - _activation_request: &Self::ActivationRequest, - ) -> Result, MmError> { - Ok(None) - } - - fn try_from_mm_coin(coin: MmCoinEnum) -> Option - where - Self: Sized, - { - match coin { - MmCoinEnum::SolanaCoin(coin) => Some(coin), - _ => None, - } - } - - fn token_initializers( - &self, - ) -> Vec>> { - vec![Box::new(SplTokenInitializer { - platform_coin: self.clone(), - })] - } - - async fn get_activation_result( - &self, - _task_handle: Option>>, - activation_request: &Self::ActivationRequest, - _nft_global: &Option, - ) -> Result> { - let current_block = self - .current_block() - .compat() - .await - .map_to_mm(Self::ActivationError::Internal)?; - - let my_address = self.my_address()?; - - let mut solana_address_info = CoinAddressInfo { - derivation_method: DerivationMethodResponse::Iguana, - pubkey: my_address.clone(), - balances: None, - tickers: None, - }; - - let mut spl_address_info = CoinAddressInfo { - derivation_method: DerivationMethodResponse::Iguana, - pubkey: my_address.clone(), - balances: None, - tickers: None, - }; - - if !activation_request.get_balances { - drop_mutability!(solana_address_info); - let tickers = self.get_spl_tokens_infos().into_keys().collect(); - spl_address_info.tickers = Some(tickers); - drop_mutability!(spl_address_info); - - return Ok(SolanaWithTokensActivationResult { - current_block, - solana_addresses_infos: HashMap::from([(my_address.clone(), solana_address_info)]), - spl_addresses_infos: HashMap::from([(my_address, spl_address_info)]), - }); - } - - let solana_balance = self - .my_balance() - .compat() - .await - .map_err(|e| Self::ActivationError::GetBalanceError(e.into_inner()))?; - solana_address_info.balances = Some(solana_balance); - drop_mutability!(solana_address_info); - - let (token_tickers, requests): (Vec<_>, Vec<_>) = self - .get_spl_tokens_infos() - .into_iter() - .map(|(ticker, info)| (ticker, self.my_balance_spl(info))) - .unzip(); - spl_address_info.balances = Some(token_tickers.into_iter().zip(try_join_all(requests).await?).collect()); - drop_mutability!(spl_address_info); - - Ok(SolanaWithTokensActivationResult { - current_block, - solana_addresses_infos: HashMap::from([(my_address.clone(), solana_address_info)]), - spl_addresses_infos: HashMap::from([(my_address, spl_address_info)]), - }) - } - - fn start_history_background_fetching( - &self, - _ctx: MmArc, - _storage: impl TxHistoryStorage + Send + 'static, - _initial_balance: Option, - ) { - } - - async fn handle_balance_streaming( - &self, - _config: &EventStreamConfiguration, - ) -> Result<(), MmError> { - Ok(()) - } - - fn rpc_task_manager( - _activation_ctx: &CoinsActivationContext, - ) -> &InitPlatformCoinWithTokensTaskManagerShared { - unimplemented!() - } -} diff --git a/mm2src/coins_activation/src/spl_token_activation.rs b/mm2src/coins_activation/src/spl_token_activation.rs deleted file mode 100644 index 2bf40b6a89..0000000000 --- a/mm2src/coins_activation/src/spl_token_activation.rs +++ /dev/null @@ -1,117 +0,0 @@ -use crate::prelude::{TryFromCoinProtocol, TryPlatformCoinFromMmCoinEnum}; -use crate::token::{EnableTokenError, TokenActivationOps, TokenProtocolParams}; -use async_trait::async_trait; -use coins::coin_errors::MyAddressError; -use coins::solana::spl::{SplProtocolConf, SplTokenCreationError}; -use coins::{BalanceError, CoinBalance, CoinProtocol, MarketCoinOps, MmCoinEnum, SolanaCoin, SplToken}; -use common::Future01CompatExt; -use mm2_err_handle::prelude::*; -use serde_derive::{Deserialize, Serialize}; -use std::collections::HashMap; - -impl TryPlatformCoinFromMmCoinEnum for SolanaCoin { - fn try_from_mm_coin(coin: MmCoinEnum) -> Option - where - Self: Sized, - { - match coin { - MmCoinEnum::SolanaCoin(coin) => Some(coin), - _ => None, - } - } -} - -#[derive(Clone, Debug, Deserialize)] -pub struct SplActivationRequest {} - -impl TryFromCoinProtocol for SplProtocolConf { - fn try_from_coin_protocol(proto: CoinProtocol) -> Result> - where - Self: Sized, - { - match proto { - CoinProtocol::SPLTOKEN { - platform, - token_contract_address, - decimals, - } => Ok(SplProtocolConf { - platform_coin_ticker: platform, - decimals, - token_contract_address, - }), - proto => MmError::err(proto), - } - } -} - -impl TokenProtocolParams for SplProtocolConf { - fn platform_coin_ticker(&self) -> &str { &self.platform_coin_ticker } -} - -#[derive(Debug, Serialize)] -pub struct SplInitResult { - balances: HashMap, - token_contract_address: String, - platform_coin: String, -} - -#[derive(Debug)] -#[allow(clippy::large_enum_variant)] -pub enum SplInitError { - GetBalanceError(BalanceError), - TokenCreationFailed(SplTokenCreationError), - MyAddressError(String), -} - -impl From for SplInitError { - fn from(err: MyAddressError) -> Self { Self::MyAddressError(err.to_string()) } -} - -impl From for SplInitError { - fn from(e: SplTokenCreationError) -> Self { SplInitError::TokenCreationFailed(e) } -} - -impl From for EnableTokenError { - fn from(err: SplInitError) -> Self { - match err { - SplInitError::GetBalanceError(rpc_err) => rpc_err.into(), - SplInitError::TokenCreationFailed(e) => EnableTokenError::Internal(format! {"{:?}", e}), - SplInitError::MyAddressError(e) => EnableTokenError::Internal(e), - } - } -} - -#[async_trait] -impl TokenActivationOps for SplToken { - type ActivationParams = SplActivationRequest; - type ProtocolInfo = SplProtocolConf; - type ActivationResult = SplInitResult; - type ActivationError = SplInitError; - - async fn enable_token( - ticker: String, - platform_coin: Self::PlatformCoin, - _activation_params: Self::ActivationParams, - protocol_conf: Self::ProtocolInfo, - ) -> Result<(Self, Self::ActivationResult), MmError> { - let token = Self::new( - protocol_conf.decimals, - ticker, - protocol_conf.token_contract_address, - platform_coin, - )?; - let balance = token - .my_balance() - .compat() - .await - .map_err(|e| SplInitError::GetBalanceError(e.into_inner()))?; - let my_address = token.my_address()?; - let balances = HashMap::from([(my_address, balance)]); - let init_result = SplInitResult { - balances, - token_contract_address: token.conf.token_contract_address.to_string(), - platform_coin: token.platform_coin.ticker().to_owned(), - }; - Ok((token, init_result)) - } -} diff --git a/mm2src/mm2_main/Cargo.toml b/mm2src/mm2_main/Cargo.toml index 07091e2631..5458c853be 100644 --- a/mm2src/mm2_main/Cargo.toml +++ b/mm2src/mm2_main/Cargo.toml @@ -18,8 +18,6 @@ native = [] # Deprecated track-ctx-pointer = ["common/track-ctx-pointer"] zhtlc-native-tests = ["coins/zhtlc-native-tests"] run-docker-tests = ["coins/run-docker-tests"] -# TODO -enable-solana = ["coins/enable-solana", "coins_activation/enable-solana"] default = [] trezor-udp = ["crypto/trezor-udp"] # use for tests to connect to trezor emulator over udp run-device-tests = [] diff --git a/mm2src/mm2_main/src/lp_ordermatch.rs b/mm2src/mm2_main/src/lp_ordermatch.rs index dc9b85082b..df1a35d946 100644 --- a/mm2src/mm2_main/src/lp_ordermatch.rs +++ b/mm2src/mm2_main/src/lp_ordermatch.rs @@ -5808,7 +5808,7 @@ pub enum OrderbookAddress { #[derive(Debug, Display)] enum OrderbookAddrErr { AddrFromPubkeyError(String), - #[cfg(any(all(feature = "enable-solana", not(target_arch = "wasm32")), feature = "enable-sia"))] + #[cfg(feature = "enable-sia")] CoinIsNotSupported(String), DeserializationError(json::Error), InvalidPlatformCoinProtocol(String), @@ -5882,10 +5882,6 @@ fn orderbook_address( ))), } }, - #[cfg(all(feature = "enable-solana", not(target_arch = "wasm32")))] - CoinProtocol::SOLANA | CoinProtocol::SPLTOKEN { .. } => { - MmError::err(OrderbookAddrErr::CoinIsNotSupported(coin.to_owned())) - }, CoinProtocol::ZHTLC { .. } => Ok(OrderbookAddress::Shielded), #[cfg(not(target_arch = "wasm32"))] // Todo: Shielded address is used for lightning for now, the lightning node public key can be used for the orderbook entry pubkey diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index c7665da02e..295e847132 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -1665,12 +1665,8 @@ pub fn detect_secret_hash_algo(maker_coin: &MmCoinEnum, taker_coin: &MmCoinEnum) (MmCoinEnum::Tendermint(_) | MmCoinEnum::TendermintToken(_) | MmCoinEnum::LightningCoin(_), _) => { SecretHashAlgo::SHA256 }, - #[cfg(all(feature = "enable-solana", not(target_arch = "wasm32")))] - (MmCoinEnum::SolanaCoin(_), _) => SecretHashAlgo::SHA256, // If taker is lightning coin the SHA256 of the secret will be sent as part of the maker signed invoice (_, MmCoinEnum::Tendermint(_) | MmCoinEnum::TendermintToken(_)) => SecretHashAlgo::SHA256, - #[cfg(all(feature = "enable-solana", not(target_arch = "wasm32")))] - (_, MmCoinEnum::SolanaCoin(_)) => SecretHashAlgo::SHA256, (_, _) => SecretHashAlgo::DHASH160, } } diff --git a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs index 5a480a0a2e..ba99379892 100644 --- a/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs +++ b/mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs @@ -39,13 +39,6 @@ use coins::z_coin::ZCoin; use coins::{add_delegation, get_my_address, get_raw_transaction, get_staking_infos, get_swap_transaction_fee_policy, nft, remove_delegation, set_swap_transaction_fee_policy, sign_message, sign_raw_transaction, verify_message, withdraw}; -#[cfg(all( - feature = "enable-solana", - not(target_os = "ios"), - not(target_os = "android"), - not(target_arch = "wasm32") -))] -use coins::{SolanaCoin, SplToken}; use coins_activation::{cancel_init_l2, cancel_init_platform_coin_with_tokens, cancel_init_standalone_coin, cancel_init_token, enable_platform_coin_with_tokens, enable_token, init_l2, init_l2_status, init_l2_user_action, init_platform_coin_with_tokens, init_platform_coin_with_tokens_status, @@ -224,17 +217,6 @@ async fn dispatcher_v2(request: MmRpcRequest, ctx: MmArc) -> DispatcherResult handle_mmrpc(ctx, request, set_swap_transaction_fee_policy).await, "send_asked_data" => handle_mmrpc(ctx, request, send_asked_data_rpc).await, "z_coin_tx_history" => handle_mmrpc(ctx, request, coins::my_tx_history_v2::z_coin_tx_history_rpc).await, - #[cfg(not(target_arch = "wasm32"))] - native_only_methods => match native_only_methods { - #[cfg(all(feature = "enable-solana", not(target_os = "ios"), not(target_os = "android")))] - "enable_solana_with_tokens" => { - handle_mmrpc(ctx, request, enable_platform_coin_with_tokens::).await - }, - #[cfg(all(feature = "enable-solana", not(target_os = "ios"), not(target_os = "android")))] - "enable_spl" => handle_mmrpc(ctx, request, enable_token::).await, - _ => MmError::err(DispatcherError::NoSuchMethod), - }, - #[cfg(target_arch = "wasm32")] _ => MmError::err(DispatcherError::NoSuchMethod), } } diff --git a/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs b/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs index 49150fab55..998cbc243a 100644 --- a/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs +++ b/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs @@ -1068,29 +1068,6 @@ pub fn slp_supplied_node() -> MarketMakerIt { .unwrap() } -pub fn _solana_supplied_node() -> MarketMakerIt { - let coins = json! ([ - {"coin": "SOL-DEVNET","name": "solana","fname": "Solana","rpcport": 80,"mm2": 1,"required_confirmations": 1,"avg_blocktime": 0.25,"protocol": {"type": "SOLANA"}}, - {"coin":"USDC-SOL-DEVNET","protocol":{"type":"SPLTOKEN","protocol_data":{"decimals":6,"token_contract_address":"4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU","platform":"SOL-DEVNET"}},"mm2": 1}, - {"coin":"ADEX-SOL-DEVNET","protocol":{"type":"SPLTOKEN","protocol_data":{"decimals":9,"token_contract_address":"5tSm6PqMosy1rz1AqV3kD28yYT5XqZW3QYmZommuFiPJ","platform":"SOL-DEVNET"}},"mm2": 1}, - ]); - - MarketMakerIt::start( - json! ({ - "gui": "nogui", - "netid": 9000, - "dht": "on", // Enable DHT without delay. - "passphrase": "federal stay trigger hour exist success game vapor become comfort action phone bright ill target wild nasty crumble dune close rare fabric hen iron", - "coins": coins, - "rpc_password": "pass", - "i_am_seed": true, - }), - "pass".to_string(), - None, - ) - .unwrap() -} - pub fn get_balance(mm: &MarketMakerIt, coin: &str) -> BalanceResponse { let rc = block_on(mm.rpc(&json!({ "userpass": mm.userpass, diff --git a/mm2src/mm2_main/tests/docker_tests/mod.rs b/mm2src/mm2_main/tests/docker_tests/mod.rs index 685f1bcad8..4b969b9fec 100644 --- a/mm2src/mm2_main/tests/docker_tests/mod.rs +++ b/mm2src/mm2_main/tests/docker_tests/mod.rs @@ -6,7 +6,6 @@ mod eth_docker_tests; pub mod qrc20_tests; #[cfg(feature = "enable-sia")] mod sia_docker_tests; mod slp_tests; -#[cfg(feature = "enable-solana")] mod solana_tests; mod swap_proto_v2_tests; mod swap_watcher_tests; mod swaps_confs_settings_sync_tests; diff --git a/mm2src/mm2_main/tests/docker_tests/solana_tests.rs b/mm2src/mm2_main/tests/docker_tests/solana_tests.rs deleted file mode 100644 index d1faa5d2a7..0000000000 --- a/mm2src/mm2_main/tests/docker_tests/solana_tests.rs +++ /dev/null @@ -1,144 +0,0 @@ -use crate::docker_tests::docker_tests_common::*; -use mm2_number::bigdecimal::Zero; -use mm2_test_helpers::for_tests::{disable_coin, enable_solana_with_tokens, enable_spl, sign_message, verify_message}; -use mm2_test_helpers::structs::{EnableSolanaWithTokensResponse, EnableSplResponse, RpcV2Response, SignatureResponse, - VerificationResponse}; -use serde_json as json; - -#[test] -fn test_solana_and_spl_balance_enable_spl_v2() { - let mm = _solana_supplied_node(); - let tx_history = false; - let enable_solana_with_tokens = block_on(enable_solana_with_tokens( - &mm, - "SOL-DEVNET", - &["USDC-SOL-DEVNET"], - "https://api.devnet.solana.com", - tx_history, - )); - let enable_solana_with_tokens: RpcV2Response = - json::from_value(enable_solana_with_tokens).unwrap(); - - let (_, solana_balance) = enable_solana_with_tokens - .result - .solana_addresses_infos - .into_iter() - .next() - .unwrap(); - assert!(solana_balance.balances.unwrap().spendable > 0.into()); - - let spl_balances = enable_solana_with_tokens - .result - .spl_addresses_infos - .into_iter() - .next() - .unwrap() - .1 - .balances - .unwrap(); - let usdc_spl = spl_balances.get("USDC-SOL-DEVNET").unwrap(); - assert!(usdc_spl.spendable.is_zero()); - - let enable_spl = block_on(enable_spl(&mm, "ADEX-SOL-DEVNET")); - let enable_spl: RpcV2Response = json::from_value(enable_spl).unwrap(); - assert_eq!(1, enable_spl.result.balances.len()); - - let (_, balance) = enable_spl.result.balances.into_iter().next().unwrap(); - assert!(balance.spendable > 0.into()); -} - -#[test] -fn test_sign_verify_message_solana() { - let mm = _solana_supplied_node(); - let tx_history = false; - block_on(enable_solana_with_tokens( - &mm, - "SOL-DEVNET", - &["USDC-SOL-DEVNET"], - "https://api.devnet.solana.com", - tx_history, - )); - - let response = block_on(sign_message(&mm, "SOL-DEVNET")); - let response: RpcV2Response = json::from_value(response).unwrap(); - let response = response.result; - - assert_eq!( - response.signature, - "3AoWCXHq3ACYHYEHUsCzPmRNiXn5c6kodXn9KDd1tz52e1da3dZKYXD5nrJW31XLtN6zzJiwHWtDta52w7Cd7qyE" - ); - - let response = block_on(verify_message( - &mm, - "SOL-DEVNET", - "3AoWCXHq3ACYHYEHUsCzPmRNiXn5c6kodXn9KDd1tz52e1da3dZKYXD5nrJW31XLtN6zzJiwHWtDta52w7Cd7qyE", - "FJktmyjV9aBHEShT4hfnLpr9ELywdwVtEL1w1rSWgbVf", - )); - let response: RpcV2Response = json::from_value(response).unwrap(); - let response = response.result; - - assert!(response.is_valid); -} - -#[test] -fn test_sign_verify_message_spl() { - let mm = _solana_supplied_node(); - let tx_history = false; - block_on(enable_solana_with_tokens( - &mm, - "SOL-DEVNET", - &["USDC-SOL-DEVNET"], - "https://api.devnet.solana.com", - tx_history, - )); - - block_on(enable_spl(&mm, "ADEX-SOL-DEVNET")); - - let response = block_on(sign_message(&mm, "ADEX-SOL-DEVNET")); - let response: RpcV2Response = json::from_value(response).unwrap(); - let response = response.result; - - assert_eq!( - response.signature, - "3AoWCXHq3ACYHYEHUsCzPmRNiXn5c6kodXn9KDd1tz52e1da3dZKYXD5nrJW31XLtN6zzJiwHWtDta52w7Cd7qyE" - ); - - let response = block_on(verify_message( - &mm, - "ADEX-SOL-DEVNET", - "3AoWCXHq3ACYHYEHUsCzPmRNiXn5c6kodXn9KDd1tz52e1da3dZKYXD5nrJW31XLtN6zzJiwHWtDta52w7Cd7qyE", - "FJktmyjV9aBHEShT4hfnLpr9ELywdwVtEL1w1rSWgbVf", - )); - let response: RpcV2Response = json::from_value(response).unwrap(); - let response = response.result; - - assert!(response.is_valid); -} - -#[test] -fn test_disable_solana_platform_coin_with_tokens() { - let mm = _solana_supplied_node(); - block_on(enable_solana_with_tokens( - &mm, - "SOL-DEVNET", - &["USDC-SOL-DEVNET"], - "https://api.devnet.solana.com", - false, - )); - block_on(enable_spl(&mm, "ADEX-SOL-DEVNET")); - - // Try to passive platform coin, SOL-DEVNET. - let res = block_on(disable_coin(&mm, "SOL-DEVNET", false)); - assert!(res.passivized); - - // Try to disable ADEX-SOL-DEVNET and USDC-SOL-DEVNET - // This should work, because platform coin is still in the memory. - let res = block_on(disable_coin(&mm, "ADEX-SOL-DEVNET", false)); - assert!(!res.passivized); - let res = block_on(disable_coin(&mm, "USDC-SOL-DEVNET", false)); - assert!(!res.passivized); - - // Then try to force disable SOL-DEVNET platform coin. - let res = block_on(disable_coin(&mm, "SOL-DEVNET", true)); - assert!(!res.passivized); -} diff --git a/mm2src/mm2_test_helpers/src/for_tests.rs b/mm2src/mm2_test_helpers/src/for_tests.rs index ffe7659a43..25ebef2df5 100644 --- a/mm2src/mm2_test_helpers/src/for_tests.rs +++ b/mm2src/mm2_test_helpers/src/for_tests.rs @@ -2029,21 +2029,6 @@ pub async fn enable_eth_coin( json::from_str(&enable.1).unwrap() } -pub async fn enable_spl(mm: &MarketMakerIt, coin: &str) -> Json { - let req = json!({ - "userpass": mm.userpass, - "method": "enable_spl", - "mmrpc": "2.0", - "params": { - "ticker": coin, - "activation_params": {} - } - }); - let enable = mm.rpc(&req).await.unwrap(); - assert_eq!(enable.0, StatusCode::OK, "'enable_spl' failed: {}", enable.1); - json::from_str(&enable.1).unwrap() -} - pub async fn enable_slp(mm: &MarketMakerIt, coin: &str) -> Json { let enable = mm .rpc(&json!({ @@ -2141,38 +2126,6 @@ pub async fn enable_bch_with_tokens( json::from_str(&enable.1).unwrap() } -pub async fn enable_solana_with_tokens( - mm: &MarketMakerIt, - platform_coin: &str, - tokens: &[&str], - solana_client_url: &str, - tx_history: bool, -) -> Json { - let spl_requests: Vec<_> = tokens.iter().map(|ticker| json!({ "ticker": ticker })).collect(); - let req = json!({ - "userpass": mm.userpass, - "method": "enable_solana_with_tokens", - "mmrpc": "2.0", - "params": { - "ticker": platform_coin, - "confirmation_commitment": "finalized", - "allow_slp_unsafe_conf": true, - "client_url": solana_client_url, - "tx_history": tx_history, - "spl_tokens_requests": spl_requests, - } - }); - - let enable = mm.rpc(&req).await.unwrap(); - assert_eq!( - enable.0, - StatusCode::OK, - "'enable_bch_with_tokens' failed: {}", - enable.1 - ); - json::from_str(&enable.1).unwrap() -} - pub async fn my_tx_history_v2( mm: &MarketMakerIt, coin: &str, diff --git a/mm2src/mm2_test_helpers/src/structs.rs b/mm2src/mm2_test_helpers/src/structs.rs index 8bdcfec722..2dd72a8435 100644 --- a/mm2src/mm2_test_helpers/src/structs.rs +++ b/mm2src/mm2_test_helpers/src/structs.rs @@ -919,14 +919,6 @@ pub struct EnableBchWithTokensResponse { pub slp_addresses_infos: HashMap>, } -#[derive(Debug, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct EnableSolanaWithTokensResponse { - pub current_block: u64, - pub solana_addresses_infos: HashMap>, - pub spl_addresses_infos: HashMap>, -} - #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] pub struct HistoryTransactionDetails { From 4e8bc50a7d0657cf37d68601bde2e76d280032ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C3=96zkan?= Date: Thu, 10 Oct 2024 21:24:51 +0300 Subject: [PATCH 29/29] fix(cosmos): fix tx broadcasting error (#2238) This commit upgrades cosmrs to version 15 along with other required dependency upgrades to fix the broadcast_tx_commit failure. --- Cargo.lock | 162 ++++++++---------- mm2src/coins/Cargo.toml | 14 +- mm2src/coins/tendermint/htlc/iris/htlc.rs | 15 +- mm2src/coins/tendermint/htlc/nucleus/htlc.rs | 15 +- mm2src/coins/tendermint/ibc/mod.rs | 1 - mm2src/coins/tendermint/ibc/transfer_v1.rs | 8 +- mm2src/coins/tendermint/tendermint_coin.rs | 8 +- mm2src/coins/utxo/pb.rs | 1 + mm2src/mm2_main/Cargo.toml | 4 +- .../src/lp_swap/komodefi.swap_v2.pb.rs | 1 + mm2src/mm2_net/Cargo.toml | 4 +- mm2src/trezor/Cargo.toml | 2 +- mm2src/trezor/src/error.rs | 4 +- mm2src/trezor/src/response.rs | 9 +- mm2src/trezor/src/transport/protocol.rs | 9 +- mm2src/trezor/src/utxo/sign_utxo.rs | 6 +- 16 files changed, 131 insertions(+), 132 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cf62d3f115..99227202a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -928,7 +928,7 @@ dependencies = [ "tower-service", "url", "utxo_signer", - "uuid 1.2.2", + "uuid", "wagyu-zcash-parameters", "wasm-bindgen", "wasm-bindgen-futures", @@ -1026,7 +1026,7 @@ dependencies = [ "sha2 0.10.7", "shared_ref_counter", "tokio", - "uuid 1.2.2", + "uuid", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test", @@ -1101,9 +1101,9 @@ dependencies = [ [[package]] name = "cosmos-sdk-proto" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73c9d2043a9e617b0d602fbc0a0ecd621568edbf3a9774890a6d562389bd8e1c" +checksum = "32560304ab4c365791fd307282f76637213d8083c1a98490c35159cd67852237" dependencies = [ "prost", "prost-types", @@ -1112,18 +1112,18 @@ dependencies = [ [[package]] name = "cosmrs" -version = "0.14.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af13955d6f356272e6def9ff5e2450a7650df536d8934f47052a20c76513d2f6" +checksum = "47126f5364df9387b9d8559dcef62e99010e1d4098f39eb3f7ee4b5c254e40ea" dependencies = [ "cosmos-sdk-proto", "ecdsa", "eyre", - "getrandom 0.2.9", "k256", "rand_core 0.6.4", "serde", "serde_json", + "signature 2.2.0", "subtle-encoding", "tendermint", "thiserror", @@ -1586,7 +1586,7 @@ dependencies = [ "rusqlite", "sql-builder", "tokio", - "uuid 1.2.2", + "uuid", ] [[package]] @@ -2066,11 +2066,10 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ - "matches", "percent-encoding", ] @@ -2764,6 +2763,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "if-addrs" version = "0.7.0" @@ -3005,9 +3014,7 @@ dependencies = [ "cfg-if 1.0.0", "ecdsa", "elliptic-curve", - "once_cell", "sha2 0.10.7", - "signature 2.2.0", ] [[package]] @@ -3883,7 +3890,7 @@ dependencies = [ "serde_json", "shared_ref_counter", "tokio", - "uuid 1.2.2", + "uuid", "wasm-bindgen-test", ] @@ -4101,7 +4108,7 @@ dependencies = [ "trie-db", "trie-root 0.16.0", "url", - "uuid 1.2.2", + "uuid", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test", @@ -4257,7 +4264,7 @@ dependencies = [ "ser_error_derive", "serde", "serde_json", - "uuid 1.2.2", + "uuid", ] [[package]] @@ -4295,7 +4302,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "uuid 1.2.2", + "uuid", ] [[package]] @@ -4727,9 +4734,9 @@ checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" [[package]] name = "peg" -version = "0.7.0" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07c0b841ea54f523f7aa556956fbd293bcbe06f2e67d2eb732b7278aaf1d166a" +checksum = "295283b02df346d1ef66052a757869b2876ac29a6bb0ac3f5f7cd44aebe40e8f" dependencies = [ "peg-macros", "peg-runtime", @@ -4737,9 +4744,9 @@ dependencies = [ [[package]] name = "peg-macros" -version = "0.7.0" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aa52829b8decbef693af90202711348ab001456803ba2a98eb4ec8fb70844c" +checksum = "bdad6a1d9cf116a059582ce415d5f5566aabcd4008646779dab7fdc2a9a9d426" dependencies = [ "peg-runtime", "proc-macro2", @@ -4748,9 +4755,9 @@ dependencies = [ [[package]] name = "peg-runtime" -version = "0.7.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c719dcf55f09a3a7e764c6649ab594c18a177e3599c467983cdf644bfc0a4088" +checksum = "e3aeb8f54c078314c2065ee649a7241f46b9d8e418e1a9581ba0546657d7aa3a" [[package]] name = "pem" @@ -4763,9 +4770,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "petgraph" @@ -4890,12 +4897,12 @@ checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" [[package]] name = "prettyplease" -version = "0.1.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28f53e8b192565862cf99343194579a022eb9c7dd3a8d03134734803c7b3125" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ "proc-macro2", - "syn 1.0.95", + "syn 2.0.38", ] [[package]] @@ -4977,9 +4984,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.11.9" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" dependencies = [ "bytes 1.4.0", "prost-derive", @@ -4987,44 +4994,43 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.11.9" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" +checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" dependencies = [ "bytes 1.4.0", "heck", "itertools", - "lazy_static", "log", "multimap", + "once_cell", "petgraph", "prettyplease", "prost", "prost-types", "regex", - "syn 1.0.95", + "syn 2.0.38", "tempfile", - "which", ] [[package]] name = "prost-derive" -version = "0.11.9" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", "itertools", "proc-macro2", "quote 1.0.33", - "syn 1.0.95", + "syn 2.0.38", ] [[package]] name = "prost-types" -version = "0.11.9" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" dependencies = [ "prost", ] @@ -6753,9 +6759,9 @@ dependencies = [ [[package]] name = "tendermint" -version = "0.32.2" +version = "0.34.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f0a7d05cf78524782337f8edd55cbc578d159a16ad4affe2135c92f7dbac7f0" +checksum = "15ab8f0a25d0d2ad49ac615da054d6a76aa6603ff95f7d18bafdd34450a1a04b" dependencies = [ "bytes 1.4.0", "digest 0.10.7", @@ -6784,9 +6790,9 @@ dependencies = [ [[package]] name = "tendermint-config" -version = "0.32.2" +version = "0.34.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71a72dbbea6dde12045d261f2c70c0de039125675e8a026c8d5ad34522756372" +checksum = "e1a02da769166e2052cd537b1a97c78017632c2d9e19266367b27e73910434fc" dependencies = [ "flex-error", "serde", @@ -6798,9 +6804,9 @@ dependencies = [ [[package]] name = "tendermint-proto" -version = "0.32.2" +version = "0.34.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cec054567d16d85e8c3f6a3139963d1a66d9d3051ed545d31562550e9bcc3d" +checksum = "b797dd3d2beaaee91d2f065e7bdf239dc8d80bba4a183a288bc1279dd5a69a1e" dependencies = [ "bytes 1.4.0", "flex-error", @@ -6816,9 +6822,9 @@ dependencies = [ [[package]] name = "tendermint-rpc" -version = "0.32.2" +version = "0.34.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d119d83a130537fc4a98c3c9eb6899ebe857fea4860400a61675bfb5f0b35129" +checksum = "71afae8bb5f6b14ed48d4e1316a643b6c2c3cbad114f510be77b4ed20b7b3e42" dependencies = [ "async-trait", "bytes 1.4.0", @@ -6826,6 +6832,7 @@ dependencies = [ "getrandom 0.2.9", "peg", "pin-project", + "rand 0.8.4", "semver 1.0.6", "serde", "serde_bytes", @@ -6838,7 +6845,7 @@ dependencies = [ "thiserror", "time 0.3.20", "url", - "uuid 0.8.2", + "uuid", "walkdir", ] @@ -7105,9 +7112,9 @@ dependencies = [ [[package]] name = "tonic" -version = "0.9.2" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" +checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" dependencies = [ "async-stream", "async-trait", @@ -7115,8 +7122,6 @@ dependencies = [ "base64 0.21.7", "bytes 1.4.0", "flate2", - "futures-core", - "futures-util", "h2", "http 0.2.12", "http-body 0.4.5", @@ -7125,6 +7130,7 @@ dependencies = [ "percent-encoding", "pin-project", "prost", + "rustls 0.21.10", "rustls-pemfile 1.0.2", "tokio", "tokio-rustls 0.24.1", @@ -7133,20 +7139,20 @@ dependencies = [ "tower-layer", "tower-service", "tracing", - "webpki-roots 0.23.1", + "webpki-roots 0.25.4", ] [[package]] name = "tonic-build" -version = "0.9.2" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6fdaae4c2c638bb70fe42803a26fbd6fc6ac8c72f5c59f67ecc2a2dcabf4b07" +checksum = "9d021fc044c18582b9a2408cd0dd05b1596e3ecdb5c4df822bb0183545683889" dependencies = [ "prettyplease", "proc-macro2", "prost-build", "quote 1.0.33", - "syn 1.0.95", + "syn 2.0.38", ] [[package]] @@ -7285,7 +7291,7 @@ dependencies = [ "futures-channel", "futures-io", "futures-util", - "idna", + "idna 0.2.3", "ipnet", "lazy_static", "rand 0.8.4", @@ -7370,12 +7376,9 @@ source = "git+https://github.com/KomodoPlatform/mm2-parity-ethereum.git?rev=mm2- [[package]] name = "unicode-bidi" -version = "0.3.4" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -dependencies = [ - "matches", -] +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" @@ -7385,9 +7388,9 @@ checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" [[package]] name = "unicode-normalization" -version = "0.1.19" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] @@ -7444,13 +7447,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.2.2" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", - "idna", - "matches", + "idna 0.5.0", "percent-encoding", "serde", ] @@ -7481,15 +7483,9 @@ dependencies = [ [[package]] name = "uuid" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" - -[[package]] -name = "uuid" -version = "1.2.2" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom 0.2.9", "rand 0.8.4", @@ -7725,7 +7721,7 @@ dependencies = [ "getrandom 0.2.9", "headers", "hex", - "idna", + "idna 0.2.3", "js-sys", "jsonrpc-core", "log", @@ -7786,16 +7782,6 @@ dependencies = [ "cc", ] -[[package]] -name = "which" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55551e42cbdf2ce2bedd2203d0cc08dba002c27510f86dab6d0ce304cba3dfe" -dependencies = [ - "either", - "libc", -] - [[package]] name = "widestring" version = "0.5.1" diff --git a/mm2src/coins/Cargo.toml b/mm2src/coins/Cargo.toml index 62c0e24ea0..8fa02034c1 100644 --- a/mm2src/coins/Cargo.toml +++ b/mm2src/coins/Cargo.toml @@ -33,7 +33,7 @@ cfg-if = "1.0" chain = { path = "../mm2_bitcoin/chain" } chrono = { version = "0.4.23", "features" = ["serde"] } common = { path = "../common" } -cosmrs = { version = "0.14.0", default-features = false } +cosmrs = { version = "0.15", default-features = false } crossbeam = "0.8" crypto = { path = "../crypto" } db_common = { path = "../db_common" } @@ -75,7 +75,7 @@ mocktopus = "0.8.0" num-traits = "0.2" parking_lot = { version = "0.12.0", features = ["nightly"] } primitives = { path = "../mm2_bitcoin/primitives" } -prost = "0.11" +prost = "0.12" protobuf = "2.20" proxy_signature = { path = "../proxy_signature" } rand = { version = "0.7", features = ["std", "small_rng"] } @@ -101,7 +101,7 @@ sha2 = "0.10" sha3 = "0.9" utxo_signer = { path = "utxo_signer" } # using the same version as cosmrs -tendermint-rpc = { version = "0.32.0", default-features = false } +tendermint-rpc = { version = "0.34", default-features = false } tokio-tungstenite-wasm = { git = "https://github.com/KomodoPlatform/tokio-tungstenite-wasm", rev = "d20abdb", features = ["rustls-tls-native-roots"]} url = { version = "2.2.2", features = ["serde"] } uuid = { version = "1.2.2", features = ["fast-rng", "serde", "v4"] } @@ -124,7 +124,7 @@ mm2_db = { path = "../mm2_db" } mm2_metamask = { path = "../mm2_metamask" } mm2_test_helpers = { path = "../mm2_test_helpers" } time = { version = "0.3.20", features = ["wasm-bindgen"] } -tonic = { version = "0.9", default-features = false, features = ["prost", "codegen", "gzip"] } +tonic = { version = "0.10", default-features = false, features = ["prost", "codegen", "gzip"] } tower-service = "0.3" wasm-bindgen = "0.2.86" wasm-bindgen-futures = { version = "0.4.1" } @@ -149,7 +149,7 @@ rustls = { version = "0.21", features = ["dangerous_configuration"] } secp256k1v24 = { version = "0.24", package = "secp256k1" } tokio = { version = "1.20" } tokio-rustls = { version = "0.24" } -tonic = { version = "0.9", features = ["tls", "tls-webpki-roots", "gzip"] } +tonic = { version = "0.10", features = ["tls", "tls-webpki-roots", "gzip"] } webpki-roots = { version = "0.25" } zcash_client_sqlite = { git = "https://github.com/KomodoPlatform/librustzcash.git", tag = "k-1.4.1" } zcash_proofs = { git = "https://github.com/KomodoPlatform/librustzcash.git", tag = "k-1.4.1", default-features = false, features = ["local-prover", "multicore"] } @@ -164,5 +164,5 @@ mm2_test_helpers = { path = "../mm2_test_helpers" } wagyu-zcash-parameters = { version = "0.2" } [build-dependencies] -prost-build = { version = "0.11", default-features = false } -tonic-build = { version = "0.9", default-features = false, features = ["prost"] } +prost-build = { version = "0.12", default-features = false } +tonic-build = { version = "0.10", default-features = false, features = ["prost"] } diff --git a/mm2src/coins/tendermint/htlc/iris/htlc.rs b/mm2src/coins/tendermint/htlc/iris/htlc.rs index 09561a4048..59cfb5cd73 100644 --- a/mm2src/coins/tendermint/htlc/iris/htlc.rs +++ b/mm2src/coins/tendermint/htlc/iris/htlc.rs @@ -1,12 +1,9 @@ use super::htlc_proto::{IrisClaimHtlcProto, IrisCreateHtlcProto}; -use cosmrs::proto::traits::TypeUrl; +use cosmrs::proto::traits::Name; use cosmrs::{tx::Msg, AccountId, Coin, ErrorReport}; use std::convert::TryFrom; -pub(crate) const IRIS_CREATE_HTLC_TYPE_URL: &str = "/irismod.htlc.MsgCreateHTLC"; -pub(crate) const IRIS_CLAIM_HTLC_TYPE_URL: &str = "/irismod.htlc.MsgClaimHTLC"; - #[derive(Clone, Debug, Eq, PartialEq)] pub(crate) struct IrisCreateHtlcMsg { /// Sender's address. @@ -87,8 +84,9 @@ impl From<&IrisCreateHtlcMsg> for IrisCreateHtlcProto { } } -impl TypeUrl for IrisCreateHtlcProto { - const TYPE_URL: &'static str = IRIS_CREATE_HTLC_TYPE_URL; +impl Name for IrisCreateHtlcProto { + const NAME: &'static str = "MsgCreateHTLC"; + const PACKAGE: &'static str = "irismod.htlc"; } #[derive(Clone)] @@ -141,6 +139,7 @@ impl From<&IrisClaimHtlcMsg> for IrisClaimHtlcProto { } } -impl TypeUrl for IrisClaimHtlcProto { - const TYPE_URL: &'static str = IRIS_CLAIM_HTLC_TYPE_URL; +impl Name for IrisClaimHtlcProto { + const NAME: &'static str = "MsgClaimHTLC"; + const PACKAGE: &'static str = "irismod.htlc"; } diff --git a/mm2src/coins/tendermint/htlc/nucleus/htlc.rs b/mm2src/coins/tendermint/htlc/nucleus/htlc.rs index a768f7fd4a..2c423285bf 100644 --- a/mm2src/coins/tendermint/htlc/nucleus/htlc.rs +++ b/mm2src/coins/tendermint/htlc/nucleus/htlc.rs @@ -1,12 +1,9 @@ use super::htlc_proto::{NucleusClaimHtlcProto, NucleusCreateHtlcProto}; -use cosmrs::proto::traits::TypeUrl; +use cosmrs::proto::traits::Name; use cosmrs::{tx::Msg, AccountId, Coin, ErrorReport}; use std::convert::TryFrom; -pub(crate) const NUCLEUS_CREATE_HTLC_TYPE_URL: &str = "/nucleus.htlc.MsgCreateHTLC"; -pub(crate) const NUCLEUS_CLAIM_HTLC_TYPE_URL: &str = "/nucleus.htlc.MsgClaimHTLC"; - #[derive(Clone, Debug, Eq, PartialEq)] pub(crate) struct NucleusCreateHtlcMsg { /// Sender's address. @@ -72,8 +69,9 @@ impl From<&NucleusCreateHtlcMsg> for NucleusCreateHtlcProto { } } -impl TypeUrl for NucleusCreateHtlcProto { - const TYPE_URL: &'static str = NUCLEUS_CREATE_HTLC_TYPE_URL; +impl Name for NucleusCreateHtlcProto { + const NAME: &'static str = "MsgCreateHTLC"; + const PACKAGE: &'static str = "nucleus.htlc"; } #[derive(Clone)] @@ -126,6 +124,7 @@ impl From<&NucleusClaimHtlcMsg> for NucleusClaimHtlcProto { } } -impl TypeUrl for NucleusClaimHtlcProto { - const TYPE_URL: &'static str = NUCLEUS_CLAIM_HTLC_TYPE_URL; +impl Name for NucleusClaimHtlcProto { + const NAME: &'static str = "MsgClaimHTLC"; + const PACKAGE: &'static str = "nucleus.htlc"; } diff --git a/mm2src/coins/tendermint/ibc/mod.rs b/mm2src/coins/tendermint/ibc/mod.rs index 51df375ab1..9e1c905398 100644 --- a/mm2src/coins/tendermint/ibc/mod.rs +++ b/mm2src/coins/tendermint/ibc/mod.rs @@ -1,7 +1,6 @@ mod ibc_proto; pub(crate) mod transfer_v1; -pub(crate) const IBC_TRANSFER_TYPE_URL: &str = "/ibc.applications.transfer.v1.MsgTransfer"; pub(crate) const IBC_OUT_SOURCE_PORT: &str = "transfer"; pub(crate) const IBC_OUT_TIMEOUT_IN_NANOS: u64 = 60000000000 * 15; // 15 minutes pub(crate) const IBC_GAS_LIMIT_DEFAULT: u64 = 150_000; diff --git a/mm2src/coins/tendermint/ibc/transfer_v1.rs b/mm2src/coins/tendermint/ibc/transfer_v1.rs index c5780e32b7..22bad1fefe 100644 --- a/mm2src/coins/tendermint/ibc/transfer_v1.rs +++ b/mm2src/coins/tendermint/ibc/transfer_v1.rs @@ -1,6 +1,5 @@ use super::{ibc_proto::IBCTransferV1Proto, IBC_OUT_SOURCE_PORT, IBC_OUT_TIMEOUT_IN_NANOS}; -use crate::tendermint::ibc::IBC_TRANSFER_TYPE_URL; -use cosmrs::proto::traits::TypeUrl; +use cosmrs::proto::traits::Name; use cosmrs::{tx::Msg, AccountId, Coin, ErrorReport}; use std::convert::TryFrom; @@ -99,6 +98,7 @@ impl From<&MsgTransfer> for IBCTransferV1Proto { } } -impl TypeUrl for IBCTransferV1Proto { - const TYPE_URL: &'static str = IBC_TRANSFER_TYPE_URL; +impl Name for IBCTransferV1Proto { + const NAME: &'static str = "MsgTransfer"; + const PACKAGE: &'static str = "ibc.applications.transfer.v1"; } diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index 9c7a1c1d96..de1753cffe 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -2531,13 +2531,13 @@ impl MarketCoinOps for TendermintCoin { let broadcast_res = try_s!(try_s!(coin.rpc_client().await).broadcast_tx_commit(tx_bytes).await); if broadcast_res.check_tx.log.contains(ACCOUNT_SEQUENCE_ERR) - || broadcast_res.deliver_tx.log.contains(ACCOUNT_SEQUENCE_ERR) + || broadcast_res.tx_result.log.contains(ACCOUNT_SEQUENCE_ERR) { return ERR!( "{}. check_tx log: {}, deliver_tx log: {}", ACCOUNT_SEQUENCE_ERR, broadcast_res.check_tx.log, - broadcast_res.deliver_tx.log + broadcast_res.tx_result.log ); } @@ -2545,8 +2545,8 @@ impl MarketCoinOps for TendermintCoin { return ERR!("Tx check failed {:?}", broadcast_res.check_tx); } - if !broadcast_res.deliver_tx.code.is_ok() { - return ERR!("Tx deliver failed {:?}", broadcast_res.deliver_tx); + if !broadcast_res.tx_result.code.is_ok() { + return ERR!("Tx deliver failed {:?}", broadcast_res.tx_result); } Ok(broadcast_res.hash.to_string()) }; diff --git a/mm2src/coins/utxo/pb.rs b/mm2src/coins/utxo/pb.rs index 3ae572bc23..a9fb741a11 100644 --- a/mm2src/coins/utxo/pb.rs +++ b/mm2src/coins/utxo/pb.rs @@ -1,3 +1,4 @@ +// This file is @generated by prost-build. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct GetMempoolInfoRequest {} diff --git a/mm2src/mm2_main/Cargo.toml b/mm2src/mm2_main/Cargo.toml index 5458c853be..259d3fc934 100644 --- a/mm2src/mm2_main/Cargo.toml +++ b/mm2src/mm2_main/Cargo.toml @@ -73,7 +73,7 @@ num-traits = "0.2" parity-util-mem = "0.11" parking_lot = { version = "0.12.0", features = ["nightly"] } primitives = { path = "../mm2_bitcoin/primitives" } -prost = "0.11" +prost = "0.12" rand = { version = "0.7", features = ["std", "small_rng"] } rand6 = { version = "0.6", package = "rand" } rmp-serde = "0.14.3" @@ -134,5 +134,5 @@ url = { version = "2.2.2", features = ["serde"] } [build-dependencies] chrono = "0.4" gstuff = { version = "0.7", features = ["nightly"] } -prost-build = { version = "0.11", default-features = false } +prost-build = { version = "0.12", default-features = false } regex = "1" diff --git a/mm2src/mm2_main/src/lp_swap/komodefi.swap_v2.pb.rs b/mm2src/mm2_main/src/lp_swap/komodefi.swap_v2.pb.rs index 130fb2e3e1..38c6e07106 100644 --- a/mm2src/mm2_main/src/lp_swap/komodefi.swap_v2.pb.rs +++ b/mm2src/mm2_main/src/lp_swap/komodefi.swap_v2.pb.rs @@ -1,3 +1,4 @@ +// This file is @generated by prost-build. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct SignedMessage { diff --git a/mm2src/mm2_net/Cargo.toml b/mm2src/mm2_net/Cargo.toml index d92e0c08b6..3962bf1136 100644 --- a/mm2src/mm2_net/Cargo.toml +++ b/mm2src/mm2_net/Cargo.toml @@ -27,7 +27,7 @@ mm2_err_handle = { path = "../mm2_err_handle" } mm2_event_stream = { path = "../mm2_event_stream", optional = true } mm2-libp2p = { path = "../mm2_p2p", package = "mm2_p2p", optional = true } parking_lot = { version = "0.12.0", features = ["nightly"], optional = true } -prost = "0.11" +prost = "0.12" rand = { version = "0.7", features = ["std", "small_rng", "wasm-bindgen"] } serde = "1" serde_json = { version = "1", features = ["preserve_order", "raw_value"] } @@ -41,7 +41,7 @@ http-body = "0.4" httparse = "1.8.0" js-sys = "0.3.27" pin-project = "1.1.2" -tonic = { version = "0.9", default-features = false, features = ["prost", "codegen"] } +tonic = { version = "0.10", default-features = false, features = ["prost", "codegen"] } tower-service = "0.3" wasm-bindgen = "0.2.86" wasm-bindgen-test = { version = "0.3.2" } diff --git a/mm2src/trezor/Cargo.toml b/mm2src/trezor/Cargo.toml index 5f0542d35a..36e5abb0ec 100644 --- a/mm2src/trezor/Cargo.toml +++ b/mm2src/trezor/Cargo.toml @@ -14,7 +14,7 @@ derive_more = "0.99" futures = { version = "0.3", package = "futures", features = ["compat", "async-await"] } hw_common = { path = "../hw_common" } mm2_err_handle = { path = "../mm2_err_handle" } -prost = "0.11" +prost = "0.12" rand = { version = "0.7", features = ["std", "wasm-bindgen"] } rpc_task = { path = "../rpc_task" } serde = "1.0" diff --git a/mm2src/trezor/src/error.rs b/mm2src/trezor/src/error.rs index 798185695c..6a67c592f2 100644 --- a/mm2src/trezor/src/error.rs +++ b/mm2src/trezor/src/error.rs @@ -1,3 +1,5 @@ +use std::convert::TryFrom; + use crate::proto::messages::MessageType; use crate::proto::messages_common::{failure::FailureType, Failure}; use crate::user_interaction::TrezorUserInteraction; @@ -57,7 +59,7 @@ pub enum OperationFailure { impl From for OperationFailure { fn from(failure: Failure) -> Self { - match failure.code.and_then(FailureType::from_i32) { + match failure.code.and_then(|t| FailureType::try_from(t).ok()) { Some(FailureType::FailurePinInvalid) | Some(FailureType::FailurePinMismatch) => { OperationFailure::InvalidPin }, diff --git a/mm2src/trezor/src/response.rs b/mm2src/trezor/src/response.rs index 869b910997..259db41840 100644 --- a/mm2src/trezor/src/response.rs +++ b/mm2src/trezor/src/response.rs @@ -6,6 +6,7 @@ use crate::{TrezorError, TrezorResult}; use async_trait::async_trait; use mm2_err_handle::prelude::*; use rpc_task::RpcTaskError; +use std::convert::TryFrom; use std::fmt; use std::sync::Arc; @@ -150,7 +151,10 @@ impl<'a, 'b, T> fmt::Debug for ButtonRequest<'a, 'b, T> { impl<'a, 'b, T: 'static> ButtonRequest<'a, 'b, T> { /// The type of button request. - pub fn request_type(&self) -> Option { self.message.code.and_then(ButtonRequestType::from_i32) } + #[inline(always)] + pub fn request_type(&self) -> Option { + self.message.code.and_then(|t| ButtonRequestType::try_from(t).ok()) + } /// Ack the request and get the next message from the device. pub async fn ack(self) -> TrezorResult> { @@ -174,8 +178,9 @@ impl<'a, 'b, T> fmt::Debug for PinMatrixRequest<'a, 'b, T> { impl<'a, 'b, T: 'static> PinMatrixRequest<'a, 'b, T> { /// The type of PIN matrix request. + #[inline(always)] pub fn request_type(&self) -> Option { - self.message.r#type.and_then(PinMatrixRequestType::from_i32) + self.message.r#type.and_then(|t| PinMatrixRequestType::try_from(t).ok()) } /// Ack the request with a PIN and get the next message from the device. diff --git a/mm2src/trezor/src/transport/protocol.rs b/mm2src/trezor/src/transport/protocol.rs index 6c553e5d76..e6c822eb3c 100644 --- a/mm2src/trezor/src/transport/protocol.rs +++ b/mm2src/trezor/src/transport/protocol.rs @@ -1,5 +1,7 @@ //! This file is inspired by https://github.com/tezedge/tezedge-client/blob/master/trezor_api/src/transport/protocol.rs +use std::convert::TryFrom; + use crate::proto::messages::MessageType; use crate::proto::ProtoMessage; use crate::{TrezorError, TrezorResult}; @@ -85,9 +87,10 @@ impl Protocol for ProtocolV1 { ); return MmError::err(TrezorError::ProtocolError(error)); } - let message_type_id = BigEndian::read_u16(&chunk[3..5]) as u32; - let message_type = MessageType::from_i32(message_type_id as i32) - .or_mm_err(|| TrezorError::ProtocolError(format!("Invalid message type: {}", message_type_id)))?; + let message_type_id = BigEndian::read_u16(&chunk[3..5]) as i32; + let message_type = MessageType::try_from(message_type_id).map_err(|e| { + TrezorError::ProtocolError(format!("Invalid message type: {}, Error: {}", message_type_id, e)) + })?; let data_length = BigEndian::read_u32(&chunk[5..9]) as usize; let mut data: Vec = chunk[9..].into(); diff --git a/mm2src/trezor/src/utxo/sign_utxo.rs b/mm2src/trezor/src/utxo/sign_utxo.rs index 8b56f4fbd4..7f53182639 100644 --- a/mm2src/trezor/src/utxo/sign_utxo.rs +++ b/mm2src/trezor/src/utxo/sign_utxo.rs @@ -1,3 +1,5 @@ +use std::convert::TryFrom; + use crate::proto::messages_bitcoin as proto_bitcoin; use crate::result_handler::ResultHandler; use crate::utxo::unsigned_tx::UnsignedUtxoTx; @@ -61,7 +63,9 @@ impl<'a> TrezorSession<'a> { loop { extract_serialized_data(&tx_request, &mut result)?; - let request_type = tx_request.request_type.and_then(ProtoTxRequestType::from_i32); + let request_type = tx_request + .request_type + .and_then(|t| ProtoTxRequestType::try_from(t).ok()); let request_type = match request_type { Some(ProtoTxRequestType::Txfinished) => return Ok(result), Some(req_type) => req_type,