Skip to content

fix(p2pk): validate expected pubkey correctly for p2pk inputs#2408

Merged
shamardy merged 32 commits intodevfrom
p2pk-spends-in-swaps
May 28, 2025
Merged

fix(p2pk): validate expected pubkey correctly for p2pk inputs#2408
shamardy merged 32 commits intodevfrom
p2pk-spends-in-swaps

Conversation

@mariocynicys
Copy link
Collaborator

For p2pk inputs, the pubkey isn't included in the scriptSig. The scriptSig only includes the signature and that's it.

This means we can't just read the pubkey from the scriptSig and deduce that such pubkey is the owner of the utxo (and then compare it with the expected pubkey, taker or maker). But rather we need to verify the signature in the scriptSig using the expected pubkey.

ref. https://learnmeabitcoin.com/technical/script/p2pk/
Fixes #2339

and integrate it in check_all_utxo_inputs_signed_by_pub
@mariocynicys mariocynicys added status: in progress priority: urgent Critical tasks requiring immediate action. bug: swap labels Apr 7, 2025
specifically, don't require coin. this makes it easier to test

also add a fixme regarding pubkey size in the scriptpub, it might be:
- OP_PUSHBYTES_33 33-byte-compressed-pubkey OP_CHECKSIG
- OP_PUSHBYTES_65 65-byte-full-pubkey OP_CHECKSIG
and change the parameters of the func for now to allow for uncompressed pubkeys since i can't find any compressed pubkey transactions online that i can use for testing
also add a fixme for a bug found with this impl for overwintered
transactions: those transactions need to have the amount field set,
which generally isn't set because it is fetched from UtxoTx object which
doesn't have the amount set as well.
found in scriptPub. i.e. we will assume the public key in scriptpub was compressed and try to verify the signature, if we fail, we will assume it's uncompressed and try to verify the signature again. if either of these two operations succeed, verification succeeds, if both fail, verification fails.
@mariocynicys mariocynicys force-pushed the p2pk-spends-in-swaps branch from 95d86db to d1b2e4e Compare April 9, 2025 07:29
@mariocynicys
Copy link
Collaborator Author

force pushed to move out unrelated commits away to #2410

@shamardy shamardy requested a review from dimxy April 14, 2025 23:03
Comment on lines +2193 to +2194
// FIXME: For overwintered txs, we also need to set the input amount as it's used in sighash calcuations!
unsigned_tx.consensus_branch_id = coin.as_ref().conf.consensus_branch_id;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

to fix this, we need to query for the amount of this input. so one rpc request per p2pk input.

i think the efficient solution for this is to disable this check all together for p2pk spends on overwintered txs.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think you may just query the previous scriptPubKey for p2pk inputs.
This would be a one for all solution as you would not need to check the signature but just retrieve the pubkey from the previous scriptPubKey

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

that's the same communication overhead, but i think is a neater solution.

we wouldn't wanna perform it though if the transaction/coin isn't overwintered, because we already have all the info needed to verify the taker is the owner of the utxos without the need for a network request.

Copy link
Collaborator

Choose a reason for hiding this comment

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

we wouldn't wanna perform it though if the transaction/coin isn't overwintered, because we already have all the info needed to verify the taker is the owner of the utxos without the need for a network request.

I prefer to not use a network request when it's not needed.

Copy link

@borngraced borngraced left a comment

Choose a reason for hiding this comment

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

great work! I have some notes. Generally, I noticed you don't really use line breaks. Please do

Comment on lines +2192 to +2194
let mut unsigned_tx: TransactionInputSigner = tx.clone().into();
// FIXME: For overwintered txs, we also need to set the input amount as it's used in sighash calcuations!
unsigned_tx.consensus_branch_id = coin.as_ref().conf.consensus_branch_id;

Choose a reason for hiding this comment

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

Would it also be okay to modify the original tx's consensus_branch_id

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this field doesn't get set when we get the TransactionInputSigner from a plain Transaction (check https://github.com/KomodoPlatform/komodo-defi-framework/blob/a97afb467eaf24fe6530f3f7341d7173a3777c41/mm2src/mm2_bitcoin/script/src/sign.rs#L177).

it must be this one of the coin anyways and matches with the one used by the taker. otherwise all the transaction signing will break and not verify.

i will might remove such mutation anyway as consensus_branch_id is only used with overwintered txs, and we already have a problem with them with them: namely, the amount must be set for signing to produce correct signature.

Choose a reason for hiding this comment

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

if we can we should for sure.

&pubkey_script,
// FIXME: But P2PK scripts never use segwit as signature version. Should we hardcode this to SignatureVersion::Base or ::ForkId?
// UPD: I think we can safely remove this fixme if the following reasoning holds:
// Since we are using p2pk here, this means the coin isn't segwit. right? but it's the coin supplied from the caller anyways
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think, KDF does not mix segwit and non segwit inputs but another app could.
(here we actually check txns created by a remote app)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

upon digging down, looks like we list p2pk utxos for segwit address:
https://github.com/KomodoPlatform/komodo-defi-framework/blob/b161cd44aff8ea5f140018c2589ee90d00952454/mm2src/coins/utxo/rpc_clients/electrum_rpc/client.rs#L805-L809

we should check address.address_format.is_segwit() and not add p2pk outputs if so. i.e. we should associate p2pk outputs only with coins enabled in non-segwit mode, i.e. coins enabled for p2pkh outputs.

another alternative is to change signature_version passed to signature_hash_to_sign for each input based on its utxo type (p.s. actually this will look inconsistent; raising the question why is a coin labeled as segwit but it can spend other address types)

Copy link
Collaborator

Choose a reason for hiding this comment

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

we should check address.address_format.is_segwit() and not add p2pk outputs

maybe just do not allow to add a pubkey to segwit addr

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

715f504 done here

@shamardy
Copy link
Collaborator

@mariocynicys please merge with latest dev for wasm tests job to pass.

…e expected pubkey

this simplifies things a lot as no verification is now needed. the down side is that we now query the rpc for each and every p2pk input and not just overwintered ones. this is prolly OK tho since p2pk inputs aren't that popular anyways.

ofc we assume the rpc server isn't lying
@mariocynicys
Copy link
Collaborator Author

@dimxy @shamardy

the latest commit applies this suggestion #2408 (comment)

the one before applies the amount fetching + sig verification suggestion.

im fine with either one of them (or removing the check for overwintered p2pk all together also).

…ainst the expected pubkey"

This reverts commit 8fdd7d3.

we will stick with only performing the network request when needed (so
only for overwintered)
as there is no need for the verbose transaction
that's either a coinbase tx or an invalid tx with no inputs (most probably the latter). if it's invalid, we don't really care about that within the scope of this function
since it won't be needed most of the time as it's only used when an input is p2pk
onur-ozkan
onur-ozkan previously approved these changes May 27, 2025
Copy link

@onur-ozkan onur-ozkan left a comment

Choose a reason for hiding this comment

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

LGTM, thanks!

@shamardy shamardy merged commit 82144f6 into dev May 28, 2025
24 checks passed
@shamardy shamardy deleted the p2pk-spends-in-swaps branch May 28, 2025 08:51
dimxy pushed a commit that referenced this pull request May 28, 2025
* dev: (29 commits)
  fix(p2pk): validate expected pubkey correctly for p2pk inputs (#2408)
  chore(docs): update old urls referencing atomicdex or old docs pages (#2428)
  improvement(p2p): remove hardcoded seeds (#2439)
  fix(evm-api): find enabled erc20 token using platform ticker (#2445)
  chore(docs): add DeepWiki badge to README (#2463)
  chore(core): organize deps using workspace.dependencies (#2449)
  feat(db-arch): more dbdir to address_dir replacements (#2398)
  chore(build-artifacts): remove duplicated mm2 build artifacts (#2448)
  feat(pubkey-banning): expirable bans (#2455)
  fix(eth-balance-events): serialize eth address using AddrToString (#2440)
  chore(deps): remove base58 and replace it completely with bs58 (#2427)
  feat(tron): initial groundwork for full TRON integration (#2425)
  fix(UTXO): improve tx fee calculation and min relay fee handling (#2316)
  deps(timed-map): bump to 1.3.1 (#2413)
  improvement(tendermint): safer IBC channel handler (#2298)
  chore(release): complete v2.4.0-beta changelogs  (#2436)
  fix(event-streaming): initial addresses registration in utxo balance streaming (#2431)
  improvement(watchers): re-write use-watchers handling (#2430)
  fix(evm): make withdraw_nft work in HD mode (#2424)
  feat(taproot): support parsing taproot output address types
  ...
dimxy pushed a commit to dimxy/komodo-defi-framework that referenced this pull request Jun 8, 2025
* lr-swap-wip: (37 commits)
  fix custom token error name
  fix getting chain_id from protocol_data
  refactor (review): use dedicated large error cfg, add new fn to FromApiValueError, fix TODO, use experimental namespace for lr rpc, more Ticker alias
  feat(walletconnect): add WalletConnect v2 support for EVM and Cosmos (GLEECBTC#2223)
  feat(ibc-routing-part-1): supporting entire Cosmos network for swaps (GLEECBTC#2459)
  fix(test): fix HD Wallet message signing tests (GLEECBTC#2474)
  improvement(builds): enable static CRT linking for MSVC builds (GLEECBTC#2464)
  feat(wallet): implement HD multi-address support for message signing (GLEECBTC#2432)
  fix(p2pk): validate expected pubkey correctly for p2pk inputs (GLEECBTC#2408)
  chore(docs): update old urls referencing atomicdex or old docs pages (GLEECBTC#2428)
  improvement(p2p): remove hardcoded seeds (GLEECBTC#2439)
  fix(evm-api): find enabled erc20 token using platform ticker (GLEECBTC#2445)
  chore(docs): add DeepWiki badge to README (GLEECBTC#2463)
  chore(core): organize deps using workspace.dependencies (GLEECBTC#2449)
  feat(db-arch): more dbdir to address_dir replacements (GLEECBTC#2398)
  chore(build-artifacts): remove duplicated mm2 build artifacts (GLEECBTC#2448)
  feat(pubkey-banning): expirable bans (GLEECBTC#2455)
  fix(eth-balance-events): serialize eth address using AddrToString (GLEECBTC#2440)
  chore(deps): remove base58 and replace it completely with bs58 (GLEECBTC#2427)
  feat(tron): initial groundwork for full TRON integration (GLEECBTC#2425)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug: swap priority: urgent Critical tasks requiring immediate action. status: pending review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants