Skip to content
Open
189 changes: 123 additions & 66 deletions mm2src/coins/utxo/utxo_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -916,7 +916,7 @@ pub enum P2SHSigner {
}

impl P2SHSigner {
fn try_from_coin<Coin>(coin: &Coin, swap_unique_data: &[u8]) -> Result<Self, String>
pub fn try_from_coin<Coin>(coin: &Coin, swap_unique_data: &[u8]) -> Result<Self, String>
where
Coin: UtxoCommonOps + SwapOps,
{
Expand Down Expand Up @@ -1047,6 +1047,7 @@ pub async fn p2sh_spending_tx<T: UtxoCommonOps>(coin: &T, input: P2SHSpendingTxI
input.prev_transaction,
input.redeem_script,
input.script_data.into(),
SIGHASH_ALL,
)
.await
.map_err(|e| format!("WalletConnect P2SH signing error: {e}")),
Expand Down Expand Up @@ -1114,7 +1115,7 @@ async fn gen_taker_funding_spend_preimage<T: UtxoCommonOps>(
pub async fn gen_and_sign_taker_funding_spend_preimage<T: UtxoCommonOps>(
coin: &T,
args: &GenTakerFundingSpendArgs<'_, T>,
htlc_keypair: &KeyPair,
p2sh_signer: &P2SHSigner,
) -> GenPreimageResult<T> {
let funding_time_lock = args
.funding_time_lock
Expand All @@ -1130,16 +1131,30 @@ pub async fn gen_and_sign_taker_funding_spend_preimage<T: UtxoCommonOps>(
args.taker_pub,
args.maker_pub,
);
let signature = calc_and_sign_sighash(
&preimage,
DEFAULT_SWAP_VOUT,
&redeem_script,
htlc_keypair,
coin.as_ref().conf.signature_version,
SIGHASH_ALL,
coin.as_ref().conf.fork_id,
)
.map_mm_err()?;

let signature = match p2sh_signer {
P2SHSigner::KeyPair(htlc_keypair) => calc_and_sign_sighash(
&preimage,
DEFAULT_SWAP_VOUT,
&redeem_script,
htlc_keypair,
coin.as_ref().conf.signature_version,
SIGHASH_ALL,
coin.as_ref().conf.fork_id,
)
.map_mm_err()?,
P2SHSigner::WalletConnect(session_topic) => wallet_connect::sign_p2sh_get_sig_only(
coin,
session_topic,
&preimage,
args.funding_tx.clone(),
redeem_script.into(),
SIGHASH_ALL,
)
.await
.mm_err(|e| TxGenError::Signing(format!("WalletConnect P2SH signing error: {e}")))?,
};

Ok(TxPreimageWithSig {
preimage: preimage.into(),
signature: signature.take().into(),
Expand Down Expand Up @@ -1236,7 +1251,7 @@ pub async fn sign_and_send_taker_funding_spend<T: UtxoCommonOps>(
coin: &T,
preimage: &TxPreimageWithSig<T>,
gen_args: &GenTakerFundingSpendArgs<'_, T>,
htlc_keypair: &KeyPair,
p2sh_signer: &P2SHSigner,
) -> Result<UtxoTx, TransactionErr> {
let redeem_script = swap_proto_v2_scripts::taker_funding_script(
try_tx_s!(gen_args.funding_time_lock.try_into()),
Expand All @@ -1251,15 +1266,29 @@ pub async fn sign_and_send_taker_funding_spend<T: UtxoCommonOps>(
payment_input.amount = funding_output.value;
signer.consensus_branch_id = coin.as_ref().conf.consensus_branch_id;

let taker_signature = try_tx_s!(calc_and_sign_sighash(
&signer,
DEFAULT_SWAP_VOUT,
&redeem_script,
htlc_keypair,
coin.as_ref().conf.signature_version,
SIGHASH_ALL,
coin.as_ref().conf.fork_id
));
let taker_signature = match p2sh_signer {
P2SHSigner::KeyPair(htlc_keypair) => try_tx_s!(calc_and_sign_sighash(
&signer,
DEFAULT_SWAP_VOUT,
&redeem_script,
htlc_keypair,
coin.as_ref().conf.signature_version,
SIGHASH_ALL,
coin.as_ref().conf.fork_id
)),
P2SHSigner::WalletConnect(session_topic) => try_tx_s!(
wallet_connect::sign_p2sh_get_sig_only(
coin,
session_topic,
&signer,
gen_args.funding_tx.clone(),
redeem_script.clone().into(),
SIGHASH_ALL,
)
.await
),
};

let sig_hash_all_fork_id = (SIGHASH_ALL | coin.as_ref().conf.fork_id) as u8;

let mut maker_signature_with_sighash = preimage.signature.to_vec();
Expand Down Expand Up @@ -1398,7 +1427,7 @@ async fn gen_taker_payment_spend_preimage<T: UtxoCommonOps + SwapOps>(
pub async fn gen_and_sign_taker_payment_spend_preimage<T: UtxoCommonOps + SwapOps>(
coin: &T,
args: &GenTakerPaymentSpendArgs<'_, T>,
htlc_keypair: &KeyPair,
p2sh_signer: &P2SHSigner,
) -> GenPreimageResult<T> {
let time_lock = args
.time_lock
Expand All @@ -1415,16 +1444,29 @@ pub async fn gen_and_sign_taker_payment_spend_preimage<T: UtxoCommonOps + SwapOp
DexFee::WithBurn { .. } | DexFee::NoFee => SIGHASH_ALL,
};

let signature = calc_and_sign_sighash(
&preimage,
DEFAULT_SWAP_VOUT,
&redeem_script,
htlc_keypair,
coin.as_ref().conf.signature_version,
sig_hash_type,
coin.as_ref().conf.fork_id,
)
.map_mm_err()?;
let signature = match p2sh_signer {
P2SHSigner::KeyPair(htlc_keypair) => calc_and_sign_sighash(
&preimage,
DEFAULT_SWAP_VOUT,
&redeem_script,
htlc_keypair,
coin.as_ref().conf.signature_version,
sig_hash_type,
coin.as_ref().conf.fork_id,
)
.map_mm_err()?,
P2SHSigner::WalletConnect(session_topic) => wallet_connect::sign_p2sh_get_sig_only(
coin,
session_topic,
&preimage,
args.taker_tx.clone(),
redeem_script.into(),
sig_hash_type,
)
.await
.mm_err(|e| TxGenError::Signing(format!("WalletConnect P2SH signing error: {e}")))?,
};

Ok(TxPreimageWithSig {
preimage: preimage.into(),
signature: signature.take().into(),
Expand Down Expand Up @@ -1494,14 +1536,14 @@ pub async fn sign_and_broadcast_taker_payment_spend<T: UtxoCommonOps>(
preimage: &TxPreimageWithSig<T>,
gen_args: &GenTakerPaymentSpendArgs<'_, T>,
secret: &[u8],
htlc_keypair: &KeyPair,
p2sh_signer: &P2SHSigner,
) -> Result<UtxoTx, TransactionErr> {
let secret_hash = dhash160(secret);
let redeem_script = swap_proto_v2_scripts::taker_payment_script(
try_tx_s!(gen_args.time_lock.try_into()),
secret_hash.as_slice(),
gen_args.taker_pub,
htlc_keypair.public(),
gen_args.maker_pub,
Comment on lines 1540 to +1546
Copy link

Copilot AI Sep 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line appears to be a bug fix - changing from htlc_keypair.public() to gen_args.maker_pub. This should be verified as it changes the public key being used in the script generation.

Copilot uses AI. Check for mistakes.
);

let mut signer: TransactionInputSigner = preimage.preimage.clone().into();
Expand Down Expand Up @@ -1533,15 +1575,29 @@ pub async fn sign_and_broadcast_taker_payment_spend<T: UtxoCommonOps>(
}
drop_mutability!(signer);

let maker_signature = try_tx_s!(calc_and_sign_sighash(
&signer,
DEFAULT_SWAP_VOUT,
&redeem_script,
htlc_keypair,
coin.as_ref().conf.signature_version,
SIGHASH_ALL,
coin.as_ref().conf.fork_id
));
let maker_signature = match p2sh_signer {
P2SHSigner::KeyPair(htlc_keypair) => try_tx_s!(calc_and_sign_sighash(
&signer,
DEFAULT_SWAP_VOUT,
&redeem_script,
htlc_keypair,
coin.as_ref().conf.signature_version,
SIGHASH_ALL,
coin.as_ref().conf.fork_id
)),
P2SHSigner::WalletConnect(session_topic) => try_tx_s!(
wallet_connect::sign_p2sh_get_sig_only(
coin,
session_topic,
&signer,
gen_args.taker_tx.clone(),
redeem_script.clone().into(),
SIGHASH_ALL,
)
.await
),
};

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,
Expand Down Expand Up @@ -1766,8 +1822,7 @@ pub async fn send_maker_spends_taker_payment<T: UtxoCommonOps + SwapOps>(
script_pubkey,
};

let signer = P2SHSigner::try_from_coin(&coin, args.swap_unique_data)
.map_err(|e| TransactionErr::Plain(ERRL!("Failed to create P2SHSigner: {}", e)))?;
let signer = try_tx_s!(P2SHSigner::try_from_coin(&coin, args.swap_unique_data));

let input = P2SHSpendingTxInput {
prev_transaction,
Expand Down Expand Up @@ -1852,17 +1907,19 @@ pub fn create_maker_payment_spend_preimage<T: UtxoCommonOps + SwapOps>(
drop_mutability!(prev_transaction);
let payment_value = try_tx_fus!(prev_transaction.first_output()).value;

let key_pair = coin.derive_htlc_key_pair(swap_unique_data);
let taker_pub = coin.derive_htlc_pubkey(swap_unique_data);

let script_data = Builder::default().into_script();
let redeem_script = payment_script(
time_lock,
secret_hash,
&try_tx_fus!(Public::from_slice(maker_pub)),
key_pair.public(),
&try_tx_fus!(Public::from_slice(&taker_pub)),
)
.into();

let coin = coin.clone();
let swap_unique_data = swap_unique_data.to_vec();
let fut = async move {
let my_address = try_tx_s!(coin.as_ref().derivation_method.single_addr_or_err().await);
let fee = try_tx_s!(
Expand Down Expand Up @@ -1890,7 +1947,7 @@ pub fn create_maker_payment_spend_preimage<T: UtxoCommonOps + SwapOps>(
script_data,
sequence: SEQUENCE_FINAL,
lock_time: time_lock,
signer: P2SHSigner::KeyPair(key_pair),
signer: try_tx_s!(P2SHSigner::try_from_coin(&coin, &swap_unique_data)),
};
let transaction = try_tx_s!(coin.p2sh_spending_tx(input).await);

Expand All @@ -1907,7 +1964,6 @@ pub fn create_taker_payment_refund_preimage<T: UtxoCommonOps + SwapOps>(
secret_hash: &[u8],
swap_unique_data: &[u8],
) -> TransactionFut {
let coin = coin.clone();
let mut prev_transaction: UtxoTx =
try_tx_fus!(deserialize(taker_payment_tx).map_err(|e| TransactionErr::Plain(format!(
"Failed to deserialize transaction (coin={}, hex={}) : {}",
Expand All @@ -1919,15 +1975,18 @@ pub fn create_taker_payment_refund_preimage<T: UtxoCommonOps + SwapOps>(
drop_mutability!(prev_transaction);
let payment_value = try_tx_fus!(prev_transaction.first_output()).value;

let key_pair = coin.derive_htlc_key_pair(swap_unique_data);
let taker_pub = coin.derive_htlc_pubkey(swap_unique_data);
let script_data = Builder::default().push_opcode(Opcode::OP_1).into_script();
let redeem_script = payment_script(
time_lock,
secret_hash,
key_pair.public(),
&try_tx_fus!(Public::from_slice(&taker_pub)),
&try_tx_fus!(Public::from_slice(maker_pub)),
)
.into();

let coin = coin.clone();
let swap_unique_data = swap_unique_data.to_vec();
let fut = async move {
let my_address = try_tx_s!(coin.as_ref().derivation_method.single_addr_or_err().await);
let fee = try_tx_s!(
Expand All @@ -1954,7 +2013,7 @@ pub fn create_taker_payment_refund_preimage<T: UtxoCommonOps + SwapOps>(
script_data,
sequence: SEQUENCE_FINAL - 1,
lock_time: time_lock,
signer: P2SHSigner::KeyPair(key_pair),
signer: try_tx_s!(P2SHSigner::try_from_coin(&coin, &swap_unique_data)),
};
let transaction = try_tx_s!(coin.p2sh_spending_tx(input).await);

Expand Down Expand Up @@ -2011,8 +2070,7 @@ pub async fn send_taker_spends_maker_payment<T: UtxoCommonOps + SwapOps>(
script_pubkey,
};

let signer = P2SHSigner::try_from_coin(&coin, args.swap_unique_data)
.map_err(|e| TransactionErr::Plain(ERRL!("Failed to create P2SHSigner: {}", e)))?;
let signer = try_tx_s!(P2SHSigner::try_from_coin(&coin, args.swap_unique_data));

let input = P2SHSpendingTxInput {
prev_transaction,
Expand Down Expand Up @@ -2073,8 +2131,7 @@ pub async fn refund_htlc_payment<T: UtxoCommonOps + SwapOps>(
script_pubkey,
};

let signer = P2SHSigner::try_from_coin(&coin, args.swap_unique_data)
.map_err(|e| TransactionErr::Plain(ERRL!("Failed to create P2SHSigner: {}", e)))?;
let signer = try_tx_s!(P2SHSigner::try_from_coin(&coin, args.swap_unique_data));

let input = P2SHSpendingTxInput {
prev_transaction,
Expand Down Expand Up @@ -5385,7 +5442,7 @@ where
let my_address = try_tx_s!(coin.as_ref().derivation_method.single_addr_or_err().await).clone();
let payment_value = try_tx_s!(args.funding_tx.first_output()).value;

let key_pair = coin.derive_htlc_key_pair(args.swap_unique_data);
let taker_pubkey = coin.derive_htlc_pubkey(args.swap_unique_data);
let script_data = Builder::default()
.push_data(args.taker_secret)
.push_opcode(Opcode::OP_0)
Expand All @@ -5396,7 +5453,7 @@ where
let redeem_script = swap_proto_v2_scripts::taker_funding_script(
time_lock,
args.taker_secret_hash,
key_pair.public(),
&try_tx_s!(Public::from_slice(&taker_pubkey)),
args.maker_pubkey,
)
.into();
Expand Down Expand Up @@ -5424,7 +5481,7 @@ where
script_data,
sequence: SEQUENCE_FINAL,
lock_time: time_lock,
signer: P2SHSigner::KeyPair(key_pair),
signer: try_tx_s!(P2SHSigner::try_from_coin(&coin, args.swap_unique_data)),
};
let transaction = try_tx_s!(coin.p2sh_spending_tx(input).await);

Expand Down Expand Up @@ -5569,7 +5626,7 @@ pub async fn spend_maker_payment_v2<T: UtxoCommonOps + SwapOps>(
let my_address = try_tx_s!(coin.as_ref().derivation_method.single_addr_or_err().await).clone();
let payment_value = try_tx_s!(args.maker_payment_tx.first_output()).value;

let key_pair = coin.derive_htlc_key_pair(args.swap_unique_data);
let taker_pub = coin.derive_htlc_pubkey(args.swap_unique_data);
let script_data = Builder::default()
.push_data(&args.maker_secret)
.push_opcode(Opcode::OP_1)
Expand All @@ -5582,7 +5639,7 @@ pub async fn spend_maker_payment_v2<T: UtxoCommonOps + SwapOps>(
args.maker_secret_hash,
args.taker_secret_hash,
args.maker_pub,
key_pair.public(),
&try_tx_s!(Public::from_slice(&taker_pub)),
)
.into();

Expand Down Expand Up @@ -5610,7 +5667,7 @@ pub async fn spend_maker_payment_v2<T: UtxoCommonOps + SwapOps>(
script_data,
sequence: SEQUENCE_FINAL,
lock_time: time_lock,
signer: P2SHSigner::KeyPair(key_pair),
signer: try_tx_s!(P2SHSigner::try_from_coin(coin, args.swap_unique_data)),
};
let transaction = try_tx_s!(coin.p2sh_spending_tx(input).await);

Expand All @@ -5631,7 +5688,7 @@ where
let my_address = try_tx_s!(coin.as_ref().derivation_method.single_addr_or_err().await).clone();
let payment_value = try_tx_s!(args.maker_payment_tx.first_output()).value;

let key_pair = coin.derive_htlc_key_pair(args.swap_unique_data);
let maker_pub = coin.derive_htlc_pubkey(args.swap_unique_data);
let script_data = Builder::default()
.push_data(args.taker_secret)
.push_opcode(Opcode::OP_0)
Expand All @@ -5643,7 +5700,7 @@ where
time_lock,
args.maker_secret_hash,
args.taker_secret_hash,
key_pair.public(),
&try_tx_s!(Public::from_slice(&maker_pub)),
args.taker_pub,
)
.into();
Expand Down Expand Up @@ -5671,7 +5728,7 @@ where
script_data,
sequence: SEQUENCE_FINAL,
lock_time: time_lock,
signer: P2SHSigner::KeyPair(key_pair),
signer: try_tx_s!(P2SHSigner::try_from_coin(&coin, args.swap_unique_data)),
};
let transaction = try_tx_s!(coin.p2sh_spending_tx(input).await);

Expand Down
Loading
Loading