-
Notifications
You must be signed in to change notification settings - Fork 37.7k
rpc: fundrawtransaction and walletcreatefundedpsbt also lock manually selected coins #18244
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
Concept ACK! This made me think that we should fail funding if some coin is already locked? |
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.
Add a release note maybe? Clients unaware of this change can have dangling utxos locked.
0af7e9f
to
ccdae9d
Compare
Added a release note.
Maybe in a separate PR. I also think it's safer for users to explicitly unlock coins before using them. |
The unspent locking was done to deal with concurrent clients and so a client shouldn't take the decision to unlock a coin so lightly - it might screw with a concurrent funding. This is a possible use case and for this reason the user should be able to lock all or nothing IMO.
|
Not sure what you mean. This PR can potentially lock a coin that's already locked, but why is that a problem? This PR does not ignore new things. |
The problem is that you might screw with a concurrent funding. When you lock a coin you should expect that it won't be used by others. |
If a concurrent wallet consumer uses manual coin selection, I don't think this PR makes their problem worse; we were already ignoring any lock they had. If it uses automatic automatic coin selection then this PR has no impact. |
ccdae9d
to
2ad4155
Compare
After IRC discussion I changed this PR to honour locks for manually selected coins. |
unspent_0 = self.nodes[1].listunspent()[0] | ||
self.nodes[1].lockunspent(False, [unspent_0]) | ||
tx = self.nodes[1].createrawtransaction([unspent_0], { self.nodes[1].getnewaddress() : 1 }) | ||
tx = self.nodes[1].fundrawtransaction(tx)['hex'] |
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.
Are you removing this case because it's used elsewhere?
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.
The original test was for auto-unlocking a coin, which now produces an error, so I check for that.
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsNo conflicts as of last run. |
2ad4155
to
991a38c
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 991a38c.
# fundrawtransaction can lock an input | ||
self.nodes[1].lockunspent(True, [unspent_0]) | ||
assert_equal(len(self.nodes[1].listlockunspent()), 0) | ||
tx = self.nodes[1].fundrawtransaction(tx,{"lockUnspents": True})['hex'] |
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, add space if you happen to push again.
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.
utACK 991a38c
991a38c
to
6433bf4
Compare
Rebased |
6433bf4
to
0c5b867
Compare
0c5b867
to
650437a
Compare
650437a
to
74992fd
Compare
ACK 74992fd |
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 made me think that we should fail funding if some coin is already locked?
After reading past discussions and some more careful analysis I now think this is not correct.
With the current approach, a client C1 must unlock the unspents to manually use them. But right after lockunspent true ...
some other client C2 can fund/send and and take C1 unspents, and C1 will error.
So I think the "is locked" check should be used only for automatic coin selection, not for those explicitly set.
This means that:
a) the client handles concurrency elsewhere and doesn't use locked unspents
b) the client locks unspents beforehand and then use them by funding transactions with those as inputs - and also sets lockUnspents
to grab the new ones
c) the user makes consecutive fundings with lockUnspents
set.
In other words, we can assume that the client that funds a transaction with locked inputs is the one that locked them in the first place.
The c) case above is relevant - with the current approach it wouldn't be possible because between each funding iteration all inputs would have to be unlocked.
Lastly, the option lockUnspents
when true
should always lock all inputs - the caller will "own" all inputs (existing inputs whether locked or not and the new ones). This was the original change.
Sorry @Sjors for the misdirection 😞
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.
Almost-ACK, review/tested, modulo I need to think about #18244 (review).
I'll await the above discussion. Will address nits if that leads to a change. |
…ed coins Previously only automatically selected coins were locked when lockUnspents is set. It now also locks selected coins.
74992fd
to
6d1f513
Compare
Based on the above discussion I changed this PR again, it now does the following:
I updated the description to clarify the discussion a bit. |
Thanks @Sjors will review 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.
Code review ACK 6d1f513
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.
Just want to point out that it's impossible to prevent 2 clients spend the same manually specified inputs.
One way to fix this is to make each client explicitly lock those inputs, and only proceed if the lock was granted.
@@ -2047,6 +2047,7 @@ static UniValue lockunspent(const JSONRPCRequest& request) | |||
"Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n" | |||
"If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n" | |||
"A locked transaction output will not be chosen by automatic coin selection, when spending bitcoins.\n" | |||
"Manually selected coins are automatically unlocked.\n" |
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 is incorrect, or am I missing something? I mean, all spent coins are unlocked right?
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.
Yes, but that's not the point. Automatically selection avoids locks, manually selection causes an unlock. This remark protects a user who assumes locked coins can't be accidentally spend by manually picking them. (it's like the difference between a Japanese toilet with shoes in front of it indicating it's occupied, and a Dutch toilets that's locked like a vault because people just pull the door open to find out)
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 think "Locks on manually selected coins are ignored." would be a more intuitive description of what happens but I understand this to mean the same thing.
Client-specific locks sounds fun, but not in this PR :-) |
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 6d1f513
if (lockUnspents) { | ||
LockCoin(txin.prevout); | ||
} | ||
|
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.
mirco-nit if you retouch: empty line seems misplaced
@@ -2047,6 +2047,7 @@ static UniValue lockunspent(const JSONRPCRequest& request) | |||
"Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n" | |||
"If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n" | |||
"A locked transaction output will not be chosen by automatic coin selection, when spending bitcoins.\n" | |||
"Manually selected coins are automatically unlocked.\n" |
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 think "Locks on manually selected coins are ignored." would be a more intuitive description of what happens but I understand this to mean the same thing.
…bt also lock manually selected coins 6d1f513 [rpc] fundrawtransaction, walletcreatefundedpsbt lock manually selected coins (Sjors Provoost) Pull request description: When using `fundrawtransaction` and `walletcreatefundedpsbt` with `lockUnspents`, it would only lock automatically selected coins, not manually selected coins. That doesn't make much sense to me if the goal is to prevent accidentally double-spending yourself before you broadcast a transaction. Note that when creating a transaction, manually selected coins are automatic "unlocked" (or more accurately: the lock is ignored). Earlier versions of this PR introduced an error when a locked coin is manually selected, but this idea was abandoned after some discussion. An application that uses this RPC should either rely on automatic coin selection (with `lockUnspents`) or handle lock concurrency itself with manual coin selection. In particular it needs to make sure to avoid/pause calls with automatic coin selection between calling `lockunspent` and the subsequent spending RPC. See bitcoin#7518 for historical background. ACKs for top commit: meshcollider: Code review ACK 6d1f513 fjahr: Code review ACK 6d1f513 Tree-SHA512: 8773c788d92f2656952e1beac147ba9956b8c5132d474e0880e4c89ff53642928b4cbfcd1cb3d17798b9284f02618a8830c93a9f7a4733e5bded96adff1d5d4d
92326d8 [rpc] add send method (Sjors Provoost) 2c2a144 [rpc] add snake case aliases for transaction methods (Sjors Provoost) 1bc8d0f [rpc] walletcreatefundedpsbt: allow inputs to be null (Sjors Provoost) Pull request description: `walletcreatefundedpsbt` has some interesting features that `sendtoaddress` and `sendmany` don't have: * manual coin selection * outputting a PSBT (it was controversial to add this, see #18201) * create a transaction without adding to wallet (which leads to broadcasting, unless `-walletbroadcast=0`) At the same time `walletcreatefundedpsbt` can't broadcast a transaction, which is inconvenient for simple use cases. This PR introduces a new `send` RPC method which creates a PSBT, signs it if possible and adds it to the wallet by default. If it can't sign all inputs, it outputs a PSBT. If `add_to_wallet` is set to `false` it will return the transaction in both PSBT and hex format. Because it uses a PSBT internally, it will much easier to add hardware wallet support to this method (see #16546). For `bitcoin-cli` users, it tries to keep the simplest use case easy to use: ```sh bitcoin-cli -regtest send '{"ADDRESS": 0.1}' 1 sat/b ``` This paves the way for deprecating `sendtoaddress` and `sendmany` though there's no rush. The only missing feature compared to these older methods is adding labels to a destination address. Depends on: - [x] #16377 (`[rpc] don't automatically append inputs in walletcreatefundedpsbt`) - [x] #11413 (`[wallet] [rpc] sendtoaddress/sendmany: Add explicit feerate option`) - [x] #18244 (`[rpc] have lockUnspents also lock manually selected coins`) ACKs for top commit: meshcollider: Light re-utACK 92326d8 achow101: ACK 92326d8 Reviewed code and test, ran tests. kallewoof: utACK 92326d8 Tree-SHA512: 7552ef1b193d4c06e381c44932fdb0d54f64383e4c7d6b988f49d059c7d4bba45ce6aa7813e03df86360ad9dad6f3010eb76ee7da480551742d5fd98c2251c0f
92326d8 [rpc] add send method (Sjors Provoost) 2c2a144 [rpc] add snake case aliases for transaction methods (Sjors Provoost) 1bc8d0f [rpc] walletcreatefundedpsbt: allow inputs to be null (Sjors Provoost) Pull request description: `walletcreatefundedpsbt` has some interesting features that `sendtoaddress` and `sendmany` don't have: * manual coin selection * outputting a PSBT (it was controversial to add this, see bitcoin#18201) * create a transaction without adding to wallet (which leads to broadcasting, unless `-walletbroadcast=0`) At the same time `walletcreatefundedpsbt` can't broadcast a transaction, which is inconvenient for simple use cases. This PR introduces a new `send` RPC method which creates a PSBT, signs it if possible and adds it to the wallet by default. If it can't sign all inputs, it outputs a PSBT. If `add_to_wallet` is set to `false` it will return the transaction in both PSBT and hex format. Because it uses a PSBT internally, it will much easier to add hardware wallet support to this method (see bitcoin#16546). For `bitcoin-cli` users, it tries to keep the simplest use case easy to use: ```sh bitcoin-cli -regtest send '{"ADDRESS": 0.1}' 1 sat/b ``` This paves the way for deprecating `sendtoaddress` and `sendmany` though there's no rush. The only missing feature compared to these older methods is adding labels to a destination address. Depends on: - [x] bitcoin#16377 (`[rpc] don't automatically append inputs in walletcreatefundedpsbt`) - [x] bitcoin#11413 (`[wallet] [rpc] sendtoaddress/sendmany: Add explicit feerate option`) - [x] bitcoin#18244 (`[rpc] have lockUnspents also lock manually selected coins`) ACKs for top commit: meshcollider: Light re-utACK 92326d8 achow101: ACK 92326d8 Reviewed code and test, ran tests. kallewoof: utACK 92326d8 Tree-SHA512: 7552ef1b193d4c06e381c44932fdb0d54f64383e4c7d6b988f49d059c7d4bba45ce6aa7813e03df86360ad9dad6f3010eb76ee7da480551742d5fd98c2251c0f
…ed coins Summary: Previously only automatically selected coins were locked when lockUnspents is set. It now also locks selected coins. This is a backport of [[bitcoin/bitcoin#18244 | core#18244]] Test Plan: `ninja all check-all` Reviewers: #bitcoin_abc, Fabien Reviewed By: #bitcoin_abc, Fabien Differential Revision: https://reviews.bitcoinabc.org/D10144
When using
fundrawtransaction
andwalletcreatefundedpsbt
withlockUnspents
, it would only lock automatically selected coins, not manually selected coins. That doesn't make much sense to me if the goal is to prevent accidentally double-spending yourself before you broadcast a transaction.Note that when creating a transaction, manually selected coins are automatic "unlocked" (or more accurately: the lock is ignored). Earlier versions of this PR introduced an error when a locked coin is manually selected, but this idea was abandoned after some discussion. An application that uses this RPC should either rely on automatic coin selection (with
lockUnspents
) or handle lock concurrency itself with manual coin selection. In particular it needs to make sure to avoid/pause calls with automatic coin selection between callinglockunspent
and the subsequent spending RPC.See #7518 for historical background.