From 6598acdee24c25221e3753def56793c057990d42 Mon Sep 17 00:00:00 2001 From: dimxy Date: Wed, 4 Oct 2023 15:04:02 +0500 Subject: [PATCH 01/17] add witness input and change output support for trezor --- mm2src/coins/utxo/utxo_common.rs | 7 ++++- mm2src/coins/utxo/utxo_withdraw.rs | 27 +++++++++++++----- mm2src/coins/utxo_signer/src/sign_params.rs | 31 +++++++++++++++++---- mm2src/trezor/src/utxo/unsigned_tx.rs | 3 ++ 4 files changed, 54 insertions(+), 14 deletions(-) diff --git a/mm2src/coins/utxo/utxo_common.rs b/mm2src/coins/utxo/utxo_common.rs index 07d1e58479..ed4d901795 100644 --- a/mm2src/coins/utxo/utxo_common.rs +++ b/mm2src/coins/utxo/utxo_common.rs @@ -959,7 +959,12 @@ impl<'a, T: AsRef + UtxoTxGenerationOps> UtxoTxBuilder<'a, T> { .from .clone() .or_mm_err(|| GenerateTxError::Internal("'from' address is not specified".to_owned()))?; - let change_script_pubkey = output_script(&from, ScriptType::P2PKH).to_bytes(); + let change_dest_type = if from.addr_format == UtxoAddressFormat::Segwit { + ScriptType::P2WPKH + } else { + ScriptType::P2PKH + }; + let change_script_pubkey = output_script(&from, change_dest_type).to_bytes(); let actual_tx_fee = match self.fee { Some(fee) => fee, diff --git a/mm2src/coins/utxo/utxo_withdraw.rs b/mm2src/coins/utxo/utxo_withdraw.rs index bc525f0c20..8c27013161 100644 --- a/mm2src/coins/utxo/utxo_withdraw.rs +++ b/mm2src/coins/utxo/utxo_withdraw.rs @@ -10,7 +10,7 @@ use common::log::info; use common::now_sec; use crypto::trezor::{TrezorError, TrezorProcessingError}; use crypto::{from_hw_error, CryptoCtx, CryptoCtxError, DerivationPath, HwError, HwProcessingError, HwRpcError}; -use keys::{AddressHashEnum, KeyPair, Private, Public as PublicKey, Type as ScriptType}; +use keys::{AddressFormat, AddressHashEnum, KeyPair, Private, Public as PublicKey, Type as ScriptType}; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use rpc::v1::types::ToTxHash; @@ -280,11 +280,21 @@ where let mut sign_params = UtxoSignTxParamsBuilder::new(); // TODO refactor [`UtxoTxBuilder::build`] to return `SpendingInputInfo` and `SendingOutputInfo` within `AdditionalTxData`. - sign_params.add_inputs_infos(unsigned_tx.inputs.iter().map(|_input| SpendingInputInfo::P2PKH { - address_derivation_path: self.from_derivation_path.clone(), - address_pubkey: self.from_pubkey, - })); - + sign_params.add_inputs_infos( + unsigned_tx + .inputs + .iter() + .map(|_input| match self.from_address.addr_format { + AddressFormat::Segwit => SpendingInputInfo::P2WPKH { + address_derivation_path: self.from_derivation_path.clone(), + address_pubkey: self.from_pubkey, + }, + _ => SpendingInputInfo::P2PKH { + address_derivation_path: self.from_derivation_path.clone(), + address_pubkey: self.from_pubkey, + }, + }), + ); sign_params.add_outputs_infos(once(SendingOutputInfo { destination_address: OutputDestination::plain(self.req.to.clone()), })); @@ -294,7 +304,10 @@ where // There is a change output. 2 => { sign_params.add_outputs_infos(once(SendingOutputInfo { - destination_address: OutputDestination::change(self.from_derivation_path.clone()), + destination_address: OutputDestination::change( + self.from_derivation_path.clone(), + self.from_address.addr_format.clone(), + ), })); }, unexpected => { diff --git a/mm2src/coins/utxo_signer/src/sign_params.rs b/mm2src/coins/utxo_signer/src/sign_params.rs index dab3ad17a4..99c7c0727a 100644 --- a/mm2src/coins/utxo_signer/src/sign_params.rs +++ b/mm2src/coins/utxo_signer/src/sign_params.rs @@ -2,7 +2,7 @@ use crate::{UtxoSignTxError, UtxoSignTxResult}; use chain::TransactionOutput; use crypto::trezor::utxo::TrezorOutputScriptType; use crypto::DerivationPath; -use keys::Public as PublicKey; +use keys::{AddressFormat, Public as PublicKey}; use mm2_err_handle::prelude::*; use script::{Script, SignatureVersion, TransactionInputSigner, UnsignedTransactionInput}; @@ -21,21 +21,33 @@ pub enum SpendingInputInfo { address_derivation_path: DerivationPath, address_pubkey: PublicKey, }, + P2WPKH { + address_derivation_path: DerivationPath, + address_pubkey: PublicKey, + }, // The fields are used to generate `trezor::proto::messages_bitcoin::MultisigRedeemScriptType` // P2SH {} } /// Either plain destination address or derivation path of a change address. pub enum OutputDestination { - Plain { address: String }, - Change { derivation_path: DerivationPath }, + Plain { + address: String, + }, + Change { + derivation_path: DerivationPath, + addr_format: AddressFormat, + }, } impl OutputDestination { pub fn plain(address: String) -> OutputDestination { OutputDestination::Plain { address } } - pub fn change(derivation_path: DerivationPath) -> OutputDestination { - OutputDestination::Change { derivation_path } + pub fn change(derivation_path: DerivationPath, addr_format: AddressFormat) -> OutputDestination { + OutputDestination::Change { + derivation_path, + addr_format, + } } } @@ -46,7 +58,14 @@ pub struct SendingOutputInfo { impl SendingOutputInfo { /// For now, returns [`TrezorOutputScriptType::PayToAddress`] since we don't support SLP tokens yet. - pub fn trezor_output_script_type(&self) -> TrezorOutputScriptType { TrezorOutputScriptType::PayToAddress } + pub fn trezor_output_script_type(&self) -> TrezorOutputScriptType { + match self.destination_address { + OutputDestination::Change { ref addr_format, .. } if *addr_format == AddressFormat::Segwit => { + TrezorOutputScriptType::PayToWitness + }, + _ => TrezorOutputScriptType::PayToAddress, + } + } } pub struct UtxoSignTxParamsBuilder { diff --git a/mm2src/trezor/src/utxo/unsigned_tx.rs b/mm2src/trezor/src/utxo/unsigned_tx.rs index 2573a3d2bc..c9bc7be4da 100644 --- a/mm2src/trezor/src/utxo/unsigned_tx.rs +++ b/mm2src/trezor/src/utxo/unsigned_tx.rs @@ -37,6 +37,8 @@ pub enum TrezorOutputScriptType { PayToAddress, /// OP_RETURN. PayToOpReturn, + /// pay to witness v0, used for the change + PayToWitness, } impl From for proto_bitcoin::OutputScriptType { @@ -44,6 +46,7 @@ impl From for proto_bitcoin::OutputScriptType { match script { TrezorOutputScriptType::PayToAddress => proto_bitcoin::OutputScriptType::Paytoaddress, TrezorOutputScriptType::PayToOpReturn => proto_bitcoin::OutputScriptType::Paytoopreturn, + TrezorOutputScriptType::PayToWitness => proto_bitcoin::OutputScriptType::Paytowitness, } } } From 1bc01b04e0d1600b429f586cc7f4a64d8caa4d85 Mon Sep 17 00:00:00 2001 From: dimxy Date: Tue, 10 Oct 2023 20:59:31 +0500 Subject: [PATCH 02/17] added trezor spending info for witness, also refactored match branches, fixed doc comments --- mm2src/coins/utxo/utxo_withdraw.rs | 2 +- mm2src/coins/utxo_signer/src/sign_params.rs | 2 +- mm2src/coins/utxo_signer/src/with_trezor.rs | 22 ++++++++++++++------- mm2src/trezor/src/utxo/unsigned_tx.rs | 4 ++-- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/mm2src/coins/utxo/utxo_withdraw.rs b/mm2src/coins/utxo/utxo_withdraw.rs index 8c27013161..8577697a74 100644 --- a/mm2src/coins/utxo/utxo_withdraw.rs +++ b/mm2src/coins/utxo/utxo_withdraw.rs @@ -289,7 +289,7 @@ where address_derivation_path: self.from_derivation_path.clone(), address_pubkey: self.from_pubkey, }, - _ => SpendingInputInfo::P2PKH { + AddressFormat::Standard | AddressFormat::CashAddress { .. } => SpendingInputInfo::P2PKH { address_derivation_path: self.from_derivation_path.clone(), address_pubkey: self.from_pubkey, }, diff --git a/mm2src/coins/utxo_signer/src/sign_params.rs b/mm2src/coins/utxo_signer/src/sign_params.rs index 99c7c0727a..4775fa0044 100644 --- a/mm2src/coins/utxo_signer/src/sign_params.rs +++ b/mm2src/coins/utxo_signer/src/sign_params.rs @@ -63,7 +63,7 @@ impl SendingOutputInfo { OutputDestination::Change { ref addr_format, .. } if *addr_format == AddressFormat::Segwit => { TrezorOutputScriptType::PayToWitness }, - _ => TrezorOutputScriptType::PayToAddress, + OutputDestination::Change { .. } | OutputDestination::Plain { .. } => TrezorOutputScriptType::PayToAddress, } } } diff --git a/mm2src/coins/utxo_signer/src/with_trezor.rs b/mm2src/coins/utxo_signer/src/with_trezor.rs index 5cbe8709ae..8da3b972bb 100644 --- a/mm2src/coins/utxo_signer/src/with_trezor.rs +++ b/mm2src/coins/utxo_signer/src/with_trezor.rs @@ -1,4 +1,4 @@ -use crate::sign_common::{complete_tx, p2pkh_spend_with_signature}; +use crate::sign_common::{complete_tx, p2pkh_spend_with_signature, p2wpkh_spend_with_signature}; use crate::sign_params::{OutputDestination, SendingOutputInfo, SpendingInputInfo, UtxoSignTxParams}; use crate::{TxProvider, UtxoSignTxError, UtxoSignTxResult}; use chain::{Transaction as UtxoTx, TransactionOutput}; @@ -9,7 +9,7 @@ use crypto::trezor::TrezorSession; use keys::bytes::Bytes; use mm2_err_handle::prelude::*; use rpc::v1::types::H256 as H256Json; -use script::{SignatureVersion, UnsignedTransactionInput}; +use script::UnsignedTransactionInput; use serialization::deserialize; pub struct TrezorTxSigner<'a, TxP> { @@ -23,10 +23,6 @@ pub struct TrezorTxSigner<'a, TxP> { impl<'a, TxP: TxProvider + Send + Sync> TrezorTxSigner<'a, TxP> { pub async fn sign_tx(mut self) -> UtxoSignTxResult { - if let SignatureVersion::WitnessV0 = self.params.signature_version { - return MmError::err(UtxoSignTxError::TrezorDoesntSupportP2WPKH); - } - let trezor_unsigned_tx = self.get_trezor_unsigned_tx().await?; let TxSignResult { @@ -49,6 +45,9 @@ impl<'a, TxP: TxProvider + Send + Sync> TrezorTxSigner<'a, TxP> { SpendingInputInfo::P2PKH { address_pubkey, .. } => { p2pkh_spend_with_signature(unsigned_input, address_pubkey, self.fork_id, Bytes::from(signature)) }, + SpendingInputInfo::P2WPKH { address_pubkey, .. } => { + p2wpkh_spend_with_signature(unsigned_input, address_pubkey, self.fork_id, Bytes::from(signature)) + }, }) .collect(); Ok(complete_tx(self.params.unsigned_tx, signed_inputs)) @@ -82,7 +81,9 @@ impl<'a, TxP: TxProvider + Send + Sync> TrezorTxSigner<'a, TxP> { fn get_trezor_output(&self, tx_output: &TransactionOutput, output_info: &SendingOutputInfo) -> TxOutput { let (address, address_derivation_path) = match output_info.destination_address { OutputDestination::Plain { ref address } => (Some(address.clone()), None), - OutputDestination::Change { ref derivation_path } => (None, Some(derivation_path.clone())), + OutputDestination::Change { + ref derivation_path, .. + } => (None, Some(derivation_path.clone())), }; TxOutput { address, @@ -108,6 +109,13 @@ impl<'a, TxP: TxProvider + Send + Sync> TrezorTxSigner<'a, TxP> { Some(address_derivation_path.clone()), TrezorInputScriptType::SpendAddress, ), + SpendingInputInfo::P2WPKH { + address_derivation_path, + .. + } => ( + Some(address_derivation_path.clone()), + TrezorInputScriptType::SpendWitness, + ), }; Ok(UnsignedTxInput { diff --git a/mm2src/trezor/src/utxo/unsigned_tx.rs b/mm2src/trezor/src/utxo/unsigned_tx.rs index c9bc7be4da..8b511c4658 100644 --- a/mm2src/trezor/src/utxo/unsigned_tx.rs +++ b/mm2src/trezor/src/utxo/unsigned_tx.rs @@ -33,11 +33,11 @@ impl From for proto_bitcoin::InputScriptType { #[derive(Clone, Copy)] pub enum TrezorOutputScriptType { - /// Used for all addresses (bitcoin, p2sh, witness). + /// Used for all addresses: bitcoin, p2sh, witness (except for the change output). PayToAddress, /// OP_RETURN. PayToOpReturn, - /// pay to witness v0, used for the change + /// pay to witness v0, used for the change output PayToWitness, } From e6bfa0548a64ec56716a37ea2df82953ceeada3d Mon Sep 17 00:00:00 2001 From: dimxy Date: Tue, 10 Oct 2023 21:14:08 +0500 Subject: [PATCH 03/17] Original source for UDP transport to connect to Trezor emulator --- mm2src/trezor/src/transport/udp.rs | 161 +++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 mm2src/trezor/src/transport/udp.rs diff --git a/mm2src/trezor/src/transport/udp.rs b/mm2src/trezor/src/transport/udp.rs new file mode 100644 index 0000000000..40fa622c0f --- /dev/null +++ b/mm2src/trezor/src/transport/udp.rs @@ -0,0 +1,161 @@ +/// This source file was borrowed from Trezor repo (https://raw.githubusercontent.com/trezor/trezor-firmware/07ba960ab4aa5aa3ddf16ae74c3658782d491250/rust/trezor-client/src/transport/udp.rs) +/// and modified to integrate into this project. +/// Adds udp transport to interact with trezor emulator. +/// To build emulator use this repo: https://github.com/trezor/trezor-firmware, build with build-docker.sh for the desired branch (tag) +/// Tested with the legacy emulator (for Trezor One). +/// After building the emulator find it as ./legacy/firmware/trezor.elf file. +/// Start it (no params needed) and initialize it with Trezor Suite: create a wallet, find a receive address. +/// You need the bridge for connecting from the Suite, it can be downloaded from trezor.io. +/// Do not use pin for the created wallet. +/// Be aware that when you rebuild the firmware the emulator flash memory file emulator.img is recreated (so save it before rebuilding code) +use super::{protocol::{Link, Protocol, ProtocolV1}, + ProtoMessage, Transport}; +use crate::{TrezorError, TrezorResult}; +use async_std::{io, net::UdpSocket, task::block_on}; +use mm2_err_handle::prelude::*; +use std::time::Duration; + +// A collection of constants related to the Emulator Ports. +mod constants { + pub const DEFAULT_HOST: &str = "127.0.0.1"; + pub const DEFAULT_PORT: &str = "21324"; + pub const DEFAULT_DEBUG_PORT: &str = "21325"; + pub const LOCAL_LISTENER: &str = "127.0.0.1:0"; +} + +use async_trait::async_trait; +use constants::{DEFAULT_DEBUG_PORT, DEFAULT_HOST, DEFAULT_PORT, LOCAL_LISTENER}; + +/// The chunk size for the serial protocol. +const CHUNK_SIZE: usize = 64; + +const READ_TIMEOUT_MS: u64 = 100000; +const WRITE_TIMEOUT_MS: u64 = 100000; + +/// A device found by the `find_devices()` method. It can be connected to using the `connect()` +/// method. +pub struct UdpAvailableDevice { + //pub model: Model, + pub debug: bool, + transport: UdpTransport, +} + +impl UdpAvailableDevice { + /// Connect to the device. + pub fn connect(self) -> TrezorResult { + block_on(async { + let transport = UdpTransport::connect(&self).await?; + Ok(transport) + }) + } +} + +pub fn find_devices() -> TrezorResult> { + block_on(async { + let debug = false; + let mut devices = Vec::new(); + let mut dest = String::new(); + dest.push_str(DEFAULT_HOST); + dest.push(':'); + dest.push_str(if debug { DEFAULT_DEBUG_PORT } else { DEFAULT_PORT }); + + let link = UdpLink::open(&dest).await?; + if link.ping().await? { + devices.push(UdpAvailableDevice { + // model: Model::TrezorEmulator, + debug, + transport: UdpTransport { + protocol: ProtocolV1 { link }, + }, + }); + } + Ok(devices) + }) +} + +/// An actual serial HID USB link to a device over which bytes can be sent. +struct UdpLink { + pub socket: UdpSocket, + pub device: (String, String), +} +// No need to implement drop as every member is owned + +#[async_trait] +impl Link for UdpLink { + async fn write_chunk(&mut self, chunk: Vec) -> TrezorResult<()> { + debug_assert_eq!(CHUNK_SIZE, chunk.len()); + io::timeout(Duration::from_millis(WRITE_TIMEOUT_MS), async move { + self.socket.send(&chunk).await + }) + .await + .map_to_mm(|_e| TrezorError::UnderlyingError(String::from("write timeout")))?; + Ok(()) + } + + async fn read_chunk(&mut self, chunk_len: u32) -> TrezorResult> { + let mut chunk = vec![0; chunk_len as usize]; + io::timeout(Duration::from_millis(READ_TIMEOUT_MS), async move { + let n = self.socket.recv(&mut chunk).await?; + if n == chunk_len as usize { + Ok(chunk) + } else { + Err(io::Error::new(io::ErrorKind::Other, "invalid read size")) + } + }) + .await + .map_to_mm(|_e| TrezorError::UnderlyingError(String::from("read timeout"))) + } +} + +impl UdpLink { + async fn open(path: &str) -> TrezorResult { + let mut parts = path.split(':'); + let link = Self { + socket: UdpSocket::bind(LOCAL_LISTENER).await?, + device: ( + parts.next().expect("Incorrect Path").to_owned(), + parts.next().expect("Incorrect Path").to_owned(), + ), + }; + link.socket.connect(path).await?; + Ok(link) + } + + // Ping the port and compare against expected response + async fn ping(&self) -> TrezorResult { + let mut resp = [0; CHUNK_SIZE]; + self.socket.send("PINGPING".as_bytes()).await?; + let size = self.socket.recv(&mut resp).await?; + Ok(&resp[..size] == "PONGPONG".as_bytes()) + } +} + +/// An implementation of the Transport interface for UDP devices. +// #[derive(Debug)] +pub struct UdpTransport { + protocol: ProtocolV1, +} + +impl UdpTransport { + /// Connect to a device over the UDP transport. + async fn connect(device: &UdpAvailableDevice) -> TrezorResult { + let transport = &device.transport; + let mut path = String::new(); + path.push_str(&transport.protocol.link.device.0); + path.push(':'); + path.push_str(&transport.protocol.link.device.1); + let link = UdpLink::open(&path).await?; + Ok(UdpTransport { + protocol: ProtocolV1 { link }, + }) + } +} + +#[async_trait] +impl Transport for UdpTransport { + async fn session_begin(&mut self) -> TrezorResult<()> { self.protocol.session_begin().await } + async fn session_end(&mut self) -> TrezorResult<()> { self.protocol.session_end().await } + + async fn write_message(&mut self, message: ProtoMessage) -> TrezorResult<()> { self.protocol.write(message).await } + async fn read_message(&mut self) -> TrezorResult { self.protocol.read().await } +} From ac7df8c1e262909ad558d1c96fde40cc0a3fcdfa Mon Sep 17 00:00:00 2001 From: dimxy Date: Mon, 30 Oct 2023 00:01:47 +0500 Subject: [PATCH 04/17] added modifications to udp.rs to integrate it --- mm2src/trezor/src/transport/udp.rs | 82 +++++++++++++++++------------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/mm2src/trezor/src/transport/udp.rs b/mm2src/trezor/src/transport/udp.rs index 40fa622c0f..45e6f3ab63 100644 --- a/mm2src/trezor/src/transport/udp.rs +++ b/mm2src/trezor/src/transport/udp.rs @@ -10,17 +10,18 @@ /// Be aware that when you rebuild the firmware the emulator flash memory file emulator.img is recreated (so save it before rebuilding code) use super::{protocol::{Link, Protocol, ProtocolV1}, ProtoMessage, Transport}; +use crate::transport::ConnectableDeviceWrapper; use crate::{TrezorError, TrezorResult}; -use async_std::{io, net::UdpSocket, task::block_on}; +use async_std::{io, net::UdpSocket}; use mm2_err_handle::prelude::*; use std::time::Duration; // A collection of constants related to the Emulator Ports. mod constants { - pub const DEFAULT_HOST: &str = "127.0.0.1"; - pub const DEFAULT_PORT: &str = "21324"; - pub const DEFAULT_DEBUG_PORT: &str = "21325"; - pub const LOCAL_LISTENER: &str = "127.0.0.1:0"; + pub(super) const DEFAULT_HOST: &str = "127.0.0.1"; + pub(super) const DEFAULT_PORT: &str = "21324"; + pub(super) const DEFAULT_DEBUG_PORT: &str = "21325"; + pub(super) const LOCAL_LISTENER: &str = "127.0.0.1:0"; } use async_trait::async_trait; @@ -42,35 +43,32 @@ pub struct UdpAvailableDevice { impl UdpAvailableDevice { /// Connect to the device. - pub fn connect(self) -> TrezorResult { - block_on(async { - let transport = UdpTransport::connect(&self).await?; - Ok(transport) - }) + async fn connect(self) -> TrezorResult { + let transport = UdpTransport::connect(&self).await?; + Ok(transport) } } -pub fn find_devices() -> TrezorResult> { - block_on(async { - let debug = false; - let mut devices = Vec::new(); - let mut dest = String::new(); - dest.push_str(DEFAULT_HOST); - dest.push(':'); - dest.push_str(if debug { DEFAULT_DEBUG_PORT } else { DEFAULT_PORT }); - - let link = UdpLink::open(&dest).await?; - if link.ping().await? { - devices.push(UdpAvailableDevice { - // model: Model::TrezorEmulator, - debug, - transport: UdpTransport { - protocol: ProtocolV1 { link }, - }, - }); - } - Ok(devices) - }) +async fn find_devices() -> TrezorResult> { + let debug = false; + let mut devices = Vec::new(); + let dest = format!( + "{}:{}", + DEFAULT_HOST, + if debug { DEFAULT_DEBUG_PORT } else { DEFAULT_PORT } + ); + + let link = UdpLink::open(&dest).await?; + if link.ping().await? { + devices.push(UdpAvailableDevice { + // model: Model::TrezorEmulator, + debug, + transport: UdpTransport { + protocol: ProtocolV1 { link }, + }, + }); + } + Ok(devices) } /// An actual serial HID USB link to a device over which bytes can be sent. @@ -140,10 +138,10 @@ impl UdpTransport { /// Connect to a device over the UDP transport. async fn connect(device: &UdpAvailableDevice) -> TrezorResult { let transport = &device.transport; - let mut path = String::new(); - path.push_str(&transport.protocol.link.device.0); - path.push(':'); - path.push_str(&transport.protocol.link.device.1); + let path = format!( + "{}:{}", + transport.protocol.link.device.0, transport.protocol.link.device.1 + ); let link = UdpLink::open(&path).await?; Ok(UdpTransport { protocol: ProtocolV1 { link }, @@ -159,3 +157,17 @@ impl Transport for UdpTransport { async fn write_message(&mut self, message: ProtoMessage) -> TrezorResult<()> { self.protocol.write(message).await } async fn read_message(&mut self) -> TrezorResult { self.protocol.read().await } } + +#[async_trait] +impl ConnectableDeviceWrapper for UdpAvailableDevice { + type T = UdpTransport; + + async fn find_devices() -> TrezorResult> + where + Self: Sized, + { + crate::transport::udp::find_devices().await + } + + async fn connect(self) -> TrezorResult { UdpAvailableDevice::connect(self).await } +} From 6bdd6dfd08a4f71fd43035d9762133d713672a91 Mon Sep 17 00:00:00 2001 From: dimxy Date: Mon, 30 Oct 2023 00:11:38 +0500 Subject: [PATCH 05/17] add tests for withdraw from witness and p2pkh with trezor, add trezor emulator support (via udp), add test config helpers, add layer to connect to Trezor or emulator over usb and udp --- Cargo.lock | 1 + mm2src/coins/utxo.rs | 64 +++++ mm2src/coins_activation/src/lib.rs | 4 +- .../src/standalone_coin/mod.rs | 3 +- .../src/utxo_activation/mod.rs | 49 ++++ mm2src/crypto/Cargo.toml | 3 + mm2src/crypto/src/hw_client.rs | 24 +- mm2src/mm2_main/Cargo.toml | 2 + mm2src/mm2_main/src/lp_init/init_hw.rs | 2 +- mm2src/mm2_main/src/mm2.rs | 1 + .../tests/integration_tests_common/mod.rs | 3 +- .../tests/mm2_tests/mm2_tests_inner.rs | 271 +++++++++++++++++- .../mm2_main/tests/mm2_tests/z_coin_tests.rs | 2 +- mm2src/mm2_test_helpers/src/for_tests.rs | 105 ++++++- mm2src/trezor/Cargo.toml | 4 + mm2src/trezor/src/device_info.rs | 2 +- mm2src/trezor/src/error.rs | 5 + mm2src/trezor/src/transport/mod.rs | 16 ++ mm2src/trezor/src/transport/usb.rs | 21 +- 19 files changed, 546 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5981ce776e..21fddc9af4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8548,6 +8548,7 @@ dependencies = [ name = "trezor" version = "0.1.1" dependencies = [ + "async-std", "async-trait", "bip32", "byteorder", diff --git a/mm2src/coins/utxo.rs b/mm2src/coins/utxo.rs index b85798406c..30f6396e72 100644 --- a/mm2src/coins/utxo.rs +++ b/mm2src/coins/utxo.rs @@ -1962,6 +1962,70 @@ fn parse_hex_encoded_u32(hex_encoded: &str) -> Result> { Ok(u32::from_be_bytes(be_bytes)) } +#[cfg(not(target_arch = "wasm32"))] +pub mod for_tests { + use super::UtxoCoinFields; + use crate::rpc_command::init_withdraw::WithdrawStatusRequest; + use crate::rpc_command::init_withdraw::{init_withdraw, withdraw_status}; + use crate::WithdrawFrom; + use crate::{utxo::{utxo_standard::UtxoStandardCoin, UtxoArc}, + WithdrawRequest}; + use crate::{MarketCoinOps, TransactionDetails, WithdrawError}; + use common::executor::Timer; + use common::{now_ms, wait_until_ms}; + use mm2_core::mm_ctx::MmArc; + use mm2_err_handle::prelude::MmResult; + use mm2_number::BigDecimal; + use rpc_task::RpcTaskStatus; + use std::str::FromStr; + + /// Helper to call init_withdraw and wait for completion + pub async fn test_withdraw_init_loop( + ctx: MmArc, + fields: UtxoCoinFields, + ticker: &str, + to: &str, + amount: &str, + from_derivation_path: &str, + ) -> MmResult { + let arc: UtxoArc = fields.into(); + let coin: UtxoStandardCoin = arc.into(); + + let withdraw_req = WithdrawRequest { + amount: BigDecimal::from_str(amount).unwrap(), + from: Some(WithdrawFrom::DerivationPath { + derivation_path: from_derivation_path.to_owned(), + }), + to: to.to_owned(), + coin: ticker.to_owned(), + max: false, + fee: None, + memo: None, + }; + let init = init_withdraw(ctx.clone(), withdraw_req).await.unwrap(); + let timeout = wait_until_ms(150000); + loop { + if now_ms() > timeout { + panic!("{} init_withdraw timed out", coin.ticker()); + } + let status = withdraw_status(ctx.clone(), WithdrawStatusRequest { + task_id: init.task_id, + forget_if_finished: true, + }) + .await; + if let Ok(status) = status { + match status { + RpcTaskStatus::Ok(tx_details) => break Ok(tx_details), + RpcTaskStatus::Error(e) => break Err(e), + _ => Timer::sleep(1.).await, + } + } else { + panic!("{} could not get withdraw_status", coin.ticker()) + } + } + } +} + #[test] fn test_parse_hex_encoded_u32() { assert_eq!(parse_hex_encoded_u32("0x892f2085"), Ok(2301567109)); diff --git a/mm2src/coins_activation/src/lib.rs b/mm2src/coins_activation/src/lib.rs index 34bd11e902..89cc5aca85 100644 --- a/mm2src/coins_activation/src/lib.rs +++ b/mm2src/coins_activation/src/lib.rs @@ -26,10 +26,12 @@ mod tendermint_token_activation; mod tendermint_with_assets_activation; mod token; mod utxo_activation; +#[cfg(not(target_arch = "wasm32"))] +pub use utxo_activation::for_tests; #[cfg(not(target_arch = "wasm32"))] mod z_coin_activation; pub use l2::{cancel_init_l2, init_l2, init_l2_status, init_l2_user_action}; pub use platform_coin_with_tokens::enable_platform_coin_with_tokens; pub use standalone_coin::{cancel_init_standalone_coin, init_standalone_coin, init_standalone_coin_status, - init_standalone_coin_user_action}; + init_standalone_coin_user_action, InitStandaloneCoinReq, InitStandaloneCoinStatusRequest}; pub use token::enable_token; diff --git a/mm2src/coins_activation/src/standalone_coin/mod.rs b/mm2src/coins_activation/src/standalone_coin/mod.rs index 6cd26b35bb..2f02aa423a 100644 --- a/mm2src/coins_activation/src/standalone_coin/mod.rs +++ b/mm2src/coins_activation/src/standalone_coin/mod.rs @@ -3,6 +3,7 @@ mod init_standalone_coin_error; pub use init_standalone_coin::{cancel_init_standalone_coin, init_standalone_coin, init_standalone_coin_status, init_standalone_coin_user_action, InitStandaloneCoinActivationOps, - InitStandaloneCoinInitialStatus, InitStandaloneCoinTask, InitStandaloneCoinTaskHandle, + InitStandaloneCoinInitialStatus, InitStandaloneCoinReq, + InitStandaloneCoinStatusRequest, InitStandaloneCoinTask, InitStandaloneCoinTaskHandle, InitStandaloneCoinTaskManagerShared}; pub use init_standalone_coin_error::InitStandaloneCoinError; diff --git a/mm2src/coins_activation/src/utxo_activation/mod.rs b/mm2src/coins_activation/src/utxo_activation/mod.rs index ef86599a76..fe51327b5b 100644 --- a/mm2src/coins_activation/src/utxo_activation/mod.rs +++ b/mm2src/coins_activation/src/utxo_activation/mod.rs @@ -7,3 +7,52 @@ mod utxo_standard_activation_result; pub use init_qtum_activation::QtumTaskManagerShared; pub use init_utxo_standard_activation::UtxoStandardTaskManagerShared; + +/// helpers for use in tests in other modules +#[cfg(not(target_arch = "wasm32"))] +pub mod for_tests { + use common::{executor::Timer, now_ms, wait_until_ms}; + use mm2_core::mm_ctx::MmArc; + use mm2_err_handle::prelude::{MmResult, NotEqual}; + use rpc_task::RpcTaskStatus; + //use serde_json::{Value, self}; + + use crate::{init_standalone_coin, init_standalone_coin_status, + standalone_coin::{InitStandaloneCoinActivationOps, InitStandaloneCoinError, + InitStandaloneCoinInitialStatus}, + InitStandaloneCoinReq, InitStandaloneCoinStatusRequest}; + + /// test helper to activate standalone coin with waiting for the result + pub async fn init_standalone_coin_loop( + ctx: MmArc, + request: InitStandaloneCoinReq, + ) -> MmResult + where + Standalone: InitStandaloneCoinActivationOps + Send + Sync + 'static, + Standalone::InProgressStatus: InitStandaloneCoinInitialStatus, + InitStandaloneCoinError: From, + (Standalone::ActivationError, InitStandaloneCoinError): NotEqual, + { + let init_result = init_standalone_coin::(ctx.clone(), request).await.unwrap(); + let timeout = wait_until_ms(150000); + loop { + if now_ms() > timeout { + panic!("init_standalone_coin timed out"); + } + let status_req = InitStandaloneCoinStatusRequest { + task_id: init_result.task_id, + forget_if_finished: true, + }; + let status_res = init_standalone_coin_status::(ctx.clone(), status_req).await; + if let Ok(status) = status_res { + match status { + RpcTaskStatus::Ok(result) => break Ok(result), + RpcTaskStatus::Error(e) => break Err(e), + _ => Timer::sleep(1.).await, + } + } else { + panic!("could not get init_standalone_coin status"); + } + } + } +} diff --git a/mm2src/crypto/Cargo.toml b/mm2src/crypto/Cargo.toml index ff28977341..c4e4a84f92 100644 --- a/mm2src/crypto/Cargo.toml +++ b/mm2src/crypto/Cargo.toml @@ -44,3 +44,6 @@ mm2_eth = { path = "../mm2_eth" } mm2_metamask = { path = "../mm2_metamask" } wasm-bindgen-test = { version = "0.3.2" } web3 = { git = "https://github.com/KomodoPlatform/rust-web3", tag = "v0.19.0", default-features = false } + +[features] +trezor-udp = ["trezor/trezor-udp"] diff --git a/mm2src/crypto/src/hw_client.rs b/mm2src/crypto/src/hw_client.rs index cec4f104c1..eda8caf36b 100644 --- a/mm2src/crypto/src/hw_client.rs +++ b/mm2src/crypto/src/hw_client.rs @@ -46,7 +46,7 @@ pub enum HwWalletType { Trezor, } -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(tag = "type")] pub enum HwDeviceInfo { Trezor(TrezorDeviceInfo), @@ -124,9 +124,14 @@ impl HwClient { ) -> MmResult> { use common::custom_futures::timeout::TimeoutError; use common::executor::Timer; - - async fn try_to_connect() -> HwResult> { - let mut devices = trezor::transport::usb::find_devices()?; + use trezor::transport::{ConnectableDeviceWrapper, Transport}; + + async fn try_to_connect() -> HwResult> + where + C: ConnectableDeviceWrapper, + ::T: Transport + Sync + Send + 'static, + { + let mut devices = C::find_devices().await?; if devices.is_empty() { return Ok(None); } @@ -134,16 +139,23 @@ impl HwClient { return MmError::err(HwError::CannotChooseDevice { count: devices.len() }); } let device = devices.remove(0); - let transport = device.connect()?; + let transport = device.connect().await?; let trezor = TrezorClient::from_transport(transport); Ok(Some(trezor)) } let fut = async move { loop { - if let Some(trezor) = try_to_connect().await? { + if let Some(trezor) = try_to_connect::().await? { return Ok(trezor); } + + #[cfg(feature = "trezor-udp")] + // try also to connect to emulator over UDP + if let Some(trezor) = try_to_connect::().await? { + return Ok(trezor); + } + Timer::sleep(1.).await; } }; diff --git a/mm2src/mm2_main/Cargo.toml b/mm2src/mm2_main/Cargo.toml index a53f1136f7..bbb11c3a79 100644 --- a/mm2src/mm2_main/Cargo.toml +++ b/mm2src/mm2_main/Cargo.toml @@ -20,6 +20,7 @@ run-docker-tests = [] # TODO enable-solana = [] default = [] +trezor-udp = ["crypto/trezor-udp"] # use for tests to connect to trezor emulator over udp [dependencies] async-std = { version = "1.5", features = ["unstable"] } @@ -113,6 +114,7 @@ rcgen = "0.10" rustls = { version = "0.20", default-features = false } rustls-pemfile = "1.0.2" tokio = { version = "1.20", features = ["io-util", "rt-multi-thread", "net"] } +mm2_test_helpers = { path = "../mm2_test_helpers" } [target.'cfg(windows)'.dependencies] winapi = "0.3" diff --git a/mm2src/mm2_main/src/lp_init/init_hw.rs b/mm2src/mm2_main/src/lp_init/init_hw.rs index 726bea7967..ab9b90b545 100644 --- a/mm2src/mm2_main/src/lp_init/init_hw.rs +++ b/mm2src/mm2_main/src/lp_init/init_hw.rs @@ -100,7 +100,7 @@ pub struct InitHwRequest { device_pubkey: Option, } -#[derive(Clone, Serialize)] +#[derive(Clone, Serialize, Debug, Deserialize)] pub struct InitHwResponse { #[serde(flatten)] device_info: HwDeviceInfo, diff --git a/mm2src/mm2_main/src/mm2.rs b/mm2src/mm2_main/src/mm2.rs index f57c6dd711..a6310ba180 100644 --- a/mm2src/mm2_main/src/mm2.rs +++ b/mm2src/mm2_main/src/mm2.rs @@ -49,6 +49,7 @@ use std::ptr::null; use std::str; #[path = "lp_native_dex.rs"] 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; use mm2_err_handle::prelude::*; diff --git a/mm2src/mm2_main/tests/integration_tests_common/mod.rs b/mm2src/mm2_main/tests/integration_tests_common/mod.rs index be7e8bcb46..56d4fde57f 100644 --- a/mm2src/mm2_main/tests/integration_tests_common/mod.rs +++ b/mm2src/mm2_main/tests/integration_tests_common/mod.rs @@ -116,8 +116,9 @@ pub async fn enable_utxo_v2_electrum( coin: &str, servers: Vec, timeout: u64, + priv_key_policy: Option<&str>, ) -> UtxoStandardActivationResult { - let init = init_utxo_electrum(mm, coin, servers).await; + let init = init_utxo_electrum(mm, coin, servers, priv_key_policy).await; let init: RpcV2Response = json::from_value(init).unwrap(); let timeout = wait_until_ms(timeout * 1000); 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 54dc1e6d24..852fa66daf 100644 --- a/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs +++ b/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs @@ -1,11 +1,23 @@ #[cfg(all(feature = "zhtlc-native-tests", not(target_arch = "wasm32")))] use super::enable_z_coin; use crate::integration_tests_common::*; +use coins::utxo::for_tests::test_withdraw_init_loop; +use coins::utxo::{utxo_builder::{UtxoArcBuilder, UtxoCoinBuilder}, + utxo_standard::UtxoStandardCoin, + UtxoActivationParams}; +use coins::PrivKeyBuildPolicy; +use coins_activation::{for_tests::init_standalone_coin_loop, InitStandaloneCoinReq}; use common::executor::Timer; +use common::now_ms; +use common::serde::Deserialize; +use common::wait_until_ms; use common::{cfg_native, cfg_wasm32, get_utc_timestamp, log, new_uuid}; use crypto::privkey::key_pair_from_seed; +use crypto::CryptoCtx; use crypto::StandardHDCoinAddress; use http::{HeaderMap, StatusCode}; +use mm2_core::mm_ctx::MmArc; +use mm2_main::mm2::init_hw::{init_trezor, init_trezor_status, InitHwRequest, InitHwResponse}; use mm2_main::mm2::lp_ordermatch::MIN_ORDER_KEEP_ALIVE_INTERVAL; use mm2_metrics::{MetricType, MetricsJson}; use mm2_number::{BigDecimal, BigRational, Fraction, MmNumber}; @@ -13,6 +25,7 @@ use mm2_rpc::data::legacy::{CoinInitResponse, MmVersionResponse, OrderbookRespon use mm2_test_helpers::electrums::*; #[cfg(all(not(target_arch = "wasm32"), not(feature = "zhtlc-native-tests")))] use mm2_test_helpers::for_tests::check_stats_swap_status; +#[cfg(all(not(target_arch = "wasm32")))] use mm2_test_helpers::for_tests::{btc_segwit_conf, btc_with_spv_conf, btc_with_sync_starting_header, check_recent_swaps, enable_eth_coin, enable_qrc20, eth_jst_testnet_conf, eth_testnet_conf, find_metrics_in_json, from_env_file, get_shared_db_id, mm_spat, @@ -23,9 +36,12 @@ use mm2_test_helpers::for_tests::{btc_segwit_conf, btc_with_spv_conf, btc_with_s MarketMakerIt, Mm2InitPrivKeyPolicy, Mm2TestConf, Mm2TestConfForSwap, RaiiDump, DOC_ELECTRUM_ADDRS, ETH_DEV_NODES, ETH_DEV_SWAP_CONTRACT, ETH_DEV_TOKEN_CONTRACT, ETH_MAINNET_NODE, ETH_MAINNET_SWAP_CONTRACT, MARTY_ELECTRUM_ADDRS, MORTY, - QRC20_ELECTRUMS, RICK, RICK_ELECTRUM_ADDRS, TBTC_ELECTRUMS, T_BCH_ELECTRUMS}; + QRC20_ELECTRUMS, RICK, RICK_ELECTRUM_ADDRS, TBTC_ELECTRUMS, T_BCH_ELECTRUMS, + init_trezor_rpc, init_trezor_status_rpc, mm_ctx_with_custom_db_with_conf, + init_withdraw, withdraw_status, tbtc_legacy_conf}; use mm2_test_helpers::get_passphrase; use mm2_test_helpers::structs::*; +use rpc_task::{rpc_common::RpcTaskStatusRequest, RpcTaskStatus}; use serde_json::{self as json, json, Value as Json}; use std::collections::HashMap; use std::env::{self, var}; @@ -7371,7 +7387,7 @@ fn test_enable_btc_with_sync_starting_header() { let (_dump_log, _dump_dashboard) = mm_bob.mm_dump(); log!("log path: {}", mm_bob.log_path.display()); - let utxo_bob = block_on(enable_utxo_v2_electrum(&mm_bob, "BTC", btc_electrums(), 80)); + let utxo_bob = block_on(enable_utxo_v2_electrum(&mm_bob, "BTC", btc_electrums(), 80, None)); log!("enable UTXO bob {:?}", utxo_bob); block_on(mm_bob.stop()).unwrap(); @@ -7401,7 +7417,7 @@ fn test_btc_block_header_sync() { let (_dump_log, _dump_dashboard) = mm_bob.mm_dump(); log!("log path: {}", mm_bob.log_path.display()); - let utxo_bob = block_on(enable_utxo_v2_electrum(&mm_bob, "BTC", btc_electrums(), 600)); + let utxo_bob = block_on(enable_utxo_v2_electrum(&mm_bob, "BTC", btc_electrums(), 600, None)); log!("enable UTXO bob {:?}", utxo_bob); block_on(mm_bob.stop()).unwrap(); @@ -7432,7 +7448,13 @@ fn test_tbtc_block_header_sync() { let (_dump_log, _dump_dashboard) = mm_bob.mm_dump(); log!("log path: {}", mm_bob.log_path.display()); - let utxo_bob = block_on(enable_utxo_v2_electrum(&mm_bob, "tBTC-TEST", tbtc_electrums(), 100000)); + let utxo_bob = block_on(enable_utxo_v2_electrum( + &mm_bob, + "tBTC-TEST", + tbtc_electrums(), + 100000, + None, + )); log!("enable UTXO bob {:?}", utxo_bob); block_on(mm_bob.stop()).unwrap(); @@ -7854,10 +7876,6 @@ fn test_sign_raw_transaction_p2wpkh() { // start bob let mm_bob = MarketMakerIt::start(conf.conf, conf.rpc_password, None).unwrap(); - - let (_bob_dump_log, _bob_dump_dashboard) = mm_bob.mm_dump(); - log!("Bob log path: {}", mm_bob.log_path.display()); - // Enable coins on Bob side. Print the replies in case we need the "address". let coin_init_resp = block_on(enable_electrum(&mm_bob, "tBTC-Segwit", false, TBTC_ELECTRUMS, None)); assert_eq!( @@ -7913,3 +7931,240 @@ fn test_sign_raw_transaction_p2wpkh() { Json::from("Invalid param: spends are from same address only") ); } + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields, tag = "status", content = "details")] +pub enum InitTrezorStatus { + Ok(InitHwResponse), + Error(Json), + InProgress(Json), + UserActionRequired(Json), +} + +pub async fn mm_ctx_with_trezor(conf: Json) -> MmArc { + let ctx = mm_ctx_with_custom_db_with_conf(Some(conf)); + + CryptoCtx::init_with_iguana_passphrase(ctx.clone(), "123456").unwrap(); // for now we need passphrase seed for init + let req: InitHwRequest = serde_json::from_value(json!({ "device_pubkey": null })).unwrap(); + let res = match init_trezor(ctx.clone(), req).await { + Ok(res) => res, + _ => { + panic!("cannot init trezor"); + }, + }; + + loop { + let status_req = RpcTaskStatusRequest { + task_id: res.task_id, + forget_if_finished: false, + }; + match init_trezor_status(ctx.clone(), status_req).await { + Ok(res) => { + log!("trezor init status={:?}", serde_json::to_string(&res).unwrap()); + match res { + RpcTaskStatus::Ok(_) => { + log!("device initialized"); + break; + }, + RpcTaskStatus::Error(_) => { + log!("device in error state"); + break; + }, + RpcTaskStatus::InProgress(_) => log!("trezor init in progress"), + RpcTaskStatus::UserActionRequired(_) => log!("device is waiting for user action"), + } + }, + _ => { + panic!("cannot get trezor status"); + }, + }; + Timer::sleep(5.).await + } + ctx +} + +/// Tool to run withdraw directly with trezor device or emulator (no rpc version, added for easier debugging) +/// run cargo test with '--ignored' option +/// to use trezor emulator add '--features trezor-udp' option to cargo test params +#[test] +#[ignore] +#[cfg(all(not(target_arch = "wasm32")))] +fn test_withdraw_from_trezor_segwit_no_rpc() { + let ticker = "tBTC-Segwit"; + let mut coin_conf = tbtc_segwit_conf(); + coin_conf["trezor_coin"] = "Testnet".into(); + let mm_conf = json!({ "coins": [coin_conf] }); + + let ctx = block_on(mm_ctx_with_trezor(mm_conf)); + let priv_key_policy = PrivKeyBuildPolicy::Trezor; + let enable_req = json!({ + "method": "electrum", + "coin": ticker, + "servers": tbtc_electrums(), + "priv_key_policy": "Trezor", + }); + let activation_params = UtxoActivationParams::from_legacy_req(&enable_req).unwrap(); + let request: InitStandaloneCoinReq = json::from_value(json!({ + "ticker": ticker, + "activation_params": activation_params + })) + .unwrap(); + + block_on(init_standalone_coin_loop::(ctx.clone(), request)) + .expect("coin activation must be successful"); + + let builder = UtxoArcBuilder::new( + &ctx, + ticker, + &coin_conf, + &activation_params, + priv_key_policy, + UtxoStandardCoin::from, + ); + let fields = block_on(builder.build_utxo_fields()).unwrap(); + let tx_details = block_on(test_withdraw_init_loop( + ctx.clone(), + fields, + ticker, + "tb1q3zkv6g29ku3jh9vdkhxlpyek44se2s0zrv7ctn", + "0.00001", + "m/84'/1'/0'/0/0", + )) + .expect("withdraw must end successfully"); + log!("tx_hex={}", serde_json::to_string(&tx_details.tx_hex).unwrap()); +} + +/// Helper to init trezor and wait for completion +pub async fn init_trezor_loop_rpc(mm: &MarketMakerIt, coin: &str, timeout: u64) -> InitHwResponse { + let init = init_trezor_rpc(mm, coin).await; + let init: RpcV2Response = json::from_value(init).unwrap(); + let timeout = wait_until_ms(timeout * 1000); + + loop { + if now_ms() > timeout { + panic!("{} init_trezor_rpc timed out", coin); + } + + let ret = init_trezor_status_rpc(mm, init.result.task_id).await; + log!("init_trezor_status_rpc: {:?}", ret); + let ret: RpcV2Response = json::from_value(ret).unwrap(); + match ret.result { + InitTrezorStatus::Ok(result) => break result, + InitTrezorStatus::Error(e) => panic!("{} trezor initialization error {:?}", coin, e), + _ => Timer::sleep(1.).await, + } + } +} + +/// Helper to run init withdraw and wait for completion +async fn init_withdraw_loop_rpc( + mm: &MarketMakerIt, + coin: &str, + to: &str, + amount: &str, + from: Option, +) -> TransactionDetails { + let init = init_withdraw(mm, coin, to, amount, from).await; + let init: RpcV2Response = json::from_value(init).unwrap(); + let timeout = wait_until_ms(150000); + + loop { + if now_ms() > timeout { + panic!("{} init_withdraw timed out", coin); + } + + let status = withdraw_status(mm, init.result.task_id).await; + log!("Withdraw status {}", json::to_string(&status).unwrap()); + let status: RpcV2Response = json::from_value(status).unwrap(); + match status.result { + WithdrawStatus::Ok(result) => break result, + WithdrawStatus::Error(e) => panic!("{} withdraw error {:?}", coin, e), + _ => Timer::sleep(1.).await, + } + } +} + +/// Tool to run withdraw rpc from trezor device or emulator segwit account +/// run cargo test with '--ignored' option +/// to use trezor emulator add '--features trezor-udp' option to cargo test params +/// Sample (for emulator): +/// cargo test -p mm2_main --features trezor-udp -- --nocapture --ignored test_withdraw_from_trezor_segwit_no_rpc +#[test] +#[ignore] +#[cfg(all(not(target_arch = "wasm32")))] +fn test_withdraw_from_trezor_segwit_rpc() { + let default_passphrase = "123"; // TODO: remove when we allow hardware wallet init w/o seed + let ticker = "tBTC-Segwit"; + let mut coin_conf = tbtc_segwit_conf(); + coin_conf["trezor_coin"] = "Testnet".into(); + + // start bob + let conf = Mm2TestConf::seednode(default_passphrase, &json!([coin_conf])); + let mm_bob = block_on(MarketMakerIt::start_async(conf.conf, conf.rpc_password, None)).unwrap(); + + let (_bob_dump_log, _bob_dump_dashboard) = mm_bob.mm_dump(); + log!("Bob log path: {}", mm_bob.log_path.display()); + + block_on(init_trezor_loop_rpc(&mm_bob, ticker, 60)); + + let utxo_bob = block_on(enable_utxo_v2_electrum( + &mm_bob, + ticker, + tbtc_electrums(), + 80, + Some("Trezor"), + )); + log!("enable UTXO bob {:?}", utxo_bob); + + let tx_details = block_on(init_withdraw_loop_rpc( + &mm_bob, + ticker, + "tb1q3zkv6g29ku3jh9vdkhxlpyek44se2s0zrv7ctn", + "0.00001", + Some(json!({"derivation_path": "m/84'/1'/0'/0/0"})), + )); + log!("tx_hex={}", serde_json::to_string(&tx_details.tx_hex).unwrap()); +} + +/// Tool to run withdraw rpc from trezor device or emulator p2pkh account +/// run cargo test with '--ignored' option +/// to use trezor emulator add '--features trezor-udp' option to cargo test params +/// Sample (for emulator): +/// cargo test -p mm2_main --features trezor-udp -- --nocapture --ignored test_withdraw_from_trezor_p2pkh_rpc +#[test] +#[ignore] +#[cfg(all(not(target_arch = "wasm32")))] +fn test_withdraw_from_trezor_p2pkh_rpc() { + let default_passphrase = "123"; // TODO: remove when we allow hardware wallet init w/o seed + let ticker = "tBTC"; + let mut coin_conf = tbtc_legacy_conf(); + coin_conf["trezor_coin"] = "Testnet".into(); + coin_conf["derivation_path"] = "m/44'/1'".into(); + + // start bob + let conf = Mm2TestConf::seednode(default_passphrase, &json!([coin_conf])); + let mm_bob = block_on(MarketMakerIt::start_async(conf.conf, conf.rpc_password, None)).unwrap(); + + let (_bob_dump_log, _bob_dump_dashboard) = mm_bob.mm_dump(); + log!("Bob log path: {}", mm_bob.log_path.display()); + + block_on(init_trezor_loop_rpc(&mm_bob, ticker, 60)); + + let utxo_bob = block_on(enable_utxo_v2_electrum( + &mm_bob, + ticker, + tbtc_electrums(), + 80, + Some("Trezor"), + )); + log!("enable UTXO bob {:?}", utxo_bob); + + let tx_details = block_on(init_withdraw_loop_rpc( + &mm_bob, + ticker, + "miuSj7rXDxbaHsqf1GmoKkygTBnoi3iwzj", + "0.00001", + Some(json!({"derivation_path": "m/44'/1'/0'/0/0"})), + )); + log!("tx_hex={}", serde_json::to_string(&tx_details.tx_hex).unwrap()); +} diff --git a/mm2src/mm2_main/tests/mm2_tests/z_coin_tests.rs b/mm2src/mm2_main/tests/mm2_tests/z_coin_tests.rs index 7fa96a4b03..4f84176fea 100644 --- a/mm2src/mm2_main/tests/mm2_tests/z_coin_tests.rs +++ b/mm2src/mm2_main/tests/mm2_tests/z_coin_tests.rs @@ -26,7 +26,7 @@ const ZOMBIE_TRADE_BOB_SEED: &str = "RICK ZOMBIE BOB"; const ZOMBIE_TRADE_ALICE_SEED: &str = "RICK ZOMBIE ALICE"; async fn withdraw(mm: &MarketMakerIt, coin: &str, to: &str, amount: &str) -> TransactionDetails { - let init = init_withdraw(mm, coin, to, amount).await; + let init = init_withdraw(mm, coin, to, amount, None).await; let init: RpcV2Response = json::from_value(init).unwrap(); let timeout = wait_until_ms(150000); diff --git a/mm2src/mm2_test_helpers/src/for_tests.rs b/mm2src/mm2_test_helpers/src/for_tests.rs index 02b204693a..a3028632a3 100644 --- a/mm2src/mm2_test_helpers/src/for_tests.rs +++ b/mm2src/mm2_test_helpers/src/for_tests.rs @@ -741,6 +741,24 @@ pub fn tbtc_with_spv_conf() -> Json { }) } +pub fn tbtc_legacy_conf() -> Json { + json!({ + "coin": "tBTC", + "asset":"tBTC", + "pubtype": 111, + "p2shtype": 196, + "wiftype": 239, + "segwit": false, + "bech32_hrp": "tb", + "txfee": 0, + "estimate_fee_mode": "ECONOMICAL", + "required_confirmations": 0, + "protocol": { + "type": "UTXO" + } + }) +} + pub fn eth_testnet_conf() -> Json { json!({ "coin": "ETH", @@ -924,11 +942,18 @@ pub fn mm_ctx_with_iguana(passphrase: Option<&str>) -> MmArc { pub fn mm_ctx_with_custom_db() -> MmArc { MmCtxBuilder::new().with_test_db_namespace().into_mm_arc() } #[cfg(not(target_arch = "wasm32"))] -pub fn mm_ctx_with_custom_db() -> MmArc { +pub fn mm_ctx_with_custom_db() -> MmArc { mm_ctx_with_custom_db_with_conf(None) } + +#[cfg(not(target_arch = "wasm32"))] +pub fn mm_ctx_with_custom_db_with_conf(conf: Option) -> MmArc { use db_common::sqlite::rusqlite::Connection; use std::sync::Arc; - let ctx = MmCtxBuilder::new().into_mm_arc(); + let mut ctx_builder = MmCtxBuilder::new(); + if let Some(conf) = conf { + ctx_builder = ctx_builder.with_conf(conf); + } + let ctx = ctx_builder.into_mm_arc(); let connection = Connection::open_in_memory().unwrap(); let _ = ctx.sqlite_connection.pin(Arc::new(Mutex::new(connection))); @@ -2384,7 +2409,7 @@ pub async fn best_orders_v2_by_number( json::from_str(&request.1).unwrap() } -pub async fn init_withdraw(mm: &MarketMakerIt, coin: &str, to: &str, amount: &str) -> Json { +pub async fn init_withdraw(mm: &MarketMakerIt, coin: &str, to: &str, amount: &str, from: Option) -> Json { let request = mm .rpc(&json!({ "userpass": mm.userpass, @@ -2394,6 +2419,7 @@ pub async fn init_withdraw(mm: &MarketMakerIt, coin: &str, to: &str, amount: &st "coin": coin, "to": to, "amount": amount, + "from": from, } })) .await @@ -2839,7 +2865,23 @@ pub async fn enable_tendermint_token(mm: &MarketMakerIt, coin: &str) -> Json { json::from_str(&request.1).unwrap() } -pub async fn init_utxo_electrum(mm: &MarketMakerIt, coin: &str, servers: Vec) -> Json { +pub async fn init_utxo_electrum( + mm: &MarketMakerIt, + coin: &str, + servers: Vec, + priv_key_policy: Option<&str>, +) -> Json { + let mut activation_params = json!({ + "mode": { + "rpc": "Electrum", + "rpc_data": { + "servers": servers + } + } + }); + if let Some(priv_key_policy) = priv_key_policy { + activation_params["priv_key_policy"] = priv_key_policy.into(); + } let request = mm .rpc(&json!({ "userpass": mm.userpass, @@ -2847,14 +2889,7 @@ pub async fn init_utxo_electrum(mm: &MarketMakerIt, coin: &str, servers: Vec Json { + let request = mm + .rpc(&json!({ + "userpass": mm.userpass, + "method": "task::init_trezor::init", + "mmrpc": "2.0", + "params": { + "ticker": coin, + } + })) + .await + .unwrap(); + assert_eq!( + request.0, + StatusCode::OK, + "'task::init_trezor::init' failed: {}", + request.1 + ); + json::from_str(&request.1).unwrap() +} + +/// Helper to call init trezor status +pub async fn init_trezor_status_rpc(mm: &MarketMakerIt, task_id: u64) -> Json { + let request = mm + .rpc(&json!({ + "userpass": mm.userpass, + "method": "task::init_trezor::status", + "mmrpc": "2.0", + "params": { + "task_id": task_id, + } + })) + .await + .unwrap(); + assert_eq!( + request.0, + StatusCode::OK, + "'task::init_trezor::status' failed: {}", + request.1 + ); + json::from_str(&request.1).unwrap() +} diff --git a/mm2src/trezor/Cargo.toml b/mm2src/trezor/Cargo.toml index c0b2196bed..bc7fca098c 100644 --- a/mm2src/trezor/Cargo.toml +++ b/mm2src/trezor/Cargo.toml @@ -22,6 +22,7 @@ serde_derive = "1.0" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] bip32 = { version = "0.2.2", default-features = false, features = ["alloc", "secp256k1-ffi"] } +async-std = { version = "1.5" } [target.'cfg(target_arch = "wasm32")'.dependencies] js-sys = { version = "0.3.27" } @@ -29,3 +30,6 @@ wasm-bindgen = "0.2.86" wasm-bindgen-futures = { version = "0.4.1" } wasm-bindgen-test = { version = "0.3.1" } web-sys = { version = "0.3.55" } + +[features] +trezor-udp = [] # use for tests to connect to trezor emulator over udp diff --git a/mm2src/trezor/src/device_info.rs b/mm2src/trezor/src/device_info.rs index 76f82f6ee2..995eddcb88 100644 --- a/mm2src/trezor/src/device_info.rs +++ b/mm2src/trezor/src/device_info.rs @@ -1,6 +1,6 @@ use crate::proto::messages_management::Features; -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct TrezorDeviceInfo { /// The device model. model: Option, diff --git a/mm2src/trezor/src/error.rs b/mm2src/trezor/src/error.rs index 60397e55a1..e141efe985 100644 --- a/mm2src/trezor/src/error.rs +++ b/mm2src/trezor/src/error.rs @@ -110,3 +110,8 @@ impl From for TrezorError { } } } + +#[cfg(all(not(target_arch = "wasm32"), not(target_os = "ios")))] +impl From for TrezorError { + fn from(e: std::io::Error) -> Self { TrezorError::UnderlyingError(e.to_string()) } +} diff --git a/mm2src/trezor/src/transport/mod.rs b/mm2src/trezor/src/transport/mod.rs index 985c4c871f..eddb856118 100644 --- a/mm2src/trezor/src/transport/mod.rs +++ b/mm2src/trezor/src/transport/mod.rs @@ -4,6 +4,8 @@ use async_trait::async_trait; use rand::RngCore; mod protocol; +#[cfg(all(feature = "trezor-udp", not(target_arch = "wasm32"), not(target_os = "ios")))] +pub mod udp; #[cfg(all(not(target_arch = "wasm32"), not(target_os = "ios")))] pub mod usb; #[cfg(target_arch = "wasm32")] pub mod webusb; @@ -66,3 +68,17 @@ impl SessionId { impl AsRef<[u8]> for SessionId { fn as_ref(&self) -> &[u8] { &self.0 } } + +/// Wrapper to abstract connectivity to usb and emulator devices +#[async_trait] +pub trait ConnectableDeviceWrapper { + type T; + + async fn find_devices() -> TrezorResult> + where + Self: Sized; + + async fn connect(self) -> TrezorResult + where + Self::T: Transport; +} diff --git a/mm2src/trezor/src/transport/usb.rs b/mm2src/trezor/src/transport/usb.rs index 69440c9858..751d5d8e2e 100644 --- a/mm2src/trezor/src/transport/usb.rs +++ b/mm2src/trezor/src/transport/usb.rs @@ -1,7 +1,8 @@ use crate::proto::ProtoMessage; use crate::transport::protocol::{Link, Protocol, ProtocolV1}; -use crate::transport::{Transport, TREZOR_DEVICES}; +use crate::transport::{ConnectableDeviceWrapper, Transport, TREZOR_DEVICES}; use crate::TrezorResult; + use async_trait::async_trait; use hw_common::transport::libusb::{GetDevicesFilters, UsbAvailableDevice as UsbAvailableDeviceImpl, UsbContext, UsbDevice}; @@ -52,7 +53,7 @@ impl Link for UsbLink { } } -pub fn find_devices() -> TrezorResult> { +async fn find_devices() -> TrezorResult> { let context = UsbContext::new()?; let filters = GetDevicesFilters { config_id: CONFIG_ID, @@ -72,7 +73,7 @@ pub struct UsbAvailableDevice(UsbAvailableDeviceImpl); impl UsbAvailableDevice { /// Please note [`hw_common::transport::libusb::UsbAvailableDevice::connect`] spawns a thread. - pub fn connect(self) -> TrezorResult { + async fn connect(self) -> TrezorResult { let link = UsbLink { device: self.0.connect()?, }; @@ -91,3 +92,17 @@ fn is_trezor(device: &UsbAvailableDeviceImpl) -> bool { .iter() .any(|expected| vendor_id == expected.vendor_id && product_id == expected.product_id) } + +#[async_trait] +impl ConnectableDeviceWrapper for UsbAvailableDevice { + type T = UsbTransport; + + async fn find_devices() -> TrezorResult> + where + Self: Sized, + { + crate::transport::usb::find_devices().await + } + + async fn connect(self) -> TrezorResult { UsbAvailableDevice::connect(self).await } +} From 642a2a5f3418ca6476ee47e3a03ab5bc8b6e65c4 Mon Sep 17 00:00:00 2001 From: dimxy Date: Wed, 18 Oct 2023 18:32:10 +0500 Subject: [PATCH 06/17] refactor trezor ConnectableDeviceWrapper (use borrow instead of consume, better naming and constraints, thanks to shamardy) --- mm2src/crypto/src/hw_client.rs | 5 ++--- mm2src/hw_common/src/transport/libusb.rs | 6 +++--- mm2src/trezor/src/transport/mod.rs | 6 ++---- mm2src/trezor/src/transport/udp.rs | 10 +++++----- mm2src/trezor/src/transport/usb.rs | 8 ++++---- 5 files changed, 16 insertions(+), 19 deletions(-) diff --git a/mm2src/crypto/src/hw_client.rs b/mm2src/crypto/src/hw_client.rs index eda8caf36b..42e92e4547 100644 --- a/mm2src/crypto/src/hw_client.rs +++ b/mm2src/crypto/src/hw_client.rs @@ -124,12 +124,11 @@ impl HwClient { ) -> MmResult> { use common::custom_futures::timeout::TimeoutError; use common::executor::Timer; - use trezor::transport::{ConnectableDeviceWrapper, Transport}; + use trezor::transport::ConnectableDeviceWrapper; async fn try_to_connect() -> HwResult> where - C: ConnectableDeviceWrapper, - ::T: Transport + Sync + Send + 'static, + C: ConnectableDeviceWrapper + 'static, { let mut devices = C::find_devices().await?; if devices.is_empty() { diff --git a/mm2src/hw_common/src/transport/libusb.rs b/mm2src/hw_common/src/transport/libusb.rs index da33494dc5..da303f4919 100644 --- a/mm2src/hw_common/src/transport/libusb.rs +++ b/mm2src/hw_common/src/transport/libusb.rs @@ -127,7 +127,7 @@ pub struct UsbAvailableDevice { } impl UsbAvailableDevice { - pub fn connect(self) -> UsbResult { + pub fn connect(&self) -> UsbResult { // This is a non-blocking function; no requests are sent over the bus. let mut device_handle = self.device.open().map_to_mm(UsbError::ErrorOpeningDevice)?; // Claiming of interfaces is a purely logical operation. @@ -277,7 +277,7 @@ impl UsbDevice { fn endpoint_number(&self) -> u8 { self.device_info.interface_info.endpoint_number } } -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] pub struct UsbDeviceInfo { pub vendor_id: u16, pub product_id: u16, @@ -286,7 +286,7 @@ pub struct UsbDeviceInfo { pub interface_info: UsbDeviceInterfaceInfo, } -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] pub struct UsbDeviceInterfaceInfo { pub interface_number: u8, pub endpoint_number: u8, diff --git a/mm2src/trezor/src/transport/mod.rs b/mm2src/trezor/src/transport/mod.rs index eddb856118..30c16a4143 100644 --- a/mm2src/trezor/src/transport/mod.rs +++ b/mm2src/trezor/src/transport/mod.rs @@ -72,13 +72,11 @@ impl AsRef<[u8]> for SessionId { /// Wrapper to abstract connectivity to usb and emulator devices #[async_trait] pub trait ConnectableDeviceWrapper { - type T; + type TransportType: Transport + Sync + Send; async fn find_devices() -> TrezorResult> where Self: Sized; - async fn connect(self) -> TrezorResult - where - Self::T: Transport; + async fn connect(&self) -> TrezorResult; } diff --git a/mm2src/trezor/src/transport/udp.rs b/mm2src/trezor/src/transport/udp.rs index 45e6f3ab63..d47ae1f31c 100644 --- a/mm2src/trezor/src/transport/udp.rs +++ b/mm2src/trezor/src/transport/udp.rs @@ -43,8 +43,8 @@ pub struct UdpAvailableDevice { impl UdpAvailableDevice { /// Connect to the device. - async fn connect(self) -> TrezorResult { - let transport = UdpTransport::connect(&self).await?; + async fn connect(&self) -> TrezorResult { + let transport = UdpTransport::connect(self).await?; Ok(transport) } } @@ -160,14 +160,14 @@ impl Transport for UdpTransport { #[async_trait] impl ConnectableDeviceWrapper for UdpAvailableDevice { - type T = UdpTransport; + type TransportType = UdpTransport; async fn find_devices() -> TrezorResult> where Self: Sized, { - crate::transport::udp::find_devices().await + find_devices().await } - async fn connect(self) -> TrezorResult { UdpAvailableDevice::connect(self).await } + async fn connect(&self) -> TrezorResult { UdpAvailableDevice::connect(self).await } } diff --git a/mm2src/trezor/src/transport/usb.rs b/mm2src/trezor/src/transport/usb.rs index 751d5d8e2e..cfdba0d36c 100644 --- a/mm2src/trezor/src/transport/usb.rs +++ b/mm2src/trezor/src/transport/usb.rs @@ -73,7 +73,7 @@ pub struct UsbAvailableDevice(UsbAvailableDeviceImpl); impl UsbAvailableDevice { /// Please note [`hw_common::transport::libusb::UsbAvailableDevice::connect`] spawns a thread. - async fn connect(self) -> TrezorResult { + async fn connect(&self) -> TrezorResult { let link = UsbLink { device: self.0.connect()?, }; @@ -95,14 +95,14 @@ fn is_trezor(device: &UsbAvailableDeviceImpl) -> bool { #[async_trait] impl ConnectableDeviceWrapper for UsbAvailableDevice { - type T = UsbTransport; + type TransportType = UsbTransport; async fn find_devices() -> TrezorResult> where Self: Sized, { - crate::transport::usb::find_devices().await + find_devices().await } - async fn connect(self) -> TrezorResult { UsbAvailableDevice::connect(self).await } + async fn connect(&self) -> TrezorResult { UsbAvailableDevice::connect(self).await } } From 762c3d68c15e4018d34930b17e9a93e3111bd520 Mon Sep 17 00:00:00 2001 From: dimxy Date: Wed, 1 Nov 2023 21:59:47 +0500 Subject: [PATCH 07/17] add mm2 stop for trezor tests --- mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs | 2 ++ 1 file changed, 2 insertions(+) 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 852fa66daf..79e7868756 100644 --- a/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs +++ b/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs @@ -8124,6 +8124,7 @@ fn test_withdraw_from_trezor_segwit_rpc() { Some(json!({"derivation_path": "m/84'/1'/0'/0/0"})), )); log!("tx_hex={}", serde_json::to_string(&tx_details.tx_hex).unwrap()); + block_on(mm_bob.stop()).unwrap(); } /// Tool to run withdraw rpc from trezor device or emulator p2pkh account @@ -8167,4 +8168,5 @@ fn test_withdraw_from_trezor_p2pkh_rpc() { Some(json!({"derivation_path": "m/44'/1'/0'/0/0"})), )); log!("tx_hex={}", serde_json::to_string(&tx_details.tx_hex).unwrap()); + block_on(mm_bob.stop()).unwrap(); } From dc0e04dc34ae4c7dd11baa85c1d988fc2ff3ddee Mon Sep 17 00:00:00 2001 From: dimxy Date: Thu, 2 Nov 2023 14:57:10 +0500 Subject: [PATCH 08/17] fix fmt --- .../tests/mm2_tests/mm2_tests_inner.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) 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 79e7868756..6bb4ab2fb0 100644 --- a/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs +++ b/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs @@ -28,17 +28,18 @@ use mm2_test_helpers::for_tests::check_stats_swap_status; #[cfg(all(not(target_arch = "wasm32")))] use mm2_test_helpers::for_tests::{btc_segwit_conf, btc_with_spv_conf, btc_with_sync_starting_header, check_recent_swaps, enable_eth_coin, enable_qrc20, eth_jst_testnet_conf, - eth_testnet_conf, find_metrics_in_json, from_env_file, get_shared_db_id, mm_spat, - morty_conf, rick_conf, sign_message, start_swaps, tbtc_segwit_conf, - tbtc_with_spv_conf, test_qrc20_history_impl, tqrc20_conf, verify_message, + eth_testnet_conf, find_metrics_in_json, from_env_file, get_shared_db_id, + init_trezor_rpc, init_trezor_status_rpc, init_withdraw, + mm_ctx_with_custom_db_with_conf, mm_spat, morty_conf, rick_conf, sign_message, + start_swaps, tbtc_legacy_conf, tbtc_segwit_conf, tbtc_with_spv_conf, + test_qrc20_history_impl, tqrc20_conf, verify_message, wait_for_swap_contract_negotiation, wait_for_swap_negotiation_failure, wait_for_swaps_finish_and_check_status, wait_till_history_has_records, - MarketMakerIt, Mm2InitPrivKeyPolicy, Mm2TestConf, Mm2TestConfForSwap, RaiiDump, - DOC_ELECTRUM_ADDRS, ETH_DEV_NODES, ETH_DEV_SWAP_CONTRACT, ETH_DEV_TOKEN_CONTRACT, - ETH_MAINNET_NODE, ETH_MAINNET_SWAP_CONTRACT, MARTY_ELECTRUM_ADDRS, MORTY, - QRC20_ELECTRUMS, RICK, RICK_ELECTRUM_ADDRS, TBTC_ELECTRUMS, T_BCH_ELECTRUMS, - init_trezor_rpc, init_trezor_status_rpc, mm_ctx_with_custom_db_with_conf, - init_withdraw, withdraw_status, tbtc_legacy_conf}; + withdraw_status, MarketMakerIt, Mm2InitPrivKeyPolicy, Mm2TestConf, + Mm2TestConfForSwap, RaiiDump, DOC_ELECTRUM_ADDRS, ETH_DEV_NODES, + ETH_DEV_SWAP_CONTRACT, ETH_DEV_TOKEN_CONTRACT, ETH_MAINNET_NODE, + ETH_MAINNET_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 rpc_task::{rpc_common::RpcTaskStatusRequest, RpcTaskStatus}; From 6e0de544f935126b814352635161efa80cc8be0e Mon Sep 17 00:00:00 2001 From: dimxy Date: Thu, 2 Nov 2023 15:43:41 +0500 Subject: [PATCH 09/17] fix use mm_ctx_with_custom_db_with_conf for non wasm --- mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 6bb4ab2fb0..1fa1464536 100644 --- a/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs +++ b/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs @@ -26,13 +26,13 @@ use mm2_test_helpers::electrums::*; #[cfg(all(not(target_arch = "wasm32"), not(feature = "zhtlc-native-tests")))] use mm2_test_helpers::for_tests::check_stats_swap_status; #[cfg(all(not(target_arch = "wasm32")))] +use mm2_test_helpers::for_tests::mm_ctx_with_custom_db_with_conf; use mm2_test_helpers::for_tests::{btc_segwit_conf, btc_with_spv_conf, btc_with_sync_starting_header, check_recent_swaps, enable_eth_coin, enable_qrc20, eth_jst_testnet_conf, eth_testnet_conf, find_metrics_in_json, from_env_file, get_shared_db_id, - init_trezor_rpc, init_trezor_status_rpc, init_withdraw, - mm_ctx_with_custom_db_with_conf, mm_spat, morty_conf, rick_conf, sign_message, - start_swaps, tbtc_legacy_conf, tbtc_segwit_conf, tbtc_with_spv_conf, - test_qrc20_history_impl, tqrc20_conf, verify_message, + init_trezor_rpc, init_trezor_status_rpc, init_withdraw, mm_spat, morty_conf, + rick_conf, sign_message, start_swaps, tbtc_legacy_conf, tbtc_segwit_conf, + tbtc_with_spv_conf, test_qrc20_history_impl, tqrc20_conf, verify_message, wait_for_swap_contract_negotiation, wait_for_swap_negotiation_failure, wait_for_swaps_finish_and_check_status, wait_till_history_has_records, withdraw_status, MarketMakerIt, Mm2InitPrivKeyPolicy, Mm2TestConf, From 0a82dfa44924f7ced189cbc5efc3b2181b222384 Mon Sep 17 00:00:00 2001 From: dimxy Date: Mon, 6 Nov 2023 14:32:29 +0500 Subject: [PATCH 10/17] remove unused param in test --- mm2src/coins/utxo.rs | 17 ++++------------- .../tests/mm2_tests/mm2_tests_inner.rs | 18 ++---------------- 2 files changed, 6 insertions(+), 29 deletions(-) diff --git a/mm2src/coins/utxo.rs b/mm2src/coins/utxo.rs index 30f6396e72..79147f034e 100644 --- a/mm2src/coins/utxo.rs +++ b/mm2src/coins/utxo.rs @@ -1964,13 +1964,8 @@ fn parse_hex_encoded_u32(hex_encoded: &str) -> Result> { #[cfg(not(target_arch = "wasm32"))] pub mod for_tests { - use super::UtxoCoinFields; - use crate::rpc_command::init_withdraw::WithdrawStatusRequest; - use crate::rpc_command::init_withdraw::{init_withdraw, withdraw_status}; - use crate::WithdrawFrom; - use crate::{utxo::{utxo_standard::UtxoStandardCoin, UtxoArc}, - WithdrawRequest}; - use crate::{MarketCoinOps, TransactionDetails, WithdrawError}; + use crate::rpc_command::init_withdraw::{init_withdraw, withdraw_status, WithdrawStatusRequest}; + use crate::{TransactionDetails, WithdrawError, WithdrawFrom, WithdrawRequest}; use common::executor::Timer; use common::{now_ms, wait_until_ms}; use mm2_core::mm_ctx::MmArc; @@ -1982,15 +1977,11 @@ pub mod for_tests { /// Helper to call init_withdraw and wait for completion pub async fn test_withdraw_init_loop( ctx: MmArc, - fields: UtxoCoinFields, ticker: &str, to: &str, amount: &str, from_derivation_path: &str, ) -> MmResult { - let arc: UtxoArc = fields.into(); - let coin: UtxoStandardCoin = arc.into(); - let withdraw_req = WithdrawRequest { amount: BigDecimal::from_str(amount).unwrap(), from: Some(WithdrawFrom::DerivationPath { @@ -2006,7 +1997,7 @@ pub mod for_tests { let timeout = wait_until_ms(150000); loop { if now_ms() > timeout { - panic!("{} init_withdraw timed out", coin.ticker()); + panic!("{} init_withdraw timed out", ticker); } let status = withdraw_status(ctx.clone(), WithdrawStatusRequest { task_id: init.task_id, @@ -2020,7 +2011,7 @@ pub mod for_tests { _ => Timer::sleep(1.).await, } } else { - panic!("{} could not get withdraw_status", coin.ticker()) + panic!("{} could not get withdraw_status", ticker) } } } 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 1fa1464536..49faff9853 100644 --- a/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs +++ b/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs @@ -2,10 +2,7 @@ use super::enable_z_coin; use crate::integration_tests_common::*; use coins::utxo::for_tests::test_withdraw_init_loop; -use coins::utxo::{utxo_builder::{UtxoArcBuilder, UtxoCoinBuilder}, - utxo_standard::UtxoStandardCoin, - UtxoActivationParams}; -use coins::PrivKeyBuildPolicy; +use coins::utxo::{utxo_standard::UtxoStandardCoin, UtxoActivationParams}; use coins_activation::{for_tests::init_standalone_coin_loop, InitStandaloneCoinReq}; use common::executor::Timer; use common::now_ms; @@ -7997,7 +7994,6 @@ fn test_withdraw_from_trezor_segwit_no_rpc() { let mm_conf = json!({ "coins": [coin_conf] }); let ctx = block_on(mm_ctx_with_trezor(mm_conf)); - let priv_key_policy = PrivKeyBuildPolicy::Trezor; let enable_req = json!({ "method": "electrum", "coin": ticker, @@ -8014,18 +8010,8 @@ fn test_withdraw_from_trezor_segwit_no_rpc() { block_on(init_standalone_coin_loop::(ctx.clone(), request)) .expect("coin activation must be successful"); - let builder = UtxoArcBuilder::new( - &ctx, - ticker, - &coin_conf, - &activation_params, - priv_key_policy, - UtxoStandardCoin::from, - ); - let fields = block_on(builder.build_utxo_fields()).unwrap(); let tx_details = block_on(test_withdraw_init_loop( - ctx.clone(), - fields, + ctx, ticker, "tb1q3zkv6g29ku3jh9vdkhxlpyek44se2s0zrv7ctn", "0.00001", From 812a2005d367f11a412046c6c698046ca391903e Mon Sep 17 00:00:00 2001 From: dimxy Date: Thu, 23 Nov 2023 23:33:36 +0500 Subject: [PATCH 11/17] Refactor rpc task_handle and trezor request processor to make them shared for extended use --- mm2src/coins/hd_confirm_address.rs | 27 ++++++------ mm2src/coins/hd_pubkey.rs | 39 ++++++++++------- mm2src/coins/rpc_command/get_new_address.rs | 14 ++++--- .../coins/rpc_command/init_account_balance.rs | 8 +++- .../coins/rpc_command/init_create_account.rs | 10 +++-- .../init_scan_for_new_addresses.rs | 5 ++- mm2src/coins/rpc_command/init_withdraw.rs | 9 ++-- mm2src/coins/utxo/qtum.rs | 4 +- mm2src/coins/utxo/utxo_common.rs | 4 +- mm2src/coins/utxo/utxo_standard.rs | 4 +- mm2src/coins/utxo/utxo_withdraw.rs | 30 +++++++++---- mm2src/coins/z_coin.rs | 5 ++- mm2src/coins_activation/src/l2/init_l2.rs | 10 +++-- .../src/lightning_activation.rs | 5 ++- .../standalone_coin/init_standalone_coin.rs | 17 ++++---- .../src/standalone_coin/mod.rs | 2 +- .../src/utxo_activation/common_impl.rs | 9 ++-- .../utxo_activation/init_qtum_activation.rs | 6 ++- .../init_utxo_standard_activation.rs | 6 ++- .../coins_activation/src/z_coin_activation.rs | 6 ++- mm2src/crypto/src/crypto_ctx.rs | 21 ++++------ mm2src/crypto/src/hw_client.rs | 12 ++++-- mm2src/crypto/src/hw_ctx.rs | 42 ++++++++++--------- mm2src/crypto/src/hw_error.rs | 1 + mm2src/crypto/src/hw_rpc_task.rs | 21 ++++++---- mm2src/mm2_main/src/lp_init/init_hw.rs | 12 ++++-- mm2src/mm2_main/src/lp_init/init_metamask.rs | 3 +- mm2src/mm2_main/src/lp_native_dex.rs | 1 + mm2src/rpc_task/src/handle.rs | 7 ++-- mm2src/rpc_task/src/lib.rs | 2 +- mm2src/rpc_task/src/manager.rs | 6 +-- mm2src/rpc_task/src/task.rs | 4 +- mm2src/trezor/src/client.rs | 17 ++++++-- mm2src/trezor/src/error.rs | 2 + mm2src/trezor/src/response.rs | 17 ++++---- mm2src/trezor/src/response_processor.rs | 16 ++++--- mm2src/trezor/src/trezor_rpc_task.rs | 26 ++++++++---- 37 files changed, 270 insertions(+), 160 deletions(-) diff --git a/mm2src/coins/hd_confirm_address.rs b/mm2src/coins/hd_confirm_address.rs index d6ee019855..2c8c9b40f3 100644 --- a/mm2src/coins/hd_confirm_address.rs +++ b/mm2src/coins/hd_confirm_address.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use async_trait::async_trait; use bip32::DerivationPath; use crypto::hw_rpc_task::HwConnectStatuses; @@ -7,7 +9,7 @@ use crypto::{CryptoCtx, CryptoCtxError, HardwareWalletArc, HwError, HwProcessing use enum_from::{EnumFromInner, EnumFromStringify}; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; -use rpc_task::{RpcTask, RpcTaskError, RpcTaskHandle}; +use rpc_task::{RpcTask, RpcTaskError, RpcTaskHandleShared}; const SHOW_ADDRESS_ON_DISPLAY: bool = true; @@ -43,6 +45,7 @@ impl From> for HDConfirmAddressError { match e { HwProcessingError::HwError(hw) => HDConfirmAddressError::from(hw), HwProcessingError::ProcessorError(rpc) => HDConfirmAddressError::RpcTaskError(rpc), + HwProcessingError::InternalError(err) => HDConfirmAddressError::Internal(err), } } } @@ -66,16 +69,16 @@ pub trait HDConfirmAddress: Sync { ) -> MmResult<(), HDConfirmAddressError>; } -pub enum RpcTaskConfirmAddress<'task, Task: RpcTask> { +pub enum RpcTaskConfirmAddress { Trezor { hw_ctx: HardwareWalletArc, - task_handle: &'task RpcTaskHandle, + task_handle: RpcTaskHandleShared, statuses: HwConnectStatuses, }, } #[async_trait] -impl<'task, Task> HDConfirmAddress for RpcTaskConfirmAddress<'task, Task> +impl HDConfirmAddress for RpcTaskConfirmAddress where Task: RpcTask, Task::InProgressStatus: ConfirmAddressStatus, @@ -95,7 +98,7 @@ where } => { Self::confirm_utxo_address_with_trezor( hw_ctx, - task_handle, + task_handle.clone(), statuses, trezor_utxo_coin, derivation_path, @@ -107,7 +110,7 @@ where } } -impl<'task, Task> RpcTaskConfirmAddress<'task, Task> +impl RpcTaskConfirmAddress where Task: RpcTask, Task::InProgressStatus: ConfirmAddressStatus, @@ -115,9 +118,9 @@ where { pub fn new( ctx: &MmArc, - task_handle: &'task RpcTaskHandle, + task_handle: RpcTaskHandleShared, statuses: HwConnectStatuses, - ) -> MmResult, HDConfirmAddressError> { + ) -> MmResult, HDConfirmAddressError> { let crypto_ctx = CryptoCtx::from_ctx(ctx)?; let hw_ctx = crypto_ctx .hw_ctx() @@ -131,24 +134,24 @@ where async fn confirm_utxo_address_with_trezor( hw_ctx: &HardwareWalletArc, - task_handle: &RpcTaskHandle, + task_handle: RpcTaskHandleShared, connect_statuses: &HwConnectStatuses, trezor_coin: String, derivation_path: DerivationPath, expected_address: String, ) -> MmResult<(), HDConfirmAddressError> { - let mut trezor_session = hw_ctx.trezor().await?; - let confirm_statuses = TrezorRequestStatuses { on_button_request: Task::InProgressStatus::confirm_addr_status(expected_address.clone()), ..connect_statuses.to_trezor_request_statuses() }; let pubkey_processor = TrezorRpcTaskProcessor::new(task_handle, confirm_statuses); + let pubkey_processor = Arc::new(pubkey_processor); + let mut trezor_session = hw_ctx.trezor(pubkey_processor.clone()).await?; let address = trezor_session .get_utxo_address(derivation_path, trezor_coin, SHOW_ADDRESS_ON_DISPLAY) .await? - .process(&pubkey_processor) + .process(pubkey_processor.clone()) .await?; if address != expected_address { diff --git a/mm2src/coins/hd_pubkey.rs b/mm2src/coins/hd_pubkey.rs index 9bb122bee1..667b9bc1f8 100644 --- a/mm2src/coins/hd_pubkey.rs +++ b/mm2src/coins/hd_pubkey.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use crate::hd_wallet::NewAccountCreatingError; use async_trait::async_trait; use crypto::hw_rpc_task::HwConnectStatuses; @@ -8,7 +10,7 @@ use crypto::{CryptoCtx, CryptoCtxError, DerivationPath, EcdsaCurve, HardwareWall XPub, XPubConverter, XpubError}; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; -use rpc_task::{RpcTask, RpcTaskError, RpcTaskHandle}; +use rpc_task::{RpcTask, RpcTaskError, RpcTaskHandleShared}; const SHOW_PUBKEY_ON_DISPLAY: bool = false; @@ -48,6 +50,7 @@ impl From> for HDExtractPubkeyError { match e { HwProcessingError::HwError(hw) => HDExtractPubkeyError::from(hw), HwProcessingError::ProcessorError(rpc) => HDExtractPubkeyError::RpcTaskError(rpc), + HwProcessingError::InternalError(err) => HDExtractPubkeyError::Internal(err), } } } @@ -93,16 +96,16 @@ pub trait HDXPubExtractor: Sync { ) -> MmResult; } -pub enum RpcTaskXPubExtractor<'task, Task: RpcTask> { +pub enum RpcTaskXPubExtractor { Trezor { hw_ctx: HardwareWalletArc, - task_handle: &'task RpcTaskHandle, + task_handle: RpcTaskHandleShared, statuses: HwConnectStatuses, }, } #[async_trait] -impl<'task, Task> HDXPubExtractor for RpcTaskXPubExtractor<'task, Task> +impl HDXPubExtractor for RpcTaskXPubExtractor where Task: RpcTask, Task::UserAction: TryIntoUserAction + Send, @@ -118,23 +121,29 @@ where task_handle, statuses, } => { - Self::extract_utxo_xpub_from_trezor(hw_ctx, task_handle, statuses, trezor_utxo_coin, derivation_path) - .await + Self::extract_utxo_xpub_from_trezor( + hw_ctx, + task_handle.clone(), + statuses, + trezor_utxo_coin, + derivation_path, + ) + .await }, } } } -impl<'task, Task> RpcTaskXPubExtractor<'task, Task> +impl RpcTaskXPubExtractor where Task: RpcTask, Task::UserAction: TryIntoUserAction + Send, { pub fn new( ctx: &MmArc, - task_handle: &'task RpcTaskHandle, + task_handle: RpcTaskHandleShared, statuses: HwConnectStatuses, - ) -> MmResult, HDExtractPubkeyError> { + ) -> MmResult, HDExtractPubkeyError> { let crypto_ctx = CryptoCtx::from_ctx(ctx)?; let hw_ctx = crypto_ctx .hw_ctx() @@ -149,22 +158,22 @@ where /// Constructs an Xpub extractor without checking if the MarketMaker is initialized with a hardware wallet. pub fn new_unchecked( ctx: &MmArc, - task_handle: &'task RpcTaskHandle, + task_handle: RpcTaskHandleShared, statuses: HwConnectStatuses, - ) -> XPubExtractorUnchecked> { + ) -> XPubExtractorUnchecked> { XPubExtractorUnchecked(Self::new(ctx, task_handle, statuses)) } async fn extract_utxo_xpub_from_trezor( hw_ctx: &HardwareWalletArc, - task_handle: &RpcTaskHandle, + task_handle: RpcTaskHandleShared, statuses: &HwConnectStatuses, trezor_coin: String, derivation_path: DerivationPath, ) -> MmResult { - let mut trezor_session = hw_ctx.trezor().await?; - let pubkey_processor = TrezorRpcTaskProcessor::new(task_handle, statuses.to_trezor_request_statuses()); + let pubkey_processor = Arc::new(pubkey_processor); + let mut trezor_session = hw_ctx.trezor(pubkey_processor.clone()).await?; let xpub = trezor_session .get_public_key( derivation_path, @@ -174,7 +183,7 @@ where IGNORE_XPUB_MAGIC, ) .await? - .process(&pubkey_processor) + .process(pubkey_processor.clone()) .await?; // Despite we pass `IGNORE_XPUB_MAGIC` to the [`TrezorSession::get_public_key`] method, diff --git a/mm2src/coins/rpc_command/get_new_address.rs b/mm2src/coins/rpc_command/get_new_address.rs index 5293d8ff69..4e3ed96586 100644 --- a/mm2src/coins/rpc_command/get_new_address.rs +++ b/mm2src/coins/rpc_command/get_new_address.rs @@ -14,7 +14,9 @@ use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use rpc_task::rpc_common::{CancelRpcTaskError, CancelRpcTaskRequest, InitRpcTaskResponse, RpcTaskStatusError, RpcTaskStatusRequest, RpcTaskUserActionError}; -use rpc_task::{RpcTask, RpcTaskError, RpcTaskHandle, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatus, RpcTaskTypes}; +use rpc_task::{RpcTask, RpcTaskError, RpcTaskHandle, RpcTaskHandleShared, RpcTaskManager, RpcTaskManagerShared, + RpcTaskStatus, RpcTaskTypes}; +use std::sync::Arc; use std::time::Duration; pub type GetNewAddressUserAction = HwRpcTaskUserAction; @@ -22,6 +24,7 @@ pub type GetNewAddressAwaitingStatus = HwRpcTaskAwaitingStatus; pub type GetNewAddressTaskManager = RpcTaskManager; pub type GetNewAddressTaskManagerShared = RpcTaskManagerShared; pub type GetNewAddressTaskHandle = RpcTaskHandle; +pub type GetNewAddressTaskHandleArc = Arc>; pub type GetNewAddressRpcTaskStatus = RpcTaskStatus< GetNewAddressResponse, GetNewAddressRpcError, @@ -193,7 +196,7 @@ impl HttpStatusCode for GetNewAddressRpcError { } } -#[derive(Deserialize)] +#[derive(Deserialize, Clone)] pub struct GetNewAddressRequest { coin: String, #[serde(flatten)] @@ -254,6 +257,7 @@ pub trait GetNewAddressRpcOps { ConfirmAddress: HDConfirmAddress; } +#[derive(Clone)] pub struct InitGetNewAddressTask { ctx: MmArc, coin: MmCoinEnum, @@ -275,12 +279,12 @@ impl RpcTask for InitGetNewAddressTask { // Do nothing if the task has been cancelled. async fn cancel(self) {} - async fn run(&mut self, task_handle: &RpcTaskHandle) -> Result> { + async fn run(&mut self, task_handle: RpcTaskHandleShared) -> Result> { async fn get_new_address_helper( ctx: &MmArc, coin: &Coin, params: GetNewAddressParams, - task_handle: &GetNewAddressTaskHandle, + task_handle: GetNewAddressTaskHandleArc, ) -> MmResult where Coin: GetNewAddressRpcOps + Send + Sync, @@ -294,7 +298,7 @@ impl RpcTask for InitGetNewAddressTask { on_passphrase_request: GetNewAddressAwaitingStatus::EnterTrezorPassphrase, on_ready: GetNewAddressInProgressStatus::RequestingAccountBalance, }; - let confirm_address: RpcTaskConfirmAddress<'_, InitGetNewAddressTask> = + let confirm_address: RpcTaskConfirmAddress = RpcTaskConfirmAddress::new(ctx, task_handle, hw_statuses)?; coin.get_new_address_rpc(params, &confirm_address).await } diff --git a/mm2src/coins/rpc_command/init_account_balance.rs b/mm2src/coins/rpc_command/init_account_balance.rs index 3317acea67..89845b6011 100644 --- a/mm2src/coins/rpc_command/init_account_balance.rs +++ b/mm2src/coins/rpc_command/init_account_balance.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use crate::coin_balance::HDAccountBalance; use crate::rpc_command::hd_account_balance_rpc_error::HDAccountBalanceRpcError; use crate::{lp_coinfind_or_err, CoinsContext, MmCoinEnum}; @@ -14,6 +16,7 @@ pub type AccountBalanceAwaitingStatus = SerdeInfallible; pub type AccountBalanceTaskManager = RpcTaskManager; pub type AccountBalanceTaskManagerShared = RpcTaskManagerShared; pub type InitAccountBalanceTaskHandle = RpcTaskHandle; +pub type InitAccountBalanceTaskHandleShared = Arc; pub type AccountBalanceRpcTaskStatus = RpcTaskStatus< HDAccountBalance, HDAccountBalanceRpcError, @@ -66,7 +69,10 @@ impl RpcTask for InitAccountBalanceTask { // Do nothing if the task has been cancelled. async fn cancel(self) {} - async fn run(&mut self, _task_handle: &InitAccountBalanceTaskHandle) -> Result> { + async fn run( + &mut self, + _task_handle: InitAccountBalanceTaskHandleShared, + ) -> Result> { match self.coin { MmCoinEnum::UtxoCoin(ref utxo) => utxo.init_account_balance_rpc(self.req.params.clone()).await, MmCoinEnum::QtumCoin(ref qtum) => qtum.init_account_balance_rpc(self.req.params.clone()).await, diff --git a/mm2src/coins/rpc_command/init_create_account.rs b/mm2src/coins/rpc_command/init_create_account.rs index 82f99587b6..aa965ff9e2 100644 --- a/mm2src/coins/rpc_command/init_create_account.rs +++ b/mm2src/coins/rpc_command/init_create_account.rs @@ -24,10 +24,11 @@ pub type CreateAccountAwaitingStatus = HwRpcTaskAwaitingStatus; pub type CreateAccountTaskManager = RpcTaskManager; pub type CreateAccountTaskManagerShared = RpcTaskManagerShared; pub type CreateAccountTaskHandle = RpcTaskHandle; +pub type CreateAccountTaskHandleShared = Arc; pub type CreateAccountRpcTaskStatus = RpcTaskStatus; -type CreateAccountXPubExtractor<'task> = RpcTaskXPubExtractor<'task, InitCreateAccountTask>; +type CreateAccountXPubExtractor = RpcTaskXPubExtractor; #[derive(Clone, Debug, Display, EnumFromTrait, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] @@ -155,7 +156,7 @@ impl HttpStatusCode for CreateAccountRpcError { } } -#[derive(Deserialize)] +#[derive(Deserialize, Clone)] pub struct CreateNewAccountRequest { coin: String, #[serde(flatten)] @@ -210,6 +211,7 @@ pub trait InitCreateAccountRpcOps { async fn revert_creating_account(&self, account_id: u32); } +#[derive(Clone)] pub struct InitCreateAccountTask { ctx: MmArc, coin: MmCoinEnum, @@ -241,13 +243,13 @@ impl RpcTask for InitCreateAccountTask { }; } - async fn run(&mut self, task_handle: &CreateAccountTaskHandle) -> Result> { + async fn run(&mut self, task_handle: CreateAccountTaskHandleShared) -> Result> { async fn create_new_account_helper( ctx: &MmArc, coin: &Coin, params: CreateNewAccountParams, state: CreateAccountState, - task_handle: &CreateAccountTaskHandle, + task_handle: CreateAccountTaskHandleShared, ) -> MmResult where Coin: InitCreateAccountRpcOps + Send + Sync, diff --git a/mm2src/coins/rpc_command/init_scan_for_new_addresses.rs b/mm2src/coins/rpc_command/init_scan_for_new_addresses.rs index d0aaa8423f..a028dfb4f4 100644 --- a/mm2src/coins/rpc_command/init_scan_for_new_addresses.rs +++ b/mm2src/coins/rpc_command/init_scan_for_new_addresses.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use crate::coin_balance::HDAddressBalance; use crate::rpc_command::hd_account_balance_rpc_error::HDAccountBalanceRpcError; use crate::{lp_coinfind_or_err, CoinsContext, MmCoinEnum}; @@ -15,6 +17,7 @@ pub type ScanAddressesAwaitingStatus = SerdeInfallible; pub type ScanAddressesTaskManager = RpcTaskManager; pub type ScanAddressesTaskManagerShared = RpcTaskManagerShared; pub type ScanAddressesTaskHandle = RpcTaskHandle; +pub type ScanAddressesTaskHandleShared = Arc; pub type ScanAddressesRpcTaskStatus = RpcTaskStatus< ScanAddressesResponse, HDAccountBalanceRpcError, @@ -78,7 +81,7 @@ impl RpcTask for InitScanAddressesTask { // Do nothing if the task has been cancelled. async fn cancel(self) {} - async fn run(&mut self, _task_handle: &ScanAddressesTaskHandle) -> Result> { + async fn run(&mut self, _task_handle: ScanAddressesTaskHandleShared) -> Result> { match self.coin { MmCoinEnum::UtxoCoin(ref utxo) => utxo.init_scan_for_new_addresses_rpc(self.req.params.clone()).await, MmCoinEnum::QtumCoin(ref qtum) => qtum.init_scan_for_new_addresses_rpc(self.req.params.clone()).await, diff --git a/mm2src/coins/rpc_command/init_withdraw.rs b/mm2src/coins/rpc_command/init_withdraw.rs index c9ba606250..4977553036 100644 --- a/mm2src/coins/rpc_command/init_withdraw.rs +++ b/mm2src/coins/rpc_command/init_withdraw.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use crate::{lp_coinfind_or_err, CoinsContext, MmCoinEnum, WithdrawError}; use crate::{TransactionDetails, WithdrawRequest}; use async_trait::async_trait; @@ -19,6 +21,7 @@ pub type WithdrawUserActionRequest = HwRpcTaskUserActionRequest; pub type WithdrawTaskManager = RpcTaskManager; pub type WithdrawTaskManagerShared = RpcTaskManagerShared; pub type WithdrawTaskHandle = RpcTaskHandle; +pub type WithdrawTaskHandleShared = Arc; pub type WithdrawRpcStatus = RpcTaskStatusAlias; pub type WithdrawInitResult = Result>; @@ -28,7 +31,7 @@ pub trait CoinWithdrawInit { fn init_withdraw( ctx: MmArc, req: WithdrawRequest, - rpc_task_handle: &WithdrawTaskHandle, + rpc_task_handle: WithdrawTaskHandleShared, ) -> WithdrawInitResult; } @@ -101,7 +104,7 @@ pub trait InitWithdrawCoin { &self, ctx: MmArc, req: WithdrawRequest, - task_handle: &WithdrawTaskHandle, + task_handle: WithdrawTaskHandleShared, ) -> Result>; } @@ -126,7 +129,7 @@ impl RpcTask for WithdrawTask { // Do nothing if the task has been cancelled. async fn cancel(self) {} - async fn run(&mut self, task_handle: &WithdrawTaskHandle) -> Result> { + async fn run(&mut self, task_handle: WithdrawTaskHandleShared) -> Result> { let ctx = self.ctx.clone(); let request = self.request.clone(); match self.coin { diff --git a/mm2src/coins/utxo/qtum.rs b/mm2src/coins/utxo/qtum.rs index 72a5727490..1ab7fe1a0d 100644 --- a/mm2src/coins/utxo/qtum.rs +++ b/mm2src/coins/utxo/qtum.rs @@ -18,7 +18,7 @@ use crate::rpc_command::init_create_account::{self, CreateAccountRpcError, Creat InitCreateAccountRpcOps}; use crate::rpc_command::init_scan_for_new_addresses::{self, InitScanAddressesRpcOps, ScanAddressesParams, ScanAddressesResponse}; -use crate::rpc_command::init_withdraw::{InitWithdrawCoin, WithdrawTaskHandle}; +use crate::rpc_command::init_withdraw::{InitWithdrawCoin, WithdrawTaskHandleShared}; use crate::tx_history_storage::{GetTxHistoryFilters, WalletId}; use crate::utxo::utxo_builder::{MergeUtxoArcOps, UtxoCoinBuildError, UtxoCoinBuilder, UtxoCoinBuilderCommonOps, UtxoFieldsWithGlobalHDBuilder, UtxoFieldsWithHardwareWalletBuilder, @@ -1013,7 +1013,7 @@ impl InitWithdrawCoin for QtumCoin { &self, ctx: MmArc, req: WithdrawRequest, - task_handle: &WithdrawTaskHandle, + task_handle: WithdrawTaskHandleShared, ) -> Result> { utxo_common::init_withdraw(ctx, self.clone(), req, task_handle).await } diff --git a/mm2src/coins/utxo/utxo_common.rs b/mm2src/coins/utxo/utxo_common.rs index ed4d901795..c0f08f5dae 100644 --- a/mm2src/coins/utxo/utxo_common.rs +++ b/mm2src/coins/utxo/utxo_common.rs @@ -8,7 +8,7 @@ use crate::hd_wallet::{AccountUpdatingError, AddressDerivingResult, HDAccountMut NewAccountCreatingError, NewAddressDeriveConfirmError, NewAddressDerivingError}; use crate::hd_wallet_storage::{HDWalletCoinWithStorageOps, HDWalletStorageResult}; use crate::lp_price::get_base_price_in_rel; -use crate::rpc_command::init_withdraw::WithdrawTaskHandle; +use crate::rpc_command::init_withdraw::WithdrawTaskHandleShared; use crate::utxo::rpc_clients::{electrum_script_hash, BlockHashOrHeight, UnspentInfo, UnspentMap, UtxoRpcClientEnum, UtxoRpcClientOps, UtxoRpcResult}; use crate::utxo::spv::SimplePaymentVerification; @@ -3309,7 +3309,7 @@ pub async fn init_withdraw( ctx: MmArc, coin: T, req: WithdrawRequest, - task_handle: &WithdrawTaskHandle, + task_handle: WithdrawTaskHandleShared, ) -> WithdrawResult where T: UtxoCommonOps diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index 12436ce961..77d6855328 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -18,7 +18,7 @@ use crate::rpc_command::init_create_account::{self, CreateAccountRpcError, Creat InitCreateAccountRpcOps}; use crate::rpc_command::init_scan_for_new_addresses::{self, InitScanAddressesRpcOps, ScanAddressesParams, ScanAddressesResponse}; -use crate::rpc_command::init_withdraw::{InitWithdrawCoin, WithdrawTaskHandle}; +use crate::rpc_command::init_withdraw::{InitWithdrawCoin, WithdrawTaskHandleShared}; use crate::tx_history_storage::{GetTxHistoryFilters, WalletId}; use crate::utxo::utxo_builder::{UtxoArcBuilder, UtxoCoinBuilder}; use crate::utxo::utxo_tx_history_v2::{UtxoMyAddressesHistoryError, UtxoTxDetailsError, UtxoTxDetailsParams, @@ -879,7 +879,7 @@ impl InitWithdrawCoin for UtxoStandardCoin { &self, ctx: MmArc, req: WithdrawRequest, - task_handle: &WithdrawTaskHandle, + task_handle: WithdrawTaskHandleShared, ) -> Result> { utxo_common::init_withdraw(ctx, self.clone(), req, task_handle).await } diff --git a/mm2src/coins/utxo/utxo_withdraw.rs b/mm2src/coins/utxo/utxo_withdraw.rs index 8577697a74..3b0c27d16c 100644 --- a/mm2src/coins/utxo/utxo_withdraw.rs +++ b/mm2src/coins/utxo/utxo_withdraw.rs @@ -1,4 +1,4 @@ -use crate::rpc_command::init_withdraw::{WithdrawInProgressStatus, WithdrawTaskHandle}; +use crate::rpc_command::init_withdraw::{WithdrawInProgressStatus, WithdrawTaskHandleShared}; use crate::utxo::utxo_common::{big_decimal_from_sat, UtxoTxBuilder}; use crate::utxo::{output_script, sat_from_big_decimal, ActualTxFee, Address, FeePolicy, GetUtxoListOps, PrivKeyPolicy, UtxoAddressFormat, UtxoCoinFields, UtxoCommonOps, UtxoFeeDetails, UtxoTx, UTXO_LOCK}; @@ -8,6 +8,8 @@ use async_trait::async_trait; use chain::TransactionOutput; use common::log::info; use common::now_sec; +use crypto::hw_rpc_task::HwRpcTaskAwaitingStatus; +use crypto::trezor::trezor_rpc_task::{TrezorRequestStatuses, TrezorRpcTaskProcessor}; use crypto::trezor::{TrezorError, TrezorProcessingError}; use crypto::{from_hw_error, CryptoCtx, CryptoCtxError, DerivationPath, HwError, HwProcessingError, HwRpcError}; use keys::{AddressFormat, AddressHashEnum, KeyPair, Private, Public as PublicKey, Type as ScriptType}; @@ -18,6 +20,7 @@ use rpc_task::RpcTaskError; use script::{Builder, Script, SignatureVersion, TransactionInputSigner}; use serialization::{serialize, serialize_with_flags, SERIALIZE_TRANSACTION_WITNESS}; use std::iter::once; +use std::sync::Arc; use utxo_signer::sign_params::{OutputDestination, SendingOutputInfo, SpendingInputInfo, UtxoSignTxParamsBuilder}; use utxo_signer::{with_key_pair, UtxoSignTxError}; use utxo_signer::{SignPolicy, UtxoSignerOps}; @@ -38,6 +41,7 @@ impl From> for WithdrawError { match e { HwProcessingError::HwError(hw) => WithdrawError::from(hw), HwProcessingError::ProcessorError(rpc_task) => WithdrawError::from(rpc_task), + HwProcessingError::InternalError(err) => WithdrawError::InternalError(err), } } } @@ -222,10 +226,10 @@ where } } -pub struct InitUtxoWithdraw<'a, Coin> { +pub struct InitUtxoWithdraw { ctx: MmArc, coin: Coin, - task_handle: &'a WithdrawTaskHandle, + task_handle: WithdrawTaskHandleShared, req: WithdrawRequest, from_address: Address, /// Displayed [`InitUtxoWithdraw::from_address`]. @@ -237,7 +241,7 @@ pub struct InitUtxoWithdraw<'a, Coin> { } #[async_trait] -impl<'a, Coin> UtxoWithdraw for InitUtxoWithdraw<'a, Coin> +impl UtxoWithdraw for InitUtxoWithdraw where Coin: UtxoCommonOps + GetUtxoListOps + UtxoSignerOps, { @@ -335,7 +339,15 @@ where .. } => SignPolicy::WithKeyPair(activated_key_pair), PrivKeyPolicy::Trezor => { - let trezor_session = hw_ctx.trezor().await?; + let trezor_statuses = TrezorRequestStatuses { + on_button_request: WithdrawInProgressStatus::FollowHwDeviceInstructions, + on_pin_request: HwRpcTaskAwaitingStatus::EnterTrezorPin, + on_passphrase_request: HwRpcTaskAwaitingStatus::EnterTrezorPassphrase, + on_ready: WithdrawInProgressStatus::FollowHwDeviceInstructions, + }; + let sign_processor = TrezorRpcTaskProcessor::new(self.task_handle.clone(), trezor_statuses); + let sign_processor = Arc::new(sign_processor); //as &dyn TrezorRequestProcessor + let trezor_session = hw_ctx.trezor(sign_processor).await?; SignPolicy::WithTrezor(trezor_session) }, #[cfg(target_arch = "wasm32")] @@ -354,13 +366,13 @@ where } } -impl<'a, Coin> InitUtxoWithdraw<'a, Coin> { +impl InitUtxoWithdraw { pub async fn new( ctx: MmArc, coin: Coin, req: WithdrawRequest, - task_handle: &'a WithdrawTaskHandle, - ) -> Result, MmError> + task_handle: WithdrawTaskHandleShared, + ) -> Result, MmError> where Coin: CoinWithDerivationMethod + GetWithdrawSenderAddress
, { @@ -381,7 +393,7 @@ impl<'a, Coin> InitUtxoWithdraw<'a, Coin> { Ok(InitUtxoWithdraw { ctx, coin, - task_handle, + task_handle: task_handle.clone(), req, from_address: from.address, from_address_string, diff --git a/mm2src/coins/z_coin.rs b/mm2src/coins/z_coin.rs index 132f1edf4b..4e91649e08 100644 --- a/mm2src/coins/z_coin.rs +++ b/mm2src/coins/z_coin.rs @@ -1,8 +1,9 @@ use crate::coin_errors::MyAddressError; #[cfg(not(target_arch = "wasm32"))] use crate::my_tx_history_v2::{MyTxHistoryErrorV2, MyTxHistoryRequestV2, MyTxHistoryResponseV2}; +use crate::rpc_command::init_withdraw::WithdrawTaskHandleShared; #[cfg(not(target_arch = "wasm32"))] -use crate::rpc_command::init_withdraw::{InitWithdrawCoin, WithdrawInProgressStatus, WithdrawTaskHandle}; +use crate::rpc_command::init_withdraw::{InitWithdrawCoin, WithdrawInProgressStatus}; use crate::utxo::rpc_clients::{ElectrumRpcRequest, UnspentInfo, UtxoRpcClientEnum, UtxoRpcError, UtxoRpcFut, UtxoRpcResult}; use crate::utxo::utxo_builder::UtxoCoinBuildError; @@ -1935,7 +1936,7 @@ impl InitWithdrawCoin for ZCoin { &self, _ctx: MmArc, req: WithdrawRequest, - task_handle: &WithdrawTaskHandle, + task_handle: WithdrawTaskHandleShared, ) -> Result> { if req.fee.is_some() { return MmError::err(WithdrawError::UnsupportedError( diff --git a/mm2src/coins_activation/src/l2/init_l2.rs b/mm2src/coins_activation/src/l2/init_l2.rs index 00cf6eb8dc..89f3bb5989 100644 --- a/mm2src/coins_activation/src/l2/init_l2.rs +++ b/mm2src/coins_activation/src/l2/init_l2.rs @@ -10,15 +10,18 @@ use common::SuccessResponse; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use rpc_task::rpc_common::{CancelRpcTaskRequest, InitRpcTaskResponse, RpcTaskStatusRequest, RpcTaskUserActionRequest}; -use rpc_task::{RpcTask, RpcTaskHandle, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatus, RpcTaskTypes}; +use rpc_task::{RpcTask, RpcTaskHandle, RpcTaskHandleShared, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatus, + RpcTaskTypes}; use serde_derive::Deserialize; use serde_json::Value as Json; +use std::sync::Arc; pub type InitL2Response = InitRpcTaskResponse; pub type InitL2StatusRequest = RpcTaskStatusRequest; pub type InitL2UserActionRequest = RpcTaskUserActionRequest; pub type InitL2TaskManagerShared = RpcTaskManagerShared>; pub type InitL2TaskHandle = RpcTaskHandle>; +pub type InitL2TaskHandleShared = Arc>; #[derive(Debug, Deserialize)] pub struct InitL2Req { @@ -61,7 +64,7 @@ pub trait InitL2ActivationOps: Into + Send + Sync + 'static { validated_params: Self::ValidatedParams, protocol_conf: Self::ProtocolInfo, coin_conf: Self::CoinConf, - task_handle: &InitL2TaskHandle, + task_handle: InitL2TaskHandleShared, ) -> Result<(Self, Self::ActivationResult), MmError>; } @@ -192,8 +195,7 @@ where }; }; } - - async fn run(&mut self, task_handle: &RpcTaskHandle) -> Result> { + async fn run(&mut self, task_handle: RpcTaskHandleShared) -> Result> { let (coin, result) = L2::init_l2( &self.ctx, self.platform_coin.clone(), diff --git a/mm2src/coins_activation/src/lightning_activation.rs b/mm2src/coins_activation/src/lightning_activation.rs index c5aed05804..144c197f54 100644 --- a/mm2src/coins_activation/src/lightning_activation.rs +++ b/mm2src/coins_activation/src/lightning_activation.rs @@ -38,6 +38,7 @@ const DEFAULT_LISTENING_PORT: u16 = 9735; pub type LightningTaskManagerShared = InitL2TaskManagerShared; pub type LightningRpcTaskHandle = InitL2TaskHandle; +pub type LightningRpcTaskHandleShared = Arc; pub type LightningAwaitingStatus = HwRpcTaskAwaitingStatus; pub type LightningUserAction = HwRpcTaskUserAction; @@ -295,7 +296,7 @@ impl InitL2ActivationOps for LightningCoin { validated_params: Self::ValidatedParams, protocol_conf: Self::ProtocolInfo, coin_conf: Self::CoinConf, - task_handle: &LightningRpcTaskHandle, + task_handle: LightningRpcTaskHandleShared, ) -> Result<(Self, Self::ActivationResult), MmError> { let lightning_coin = start_lightning( ctx, @@ -329,7 +330,7 @@ async fn start_lightning( protocol_conf: LightningProtocolConf, conf: LightningCoinConf, params: LightningValidatedParams, - task_handle: &LightningRpcTaskHandle, + task_handle: LightningRpcTaskHandleShared, ) -> EnableLightningResult { // Todo: add support for Hardware wallets for funding transactions and spending spendable outputs (channel closing transactions) if let coins::DerivationMethod::HDWallet(_) = platform_coin.as_ref().derivation_method { diff --git a/mm2src/coins_activation/src/standalone_coin/init_standalone_coin.rs b/mm2src/coins_activation/src/standalone_coin/init_standalone_coin.rs index 46b9638a4c..89861d8b1a 100644 --- a/mm2src/coins_activation/src/standalone_coin/init_standalone_coin.rs +++ b/mm2src/coins_activation/src/standalone_coin/init_standalone_coin.rs @@ -8,24 +8,26 @@ use coins::my_tx_history_v2::TxHistoryStorage; use coins::tx_history_storage::{CreateTxHistoryStorageError, TxHistoryStorageBuilder}; use coins::{lp_coinfind, lp_register_coin, CoinsContext, MmCoinEnum, RegisterCoinError, RegisterCoinParams}; use common::{log, SuccessResponse}; -use crypto::trezor::trezor_rpc_task::RpcTaskHandle; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use mm2_metrics::MetricsArc; use mm2_number::BigDecimal; use rpc_task::rpc_common::{CancelRpcTaskRequest, InitRpcTaskResponse, RpcTaskStatusRequest, RpcTaskUserActionRequest}; -use rpc_task::{RpcTask, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatus, RpcTaskTypes}; +use rpc_task::{RpcTask, RpcTaskHandle, RpcTaskHandleShared, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatus, + RpcTaskTypes}; use serde_derive::Deserialize; use serde_json::Value as Json; use std::collections::HashMap; +use std::sync::Arc; pub type InitStandaloneCoinResponse = InitRpcTaskResponse; pub type InitStandaloneCoinStatusRequest = RpcTaskStatusRequest; pub type InitStandaloneCoinUserActionRequest = RpcTaskUserActionRequest; pub type InitStandaloneCoinTaskManagerShared = RpcTaskManagerShared>; pub type InitStandaloneCoinTaskHandle = RpcTaskHandle>; +pub type InitStandaloneCoinTaskHandleShared = Arc>; -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Clone)] pub struct InitStandaloneCoinReq { ticker: String, activation_params: T, @@ -59,13 +61,13 @@ pub trait InitStandaloneCoinActivationOps: Into + Send + Sync + 'sta coin_conf: Json, activation_request: &Self::ActivationRequest, protocol_info: Self::StandaloneProtocol, - task_handle: &InitStandaloneCoinTaskHandle, + task_handle: InitStandaloneCoinTaskHandleShared, ) -> Result>; async fn get_activation_result( &self, ctx: MmArc, - task_handle: &InitStandaloneCoinTaskHandle, + task_handle: InitStandaloneCoinTaskHandleShared, activation_request: &Self::ActivationRequest, ) -> Result>; @@ -159,6 +161,7 @@ pub async fn cancel_init_standalone_coin { ctx: MmArc, request: InitStandaloneCoinReq, @@ -192,7 +195,7 @@ where }; } - async fn run(&mut self, task_handle: &RpcTaskHandle) -> Result> { + async fn run(&mut self, task_handle: RpcTaskHandleShared) -> Result> { let ticker = self.request.ticker.clone(); let coin = Standalone::init_standalone_coin( self.ctx.clone(), @@ -200,7 +203,7 @@ where self.coin_conf.clone(), &self.request.activation_params, self.protocol_info.clone(), - task_handle, + task_handle.clone(), ) .await?; diff --git a/mm2src/coins_activation/src/standalone_coin/mod.rs b/mm2src/coins_activation/src/standalone_coin/mod.rs index 2f02aa423a..e33cb38280 100644 --- a/mm2src/coins_activation/src/standalone_coin/mod.rs +++ b/mm2src/coins_activation/src/standalone_coin/mod.rs @@ -5,5 +5,5 @@ pub use init_standalone_coin::{cancel_init_standalone_coin, init_standalone_coin init_standalone_coin_user_action, InitStandaloneCoinActivationOps, InitStandaloneCoinInitialStatus, InitStandaloneCoinReq, InitStandaloneCoinStatusRequest, InitStandaloneCoinTask, InitStandaloneCoinTaskHandle, - InitStandaloneCoinTaskManagerShared}; + InitStandaloneCoinTaskHandleShared, InitStandaloneCoinTaskManagerShared}; pub use init_standalone_coin_error::InitStandaloneCoinError; diff --git a/mm2src/coins_activation/src/utxo_activation/common_impl.rs b/mm2src/coins_activation/src/utxo_activation/common_impl.rs index f33dbd41c5..d997b5e851 100644 --- a/mm2src/coins_activation/src/utxo_activation/common_impl.rs +++ b/mm2src/coins_activation/src/utxo_activation/common_impl.rs @@ -1,4 +1,4 @@ -use crate::standalone_coin::{InitStandaloneCoinActivationOps, InitStandaloneCoinTaskHandle}; +use crate::standalone_coin::{InitStandaloneCoinActivationOps, InitStandaloneCoinTaskHandleShared}; use crate::utxo_activation::init_utxo_standard_activation_error::InitUtxoStandardError; use crate::utxo_activation::init_utxo_standard_statuses::{UtxoStandardAwaitingStatus, UtxoStandardInProgressStatus, UtxoStandardUserAction}; @@ -22,7 +22,7 @@ use std::collections::HashMap; pub(crate) async fn get_activation_result( ctx: &MmArc, coin: &Coin, - task_handle: &InitStandaloneCoinTaskHandle, + task_handle: InitStandaloneCoinTaskHandleShared, activation_params: &UtxoActivationParams, ) -> MmResult where @@ -32,7 +32,8 @@ where AwaitingStatus = UtxoStandardAwaitingStatus, UserAction = UtxoStandardUserAction, > + EnableCoinBalanceOps - + MarketCoinOps, + + MarketCoinOps + + Clone, { let ticker = coin.ticker().to_owned(); let current_block = coin @@ -44,7 +45,7 @@ where // Construct an Xpub extractor without checking if the MarketMaker supports HD wallet ops. // [`EnableCoinBalanceOps::enable_coin_balance`] won't just use `xpub_extractor` // if the coin has been initialized with an Iguana priv key. - let xpub_extractor = RpcTaskXPubExtractor::new_unchecked(ctx, task_handle, xpub_extractor_rpc_statuses()); + let xpub_extractor = RpcTaskXPubExtractor::new_unchecked(ctx, task_handle.clone(), xpub_extractor_rpc_statuses()); task_handle.update_in_progress_status(UtxoStandardInProgressStatus::RequestingWalletBalance)?; let wallet_balance = coin .enable_coin_balance(&xpub_extractor, activation_params.enable_params.clone()) diff --git a/mm2src/coins_activation/src/utxo_activation/init_qtum_activation.rs b/mm2src/coins_activation/src/utxo_activation/init_qtum_activation.rs index 74e7d12a77..3dc6c91ed4 100644 --- a/mm2src/coins_activation/src/utxo_activation/init_qtum_activation.rs +++ b/mm2src/coins_activation/src/utxo_activation/init_qtum_activation.rs @@ -20,9 +20,11 @@ use mm2_metrics::MetricsArc; use mm2_number::BigDecimal; use serde_json::Value as Json; use std::collections::HashMap; +use std::sync::Arc; pub type QtumTaskManagerShared = InitStandaloneCoinTaskManagerShared; pub type QtumRpcTaskHandle = InitStandaloneCoinTaskHandle; +pub type QtumRpcTaskHandleShared = Arc; #[derive(Clone)] pub struct QtumProtocolInfo; @@ -59,7 +61,7 @@ impl InitStandaloneCoinActivationOps for QtumCoin { coin_conf: Json, activation_request: &Self::ActivationRequest, _protocol_info: Self::StandaloneProtocol, - _task_handle: &QtumRpcTaskHandle, + _task_handle: QtumRpcTaskHandleShared, ) -> Result> { let priv_key_policy = priv_key_build_policy(&ctx, activation_request.priv_key_policy)?; @@ -73,7 +75,7 @@ impl InitStandaloneCoinActivationOps for QtumCoin { async fn get_activation_result( &self, ctx: MmArc, - task_handle: &QtumRpcTaskHandle, + task_handle: QtumRpcTaskHandleShared, activation_request: &Self::ActivationRequest, ) -> MmResult { get_activation_result(&ctx, self, task_handle, activation_request).await diff --git a/mm2src/coins_activation/src/utxo_activation/init_utxo_standard_activation.rs b/mm2src/coins_activation/src/utxo_activation/init_utxo_standard_activation.rs index 342d070469..8d2e9a28c8 100644 --- a/mm2src/coins_activation/src/utxo_activation/init_utxo_standard_activation.rs +++ b/mm2src/coins_activation/src/utxo_activation/init_utxo_standard_activation.rs @@ -21,9 +21,11 @@ use mm2_metrics::MetricsArc; use mm2_number::BigDecimal; use serde_json::Value as Json; use std::collections::HashMap; +use std::sync::Arc; pub type UtxoStandardTaskManagerShared = InitStandaloneCoinTaskManagerShared; pub type UtxoStandardRpcTaskHandle = InitStandaloneCoinTaskHandle; +pub type UtxoStandardRpcTaskHandleShared = Arc; #[derive(Clone)] pub struct UtxoStandardProtocolInfo; @@ -60,7 +62,7 @@ impl InitStandaloneCoinActivationOps for UtxoStandardCoin { coin_conf: Json, activation_request: &Self::ActivationRequest, _protocol_info: Self::StandaloneProtocol, - task_handle: &UtxoStandardRpcTaskHandle, + task_handle: UtxoStandardRpcTaskHandleShared, ) -> MmResult { let priv_key_policy = priv_key_build_policy(&ctx, activation_request.priv_key_policy)?; @@ -114,7 +116,7 @@ impl InitStandaloneCoinActivationOps for UtxoStandardCoin { async fn get_activation_result( &self, ctx: MmArc, - task_handle: &UtxoStandardRpcTaskHandle, + task_handle: UtxoStandardRpcTaskHandleShared, activation_request: &Self::ActivationRequest, ) -> MmResult { get_activation_result(&ctx, self, task_handle, activation_request).await diff --git a/mm2src/coins_activation/src/z_coin_activation.rs b/mm2src/coins_activation/src/z_coin_activation.rs index ba1997b1a3..9bae24fec4 100644 --- a/mm2src/coins_activation/src/z_coin_activation.rs +++ b/mm2src/coins_activation/src/z_coin_activation.rs @@ -23,10 +23,12 @@ use ser_error_derive::SerializeErrorType; use serde_derive::Serialize; use serde_json::Value as Json; use std::collections::HashMap; +use std::sync::Arc; use std::time::Duration; pub type ZcoinTaskManagerShared = InitStandaloneCoinTaskManagerShared; pub type ZcoinRpcTaskHandle = InitStandaloneCoinTaskHandle; +pub type ZcoinRpcTaskHandleShared = Arc; pub type ZcoinAwaitingStatus = HwRpcTaskAwaitingStatus; pub type ZcoinUserAction = HwRpcTaskUserAction; @@ -227,7 +229,7 @@ impl InitStandaloneCoinActivationOps for ZCoin { coin_conf: Json, activation_request: &ZcoinActivationParams, protocol_info: ZcoinProtocolInfo, - task_handle: &ZcoinRpcTaskHandle, + task_handle: ZcoinRpcTaskHandleShared, ) -> MmResult { // When `ZCoin` supports Trezor, we'll need to check [`ZcoinActivationParams::priv_key_policy`] // instead of using [`PrivKeyBuildPolicy::detect_priv_key_policy`]. @@ -276,7 +278,7 @@ impl InitStandaloneCoinActivationOps for ZCoin { async fn get_activation_result( &self, _ctx: MmArc, - task_handle: &ZcoinRpcTaskHandle, + task_handle: ZcoinRpcTaskHandleShared, _activation_request: &Self::ActivationRequest, ) -> MmResult { task_handle.update_in_progress_status(ZcoinInProgressStatus::RequestingWalletBalance)?; diff --git a/mm2src/crypto/src/crypto_ctx.rs b/mm2src/crypto/src/crypto_ctx.rs index 4f91448af4..92ac1f2196 100644 --- a/mm2src/crypto/src/crypto_ctx.rs +++ b/mm2src/crypto/src/crypto_ctx.rs @@ -16,6 +16,7 @@ use mm2_err_handle::common_errors::InternalError; use mm2_err_handle::prelude::*; use parking_lot::RwLock; use primitives::hash::H160; +use rpc_task::RpcTaskError; use std::ops::Deref; use std::sync::Arc; @@ -62,6 +63,7 @@ pub enum HwCtxInitError { }, HwError(HwError), ProcessorError(ProcessorError), + InternalError(String), } impl From> for HwCtxInitError { @@ -69,6 +71,7 @@ impl From> for HwCtxInitError< match e { HwProcessingError::HwError(hw_error) => HwCtxInitError::HwError(hw_error), HwProcessingError::ProcessorError(processor_error) => HwCtxInitError::ProcessorError(processor_error), + HwProcessingError::InternalError(internal_error) => HwCtxInitError::InternalError(internal_error), } } } @@ -223,14 +226,11 @@ impl CryptoCtx { Self::init_crypto_ctx_with_policy_builder(ctx, passphrase, builder) } - pub async fn init_hw_ctx_with_trezor( + pub async fn init_hw_ctx_with_trezor( &self, - processor: &Processor, + processor: Arc>, expected_pubkey: Option, - ) -> MmResult<(HwDeviceInfo, HardwareWalletArc), HwCtxInitError> - where - Processor: TrezorConnectProcessor + Sync, - { + ) -> MmResult<(HwDeviceInfo, HardwareWalletArc), HwCtxInitError> { { let mut state = self.hw_ctx.write(); if let InitializationState::Initializing = state.deref() { @@ -355,13 +355,10 @@ pub enum KeyPairPolicy { GlobalHDAccount(GlobalHDAccountArc), } -async fn init_check_hw_ctx_with_trezor( - processor: &Processor, +async fn init_check_hw_ctx_with_trezor( + processor: Arc>, expected_pubkey: Option, -) -> MmResult<(HwDeviceInfo, HardwareWalletArc), HwCtxInitError> -where - Processor: TrezorConnectProcessor + Sync, -{ +) -> MmResult<(HwDeviceInfo, HardwareWalletArc), HwCtxInitError> { let (hw_device_info, hw_ctx) = HardwareWalletCtx::init_with_trezor(processor).await?; let expected_pubkey = match expected_pubkey { Some(expected) => expected, diff --git a/mm2src/crypto/src/hw_client.rs b/mm2src/crypto/src/hw_client.rs index 42e92e4547..cc83473190 100644 --- a/mm2src/crypto/src/hw_client.rs +++ b/mm2src/crypto/src/hw_client.rs @@ -8,6 +8,8 @@ use derive_more::Display; use futures::FutureExt; use mm2_err_handle::prelude::*; use rpc::v1::types::H160 as H160Json; +use rpc_task::RpcTaskError; +use std::sync::Arc; use std::time::Duration; use trezor::client::TrezorClient; use trezor::device_info::TrezorDeviceInfo; @@ -19,6 +21,7 @@ pub type HwPubkey = H160Json; pub enum HwProcessingError { HwError(HwError), ProcessorError(E), + InternalError(String), } impl From for HwProcessingError { @@ -67,6 +70,9 @@ pub trait TrezorConnectProcessor: TrezorRequestProcessor { async fn on_connected(&self) -> MmResult<(), HwProcessingError>; async fn on_connection_failed(&self) -> MmResult<(), HwProcessingError>; + + /// Helper to upcast to super trait object + fn as_base_shared(&self) -> Arc>; } #[derive(Clone)] @@ -119,9 +125,9 @@ impl HwClient { } #[cfg(all(not(target_arch = "wasm32"), not(target_os = "ios")))] - pub(crate) async fn trezor( - processor: &Processor, - ) -> MmResult> { + pub(crate) async fn trezor( + processor: Arc>, + ) -> MmResult> { use common::custom_futures::timeout::TimeoutError; use common::executor::Timer; use trezor::transport::ConnectableDeviceWrapper; diff --git a/mm2src/crypto/src/hw_ctx.rs b/mm2src/crypto/src/hw_ctx.rs index f5dc984407..1ac7c9877f 100644 --- a/mm2src/crypto/src/hw_ctx.rs +++ b/mm2src/crypto/src/hw_ctx.rs @@ -8,6 +8,7 @@ use hw_common::primitives::{EcdsaCurve, Secp256k1ExtendedPublicKey}; use keys::Public as PublicKey; use mm2_err_handle::prelude::*; use primitives::hash::{H160, H264}; +use rpc_task::RpcTaskError; use std::fmt; use std::ops::Deref; use std::str::FromStr; @@ -47,17 +48,15 @@ pub struct HardwareWalletCtx { } impl HardwareWalletCtx { - pub(crate) async fn init_with_trezor( - processor: &Processor, - ) -> MmResult<(HwDeviceInfo, HardwareWalletArc), HwProcessingError> - where - Processor: TrezorConnectProcessor + Sync, - { - let trezor = HwClient::trezor(processor).await?; + pub(crate) async fn init_with_trezor( + processor: Arc>, + ) -> MmResult<(HwDeviceInfo, HardwareWalletArc), HwProcessingError> { + let mut trezor = HwClient::trezor(processor.clone()).await?; let (hw_device_info, hw_internal_pubkey) = { - let (device_info, mut session) = trezor.init_new_session().await?; - let hw_internal_pubkey = HardwareWalletCtx::trezor_mm_internal_pubkey(&mut session, processor).await?; + let processor = processor.as_base_shared(); + let (device_info, mut session) = trezor.init_new_session(processor).await?; + let hw_internal_pubkey = HardwareWalletCtx::trezor_mm_internal_pubkey(&mut session).await?; (HwDeviceInfo::Trezor(device_info), hw_internal_pubkey) }; @@ -74,13 +73,16 @@ impl HardwareWalletCtx { pub fn hw_wallet_type(&self) -> HwWalletType { self.hw_wallet_type } /// Returns a Trezor session. - pub async fn trezor(&self) -> MmResult, HwError> { + pub async fn trezor( + &self, + processor: Arc>, + ) -> MmResult, HwError> { if !self.hw_wallet_connected.load(Ordering::Relaxed) { return MmError::err(HwError::DeviceDisconnected); } let HwClient::Trezor(ref trezor) = self.hw_wallet; - let session = trezor.session().await; + let session = trezor.session(processor).await; self.check_if_connected(session).await } @@ -91,6 +93,7 @@ impl HardwareWalletCtx { let HwClient::Trezor(ref trezor) = self.hw_wallet; let session = match trezor.try_session_if_not_occupied() { + // No 'processor' in the returned session, so it is only for checking conn Some(session) => session, // If we got `None`, the session mutex is occupied by another task, // so for now we can consider the Trezor device as connected. @@ -111,15 +114,16 @@ impl HardwareWalletCtx { /// Returns serializable/deserializable Hardware wallet pubkey. pub fn hw_pubkey(&self) -> HwPubkey { hw_pubkey_from_h264(&self.hw_internal_pubkey) } - pub(crate) async fn trezor_mm_internal_pubkey( - trezor: &mut TrezorSession<'_>, - processor: &Processor, - ) -> MmResult> - where - Processor: TrezorRequestProcessor + Sync, - { + pub(crate) async fn trezor_mm_internal_pubkey( + trezor_session: &mut TrezorSession<'_>, + ) -> MmResult> { let path = mm2_internal_der_path(); - let mm2_internal_xpub = trezor + let processor = trezor_session + .processor + .as_ref() + .or_mm_err(|| HwProcessingError::InternalError("No processor in session object".to_string()))? + .clone(); + let mm2_internal_xpub = trezor_session .get_public_key( path, MM2_TREZOR_INTERNAL_COIN.to_string(), diff --git a/mm2src/crypto/src/hw_error.rs b/mm2src/crypto/src/hw_error.rs index 1efd4b243f..e75cf30f81 100644 --- a/mm2src/crypto/src/hw_error.rs +++ b/mm2src/crypto/src/hw_error.rs @@ -77,6 +77,7 @@ impl From for HwError { TrezorError::UnexpectedInteractionRequest(req) => HwError::UnexpectedUserInteractionRequest(req), TrezorError::Internal(_) => HwError::Internal(error), TrezorError::PongMessageMismatch => HwError::PongMessageMismatch, + TrezorError::InternalNoProcessor => HwError::Internal("no processor object set".to_string()), } } } diff --git a/mm2src/crypto/src/hw_rpc_task.rs b/mm2src/crypto/src/hw_rpc_task.rs index 41a0516ab6..515ceca84e 100644 --- a/mm2src/crypto/src/hw_rpc_task.rs +++ b/mm2src/crypto/src/hw_rpc_task.rs @@ -5,9 +5,10 @@ use mm2_err_handle::prelude::*; use rpc_task::rpc_common::RpcTaskUserActionRequest; use serde::Serialize; use std::convert::TryFrom; +use std::sync::Arc; use std::time::Duration; -use trezor::trezor_rpc_task::{RpcTask, RpcTaskError, RpcTaskHandle, TrezorRequestStatuses, TrezorRpcTaskProcessor, - TryIntoUserAction}; +use trezor::trezor_rpc_task::{RpcTask, RpcTaskError, RpcTaskHandleShared, TrezorRequestStatuses, + TrezorRpcTaskProcessor, TryIntoUserAction}; use trezor::user_interaction::TrezorPassphraseResponse; use trezor::{TrezorProcessingError, TrezorRequestProcessor}; @@ -84,8 +85,8 @@ where } } -pub struct TrezorRpcTaskConnectProcessor<'a, Task: RpcTask> { - request_processor: TrezorRpcTaskProcessor<'a, Task>, +pub struct TrezorRpcTaskConnectProcessor { + request_processor: TrezorRpcTaskProcessor, on_connect: Task::InProgressStatus, on_connected: Task::InProgressStatus, on_connection_failed: Task::InProgressStatus, @@ -93,7 +94,7 @@ pub struct TrezorRpcTaskConnectProcessor<'a, Task: RpcTask> { } #[async_trait] -impl<'a, Task> TrezorRequestProcessor for TrezorRpcTaskConnectProcessor<'a, Task> +impl TrezorRequestProcessor for TrezorRpcTaskConnectProcessor where Task: RpcTask, Task::UserAction: TryIntoUserAction + Send, @@ -118,7 +119,7 @@ where } #[async_trait] -impl<'a, Task> TrezorConnectProcessor for TrezorRpcTaskConnectProcessor<'a, Task> +impl TrezorConnectProcessor for TrezorRpcTaskConnectProcessor where Task: RpcTask, Task::UserAction: TryIntoUserAction, @@ -140,11 +141,15 @@ where .request_processor .update_in_progress_status(self.on_connection_failed.clone())?) } + + fn as_base_shared(&self) -> Arc> { + Arc::new(self.request_processor.clone()) + } } -impl<'a, Task: RpcTask> TrezorRpcTaskConnectProcessor<'a, Task> { +impl TrezorRpcTaskConnectProcessor { pub fn new( - task_handle: &'a RpcTaskHandle, + task_handle: RpcTaskHandleShared, statuses: HwConnectStatuses, ) -> Self { let request_statuses = TrezorRequestStatuses { diff --git a/mm2src/mm2_main/src/lp_init/init_hw.rs b/mm2src/mm2_main/src/lp_init/init_hw.rs index ab9b90b545..3b00b63139 100644 --- a/mm2src/mm2_main/src/lp_init/init_hw.rs +++ b/mm2src/mm2_main/src/lp_init/init_hw.rs @@ -13,6 +13,7 @@ use mm2_err_handle::prelude::*; use rpc_task::rpc_common::{CancelRpcTaskError, CancelRpcTaskRequest, InitRpcTaskResponse, RpcTaskStatusError, RpcTaskStatusRequest, RpcTaskUserActionError}; use rpc_task::{RpcTask, RpcTaskError, RpcTaskHandle, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatus, RpcTaskTypes}; +use std::sync::Arc; use std::time::Duration; const TREZOR_CONNECT_TIMEOUT: Duration = Duration::from_secs(300); @@ -24,6 +25,7 @@ pub type InitHwUserAction = HwRpcTaskUserAction; pub type InitHwTaskManagerShared = RpcTaskManagerShared; pub type InitHwStatus = RpcTaskStatus; type InitHwTaskHandle = RpcTaskHandle; +type InitHwTaskHandleShared = Arc; #[derive(Clone, Display, EnumFromTrait, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] @@ -58,6 +60,7 @@ impl From> for InitHwError { HwCtxInitError::UnexpectedPubkey { .. } => InitHwError::HwError(HwRpcError::FoundUnexpectedDevice), HwCtxInitError::HwError(hw_error) => InitHwError::from(hw_error), HwCtxInitError::ProcessorError(rpc) => InitHwError::from(rpc), + HwCtxInitError::InternalError(err) => InitHwError::Internal(err), } } } @@ -95,7 +98,7 @@ pub enum InitHwInProgressStatus { FollowHwDeviceInstructions, } -#[derive(Deserialize)] +#[derive(Deserialize, Clone)] pub struct InitHwRequest { device_pubkey: Option, } @@ -107,6 +110,7 @@ pub struct InitHwResponse { device_pubkey: HwPubkey, } +#[derive(Clone)] pub struct InitHwTask { ctx: MmArc, hw_wallet_type: HwWalletType, @@ -131,7 +135,7 @@ impl RpcTask for InitHwTask { } } - async fn run(&mut self, task_handle: &InitHwTaskHandle) -> Result> { + async fn run(&mut self, task_handle: InitHwTaskHandleShared) -> Result> { let crypto_ctx = CryptoCtx::from_ctx(&self.ctx)?; match self.hw_wallet_type { @@ -147,9 +151,9 @@ impl RpcTask for InitHwTask { }) .with_connect_timeout(TREZOR_CONNECT_TIMEOUT) .with_pin_timeout(TREZOR_PIN_TIMEOUT); - + let trezor_connect_processor = Arc::new(trezor_connect_processor); let (device_info, hw_ctx) = crypto_ctx - .init_hw_ctx_with_trezor(&trezor_connect_processor, self.req.device_pubkey) + .init_hw_ctx_with_trezor(trezor_connect_processor, self.req.device_pubkey) .await?; let device_pubkey = hw_ctx.hw_pubkey(); Ok(InitHwResponse { diff --git a/mm2src/mm2_main/src/lp_init/init_metamask.rs b/mm2src/mm2_main/src/lp_init/init_metamask.rs index b362920a54..fb3494dc58 100644 --- a/mm2src/mm2_main/src/lp_init/init_metamask.rs +++ b/mm2src/mm2_main/src/lp_init/init_metamask.rs @@ -21,6 +21,7 @@ pub type InitMetamaskStatus = type InitMetamaskUserAction = SerdeInfallible; type InitMetamaskAwaitingStatus = SerdeInfallible; type InitMetamaskTaskHandle = RpcTaskHandle; +type InitMetamaskTaskHandleShared = Arc; #[derive(Clone, Display, EnumFromTrait, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] @@ -119,7 +120,7 @@ impl RpcTask for InitMetamaskTask { } } - async fn run(&mut self, _task_handle: &InitMetamaskTaskHandle) -> Result> { + async fn run(&mut self, task_handle: InitMetamaskTaskHandleShared) -> Result> { let crypto_ctx = CryptoCtx::from_ctx(&self.ctx)?; let metamask = crypto_ctx.init_metamask_ctx(self.req.project.clone()).await?; diff --git a/mm2src/mm2_main/src/lp_native_dex.rs b/mm2src/mm2_main/src/lp_native_dex.rs index 7831aa71b3..8f98bfc975 100644 --- a/mm2src/mm2_main/src/lp_native_dex.rs +++ b/mm2src/mm2_main/src/lp_native_dex.rs @@ -288,6 +288,7 @@ impl From> for MmInitError { match e { HwProcessingError::HwError(hw) => MmInitError::from(hw), HwProcessingError::ProcessorError(rpc_task) => MmInitError::from(rpc_task), + HwProcessingError::InternalError(err) => MmInitError::Internal(err), } } } diff --git a/mm2src/rpc_task/src/handle.rs b/mm2src/rpc_task/src/handle.rs index 02657569c3..bd9f8624af 100644 --- a/mm2src/rpc_task/src/handle.rs +++ b/mm2src/rpc_task/src/handle.rs @@ -4,10 +4,11 @@ use common::custom_futures::timeout::FutureTimerExt; use common::log::LogOnError; use futures::channel::oneshot; use mm2_err_handle::prelude::*; -use std::sync::MutexGuard; +use std::sync::{Arc, MutexGuard}; use std::time::Duration; type TaskManagerLock<'a, Task> = MutexGuard<'a, RpcTaskManager>; +pub type RpcTaskHandleShared = Arc>; pub struct RpcTaskHandle { pub(crate) task_manager: RpcTaskManagerWeak, @@ -56,13 +57,13 @@ impl RpcTaskHandle { .map_to_mm(|_canceled| RpcTaskError::Cancelled) } - pub(crate) fn finish(self, result: Result>) { + pub(crate) fn finish(&self, result: Result>) { let task_status = Self::prepare_task_result(result); self.lock_and_then(|mut task_manager| task_manager.update_task_status(self.task_id, task_status)) .warn_log(); } - pub(crate) fn on_cancelled(self) { + pub(crate) fn on_cancelled(&self) { self.lock_and_then(|mut task_manager| task_manager.on_task_cancelling_finished(self.task_id)) .warn_log(); } diff --git a/mm2src/rpc_task/src/lib.rs b/mm2src/rpc_task/src/lib.rs index b18fa15047..f5861f37cc 100644 --- a/mm2src/rpc_task/src/lib.rs +++ b/mm2src/rpc_task/src/lib.rs @@ -14,7 +14,7 @@ mod manager; pub mod rpc_common; mod task; -pub use handle::RpcTaskHandle; +pub use handle::{RpcTaskHandle, RpcTaskHandleShared}; pub use manager::{RpcTaskManager, RpcTaskManagerShared}; pub use task::{RpcTask, RpcTaskTypes}; diff --git a/mm2src/rpc_task/src/manager.rs b/mm2src/rpc_task/src/manager.rs index 207c1ffba6..950eac97f4 100644 --- a/mm2src/rpc_task/src/manager.rs +++ b/mm2src/rpc_task/src/manager.rs @@ -50,14 +50,14 @@ impl RpcTaskManager { .map_to_mm(|e| RpcTaskError::Internal(format!("RpcTaskManager is not available: {}", e)))?; task_manager.register_task(initial_task_status)? }; - let task_handle = RpcTaskHandle { + let task_handle = Arc::new(RpcTaskHandle { task_manager: RpcTaskManagerShared::downgrade(this), task_id, - }; + }); let fut = async move { debug!("Spawn RPC task '{}'", task_id); - let task_fut = task.run(&task_handle); + let task_fut = task.run(task_handle.clone()); let task_result = match select(task_fut, task_abort_handler).await { // The task has finished. Either::Left((task_result, _abort_handler)) => Some(task_result), diff --git a/mm2src/rpc_task/src/task.rs b/mm2src/rpc_task/src/task.rs index 860257515f..6c38f75050 100644 --- a/mm2src/rpc_task/src/task.rs +++ b/mm2src/rpc_task/src/task.rs @@ -1,4 +1,4 @@ -use crate::handle::RpcTaskHandle; +use crate::handle::RpcTaskHandleShared; use async_trait::async_trait; use mm2_err_handle::prelude::*; use serde::Serialize; @@ -18,5 +18,5 @@ pub trait RpcTask: RpcTaskTypes + Sized + Send + 'static { /// The method is invoked when the task has been cancelled. async fn cancel(self); - async fn run(&mut self, task_handle: &RpcTaskHandle) -> Result>; + async fn run(&mut self, task_handle: RpcTaskHandleShared) -> Result>; } diff --git a/mm2src/trezor/src/client.rs b/mm2src/trezor/src/client.rs index 3e06023ad5..6339955c25 100644 --- a/mm2src/trezor/src/client.rs +++ b/mm2src/trezor/src/client.rs @@ -9,10 +9,12 @@ use crate::proto::{ProtoMessage, TrezorMessage}; use crate::response::TrezorResponse; use crate::result_handler::ResultHandler; use crate::transport::Transport; +use crate::TrezorRequestProcessor; use crate::{TrezorError, TrezorResult}; use common::now_ms; use futures::lock::{Mutex as AsyncMutex, MutexGuard as AsyncMutexGuard}; use mm2_err_handle::prelude::*; +use rpc_task::RpcTaskError; use std::sync::Arc; #[derive(Clone)] @@ -33,25 +35,33 @@ impl TrezorClient { /// Initialize a Trezor session by sending /// [Initialize](https://docs.trezor.io/trezor-firmware/common/communication/sessions.html#examples). /// Returns `TrezorDeviceInfo` and `TrezorSession`. - pub async fn init_new_session(&self) -> TrezorResult<(TrezorDeviceInfo, TrezorSession<'_>)> { + pub async fn init_new_session( + &mut self, + processor: Arc>, + ) -> TrezorResult<(TrezorDeviceInfo, TrezorSession<'_>)> { let mut session = TrezorSession { inner: self.inner.lock().await, + processor: Some(processor.clone()), }; let features = session.initialize_device().await?; Ok((TrezorDeviceInfo::from(features), session)) } /// Occupies the Trezor device for further interactions by locking a mutex. - pub async fn session(&self) -> TrezorSession<'_> { + pub async fn session(&self, processor: Arc>) -> TrezorSession<'_> { TrezorSession { inner: self.inner.lock().await, + processor: Some(processor.clone()), } } /// Checks if the Trezor device is vacant (not occupied). /// Returns `None` if it is occupied already. + /// Note: does not return processor and should be used to check connections only pub fn try_session_if_not_occupied(&self) -> Option> { - self.inner.try_lock().map(|inner| TrezorSession { inner }) + self.inner + .try_lock() + .map(|inner| TrezorSession { inner, processor: None }) } } @@ -61,6 +71,7 @@ pub struct TrezorClientImpl { pub struct TrezorSession<'a> { inner: AsyncMutexGuard<'a, TrezorClientImpl>, + pub processor: Option>>, } impl<'a> TrezorSession<'a> { diff --git a/mm2src/trezor/src/error.rs b/mm2src/trezor/src/error.rs index e141efe985..798185695c 100644 --- a/mm2src/trezor/src/error.rs +++ b/mm2src/trezor/src/error.rs @@ -33,6 +33,8 @@ pub enum TrezorError { UnexpectedInteractionRequest(TrezorUserInteraction), Internal(String), PongMessageMismatch, + #[display("no processor for trezor response")] + InternalNoProcessor, } #[derive(Clone, Debug, Display)] diff --git a/mm2src/trezor/src/response.rs b/mm2src/trezor/src/response.rs index 0fda4c1c16..da835692d7 100644 --- a/mm2src/trezor/src/response.rs +++ b/mm2src/trezor/src/response.rs @@ -5,7 +5,9 @@ use crate::user_interaction::TrezorUserInteraction; use crate::{TrezorError, TrezorResult}; use async_trait::async_trait; use mm2_err_handle::prelude::*; +use rpc_task::RpcTaskError; use std::fmt; +use std::sync::Arc; pub use crate::proto::messages_common::button_request::ButtonRequestType; pub use crate::proto::messages_common::pin_matrix_request::PinMatrixRequestType; @@ -131,25 +133,26 @@ impl<'a, 'b, T> ProcessTrezorResponse for TrezorResponse<'a, 'b, T> where T: Send + Sync + 'static, { - async fn process(self, processor: &Processor) -> MmResult> - where - Processor: TrezorRequestProcessor + Sync, - { + async fn process( + self, + processor: Arc>, + ) -> MmResult> { + let processor_req = processor.clone(); let fut = async move { let mut response = self; loop { response = match response { TrezorResponse::Ready(result) => return Ok(result), TrezorResponse::ButtonRequest(button_req) => { - processor.on_button_request().await?; + processor_req.on_button_request().await?; button_req.ack().await? }, TrezorResponse::PinMatrixRequest(pin_req) => { - let pin_response = processor.on_pin_request().await?; + let pin_response = processor_req.on_pin_request().await?; pin_req.ack_pin(pin_response.pin).await? }, TrezorResponse::PassphraseRequest(passphrase_req) => { - let passphrase_response = processor.on_passphrase_request().await?; + let passphrase_response = processor_req.on_passphrase_request().await?; passphrase_req.ack_passphrase(passphrase_response.passphrase).await? }, }; diff --git a/mm2src/trezor/src/response_processor.rs b/mm2src/trezor/src/response_processor.rs index 5ad235ad83..33a3e0c46a 100644 --- a/mm2src/trezor/src/response_processor.rs +++ b/mm2src/trezor/src/response_processor.rs @@ -1,8 +1,11 @@ +use std::sync::Arc; + use crate::user_interaction::TrezorPassphraseResponse; use crate::{TrezorError, TrezorPinMatrix3x3Response}; use async_trait::async_trait; use derive_more::Display; use mm2_err_handle::prelude::*; +use rpc_task::RpcTaskError; #[derive(Display)] pub enum TrezorProcessingError { @@ -18,7 +21,10 @@ impl From for TrezorProcessingError { impl NotEqual for TrezorProcessingError {} #[async_trait] -pub trait TrezorRequestProcessor { +pub trait TrezorRequestProcessor +where + Self: Send + Sync, +{ type Error: NotMmError + Send; async fn on_button_request(&self) -> MmResult<(), TrezorProcessingError>; @@ -35,8 +41,8 @@ pub trait ProcessTrezorResponse where T: Send + Sync + 'static, { - async fn process(self, processor: &Processor) -> MmResult> - where - Self: Sized, - Processor: TrezorRequestProcessor + Sync; + async fn process( + self, + processor: Arc>, + ) -> MmResult>; } diff --git a/mm2src/trezor/src/trezor_rpc_task.rs b/mm2src/trezor/src/trezor_rpc_task.rs index a84c7dd4a1..41fbf5bc75 100644 --- a/mm2src/trezor/src/trezor_rpc_task.rs +++ b/mm2src/trezor/src/trezor_rpc_task.rs @@ -6,7 +6,7 @@ use mm2_err_handle::prelude::*; use std::convert::TryInto; use std::time::Duration; -pub use rpc_task::{RpcTask, RpcTaskError, RpcTaskHandle}; +pub use rpc_task::{RpcTask, RpcTaskError, RpcTaskHandleShared}; const DEFAULT_USER_ACTION_TIMEOUT: Duration = Duration::from_secs(300); @@ -24,6 +24,7 @@ impl TryIntoUserAction for T where { } +#[derive(Clone)] pub struct TrezorRequestStatuses { pub on_button_request: InProgressStatus, pub on_pin_request: AwaitingStatus, @@ -31,14 +32,25 @@ pub struct TrezorRequestStatuses { pub on_ready: InProgressStatus, } -pub struct TrezorRpcTaskProcessor<'a, Task: RpcTask> { - task_handle: &'a RpcTaskHandle, +pub struct TrezorRpcTaskProcessor { + task_handle: RpcTaskHandleShared, statuses: TrezorRequestStatuses, user_action_timeout: Duration, } +/// Custom Clone to avoid clone derivations for structs implementing RpcTask +impl Clone for TrezorRpcTaskProcessor { + fn clone(&self) -> Self { + Self { + task_handle: self.task_handle.clone(), + statuses: self.statuses.clone(), + user_action_timeout: self.user_action_timeout, + } + } +} + #[async_trait] -impl<'a, Task> TrezorRequestProcessor for TrezorRpcTaskProcessor<'a, Task> +impl TrezorRequestProcessor for TrezorRpcTaskProcessor where Task: RpcTask, Task::UserAction: TryIntoUserAction + Send, @@ -78,11 +90,11 @@ where } } -impl<'a, Task: RpcTask> TrezorRpcTaskProcessor<'a, Task> { +impl TrezorRpcTaskProcessor { pub fn new( - task_handle: &'a RpcTaskHandle, + task_handle: RpcTaskHandleShared, statuses: TrezorRequestStatuses, - ) -> TrezorRpcTaskProcessor<'a, Task> { + ) -> TrezorRpcTaskProcessor { TrezorRpcTaskProcessor { task_handle, statuses, From da7361f70dc334076da39cf33f2ba7ee997c265b Mon Sep 17 00:00:00 2001 From: dimxy Date: Thu, 23 Nov 2023 23:36:47 +0500 Subject: [PATCH 12/17] add pin and passphrase processing for trezor sign_utxo_tx --- mm2src/trezor/src/response.rs | 30 ----------- mm2src/trezor/src/utxo/sign_utxo.rs | 80 ++++++++++++++++++++++++++--- 2 files changed, 72 insertions(+), 38 deletions(-) diff --git a/mm2src/trezor/src/response.rs b/mm2src/trezor/src/response.rs index da835692d7..869b910997 100644 --- a/mm2src/trezor/src/response.rs +++ b/mm2src/trezor/src/response.rs @@ -51,33 +51,6 @@ impl<'a, 'b, T: 'static> TrezorResponse<'a, 'b, T> { } } - /// Agrees to wait for all `HW button press` requests and returns final `Result`. - /// - /// # Error - /// - /// Will error if it receives requests, which require input like: `PinMatrixRequest`. - pub async fn ack_all(self) -> TrezorResult { - let mut resp = self; - loop { - resp = match resp { - Self::Ready(val) => { - return Ok(val); - }, - Self::ButtonRequest(req) => req.ack().await?, - Self::PinMatrixRequest(_) => { - return MmError::err(TrezorError::UnexpectedInteractionRequest( - TrezorUserInteraction::PinMatrix3x3, - )); - }, - Self::PassphraseRequest(_) => { - return MmError::err(TrezorError::UnexpectedInteractionRequest( - TrezorUserInteraction::PassphraseRequest, - )); - }, - }; - } - } - /// Returns `Some(T)` if the result is ready, otherwise cancels the request. pub async fn cancel_if_not_ready(self) -> Option { match self { @@ -185,9 +158,6 @@ impl<'a, 'b, T: 'static> ButtonRequest<'a, 'b, T> { self.session.call(req, self.result_handler).await } - /// TODO add an optional `timeout` param. - pub async fn ack_all(self) -> TrezorResult { self.ack().await?.ack_all().await } - pub async fn cancel(self) { self.session.cancel_last_op().await } } diff --git a/mm2src/trezor/src/utxo/sign_utxo.rs b/mm2src/trezor/src/utxo/sign_utxo.rs index 9c3a452b24..8b56f4fbd4 100644 --- a/mm2src/trezor/src/utxo/sign_utxo.rs +++ b/mm2src/trezor/src/utxo/sign_utxo.rs @@ -2,7 +2,7 @@ use crate::proto::messages_bitcoin as proto_bitcoin; use crate::result_handler::ResultHandler; use crate::utxo::unsigned_tx::UnsignedUtxoTx; use crate::utxo::Signature; -use crate::{TrezorError, TrezorResponse, TrezorResult, TrezorSession}; +use crate::{ProcessTrezorResponse, TrezorError, TrezorResponse, TrezorResult, TrezorSession}; use common::log::{debug, info}; use mm2_err_handle::prelude::*; @@ -37,8 +37,18 @@ impl<'a> TrezorSession<'a> { use proto_bitcoin::tx_request::RequestType as ProtoTxRequestType; let mut result = TxSignResult::new_with_inputs_count(unsigned.inputs.len()); + let processor = self + .processor + .as_ref() + .or_mm_err(|| TrezorError::InternalNoProcessor)? + .clone(); // Please note `tx_request` is changed within the following loop. - let mut tx_request = self.sign_tx(unsigned.sign_tx_message()).await?.ack_all().await?; + let mut tx_request = self + .sign_tx(unsigned.sign_tx_message()) + .await? + .process(processor) + .await + .mm_err(|e| TrezorError::Internal(e.to_string()))?; info!( "Start transaction signing: COIN={} INPUTS_COUNT={} OUTPUTS_COUNT={} OVERWINTERED={}", @@ -102,7 +112,16 @@ impl<'a> TrezorSession<'a> { let req = prev_tx.meta_message(); let result_handler = ResultHandler::::new(Ok); - self.call(req, result_handler).await?.ack_all().await + let processor = self + .processor + .as_ref() + .or_mm_err(|| TrezorError::InternalNoProcessor)? + .clone(); + self.call(req, result_handler) + .await? + .process(processor) + .await + .mm_err(|e| TrezorError::Internal(e.to_string())) } async fn send_prev_input<'b>( @@ -120,7 +139,16 @@ impl<'a> TrezorSession<'a> { let req = prev_tx.input_message(prev_input_index)?; let result_handler = ResultHandler::::new(Ok); - self.call(req, result_handler).await?.ack_all().await + let processor = self + .processor + .as_ref() + .or_mm_err(|| TrezorError::InternalNoProcessor)? + .clone(); + self.call(req, result_handler) + .await? + .process(processor) + .await + .mm_err(|e| TrezorError::Internal(e.to_string())) } async fn send_prev_output<'b>( @@ -138,7 +166,16 @@ impl<'a> TrezorSession<'a> { let req = prev_tx.output_message(prev_output_index)?; let result_handler = ResultHandler::::new(Ok); - self.call(req, result_handler).await?.ack_all().await + let processor = self + .processor + .as_ref() + .or_mm_err(|| TrezorError::InternalNoProcessor)? + .clone(); + self.call(req, result_handler) + .await? + .process(processor) + .await + .mm_err(|e| TrezorError::Internal(e.to_string())) } async fn send_input<'b>( @@ -153,7 +190,16 @@ impl<'a> TrezorSession<'a> { let req = unsigned.input_message(input_index)?; let result_handler = ResultHandler::::new(Ok); - self.call(req, result_handler).await?.ack_all().await + let processor = self + .processor + .as_ref() + .or_mm_err(|| TrezorError::InternalNoProcessor)? + .clone(); + self.call(req, result_handler) + .await? + .process(processor) + .await + .mm_err(|e| TrezorError::Internal(e.to_string())) } async fn send_output<'b>( @@ -168,7 +214,16 @@ impl<'a> TrezorSession<'a> { let req = unsigned.output_message(output_index)?; let result_handler = ResultHandler::::new(Ok); - self.call(req, result_handler).await?.ack_all().await + let processor = self + .processor + .as_ref() + .or_mm_err(|| TrezorError::InternalNoProcessor)? + .clone(); + self.call(req, result_handler) + .await? + .process(processor) + .await + .mm_err(|e| TrezorError::Internal(e.to_string())) } async fn send_extra_data<'b>( @@ -189,7 +244,16 @@ impl<'a> TrezorSession<'a> { let req = prev_tx.extra_data_message(offset, len)?; let result_handler = ResultHandler::::new(Ok); - self.call(req, result_handler).await?.ack_all().await + let processor = self + .processor + .as_ref() + .or_mm_err(|| TrezorError::InternalNoProcessor)? + .clone(); + self.call(req, result_handler) + .await? + .process(processor) + .await + .mm_err(|e| TrezorError::Internal(e.to_string())) } async fn sign_tx<'b>( From d784eaaea4d8345c91a3b06c6ef45e65825e3824 Mon Sep 17 00:00:00 2001 From: dimxy Date: Sun, 26 Nov 2023 14:18:55 +0500 Subject: [PATCH 13/17] make as_base_shared fn more generic --- mm2src/coins/utxo/utxo_withdraw.rs | 2 +- mm2src/crypto/src/hw_client.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mm2src/coins/utxo/utxo_withdraw.rs b/mm2src/coins/utxo/utxo_withdraw.rs index 3b0c27d16c..0ab24bd5fc 100644 --- a/mm2src/coins/utxo/utxo_withdraw.rs +++ b/mm2src/coins/utxo/utxo_withdraw.rs @@ -346,7 +346,7 @@ where on_ready: WithdrawInProgressStatus::FollowHwDeviceInstructions, }; let sign_processor = TrezorRpcTaskProcessor::new(self.task_handle.clone(), trezor_statuses); - let sign_processor = Arc::new(sign_processor); //as &dyn TrezorRequestProcessor + let sign_processor = Arc::new(sign_processor); let trezor_session = hw_ctx.trezor(sign_processor).await?; SignPolicy::WithTrezor(trezor_session) }, diff --git a/mm2src/crypto/src/hw_client.rs b/mm2src/crypto/src/hw_client.rs index cc83473190..fbd7239e11 100644 --- a/mm2src/crypto/src/hw_client.rs +++ b/mm2src/crypto/src/hw_client.rs @@ -72,7 +72,7 @@ pub trait TrezorConnectProcessor: TrezorRequestProcessor { async fn on_connection_failed(&self) -> MmResult<(), HwProcessingError>; /// Helper to upcast to super trait object - fn as_base_shared(&self) -> Arc>; + fn as_base_shared(&self) -> Arc>; } #[derive(Clone)] From 7c0a1dc489b797f1702c1c31005f9731529fe0f1 Mon Sep 17 00:00:00 2001 From: dimxy Date: Mon, 27 Nov 2023 11:55:58 +0500 Subject: [PATCH 14/17] put trezor tests in a module, add trezor pin handling in test init loop --- mm2src/crypto/src/hw_rpc_task.rs | 2 +- mm2src/mm2_main/Cargo.toml | 1 + .../tests/mm2_tests/mm2_tests_inner.rs | 494 ++++++++++-------- mm2src/mm2_test_helpers/src/for_tests.rs | 22 + 4 files changed, 292 insertions(+), 227 deletions(-) diff --git a/mm2src/crypto/src/hw_rpc_task.rs b/mm2src/crypto/src/hw_rpc_task.rs index 515ceca84e..9a1c06d83b 100644 --- a/mm2src/crypto/src/hw_rpc_task.rs +++ b/mm2src/crypto/src/hw_rpc_task.rs @@ -18,7 +18,7 @@ pub type HwRpcTaskUserActionRequest = RpcTaskUserActionRequest MmArc { - let ctx = mm_ctx_with_custom_db_with_conf(Some(conf)); +#[cfg(all(feature = "run-device-tests", not(target_arch = "wasm32")))] +mod trezor_tests { + use super::enable_utxo_v2_electrum; + use coins::utxo::for_tests::test_withdraw_init_loop; + use coins::utxo::{utxo_standard::UtxoStandardCoin, UtxoActivationParams}; + use coins_activation::{for_tests::init_standalone_coin_loop, InitStandaloneCoinReq}; + use common::executor::Timer; + use common::serde::Deserialize; + use common::{block_on, log, now_ms, wait_until_ms}; + use crypto::hw_rpc_task::HwRpcTaskAwaitingStatus; + use crypto::CryptoCtx; + use mm2_core::mm_ctx::MmArc; + use mm2_main::mm2::init_hw::init_trezor_user_action; + use mm2_main::mm2::init_hw::{init_trezor, init_trezor_status, InitHwRequest, InitHwResponse}; + use mm2_test_helpers::electrums::tbtc_electrums; + use mm2_test_helpers::for_tests::{init_trezor_rpc, init_trezor_status_rpc, init_trezor_user_action_rpc, + init_withdraw, mm_ctx_with_custom_db_with_conf, tbtc_legacy_conf, + tbtc_segwit_conf, withdraw_status, MarketMakerIt, Mm2TestConf}; + use mm2_test_helpers::structs::{InitTaskResult, RpcV2Response, TransactionDetails, WithdrawStatus}; + use rpc_task::{rpc_common::RpcTaskStatusRequest, RpcTaskStatus}; + use serde_json::{self as json, json, Value as Json}; + use std::io::{stdin, stdout, BufRead, Write}; + + #[derive(Debug, Deserialize)] + #[serde(deny_unknown_fields, tag = "status", content = "details")] + pub enum InitTrezorStatus { + Ok(InitHwResponse), + Error(Json), + InProgress(Json), + UserActionRequired(Json), + } - CryptoCtx::init_with_iguana_passphrase(ctx.clone(), "123456").unwrap(); // for now we need passphrase seed for init - let req: InitHwRequest = serde_json::from_value(json!({ "device_pubkey": null })).unwrap(); - let res = match init_trezor(ctx.clone(), req).await { - Ok(res) => res, - _ => { - panic!("cannot init trezor"); - }, - }; + pub async fn mm_ctx_with_trezor(conf: Json) -> MmArc { + let ctx = mm_ctx_with_custom_db_with_conf(Some(conf)); - loop { - let status_req = RpcTaskStatusRequest { - task_id: res.task_id, - forget_if_finished: false, - }; - match init_trezor_status(ctx.clone(), status_req).await { - Ok(res) => { - log!("trezor init status={:?}", serde_json::to_string(&res).unwrap()); - match res { - RpcTaskStatus::Ok(_) => { - log!("device initialized"); - break; - }, - RpcTaskStatus::Error(_) => { - log!("device in error state"); - break; - }, - RpcTaskStatus::InProgress(_) => log!("trezor init in progress"), - RpcTaskStatus::UserActionRequired(_) => log!("device is waiting for user action"), - } - }, + CryptoCtx::init_with_iguana_passphrase(ctx.clone(), "123456").unwrap(); // for now we need passphrase seed for init + let req: InitHwRequest = serde_json::from_value(json!({ "device_pubkey": null })).unwrap(); + let res = match init_trezor(ctx.clone(), req).await { + Ok(res) => res, _ => { - panic!("cannot get trezor status"); + panic!("cannot init trezor"); }, }; - Timer::sleep(5.).await - } - ctx -} -/// Tool to run withdraw directly with trezor device or emulator (no rpc version, added for easier debugging) -/// run cargo test with '--ignored' option -/// to use trezor emulator add '--features trezor-udp' option to cargo test params -#[test] -#[ignore] -#[cfg(all(not(target_arch = "wasm32")))] -fn test_withdraw_from_trezor_segwit_no_rpc() { - let ticker = "tBTC-Segwit"; - let mut coin_conf = tbtc_segwit_conf(); - coin_conf["trezor_coin"] = "Testnet".into(); - let mm_conf = json!({ "coins": [coin_conf] }); - - let ctx = block_on(mm_ctx_with_trezor(mm_conf)); - let enable_req = json!({ - "method": "electrum", - "coin": ticker, - "servers": tbtc_electrums(), - "priv_key_policy": "Trezor", - }); - let activation_params = UtxoActivationParams::from_legacy_req(&enable_req).unwrap(); - let request: InitStandaloneCoinReq = json::from_value(json!({ - "ticker": ticker, - "activation_params": activation_params - })) - .unwrap(); + let task_id = res.task_id; + loop { + let status_req = RpcTaskStatusRequest { + task_id, + forget_if_finished: false, + }; + match init_trezor_status(ctx.clone(), status_req).await { + Ok(res) => { + log!("trezor init status={:?}", serde_json::to_string(&res).unwrap()); + match res { + RpcTaskStatus::Ok(_) => { + log!("device initialized"); + break; + }, + RpcTaskStatus::Error(_) => { + log!("device in error state"); + break; + }, + RpcTaskStatus::InProgress(_) => log!("trezor init in progress"), + RpcTaskStatus::UserActionRequired(device_req) => { + log!("device is waiting for user action"); + match device_req { + HwRpcTaskAwaitingStatus::EnterTrezorPin => { + print!("Enter pin:"); + let _ = stdout().flush(); + let pin = stdin().lock().lines().next().unwrap().unwrap(); // read pin from console + let pin_req = serde_json::from_value(json!({ + "task_id": task_id, + "user_action": { + "action_type": "TrezorPin", + "pin": pin + } + })) + .unwrap(); + let _ = init_trezor_user_action(ctx.clone(), pin_req).await; + }, + _ => { + panic!("Trezor passphrase is not supported in tests"); + }, + } + }, + } + }, + _ => { + panic!("cannot get trezor status"); + }, + }; + Timer::sleep(5.).await + } + ctx + } - block_on(init_standalone_coin_loop::(ctx.clone(), request)) - .expect("coin activation must be successful"); + /// Tool to run withdraw directly with trezor device or emulator (no rpc version, added for easier debugging) + /// run cargo test with '--features run-device-tests' option + /// to use trezor emulator also add '--features trezor-udp' option to cargo params + #[test] + fn test_withdraw_from_trezor_segwit_no_rpc() { + let ticker = "tBTC-Segwit"; + let mut coin_conf = tbtc_segwit_conf(); + coin_conf["trezor_coin"] = "Testnet".into(); + let mm_conf = json!({ "coins": [coin_conf] }); + + let ctx = block_on(mm_ctx_with_trezor(mm_conf)); + let enable_req = json!({ + "method": "electrum", + "coin": ticker, + "servers": tbtc_electrums(), + "priv_key_policy": "Trezor", + }); + let activation_params = UtxoActivationParams::from_legacy_req(&enable_req).unwrap(); + let request: InitStandaloneCoinReq = json::from_value(json!({ + "ticker": ticker, + "activation_params": activation_params + })) + .unwrap(); - let tx_details = block_on(test_withdraw_init_loop( - ctx, - ticker, - "tb1q3zkv6g29ku3jh9vdkhxlpyek44se2s0zrv7ctn", - "0.00001", - "m/84'/1'/0'/0/0", - )) - .expect("withdraw must end successfully"); - log!("tx_hex={}", serde_json::to_string(&tx_details.tx_hex).unwrap()); -} + block_on(init_standalone_coin_loop::(ctx.clone(), request)) + .expect("coin activation must be successful"); + + let tx_details = block_on(test_withdraw_init_loop( + ctx, + ticker, + "tb1q3zkv6g29ku3jh9vdkhxlpyek44se2s0zrv7ctn", + "0.00001", + "m/84'/1'/0'/0/0", + )) + .expect("withdraw must end successfully"); + log!("tx_hex={}", serde_json::to_string(&tx_details.tx_hex).unwrap()); + } -/// Helper to init trezor and wait for completion -pub async fn init_trezor_loop_rpc(mm: &MarketMakerIt, coin: &str, timeout: u64) -> InitHwResponse { - let init = init_trezor_rpc(mm, coin).await; - let init: RpcV2Response = json::from_value(init).unwrap(); - let timeout = wait_until_ms(timeout * 1000); + /// Helper to init trezor and wait for completion + pub async fn init_trezor_loop_rpc(mm: &MarketMakerIt, coin: &str, timeout: u64) -> InitHwResponse { + let init = init_trezor_rpc(mm, coin).await; + let init: RpcV2Response = json::from_value(init).unwrap(); + let timeout = wait_until_ms(timeout * 1000); - loop { - if now_ms() > timeout { - panic!("{} init_trezor_rpc timed out", coin); - } + loop { + if now_ms() > timeout { + panic!("{} init_trezor_rpc timed out", coin); + } - let ret = init_trezor_status_rpc(mm, init.result.task_id).await; - log!("init_trezor_status_rpc: {:?}", ret); - let ret: RpcV2Response = json::from_value(ret).unwrap(); - match ret.result { - InitTrezorStatus::Ok(result) => break result, - InitTrezorStatus::Error(e) => panic!("{} trezor initialization error {:?}", coin, e), - _ => Timer::sleep(1.).await, + let ret = init_trezor_status_rpc(mm, init.result.task_id).await; + log!("init_trezor_status_rpc: {:?}", ret); + let ret: RpcV2Response = json::from_value(ret).unwrap(); + match ret.result { + InitTrezorStatus::Ok(result) => break result, + InitTrezorStatus::Error(e) => panic!("{} trezor initialization error {:?}", coin, e), + InitTrezorStatus::UserActionRequired(device_req) => { + log!("device is waiting for user action"); + let device_req = json::from_value(device_req).unwrap(); + match device_req { + HwRpcTaskAwaitingStatus::EnterTrezorPin => { + print!("Enter pin:"); + let _ = stdout().flush(); + let pin = stdin().lock().lines().next().unwrap().unwrap(); // read pin from console + let pin_action = json!({ + "action_type": "TrezorPin", + "pin": pin + }); + let _ = init_trezor_user_action_rpc(mm, init.result.task_id, pin_action).await; + }, + _ => { + panic!("Trezor passphrase is not supported in tests"); + }, + } + }, + _ => Timer::sleep(1.).await, + } } } -} -/// Helper to run init withdraw and wait for completion -async fn init_withdraw_loop_rpc( - mm: &MarketMakerIt, - coin: &str, - to: &str, - amount: &str, - from: Option, -) -> TransactionDetails { - let init = init_withdraw(mm, coin, to, amount, from).await; - let init: RpcV2Response = json::from_value(init).unwrap(); - let timeout = wait_until_ms(150000); - - loop { - if now_ms() > timeout { - panic!("{} init_withdraw timed out", coin); - } + /// Helper to run init withdraw and wait for completion + async fn init_withdraw_loop_rpc( + mm: &MarketMakerIt, + coin: &str, + to: &str, + amount: &str, + from: Option, + ) -> TransactionDetails { + let init = init_withdraw(mm, coin, to, amount, from).await; + let init: RpcV2Response = json::from_value(init).unwrap(); + let timeout = wait_until_ms(150000); + + loop { + if now_ms() > timeout { + panic!("{} init_withdraw timed out", coin); + } - let status = withdraw_status(mm, init.result.task_id).await; - log!("Withdraw status {}", json::to_string(&status).unwrap()); - let status: RpcV2Response = json::from_value(status).unwrap(); - match status.result { - WithdrawStatus::Ok(result) => break result, - WithdrawStatus::Error(e) => panic!("{} withdraw error {:?}", coin, e), - _ => Timer::sleep(1.).await, + let status = withdraw_status(mm, init.result.task_id).await; + log!("Withdraw status {}", json::to_string(&status).unwrap()); + let status: RpcV2Response = json::from_value(status).unwrap(); + match status.result { + WithdrawStatus::Ok(result) => break result, + WithdrawStatus::Error(e) => panic!("{} withdraw error {:?}", coin, e), + _ => Timer::sleep(1.).await, + } } } -} - -/// Tool to run withdraw rpc from trezor device or emulator segwit account -/// run cargo test with '--ignored' option -/// to use trezor emulator add '--features trezor-udp' option to cargo test params -/// Sample (for emulator): -/// cargo test -p mm2_main --features trezor-udp -- --nocapture --ignored test_withdraw_from_trezor_segwit_no_rpc -#[test] -#[ignore] -#[cfg(all(not(target_arch = "wasm32")))] -fn test_withdraw_from_trezor_segwit_rpc() { - let default_passphrase = "123"; // TODO: remove when we allow hardware wallet init w/o seed - let ticker = "tBTC-Segwit"; - let mut coin_conf = tbtc_segwit_conf(); - coin_conf["trezor_coin"] = "Testnet".into(); - // start bob - let conf = Mm2TestConf::seednode(default_passphrase, &json!([coin_conf])); - let mm_bob = block_on(MarketMakerIt::start_async(conf.conf, conf.rpc_password, None)).unwrap(); - - let (_bob_dump_log, _bob_dump_dashboard) = mm_bob.mm_dump(); - log!("Bob log path: {}", mm_bob.log_path.display()); - - block_on(init_trezor_loop_rpc(&mm_bob, ticker, 60)); - - let utxo_bob = block_on(enable_utxo_v2_electrum( - &mm_bob, - ticker, - tbtc_electrums(), - 80, - Some("Trezor"), - )); - log!("enable UTXO bob {:?}", utxo_bob); - - let tx_details = block_on(init_withdraw_loop_rpc( - &mm_bob, - ticker, - "tb1q3zkv6g29ku3jh9vdkhxlpyek44se2s0zrv7ctn", - "0.00001", - Some(json!({"derivation_path": "m/84'/1'/0'/0/0"})), - )); - log!("tx_hex={}", serde_json::to_string(&tx_details.tx_hex).unwrap()); - block_on(mm_bob.stop()).unwrap(); -} - -/// Tool to run withdraw rpc from trezor device or emulator p2pkh account -/// run cargo test with '--ignored' option -/// to use trezor emulator add '--features trezor-udp' option to cargo test params -/// Sample (for emulator): -/// cargo test -p mm2_main --features trezor-udp -- --nocapture --ignored test_withdraw_from_trezor_p2pkh_rpc -#[test] -#[ignore] -#[cfg(all(not(target_arch = "wasm32")))] -fn test_withdraw_from_trezor_p2pkh_rpc() { - let default_passphrase = "123"; // TODO: remove when we allow hardware wallet init w/o seed - let ticker = "tBTC"; - let mut coin_conf = tbtc_legacy_conf(); - coin_conf["trezor_coin"] = "Testnet".into(); - coin_conf["derivation_path"] = "m/44'/1'".into(); - - // start bob - let conf = Mm2TestConf::seednode(default_passphrase, &json!([coin_conf])); - let mm_bob = block_on(MarketMakerIt::start_async(conf.conf, conf.rpc_password, None)).unwrap(); - - let (_bob_dump_log, _bob_dump_dashboard) = mm_bob.mm_dump(); - log!("Bob log path: {}", mm_bob.log_path.display()); - - block_on(init_trezor_loop_rpc(&mm_bob, ticker, 60)); - - let utxo_bob = block_on(enable_utxo_v2_electrum( - &mm_bob, - ticker, - tbtc_electrums(), - 80, - Some("Trezor"), - )); - log!("enable UTXO bob {:?}", utxo_bob); + /// Tool to run withdraw rpc from trezor device or emulator segwit account + /// run cargo test with '--features run-device-tests' option + /// to use trezor emulator also add '--features trezor-udp' option to cargo params + #[test] + fn test_withdraw_from_trezor_segwit_rpc() { + let default_passphrase = "123"; // TODO: remove when we allow hardware wallet init w/o seed + let ticker = "tBTC-Segwit"; + let mut coin_conf = tbtc_segwit_conf(); + coin_conf["trezor_coin"] = "Testnet".into(); + + // start bob + let conf = Mm2TestConf::seednode(default_passphrase, &json!([coin_conf])); + let mm_bob = block_on(MarketMakerIt::start_async(conf.conf, conf.rpc_password, None)).unwrap(); + + let (_bob_dump_log, _bob_dump_dashboard) = mm_bob.mm_dump(); + log!("Bob log path: {}", mm_bob.log_path.display()); + + block_on(init_trezor_loop_rpc(&mm_bob, ticker, 60)); + + let utxo_bob = block_on(enable_utxo_v2_electrum( + &mm_bob, + ticker, + tbtc_electrums(), + 80, + Some("Trezor"), + )); + log!("enable UTXO bob {:?}", utxo_bob); + + let tx_details = block_on(init_withdraw_loop_rpc( + &mm_bob, + ticker, + "tb1q3zkv6g29ku3jh9vdkhxlpyek44se2s0zrv7ctn", + "0.00001", + Some(json!({"derivation_path": "m/84'/1'/0'/0/0"})), + )); + log!("tx_hex={}", serde_json::to_string(&tx_details.tx_hex).unwrap()); + block_on(mm_bob.stop()).unwrap(); + } - let tx_details = block_on(init_withdraw_loop_rpc( - &mm_bob, - ticker, - "miuSj7rXDxbaHsqf1GmoKkygTBnoi3iwzj", - "0.00001", - Some(json!({"derivation_path": "m/44'/1'/0'/0/0"})), - )); - log!("tx_hex={}", serde_json::to_string(&tx_details.tx_hex).unwrap()); - block_on(mm_bob.stop()).unwrap(); + /// Tool to run withdraw rpc from trezor device or emulator p2pkh account + /// run cargo test with '--features run-device-tests' option + /// to use trezor emulator also add '--features trezor-udp' option to cargo params + #[test] + fn test_withdraw_from_trezor_p2pkh_rpc() { + let default_passphrase = "123"; // TODO: remove when we allow hardware wallet init w/o seed + let ticker = "tBTC"; + let mut coin_conf = tbtc_legacy_conf(); + coin_conf["trezor_coin"] = "Testnet".into(); + coin_conf["derivation_path"] = "m/44'/1'".into(); + + // start bob + let conf = Mm2TestConf::seednode(default_passphrase, &json!([coin_conf])); + let mm_bob = block_on(MarketMakerIt::start_async(conf.conf, conf.rpc_password, None)).unwrap(); + + let (_bob_dump_log, _bob_dump_dashboard) = mm_bob.mm_dump(); + log!("Bob log path: {}", mm_bob.log_path.display()); + + block_on(init_trezor_loop_rpc(&mm_bob, ticker, 60)); + + let utxo_bob = block_on(enable_utxo_v2_electrum( + &mm_bob, + ticker, + tbtc_electrums(), + 80, + Some("Trezor"), + )); + log!("enable UTXO bob {:?}", utxo_bob); + + let tx_details = block_on(init_withdraw_loop_rpc( + &mm_bob, + ticker, + "miuSj7rXDxbaHsqf1GmoKkygTBnoi3iwzj", + "0.00001", + Some(json!({"derivation_path": "m/44'/1'/0'/0/0"})), + )); + log!("tx_hex={}", serde_json::to_string(&tx_details.tx_hex).unwrap()); + block_on(mm_bob.stop()).unwrap(); + } } diff --git a/mm2src/mm2_test_helpers/src/for_tests.rs b/mm2src/mm2_test_helpers/src/for_tests.rs index a3028632a3..11218ce2bf 100644 --- a/mm2src/mm2_test_helpers/src/for_tests.rs +++ b/mm2src/mm2_test_helpers/src/for_tests.rs @@ -3295,3 +3295,25 @@ pub async fn init_trezor_status_rpc(mm: &MarketMakerIt, task_id: u64) -> Json { ); json::from_str(&request.1).unwrap() } + +pub async fn init_trezor_user_action_rpc(mm: &MarketMakerIt, task_id: u64, user_action: Json) -> Json { + let request = mm + .rpc(&json!({ + "userpass": mm.userpass, + "method": "task::init_trezor::user_action", + "mmrpc": "2.0", + "params": { + "task_id": task_id, + "user_action": user_action + } + })) + .await + .unwrap(); + assert_eq!( + request.0, + StatusCode::OK, + "'task::init_trezor::user_action' failed: {}", + request.1 + ); + json::from_str(&request.1).unwrap() +} From ef10df937cecda46d359f21915858f062f6852b7 Mon Sep 17 00:00:00 2001 From: dimxy Date: Mon, 27 Nov 2023 17:37:26 +0500 Subject: [PATCH 15/17] fix wasm build for trezor init --- mm2src/coins/z_coin.rs | 1 + mm2src/crypto/src/hw_client.rs | 12 ++++++------ mm2src/mm2_main/src/lp_init/init_metamask.rs | 3 ++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/mm2src/coins/z_coin.rs b/mm2src/coins/z_coin.rs index 4e91649e08..3d2f8056ea 100644 --- a/mm2src/coins/z_coin.rs +++ b/mm2src/coins/z_coin.rs @@ -1,6 +1,7 @@ use crate::coin_errors::MyAddressError; #[cfg(not(target_arch = "wasm32"))] use crate::my_tx_history_v2::{MyTxHistoryErrorV2, MyTxHistoryRequestV2, MyTxHistoryResponseV2}; +#[cfg(not(target_arch = "wasm32"))] use crate::rpc_command::init_withdraw::WithdrawTaskHandleShared; #[cfg(not(target_arch = "wasm32"))] use crate::rpc_command::init_withdraw::{InitWithdrawCoin, WithdrawInProgressStatus}; diff --git a/mm2src/crypto/src/hw_client.rs b/mm2src/crypto/src/hw_client.rs index fbd7239e11..4cb78b05b5 100644 --- a/mm2src/crypto/src/hw_client.rs +++ b/mm2src/crypto/src/hw_client.rs @@ -92,9 +92,9 @@ impl HwClient { } #[cfg(target_arch = "wasm32")] - pub(crate) async fn trezor( - processor: &Processor, - ) -> MmResult> { + pub(crate) async fn trezor( + processor: Arc>, + ) -> MmResult> { let timeout = processor.on_connect().await?; let fut = async move { @@ -184,9 +184,9 @@ impl HwClient { } #[cfg(target_os = "ios")] - pub(crate) async fn trezor( - _processor: &Processor, - ) -> MmResult> { + pub(crate) async fn trezor( + _processor: Arc>, + ) -> MmResult> { MmError::err(HwProcessingError::HwError(HwError::Internal( "Not supported on iOS!".into(), ))) diff --git a/mm2src/mm2_main/src/lp_init/init_metamask.rs b/mm2src/mm2_main/src/lp_init/init_metamask.rs index fb3494dc58..56f4a4a705 100644 --- a/mm2src/mm2_main/src/lp_init/init_metamask.rs +++ b/mm2src/mm2_main/src/lp_init/init_metamask.rs @@ -13,6 +13,7 @@ use rpc_task::rpc_common::{CancelRpcTaskError, CancelRpcTaskRequest, InitRpcTask RpcTaskStatusRequest}; use rpc_task::{RpcTask, RpcTaskError, RpcTaskHandle, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatus, RpcTaskTypes}; use std::time::Duration; +use std::sync::Arc; pub type InitMetamaskManagerShared = RpcTaskManagerShared; pub type InitMetamaskStatus = @@ -120,7 +121,7 @@ impl RpcTask for InitMetamaskTask { } } - async fn run(&mut self, task_handle: InitMetamaskTaskHandleShared) -> Result> { + async fn run(&mut self, _task_handle: InitMetamaskTaskHandleShared) -> Result> { let crypto_ctx = CryptoCtx::from_ctx(&self.ctx)?; let metamask = crypto_ctx.init_metamask_ctx(self.req.project.clone()).await?; From ad8a86d465305a36ec1e5d4e771f977191083e5f Mon Sep 17 00:00:00 2001 From: dimxy Date: Wed, 29 Nov 2023 18:44:11 +0500 Subject: [PATCH 16/17] make a few fn's inline --- mm2src/coins/utxo_signer/src/sign_params.rs | 2 ++ mm2src/coins_activation/src/lib.rs | 1 + mm2src/coins_activation/src/utxo_activation/mod.rs | 1 - 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mm2src/coins/utxo_signer/src/sign_params.rs b/mm2src/coins/utxo_signer/src/sign_params.rs index 4775fa0044..e66d504eb2 100644 --- a/mm2src/coins/utxo_signer/src/sign_params.rs +++ b/mm2src/coins/utxo_signer/src/sign_params.rs @@ -43,6 +43,7 @@ pub enum OutputDestination { impl OutputDestination { pub fn plain(address: String) -> OutputDestination { OutputDestination::Plain { address } } + #[inline] pub fn change(derivation_path: DerivationPath, addr_format: AddressFormat) -> OutputDestination { OutputDestination::Change { derivation_path, @@ -58,6 +59,7 @@ pub struct SendingOutputInfo { impl SendingOutputInfo { /// For now, returns [`TrezorOutputScriptType::PayToAddress`] since we don't support SLP tokens yet. + #[inline] pub fn trezor_output_script_type(&self) -> TrezorOutputScriptType { match self.destination_address { OutputDestination::Change { ref addr_format, .. } if *addr_format == AddressFormat::Segwit => { diff --git a/mm2src/coins_activation/src/lib.rs b/mm2src/coins_activation/src/lib.rs index 89cc5aca85..fc3982e17e 100644 --- a/mm2src/coins_activation/src/lib.rs +++ b/mm2src/coins_activation/src/lib.rs @@ -26,6 +26,7 @@ mod tendermint_token_activation; mod tendermint_with_assets_activation; mod token; mod utxo_activation; + #[cfg(not(target_arch = "wasm32"))] pub use utxo_activation::for_tests; #[cfg(not(target_arch = "wasm32"))] mod z_coin_activation; diff --git a/mm2src/coins_activation/src/utxo_activation/mod.rs b/mm2src/coins_activation/src/utxo_activation/mod.rs index fe51327b5b..5ef6021199 100644 --- a/mm2src/coins_activation/src/utxo_activation/mod.rs +++ b/mm2src/coins_activation/src/utxo_activation/mod.rs @@ -15,7 +15,6 @@ pub mod for_tests { use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::{MmResult, NotEqual}; use rpc_task::RpcTaskStatus; - //use serde_json::{Value, self}; use crate::{init_standalone_coin, init_standalone_coin_status, standalone_coin::{InitStandaloneCoinActivationOps, InitStandaloneCoinError, From bbe849ff50d553e698c1fb4cf209d821c5b65fef Mon Sep 17 00:00:00 2001 From: dimxy Date: Fri, 15 Dec 2023 19:49:16 +0500 Subject: [PATCH 17/17] refactor: eliminate unused TaskHandle aliases --- mm2src/coins/hd_confirm_address.rs | 3 +-- mm2src/coins/rpc_command/get_new_address.rs | 10 ++++------ mm2src/coins/rpc_command/init_account_balance.rs | 7 ++----- mm2src/coins/rpc_command/init_create_account.rs | 6 +++--- .../coins/rpc_command/init_scan_for_new_addresses.rs | 7 ++----- mm2src/coins/rpc_command/init_withdraw.rs | 7 ++----- mm2src/coins_activation/src/l2/init_l2.rs | 7 ++----- mm2src/coins_activation/src/l2/mod.rs | 2 +- mm2src/coins_activation/src/lightning_activation.rs | 7 +++---- .../src/standalone_coin/init_standalone_coin.rs | 7 ++----- mm2src/coins_activation/src/standalone_coin/mod.rs | 2 +- .../src/utxo_activation/common_impl.rs | 3 +-- .../src/utxo_activation/init_qtum_activation.rs | 6 ++---- .../utxo_activation/init_utxo_standard_activation.rs | 6 ++---- mm2src/coins_activation/src/z_coin_activation.rs | 6 ++---- mm2src/mm2_main/src/lp_init/init_hw.rs | 6 +++--- mm2src/mm2_main/src/lp_init/init_metamask.rs | 5 ++--- 17 files changed, 35 insertions(+), 62 deletions(-) diff --git a/mm2src/coins/hd_confirm_address.rs b/mm2src/coins/hd_confirm_address.rs index 2c8c9b40f3..b028f9fd9e 100644 --- a/mm2src/coins/hd_confirm_address.rs +++ b/mm2src/coins/hd_confirm_address.rs @@ -1,5 +1,3 @@ -use std::sync::Arc; - use async_trait::async_trait; use bip32::DerivationPath; use crypto::hw_rpc_task::HwConnectStatuses; @@ -10,6 +8,7 @@ use enum_from::{EnumFromInner, EnumFromStringify}; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use rpc_task::{RpcTask, RpcTaskError, RpcTaskHandleShared}; +use std::sync::Arc; const SHOW_ADDRESS_ON_DISPLAY: bool = true; diff --git a/mm2src/coins/rpc_command/get_new_address.rs b/mm2src/coins/rpc_command/get_new_address.rs index 4e3ed96586..6d57870ac5 100644 --- a/mm2src/coins/rpc_command/get_new_address.rs +++ b/mm2src/coins/rpc_command/get_new_address.rs @@ -14,17 +14,15 @@ use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use rpc_task::rpc_common::{CancelRpcTaskError, CancelRpcTaskRequest, InitRpcTaskResponse, RpcTaskStatusError, RpcTaskStatusRequest, RpcTaskUserActionError}; -use rpc_task::{RpcTask, RpcTaskError, RpcTaskHandle, RpcTaskHandleShared, RpcTaskManager, RpcTaskManagerShared, - RpcTaskStatus, RpcTaskTypes}; -use std::sync::Arc; +use rpc_task::{RpcTask, RpcTaskError, RpcTaskHandleShared, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatus, + RpcTaskTypes}; use std::time::Duration; pub type GetNewAddressUserAction = HwRpcTaskUserAction; pub type GetNewAddressAwaitingStatus = HwRpcTaskAwaitingStatus; pub type GetNewAddressTaskManager = RpcTaskManager; pub type GetNewAddressTaskManagerShared = RpcTaskManagerShared; -pub type GetNewAddressTaskHandle = RpcTaskHandle; -pub type GetNewAddressTaskHandleArc = Arc>; +pub type GetNewAddressTaskHandleShared = RpcTaskHandleShared; pub type GetNewAddressRpcTaskStatus = RpcTaskStatus< GetNewAddressResponse, GetNewAddressRpcError, @@ -284,7 +282,7 @@ impl RpcTask for InitGetNewAddressTask { ctx: &MmArc, coin: &Coin, params: GetNewAddressParams, - task_handle: GetNewAddressTaskHandleArc, + task_handle: GetNewAddressTaskHandleShared, ) -> MmResult where Coin: GetNewAddressRpcOps + Send + Sync, diff --git a/mm2src/coins/rpc_command/init_account_balance.rs b/mm2src/coins/rpc_command/init_account_balance.rs index 89845b6011..46df549783 100644 --- a/mm2src/coins/rpc_command/init_account_balance.rs +++ b/mm2src/coins/rpc_command/init_account_balance.rs @@ -1,5 +1,3 @@ -use std::sync::Arc; - use crate::coin_balance::HDAccountBalance; use crate::rpc_command::hd_account_balance_rpc_error::HDAccountBalanceRpcError; use crate::{lp_coinfind_or_err, CoinsContext, MmCoinEnum}; @@ -9,14 +7,13 @@ use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use rpc_task::rpc_common::{CancelRpcTaskError, CancelRpcTaskRequest, InitRpcTaskResponse, RpcTaskStatusError, RpcTaskStatusRequest}; -use rpc_task::{RpcTask, RpcTaskHandle, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatus, RpcTaskTypes}; +use rpc_task::{RpcTask, RpcTaskHandleShared, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatus, RpcTaskTypes}; pub type AccountBalanceUserAction = SerdeInfallible; pub type AccountBalanceAwaitingStatus = SerdeInfallible; pub type AccountBalanceTaskManager = RpcTaskManager; pub type AccountBalanceTaskManagerShared = RpcTaskManagerShared; -pub type InitAccountBalanceTaskHandle = RpcTaskHandle; -pub type InitAccountBalanceTaskHandleShared = Arc; +pub type InitAccountBalanceTaskHandleShared = RpcTaskHandleShared; pub type AccountBalanceRpcTaskStatus = RpcTaskStatus< HDAccountBalance, HDAccountBalanceRpcError, diff --git a/mm2src/coins/rpc_command/init_create_account.rs b/mm2src/coins/rpc_command/init_create_account.rs index aa965ff9e2..c67cd8cd3d 100644 --- a/mm2src/coins/rpc_command/init_create_account.rs +++ b/mm2src/coins/rpc_command/init_create_account.rs @@ -15,7 +15,8 @@ use mm2_err_handle::prelude::*; use parking_lot::Mutex as PaMutex; use rpc_task::rpc_common::{CancelRpcTaskError, CancelRpcTaskRequest, InitRpcTaskResponse, RpcTaskStatusError, RpcTaskStatusRequest, RpcTaskUserActionError}; -use rpc_task::{RpcTask, RpcTaskError, RpcTaskHandle, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatus, RpcTaskTypes}; +use rpc_task::{RpcTask, RpcTaskError, RpcTaskHandleShared, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatus, + RpcTaskTypes}; use std::sync::Arc; use std::time::Duration; @@ -23,8 +24,7 @@ pub type CreateAccountUserAction = HwRpcTaskUserAction; pub type CreateAccountAwaitingStatus = HwRpcTaskAwaitingStatus; pub type CreateAccountTaskManager = RpcTaskManager; pub type CreateAccountTaskManagerShared = RpcTaskManagerShared; -pub type CreateAccountTaskHandle = RpcTaskHandle; -pub type CreateAccountTaskHandleShared = Arc; +pub type CreateAccountTaskHandleShared = RpcTaskHandleShared; pub type CreateAccountRpcTaskStatus = RpcTaskStatus; diff --git a/mm2src/coins/rpc_command/init_scan_for_new_addresses.rs b/mm2src/coins/rpc_command/init_scan_for_new_addresses.rs index a028dfb4f4..7f0c1e4ce9 100644 --- a/mm2src/coins/rpc_command/init_scan_for_new_addresses.rs +++ b/mm2src/coins/rpc_command/init_scan_for_new_addresses.rs @@ -1,5 +1,3 @@ -use std::sync::Arc; - use crate::coin_balance::HDAddressBalance; use crate::rpc_command::hd_account_balance_rpc_error::HDAccountBalanceRpcError; use crate::{lp_coinfind_or_err, CoinsContext, MmCoinEnum}; @@ -10,14 +8,13 @@ use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use rpc_task::rpc_common::{CancelRpcTaskError, CancelRpcTaskRequest, InitRpcTaskResponse, RpcTaskStatusError, RpcTaskStatusRequest}; -use rpc_task::{RpcTask, RpcTaskHandle, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatus, RpcTaskTypes}; +use rpc_task::{RpcTask, RpcTaskHandleShared, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatus, RpcTaskTypes}; pub type ScanAddressesUserAction = SerdeInfallible; pub type ScanAddressesAwaitingStatus = SerdeInfallible; pub type ScanAddressesTaskManager = RpcTaskManager; pub type ScanAddressesTaskManagerShared = RpcTaskManagerShared; -pub type ScanAddressesTaskHandle = RpcTaskHandle; -pub type ScanAddressesTaskHandleShared = Arc; +pub type ScanAddressesTaskHandleShared = RpcTaskHandleShared; pub type ScanAddressesRpcTaskStatus = RpcTaskStatus< ScanAddressesResponse, HDAccountBalanceRpcError, diff --git a/mm2src/coins/rpc_command/init_withdraw.rs b/mm2src/coins/rpc_command/init_withdraw.rs index 4977553036..e7c6d94ae2 100644 --- a/mm2src/coins/rpc_command/init_withdraw.rs +++ b/mm2src/coins/rpc_command/init_withdraw.rs @@ -1,5 +1,3 @@ -use std::sync::Arc; - use crate::{lp_coinfind_or_err, CoinsContext, MmCoinEnum, WithdrawError}; use crate::{TransactionDetails, WithdrawRequest}; use async_trait::async_trait; @@ -9,7 +7,7 @@ use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use rpc_task::rpc_common::{CancelRpcTaskError, CancelRpcTaskRequest, InitRpcTaskResponse, RpcTaskStatusError, RpcTaskStatusRequest, RpcTaskUserActionError}; -use rpc_task::{RpcTask, RpcTaskHandle, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatusAlias, RpcTaskTypes}; +use rpc_task::{RpcTask, RpcTaskHandleShared, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatusAlias, RpcTaskTypes}; pub type WithdrawAwaitingStatus = HwRpcTaskAwaitingStatus; pub type WithdrawUserAction = HwRpcTaskUserAction; @@ -20,8 +18,7 @@ pub type WithdrawStatusRequest = RpcTaskStatusRequest; pub type WithdrawUserActionRequest = HwRpcTaskUserActionRequest; pub type WithdrawTaskManager = RpcTaskManager; pub type WithdrawTaskManagerShared = RpcTaskManagerShared; -pub type WithdrawTaskHandle = RpcTaskHandle; -pub type WithdrawTaskHandleShared = Arc; +pub type WithdrawTaskHandleShared = RpcTaskHandleShared; pub type WithdrawRpcStatus = RpcTaskStatusAlias; pub type WithdrawInitResult = Result>; diff --git a/mm2src/coins_activation/src/l2/init_l2.rs b/mm2src/coins_activation/src/l2/init_l2.rs index 89f3bb5989..20e66ebbde 100644 --- a/mm2src/coins_activation/src/l2/init_l2.rs +++ b/mm2src/coins_activation/src/l2/init_l2.rs @@ -10,18 +10,15 @@ use common::SuccessResponse; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use rpc_task::rpc_common::{CancelRpcTaskRequest, InitRpcTaskResponse, RpcTaskStatusRequest, RpcTaskUserActionRequest}; -use rpc_task::{RpcTask, RpcTaskHandle, RpcTaskHandleShared, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatus, - RpcTaskTypes}; +use rpc_task::{RpcTask, RpcTaskHandleShared, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatus, RpcTaskTypes}; use serde_derive::Deserialize; use serde_json::Value as Json; -use std::sync::Arc; pub type InitL2Response = InitRpcTaskResponse; pub type InitL2StatusRequest = RpcTaskStatusRequest; pub type InitL2UserActionRequest = RpcTaskUserActionRequest; pub type InitL2TaskManagerShared = RpcTaskManagerShared>; -pub type InitL2TaskHandle = RpcTaskHandle>; -pub type InitL2TaskHandleShared = Arc>; +pub type InitL2TaskHandleShared = RpcTaskHandleShared>; #[derive(Debug, Deserialize)] pub struct InitL2Req { diff --git a/mm2src/coins_activation/src/l2/mod.rs b/mm2src/coins_activation/src/l2/mod.rs index b5168fa87e..f1b0f93614 100644 --- a/mm2src/coins_activation/src/l2/mod.rs +++ b/mm2src/coins_activation/src/l2/mod.rs @@ -2,5 +2,5 @@ mod init_l2; mod init_l2_error; pub use init_l2::{cancel_init_l2, init_l2, init_l2_status, init_l2_user_action, InitL2ActivationOps, - InitL2InitialStatus, InitL2Task, InitL2TaskHandle, InitL2TaskManagerShared, L2ProtocolParams}; + InitL2InitialStatus, InitL2Task, InitL2TaskHandleShared, InitL2TaskManagerShared, L2ProtocolParams}; pub use init_l2_error::InitL2Error; diff --git a/mm2src/coins_activation/src/lightning_activation.rs b/mm2src/coins_activation/src/lightning_activation.rs index 144c197f54..1d2f9ec232 100644 --- a/mm2src/coins_activation/src/lightning_activation.rs +++ b/mm2src/coins_activation/src/lightning_activation.rs @@ -1,6 +1,6 @@ use crate::context::CoinsActivationContext; -use crate::l2::{InitL2ActivationOps, InitL2Error, InitL2InitialStatus, InitL2TaskHandle, InitL2TaskManagerShared, - L2ProtocolParams}; +use crate::l2::{InitL2ActivationOps, InitL2Error, InitL2InitialStatus, InitL2TaskHandleShared, + InitL2TaskManagerShared, L2ProtocolParams}; use crate::prelude::*; use async_trait::async_trait; use coins::coin_errors::MyAddressError; @@ -37,8 +37,7 @@ use std::sync::Arc; const DEFAULT_LISTENING_PORT: u16 = 9735; pub type LightningTaskManagerShared = InitL2TaskManagerShared; -pub type LightningRpcTaskHandle = InitL2TaskHandle; -pub type LightningRpcTaskHandleShared = Arc; +pub type LightningRpcTaskHandleShared = InitL2TaskHandleShared; pub type LightningAwaitingStatus = HwRpcTaskAwaitingStatus; pub type LightningUserAction = HwRpcTaskUserAction; diff --git a/mm2src/coins_activation/src/standalone_coin/init_standalone_coin.rs b/mm2src/coins_activation/src/standalone_coin/init_standalone_coin.rs index 89861d8b1a..314f3066b4 100644 --- a/mm2src/coins_activation/src/standalone_coin/init_standalone_coin.rs +++ b/mm2src/coins_activation/src/standalone_coin/init_standalone_coin.rs @@ -13,19 +13,16 @@ use mm2_err_handle::prelude::*; use mm2_metrics::MetricsArc; use mm2_number::BigDecimal; use rpc_task::rpc_common::{CancelRpcTaskRequest, InitRpcTaskResponse, RpcTaskStatusRequest, RpcTaskUserActionRequest}; -use rpc_task::{RpcTask, RpcTaskHandle, RpcTaskHandleShared, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatus, - RpcTaskTypes}; +use rpc_task::{RpcTask, RpcTaskHandleShared, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatus, RpcTaskTypes}; use serde_derive::Deserialize; use serde_json::Value as Json; use std::collections::HashMap; -use std::sync::Arc; pub type InitStandaloneCoinResponse = InitRpcTaskResponse; pub type InitStandaloneCoinStatusRequest = RpcTaskStatusRequest; pub type InitStandaloneCoinUserActionRequest = RpcTaskUserActionRequest; pub type InitStandaloneCoinTaskManagerShared = RpcTaskManagerShared>; -pub type InitStandaloneCoinTaskHandle = RpcTaskHandle>; -pub type InitStandaloneCoinTaskHandleShared = Arc>; +pub type InitStandaloneCoinTaskHandleShared = RpcTaskHandleShared>; #[derive(Debug, Deserialize, Clone)] pub struct InitStandaloneCoinReq { diff --git a/mm2src/coins_activation/src/standalone_coin/mod.rs b/mm2src/coins_activation/src/standalone_coin/mod.rs index e33cb38280..0ab208abc2 100644 --- a/mm2src/coins_activation/src/standalone_coin/mod.rs +++ b/mm2src/coins_activation/src/standalone_coin/mod.rs @@ -4,6 +4,6 @@ mod init_standalone_coin_error; pub use init_standalone_coin::{cancel_init_standalone_coin, init_standalone_coin, init_standalone_coin_status, init_standalone_coin_user_action, InitStandaloneCoinActivationOps, InitStandaloneCoinInitialStatus, InitStandaloneCoinReq, - InitStandaloneCoinStatusRequest, InitStandaloneCoinTask, InitStandaloneCoinTaskHandle, + InitStandaloneCoinStatusRequest, InitStandaloneCoinTask, InitStandaloneCoinTaskHandleShared, InitStandaloneCoinTaskManagerShared}; pub use init_standalone_coin_error::InitStandaloneCoinError; diff --git a/mm2src/coins_activation/src/utxo_activation/common_impl.rs b/mm2src/coins_activation/src/utxo_activation/common_impl.rs index d997b5e851..7f9a69faa5 100644 --- a/mm2src/coins_activation/src/utxo_activation/common_impl.rs +++ b/mm2src/coins_activation/src/utxo_activation/common_impl.rs @@ -32,8 +32,7 @@ where AwaitingStatus = UtxoStandardAwaitingStatus, UserAction = UtxoStandardUserAction, > + EnableCoinBalanceOps - + MarketCoinOps - + Clone, + + MarketCoinOps, { let ticker = coin.ticker().to_owned(); let current_block = coin diff --git a/mm2src/coins_activation/src/utxo_activation/init_qtum_activation.rs b/mm2src/coins_activation/src/utxo_activation/init_qtum_activation.rs index 3dc6c91ed4..ae8cdec6ce 100644 --- a/mm2src/coins_activation/src/utxo_activation/init_qtum_activation.rs +++ b/mm2src/coins_activation/src/utxo_activation/init_qtum_activation.rs @@ -1,6 +1,6 @@ use crate::context::CoinsActivationContext; use crate::prelude::TryFromCoinProtocol; -use crate::standalone_coin::{InitStandaloneCoinActivationOps, InitStandaloneCoinTaskHandle, +use crate::standalone_coin::{InitStandaloneCoinActivationOps, InitStandaloneCoinTaskHandleShared, InitStandaloneCoinTaskManagerShared}; use crate::utxo_activation::common_impl::{get_activation_result, priv_key_build_policy, start_history_background_fetching}; @@ -20,11 +20,9 @@ use mm2_metrics::MetricsArc; use mm2_number::BigDecimal; use serde_json::Value as Json; use std::collections::HashMap; -use std::sync::Arc; pub type QtumTaskManagerShared = InitStandaloneCoinTaskManagerShared; -pub type QtumRpcTaskHandle = InitStandaloneCoinTaskHandle; -pub type QtumRpcTaskHandleShared = Arc; +pub type QtumRpcTaskHandleShared = InitStandaloneCoinTaskHandleShared; #[derive(Clone)] pub struct QtumProtocolInfo; diff --git a/mm2src/coins_activation/src/utxo_activation/init_utxo_standard_activation.rs b/mm2src/coins_activation/src/utxo_activation/init_utxo_standard_activation.rs index 8d2e9a28c8..206c750f15 100644 --- a/mm2src/coins_activation/src/utxo_activation/init_utxo_standard_activation.rs +++ b/mm2src/coins_activation/src/utxo_activation/init_utxo_standard_activation.rs @@ -1,6 +1,6 @@ use crate::context::CoinsActivationContext; use crate::prelude::TryFromCoinProtocol; -use crate::standalone_coin::{InitStandaloneCoinActivationOps, InitStandaloneCoinTaskHandle, +use crate::standalone_coin::{InitStandaloneCoinActivationOps, InitStandaloneCoinTaskHandleShared, InitStandaloneCoinTaskManagerShared}; use crate::utxo_activation::common_impl::{get_activation_result, priv_key_build_policy, start_history_background_fetching}; @@ -21,11 +21,9 @@ use mm2_metrics::MetricsArc; use mm2_number::BigDecimal; use serde_json::Value as Json; use std::collections::HashMap; -use std::sync::Arc; pub type UtxoStandardTaskManagerShared = InitStandaloneCoinTaskManagerShared; -pub type UtxoStandardRpcTaskHandle = InitStandaloneCoinTaskHandle; -pub type UtxoStandardRpcTaskHandleShared = Arc; +pub type UtxoStandardRpcTaskHandleShared = InitStandaloneCoinTaskHandleShared; #[derive(Clone)] pub struct UtxoStandardProtocolInfo; diff --git a/mm2src/coins_activation/src/z_coin_activation.rs b/mm2src/coins_activation/src/z_coin_activation.rs index 9bae24fec4..f8140e2685 100644 --- a/mm2src/coins_activation/src/z_coin_activation.rs +++ b/mm2src/coins_activation/src/z_coin_activation.rs @@ -1,7 +1,7 @@ use crate::context::CoinsActivationContext; use crate::prelude::*; use crate::standalone_coin::{InitStandaloneCoinActivationOps, InitStandaloneCoinError, - InitStandaloneCoinInitialStatus, InitStandaloneCoinTaskHandle, + InitStandaloneCoinInitialStatus, InitStandaloneCoinTaskHandleShared, InitStandaloneCoinTaskManagerShared}; use async_trait::async_trait; use coins::coin_balance::{CoinBalanceReport, IguanaWalletBalance}; @@ -23,12 +23,10 @@ use ser_error_derive::SerializeErrorType; use serde_derive::Serialize; use serde_json::Value as Json; use std::collections::HashMap; -use std::sync::Arc; use std::time::Duration; pub type ZcoinTaskManagerShared = InitStandaloneCoinTaskManagerShared; -pub type ZcoinRpcTaskHandle = InitStandaloneCoinTaskHandle; -pub type ZcoinRpcTaskHandleShared = Arc; +pub type ZcoinRpcTaskHandleShared = InitStandaloneCoinTaskHandleShared; pub type ZcoinAwaitingStatus = HwRpcTaskAwaitingStatus; pub type ZcoinUserAction = HwRpcTaskUserAction; diff --git a/mm2src/mm2_main/src/lp_init/init_hw.rs b/mm2src/mm2_main/src/lp_init/init_hw.rs index 3b00b63139..d9bc45da49 100644 --- a/mm2src/mm2_main/src/lp_init/init_hw.rs +++ b/mm2src/mm2_main/src/lp_init/init_hw.rs @@ -12,7 +12,8 @@ use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use rpc_task::rpc_common::{CancelRpcTaskError, CancelRpcTaskRequest, InitRpcTaskResponse, RpcTaskStatusError, RpcTaskStatusRequest, RpcTaskUserActionError}; -use rpc_task::{RpcTask, RpcTaskError, RpcTaskHandle, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatus, RpcTaskTypes}; +use rpc_task::{RpcTask, RpcTaskError, RpcTaskHandleShared, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatus, + RpcTaskTypes}; use std::sync::Arc; use std::time::Duration; @@ -24,8 +25,7 @@ pub type InitHwUserAction = HwRpcTaskUserAction; pub type InitHwTaskManagerShared = RpcTaskManagerShared; pub type InitHwStatus = RpcTaskStatus; -type InitHwTaskHandle = RpcTaskHandle; -type InitHwTaskHandleShared = Arc; +type InitHwTaskHandleShared = RpcTaskHandleShared; #[derive(Clone, Display, EnumFromTrait, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")] diff --git a/mm2src/mm2_main/src/lp_init/init_metamask.rs b/mm2src/mm2_main/src/lp_init/init_metamask.rs index 56f4a4a705..f624a7c5c4 100644 --- a/mm2src/mm2_main/src/lp_init/init_metamask.rs +++ b/mm2src/mm2_main/src/lp_init/init_metamask.rs @@ -11,7 +11,7 @@ use mm2_err_handle::common_errors::WithInternal; use mm2_err_handle::prelude::*; use rpc_task::rpc_common::{CancelRpcTaskError, CancelRpcTaskRequest, InitRpcTaskResponse, RpcTaskStatusError, RpcTaskStatusRequest}; -use rpc_task::{RpcTask, RpcTaskError, RpcTaskHandle, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatus, RpcTaskTypes}; +use rpc_task::{RpcTask, RpcTaskError, RpcTaskHandleShared, RpcTaskManager, RpcTaskManagerShared, RpcTaskStatus, RpcTaskTypes}; use std::time::Duration; use std::sync::Arc; @@ -21,8 +21,7 @@ pub type InitMetamaskStatus = type InitMetamaskUserAction = SerdeInfallible; type InitMetamaskAwaitingStatus = SerdeInfallible; -type InitMetamaskTaskHandle = RpcTaskHandle; -type InitMetamaskTaskHandleShared = Arc; +type InitMetamaskTaskHandleShared = RpcTaskHandleShared; #[derive(Clone, Display, EnumFromTrait, Serialize, SerializeErrorType)] #[serde(tag = "error_type", content = "error_data")]