diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 53e3b472a0..b28a897d6b 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -110,23 +110,24 @@ cfg_wasm32! { use super::{coin_conf, lp_coinfind_or_err, AsyncMutex, BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, CoinFutSpawner, CoinProtocol, CoinTransportMetrics, CoinsContext, ConfirmPaymentInput, - EthValidateFeeArgs, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, IguanaPrivKey, MakerSwapTakerCoin, - MarketCoinOps, MmCoin, MmCoinEnum, MyAddressError, MyWalletAddress, NegotiateSwapContractAddrErr, - NumConversError, NumConversResult, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, - PrivKeyBuildPolicy, PrivKeyPolicyNotAllowed, RawTransactionError, RawTransactionFut, - RawTransactionRequest, RawTransactionRes, RawTransactionResult, RefundError, RefundPaymentArgs, - RefundResult, RewardTarget, RpcClientType, RpcTransportEventHandler, RpcTransportEventHandlerShared, - SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignEthTransactionParams, - SignRawTransactionEnum, SignRawTransactionRequest, SignatureError, SignatureResult, SpendPaymentArgs, - SwapOps, SwapTxFeePolicy, TakerSwapMakerCoin, TradeFee, TradePreimageError, TradePreimageFut, - TradePreimageResult, TradePreimageValue, Transaction, TransactionDetails, TransactionEnum, TransactionErr, - TransactionFut, TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, - ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, - ValidatePaymentFut, ValidatePaymentInput, VerificationError, VerificationResult, WaitForHTLCTxSpendArgs, - WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, - WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFee, WithdrawFut, - WithdrawRequest, WithdrawResult, EARLY_CONFIRMATION_ERR_LOG, INVALID_CONTRACT_ADDRESS_ERR_LOG, - INVALID_PAYMENT_STATE_ERR_LOG, INVALID_RECEIVER_ERR_LOG, INVALID_SENDER_ERR_LOG, INVALID_SWAP_ID_ERR_LOG}; + EthValidateFeeArgs, FeeApproxStage, FoundSwapTxSpend, GenTakerPaymentPreimageArgs, HistorySyncState, + IguanaPrivKey, MakerSwapTakerCoin, MarketCoinOps, MmCoin, MmCoinEnum, MyAddressError, MyWalletAddress, + NegotiateSwapContractAddrErr, NumConversError, NumConversResult, PaymentInstructionArgs, + PaymentInstructions, PaymentInstructionsErr, PrivKeyBuildPolicy, PrivKeyPolicyNotAllowed, + RawTransactionError, RawTransactionFut, RawTransactionRequest, RawTransactionRes, RawTransactionResult, + RefundError, RefundPaymentArgs, RefundResult, RewardTarget, RpcClientType, RpcTransportEventHandler, + RpcTransportEventHandlerShared, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, + SendPaymentArgs, SignEthTransactionParams, SignRawTransactionEnum, SignRawTransactionRequest, + SignatureError, SignatureResult, SpendPaymentArgs, SwapOps, SwapTxFeePolicy, TakerSwapMakerCoin, TradeFee, + TradePreimageError, TradePreimageFut, TradePreimageResult, TradePreimageValue, Transaction, + TransactionDetails, TransactionEnum, TransactionErr, TransactionFut, TransactionType, TxMarshalingErr, + UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, + ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationError, + VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, + WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, + WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, WithdrawResult, EARLY_CONFIRMATION_ERR_LOG, + INVALID_CONTRACT_ADDRESS_ERR_LOG, INVALID_PAYMENT_STATE_ERR_LOG, INVALID_RECEIVER_ERR_LOG, + INVALID_SENDER_ERR_LOG, INVALID_SWAP_ID_ERR_LOG}; pub use rlp; cfg_native! { use std::path::PathBuf; @@ -7306,9 +7307,9 @@ impl TakerCoinSwapOpsV2 for EthCoin { } /// Eth doesnt have preimages - async fn gen_taker_funding_spend_preimage( + async fn gen_taker_payment_preimage( &self, - args: &GenTakerFundingSpendArgs<'_, Self>, + args: &GenTakerPaymentPreimageArgs<'_, Self>, _swap_unique_data: &[u8], ) -> GenPreimageResult { Ok(TxPreimageWithSig { @@ -7318,19 +7319,19 @@ impl TakerCoinSwapOpsV2 for EthCoin { } /// Eth doesnt have preimages - async fn validate_taker_funding_spend_preimage( + async fn validate_taker_payment_preimage( &self, - _gen_args: &GenTakerFundingSpendArgs<'_, Self>, + _gen_args: &GenTakerPaymentPreimageArgs<'_, Self>, _preimage: &TxPreimageWithSig, ) -> ValidateTakerFundingSpendPreimageResult { Ok(()) } /// Wrapper for [EthCoin::taker_payment_approve] - async fn sign_and_send_taker_funding_spend( + async fn sign_and_send_taker_payment( &self, _preimage: &TxPreimageWithSig, - args: &GenTakerFundingSpendArgs<'_, Self>, + args: &GenTakerPaymentPreimageArgs<'_, Self>, _swap_unique_data: &[u8], ) -> Result { self.taker_payment_approve(args).await @@ -7384,6 +7385,12 @@ impl TakerCoinSwapOpsV2 for EthCoin { ) -> MmResult { self.wait_for_taker_payment_spend_impl(taker_payment, wait_until).await } + + async fn get_funding_fee(&self, _value: TradePreimageValue) -> TradePreimageResult { todo!() } + + async fn get_taker_payment_fee(&self) -> TradePreimageResult { todo!() } + + async fn get_taker_payment_spend_fee(&self) -> TradePreimageResult { todo!() } } impl CommonSwapOpsV2 for EthCoin { @@ -7478,4 +7485,8 @@ impl MakerCoinSwapOpsV2 for EthCoin { async fn spend_maker_payment_v2(&self, args: SpendMakerPaymentArgs<'_, Self>) -> Result { self.spend_maker_payment_v2_impl(args).await } + + async fn get_maker_payment_fee(&self, _value: TradePreimageValue) -> TradePreimageResult { todo!() } + + async fn get_maker_payment_spend_fee(&self) -> TradePreimageResult { todo!() } } diff --git a/mm2src/coins/eth/eth_swap_v2/eth_taker_swap_v2.rs b/mm2src/coins/eth/eth_swap_v2/eth_taker_swap_v2.rs index fea91b0408..6b9e681902 100644 --- a/mm2src/coins/eth/eth_swap_v2/eth_taker_swap_v2.rs +++ b/mm2src/coins/eth/eth_swap_v2/eth_taker_swap_v2.rs @@ -4,7 +4,7 @@ use crate::eth::{decode_contract_call, get_function_input_data, wei_from_big_dec ParseCoinAssocTypes, RefundFundingSecretArgs, RefundTakerPaymentArgs, SendTakerFundingArgs, SignedEthTx, SwapTxTypeWithSecretHash, TakerPaymentStateV2, TransactionErr, ValidateSwapV2TxError, ValidateSwapV2TxResult, ValidateTakerFundingArgs, TAKER_SWAP_V2}; -use crate::{FundingTxSpend, GenTakerFundingSpendArgs, GenTakerPaymentSpendArgs, SearchForFundingSpendErr, +use crate::{FundingTxSpend, GenTakerPaymentPreimageArgs, GenTakerPaymentSpendArgs, SearchForFundingSpendErr, WaitForPaymentSpendError}; use common::executor::Timer; use common::now_sec; @@ -222,7 +222,7 @@ impl EthCoin { /// Function accepts taker payment transaction, returns taker approve payment transaction. pub(crate) async fn taker_payment_approve( &self, - args: &GenTakerFundingSpendArgs<'_, Self>, + args: &GenTakerPaymentPreimageArgs<'_, Self>, ) -> Result { let gas_limit = match self.coin_type { EthCoinType::Eth | EthCoinType::Erc20 { .. } => U256::from(self.gas_limit_v2.taker.approve_payment), @@ -568,7 +568,7 @@ impl EthCoin { /// The `decoded` parameter should contain the transaction input data from the `ethTakerPayment` or `erc20TakerPayment` function of the EtomicSwapTakerV2 contract. async fn prepare_taker_payment_approve_data( &self, - args: &GenTakerFundingSpendArgs<'_, Self>, + args: &GenTakerPaymentPreimageArgs<'_, Self>, decoded: Vec, token_address: Address, ) -> Result, PrepareTxDataError> { diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 9b0e43fc2e..fea58d19ed 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -1271,6 +1271,8 @@ pub struct SendTakerFundingArgs<'a> { pub maker_pub: &'a [u8], /// DEX fee pub dex_fee: &'a DexFee, + /// The extra fee added to the trading volume to cover the taker payment transaction fees + pub taker_payment_fee: BigDecimal, /// Additional reward for maker (premium) pub premium_amount: BigDecimal, /// Actual volume of taker's payment @@ -1297,8 +1299,8 @@ pub struct RefundFundingSecretArgs<'a, Coin: ParseCoinAssocTypes + ?Sized> { pub watcher_reward: bool, } -/// Helper struct wrapping arguments for [TakerCoinSwapOpsV2::gen_taker_funding_spend_preimage] -pub struct GenTakerFundingSpendArgs<'a, Coin: ParseCoinAssocTypes + ?Sized> { +/// Helper struct wrapping arguments for [TakerCoinSwapOpsV2::gen_taker_payment_preimage] +pub struct GenTakerPaymentPreimageArgs<'a, Coin: ParseCoinAssocTypes + ?Sized> { /// Taker payment transaction serialized to raw bytes pub funding_tx: &'a Coin::Tx, /// Maker's pubkey @@ -1313,6 +1315,8 @@ pub struct GenTakerFundingSpendArgs<'a, Coin: ParseCoinAssocTypes + ?Sized> { pub taker_payment_time_lock: u64, /// The hash of the secret generated by maker pub maker_secret_hash: &'a [u8], + /// The extra fee added to the trading volume to cover the taker payment transaction fees + pub taker_payment_fee: BigDecimal, } /// Helper struct wrapping arguments for [TakerCoinSwapOpsV2::validate_taker_funding] @@ -1333,6 +1337,8 @@ pub struct ValidateTakerFundingArgs<'a, Coin: ParseCoinAssocTypes + ?Sized> { pub taker_pub: &'a Coin::Pubkey, /// DEX fee amount pub dex_fee: &'a DexFee, + /// The extra fee added to the trading volume to cover the taker payment transaction fees + pub taker_payment_fee: BigDecimal, /// Additional reward for maker (premium) pub premium_amount: BigDecimal, /// Actual volume of taker's payment @@ -1481,6 +1487,8 @@ impl From for ValidateSwapV2TxError { /// Enum covering error cases that can happen during taker funding spend preimage validation. #[derive(Debug, Display, EnumFromStringify)] pub enum ValidateTakerFundingSpendPreimageError { + /// Error during conversion of BigDecimal amount to coin's specific monetary units (satoshis, wei, etc.). + NumConversion(String), /// Funding tx has no outputs FundingTxNoOutputs, /// Actual preimage fee is either too high or too small @@ -1501,6 +1509,10 @@ pub enum ValidateTakerFundingSpendPreimageError { Rpc(String), } +impl From for ValidateTakerFundingSpendPreimageError { + fn from(err: NumConversError) -> Self { ValidateTakerFundingSpendPreimageError::NumConversion(err.to_string()) } +} + impl From for ValidateTakerFundingSpendPreimageError { fn from(err: TxGenError) -> Self { ValidateTakerFundingSpendPreimageError::TxGenError(format!("{:?}", err)) } } @@ -1747,6 +1759,12 @@ pub trait MakerCoinSwapOpsV2: ParseCoinAssocTypes + CommonSwapOpsV2 + Send + Syn /// Spend maker payment transaction async fn spend_maker_payment_v2(&self, args: SpendMakerPaymentArgs<'_, Self>) -> Result; + + /// Get the fee to be paid for the processing of the maker payment transaction. + async fn get_maker_payment_fee(&self, value: TradePreimageValue) -> TradePreimageResult; + + /// Get the fee to be paid for the processing of the maker payment spend transaction aka the claiming transaction. + async fn get_maker_payment_spend_fee(&self) -> TradePreimageResult; } #[async_trait] @@ -1914,24 +1932,24 @@ pub trait TakerCoinSwapOpsV2: ParseCoinAssocTypes + CommonSwapOpsV2 + Send + Syn ) -> Result>, SearchForFundingSpendErr>; /// Generates and signs a preimage spending funding tx to the combined taker payment - async fn gen_taker_funding_spend_preimage( + async fn gen_taker_payment_preimage( &self, - args: &GenTakerFundingSpendArgs<'_, Self>, + args: &GenTakerPaymentPreimageArgs<'_, Self>, swap_unique_data: &[u8], ) -> GenPreimageResult; /// Validates taker funding spend preimage generated and signed by maker - async fn validate_taker_funding_spend_preimage( + async fn validate_taker_payment_preimage( &self, - gen_args: &GenTakerFundingSpendArgs<'_, Self>, + gen_args: &GenTakerPaymentPreimageArgs<'_, Self>, preimage: &TxPreimageWithSig, ) -> ValidateTakerFundingSpendPreimageResult; /// Sign and send a spending funding tx to the combined taker payment - async fn sign_and_send_taker_funding_spend( + async fn sign_and_send_taker_payment( &self, preimage: &TxPreimageWithSig, - args: &GenTakerFundingSpendArgs<'_, Self>, + args: &GenTakerPaymentPreimageArgs<'_, Self>, swap_unique_data: &[u8], ) -> Result; @@ -1970,6 +1988,18 @@ pub trait TakerCoinSwapOpsV2: ParseCoinAssocTypes + CommonSwapOpsV2 + Send + Syn from_block: u64, wait_until: u64, ) -> MmResult; + + /// Get the fee to be paid for the processing of the funding transaction. + /// Txs in success case (no refund) go as follows: + /// Chain A: funding -> taker payment -> taker payment spend (aka preimage) (to the maker & dex) + /// Chain B: maker payment -> maker payment spend (aka claiming) (to the taker) + async fn get_funding_fee(&self, value: TradePreimageValue) -> TradePreimageResult; + + /// Get the fee to be paid for the processing of the taker payment transaction. + async fn get_taker_payment_fee(&self) -> TradePreimageResult; + + /// Get the fee to be paid for the processing of the taker payment spend transaction aka the preimage transaction. + async fn get_taker_payment_spend_fee(&self) -> TradePreimageResult; } #[async_trait] diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index 43765ab0ba..eb8a2e936d 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -5,7 +5,7 @@ use super::{CoinBalance, CommonSwapOpsV2, FundingTxSpend, HistorySyncState, Mark TransactionEnum, TransactionFut, WaitForPaymentSpendError}; use crate::coin_errors::ValidatePaymentResult; use crate::{coin_errors::MyAddressError, BalanceFut, CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinFutSpawner, - ConfirmPaymentInput, FeeApproxStage, FoundSwapTxSpend, GenPreimageResult, GenTakerFundingSpendArgs, + ConfirmPaymentInput, FeeApproxStage, FoundSwapTxSpend, GenPreimageResult, GenTakerPaymentPreimageArgs, GenTakerPaymentSpendArgs, MakerSwapTakerCoin, MmCoinEnum, NegotiateSwapContractAddrErr, ParseCoinAssocTypes, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, RawTransactionResult, RefundFundingSecretArgs, RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput, @@ -499,26 +499,26 @@ impl TakerCoinSwapOpsV2 for TestCoin { todo!() } - async fn gen_taker_funding_spend_preimage( + async fn gen_taker_payment_preimage( &self, - args: &GenTakerFundingSpendArgs<'_, Self>, + args: &GenTakerPaymentPreimageArgs<'_, Self>, swap_unique_data: &[u8], ) -> GenPreimageResult { todo!() } - async fn validate_taker_funding_spend_preimage( + async fn validate_taker_payment_preimage( &self, - gen_args: &GenTakerFundingSpendArgs<'_, Self>, + gen_args: &GenTakerPaymentPreimageArgs<'_, Self>, preimage: &TxPreimageWithSig, ) -> ValidateTakerFundingSpendPreimageResult { todo!() } - async fn sign_and_send_taker_funding_spend( + async fn sign_and_send_taker_payment( &self, preimage: &TxPreimageWithSig, - args: &GenTakerFundingSpendArgs<'_, Self>, + args: &GenTakerPaymentPreimageArgs<'_, Self>, swap_unique_data: &[u8], ) -> Result { todo!() @@ -565,10 +565,16 @@ impl TakerCoinSwapOpsV2 for TestCoin { ) -> MmResult { unimplemented!() } + + async fn get_funding_fee(&self, value: TradePreimageValue) -> TradePreimageResult { todo!() } + + async fn get_taker_payment_fee(&self) -> TradePreimageResult { todo!() } + + async fn get_taker_payment_spend_fee(&self) -> TradePreimageResult { todo!() } } impl CommonSwapOpsV2 for TestCoin { - fn derive_htlc_pubkey_v2(&self, _swap_unique_data: &[u8]) -> Self::Pubkey { todo!() } + fn derive_htlc_pubkey_v2(&self, swap_unique_data: &[u8]) -> Self::Pubkey { todo!() } fn derive_htlc_pubkey_v2_bytes(&self, _swap_unique_data: &[u8]) -> Vec { todo!() } } diff --git a/mm2src/coins/utxo.rs b/mm2src/coins/utxo.rs index 6d98451c7f..0ab9346a1e 100644 --- a/mm2src/coins/utxo.rs +++ b/mm2src/coins/utxo.rs @@ -540,7 +540,7 @@ pub struct UtxoCoinConf { /// Address and privkey checksum type pub checksum_type: ChecksumType, /// Fork id used in sighash - pub fork_id: u32, + pub fork_id: u8, /// Signature version pub signature_version: SignatureVersion, pub required_confirmations: AtomicU64, diff --git a/mm2src/coins/utxo/qtum.rs b/mm2src/coins/utxo/qtum.rs index c5fbc67293..e1565f2f0b 100644 --- a/mm2src/coins/utxo/qtum.rs +++ b/mm2src/coins/utxo/qtum.rs @@ -1042,7 +1042,7 @@ impl UtxoSignerOps for QtumCoin { }) } - fn fork_id(&self) -> u32 { self.utxo_arc.conf.fork_id } + fn fork_id(&self) -> u8 { self.utxo_arc.conf.fork_id } fn branch_id(&self) -> u32 { self.utxo_arc.conf.consensus_branch_id } diff --git a/mm2src/coins/utxo/utxo_builder/utxo_conf_builder.rs b/mm2src/coins/utxo/utxo_builder/utxo_conf_builder.rs index 5ae7fcb405..a5249e2f6c 100644 --- a/mm2src/coins/utxo/utxo_builder/utxo_conf_builder.rs +++ b/mm2src/coins/utxo/utxo_builder/utxo_conf_builder.rs @@ -34,6 +34,8 @@ pub enum UtxoConfError { InvalidVersionGroupId(String), InvalidAddressFormat(String), InvalidDecimals(String), + #[display(fmt = "Invalid fork id: {}, valid fork ids are values between 0x00 - 0xff", _0)] + InvalidForkId(String), } impl From for UtxoConfError { @@ -91,8 +93,8 @@ impl<'a> UtxoConfBuilder<'a> { let tx_fee_volatility_percent = self.tx_fee_volatility_percent(); let version_group_id = self.version_group_id(tx_version, overwintered)?; let consensus_branch_id = self.consensus_branch_id(tx_version)?; - let signature_version = self.signature_version(); - let fork_id = self.fork_id(); + let signature_version = self.signature_version()?; + let fork_id = self.fork_id()?; // should be sufficient to detect zcash by overwintered flag let zcash = overwintered; @@ -243,23 +245,23 @@ impl<'a> UtxoConfBuilder<'a> { Ok(consensus_branch_id) } - fn signature_version(&self) -> SignatureVersion { - let default_signature_version = if self.ticker == "BCH" || self.fork_id() != 0 { + fn signature_version(&self) -> UtxoConfResult { + let default_signature_version = if self.ticker == "BCH" || self.fork_id()? != 0 { SignatureVersion::ForkId } else { SignatureVersion::Base }; - json::from_value(self.conf["signature_version"].clone()).unwrap_or(default_signature_version) + Ok(json::from_value(self.conf["signature_version"].clone()).unwrap_or(default_signature_version)) } - fn fork_id(&self) -> u32 { + fn fork_id(&self) -> UtxoConfResult { let default_fork_id = match self.ticker { "BCH" => "0x40", _ => "0x0", }; let hex_string = self.conf["fork_id"].as_str().unwrap_or(default_fork_id); - let fork_id = u32::from_str_radix(hex_string.trim_start_matches("0x"), 16).unwrap(); - fork_id + u8::from_str_radix(hex_string.trim_start_matches("0x"), 16) + .map_to_mm(|e| UtxoConfError::InvalidForkId(e.to_string())) } fn required_confirmations(&self) -> u64 { diff --git a/mm2src/coins/utxo/utxo_common.rs b/mm2src/coins/utxo/utxo_common.rs index 37955e9030..8effd1d97f 100644 --- a/mm2src/coins/utxo/utxo_common.rs +++ b/mm2src/coins/utxo/utxo_common.rs @@ -13,21 +13,22 @@ use crate::utxo::utxo_hd_wallet::UtxoHDAddress; use crate::utxo::utxo_withdraw::{InitUtxoWithdraw, StandardUtxoWithdraw, UtxoWithdraw}; use crate::watcher_common::validate_watcher_reward; use crate::{scan_for_new_addresses_impl, CanRefundHtlc, CoinBalance, CoinWithDerivationMethod, ConfirmPaymentInput, - DexFee, GenPreimageResult, GenTakerFundingSpendArgs, GenTakerPaymentSpendArgs, GetWithdrawSenderAddress, - RawTransactionError, RawTransactionRequest, RawTransactionRes, RawTransactionResult, - RefundFundingSecretArgs, RefundMakerPaymentSecretArgs, RefundPaymentArgs, RewardTarget, - SearchForSwapTxSpendInput, SendMakerPaymentArgs, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, - SendTakerFundingArgs, SignRawTransactionEnum, SignRawTransactionRequest, SignUtxoTransactionParams, - SignatureError, SignatureResult, SpendMakerPaymentArgs, SpendPaymentArgs, SwapOps, - SwapTxTypeWithSecretHash, TradePreimageValue, TransactionData, TransactionFut, TransactionResult, - TxFeeDetails, TxGenError, TxMarshalingErr, TxPreimageWithSig, ValidateAddressResult, - ValidateOtherPubKeyErr, ValidatePaymentFut, ValidatePaymentInput, ValidateSwapV2TxError, - ValidateSwapV2TxResult, ValidateTakerFundingArgs, ValidateTakerFundingSpendPreimageError, - ValidateTakerFundingSpendPreimageResult, ValidateTakerPaymentSpendPreimageError, - ValidateTakerPaymentSpendPreimageResult, ValidateWatcherSpendInput, VerificationError, VerificationResult, - WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, - WithdrawResult, WithdrawSenderAddress, EARLY_CONFIRMATION_ERR_LOG, INVALID_RECEIVER_ERR_LOG, - INVALID_REFUND_TX_ERR_LOG, INVALID_SCRIPT_ERR_LOG, INVALID_SENDER_ERR_LOG, OLD_TRANSACTION_ERR_LOG}; + DexFee, GenPreimageResult, GenTakerPaymentPreimageArgs, GenTakerPaymentSpendArgs, + GetWithdrawSenderAddress, RawTransactionError, RawTransactionRequest, RawTransactionRes, + RawTransactionResult, RefundFundingSecretArgs, RefundMakerPaymentSecretArgs, RefundPaymentArgs, + RewardTarget, SearchForSwapTxSpendInput, SendMakerPaymentArgs, SendMakerPaymentSpendPreimageInput, + SendPaymentArgs, SendTakerFundingArgs, SignRawTransactionEnum, SignRawTransactionRequest, + SignUtxoTransactionParams, SignatureError, SignatureResult, SpendMakerPaymentArgs, SpendPaymentArgs, + SwapOps, SwapTxTypeWithSecretHash, TakerCoinSwapOpsV2, TradePreimageValue, TransactionData, + TransactionFut, TransactionResult, TxFeeDetails, TxGenError, TxMarshalingErr, TxPreimageWithSig, + ValidateAddressResult, ValidateOtherPubKeyErr, ValidatePaymentFut, ValidatePaymentInput, + ValidateSwapV2TxError, ValidateSwapV2TxResult, ValidateTakerFundingArgs, + ValidateTakerFundingSpendPreimageError, ValidateTakerFundingSpendPreimageResult, + ValidateTakerPaymentSpendPreimageError, ValidateTakerPaymentSpendPreimageResult, + ValidateWatcherSpendInput, VerificationError, VerificationResult, WatcherSearchForSwapTxSpendInput, + WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawResult, WithdrawSenderAddress, + EARLY_CONFIRMATION_ERR_LOG, INVALID_RECEIVER_ERR_LOG, INVALID_REFUND_TX_ERR_LOG, INVALID_SCRIPT_ERR_LOG, + INVALID_SENDER_ERR_LOG, OLD_TRANSACTION_ERR_LOG}; use crate::{MmCoinEnum, WatcherReward, WatcherRewardError}; use base64::engine::general_purpose::STANDARD; use base64::Engine; @@ -881,6 +882,188 @@ enum FundingSpendFeeSetting { UseExact(u64), } +pub mod tx_sizes { + use super::*; + + /// Returns the taker payment transaction size in vbytes. + pub fn get_taker_payment_tx_size(coin: &impl UtxoCommonOps) -> usize { + let preimage = TransactionInputSigner { + lock_time: 0, + version: coin.as_ref().conf.tx_version, + n_time: None, + overwintered: coin.as_ref().conf.overwintered, + inputs: vec![UnsignedTransactionInput { + sequence: SEQUENCE_FINAL, + previous_output: OutPoint { + hash: H256::default(), + index: DEFAULT_SWAP_VOUT as u32, + }, + prev_script: Vec::new().into(), + amount: 0, + }], + outputs: vec![ + // This output is used in taker payment spent tx. + TransactionOutput { + value: 0, + script_pubkey: Builder::build_p2sh(&AddressHashEnum::default_address_hash()).to_bytes(), + }, + ], + expiry_height: 0, + join_splits: vec![], + shielded_spends: vec![], + shielded_outputs: vec![], + value_balance: 0, + version_group_id: coin.as_ref().conf.version_group_id, + consensus_branch_id: coin.as_ref().conf.consensus_branch_id, + zcash: coin.as_ref().conf.zcash, + posv: coin.as_ref().conf.is_posv, + str_d_zeel: None, + hash_algo: coin.as_ref().tx_hash_algo.into(), + }; + + let redeem_script = swap_proto_v2_scripts::taker_funding_script( + 0, + H160::default().as_slice(), + &Public::default(), + &Public::default(), + ); + + let mut final_tx: UtxoTx = preimage.into(); + final_tx.inputs[0].script_sig = Builder::default() + // Maximum of 72 byte maker signature + a sighash byte. + .push_data(&[0; 72 + 1]) + // Maximum of 72 byte taker signature + a sighash byte. + .push_data(&[0; 72 + 1]) + .push_opcode(Opcode::OP_1) + .push_opcode(Opcode::OP_0) + .push_data(&redeem_script) + .into_bytes(); + + // We aren't spending from the segwit address, we are spending from a P2SH address. + tx_size_in_v_bytes(&UtxoAddressFormat::Standard, &final_tx) + } + + /// Returns the taker payment spend transaction size in vbytes. + pub fn get_taker_payment_spend_tx_size(coin: &impl UtxoCommonOps) -> usize { + let preimage = TransactionInputSigner { + lock_time: 0, + version: coin.as_ref().conf.tx_version, + n_time: None, + overwintered: coin.as_ref().conf.overwintered, + inputs: vec![UnsignedTransactionInput { + sequence: SEQUENCE_FINAL, + previous_output: OutPoint { + hash: H256::default(), + index: DEFAULT_SWAP_VOUT as u32, + }, + prev_script: Vec::new().into(), + amount: 0, + }], + outputs: vec![ + // An output for the dex. + TransactionOutput { + value: 0, + script_pubkey: Builder::build_p2sh(&AddressHashEnum::default_address_hash()).to_bytes(), + }; + // And another for the maker. + 2 + ], + expiry_height: 0, + join_splits: vec![], + shielded_spends: vec![], + shielded_outputs: vec![], + value_balance: 0, + version_group_id: coin.as_ref().conf.version_group_id, + consensus_branch_id: coin.as_ref().conf.consensus_branch_id, + zcash: coin.as_ref().conf.zcash, + posv: coin.as_ref().conf.is_posv, + str_d_zeel: None, + hash_algo: coin.as_ref().tx_hash_algo.into(), + }; + + let redeem_script = swap_proto_v2_scripts::taker_payment_script( + 0, + H160::default().as_slice(), + &Public::default(), + &Public::default(), + ); + + let mut final_tx: UtxoTx = preimage.into(); + final_tx.inputs[0].script_sig = Builder::default() + // Maximum of 72 byte maker signature + a sighash byte. + .push_data(&[0; 72 + 1]) + // Maximum of 72 byte taker signature + a sighash byte. + .push_data(&[0; 72 + 1]) + // Maker's secret hash. + .push_data(&[0; 32]) + .push_opcode(Opcode::OP_0) + .push_data(&redeem_script) + .into_bytes(); + + // We aren't spending from the segwit address, we are spending from a P2SH address. + tx_size_in_v_bytes(&UtxoAddressFormat::Standard, &final_tx) + } + + /// Returns the maker payment spend transaction size in vbytes. + pub fn get_maker_payment_spend_tx_size(coin: &impl UtxoCommonOps) -> usize { + let preimage = TransactionInputSigner { + lock_time: 0, + version: coin.as_ref().conf.tx_version, + n_time: None, + overwintered: coin.as_ref().conf.overwintered, + inputs: vec![UnsignedTransactionInput { + sequence: SEQUENCE_FINAL, + previous_output: OutPoint { + hash: H256::default(), + index: DEFAULT_SWAP_VOUT as u32, + }, + prev_script: Vec::new().into(), + amount: 0, + }], + outputs: vec![ + // An output for the taker + TransactionOutput { + value: 0, + script_pubkey: Builder::build_p2sh(&AddressHashEnum::default_address_hash()).to_bytes(), + }, + ], + expiry_height: 0, + join_splits: vec![], + shielded_spends: vec![], + shielded_outputs: vec![], + value_balance: 0, + version_group_id: coin.as_ref().conf.version_group_id, + consensus_branch_id: coin.as_ref().conf.consensus_branch_id, + zcash: coin.as_ref().conf.zcash, + posv: coin.as_ref().conf.is_posv, + str_d_zeel: None, + hash_algo: coin.as_ref().tx_hash_algo.into(), + }; + + let redeem_script = swap_proto_v2_scripts::maker_payment_script( + 0, + H160::default().as_slice(), + H160::default().as_slice(), + &Public::default(), + &Public::default(), + ); + + let mut final_tx: UtxoTx = preimage.into(); + final_tx.inputs[0].script_sig = Builder::default() + // Maximum of 72 byte taker signature + a sighash byte. + .push_data(&[0; 72 + 1]) + // Maker's secret hash. + .push_data(&[0; 32]) + .push_opcode(Opcode::OP_1) + .push_opcode(Opcode::OP_0) + .push_data(&redeem_script) + .into_bytes(); + + // We aren't spending from the segwit address, we are spending from a P2SH address. + tx_size_in_v_bytes(&UtxoAddressFormat::Standard, &final_tx) + } +} + async fn p2sh_spending_tx_preimage( coin: &T, prev_tx: &UtxoTx, @@ -983,12 +1166,16 @@ pub async fn p2sh_spending_tx(coin: &T, input: P2SHSpendingTxI type GenPreimageResInner = MmResult; -async fn gen_taker_funding_spend_preimage( - coin: &T, - args: &GenTakerFundingSpendArgs<'_, T>, +async fn gen_taker_funding_spend_preimage( + coin: &Coin, + args: &GenTakerPaymentPreimageArgs<'_, T>, n_time: NTimeSetting, fee: FundingSpendFeeSetting, -) -> GenPreimageResInner { +) -> GenPreimageResInner +where + T: UtxoCommonOps, + Coin: UtxoCommonOps + TakerCoinSwapOpsV2, +{ let payment_time_lock = args .taker_payment_time_lock .try_into() @@ -1009,8 +1196,13 @@ async fn gen_taker_funding_spend_preimage( let fee = match fee { FundingSpendFeeSetting::GetFromCoin => { - coin.get_htlc_spend_fee(DEFAULT_SWAP_TX_SPEND_SIZE, &FeeApproxStage::WithoutApprox) - .await? + let calculated_fee = coin.get_taker_payment_fee().await.unwrap().amount.to_decimal(); + let calculated_fee = sat_from_big_decimal(&calculated_fee, coin.as_ref().decimals).mm_err(|e| e.into())?; + let taker_instructed_fee = + sat_from_big_decimal(&args.taker_payment_fee, coin.as_ref().decimals).mm_err(|e| e.into())?; + // Cap the paid fee to the taker instructed fee. + // Since any fee above that will be paid from our (maker) trading volume. + calculated_fee.min(taker_instructed_fee) }, FundingSpendFeeSetting::UseExact(f) => f, }; @@ -1040,11 +1232,15 @@ async fn gen_taker_funding_spend_preimage( .map_to_mm(TxGenError::Legacy) } -pub async fn gen_and_sign_taker_funding_spend_preimage( - coin: &T, - args: &GenTakerFundingSpendArgs<'_, T>, +pub async fn gen_and_sign_taker_payment_preimage( + coin: &Coin, + args: &GenTakerPaymentPreimageArgs<'_, T>, htlc_keypair: &KeyPair, -) -> GenPreimageResult { +) -> GenPreimageResult +where + T: UtxoCommonOps, + Coin: UtxoCommonOps + TakerCoinSwapOpsV2, +{ let funding_time_lock = args .funding_time_lock .try_into() @@ -1076,11 +1272,15 @@ pub async fn gen_and_sign_taker_funding_spend_preimage( /// Common implementation of taker funding spend preimage validation for UTXO coins. /// Checks maker's signature and compares received preimage with the expected tx. -pub async fn validate_taker_funding_spend_preimage( - coin: &T, - gen_args: &GenTakerFundingSpendArgs<'_, T>, +pub async fn validate_taker_payment_preimage( + coin: &Coin, + gen_args: &GenTakerPaymentPreimageArgs<'_, T>, preimage: &TxPreimageWithSig, -) -> ValidateTakerFundingSpendPreimageResult { +) -> ValidateTakerFundingSpendPreimageResult +where + T: UtxoCommonOps, + Coin: UtxoCommonOps + TakerCoinSwapOpsV2, +{ let funding_amount = gen_args .funding_tx .first_output() @@ -1100,18 +1300,16 @@ pub async fn validate_taker_funding_spend_preimage( ))); } - let expected_fee = coin - .get_htlc_spend_fee(DEFAULT_SWAP_TX_SPEND_SIZE, &FeeApproxStage::WithoutApprox) - .await?; - let actual_fee = funding_amount - payment_amount; + let instructed_fee = + sat_from_big_decimal(&gen_args.taker_payment_fee, coin.as_ref().decimals).mm_err(|e| e.into())?; - let fee_div = expected_fee as f64 / actual_fee as f64; - - if !(0.9..=1.1).contains(&fee_div) { + if actual_fee > instructed_fee { + // Reject a high fee. Such fee will typically be paid from the maker's trading volume, + // but the maker might fail the swap later and this would make us incur this fee instead. return MmError::err(ValidateTakerFundingSpendPreimageError::UnexpectedPreimageFee(format!( - "Too large difference between expected {} and actual {} fees", - expected_fee, actual_fee + "A fee of {} was used, more than the agreed upon fee of {}.", + actual_fee, instructed_fee ))); } @@ -1159,10 +1357,10 @@ pub async fn validate_taker_funding_spend_preimage( } /// Common implementation of taker funding spend finalization and broadcast for UTXO coins. -pub async fn sign_and_send_taker_funding_spend( +pub async fn sign_and_send_taker_payment( coin: &T, preimage: &TxPreimageWithSig, - gen_args: &GenTakerFundingSpendArgs<'_, T>, + gen_args: &GenTakerPaymentPreimageArgs<'_, T>, htlc_keypair: &KeyPair, ) -> Result { let redeem_script = swap_proto_v2_scripts::taker_funding_script( @@ -1187,13 +1385,13 @@ pub async fn sign_and_send_taker_funding_spend( SIGHASH_ALL, coin.as_ref().conf.fork_id )); - let sig_hash_all_fork_id = (SIGHASH_ALL | coin.as_ref().conf.fork_id) as u8; + let sig_hash_all_fork_id = SIGHASH_ALL | coin.as_ref().conf.fork_id; let mut maker_signature_with_sighash = preimage.signature.to_vec(); maker_signature_with_sighash.push(sig_hash_all_fork_id); drop_mutability!(maker_signature_with_sighash); - let mut taker_signature_with_sighash: Vec = taker_signature.take(); + let mut taker_signature_with_sighash = taker_signature.take(); taker_signature_with_sighash.push(sig_hash_all_fork_id); drop_mutability!(taker_signature_with_sighash); @@ -1432,14 +1630,14 @@ pub async fn sign_and_broadcast_taker_payment_spend( )); let mut taker_signature_with_sighash = preimage.signature.to_vec(); let taker_sig_hash = match gen_args.dex_fee { - DexFee::Standard(_) => (SIGHASH_SINGLE | coin.as_ref().conf.fork_id) as u8, - DexFee::WithBurn { .. } => (SIGHASH_ALL | coin.as_ref().conf.fork_id) as u8, + DexFee::Standard(_) => SIGHASH_SINGLE | coin.as_ref().conf.fork_id, + DexFee::WithBurn { .. } => SIGHASH_ALL | coin.as_ref().conf.fork_id, }; taker_signature_with_sighash.push(taker_sig_hash); drop_mutability!(taker_signature_with_sighash); - let sig_hash_all_fork_id = (SIGHASH_ALL | coin.as_ref().conf.fork_id) as u8; + let sig_hash_all_fork_id = SIGHASH_ALL | coin.as_ref().conf.fork_id; let mut maker_signature_with_sighash: Vec = maker_signature.take(); maker_signature_with_sighash.push(sig_hash_all_fork_id); drop_mutability!(maker_signature_with_sighash); @@ -3806,6 +4004,9 @@ pub fn get_trade_fee(coin: T) -> Box) -> Result { utxo_common::spend_maker_payment_v2(self, args).await } + + async fn get_maker_payment_fee(&self, value: TradePreimageValue) -> TradePreimageResult { + let mut fee = utxo_common::get_sender_trade_fee(self, value, FeeApproxStage::StartSwap).await?; + fee.paid_from_trading_vol = true; + Ok(fee) + } + + async fn get_maker_payment_spend_fee(&self) -> TradePreimageResult { + let maker_payment_spend_tx_size = utxo_common::tx_sizes::get_maker_payment_spend_tx_size(self); + let fee_sat = self + .get_htlc_spend_fee(maker_payment_spend_tx_size as u64, &FeeApproxStage::TradePreimage) + .await?; + let amount = big_decimal_from_sat_unsigned(fee_sat, self.as_ref().decimals).into(); + Ok(TradeFee { + coin: self.as_ref().conf.ticker.clone(), + amount, + paid_from_trading_vol: true, + }) + } } #[async_trait] @@ -795,31 +814,31 @@ impl TakerCoinSwapOpsV2 for UtxoStandardCoin { } } - async fn gen_taker_funding_spend_preimage( + async fn gen_taker_payment_preimage( &self, - args: &GenTakerFundingSpendArgs<'_, Self>, + args: &GenTakerPaymentPreimageArgs<'_, Self>, swap_unique_data: &[u8], ) -> GenPreimageResult { let htlc_keypair = self.derive_htlc_key_pair(swap_unique_data); - utxo_common::gen_and_sign_taker_funding_spend_preimage(self, args, &htlc_keypair).await + utxo_common::gen_and_sign_taker_payment_preimage(self, args, &htlc_keypair).await } - async fn validate_taker_funding_spend_preimage( + async fn validate_taker_payment_preimage( &self, - gen_args: &GenTakerFundingSpendArgs<'_, Self>, + gen_args: &GenTakerPaymentPreimageArgs<'_, Self>, preimage: &TxPreimageWithSig, ) -> ValidateTakerFundingSpendPreimageResult { - utxo_common::validate_taker_funding_spend_preimage(self, gen_args, preimage).await + utxo_common::validate_taker_payment_preimage(self, gen_args, preimage).await } - async fn sign_and_send_taker_funding_spend( + async fn sign_and_send_taker_payment( &self, preimage: &TxPreimageWithSig, - args: &GenTakerFundingSpendArgs<'_, Self>, + args: &GenTakerPaymentPreimageArgs<'_, Self>, swap_unique_data: &[u8], ) -> Result { let htlc_keypair = self.derive_htlc_key_pair(swap_unique_data); - utxo_common::sign_and_send_taker_funding_spend(self, preimage, args, &htlc_keypair).await + utxo_common::sign_and_send_taker_payment(self, preimage, args, &htlc_keypair).await } async fn refund_combined_taker_payment( @@ -883,6 +902,38 @@ impl TakerCoinSwapOpsV2 for UtxoStandardCoin { .await?; Ok(res) } + + async fn get_funding_fee(&self, value: TradePreimageValue) -> TradePreimageResult { + let mut fee = utxo_common::get_sender_trade_fee(self, value, FeeApproxStage::StartSwap).await?; + fee.paid_from_trading_vol = true; + Ok(fee) + } + + async fn get_taker_payment_fee(&self) -> TradePreimageResult { + let taker_payment_tx_size = utxo_common::tx_sizes::get_taker_payment_tx_size(self); + let fee_sat = self + .get_htlc_spend_fee(taker_payment_tx_size as u64, &FeeApproxStage::TradePreimage) + .await?; + let amount = big_decimal_from_sat_unsigned(fee_sat, self.as_ref().decimals).into(); + Ok(TradeFee { + coin: self.as_ref().conf.ticker.clone(), + amount, + paid_from_trading_vol: true, + }) + } + + async fn get_taker_payment_spend_fee(&self) -> TradePreimageResult { + let taker_payment_spend_tx_size = utxo_common::tx_sizes::get_taker_payment_spend_tx_size(self); + let fee_sat = self + .get_htlc_spend_fee(taker_payment_spend_tx_size as u64, &FeeApproxStage::TradePreimage) + .await?; + let amount = big_decimal_from_sat_unsigned(fee_sat, self.as_ref().decimals).into(); + Ok(TradeFee { + coin: self.as_ref().conf.ticker.clone(), + amount, + paid_from_trading_vol: true, + }) + } } impl CommonSwapOpsV2 for UtxoStandardCoin { @@ -1114,7 +1165,7 @@ impl UtxoSignerOps for UtxoStandardCoin { }) } - fn fork_id(&self) -> u32 { self.utxo_arc.conf.fork_id } + fn fork_id(&self) -> u8 { self.utxo_arc.conf.fork_id } fn branch_id(&self) -> u32 { self.utxo_arc.conf.consensus_branch_id } diff --git a/mm2src/coins/utxo_signer/src/lib.rs b/mm2src/coins/utxo_signer/src/lib.rs index 8032e9acf7..901ad9a63d 100644 --- a/mm2src/coins/utxo_signer/src/lib.rs +++ b/mm2src/coins/utxo_signer/src/lib.rs @@ -126,7 +126,7 @@ pub trait UtxoSignerOps { fn trezor_coin(&self) -> UtxoSignTxResult; - fn fork_id(&self) -> u32; + fn fork_id(&self) -> u8; fn branch_id(&self) -> u32; diff --git a/mm2src/coins/utxo_signer/src/sign_common.rs b/mm2src/coins/utxo_signer/src/sign_common.rs index 1ba966aa6b..f7e930a508 100644 --- a/mm2src/coins/utxo_signer/src/sign_common.rs +++ b/mm2src/coins/utxo_signer/src/sign_common.rs @@ -1,4 +1,4 @@ -use crate::Signature; +use crate::{with_key_pair::SIGHASH_ALL, Signature}; use chain::{Transaction as UtxoTx, TransactionInput}; use keys::bytes::Bytes; use keys::Public as PublicKey; @@ -32,7 +32,7 @@ pub(crate) fn complete_tx(unsigned: TransactionInputSigner, signed_inputs: Vec TransactionInput { let script_sig = script_sig(signature, fork_id); @@ -49,7 +49,7 @@ pub(crate) fn p2pk_spend_with_signature( pub(crate) fn p2pkh_spend_with_signature( unsigned_input: &UnsignedTransactionInput, public_key: &PublicKey, - fork_id: u32, + fork_id: u8, signature: Signature, ) -> TransactionInput { let script_sig = script_sig_with_pub(public_key, fork_id, signature); @@ -67,7 +67,7 @@ pub(crate) fn p2sh_spend_with_signature( unsigned_input: &UnsignedTransactionInput, redeem_script: Script, script_data: Script, - fork_id: u32, + fork_id: u8, signature: Signature, ) -> TransactionInput { let script_sig = script_sig(signature, fork_id); @@ -92,7 +92,7 @@ pub(crate) fn p2sh_spend_with_signature( pub(crate) fn p2wpkh_spend_with_signature( unsigned_input: &UnsignedTransactionInput, public_key: &PublicKey, - fork_id: u32, + fork_id: u8, signature: Signature, ) -> TransactionInput { let script_sig = script_sig(signature, fork_id); @@ -105,7 +105,7 @@ pub(crate) fn p2wpkh_spend_with_signature( } } -pub(crate) fn script_sig_with_pub(public_key: &PublicKey, fork_id: u32, signature: Signature) -> Bytes { +pub(crate) fn script_sig_with_pub(public_key: &PublicKey, fork_id: u8, signature: Signature) -> Bytes { let script_sig = script_sig(signature, fork_id); let builder = Builder::default(); builder @@ -114,12 +114,12 @@ pub(crate) fn script_sig_with_pub(public_key: &PublicKey, fork_id: u32, signatur .into_bytes() } -pub(crate) fn script_sig(mut signature: Signature, fork_id: u32) -> Bytes { +pub(crate) fn script_sig(mut signature: Signature, fork_id: u8) -> Bytes { let mut sig_script = Bytes::default(); sig_script.append(&mut signature); // Using SIGHASH_ALL only for now - sig_script.append(&mut Bytes::from(vec![1 | fork_id as u8])); + sig_script.append(&mut Bytes::from(vec![SIGHASH_ALL | fork_id])); sig_script } diff --git a/mm2src/coins/utxo_signer/src/with_key_pair.rs b/mm2src/coins/utxo_signer/src/with_key_pair.rs index b62fa695f2..f1782adf90 100644 --- a/mm2src/coins/utxo_signer/src/with_key_pair.rs +++ b/mm2src/coins/utxo_signer/src/with_key_pair.rs @@ -9,9 +9,9 @@ use mm2_err_handle::prelude::*; use primitives::hash::H256; use script::{Builder, Script, ScriptType, SignatureVersion, TransactionInputSigner, UnsignedTransactionInput}; -pub const SIGHASH_ALL: u32 = 1; -pub const _SIGHASH_NONE: u32 = 2; -pub const SIGHASH_SINGLE: u32 = 3; +pub const SIGHASH_ALL: u8 = 1; +pub const _SIGHASH_NONE: u8 = 2; +pub const SIGHASH_SINGLE: u8 = 3; pub type UtxoSignWithKeyPairResult = Result>; @@ -47,7 +47,7 @@ pub fn sign_tx( unsigned: TransactionInputSigner, key_pair: &KeyPair, signature_version: SignatureVersion, - fork_id: u32, + fork_id: u8, ) -> UtxoSignWithKeyPairResult { let signed_inputs = unsigned .inputs @@ -74,7 +74,7 @@ pub fn p2pk_spend( input_index: usize, key_pair: &KeyPair, signature_version: SignatureVersion, - fork_id: u32, + fork_id: u8, ) -> UtxoSignWithKeyPairResult { let unsigned_input = get_input(signer, input_index)?; @@ -105,7 +105,7 @@ pub fn p2pkh_spend( input_index: usize, key_pair: &KeyPair, signature_version: SignatureVersion, - fork_id: u32, + fork_id: u8, ) -> UtxoSignWithKeyPairResult { let unsigned_input = get_input(signer, input_index)?; @@ -143,7 +143,7 @@ pub fn p2sh_spend( script_data: Script, redeem_script: Script, signature_version: SignatureVersion, - fork_id: u32, + fork_id: u8, ) -> UtxoSignWithKeyPairResult { let unsigned_input = get_input(signer, input_index)?; @@ -171,7 +171,7 @@ pub fn p2wpkh_spend( input_index: usize, key_pair: &KeyPair, signature_version: SignatureVersion, - fork_id: u32, + fork_id: u8, ) -> UtxoSignWithKeyPairResult { let unsigned_input = get_input(signer, input_index)?; @@ -209,8 +209,8 @@ pub fn calc_and_sign_sighash( output_script: &Script, key_pair: &KeyPair, signature_version: SignatureVersion, - sighash_type: u32, - fork_id: u32, + sighash_type: u8, + fork_id: u8, ) -> UtxoSignWithKeyPairResult { let sighash = signature_hash_to_sign( signer, @@ -228,8 +228,8 @@ pub fn signature_hash_to_sign( input_index: usize, output_script: &Script, signature_version: SignatureVersion, - sighash_type: u32, - fork_id: u32, + sighash_type: u8, + fork_id: u8, ) -> UtxoSignWithKeyPairResult { let input_amount = get_input(signer, input_index)?.amount; diff --git a/mm2src/coins/utxo_signer/src/with_trezor.rs b/mm2src/coins/utxo_signer/src/with_trezor.rs index 8da3b972bb..a949dc0376 100644 --- a/mm2src/coins/utxo_signer/src/with_trezor.rs +++ b/mm2src/coins/utxo_signer/src/with_trezor.rs @@ -17,7 +17,7 @@ pub struct TrezorTxSigner<'a, TxP> { pub tx_provider: TxP, pub trezor_coin: String, pub params: UtxoSignTxParams, - pub fork_id: u32, + pub fork_id: u8, pub branch_id: u32, } diff --git a/mm2src/mm2_bitcoin/script/src/sign.rs b/mm2src/mm2_bitcoin/script/src/sign.rs index a70e423eea..c7f4210407 100644 --- a/mm2src/mm2_bitcoin/script/src/sign.rs +++ b/mm2src/mm2_bitcoin/script/src/sign.rs @@ -37,8 +37,8 @@ pub enum SighashBase { Single = 3, } -impl From for u32 { - fn from(s: SighashBase) -> Self { s as u32 } +impl From for u8 { + fn from(s: SighashBase) -> Self { s as u8 } } #[cfg_attr(feature = "cargo-clippy", allow(clippy::doc_markdown))] @@ -50,16 +50,13 @@ pub struct Sighash { pub fork_id: bool, } -impl From for u32 { +impl From for u8 { fn from(s: Sighash) -> Self { - let base = s.base as u32; - let base = if s.anyone_can_pay { base | 0x80 } else { base }; + let mut base = s.base as u8; + base = if s.anyone_can_pay { base | 0x80 } else { base }; + base = if s.fork_id { base | 0x40 } else { base }; - if s.fork_id { - base | 0x40 - } else { - base - } + base } } @@ -73,7 +70,7 @@ impl Sighash { } /// Used by SCRIPT_VERIFY_STRICTENC - pub fn is_defined(version: SignatureVersion, u: u32) -> bool { + pub fn is_defined(version: SignatureVersion, u: u8) -> bool { // reset anyone_can_pay && fork_id (if applicable) bits let u = match version { SignatureVersion::ForkId => u & !(0x40 | 0x80), @@ -85,7 +82,7 @@ impl Sighash { } /// Creates Sighash from any u, even if is_defined() == false - pub fn from_u32(version: SignatureVersion, u: u32) -> Self { + pub fn from_u8(version: SignatureVersion, u: u8) -> Self { let anyone_can_pay = (u & 0x80) == 0x80; let fork_id = version == SignatureVersion::ForkId && (u & 0x40) == 0x40; let base = match u & 0x1f { @@ -233,9 +230,9 @@ impl TransactionInputSigner { input_amount: u64, script_pubkey: &Script, sigversion: SignatureVersion, - sighashtype: u32, + sighashtype: u8, ) -> H256 { - let sighash = Sighash::from_u32(sigversion, sighashtype); + let sighash = Sighash::from_u8(sigversion, sighashtype); match sigversion { SignatureVersion::ForkId if sighash.fork_id => { self.signature_hash_fork_id(input_index, input_amount, script_pubkey, sighashtype, sighash) @@ -258,12 +255,12 @@ impl TransactionInputSigner { input_amount: u64, script_pubkey: &Script, sigversion: SignatureVersion, - sighash: u32, + sighash: u8, ) -> TransactionInput { let hash = self.signature_hash(input_index, input_amount, script_pubkey, sigversion, sighash); let mut signature: Vec = keypair.private().sign_low_r(&hash).unwrap().into(); - signature.push(sighash as u8); + signature.push(sighash); let script_sig = Builder::default() .push_data(&signature) //.push_data(keypair.public()) @@ -282,7 +279,7 @@ impl TransactionInputSigner { &self, input_index: usize, script_pubkey: &Script, - sighashtype: u32, + sighashtype: u8, sighash: Sighash, ) -> H256 { if input_index >= self.inputs.len() { @@ -374,7 +371,7 @@ impl TransactionInputSigner { let mut stream = Stream::default(); stream.append(&tx); - stream.append(&sighashtype); + stream.append(&(sighashtype as u32)); let out = stream.out(); match self.hash_algo { SignerHashAlgo::DSHA256 => dhash256(&out), @@ -387,7 +384,7 @@ impl TransactionInputSigner { input_index: usize, input_amount: u64, script_pubkey: &Script, - sighashtype: u32, + sighashtype: u8, sighash: Sighash, ) -> H256 { let hash_prevouts = compute_hash_prevouts(sighash, &self.inputs); @@ -404,7 +401,8 @@ impl TransactionInputSigner { stream.append(&self.inputs[input_index].sequence); stream.append(&hash_outputs); stream.append(&self.lock_time); - stream.append(&sighashtype); // this also includes 24-bit fork id. which is 0 for BitcoinCash + // TODO: This is missing the fork id. + stream.append(&(sighashtype as u32)); // this also includes 24-bit fork id. which is 0 for BitcoinCash let out = stream.out(); dhash256(&out) } @@ -414,7 +412,7 @@ impl TransactionInputSigner { input_index: usize, input_amount: u64, script_pubkey: &Script, - sighashtype: u32, + sighashtype: u8, sighash: Sighash, ) -> H256 { if input_index >= self.inputs.len() { @@ -436,7 +434,7 @@ impl TransactionInputSigner { &self, input_index: usize, script_pubkey: &Script, - sighashtype: u32, + sighashtype: u8, sighash: Sighash, ) -> Result { let mut sig_hash_stream = Stream::new(); @@ -553,7 +551,7 @@ impl TransactionInputSigner { sig_hash_stream.append(&self.lock_time); sig_hash_stream.append(&self.expiry_height); sig_hash_stream.append(&self.value_balance); - sig_hash_stream.append(&sighashtype); + sig_hash_stream.append(&(sighashtype as u32)); sig_hash_stream.append(&self.inputs[input_index].previous_output); sig_hash_stream.append(&script_pubkey.to_bytes()); @@ -752,7 +750,7 @@ mod tests { tx: &'static str, script: &'static str, input_index: usize, - hash_type: i32, + hash_type: u8, result: &'static str, ) { let tx: Transaction = tx.into(); @@ -760,22 +758,20 @@ mod tests { let script: Script = script.into(); let expected = H256::from_reversed_str(result); - let sighash = Sighash::from_u32(SignatureVersion::Base, hash_type as u32); - let hash = signer.signature_hash_original(input_index, &script, hash_type as u32, sighash); + let sighash = Sighash::from_u8(SignatureVersion::Base, hash_type); + let hash = signer.signature_hash_original(input_index, &script, hash_type, sighash); assert_eq!(expected, hash); } #[test] fn test_sighash_forkid_from_u32() { - assert!(!Sighash::is_defined(SignatureVersion::Base, 0xFFFFFF82)); - assert!(!Sighash::is_defined(SignatureVersion::Base, 0x00000182)); - assert!(!Sighash::is_defined(SignatureVersion::Base, 0x00000080)); - assert!(Sighash::is_defined(SignatureVersion::Base, 0x00000001)); - assert!(Sighash::is_defined(SignatureVersion::Base, 0x00000082)); - assert!(Sighash::is_defined(SignatureVersion::Base, 0x00000003)); - - assert!(!Sighash::is_defined(SignatureVersion::ForkId, 0xFFFFFFC2)); - assert!(!Sighash::is_defined(SignatureVersion::ForkId, 0x000001C2)); + assert!(!Sighash::is_defined(SignatureVersion::Base, 0x80)); + assert!(!Sighash::is_defined(SignatureVersion::Base, 0x84)); + assert!(!Sighash::is_defined(SignatureVersion::Base, 0x85)); + assert!(Sighash::is_defined(SignatureVersion::Base, 0x01)); + assert!(Sighash::is_defined(SignatureVersion::Base, 0x82)); + assert!(Sighash::is_defined(SignatureVersion::Base, 0x03)); + assert!(Sighash::is_defined(SignatureVersion::ForkId, 0x00000081)); assert!(Sighash::is_defined(SignatureVersion::ForkId, 0x000000C2)); assert!(Sighash::is_defined(SignatureVersion::ForkId, 0x00000043)); @@ -800,7 +796,7 @@ mod tests { signer.inputs[0].amount = 50000000; signer.consensus_branch_id = 0x76b809bb; - let sig_hash = Sighash::from_u32(SignatureVersion::Base, 1); + let sig_hash = Sighash::from_u8(SignatureVersion::Base, 1); let hash = signer.signature_hash_overwintered( 0, &Script::from("1976a914507173527b4c3318a2aecd793bf1cfed705950cf88ac"), @@ -834,7 +830,7 @@ mod tests { signer.inputs[0].amount = 9924260; signer.consensus_branch_id = 0x76b809bb; - let sig_hash = Sighash::from_u32(SignatureVersion::Base, 1); + let sig_hash = Sighash::from_u8(SignatureVersion::Base, 1); let hash = signer.signature_hash_overwintered( 0, &Script::from("76a91405aab5342166f8594baf17a7d9bef5d56744332788ac"), @@ -868,7 +864,7 @@ mod tests { signer.inputs[0].amount = 100000000; signer.consensus_branch_id = 0x76b809bb; - let sig_hash = Sighash::from_u32(SignatureVersion::Base, 1); + let sig_hash = Sighash::from_u8(SignatureVersion::Base, 1); let hash = signer.signature_hash_overwintered( 0, &Script::from("6304e5928060b17521031c632dad67a611de77d9666cbc61e65957c7d7544c25e384f4e76de729e6a1bfac6782012088a914b78f0b837e2c710f8b28e59d06473d489e5315c88821037310a8fb9fd8f198a1a21db830252ad681fccda580ed4101f3f6bfb98b34fab5ac68"), diff --git a/mm2src/mm2_bitcoin/script/src/verify.rs b/mm2src/mm2_bitcoin/script/src/verify.rs index 804a856d34..2ba19a3ace 100644 --- a/mm2src/mm2_bitcoin/script/src/verify.rs +++ b/mm2src/mm2_bitcoin/script/src/verify.rs @@ -11,7 +11,7 @@ pub trait SignatureChecker { signature: &Signature, public: &Public, script_code: &Script, - sighashtype: u32, + sighashtype: u8, version: SignatureVersion, ) -> bool; @@ -23,7 +23,7 @@ pub trait SignatureChecker { pub struct NoopSignatureChecker; impl SignatureChecker for NoopSignatureChecker { - fn check_signature(&self, _: &Signature, _: &Public, _: &Script, _: u32, _: SignatureVersion) -> bool { false } + fn check_signature(&self, _: &Signature, _: &Public, _: &Script, _: u8, _: SignatureVersion) -> bool { false } fn check_lock_time(&self, _: Num) -> bool { false } @@ -43,7 +43,7 @@ impl SignatureChecker for TransactionSignatureChecker { signature: &Signature, public: &Public, script_code: &Script, - sighashtype: u32, + sighashtype: u8, version: SignatureVersion, ) -> bool { let hash = self diff --git a/mm2src/mm2_main/src/lp_ordermatch.rs b/mm2src/mm2_main/src/lp_ordermatch.rs index 620cb79bfb..f41172dd8e 100644 --- a/mm2src/mm2_main/src/lp_ordermatch.rs +++ b/mm2src/mm2_main/src/lp_ordermatch.rs @@ -3163,7 +3163,7 @@ fn lp_connected_alice(ctx: MmArc, taker_order: TakerOrder, taker_match: TakerMat maker_p2p_pubkey: match maker_p2p_pubkey { PublicKey::Secp256k1(pubkey) => pubkey.into(), }, - require_maker_payment_confirm_before_funding_spend: true, + require_maker_payment_confirm_before_taker_payment: true, }; #[allow(clippy::box_default)] taker_swap_state_machine diff --git a/mm2src/mm2_main/src/lp_swap/komodefi.swap_v2.pb.rs b/mm2src/mm2_main/src/lp_swap/komodefi.swap_v2.pb.rs index 38c6e07106..92afc129d9 100644 --- a/mm2src/mm2_main/src/lp_swap/komodefi.swap_v2.pb.rs +++ b/mm2src/mm2_main/src/lp_swap/komodefi.swap_v2.pb.rs @@ -54,6 +54,8 @@ pub struct TakerNegotiationData { pub maker_coin_swap_contract: ::core::option::Option<::prost::alloc::vec::Vec>, #[prost(bytes = "vec", optional, tag = "8")] pub taker_coin_swap_contract: ::core::option::Option<::prost::alloc::vec::Vec>, + #[prost(uint64, tag = "9")] + pub taker_payment_fee: u64, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -105,9 +107,9 @@ pub struct MakerPaymentInfo { #[prost(bytes = "vec", optional, tag = "2")] pub next_step_instructions: ::core::option::Option<::prost::alloc::vec::Vec>, #[prost(bytes = "vec", tag = "3")] - pub funding_preimage_sig: ::prost::alloc::vec::Vec, + pub taker_payment_preimage_sig: ::prost::alloc::vec::Vec, #[prost(bytes = "vec", tag = "4")] - pub funding_preimage_tx: ::prost::alloc::vec::Vec, + pub taker_payment_preimage_tx: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index 84c1bbc6aa..83902a5ac6 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -497,7 +497,7 @@ impl MakerSwap { let params = MakerSwapPreparedParams { maker_payment_trade_fee: maker_payment_trade_fee.clone(), - taker_payment_spend_trade_fee: taker_payment_spend_trade_fee.clone(), + taker_payment_fee: taker_payment_spend_trade_fee.clone(), }; match check_balance_for_maker_swap( &self.ctx, @@ -2165,7 +2165,7 @@ pub async fn run_maker_swap(swap: RunMakerSwapInput, ctx: MmArc) { pub struct MakerSwapPreparedParams { pub(super) maker_payment_trade_fee: TradeFee, - pub(super) taker_payment_spend_trade_fee: TradeFee, + pub(super) taker_payment_fee: TradeFee, } pub async fn check_balance_for_maker_swap( @@ -2180,7 +2180,7 @@ pub async fn check_balance_for_maker_swap( let (maker_payment_trade_fee, taker_payment_spend_trade_fee) = match prepared_params { Some(MakerSwapPreparedParams { maker_payment_trade_fee, - taker_payment_spend_trade_fee, + taker_payment_fee: taker_payment_spend_trade_fee, }) => (maker_payment_trade_fee, taker_payment_spend_trade_fee), None => { let preimage_value = TradePreimageValue::Exact(volume.to_decimal()); @@ -2255,7 +2255,7 @@ pub async fn maker_swap_trade_preimage( } else { let prepared_params = MakerSwapPreparedParams { maker_payment_trade_fee: base_coin_fee.clone(), - taker_payment_spend_trade_fee: rel_coin_fee.clone(), + taker_payment_fee: rel_coin_fee.clone(), }; check_balance_for_maker_swap( ctx, diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs b/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs index d0e667a752..ba7259fd31 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs @@ -3,12 +3,13 @@ use super::{swap_v2_topic, LockedAmount, LockedAmountInfo, SavedTradeFee, SwapsC NEGOTIATION_TIMEOUT_SEC}; use crate::lp_swap::maker_swap::MakerSwapPreparedParams; use crate::lp_swap::swap_lock::SwapLock; +use crate::lp_swap::swap_v2_pb::*; use crate::lp_swap::{broadcast_swap_v2_msg_every, check_balance_for_maker_swap, recv_swap_v2_msg, SecretHashAlgo, SwapConfirmationsSettings, TransactionIdentifier, MAKER_SWAP_V2_TYPE, MAX_STARTED_AT_DIFF}; -use crate::lp_swap::{swap_v2_pb::*, NO_REFUND_FEE}; use async_trait::async_trait; use bitcrypto::{dhash160, sha256}; -use coins::{CanRefundHtlc, ConfirmPaymentInput, DexFee, FeeApproxStage, FundingTxSpend, GenTakerFundingSpendArgs, +use coins::utxo::utxo_common::big_decimal_from_sat_unsigned; +use coins::{CanRefundHtlc, ConfirmPaymentInput, DexFee, FeeApproxStage, FundingTxSpend, GenTakerPaymentPreimageArgs, GenTakerPaymentSpendArgs, MakerCoinSwapOpsV2, MmCoin, ParseCoinAssocTypes, RefundMakerPaymentSecretArgs, RefundMakerPaymentTimelockArgs, SearchForFundingSpendErr, SendMakerPaymentArgs, SwapTxTypeWithSecretHash, TakerCoinSwapOpsV2, ToBytes, TradePreimageValue, Transaction, TxPreimageWithSig, ValidateTakerFundingArgs}; @@ -21,9 +22,10 @@ use keys::KeyPair; use mm2_core::mm_ctx::MmArc; use mm2_err_handle::prelude::*; use mm2_libp2p::Secp256k1PubkeySerialize; -use mm2_number::MmNumber; +use mm2_number::{BigDecimal, MmNumber}; use mm2_state_machine::prelude::*; use mm2_state_machine::storable_state_machine::*; +use num_traits::Zero; use primitives::hash::H256; use rpc::v1::types::{Bytes as BytesJson, H256 as H256Json}; use secp256k1::PublicKey; @@ -55,6 +57,7 @@ pub struct StoredNegotiationData { maker_coin_swap_contract: Option, taker_coin_swap_contract: Option, taker_secret_hash: BytesJson, + taker_payment_fee: BigDecimal, } /// Represents events produced by maker swap states. @@ -65,15 +68,15 @@ pub enum MakerSwapEvent { Initialized { maker_coin_start_block: u64, taker_coin_start_block: u64, - maker_payment_trade_fee: SavedTradeFee, - taker_payment_spend_trade_fee: SavedTradeFee, + maker_payment_fee: SavedTradeFee, + taker_payment_fee: SavedTradeFee, }, /// Started waiting for taker funding tx. WaitingForTakerFunding { maker_coin_start_block: u64, taker_coin_start_block: u64, negotiation_data: StoredNegotiationData, - maker_payment_trade_fee: SavedTradeFee, + maker_payment_fee: SavedTradeFee, }, /// Received taker funding info. TakerFundingReceived { @@ -81,16 +84,16 @@ pub enum MakerSwapEvent { taker_coin_start_block: u64, negotiation_data: StoredNegotiationData, taker_funding: TransactionIdentifier, - maker_payment_trade_fee: SavedTradeFee, + maker_payment_fee: SavedTradeFee, }, - /// Sent maker payment and generated funding spend preimage. - MakerPaymentSentFundingSpendGenerated { + /// Sent maker payment and generated taker payment preimage. + MakerPaymentSentTakerPaymentPreimageGenerated { maker_coin_start_block: u64, taker_coin_start_block: u64, negotiation_data: StoredNegotiationData, maker_payment: TransactionIdentifier, taker_funding: TransactionIdentifier, - funding_spend_preimage: StoredTxPreimage, + taker_payment_preimage: StoredTxPreimage, }, /// Something went wrong, so maker payment refund is required. MakerPaymentRefundRequired { @@ -465,21 +468,21 @@ impl Box::new(Initialized { maker_coin: Default::default(), taker_coin: Default::default(), maker_coin_start_block, taker_coin_start_block, - maker_payment_trade_fee, - taker_payment_spend_trade_fee, + maker_payment_fee, + taker_payment_fee, }), MakerSwapEvent::WaitingForTakerFunding { maker_coin_start_block, taker_coin_start_block, negotiation_data, - maker_payment_trade_fee, + maker_payment_fee, } => Box::new(WaitingForTakerFunding { maker_coin_start_block, taker_coin_start_block, @@ -488,14 +491,14 @@ impl Box::new(TakerFundingReceived { maker_coin_start_block, taker_coin_start_block, @@ -508,16 +511,16 @@ impl Box::new(MakerPaymentSentFundingSpendGenerated { + taker_payment_preimage, + } => Box::new(MakerPaymentSentTakerPaymentPreimageGenerated { maker_coin_start_block, taker_coin_start_block, negotiation_data: NegotiationData::from_stored_data( @@ -529,14 +532,14 @@ impl { + MakerSwapEvent::Initialized { maker_payment_fee, .. } => { let swaps_ctx = SwapsContext::from_ctx(&self.ctx).expect("from_ctx should not fail at this point"); let maker_coin_ticker: String = self.maker_coin.ticker().into(); let new_locked = LockedAmountInfo { @@ -691,7 +690,7 @@ impl { + MakerSwapEvent::MakerPaymentSentTakerPaymentPreimageGenerated { .. } => { let swaps_ctx = SwapsContext::from_ctx(&self.ctx).expect("from_ctx should not fail at this point"); let ticker = self.maker_coin.ticker(); if let Some(maker_coin_locked) = swaps_ctx.locked_amounts.lock().unwrap().get_mut(ticker) { @@ -725,18 +724,9 @@ impl::DbRepr as StateMachineDbRepr>::Event, ) { match event { - MakerSwapEvent::Initialized { - maker_payment_trade_fee, - .. - } - | MakerSwapEvent::WaitingForTakerFunding { - maker_payment_trade_fee, - .. - } - | MakerSwapEvent::TakerFundingReceived { - maker_payment_trade_fee, - .. - } => { + MakerSwapEvent::Initialized { maker_payment_fee, .. } + | MakerSwapEvent::WaitingForTakerFunding { maker_payment_fee, .. } + | MakerSwapEvent::TakerFundingReceived { maker_payment_fee, .. } => { let swaps_ctx = SwapsContext::from_ctx(&self.ctx).expect("from_ctx should not fail at this point"); let maker_coin_ticker: String = self.maker_coin.ticker().into(); let new_locked = LockedAmountInfo { @@ -744,7 +734,7 @@ impl fee, Err(e) => { let reason = AbortReason::FailedToGetMakerPaymentFee(e.to_string()); @@ -824,18 +809,17 @@ impl fee, Err(e) => { - let reason = AbortReason::FailedToGetTakerPaymentSpendFee(e.to_string()); + let reason = AbortReason::FailedToGetTakerPaymentFee(e.to_string()); return Self::change_state(Aborted::new(reason), state_machine).await; }, }; let prepared_params = MakerSwapPreparedParams { - maker_payment_trade_fee: maker_payment_trade_fee.clone(), - taker_payment_spend_trade_fee: taker_payment_spend_trade_fee.clone(), + maker_payment_trade_fee: maker_payment_fee.clone(), + taker_payment_fee: taker_payment_fee.clone(), }; if let Err(e) = check_balance_for_maker_swap( @@ -845,7 +829,7 @@ impl { taker_coin: PhantomData, maker_coin_start_block: u64, taker_coin_start_block: u64, - maker_payment_trade_fee: SavedTradeFee, - taker_payment_spend_trade_fee: SavedTradeFee, + maker_payment_fee: SavedTradeFee, + taker_payment_fee: SavedTradeFee, } impl TransitionFrom> for Initialized {} @@ -886,8 +870,8 @@ impl p, @@ -999,8 +1002,9 @@ impl>, taker_coin_swap_contract: Option>, taker_secret_hash: Vec, + taker_payment_fee: BigDecimal, } impl NegotiationData { @@ -1026,6 +1031,7 @@ impl Negotiation maker_coin_swap_contract: self.maker_coin_swap_contract.clone().map(|b| b.into()), taker_coin_swap_contract: self.taker_coin_swap_contract.clone().map(|b| b.into()), taker_secret_hash: self.taker_secret_hash.clone().into(), + taker_payment_fee: self.taker_payment_fee.clone(), } } @@ -1046,6 +1052,7 @@ impl Negotiation maker_coin_swap_contract: None, taker_coin_swap_contract: None, taker_secret_hash: stored.taker_secret_hash.into(), + taker_payment_fee: stored.taker_payment_fee, }) } } @@ -1054,7 +1061,7 @@ struct WaitingForTakerFunding, - maker_payment_trade_fee: SavedTradeFee, + maker_payment_fee: SavedTradeFee, } impl TransitionFrom> @@ -1114,7 +1121,7 @@ impl, taker_funding: TakerCoin::Tx, - maker_payment_trade_fee: SavedTradeFee, + maker_payment_fee: SavedTradeFee, } impl @@ -1165,6 +1172,7 @@ impl p, Err(e) => { - let reason = AbortReason::FailedToGenerateFundingSpend(e.to_string()); + let reason = AbortReason::FailedToGenerateTakerPaymentPreimage(e.to_string()); return Self::change_state(Aborted::new(reason), state_machine).await; }, }; @@ -1217,12 +1226,12 @@ impl { +struct MakerPaymentSentTakerPaymentPreimageGenerated { maker_coin_start_block: u64, taker_coin_start_block: u64, negotiation_data: NegotiationData, taker_funding: TakerCoin::Tx, - funding_spend_preimage: TxPreimageWithSig, + taker_payment_preimage: TxPreimageWithSig, maker_payment: MakerCoin::Tx, } impl TransitionFrom> - for MakerPaymentSentFundingSpendGenerated + for MakerPaymentSentTakerPaymentPreimageGenerated { } #[async_trait] impl State - for MakerPaymentSentFundingSpendGenerated + for MakerPaymentSentTakerPaymentPreimageGenerated { type StateMachine = MakerSwapStateMachine; @@ -1274,8 +1283,8 @@ impl StorableState - for MakerPaymentSentFundingSpendGenerated + for MakerPaymentSentTakerPaymentPreimageGenerated { type StateMachine = MakerSwapStateMachine; fn get_event(&self) -> MakerSwapEvent { - MakerSwapEvent::MakerPaymentSentFundingSpendGenerated { + MakerSwapEvent::MakerPaymentSentTakerPaymentPreimageGenerated { maker_coin_start_block: self.maker_coin_start_block, taker_coin_start_block: self.taker_coin_start_block, negotiation_data: self.negotiation_data.to_stored_data(), @@ -1396,9 +1405,9 @@ impl - TransitionFrom> + TransitionFrom> for MakerPaymentRefundRequired { } @@ -1563,7 +1572,7 @@ struct TakerPaymentReceived - TransitionFrom> + TransitionFrom> for TakerPaymentReceived { } @@ -1795,15 +1804,16 @@ pub enum AbortReason { DidNotReceiveTakerFundingInfo(String), FailedToParseTakerFunding(String), TakerFundingValidationFailed(String), - FailedToGenerateFundingSpend(String), + FailedToGenerateTakerPaymentPreimage(String), FailedToSendMakerPayment(String), TooLargeStartedAtDiff(u64), TakerProvidedInvalidFundingLocktime(u64), TakerProvidedInvalidPaymentLocktime(u64), FailedToParsePubkey(String), + TakerPaymentSpentFeeTooLow(BigDecimal), MakerPaymentRefundFailed(String), FailedToGetMakerPaymentFee(String), - FailedToGetTakerPaymentSpendFee(String), + FailedToGetTakerPaymentFee(String), } struct Aborted { diff --git a/mm2src/mm2_main/src/lp_swap/swap_v2.proto b/mm2src/mm2_main/src/lp_swap/swap_v2.proto index 9d8d92d28f..ce86744520 100644 --- a/mm2src/mm2_main/src/lp_swap/swap_v2.proto +++ b/mm2src/mm2_main/src/lp_swap/swap_v2.proto @@ -32,6 +32,7 @@ message TakerNegotiationData { bytes taker_coin_htlc_pub = 6; optional bytes maker_coin_swap_contract = 7; optional bytes taker_coin_swap_contract = 8; + uint64 taker_payment_fee = 9; } message TakerNegotiation { @@ -60,8 +61,8 @@ message TakerPaymentInfo { message MakerPaymentInfo { bytes tx_bytes = 1; optional bytes next_step_instructions = 2; - bytes funding_preimage_sig = 3; - bytes funding_preimage_tx = 4; + bytes taker_payment_preimage_sig = 3; + bytes taker_payment_preimage_tx = 4; } message TakerPaymentSpendPreimage { diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index c7b1cf59a9..c2ed9d1de7 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -1063,8 +1063,8 @@ impl TakerSwap { let params = TakerSwapPreparedParams { dex_fee: dex_fee.total_spend_amount(), fee_to_send_dex_fee: fee_to_send_dex_fee.clone(), - taker_payment_trade_fee: taker_payment_trade_fee.clone(), - maker_payment_spend_trade_fee: maker_payment_spend_trade_fee.clone(), + funding_fee: taker_payment_trade_fee.clone(), + maker_payment_spend_fee: maker_payment_spend_trade_fee.clone(), }; let check_balance_f = check_balance_for_taker_swap( &self.ctx, @@ -2408,8 +2408,8 @@ impl AtomicSwap for TakerSwap { pub struct TakerSwapPreparedParams { pub(super) dex_fee: MmNumber, pub(super) fee_to_send_dex_fee: TradeFee, - pub(super) taker_payment_trade_fee: TradeFee, - pub(super) maker_payment_spend_trade_fee: TradeFee, + pub(super) funding_fee: TradeFee, + pub(super) maker_payment_spend_fee: TradeFee, } pub async fn check_balance_for_taker_swap( @@ -2430,11 +2430,11 @@ pub async fn check_balance_for_taker_swap( .await .mm_err(|e| CheckBalanceError::from_trade_preimage_error(e, my_coin.ticker()))?; let preimage_value = TradePreimageValue::Exact(volume.to_decimal()); - let taker_payment_trade_fee = my_coin + let funding_fee = my_coin .get_sender_trade_fee(preimage_value, stage, INCLUDE_REFUND_FEE) .await .mm_err(|e| CheckBalanceError::from_trade_preimage_error(e, my_coin.ticker()))?; - let maker_payment_spend_trade_fee = other_coin + let maker_payment_spend_fee = other_coin .get_receiver_trade_fee(stage) .compat() .await @@ -2442,8 +2442,8 @@ pub async fn check_balance_for_taker_swap( TakerSwapPreparedParams { dex_fee: dex_fee.total_spend_amount(), fee_to_send_dex_fee, - taker_payment_trade_fee, - maker_payment_spend_trade_fee, + funding_fee, + maker_payment_spend_fee, } }, }; @@ -2453,17 +2453,9 @@ pub async fn check_balance_for_taker_swap( fee_to_send_dex_fee: params.fee_to_send_dex_fee, }; - check_my_coin_balance_for_swap( - ctx, - my_coin, - swap_uuid, - volume, - params.taker_payment_trade_fee, - Some(taker_fee), - ) - .await?; - if !params.maker_payment_spend_trade_fee.paid_from_trading_vol { - check_other_coin_balance_for_swap(ctx, other_coin, swap_uuid, params.maker_payment_spend_trade_fee).await?; + check_my_coin_balance_for_swap(ctx, my_coin, swap_uuid, volume, params.funding_fee, Some(taker_fee)).await?; + if !params.maker_payment_spend_fee.paid_from_trading_vol { + check_other_coin_balance_for_swap(ctx, other_coin, swap_uuid, params.maker_payment_spend_fee).await?; } Ok(()) } @@ -2538,8 +2530,8 @@ pub async fn taker_swap_trade_preimage( let prepared_params = TakerSwapPreparedParams { dex_fee: dex_amount.total_spend_amount(), fee_to_send_dex_fee: fee_to_send_taker_fee.clone(), - taker_payment_trade_fee: my_coin_trade_fee.clone(), - maker_payment_spend_trade_fee: other_coin_trade_fee.clone(), + funding_fee: my_coin_trade_fee.clone(), + maker_payment_spend_fee: other_coin_trade_fee.clone(), }; check_balance_for_taker_swap( ctx, diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs b/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs index 29f3d07277..54314af673 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs @@ -2,13 +2,14 @@ use super::swap_v2_common::*; use super::{LockedAmount, LockedAmountInfo, SavedTradeFee, SwapsContext, TakerSwapPreparedParams, NEGOTIATE_SEND_INTERVAL, NEGOTIATION_TIMEOUT_SEC}; use crate::lp_swap::swap_lock::SwapLock; +use crate::lp_swap::swap_v2_pb::*; use crate::lp_swap::{broadcast_swap_v2_msg_every, check_balance_for_taker_swap, recv_swap_v2_msg, swap_v2_topic, SecretHashAlgo, SwapConfirmationsSettings, TransactionIdentifier, MAX_STARTED_AT_DIFF, TAKER_SWAP_V2_TYPE}; -use crate::lp_swap::{swap_v2_pb::*, NO_REFUND_FEE}; use async_trait::async_trait; use bitcrypto::{dhash160, sha256}; -use coins::{CanRefundHtlc, ConfirmPaymentInput, DexFee, FeeApproxStage, GenTakerFundingSpendArgs, +use coins::utxo::sat_from_big_decimal; +use coins::{CanRefundHtlc, ConfirmPaymentInput, DexFee, FeeApproxStage, GenTakerPaymentPreimageArgs, GenTakerPaymentSpendArgs, MakerCoinSwapOpsV2, MmCoin, ParseCoinAssocTypes, RefundFundingSecretArgs, RefundTakerPaymentArgs, SendTakerFundingArgs, SpendMakerPaymentArgs, SwapTxTypeWithSecretHash, TakerCoinSwapOpsV2, ToBytes, TradeFee, TradePreimageValue, Transaction, TxPreimageWithSig, @@ -66,16 +67,16 @@ pub enum TakerSwapEvent { Initialized { maker_coin_start_block: u64, taker_coin_start_block: u64, + funding_fee: SavedTradeFee, taker_payment_fee: SavedTradeFee, - maker_payment_spend_fee: SavedTradeFee, }, /// Negotiated swap data with maker. Negotiated { maker_coin_start_block: u64, taker_coin_start_block: u64, negotiation_data: StoredNegotiationData, + funding_fee: SavedTradeFee, taker_payment_fee: SavedTradeFee, - maker_payment_spend_fee: SavedTradeFee, }, /// Sent taker funding tx. TakerFundingSent { @@ -83,6 +84,7 @@ pub enum TakerSwapEvent { taker_coin_start_block: u64, negotiation_data: StoredNegotiationData, taker_funding: TransactionIdentifier, + taker_payment_fee: SavedTradeFee, }, /// Taker funding tx refund is required. TakerFundingRefundRequired { @@ -93,12 +95,13 @@ pub enum TakerSwapEvent { reason: TakerFundingRefundReason, }, /// Received maker payment and taker funding spend preimage - MakerPaymentAndFundingSpendPreimgReceived { + MakerPaymentAndTakerPaymentPreimageReceived { maker_coin_start_block: u64, taker_coin_start_block: u64, negotiation_data: StoredNegotiationData, + taker_payment_fee: SavedTradeFee, taker_funding: TransactionIdentifier, - funding_spend_preimage: StoredTxPreimage, + taker_payment_preimage: StoredTxPreimage, maker_payment: TransactionIdentifier, }, /// Sent taker payment. @@ -121,7 +124,7 @@ pub enum TakerSwapEvent { taker_coin_start_block: u64, maker_payment: TransactionIdentifier, taker_funding: TransactionIdentifier, - funding_spend_preimage: StoredTxPreimage, + taker_payment_preimage: StoredTxPreimage, negotiation_data: StoredNegotiationData, }, /// Maker spent taker's payment and taker discovered the tx on-chain. @@ -420,7 +423,7 @@ pub struct TakerSwapStateMachine @@ -495,22 +498,22 @@ impl Box::new(Initialized { maker_coin: Default::default(), taker_coin: Default::default(), maker_coin_start_block, taker_coin_start_block, + funding_fee, taker_payment_fee, - maker_payment_spend_fee, }), TakerSwapEvent::Negotiated { maker_coin_start_block, taker_coin_start_block, negotiation_data, + funding_fee, taker_payment_fee, - maker_payment_spend_fee, } => Box::new(Negotiated { maker_coin_start_block, taker_coin_start_block, @@ -519,14 +522,15 @@ impl Box::new(TakerFundingSent { maker_coin_start_block, taker_coin_start_block, @@ -539,6 +543,7 @@ impl Box::new(MakerPaymentAndFundingSpendPreimgReceived { + taker_payment_preimage, + } => Box::new(MakerPaymentAndTakerPaymentPreimageReceived { maker_coin_start_block, taker_coin_start_block, negotiation_data: NegotiationData::from_stored_data( @@ -575,18 +581,19 @@ impl Box::new(MakerPaymentConfirmed { maker_coin_start_block, @@ -651,14 +658,14 @@ impl { let swaps_ctx = SwapsContext::from_ctx(&self.ctx).expect("from_ctx should not fail at this point"); @@ -806,8 +813,9 @@ impl::DbRepr as StateMachineDbRepr>::Event, ) { match event { - TakerSwapEvent::Initialized { taker_payment_fee, .. } - | TakerSwapEvent::Negotiated { taker_payment_fee, .. } => { + TakerSwapEvent::Initialized { + funding_fee, + taker_payment_fee, + .. + } + | TakerSwapEvent::Negotiated { + funding_fee, + taker_payment_fee, + .. + } => { let swaps_ctx = SwapsContext::from_ctx(&self.ctx).expect("from_ctx should not fail at this point"); let taker_coin_ticker: String = self.taker_coin.ticker().into(); let new_locked = LockedAmountInfo { swap_uuid: self.uuid, locked_amount: LockedAmount { coin: taker_coin_ticker.clone(), - amount: &(&self.taker_volume + &self.dex_fee.total_spend_amount()) + &self.taker_premium, - trade_fee: Some(taker_payment_fee.into()), + amount: &(&self.taker_volume + &self.dex_fee.total_spend_amount()) + + &(&self.taker_premium + &taker_payment_fee.amount.into()), + trade_fee: Some(funding_fee.into()), }, }; swaps_ctx @@ -867,7 +884,7 @@ impl fee, + Err(e) => { + let reason = AbortReason::FailedToGetTakerPaymentSpendFee(e.to_string()); + return Self::change_state(Aborted::new(reason), state_machine).await; + }, + }; + + // TODO: Add the price of maker_payment fee (in taker coin) + taker_payment_spend fee to the total payment value. + let total_payment_value = &(&state_machine.taker_volume + &state_machine.dex_fee.total_spend_amount()) + + &(&state_machine.taker_premium + &taker_payment_fee.amount); let preimage_value = TradePreimageValue::Exact(total_payment_value.to_decimal()); - let stage = FeeApproxStage::StartSwap; - let taker_payment_fee = match state_machine - .taker_coin - .get_sender_trade_fee(preimage_value, stage, NO_REFUND_FEE) - .await - { + let funding_fee = match state_machine.taker_coin.get_funding_fee(preimage_value).await { Ok(fee) => fee, Err(e) => { let reason = AbortReason::FailedToGetTakerPaymentFee(e.to_string()); @@ -942,7 +965,7 @@ impl fee, Err(e) => { let reason = AbortReason::FailedToGetMakerPaymentSpendFee(e.to_string()); @@ -951,14 +974,18 @@ impl { taker_coin: PhantomData, maker_coin_start_block: u64, taker_coin_start_block: u64, + funding_fee: SavedTradeFee, taker_payment_fee: SavedTradeFee, - maker_payment_spend_fee: SavedTradeFee, } impl TransitionFrom> for Initialized {} @@ -1009,8 +1036,8 @@ impl, + funding_fee: SavedTradeFee, taker_payment_fee: SavedTradeFee, - maker_payment_spend_fee: SavedTradeFee, } impl TransitionFrom> @@ -1229,6 +1261,7 @@ impl, + taker_payment_fee: SavedTradeFee, } #[async_trait] @@ -1302,7 +1337,7 @@ impl p, Err(e) => { @@ -1355,7 +1390,7 @@ impl p, Err(e) => { @@ -1372,18 +1407,19 @@ impl { +struct MakerPaymentAndTakerPaymentPreimageReceived { maker_coin_start_block: u64, taker_coin_start_block: u64, negotiation_data: NegotiationData, + taker_payment_fee: SavedTradeFee, taker_funding: TakerCoin::Tx, - funding_spend_preimage: TxPreimageWithSig, + taker_payment_preimage: TxPreimageWithSig, maker_payment: MakerCoin::Tx, } impl TransitionFrom> - for MakerPaymentAndFundingSpendPreimgReceived + for MakerPaymentAndTakerPaymentPreimageReceived { } impl StorableState - for MakerPaymentAndFundingSpendPreimgReceived + for MakerPaymentAndTakerPaymentPreimageReceived { type StateMachine = TakerSwapStateMachine; fn get_event(&self) -> TakerSwapEvent { - TakerSwapEvent::MakerPaymentAndFundingSpendPreimgReceived { + TakerSwapEvent::MakerPaymentAndTakerPaymentPreimageReceived { maker_coin_start_block: self.maker_coin_start_block, taker_coin_start_block: self.taker_coin_start_block, negotiation_data: self.negotiation_data.to_stored_data(), + taker_payment_fee: self.taker_payment_fee.clone(), taker_funding: TransactionIdentifier { tx_hex: self.taker_funding.tx_hex().into(), tx_hash: self.taker_funding.tx_hash_as_bytes(), }, - funding_spend_preimage: StoredTxPreimage { - preimage: self.funding_spend_preimage.preimage.to_bytes().into(), - signature: self.funding_spend_preimage.signature.to_bytes().into(), + taker_payment_preimage: StoredTxPreimage { + preimage: self.taker_payment_preimage.preimage.to_bytes().into(), + signature: self.taker_payment_preimage.signature.to_bytes().into(), }, maker_payment: TransactionIdentifier { tx_hex: self.maker_payment.tx_hex().into(), @@ -1459,7 +1498,7 @@ impl State - for MakerPaymentAndFundingSpendPreimgReceived + for MakerPaymentAndTakerPaymentPreimageReceived { type StateMachine = TakerSwapStateMachine; @@ -1487,7 +1526,7 @@ impl tx, @@ -1586,7 +1627,7 @@ impl TransitionFrom> for TakerPaymentSent { } + impl - TransitionFrom> + TransitionFrom> for TakerPaymentSent { } @@ -1616,23 +1658,21 @@ impl; async fn on_changed(self: Box, state_machine: &mut Self::StateMachine) -> StateResult { - if !state_machine.require_maker_payment_confirm_before_funding_spend { - let input = ConfirmPaymentInput { - payment_tx: self.maker_payment.tx_hex(), - confirmations: state_machine.conf_settings.maker_coin_confs, - requires_nota: state_machine.conf_settings.maker_coin_nota, - wait_until: state_machine.maker_payment_conf_timeout(), - check_every: 10, - }; + let input = ConfirmPaymentInput { + payment_tx: self.maker_payment.tx_hex(), + confirmations: state_machine.conf_settings.maker_coin_confs, + requires_nota: state_machine.conf_settings.maker_coin_nota, + wait_until: state_machine.maker_payment_conf_timeout(), + check_every: 10, + }; - if let Err(e) = state_machine.maker_coin.wait_for_confirmations(input).compat().await { - let next_state = TakerPaymentRefundRequired { - taker_payment: self.taker_payment, - negotiation_data: self.negotiation_data, - reason: TakerPaymentRefundReason::MakerPaymentNotConfirmedInTime(e), - }; - return Self::change_state(next_state, state_machine).await; - } + if let Err(e) = state_machine.maker_coin.wait_for_confirmations(input).compat().await { + let next_state = TakerPaymentRefundRequired { + taker_payment: self.taker_payment, + negotiation_data: self.negotiation_data, + reason: TakerPaymentRefundReason::MakerPaymentNotConfirmedInTime(e), + }; + return Self::change_state(next_state, state_machine).await; } let unique_data = state_machine.unique_data(); @@ -1747,11 +1787,11 @@ impl { } impl - TransitionFrom> + TransitionFrom> for TakerFundingRefundRequired { } @@ -1862,10 +1902,6 @@ impl TransitionFrom> for TakerPaymentRefundRequired { } -impl - TransitionFrom> for TakerPaymentRefundRequired -{ -} #[async_trait] impl State @@ -1956,12 +1992,12 @@ struct MakerPaymentConfirmed, + taker_payment_preimage: TxPreimageWithSig, negotiation_data: NegotiationData, } impl - TransitionFrom> + TransitionFrom> for MakerPaymentConfirmed { } @@ -1975,7 +2011,7 @@ impl, state_machine: &mut Self::StateMachine) -> StateResult { let unique_data = state_machine.unique_data(); - let args = GenTakerFundingSpendArgs { + let args = GenTakerPaymentPreimageArgs { funding_tx: &self.taker_funding, maker_pub: &self.negotiation_data.taker_coin_htlc_pub_from_maker, taker_pub: &state_machine.taker_coin.derive_htlc_pubkey_v2(&unique_data), @@ -1983,11 +2019,12 @@ impl tx, @@ -2039,9 +2076,9 @@ impl