-
Notifications
You must be signed in to change notification settings - Fork 37.7k
wallet: Correctly identify external inputs that are also in the wallet #25679
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
Conversation
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. 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. |
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, good catch.
Discussion point about the implementation:
Wouldn't be better to replace 09a0dcb for furszy/bitcoin@0fe6524?
The rationale is the same as fd98ced:
If we have an external input that the wallet knows about, we will be able
to get the parent transaction but not be able to calculate the size nor solve it locally (needing the external provider).
So, instead of continue asking if the parent tx exist in the wallet to know whether
we own the output or not. We could use IsMine(outpoint)
directly.
Which saves us from doing the double CalculateMaximumSignedInputSize
calculation that 09a0dcb is introducing.
This was the original approach, but I decided it was insufficient. I can think of a case where it would fail, but the approach that I have implemented does not. Consider a watch-only wallet where an address has been imported (either legacy and An alternative is to use |
Yeah ok, I was also flirting a bit with the The only thing that still makes some noice on me is the first call to What about simplifying 09a0dcb by only moving the second
|
@furszy Implemented as suggested. |
aeb9542
to
65bb2fa
Compare
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.
Code review ACK 65bb2fa
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 65bb2fa
I cherry-picked the commit which adds the test onto the current master and the added test failed (as expected). I think that the IsMine function which takes an outpoint that was introduced in 9b4461d can be called in a few other places to allow for some simplification, but that doesn't really need to be done in this PR.
It is useful to have an IsMine function that can take an outpoint.
65bb2fa
to
4a33786
Compare
Rebased for a silent merge conflict in the test. |
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.
ut-reACK 65bb2fa
What was the cause of the silent merge conflict in the test? It seems like the test will pass both with the changes applied because of the conflict as well as without.
Edit: sorry, never mind.
If an external input's utxo was created by a transaction that the wallet knows about, then it would not be selected using SelectExternal. This results in either funding failure or incorrect weight calculation.
Instead of choosing whether to use the wallet or external data when estimating the size of an input, first use the wallet, then try external data if that failed.
4a33786
to
ef8e2a5
Compare
ACK ef8e2a5 |
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 ef8e2a5
With a note: had to re-review the test step by step once more because, at least for me, it was not so clear why we are testing this specific error inside test_weight_calculation
.
Probably a comment saying something like "let's now select the external vout as input of a new tx, then fund the tx using the wallet's coins plus provide the external descriptor so the process is able to calculate the size of the input that doesn't belong to the wallet" in the test would had been useful.
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.
reACK ef8e2a5
if (!coin_control.GetExternalOutput(outpoint, txout)) { | ||
return std::nullopt; |
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 tested this myself (by applying this change on top of this PR and running the functional tests) and it doesn't seem like GetExternalOutput()
will return false now because, if I'm understanding this correctly, then I think that we no longer Select()
utxos which we can't find in the wallet or the coins
map and end up throwing an error before we get to this point now instead. For this reason might it make sense for this to be an assertion instead?
if (!coin_control.GetExternalOutput(outpoint, txout)) { | |
return std::nullopt; | |
assert(coin_control.GetExternalOutput(outpoint, txout)); |
…are also in the wallet
ext_desc = self.nodes[0].getaddressinfo(ext_addr)["desc"] | ||
fundedtx = wallet.fundrawtransaction(rawtx, {'fee_rate': 10, "change_type": "bech32", "solving_data": {"descriptors": [ext_desc]}}) | ||
# tx overhead (10) + 3 inputs (41 each) + 2 p2wpkh(31 each) + (segwit marker and flag (2) + 2 p2wpkh 71 bytes sig witnesses (107 each) + p2wpkh 72 byte sig witness (108)) / witness scaling factor (4) | ||
tx_size = ceil(10 + 41*3 + 31*2 + (2 + 107*2 + 108)/4) |
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'm not sure this test have enough sensitivity to detect 72 vs 71 bytes sig.
ceil(10 + 41*3 + 31*2 + (2 + 107*2 + 108)/4) == ceil(10 + 41*3 + 31*2 + (2 + 107*2 + 107)/4)
Probably we need more than one external input or to actually detect larger sig size.
UPD: maybe using just two inputs would work
ceil(10 + 41*2 + 31*2 + (2 + 107 + 108)/4) = 209
but
ceil(10 + 41*2 + 31*2 + (2 + 107 + 107)/4) = 208
b2544d1 qt: Update translation source file for string freeze (Hennadii Stepanov) Pull request description: This PR updates the `src/qt/locale/bitcoin_en.xlf` translation source file according to [Release schedule for 24.0](bitcoin/bitcoin#24987). There were some new strings added since the [recent](#654) update: - "Unable to find UTXO for external input" in bitcoin/bitcoin#25679 - "Pre-syncing Headers (%1%)…" in bitcoin/bitcoin#25717 - "Unknown. Pre-syncing Headers (%1, %2%)…" in bitcoin/bitcoin#25717 ACKs for top commit: jarolrod: ACK b2544d1 Tree-SHA512: cc3785a3279b2fae8cd477cd3a5b07f5321b25e963f4e03429538a6d43a858f6636e118eccfa64c77dc6279624937ddb6b7dd5d52021193ed93392a98662f25a
if a transaction is being funded that has an external input, and that input's parent is also in the wallet, we will fail to detect that and fail to fund the transaction. In order to correctly detect such inputs, we need to be doing
IsMine
on all specified inputs in order to useSelect
andSelectExternal
correctly. AdditionallySelectCoins
needs to callCalculateMaximumSignedInputSize
with the correct parameters which depends on whether the wallet is able to solve for the input. Because there are some situations where the wallet could find an external input to belong to it (e.g. watching an address - unable to solve, but will be ISMINE_WATCHONLY), instead of switching whichCalculateMaximumSignedInputSize
to use, we should call the one that uses the wallet, and if that fails, try again with the one that uses external solving data.Also adds a test for this case.