Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
4e61580
add a p2pk signature verifier
mariocynicys Apr 7, 2025
15f867f
simplify the parameters of verify_p2pk_input_pubkey
mariocynicys Apr 7, 2025
d638bc9
add test for p2pk detection function
mariocynicys Apr 7, 2025
6f24451
add a test for verify_p2pk_input_pubkey
mariocynicys Apr 7, 2025
a89e101
add a test for verify_p2pk_input_pubkey (33-byte pubkeys)
mariocynicys Apr 8, 2025
1f0e0f3
add more details to the signature version fixme
mariocynicys Apr 8, 2025
d127ade
fix compilation error
mariocynicys Apr 8, 2025
2ce3c97
add to_secp256k1_pubkey() to Public
mariocynicys Apr 8, 2025
d1b2e4e
adjust verify_p2pk_input_pubkey() to account for both pubkey formats
mariocynicys Apr 8, 2025
a97afb4
simplify does_script_spend_p2pk
mariocynicys Apr 9, 2025
8a9923a
review(sami): resolve a couple of comments - readability, DRY, cleanness
mariocynicys Apr 16, 2025
ba82af5
import the correct Deref
mariocynicys Apr 16, 2025
b262954
review(dimxy): fix typo in 'signature'
mariocynicys Apr 17, 2025
3f6ce9b
review(dimxy): utilize the match for the successfulness check
mariocynicys Apr 17, 2025
b161cd4
review(dimxy): add a doc comment for `check_all_utxo_inputs_signed_by…
mariocynicys Apr 17, 2025
715f504
don't list (and thus spend) p2pk outputs for segwit coins
mariocynicys Apr 21, 2025
3e0bb9a
Merge remote-tracking branch 'origin/dev' into p2pk-spends-in-swaps
mariocynicys Apr 24, 2025
bdb3d05
fix utxo inputs sig check doc comment
mariocynicys May 20, 2025
f435492
be more idomatic with matches
mariocynicys May 20, 2025
f33ff36
Merge remote-tracking branch 'origin/dev' into p2pk-spends-in-swaps
mariocynicys May 21, 2025
04dfbf4
get the amount from rpc for overwintered txs
mariocynicys May 21, 2025
75eabfa
add a test for overwintered txs p2pk input verification
mariocynicys May 21, 2025
8fdd7d3
review(dimxy): just query for the prev_output and check it against th…
mariocynicys May 21, 2025
86b0949
Revert "review(dimxy): just query for the prev_output and check it ag…
mariocynicys May 23, 2025
95411c0
use get_transaction_bytes instead of get_versbose_transaction
mariocynicys May 23, 2025
72d6754
review(onur): inline a small frequently used method
mariocynicys May 27, 2025
cffe6c0
review(onur): reorder if conditions for readability - make the p2pk f…
mariocynicys May 27, 2025
440f1e7
review(onur): move scriptsig utitlity method to impl Script
mariocynicys May 27, 2025
0a84be1
review(onur): separate prev tx fetching and amount setting logic in p…
mariocynicys May 27, 2025
a2c8f8f
review(onur): add a pre-check to exit early if the inputs are empty
mariocynicys May 27, 2025
77aa1b1
lazily calculate `unsigned_tx` only when needed
mariocynicys May 27, 2025
e3d1edb
Revert "review(onur): add a pre-check to exit early if the inputs are…
mariocynicys May 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mm2src/coins/qrc20.rs
Original file line number Diff line number Diff line change
Expand Up @@ -843,7 +843,7 @@ impl SwapOps for Qrc20Coin {
};
let fee_tx_hash = fee_tx.hash().reversed().into();
let inputs_signed_by_pub =
check_all_utxo_inputs_signed_by_pub(self, fee_tx, validate_fee_args.expected_sender)?;
check_all_utxo_inputs_signed_by_pub(self, fee_tx, validate_fee_args.expected_sender).await?;
if !inputs_signed_by_pub {
return MmError::err(ValidatePaymentError::WrongPaymentTx(
"The dex fee was sent from wrong address".to_string(),
Expand Down
54 changes: 39 additions & 15 deletions mm2src/coins/utxo/utxo_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2162,27 +2162,48 @@ where
/// It's used to verify that all the inputs of the taker-sent dex fee are signed/owned by the taker's pubkey.
/// It's used also by watcher to verify that all the taker payment inputs are signed/owned by the taker's pubkey.
/// The `expected_pub` should be the taker's pubkey in compressed (33-byte) format.
pub fn check_all_utxo_inputs_signed_by_pub<T: UtxoCommonOps>(
pub async fn check_all_utxo_inputs_signed_by_pub<T: UtxoCommonOps>(
coin: &T,
tx: &UtxoTx,
expected_pub: &[u8],
) -> Result<bool, MmError<ValidatePaymentError>> {
let expected_pub =
H264::from_slice(expected_pub).map_to_mm(|e| ValidatePaymentError::TxDeserializationError(e.to_string()))?;
let mut unsigned_tx: TransactionInputSigner = tx.clone().into();
unsigned_tx.consensus_branch_id = coin.as_ref().conf.consensus_branch_id;

for (idx, input) in tx.inputs.iter().enumerate() {
let script = Script::from(input.script_sig.clone());
let pubkey = if input.has_witness() {
// Extract the pubkey from a P2WPKH scriptSig.
pubkey_from_witness_script(&input.script_witness).map_to_mm(ValidatePaymentError::TxDeserializationError)?
} else if does_script_spend_p2pk(&script) {
// For P2PK scriptsSigs, verfiy that the signature corresponds to the expected public key.
// If the transaction is overwintered, we need to set the consensus branch id and the input's amount.
// This is needed for the sighash calculation.
if unsigned_tx.overwintered {
let prev_output_tx_hash = input.previous_output.hash.reversed().into();
let prev_output_index = input.previous_output.index as usize;
let prev_tx = coin
.as_ref()
.rpc_client
.get_verbose_transaction(&prev_output_tx_hash)
.compat()
.await
.map_err(|e| ValidatePaymentError::TxDeserializationError(format!("Failed to get prev tx: {e}")))?;
let prev_tx: UtxoTx = deserialize(prev_tx.hex.0.as_slice())?;
let prev_output = prev_tx.outputs.get(prev_output_index).ok_or_else(|| {
ValidatePaymentError::TxDeserializationError(format!(
"Prev tx output index {} out of bounds for tx {}",
input.previous_output.index,
prev_tx.hash()
))
})?;
unsigned_tx.inputs[idx].amount = prev_output.value;
unsigned_tx.consensus_branch_id = coin.as_ref().conf.consensus_branch_id;
}
// Verfiy that the P2PK input's scriptSig corresponds to the expected public key.
let successful_verification = verify_p2pk_input_pubkey(
&script,
&Public::Compressed(expected_pub),
// FIXME: For overwintered txs, we also need to set the input amount as it's used in sighash calcuations!
&unsigned_tx,
idx,
coin.as_ref().conf.signature_version,
Expand Down Expand Up @@ -2247,7 +2268,8 @@ pub fn watcher_validate_taker_fee<T: UtxoCommonOps + SwapOps>(
};

let taker_fee_tx: UtxoTx = deserialize(tx_from_rpc.hex.0.as_slice())?;
let inputs_signed_by_pub = check_all_utxo_inputs_signed_by_pub(&coin, &taker_fee_tx, &sender_pubkey)?;
let inputs_signed_by_pub =
check_all_utxo_inputs_signed_by_pub(&coin, &taker_fee_tx, &sender_pubkey).await?;
if !inputs_signed_by_pub {
return MmError::err(ValidatePaymentError::WrongPaymentTx(format!(
"{}: Taker fee does not belong to the verified public key",
Expand Down Expand Up @@ -2379,17 +2401,18 @@ pub fn validate_fee<T: UtxoCommonOps + SwapOps>(
) -> ValidatePaymentFut<()> {
let dex_address = try_f!(dex_address(&coin).map_to_mm(ValidatePaymentError::InternalError));
let burn_address = try_f!(burn_address(&coin).map_to_mm(ValidatePaymentError::InternalError));
let inputs_signed_by_pub = try_f!(check_all_utxo_inputs_signed_by_pub(&coin, &tx, sender_pubkey));
if !inputs_signed_by_pub {
return Box::new(futures01::future::err(
ValidatePaymentError::WrongPaymentTx(format!(
"{INVALID_SENDER_ERR_LOG}: Taker payment does not belong to the verified public key"
))
.into(),
));
}

let sender_pubkey = sender_pubkey.to_vec();
let fut = async move {
match check_all_utxo_inputs_signed_by_pub(&coin, &tx, &sender_pubkey).await {
Ok(true) => {},
Ok(false) => {
return MmError::err(ValidatePaymentError::WrongPaymentTx(format!(
"{INVALID_SENDER_ERR_LOG}: Taker payment does not belong to the verified public key"
)))
},
Err(e) => return Err(e),
};
let tx_from_rpc = coin
.as_ref()
.rpc_client
Expand Down Expand Up @@ -2495,7 +2518,8 @@ pub fn watcher_validate_taker_payment<T: UtxoCommonOps + SwapOps>(
let coin = coin.clone();

let fut = async move {
let inputs_signed_by_pub = check_all_utxo_inputs_signed_by_pub(&coin, &taker_payment_tx, &input.taker_pub)?;
let inputs_signed_by_pub =
check_all_utxo_inputs_signed_by_pub(&coin, &taker_payment_tx, &input.taker_pub).await?;
if !inputs_signed_by_pub {
return MmError::err(ValidatePaymentError::WrongPaymentTx(format!(
"{INVALID_SENDER_ERR_LOG}: Taker payment does not belong to the verified public key"
Expand Down