Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 45 additions & 31 deletions mm2src/coins/utxo/utxo_withdraw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -91,6 +91,28 @@ impl From<keys::Error> for WithdrawError {
fn from(e: keys::Error) -> Self { WithdrawError::InternalError(e.to_string()) }
}

fn derive_hd_key_pair<Coin>(
coin: &Coin,
derivation_path: &DerivationPath,
) -> Result<KeyPair, MmError<UnexpectedDerivationMethod>>
where
Coin: AsRef<UtxoCoinFields>,
{
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<Coin>
where
Expand Down Expand Up @@ -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,
Expand All @@ -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(_) => {
Expand All @@ -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)
}
}
Expand Down Expand Up @@ -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();
Expand Down