Skip to content

Conversation

S3RK
Copy link
Contributor

@S3RK S3RK commented Mar 23, 2022

Correctly differentiating between external vs non-external utxos in coin control produces more accurate weight and fee estimations.

Weight for external utxos is estimated based on the maximum signature size, while for the wallet utxos we expect minimal signature due to signature grinding.

@achow101
Copy link
Member

I'm not entirely sure that this is what we want to do. When I implemented that external input weight feature, I specifically decided to allow user provided weight to override what the wallet does. This is because if a user were using walletcreatefundedpsbt or fundrawtransaction, it is possible that they are going to use a different signer and are merely using the wallet as a watchonly wallet. We can't know how the other signer is going to behave. Furthermore, with taproot descriptors, and with miniscript coming up soon, the wallet does not necessarily know which script branch is going to be taken and may end up using the wrong script branch when estimating size. By allowing the user to override the size estimation, the user can effectively tell provide a more accurate size estimation depending on the branch taken.

Copy link

@uvhw uvhw left a comment

Choose a reason for hiding this comment

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

Copy link

@uvhw uvhw left a comment

Choose a reason for hiding this comment

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

@S3RK
Copy link
Contributor Author

S3RK commented Mar 24, 2022

I believe input_weights option always takes precedences and this PR doesn't change it. Rather it improves precision when input_weights option is not provided.

@achow101
Copy link
Member

Oh indeed. Looking at SelectCoins again, we only check to see if an output is external after we determine it is not in the wallet. I'm not sure that this PR actually has an effect. Can you provide a test case where the weights end up calculated differently before and after this PR?

@S3RK
Copy link
Contributor Author

S3RK commented Mar 29, 2022

Yes, but it doesn't do so in CWallet::DummySignTx, so CalculateMaximumSignedTxSize does produce less precise result, which might cause a lower change than needed. I'll see if I can create a test exposing this issue.

@S3RK
Copy link
Contributor Author

S3RK commented Mar 30, 2022

@achow101 added a test case

@DrahtBot
Copy link
Contributor

DrahtBot commented Mar 31, 2022

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #25291 (test: Refactor rpc_fundrawtransaction.py by akankshakashyap)
  • #25273 (wallet: Pass through transaction locktime and preset input sequences and scripts to CreateTransaction by achow101)
  • #25218 (refactor: introduce generic 'Result' classes and connect them to CreateTransaction and GetNewDestination by furszy)
  • #25118 (wallet: unify “allow/block other inputs“ concept by furszy)
  • #20892 (tests: Run both descriptor and legacy tests within a single test invocation by achow101)

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.

@achow101
Copy link
Member

achow101 commented Apr 1, 2022

ACK 3b83b8a

Thanks for the test case, now I see what the problem is.

@murchandamus
Copy link
Contributor

ACK 3b83b8a

@S3RK S3RK force-pushed the wallet_correct_external_utxo branch from 3b83b8a to 7832e94 Compare May 18, 2022 06:25
luke-jr pushed a commit to luke-jr/bitcoin that referenced this pull request May 21, 2022
luke-jr pushed a commit to luke-jr/bitcoin that referenced this pull request May 21, 2022
Copy link
Contributor

@murchandamus murchandamus left a comment

Choose a reason for hiding this comment

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

re-ACK 7832e94

wallet.chain().findCoins(coins);

for (const CTxIn& txin : tx.vin) {
// if it's not in the wallet and corresponding UTXO is found than select as external output
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit, if you have to rebase or touch something else:

Suggested change
// if it's not in the wallet and corresponding UTXO is found than select as external output
// if it's not in the wallet and corresponding UTXO is found, then select as external output

@achow101
Copy link
Member

achow101 commented Jun 3, 2022

re-ACK 7832e94

Copy link
Member

@furszy furszy left a comment

Choose a reason for hiding this comment

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

Code reviewed 7832e94

for (const CTxIn& txin : tx.vin) {
coins[txin.prevout]; // Create empty map entry keyed by prevout.
}
wallet.chain().findCoins(coins);
Copy link
Member

Choose a reason for hiding this comment

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

At this point, we have cs_wallet locked (lock is few lines above) and inside findCoins we lock cs_main

Wonder if it's a potential deadlock cause or not. Because, cs_main should be taking precedence over cs_wallet in other processes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agree it could be a problem in theory, but I'd like to understand the problem better before making a change.

Right now I couldn't find any places in our code that acquire cs_wallet while holding cs_main. To experiment I added LOCK(cs_main) to wallet/rpc/coins.cpp:197 and immediately this triggered our deadlock detection mechanism because it turns out that loadwallet rpc acquires lock in a different order (first cs_wallet and then cs_main).

What is our long-term direction with regards to using cs_main and cs_wallet together?
@achow101 any thoughts?

I'm also curious how we should think of these locks in light of multiprocess (cc @ryanofsky )

Copy link
Member

Choose a reason for hiding this comment

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

cs_main is really for things in node operation and so there should never be a scenario within the wallet where we lock cs_main before cs_wallet. That would imply that the node is doing something to the wallet, but the model we (want to) have is the wallet asks for things from the node and otherwise operates asynchronously from the node. So it should always be cs_wallet before cs_main.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@furszy Looks like the current lock order is fine. Is there anything left before you can ACK this?

Copy link
Member

Choose a reason for hiding this comment

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

Was thinking on moving the "fetch coins" block of code above the cs_wallet lock, so both mutexes are locked independently from each other.

But, at this point, it's merely a nit.

for (const CTxIn& txin : tx.vin) {
// if it's not in the wallet and corresponding UTXO is found than select as external output
const auto& outPoint = txin.prevout;
if (wallet.mapWallet.find(outPoint.hash) == wallet.mapWallet.end() && !coins[outPoint].out.IsNull()) {
Copy link
Member

Choose a reason for hiding this comment

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

nit: would be better to use wallet.GetWalletTx(hash) instead of accessing the wallet's map directly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will do if I have to retouch

Copy link
Member

@furszy furszy left a comment

Choose a reason for hiding this comment

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

ACK 7832e94

(Thanks for the reminder)

for (const CTxIn& txin : tx.vin) {
coins[txin.prevout]; // Create empty map entry keyed by prevout.
}
wallet.chain().findCoins(coins);
Copy link
Member

Choose a reason for hiding this comment

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

Was thinking on moving the "fetch coins" block of code above the cs_wallet lock, so both mutexes are locked independently from each other.

But, at this point, it's merely a nit.

@achow101 achow101 merged commit b0c8306 into bitcoin:master Jun 16, 2022
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Jun 17, 2022
@bitcoin bitcoin locked and limited conversation to collaborators Jun 16, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants