From 6f0d1a6dd56b43b17b505c59bb01b6c4a56b0bfc 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 | 40 +++++++++++++++++++ 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(+), 6 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..7612d746698 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 @@ -739,6 +739,46 @@ impl EthCoin { Ok((decoded, taker_swap_v2_contract)) } + + /// Extracts the maker's secret from the input of transaction that calls the `spendTakerPayment` smart contract method. + /// + /// function spendTakerPayment( + /// bytes32 id, + /// uint256 amount, + /// uint256 dexFee, + /// address taker, + /// 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!(TAKER_SWAP_V2.function("spendTakerPayment")); + // should be 0xcc90c199 + let expected_signature = function.short_signature(); + let signature = &spend_tx.unsigned().data()[0..4]; + if signature != expected_signature { + return ERR!( + "Expected 'spendTakerPayment' contract call signature: {:?}, found {:?}", + expected_signature, + signature + ); + }; + let decoded = try_s!(decode_contract_call(function, spend_tx.unsigned().data())); + if decoded.len() < 7 { + return ERR!("Invalid arguments in 'spendTakerPayment' call: {:?}", decoded); + } + match &decoded[5] { + 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,