← index

Disposing of "dust attack" UTXOs

An archive of delvingbitcoin.org · view original topic →

bubb1es · #1 ·

Hi, I’d like to find a standard/preferred way to dispose of “dust attack” UTXOs in on-chain wallets. In particular the dust I’m looking to get rid of are the ones created by an adversary to trick wallet users into revealing common ownership between otherwise unrelated UTXOs. This happens when the adversary sends dust amount UTXOs to all the anonymous on-chain addresses they are interested in and hope that some of these will be unintentionally spent together with an unrelated UTXO. (see Output linking | Bitcoin Optech )

The best data I could find on the distribution of output sizes in the UTXO set is from UTXO Set Report . But this report focuses on spam meta protocols and doesn’t break out details for dust attack UTXOs which are generally at or around the core dust limit.

Most modern wallets automatically lock dust amount UTXOs so they are never spent. This solves the problem but has a cost and future risks. The cost is the bloating of the mempool with UTXOs that will never be spent. Risks are that a wallet software bug, restore from keys, or migration to a new wallet could “unlock” the dust. There’s also a risk that a future wallet owner (such a with inheritance) could misunderstand the reason the dust is locked and spend it. In any case there are risks in the future of accidentally de-anonymizing the wallet.

Now that the default minimum relay fee rate (for core 30) has been lowered to 0.1 sats/vb it should be possible to spend typical dust UTXO by creating a transaction that uses the entire amount for fees and has an OP_RETURN output. The transaction only needs to be greater than the minimum relay size of 82 bytes and have a fee > 0.1 sats/vb.

Scenario 1: P2WPKH: 10.5 vb (overhead) + 68 vb (input) + 5 vb (OP_RETURN output) = 83.5 vb 250, 300, 350 sats input, fee rate is ~ 2.99, 3.59, 4.19 sats/vb. testnet4 example (I think mempool’s size calc is wrong)

Scenario 2: P2SH 2-of-3 multisig: 10 vb + 297 vb + 1 vb = 308.5 vb 250, 300, 350 sats input, fee rate is 0.81, 0.97, 1.13 sats/vb

Scenario 3: P2WSH 2-of-3 multisig: 10.5 vb + 104.5 vb + 1 vb = 116 vb 250, 300, 350 sats input, fee rate is 2.16, 2.59, 3.02 sats/vb

(thanks Transaction size calculator | Bitcoin Optech )

A few risks with implementing this feature in wallets are:

  1. if only one or a few wallets support it then that “fingerprints” the user.
  2. if multiple dust Tx are broadcast for a wallet at the same time that could correlate them.
  3. if fee rates go up these Tx may need to be re-broadcast when rates come back down.
  4. signing for dust Tx could be confusing/annoying to multisig and hardware signing device users.

Comments and suggestions welcome! I’m especially interested in hearing from wallet or wallet library developers if this is something they would consider supporting.

Anthony Towns · #2 ·

The minimum relay size is 65 bytes since Bitcoin Core 25.0. Here’s a mainnet example of a 75 byte tx (13 bytes of which is OP_RETURN data).

Doing an ANYONECANPAY|ALL signature spending to a single 0 sat, 3-byte OP_RETURN output of "ash" (“ashes to ashes, dust to dust”?) would be enough to ensure you avoid going below the 65 byte limit, and also would allow txs to be combined, for a slight saving in blockspace (23 bytes per input) and a matching slight increase in feerate.

Richard Myers · #3 ·

I like the idea! I can’t think of any way to make it more fee efficient than what @ajtowns suggested.

My only real concern is that people are careful about publishing their dust spends in a way that ensures privacy. It would be counter productive to clean up your dust outputs and then broadcast them from your home IP all at the same time. The same caution must be exercised as for regular spends from dusted addresses.

Adam Gibson · #4 · · in reply to #3

You make a very good point, but I guess it’s worth mentioning that there’s a big difference between doing this from a full node vs not. Albeit the normal question about spy nodes trying to triangulate applies, right, but then it’d be worth observing that if someone is able to “get through” the full node defence [1] and if you’re choosing to use a home, or in any case fixed, IP, you don’t have any strategy to not couple your utxos anyway … again, if that attack is active and it works, big if!

(And of course, Tor exists).

[1] i don’t know the state of the art on this, I only know that such attacks were outlined, hypothesized, and I vaguely recall, evidenced in the past. We don’t have dandelion but we have other things, right.. not sure but I guess p2p encryption doesn’t really change the threat much.

bubb1es · #5 · · in reply to #2

Thanks for the relay size correction and sighash and OP_RETURN data suggestions. I love giving users de-dusting their wallets the option to help others by combining higher fee-rate de-dusting txs with lower rate de-dusting txs.

I’ve created a simple “ddust” CLI app that demonstrates how to create these sort of de-dusting transactions. If there’s interest I’ll add the logic to combine unconfirmed de-dust txs.

I also fixed (I hope) my tx size and fee rate estimates:

Scenario 1: P2WPKH

Scenario 2: P2SH 2-of-3 multisite

Scenario 3: P2WSH 2-of-3 multisig

bubb1es · #6 · · in reply to #3

Thanks! For the tx broadcasting I agree it’s a risk but as @AdamISZ notes broadcast one of these de-dusting tx from a full-node is no worse for privacy than for sending any other transaction.

In my example ddust tool I provide an option to broadcast with your (required) local bitcoind node.

bubb1es · #7 · · in reply to #5

:waving_hand: @0xB10C I’m also trying to determine the nature and number of obvious dust attack UTXOs, do you know if there’s anyone in your new NOC group also looking into this?

0xB10C · #8 · · in reply to #7

I’m not aware of anyone looking into it, no. Congratulations, you are now the person looking into it! :slight_smile:

You can use the dumptxoutset RPC and e.g. this script

to export the current UTXO set. From there, it’s probably just a few SQL queries to figure out how many close-to-dust UTXOs remain and when they were created.

There is also a PR for a utxo to CSV script open. If you end up using it, feedback on the PR is surely welcome.

bubb1es · #9 · · in reply to #8

Thanks for the vote of confidence! I’ve written my own tool to parse the utxodump data and already have some preliminary results. Hope this stimulates some interest and discussion on the topic.

EDIT:

The cumulative graph by script type is more interesting.

Andrew Toth · #10 ·

Just wanted to point out that Private Broadcast (Broadcast own transactions only via short-lived Tor or I2P connections by vasild · Pull Request #29415 · bitcoin/bitcoin · GitHub) will be released in Bitcoin Core v31. It solves this privacy issue, obviating the need for dandelion.

Pieter Wuille · #11 · · in reply to #10

I think that’s an exaggeration, and while they’re both privacy improvements, they’re also partially orthogonal. Dandelion improves transaction relay privacy in general across the network. Private broadcast only improves the first hop, and does so by relying on privacy networks, which may not be accessible to everyone, and bring their own trade-offs.

Andrew Toth · #12 · · in reply to #11

I don’t see how they’re orthogonal. They both have the same goal of removing the link between a transaction and the originator’s IP (or persistent Tor/I2P address). Is there another goal that we wish to achieve that I am missing?

If the first hop is done through a private broadcast, then it achieves the above goal. I’m not sure how you mean “in general across the network” to be an improvement. Dandelion requires multiple hops and other nodes to be honest throughout the hops to achieve the same goal.

Pieter Wuille · #13 · · in reply to #12

No, you’re right. In my mind Dandelion had privacy advantages beyond hiding transaction origin, but reading material from the time it was proposed, that doesn’t seem to be the case.

If the first hop is done through a private broadcast, then it achieves the above goal.

If that’s available and reliable, yes. But I don’t think that’s true in general. Tor/I2P may not be available in all environments, or desirable as it relies on somewhat centralized directories. I2P doesn’t have those, but the hidden-service-only model means it’s relatively cheap to Sybil attack (spining up tons of I2P Bitcoin nodes cheaply which don’t relay transactions might be enough to make I2P-private-broadcast unreliable).

I didn’t mean to suggest that Dandelion is better, or even a worthwhile thing to pursue in addition to private broadcast; I think its DoS concerns mostly make it a non-starter. I was just a bit triggered by your implication that transaction privacy problems are solved with private broadcast, while I wouldn’t say that for something that is still opt-in with trade-offs. I may have jumped the gun in reading that much into your claim, though.

bubb1es · #14 ·

I’m happy to see this thread included in the recent Optech newsletter! My plans going forward are:

  1. continue to gather feedback on this approach (or any others) for disposing of dust
  2. gather more on-chain data to improve my estimates of how big a problem this is
  3. try to implement combining dust spend tx found in the mempool (as proposed by @ajtowns)
  4. if a safe and effective approach is found to dispose of dust draft a BIP for it
  5. reach out to wallet developers and see if they’d like to incorporate the feature

Call to action is for help on any of the above. :folded_hands:

gmaxwell · #15 ·

bubb1es · #16 · · in reply to #15

Wow this is great prior work and supports the axiom there are no new ideas in bitcoin! or at least the obvious ones were written about in the first epoch.

I see some pros and cons to Peter’s approach. It’s certainly more efficient and avoids making timing data publicly available by consolidating and broadcasting dust spend transactions through his server. But even if you connect via Tor it could be logging and collecting timing data to associate your dust. And having multiple people running these servers doesn’t help since you wouldn’t know which are run by the same entity.

I prefer my proposed approach of leaving it up to the users to privately broadcast via their nodes and possibly coordinate grouping adhoc by finding others in the mempool you can spend with. Wallet software should be able to put in some safeguards to prevent disposing of multiple dust UTXOs at the same time.

Thank you for sharing this and I’ll dig a bit more into his implementation to see if I’ve missed anything.

harris · #17 ·

@bubb1es Thanks for reviving the issue of dust attack and thanks for your Rust based dedust cli tool. I read through the discussion here and it is interesting to know that this problem actually exists and it is not a new idea at all and there have been attempts to address it. I am interested in taking the project further and collaborating on improving the solution. I was wondering if you are still active on the dedust Rust cli and if so, I would be happy to get involved and start contributing.

Proposals would be e.g. dust attack detection logic, Combining transactions using ANYONECANPAY (ajtowns’ suggestion) and finally we can include a PSBT workflow for hardware wallets.

Please let me know if this aligns with your plans. For context, i intend to work on this as a portfolio project for the BOSS Challenge 2026.

bubb1es · #18 · · in reply to #17

@harris yes I’d be happy to collaborate with you (and anyone else) on improving the ddust tool and/or further exploring mitigations for this issue.

As for attack detection logic the tool already finds any dust size UTXOs in your (pub key descriptor) wallet. And it should work with any hardware signers that can sign a PSBT, but I haven’t done much testing on that front.

Figuring out how to combine unconfirmed dust disposal transactions is my current focus so we can start there. First try building and testing the project with a regtest bitcoind node. Then I recommend getting to know how the bdk_wallet and bdk_bitcoind_rpc rust crates work, they do most of the heavy lifting.

harris · #19 · · in reply to #18

Thanks, lets collaborate then. I will go through the project, test it and give an update here

bubb1es · #20 ·

A quick update on this project:

Thanks to @harris the ddust tool now 1) batches new dust inputs with unconfirmed dust disposal txs it finds in the mempool and 2) has a regtest integration test framework and initial tests.

@harris and I have also created a draft BIP specification for the protocol described in this thread and implemented in the ddust tool.

We’d love to get feedback from the community on the latest updates to the tool and on the draft BIP.

We’d especially like to hear from anyone who has written or reviewed BIPs to make sure this is a good draft before we submit a PR to the bips repo. And also from wallet (or wallet lib) maintainers on if this is a feature/spec you’d consider implementing, or if not why.

If you’d like get get involved on the ddust project a few top of mind ideas are:

Or on the dusts dust stats project:

bubb1es · #21 · · in reply to #20

Link to latest revision of draft BIP is: https://github.com/bubb1es71/ddust/blob/main/docs/bip-dust-disposal.md

nothingmuch · #22 ·

The details seem a bit tailored to Bitcoin Core’s current policy.

SIGHASH_NONE|SIGHASH_ANYONECANPAY would allow 1 input, ash OP_RETURN output transactions to be constructed and then aggregated by a third party into a transaction with an empty OP_RETURN output. However, this also allows miners or third party aggregators to add txouts. As far as I can tell that makes no difference, i.e. does not increase DoS concern.

Alternatively, if OP_RETURN ash was always required (i.e. not allowing empty OP_RETURN), then there would be just one class of such transactions that can always be aggregated into a valid transaction, with 3 virtual bytes of shared overhead even when that is unnecessary. The rationale section for SIGHASH_ALL|SIGHASH_ANYONECANPAY only discusses the ANYONECANPAY part and not ALL vs NONE.

However, it seems to me like a policy carveout for single input, empty OP_RETURN output transactions would be even better? This allows the smallest single output txs, and does not present any more of DoS concern than what is already allowed.

Furthermore, if every dust input was relayed separately in a standalone minimal transaction, and always aggregated in the mempool using deterministic sorting rules, this would effectively be a carveout for the entire space of aggregate transactions, removing the need for any replacement logic for such transactions, which is asymptotically optimal bandwidth per output destroyed and maximizes revenues. This is backwards compatible in that peers that do not understand aggregation at all could simply enforce current standardness rules and replacement rules while still relaying 1 input 1 output transactions that could be aggregated by miners etc, but that requires either the 3 vbyte overhead of ash or SIGHASH_NONE to be blockspace optimal.

bubb1es · #23 · · in reply to #22

Thanks for these helpful insights. I will address them in order.

Yes, part of the motivation for this proposal was the ability of Bitcoin Core 30.0+ to relay sub 1 sat/vb fee-rate txs. But I see your point that we don’t need to specify a specific fee rates in the proposal since the standard min fee rate could change over time (up or down). I’ll see if I can remove unnecessary restrictions that tailor it for Bitcoin Core’s current policy.

My original concern with SIGHASH_NONE|SIGHASH_ANYONECANPAY was that dust disposal tx with high enough amounts would get poached into transactions with a regular output and take up more space on-chain. But I think you’re correct, allowing batching to remove the “ash” marker is more important. If someone is able to poach these dust inputs into a tx with other types of outputs they still need to follow the RBF rules so this isn’t a spam vector. If @harris and other’s agree I’m good with this change, and will add these thoughts to the rationale section of the draft BIP.

I’m now leaning towards your SIGHASH_NONE|SIGHASH_ANYONECANPAY approach.

I agree that even with SIGHASH_NONE|SIGHASH_ANYONECANPAY original disposal tx should never have the OP_RETURN “ash” if not needed. I expect many/most disposal tx won’t get batched or poached so need to make them as efficient as possible.

Are you talking about mempool signature aggregation here?

Anyway I don’t want to rely on any mempool or dedicated third-parties batching/aggregating disposal tx inputs in a certain way. I think it will be more reliable (even if less efficient) to let any wallets do it opportunistically when they find other dust disposal txs in the mempool at the same time they are creating their dust disposal tx. This is how @harris implemented it in the ddust reference client. But still even with opportunistic client batching I favor using SIGHASH_NONE over always padding with ash.

nothingmuch · #24 · · in reply to #23

it seems to me like miners can do this at their discretion, i.e. they equally “unpoach” any such inputs that have been poached, or choose to include such inputs with 0 overhead into other transactions that already have ANYONECANPAY effectively removing the full overhead (i.e. locktime version etc, as well as the cost of an empty OP_RETURN), so it seems like this is a potential net increase in efficiency, and i couldn’t think of anything that is made concretely worse by removing this additional restriction

fwiw this isn’t my approach, IIRC it’s how dustbgone was implemented

i don’t think there’s a conflict between your approach and pursuing a policy carveout, the former can have a positive impact right now whereas the latter will take at least a release cycle and can impact the former positively on that longer time frame

not signature aggregation, but yes mempool, specifically my point is about the DoS surface and sub-optimality of relying on transaction replacement policy.

given n dust outputs for which an ANYONECANPAY signature is known, anyone can produce a total of 2^n -1= \sum_{k=1}^n {n \choose k} and for each size k and for each subset of size k there are k! possible orderings, so \sum_{k=1}^n k! {n \choose k} possible transactions with SIGHASH_ALL. with SIGHASH_NONE, the outputs of the transaction is completely unrestricted. let’s ignore ordering ([edit: because it has no effect on payoffs] suppose inputs are always ordered by effective value descending, with lexicographical ordering of outpoint for tie breaking), and the output side (since that isn’t costless, for a given set of dust inputs the feerate of a poaching transaction will always be strictly lower, and miners can always omit or substitute such outputs), this still leaves just 2^k-1 choices.

the problem i see with this is that there is path dependence in this exponential sized space. suppose there are dust outputs a, b, c and one user broadcast \{a, b\} and another attempts to broadcast \{b, c\} or \{a, b, c\}. this may not be possible if output c ’s effective value insufficient for these transactions to be considered valid replacements for the one spending \{a, b\}. an attacker may strategically choose to construct such transactions, and even for small n be able to produce O(2^n) conflicting transactions that it can then broadcast to different mempools, each of which is a local maxima, in order to attempt to impede not just the broadcast of other such transactions, but to cause block propagation delays as well.

while i haven’t worked out the details of how bad this could get, my point is just that if you assume miners will always combine dust input spends into a single bulk dust removal transaction with just one OP_RETURN (or arbitrary outputs of their choice, which if it’s a miner doesn’t matter), then a policy carveout for 1 input transaction and policy restriction on multi input transactions will enforce that only n distinct transactions are ever actually broadcast instead of O(2^n) potential ones. RBF is no longer an issue, as the batch transactions are replaced implicitly. modulu eviction, every peer will spend at most a fixed amount of bandwidth per dust input, as opposed to per potential combination of dust inputs, but the theoretical minimum blockspace required to spend these outputs is still accessible to miners without needing to relax relay policy. if replacement rules were just relaxed and larger and larger combinations of transactions were repeatedly broadcast as new dust outputs are spent, the minimum blockspace transactions be available, but the bandwidth cost would scale exponentially in the worst case.

or put more simply, the DoS surface is substantially reduced if the rule is that dust spends are only relayed as 1 input transactions, even though these can be freely aggregated by anyone. compact block relay could also be extended to take this special case into account.

again i don’t think there is a conflict between client side aggregation in the near term and designing for something like this in the long term, it just seemed notable that a policy carveout could simultaneously reduce DoS risks and remove restrictions on how efficient such batching could get.

bubb1es · #25 · · in reply to #24

I double checked and yes you are correct, in dustbgone Peter constructs a tx with NONE|ANYONECANPAY inputs and a single empty OP_RETURN output. So very much like this BIP proposal in those regards.

Thanks for the details, I understand the DoS issue better now and why the policy carveout would help. I certainly hope we eventually see enough dust UTXOs being disposed this way that a policy carveout is needed. My more immediate concern is having a spec wallets can implement that is safe and effective before the problem gets any worse.

nothingmuch · #26 · · in reply to #25

Makes sense! For what it’s worth if this was my project the way i would probably go about this is:

  1. demonstrate interest in fixing this, as you are already doing
  2. if SIGHASH_NONE proves problematic with increased adoption, advocate for very narrow policy careveout just to eliminate the empty vs. 3 vbyte OP_RETURN consideration
  3. very long term, try to integrate my implicit aggregation suggestion into mempool code, since this makes the wallet implementation trivial (always just broadcast 1 input txs), makes it easier for optimal blockspace cost to be achieved and removes the DoS concern of replacement logic. this is very long term because, assuming there’s even any buy in or sufficient demand, it would be a pretty involved change likely to take years to actually land

BTW one last nitpick (and this really is a nitpick) about the proposed BIP text itself, the requirement to not aggregate coins originating from a single wallet seems a bit too strong, since by relying on PR #29415 (-privatebroadcast=1, already mentioned in this thread) a node could arguably reliably simulate aggregation by multiple independent parties (broadcast a tx spending the first input, then reuse the exact same signature in a tx spending another), but in the absence of -privatebroadcast=1 or when Tor daemon is unavailable, an adversary model for dust attacks (which used to be a much bigger problem with the old rebroadcast behavior) where the adversary is monitoring for (re)broadcast of received dust transactions is arguably still a concern for these spending transactions, and disallowing direct submission to 3rd party aggregators seems to be motivated by such leaks.

i think the rationale there could be improved to discuss how privatebroadcast changes the threat model, as well as the issue with spending dust coins with more than one distinct address being contingent on the adversary being able to observe the absence of a prior transaction that only spends from one distinct address. in fact, if a third party aggregator buffers such spends before broadcasting a batch spend, and the adversary then concludes that this suggests that those coins are related similarly to the common input ownership heuristic, this can poison clustering data with counterfactual information, (“cluster collapse”, c.f. MtGoxAndOthers supercluster on wallet explorer for example), which is arguably beneficial for privacy and fungibility.

harris · #27 ·

Thanks nothingmuch and bubb1es. Lots of interesting details over here. After reading through this new discussion, i agree to SIGHASH_NONE|SIGHASH_ANYONECANPAY suggestion and relaying of every dust input separately in a standalone transaction, and finally the miner performing the aggregation locally.

I think the “single input, single output“ pattern tx is important to prevent introducing another DoS surface of batching at relay level(the current approach) as already described by nothingmuch. This way, relying on standard min fee rate is also not needed. We could preserve relay-level batching while avoiding the 2^n explosion by enforcing an append-only rule - i.e. a batched dust transaction can only be replaced by one that contains all the same inputs plus additional ones, with inputs always in a deterministic order(No removing inputs, no alternative subsets). This would make the replacement space linear i.e. at most n replacements to go from 1 input to n inputs.

But even with this constraint, it still doesn’t beat the cleaner approach of single-input relay + miner aggregation

bubb1es · #28 · · in reply to #26

I see how technically with -privatebroadcast=1 a client wallet could simulate a third-party batching their dust while doing it all themselves. And doing this could be a bit more block space efficient and relay efficient. But I’m more concerned that in an environment where dust disposal tx are few and far between an attacker could easily correlate dust UTXOs confirmed and/or broadcast around the same time. For convenience I’d rather see wallets pre-sign dust disposal txs and then broadcast them at random times, and/or when they see some others in the mempool they can batch with.

I’ll try to improve the rational section of the proposal around this point.

nothingmuch · #29 ·

Note that a miner aggregating this way can always choose to omit inputs that are less lucrative, so the replacement space can’t be enforced linear without some form of consistent broadcast. That said the fact that it’s still O(2^n) is no more of a problem than block contents being an arbitrary choice of the mempool, especially since totally ordering the 1-in-1-out txs is trivial.

From this point of view ANYONECANPAY aggregation can just be modeled as a weight discount on the 1-in-1-out txs. If at least one such transaction is included then it would not be discounted, but penalized with an additional virtual byte for worst case nIns compact size increase (i believe this is the only non-linearity), and then every additional one could be counted as if its weight is just that of its TxIn.

Miners can already do this, so I think the right question to ask is why haven’t they? If i understood the charts @bubb1es shared above suggests in an upper bound of about 4 btc of revenue in the best case. ddust seems like a good way of strengthening this incentive.

You’re right I think that’s the bigger concern

ArmchairCryptologist · #30 ·

Since it has not been mentioned, I feel it should be brought up in the “Security Considerations” section that wallets SHOULD NOT enable such a dust disposal function for UTXOs if there are “real” unspent funds on the same address, especially if a) no funds have previously been spent from the address and b) it is an address type with a hashed public key.

Rationale being that even if you don’t want to consolidate your real funds with the dust, disposing of said dust without moving the real funds first would reveal the public key for the address, which would make the real funds vulnerable in a hypothetical future where CRQCs capable of long-exposure attacks against 256-bit ECDLP public keys exist. In such a future, I could see attackers sending dust to wallets specifically to attempt tricking people into disposing of dust in order to reveal their public key, thus enabling such an attack.

bubb1es · #31 · · in reply to #30

Good suggestion, I’ll update the proposed draft BIP “Security Considerations”. I’ll also add an issue to update the ddust reference implementation to make this an optional (but default) feature when selecting dust UTXOs to dispose of.

Beyond the quantum concern, spending non-dust UTXOs before the dust is also a good way to make sure large, non-dust UTXOs can never be accidentally signed as NONE|ANYONECANPAY inputs.

bubb1es · #32 ·

For those of you following along at home, some big new developments on this project.

See all the PRs details.

We’re still looking for feedback on the draft BIP if anyone has any new suggestions.

A big thanks to everyone in this thread, the current spec is vastly better because of your constructive input.

bubb1es · #33 · · in reply to #32

Another update, we’ve created a new bip451 GitHub org for the ddust, dusts and any future related repos. Please see or open issues and PRs at these new locations.

We are also making a small adjustment to the license on these repos to be MIT or Apache 2.0, at the users discretion, to ensure easier adoption for anyone who needs an explicit patent grant.

A few next steps on the road to moving BIP-451 from “draft” to “complete” and ultimately “deployed”:

  1. improve test coverage and fix any bugs in the reference ddust client and/or spec
  2. gather more on-chain data about dust attacks to better understand the scope of the issue
  3. work with wallet and wallet lib projects to add BIP-451 support
  4. create a website to educate bitcoin users about BIP-451 and disposing of dust UTXOs
  5. track and report progress on disposing of main chain dust

If you’re a wallet developer please reach out if you’d be interested in adding BIP-451 support to your project. :folded_hands:

Adam Gibson · #34 · · in reply to #33

I’m curious about the section: bips/bip-0451.md at 686c1b0ed7fc550ab567b2ec94f7e3f58b319e58 · bubb1es71/bips · GitHub

From your previous post, you seem to be saying this is principally just about not revealing public keys (i.e. the second reason in the BIP draft):

By default (but optional) dust UTXOs will only be dispose of if there are no non-dust UTXOs using the same address; prevents prematurely revealing the address public key

I don’t quite understand the logic: surely, in practice, it will be extremely common that there remain non-dust utxos at the address being dusted. Thus you’d be recommending people not to move a lot of the dust (even worse (imo) they might interpret it as “hurry up and spend your coins at dusted addresses!” which could be bad for various reasons, not least, creating another heuristic for onchain/metadata observers, possibly).

The “problem” of exposed pubkeys already exists in every single wallet/address/bitcoin usage, you cannot redesign protocols for public key cryptography to avoid revealing public keys. Solving the theoretical quantum threat is orthogonal imo. I personally would remove any reference to that.

(Edit: OK, fair enough, this is a special case of reuse which wouldn’t exist without dusting. I still don’t buy it as a good reason not to de-dust, except perhaps in case of exotic scripts existing “under” a taproot or script-hash address; that seems like a reasonable reason, that you don’t want to expose it if there is no taproot override (external key signing) that could hide the business logic).

But you wrote two cases, and the first of the two didn’t mention hashed pubkeys (I presume you’re referring to taproot, then?); is there another reason I’m not noticing?

bubb1es · #35 · · in reply to #34

Perhaps we’re being overly cautious here by trying to avoid revealing the public key of an address that contains non-dust UTXOs. But one of the goals with disposing of dust is to avoid any potential reduction in wallet privacy or security just by disposing of the dust.

We did use the SHOULD language in dust selection part of the spec. since in our reference implementation gives users the option to spend dust that has unspent non-dust UTXOs. But does this cause a user fingerprinting issue? I have to think about that since unlike the quantum stuff it’s a real, non-hypothetical concern.

On the other hand you bring up another real issue that we don’t want to expose unique hidden tap script paths just to dispose of dust.

Overall I agree that we want to make disposing of dust as convenient as possible so it actually gets done. But we need to do our Hippocratic duty of first do no harm. I’d like to hear what you and others think about the relative importance of these potential risks:

  1. revealing public keys when spending dust
  2. fingerprinting the user/wallet by giving them dust selection options
  3. revealing tap script tree scripts when disposing of TR dust
  4. requiring non-dust UTXOs MUST be spent before disposing of dust for the same address

The happy path dust disposal scenario I have in mind is:

  1. dust UTXOs are automatically locked when received (can’t be spent in normal coin selection)
  2. non-dust UTXOs are spent in the normal course of coin selection
  3. dust UTXOs becomes eligible for disposal
  4. wallet creates & signs dust disposal txs when keys are available
  5. at random times the wallet broadcasts (and tries to batch) signed disposal txs

This procedure applies as well to when someone migrates all their non-dust UTXOs to a new wallet keys/descriptor leaving behind the dust UTXOs.

Murch · #36 · · in reply to #34

That doesn’t match my understanding of what forced-address-reuse attacks are trying to achieve. The idea is to coax the wallet into using the dust UTXO in a new transaction that allows the observer to leverage the common-input-ownership heuristic to gain more information about the wallet cluster. If an existing UTXO is dusted, this should fail, because best practice is to spend all UTXOs corresponding to the one output script at once, and the forced-address-reuse produces no additional information.

So, if an existing UTXO is dusted, the dust UTXO should be spent along the other UTXOs associated with that output script, especially if the feerate makes it a net-benefit to the transaction. If the additional dust input would cause an objectionable increase in fees, it should be disposed later. I don’t see why the dust should ever be disposed alone first, if there are other UTXOs associated with the same output script.

Adam Gibson · #37 · · in reply to #36

That’s a fair point that I didn’t consider. I’m not sure how universal that rule should be; it makes sense from a privacy maximizing point of view, but it wouldn’t always make sense from an economic point of view, right?

Indeed, if utxo selection policies were always ideal (best practice), I feel like no such attack can ever make sense: I think their point is to exploit non-sophisticated utxo selection techniques in wallets.

Yes, I think I can’t argue with that logic. It can always be after, not before, even when it’s not economic to combine.

ArmchairCryptologist · #38 · · in reply to #36

The main reason we should avoid adding mechanics that would reveal public keys prematurely is there will be significant urgency to move funds with exposed keys to a PQC (or even non-exposed) address if a CRQC ever materializes, and it seems counter-productive to add to this rush unnecessarily. Granted, this would really only matter in certain scenarios - specifically those where only “slow-clock” CRQCs are available, where the time required to break a key would be on the order of a few days to a couple of weeks, meaning there is little danger of a key being broken in the window between broadcast and mining.

There is another and (in my experience) more common reason to dust an existing UTXO: attempting to trick users into sending funds to an attacker’s address, specifically by leveraging weaknesses in certain UIs combined with poor user practices of copying addresses from previous transactions. Such transactions will generally be sent from or have additional outputs to intermediate addresses that have been brute-forced to be superficially similar to the target address, say by starting and ending with the same four characters. These attacks will inevitably target addresses holding unspent UTXOs.

You may not want to do that in certain situations, since to the potential “taint” can negatively affect KYC/AML source-of-funds type processes, especially since those are increasingly handled by AI. Whenever that is a concern, you would want to leave the dust UTXO behind and dispose of it in a separate transaction, just to avoid the headache of trying to explain to a machine that someone you don’t know sent it to you.

Murch · #39 · · in reply to #38

Interesting point on the taint, I hadn’t thought of that!