From a6052b34aba49e9d461e0ef4338a369e4470986e Mon Sep 17 00:00:00 2001 From: laruh Date: Thu, 31 Oct 2024 21:04:55 +0700 Subject: [PATCH] provide extract_secret_v2 in TakerSwapOpsV2, implement EthCoin extract_secret_v2 --- mm2src/coins/eth.rs | 4 ++ .../eth/eth_swap_v2/eth_taker_swap_v2.rs | 41 ++++++++++++++++++- mm2src/coins/lp_coins.rs | 2 + mm2src/coins/test_coin.rs | 4 ++ mm2src/coins/utxo/utxo_common.rs | 24 +++++++++++ mm2src/coins/utxo/utxo_standard.rs | 4 ++ mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs | 7 +--- 7 files changed, 79 insertions(+), 7 deletions(-) diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 3768166dff8..aea8cc80617 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -7389,6 +7389,10 @@ impl TakerCoinSwapOpsV2 for EthCoin { self.find_taker_payment_spend_tx_impl(taker_payment, from_block, wait_until, 10.) .await } + + async fn extract_secret_v2(&self, secret_hash: &[u8], spend_tx: &Self::Tx) -> Result, String> { + self.extract_secret_v2_impl(secret_hash, spend_tx).await + } } impl CommonSwapOpsV2 for EthCoin { 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 f8d45af0fd7..b8d58eb16fa 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 @@ -3,7 +3,7 @@ use super::{check_decoded_length, validate_from_to_and_status, validate_payment_ use crate::eth::{decode_contract_call, get_function_input_data, signed_tx_from_web3_tx, wei_from_big_decimal, EthCoin, EthCoinType, ParseCoinAssocTypes, RefundFundingSecretArgs, RefundTakerPaymentArgs, SendTakerFundingArgs, SignedEthTx, SwapTxTypeWithSecretHash, TakerPaymentStateV2, TransactionErr, - ValidateSwapV2TxError, ValidateSwapV2TxResult, ValidateTakerFundingArgs, TAKER_SWAP_V2}; + ValidateSwapV2TxError, ValidateSwapV2TxResult, ValidateTakerFundingArgs, MAKER_SWAP_V2, TAKER_SWAP_V2}; use crate::{FindPaymentSpendError, FundingTxSpend, GenTakerFundingSpendArgs, GenTakerPaymentSpendArgs, MarketCoinOps, SearchForFundingSpendErr}; use common::executor::Timer; @@ -739,6 +739,45 @@ impl EthCoin { Ok((decoded, taker_swap_v2_contract)) } + + /// Extracts the maker's secret from the input of transaction that calls the `spendMakerPayment` smart contract method. + /// + /// function spendMakerPayment( + /// bytes32 id, + /// uint256 amount, + /// address maker, + /// bytes32 takerSecretHash, + /// bytes32 makerSecret, + /// address tokenAddress + /// ) + pub(crate) async fn extract_secret_v2_impl( + &self, + _secret_hash: &[u8], + spend_tx: &SignedEthTx, + ) -> Result, String> { + let function = try_s!(MAKER_SWAP_V2.function("spendMakerPayment")); + // should be 0x1299a27a + let expected_signature = function.short_signature(); + let signature = &spend_tx.unsigned().data()[0..4]; + if signature != expected_signature { + return ERR!( + "Expected 'spendMakerPayment' contract call signature: {:?}, found {:?}", + expected_signature, + signature + ); + }; + let decoded = try_s!(decode_contract_call(function, spend_tx.unsigned().data())); + if decoded.len() < 5 { + return ERR!("Invalid arguments in 'spendMakerPayment' call: {:?}", decoded); + } + match &decoded[4] { + Token::FixedBytes(secret) => Ok(secret.to_vec()), + _ => ERR!( + "Expected secret to be fixed bytes, but decoded function data is {:?}", + decoded + ), + } + } } /// Validation function for ETH taker payment data diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 7d11dbdb522..3380f7cdde3 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -1969,6 +1969,8 @@ pub trait TakerCoinSwapOpsV2: ParseCoinAssocTypes + CommonSwapOpsV2 + Send + Syn from_block: u64, wait_until: u64, ) -> MmResult; + + async fn extract_secret_v2(&self, secret_hash: &[u8], spend_tx: &Self::Tx) -> Result, String>; } #[async_trait] diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index 22a64685b98..38816cb130c 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -565,6 +565,10 @@ impl TakerCoinSwapOpsV2 for TestCoin { ) -> MmResult { unimplemented!() } + + async fn extract_secret_v2(&self, secret_hash: &[u8], spend_tx: &Self::Tx) -> Result, String> { + unimplemented!() + } } impl CommonSwapOpsV2 for TestCoin { diff --git a/mm2src/coins/utxo/utxo_common.rs b/mm2src/coins/utxo/utxo_common.rs index 175454e1da3..aeccd4d537f 100644 --- a/mm2src/coins/utxo/utxo_common.rs +++ b/mm2src/coins/utxo/utxo_common.rs @@ -2605,6 +2605,30 @@ pub fn extract_secret(secret_hash: &[u8], spend_tx: &[u8]) -> Result, St ERR!("Couldn't extract secret") } +/// Extract a secret from the `spend_tx`. +/// Note spender could generate the spend with several inputs where the only one input is the p2sh script. +pub fn extract_secret_v2(secret_hash: &[u8], spend_tx: &UtxoTx) -> Result, String> { + let expected_secret_hash = if secret_hash.len() == 32 { + ripemd160(secret_hash) + } else { + H160::from(secret_hash) + }; + for input in spend_tx.inputs.iter() { + let script: Script = input.script_sig.clone().into(); + for instruction in script.iter().flatten() { + if instruction.opcode == Opcode::OP_PUSHBYTES_32 { + if let Some(secret) = instruction.data { + let actual_secret_hash = dhash160(secret); + if actual_secret_hash == expected_secret_hash { + return Ok(secret.to_vec()); + } + } + } + } + } + ERR!("Couldn't extract secret") +} + pub fn my_address(coin: &T) -> MmResult { match coin.as_ref().derivation_method { DerivationMethod::SingleAddress(ref my_address) => { diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index 3dd7cc4077d..cd6f66d2dfe 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -883,6 +883,10 @@ impl TakerCoinSwapOpsV2 for UtxoStandardCoin { .await?; Ok(res) } + + async fn extract_secret_v2(&self, secret_hash: &[u8], spend_tx: &Self::Tx) -> Result, String> { + utxo_common::extract_secret_v2(secret_hash, spend_tx) + } } impl CommonSwapOpsV2 for UtxoStandardCoin { 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 55d90e9dfa0..3ea6261b0ec 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs @@ -2087,14 +2087,9 @@ impl, state_machine: &mut Self::StateMachine) -> StateResult { let unique_data = state_machine.unique_data(); - // TODO: impl extract_secret_v2 as we cant reuse legacy method for Eth which uses v2 smart contracts let secret = match state_machine .taker_coin - .extract_secret( - &self.negotiation_data.maker_secret_hash, - &self.taker_payment_spend.tx_hex(), - false, - ) + .extract_secret_v2(&self.negotiation_data.maker_secret_hash, &self.taker_payment_spend) .await { Ok(s) => s,