wallet: "avoid_reuse" wallet flag for improved privacy#13756
wallet: "avoid_reuse" wallet flag for improved privacy#13756meshcollider merged 10 commits intobitcoin:masterfrom
Conversation
promag
left a comment
There was a problem hiding this comment.
Concept ACK.
Should we also disallow sending to a dirty address? If we don't then those coins can't be spend if the flag is set, or am I missing something?
|
Concept ACK Nice work! |
|
Perhaps fodder for a separate PR, but listtransactions and the GUI should get indicators that an incoming payment was dirty when received. By having that parties that reuse addresses will start getting a frowny-recycle-icon or whatever on their payments, which will increase awareness that their behaviour has downsides. Edit: I see I'm repeating myself from an earlier PR. :P |
9e31139 to
8cd0e8f
Compare
You can always spend them by using the I do think we should at least warn users about that kind of behaviour though, but it feels like a UI fix that can come later.
I will see about making that the case for the CLI side of things. I think a GUI side fix would be great, but unfortunately I don't seem to be talented at writing QT code. (Practice, I guess.)
Sorry about that. I felt like it was worthwhile to re-PR since the old PR has a lot of outdated talk that is mitigated by #12257. |
8cd0e8f to
389d97a
Compare
|
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. |
I think "dirty" is a confusing concept. Maybe add an exclamation mark (or a detective icon) next to the transaction and when the user clicks on that, say something like "This address has been used before, for privacy reasons it's better to create a new address each time you wish to receive coins, even from the same person". Detective icon is a nice hint that there's privacy related information, but an exclamation mark can also be used to explain other potential problems with an incoming transaction, e.g. if the fee is extremely low. A parallel measure, that doesn't involve UI changes, could be for coin selection to not spend from dirty addresses as long as possible. And then try to spend them if there's an exact match (no other outputs, no change), perhaps even only if there's no other exact match, depending on how strongly you want to avoid spending these things. When the user wants to "spend all" funds and the dirty amount is less than x%, a dialog could popup and suggest to "exclude a small amount for privacy reasons". This is the kind of behavior that if we want people to use it, should be on by default. That can wait for another PR, but it's useful to think about it a bit when designing the RPC. Telling people to manually go into coin selection is not a good idea. If a user receives a non-trivial amount, they expect to be able to just spend it with no additional hoops. That means in the current form it's probably never a user friendly default. |
@Sjors I like that, and then a tooltip/popup could explain the warning.
"Avoid" is not binary right? |
It's called "dirty" in the CLI as well, but there's also a different concept in the wallet code called 'dirty' which is completely unrelated. In short, it may be a good idea to rename this feature, but I can't think of a good name. "Compromised" is too long. "Seen" is too vague. @luke-jr any ideas?
I like "!" too. If it is used for multiple things, it could take away from the importance though (e.g. user ignores it thinking it's 'just cause of the 1 sat/b fee'). As a sidenote, we also should mark UTXOs in coin selection dialog somehow. Same approach? Maybe need to include a warning popup if they mix dirty and clean.
I don't know, I think we should always avoid dirty inputs unless the user explicitly marks them as 'clean' or picks them directly using manual coin control.
I didn't think about this case, and it's probably fairly common. Then again, I don't think I can do a lot from the CLI side, so this is probably GUI level stuff.
What about it is too aggressive?
Maybe it should be sensitive to the amount. A good middle ground may be to, for any input that is X times higher than current dust threshold, that goes to an already spent-from address, to show the user a dialog saying the input is dirty and ask them whether to force-mark it as clean or keep it as dirty. |
A user might receive their entire salary on a reused address. If the current implementation of This is where the threshold comes in, but I think it's non-trivial to figure out what the right threshold is, and it might be game-able. Perhaps the ! / detective icon in the transaction view could offer the user a choice to manually quarantine funds. Something like "If you did not expect this transaction someone may be spying on you, waiting for you to spend the coins. Would you like to quarantine them?" You could even quarantine funds by default based on some heuristic as long as the UI makes that very clear and it's easy for a user to unquarantine it (similar to firewall and anti-virus popups). But maybe try the opt-in quarantine approach first. So then there's Depending on what ends up happening, the documentation should perhaps point out that the GUI ignores this setting. Alternatively (for the current implementation) dirty coins could be hidden in the GUI, not shown in coin selection and not used when spending all, when this setting is enabled. Both options require a warning, and neither seems ideal, but updating the GUI in a more sophisticated way is pretty time consuming. |
|
@Sjors That makes sense to me. It seems like adding this default off (as is the current proposal) makes the most sense. GUI work seems like it will be a good deal of work to get right, but in the meantime, there are real (advanced) users who would benefit from having this feature now, even without the GUI/intuitive component. |
|
N.B. This doesn't actually fully solve #10065, since transactions received with a dirty address are still shown in the GUI / RPC. |
|
(as for a term... "reused" perhaps? I don't know that this flag should be exposed as-is, but more like a "will never confirm until spent" status) |
|
I think "reused" is a bit vague, and doesn't convey the fact it's considered a thing to be avoided if possible, in the way that "dirty" does. |
57fca7d to
e4df634
Compare
|
Note: I realized the test was invalid, as |
8452b59 to
9337fb3
Compare
|
It feels to me that this should be a persistent per-wallet setting, rather than a global config option (I think we should be eliminating the global wallet config options wherever possible. See #13044) |
createwallet, getbalance, getwalletinfo, listunspent, sendtoaddress rpc/wallet: listunspent include reused flag and show reused utxos by default
…enable for avoid_reuse wallets
|
Removed secondary parameter to |
|
ACK 5ebc6b0 (Reran the rebase from 66f3e97 myself and checked the diff) |
|
Concept and code-review ACK 5ebc6b0 Would be nice if this had final sign-off by @meshcollider as wallet maintainer before merge. Thanks for working on privacy! Hehe. If this use of "dirty addresses" would catch on, maybe people would start avoiding re-using addresses out of a sense of hygiene. One can dream, right. |
|
Since I don't know the wallet code all that well, it would take a lot of time to be able to properly review this, but concept ACK. |
|
@achow101 Might also want to take a final look? |
|
Code review ACK 5ebc6b0 Only lightly reviewed the test (8f2e208) but the actual code looks good, thank you @kallewoof for being so proactive in addressing review comments and rebasing! About time this finally gets merged. I'll wait a day for Andrew to review but otherwise I'll merge this tomorrow. |
| assert_raises_rpc_error(-8, "Wallet flag is already set to false", self.nodes[0].setwalletflag, 'avoid_reuse', False) | ||
| assert_raises_rpc_error(-8, "Wallet flag is already set to true", self.nodes[1].setwalletflag, 'avoid_reuse', True) | ||
|
|
||
| def test_immutable(self): |
There was a problem hiding this comment.
In addition to the above TODO, I also suggest this be moved to a different file later (its independent of the avoid-reuse tests)
There was a problem hiding this comment.
Makes sense to me.
| ISMINE_SPENDABLE = 1 << 1, | ||
| ISMINE_USED = 1 << 2, | ||
| ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE, | ||
| ISMINE_ALL_USED = ISMINE_ALL | ISMINE_USED, |
There was a problem hiding this comment.
Is it really necessary to add new ismine types? AFAICT, these are only used as ismine filters which I think could just as easily be done with a boolean. IsMine() isn't returning these values, and ISMINE_ALL_USED isn't used anywhere.
There was a problem hiding this comment.
You get most of the combinations with the new "used" flag, because a bunch of calls have a with and without "avoid reuse" state... so I think this is the cleanest approach.
ISMINE_ALL_USED is indeed not used; I added it to indicate that ISMINE_ALL is in fact not include the used ones. It's sort of a comment, with the added side effect that smart IDEs will show it when giving suggestions.
| return NullUniValue; | ||
| } | ||
|
|
||
| if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { |
There was a problem hiding this comment.
nit: Use RPCHelpMan help{...}. See https://github.com/bitcoin/bitcoin/blob/master/src/rpc/rawtransaction.cpp#L74 for an example.
There was a problem hiding this comment.
Will address if rebase becomes necessary, otherwise will try to preserve ACKs and do a quick follow-up post-merge.
There was a problem hiding this comment.
I can do this but it would mean some unnecessary calculations when users are not asking for help:
bitcoin/src/wallet/rpcwallet.cpp
Lines 2671 to 2674 in 44d8172
Still worth it?
| " \"witnessScript\" : \"script\" (string) witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH\n" | ||
| " \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n" | ||
| " \"solvable\" : xxx, (bool) Whether we know how to spend this output, ignoring the lack of keys\n" | ||
| + (avoid_reuse ? |
There was a problem hiding this comment.
I don't think we should have help output that switches on the active wallet for that command. This breaks help listunspent.
There was a problem hiding this comment.
Why does it break?
There was a problem hiding this comment.
You can do help listunspent without specifying -rpcwallet in order to just get the help for listunspent (and other commands). By making the help dependent on -rpcwallet, this makes the output of help listunspent less helpful because then the help sometimes will include reused and other times not. The help text should be consistent regardless of the other options specified. In general, all options and output should be displayed in the help even if some of those results will be omitted in actual command output. For example, getaddressinfo has a ton of extra fields that may or may not be part of the actual result, but all of them are listed in the help output.
There was a problem hiding this comment.
Ahh, I see what you're saying. Will rework into static output post-merge and/or if rebase becomes necessary.
|
ACK 5ebc6b0 modulo above nits Reviewed the code and did a couple of manual tests. Still not a fan of adding new isminetypes but I won't block this on that. |
|
The few remaining nits are not worth blocking merge on this, it has waited long enough. A follow-up PR to address them would be good. |
|
I took a very brief |
maflcko
left a comment
There was a problem hiding this comment.
Looked at the documentation and left some comments
| - getbalance | ||
| - sendtoaddress | ||
|
|
||
| In addition, `sendtoaddress` has been changed to enable `-avoidpartialspends` when |
There was a problem hiding this comment.
Can you explain what that means? sendtoaddress is an RPC call, how would it enable a command line flag (-avoidpartialspends) of a running program?
There was a problem hiding this comment.
Changing wording to
In addition, `sendtoaddress` has been changed to always use `-avoidpartialspends`
when `avoid_reuse` is enabled, as it would otherwise risk using up the "wrong" UTXO
for an address reuse case.
Does that look better?
| These include: | ||
|
|
||
| - createwallet | ||
| - getbalance |
| a wallet will distinguish between used and unused addresses, and default to not | ||
| use the former in coin selection. | ||
|
|
||
| (Note: rescanning the blockchain is required, to correctly mark previously |
There was a problem hiding this comment.
Could remove (Note: , since all sentences in this doc are release notes
There was a problem hiding this comment.
Removed. I wanted to use it as in "Do note that [word of caution]", but it's no big deal.
| " \"ECONOMICAL\"\n" | ||
| " \"CONSERVATIVE\""}, | ||
| {"avoid_reuse", RPCArg::Type::BOOL, /* default */ pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) ? "true" : "unavailable", "Avoid spending from dirty addresses; addresses are considered\n" | ||
| " dirty if they have previously been used in a transaction."}, |
There was a problem hiding this comment.
This makes the help text change at run time. I'd prefer to make it static and explain it with something like "true if wallet falg is set, otherwise unavailable"
| {"dummy", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Remains for backward compatibility. Must be excluded or set to \"*\"."}, | ||
| {"minconf", RPCArg::Type::NUM, /* default */ "0", "Only include transactions confirmed at least this many times."}, | ||
| {"include_watchonly", RPCArg::Type::BOOL, /* default */ "false", "Also include balance in watch-only addresses (see 'importaddress')"}, | ||
| {"avoid_reuse", RPCArg::Type::BOOL, /* default */ pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) ? "true" : "unavailable", "Do not include balance in dirty outputs; addresses are considered dirty if they have previously been used in a transaction."}, |
Add a new wallet flag called
avoid_reusewhich, when enabled, will keep track of when a specific destination has been spent from, and will actively "blacklist" any new UTXOs which send to an already-spent-from destination.This improves privacy, as a payer could otherwise begin tracking a payee's wallet by regularly peppering a known UTXO with dust outputs, which would then be scooped up and used in payments by the payee, allowing the payer to map out (1) the inputs owned by the payee and (2) the destinations to which the payee is making payments.
This replaces #10386 and together with the (now merged) #12257 it addresses #10065 in full. The concerns raised in #10386 (comment) are also addressed due to #12257.
Note: this builds on top of #15780.(merged)