-
Notifications
You must be signed in to change notification settings - Fork 36.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix cases of calls to FillPSBT
errantly returning complete=true
#30357
Fix cases of calls to FillPSBT
errantly returning complete=true
#30357
Conversation
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. Code CoverageFor detailed information about the code coverage, see the test coverage report. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
concept ACK Confirmed the bug on master, fixed by the patch in this PR. I'm not sure responding to the user-provided Lines 2226 to 2230 in fe70be5
...because as the issue demonstrates, the In other words, I think |
efa8138
to
b6cea03
Compare
walletprocesspsbt
FillPSBT
errantly returning complete=true
Fix cases of calls to `FillPSBT` returning `complete=true` when it's not the case. This can happen when some inputs have been signed but the transaction is subsequently modified, e.g. in the context of PayJoins. Also fixes a related bug where a finalized hex string is attempted to be added during `walletprocesspsbt` but a CHECK_NONFATAL causes an abort. Reported in bitcoin#30077.
b6cea03
to
f57f68b
Compare
Rebased to fix CI |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK f57f68b
Reviewed code and confirmed test fails without patch. Approach is great, one question below about how much farther to take this approach...
Show Signature
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
ACK f57f68ba95fc8778fe2dc755da1e631fe60592c4
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEE5hdzzW4BBA4vG9eM5+KYS2KJyToFAmaECVgACgkQ5+KYS2KJ
yTq5vw//QNiwjThHf65qtM+57G6YDPWQ3zVu2s278T0aZU5ND62Hu4A9/DHqDBSQ
36A4klRON1ddIfFX91tE3GrwT4PZ51vK3uhX0xJYkZ66xkhObVZz2lCifMs+A2ng
ldEoEBT86d17fyuQLle8/VNolqXqWx9Dw5RZY3WclnO0Hex2l9xiWepel4sFzAM8
P4YUgsw6ZaEbRUjh67tZ3kLlmz5l5yBNV5NaO7rbsPeBiIqizRbGl7CQlhlxgM4g
ZF4PGUd9rk0sSnWWMHUcVA+Uskuw64bDH0keJBLUYeKpNLfRUZ5KTRSJilurayY5
M2B7veORZ4MWjGHjycbpV+hdpjpQzX5z1cyCxGqYN4gC8P7kpFVBrB6sMUvOLoku
lGTC+SKE+sxjRmgYcE1QpPMz7uYs8KTgkTHpzlbyj61oEL1SylXDtOlc7nRN2I0A
XEnV8kK/2VKF3V45dTRPUwFm+2qX1NnCEyJ53x2srg9+U3px9UxfUMAuwTmZYyOV
E/9XCDouqE63CqCzqM683lmdL+sCsfA5IHi9sqzVRP2cY8s1NOkj7QdXQO0Z8V07
ycsGpj8nI8GAq/b647aYOU9O+Ni2PGq2wdkpP8ciD0Iafp7S9MkpCjHx9J+p4YPE
TQVElxmGYgx5zIPFrdrBehZCJGX/YDqj8Zd9Ra0QYwVzYg9aqow=
=KJef
-----END PGP SIGNATURE-----
pinheadmz's public key is on keybase
signed_psbt_incomplete = wallet.walletprocesspsbt(signed_psbt_obj.to_base64(), finalize=False) | ||
assert signed_psbt_incomplete["complete"] is False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes me wonder if finalize=True
should result in "complete": True
. It's another case of using PSBTInputSigned()
instead of PSBTInputSignedAndVerified()
. We could update this code below to automatically replace / update invalid signatures.
Lines 2181 to 2194 in d2c8d16
std::optional<PSBTError> CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& complete, int sighash_type, bool sign, bool bip32derivs, size_t * n_signed, bool finalize) const | |
{ | |
if (n_signed) { | |
*n_signed = 0; | |
} | |
LOCK(cs_wallet); | |
// Get all of the previous transactions | |
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { | |
const CTxIn& txin = psbtx.tx->vin[i]; | |
PSBTInput& input = psbtx.inputs.at(i); | |
if (PSBTInputSigned(input)) { | |
continue; | |
} |
test/functional/rpc_psbt.py
Outdated
signed_psbt_obj = PSBT.from_base64(signed_psbt) | ||
substitute_addr = wallet.getnewaddress(address_type="bech32m") | ||
raw = wallet.createrawtransaction([{"txid": utxos[0]["txid"], "vout": utxos[0]["vout"]}], [{substitute_addr: 0.9999}]) | ||
signed_psbt_obj.g.map[PSBT_GLOBAL_UNSIGNED_TX] = bytes.fromhex(raw) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There might be a simpler way to malleate the PSBT. You could at least skip the getnewaddress
call and just hard-code something, or change one of the output amounts. Maybe only if you retouch.
Thanks @willcl-ark for picking this up! Confirmed this fixes the CHECK_NONFATAL abort when a witness signature is invalid. tACK f57f68b |
test/functional/rpc_psbt.py
Outdated
self.log.info("Check that PSBT is correctly marked as incomplete after invalid modification") | ||
node = self.nodes[2] | ||
wallet = node.get_wallet_rpc(self.default_wallet_name) | ||
address = wallet.getnewaddress(address_type="bech32m") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 7e36dca
This test checks that we can successfully process PSBTs and opt out of finalization. Previously trying to call `walletprocesspsbt` would attempt to auto-finalize (as a convenience), and would not permit opt-out of finalization, instead aborting via `CHECK_NONFATAL`.
f57f68b
to
7e36dca
Compare
utxos = wallet.listunspent(addresses=[address]) | ||
psbt = wallet.createpsbt([{"txid": utxos[0]["txid"], "vout": utxos[0]["vout"]}], [{wallet.getnewaddress(): 0.9999}]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nano nit:
utxos = wallet.listunspent(addresses=[address]) | |
psbt = wallet.createpsbt([{"txid": utxos[0]["txid"], "vout": utxos[0]["vout"]}], [{wallet.getnewaddress(): 0.9999}]) | |
psbt = wallet.createpsbt(utxos, [{wallet.getnewaddress(): 0.9999}]) |
signed_psbt_obj = PSBT.from_base64(signed_psbt) | ||
substitute_addr = wallet.getnewaddress() | ||
raw = wallet.createrawtransaction([{"txid": utxos[0]["txid"], "vout": utxos[0]["vout"]}], [{substitute_addr: 0.9999}]) | ||
signed_psbt_obj.g.map[PSBT_GLOBAL_UNSIGNED_TX] = bytes.fromhex(raw) | ||
|
||
# Check that the walletprocesspsbt call succeeds but also recognizes that the transaction is not complete | ||
signed_psbt_incomplete = wallet.walletprocesspsbt(signed_psbt_obj.to_base64(), finalize=False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems that the PSBT 'outputs' field retains the previous output derivation path after replacement. walletprocesspsbt
appends the new information without removing/updating the previous one. See furszy@698e090.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @furszy, this indeed feels unclean to me.
I made a few changes in another test branch and I wonder if they make sense to you: master...willcl-ark:bitcoin:walletprocesspsbt-no-finalize-fixups
I took inspiration from furszy@698e090 (thanks) but it seems that I can probably just clear the entire output map as done in L94 and then update the PSBT_GLOBAL_UNSIGNED_TX
in the global map before calling walletprocesspsbt
again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made a few changes in another test branch and I wonder if they make sense to you: master...willcl-ark:bitcoin:walletprocesspsbt-no-finalize-fixups
The question here is whether walletprocesspsbt should update the existing PSBT 'outputs' information (similar to how it updates the transaction inputs) or should only append data to it as it is currently doing. In the current test form, the first entry of the PSBT 'outputs' field ends up with two BIP32 derivation paths that cannot coexist: they share the same master key fingerprint and are under the same derivation path: the first one is m/.../15 while the other one is m/.../16.
Clearing the map on the test side hides this topic under the carpet (which is also a possibility if you want to leave it for a follow-up too).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of our PSBT updating operations should never remove any data from the PSBT. If the user decides to do something that invalidates a field in the PSBT, it's their duty to fix it themselves.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concept ACK
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
signed_psbt_obj.g.map[PSBT_GLOBAL_UNSIGNED_TX] = bytes.fromhex(raw) | ||
|
||
# Check that the walletprocesspsbt call succeeds but also recognizes that the transaction is not complete | ||
signed_psbt_incomplete = wallet.walletprocesspsbt(signed_psbt_obj.to_base64(), finalize=False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why pass finalize=false
? the test still passes without it.
|
||
# Check that the walletprocesspsbt call succeeds but also recognizes that the transaction is not complete | ||
signed_psbt_incomplete = wallet.walletprocesspsbt(signed_psbt_obj.to_base64(), finalize=False) | ||
assert signed_psbt_incomplete["complete"] is False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit
assert signed_psbt_incomplete["complete"] is False | |
assert_equal(signed_psbt_incomplete["complete"], False) | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re-ACK 7e36dca
Changes since last ACK are minimal, slight simplification in tests to enable more legacy compatability
Show Signature
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
ACK 7e36dca657c66bc70b04d5b850e5a335aecfb902
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEE5hdzzW4BBA4vG9eM5+KYS2KJyToFAmaRgqoACgkQ5+KYS2KJ
yTq5NA/+NL1fOaCfl+P9yDIsXebxam/8Adf6iH5WUBt2TvUju06+305KL1dwvO6O
n0j8wmAeaJQe9tevuSTFYopxRaH+7uVQmrMDxKucURgBZ5I/VdBWk8v3+CPUsnKQ
v/hyt4RPMsL12rcti936UqKi77nfRc6E+7zsDOtRU0Rf6ME222lCaWUpqAjm4vPm
3nN18eXw6Az9rKHwG3KFzXxMy/bthlI6bsRtvbUqtFc6PkZ7xR7f79EauawCkJWS
vCr4RXVcrC+1FMB8MHMcbLiNTmNjGApg48PAYnaoJsolpGQoRaHRNwKeRo2w0Uvw
tk3uXeVNfwxRbYwzBI6pZ/JYTSa3u31qsVGqx3mVVxm8y1F/sA+fwsRB+AqToDaj
Kzf4P8FLkyYT1t83i1n5yRKawHrQTVcsMVhxsN/+4nqBbvdGba6E5SpXG3fBJ3sI
L/fJocldQmO9maY+I9CgjJqmM9GauMvhjSq+g0PnWE9pRUnt1n0bFy7DwdonzRFK
bXtFx2xcWxtrHSAvLZt6oxTXTqwfh5XiFMHwG93ULySpzOJTHB+lTED1TKf4gwhk
QcaVW4Oi0lQ8QE6rtq+o0nx0FFz+lA9RoT2MFWQU2L+HFGqokwI9PQTVWxzm9Vjd
ZWOF04PI0QvCz0DITFHV0HTX1YsyQobWWWXRxwttqy4GDUi0PPw=
=DAZD
-----END PGP SIGNATURE-----
pinheadmz's public key is on keybase
ACK 7e36dca |
Fix cases of calls to `FillPSBT` returning `complete=true` when it's not the case. This can happen when some inputs have been signed but the transaction is subsequently modified, e.g. in the context of PayJoins. Also fixes a related bug where a finalized hex string is attempted to be added during `walletprocesspsbt` but a CHECK_NONFATAL causes an abort. Reported in bitcoin#30077. Github-Pull: bitcoin#30357 Rebased-From: 39cea21
This test checks that we can successfully process PSBTs and opt out of finalization. Previously trying to call `walletprocesspsbt` would attempt to auto-finalize (as a convenience), and would not permit opt-out of finalization, instead aborting via `CHECK_NONFATAL`. Github-Pull: bitcoin#30357 Rebased-From: 7e36dca
Fix cases of calls to `FillPSBT` returning `complete=true` when it's not the case. This can happen when some inputs have been signed but the transaction is subsequently modified, e.g. in the context of PayJoins. Also fixes a related bug where a finalized hex string is attempted to be added during `walletprocesspsbt` but a CHECK_NONFATAL causes an abort. Reported in bitcoin#30077. Github-Pull: bitcoin#30357 Rebased-From: 39cea21
This test checks that we can successfully process PSBTs and opt out of finalization. Previously trying to call `walletprocesspsbt` would attempt to auto-finalize (as a convenience), and would not permit opt-out of finalization, instead aborting via `CHECK_NONFATAL`. Github-Pull: bitcoin#30357 Rebased-From: 7e36dca
Backported to 27.x in #30467. |
4f23c86 [WIP] doc: update release notes for 27.x (fanquake) 54bb9b0 test: add test for modififed walletprocesspsbt calls (willcl-ark) f22b9ca wallet: fix FillPSBT errantly showing as complete (willcl-ark) 05192ba init: change shutdown order of load block thread and scheduler (Martin Zumsande) ab42206 Reapply "test: p2p: check that connecting to ourself leads to disconnect" (Sebastian Falbesoner) 064f214 net: prevent sending messages in `NetEventsInterface::InitializeNode` (Sebastian Falbesoner) 0933cf5 net: fix race condition in self-connect detection (Sebastian Falbesoner) fa90989 psbt: Check non witness utxo outpoint early (Ava Chow) Pull request description: Backports: * #29855 * #30357 * #30394 (modified test commit) * #30435 ACKs for top commit: stickies-v: ACK 4f23c86 willcl-ark: ACK 4f23c86 Tree-SHA512: 5c26445f0855f9d14890369ce19873b0686804eeb659e7d6da36a6f404f64d019436e1e6471579eaa60a96ebf8f64311883b4aef9d0ed528a95bd610c101c079
Fix cases of calls to `FillPSBT` returning `complete=true` when it's not the case. This can happen when some inputs have been signed but the transaction is subsequently modified, e.g. in the context of PayJoins. Also fixes a related bug where a finalized hex string is attempted to be added during `walletprocesspsbt` but a CHECK_NONFATAL causes an abort. Reported in bitcoin#30077. Github-Pull: bitcoin#30357 Rebased-From: 39cea21
This test checks that we can successfully process PSBTs and opt out of finalization. Previously trying to call `walletprocesspsbt` would attempt to auto-finalize (as a convenience), and would not permit opt-out of finalization, instead aborting via `CHECK_NONFATAL`. Github-Pull: bitcoin#30357 Rebased-From: 7e36dca
Fixes: #30077
Fix cases of calls to
FillPSBT
returningcomplete=true
when it's notthe case.
This can happen when some inputs have been signed but the transaction is
subsequently modified, e.g. in the context of PayJoins.
Also fixes a related bug where a finalized hex string is attempted to be
added during
walletprocesspsbt
but a CHECK_NONFATAL causes an abort.