diff --git a/mm2src/coins/utxo/utxo_withdraw.rs b/mm2src/coins/utxo/utxo_withdraw.rs index 8721a6a433..29ef2f5963 100644 --- a/mm2src/coins/utxo/utxo_withdraw.rs +++ b/mm2src/coins/utxo/utxo_withdraw.rs @@ -3,7 +3,7 @@ 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}; use crate::{CoinWithDerivationMethod, GetWithdrawSenderAddress, MarketCoinOps, TransactionData, TransactionDetails, - WithdrawError, WithdrawFee, WithdrawRequest, WithdrawResult}; + UnexpectedDerivationMethod, WithdrawError, WithdrawFee, WithdrawRequest, WithdrawResult}; use async_trait::async_trait; use chain::TransactionOutput; use common::log::info; @@ -91,6 +91,28 @@ impl From for WithdrawError { fn from(e: keys::Error) -> Self { WithdrawError::InternalError(e.to_string()) } } +fn derive_hd_key_pair( + coin: &Coin, + derivation_path: &DerivationPath, +) -> Result> +where + Coin: AsRef, +{ + let secret = coin + .as_ref() + .priv_key_policy + .hd_wallet_derived_priv_key_or_err(derivation_path)?; + + let private = Private { + prefix: coin.as_ref().conf.wif_prefix, + secret, + compressed: true, + checksum_type: coin.as_ref().conf.checksum_type, + }; + + KeyPair::from_private(private).map_to_mm(|err| UnexpectedDerivationMethod::InternalError(err.to_string())) +} + #[async_trait] pub trait UtxoWithdraw where @@ -312,18 +334,18 @@ where .with_unsigned_tx(unsigned_tx); let sign_params = sign_params.build()?; - let crypto_ctx = CryptoCtx::from_ctx(&self.ctx)?; - let hw_ctx = crypto_ctx - .hw_ctx() - .or_mm_err(|| WithdrawError::HwError(HwRpcError::NoTrezorDeviceAvailable))?; - - let sign_policy = match self.coin.as_ref().priv_key_policy { - PrivKeyPolicy::Iguana(ref key_pair) => SignPolicy::WithKeyPair(key_pair), - // InitUtxoWithdraw works only for hardware wallets so it's ok to use signing with activated keypair here as a placeholder. - PrivKeyPolicy::HDWallet { - activated_key: ref activated_key_pair, - .. - } => SignPolicy::WithKeyPair(activated_key_pair), + let signed = match self.coin.as_ref().priv_key_policy { + PrivKeyPolicy::Iguana(ref key_pair) => { + self.coin + .sign_tx(sign_params, SignPolicy::WithKeyPair(key_pair)) + .await? + }, + PrivKeyPolicy::HDWallet { .. } => { + let from_key_pair = derive_hd_key_pair(self.coin(), &self.from_derivation_path)?; + self.coin() + .sign_tx(sign_params, SignPolicy::WithKeyPair(&from_key_pair)) + .await? + }, PrivKeyPolicy::Trezor => { let trezor_statuses = TrezorRequestStatuses { on_button_request: WithdrawInProgressStatus::FollowHwDeviceInstructions, @@ -333,8 +355,16 @@ where }; let sign_processor = TrezorRpcTaskProcessor::new(self.task_handle.clone(), trezor_statuses); let sign_processor = Arc::new(sign_processor); + let crypto_ctx = CryptoCtx::from_ctx(&self.ctx)?; + let hw_ctx = crypto_ctx + .hw_ctx() + .or_mm_err(|| WithdrawError::HwError(HwRpcError::NoTrezorDeviceAvailable))?; let trezor_session = hw_ctx.trezor(sign_processor).await?; - SignPolicy::WithTrezor(trezor_session) + self.task_handle + .update_in_progress_status(WithdrawInProgressStatus::WaitingForUserToConfirmSigning)?; + self.coin + .sign_tx(sign_params, SignPolicy::WithTrezor(trezor_session)) + .await? }, #[cfg(target_arch = "wasm32")] PrivKeyPolicy::Metamask(_) => { @@ -344,10 +374,6 @@ where }, }; - self.task_handle - .update_in_progress_status(WithdrawInProgressStatus::WaitingForUserToConfirmSigning)?; - let signed = self.coin.sign_tx(sign_params, sign_policy).await?; - Ok(signed) } } @@ -437,19 +463,7 @@ where let from_address_string = from.address.display_address().map_to_mm(WithdrawError::InternalError)?; let key_pair = match from.derivation_path { - Some(der_path) => { - let secret = coin - .as_ref() - .priv_key_policy - .hd_wallet_derived_priv_key_or_err(&der_path)?; - let private = Private { - prefix: coin.as_ref().conf.wif_prefix, - secret, - compressed: true, - checksum_type: coin.as_ref().conf.checksum_type, - }; - KeyPair::from_private(private).map_to_mm(|e| WithdrawError::InternalError(e.to_string()))? - }, + Some(der_path) => derive_hd_key_pair(&coin, &der_path)?, // [`WithdrawSenderAddress::derivation_path`] is not set, but the coin is initialized with an HD wallet derivation method. None if coin.has_hd_wallet_derivation_method() => { let error = "Cannot determine 'from' address derivation path".to_owned();