Skip to content

Commit

Permalink
provide extract_secret_v2 in TakerSwapOpsV2, implement EthCoin extrac…
Browse files Browse the repository at this point in the history
…t_secret_v2
  • Loading branch information
laruh committed Nov 4, 2024
1 parent b11f889 commit 6f0d1a6
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 6 deletions.
4 changes: 4 additions & 0 deletions mm2src/coins/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vec<u8>, String> {
self.extract_secret_v2_impl(secret_hash, spend_tx).await
}
}

impl CommonSwapOpsV2 for EthCoin {
Expand Down
40 changes: 40 additions & 0 deletions mm2src/coins/eth/eth_swap_v2/eth_taker_swap_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vec<u8>, 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
Expand Down
2 changes: 2 additions & 0 deletions mm2src/coins/lp_coins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1969,6 +1969,8 @@ pub trait TakerCoinSwapOpsV2: ParseCoinAssocTypes + CommonSwapOpsV2 + Send + Syn
from_block: u64,
wait_until: u64,
) -> MmResult<Self::Tx, FindPaymentSpendError>;

async fn extract_secret_v2(&self, secret_hash: &[u8], spend_tx: &Self::Tx) -> Result<Vec<u8>, String>;
}

#[async_trait]
Expand Down
4 changes: 4 additions & 0 deletions mm2src/coins/test_coin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,10 @@ impl TakerCoinSwapOpsV2 for TestCoin {
) -> MmResult<Self::Tx, FindPaymentSpendError> {
unimplemented!()
}

async fn extract_secret_v2(&self, secret_hash: &[u8], spend_tx: &Self::Tx) -> Result<Vec<u8>, String> {
unimplemented!()
}
}

impl CommonSwapOpsV2 for TestCoin {
Expand Down
24 changes: 24 additions & 0 deletions mm2src/coins/utxo/utxo_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2605,6 +2605,30 @@ pub fn extract_secret(secret_hash: &[u8], spend_tx: &[u8]) -> Result<Vec<u8>, 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<Vec<u8>, 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<T: UtxoCommonOps>(coin: &T) -> MmResult<String, MyAddressError> {
match coin.as_ref().derivation_method {
DerivationMethod::SingleAddress(ref my_address) => {
Expand Down
4 changes: 4 additions & 0 deletions mm2src/coins/utxo/utxo_standard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vec<u8>, String> {
utxo_common::extract_secret_v2(secret_hash, spend_tx)
}
}

impl CommonSwapOpsV2 for UtxoStandardCoin {
Expand Down
7 changes: 1 addition & 6 deletions mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2087,14 +2087,9 @@ impl<MakerCoin: MmCoin + MakerCoinSwapOpsV2, TakerCoin: MmCoin + TakerCoinSwapOp
async fn on_changed(self: Box<Self>, state_machine: &mut Self::StateMachine) -> StateResult<Self::StateMachine> {
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,
Expand Down

0 comments on commit 6f0d1a6

Please sign in to comment.