wallet: ensure VSP payment doesn't create unmixable change#2595
wallet: ensure VSP payment doesn't create unmixable change#2595matthawkins90 wants to merge 1 commit intodecred:masterfrom
Conversation
78bd064 to
8839841
Compare
20317e4 to
fd05433
Compare
fd05433 to
43be2cf
Compare
|
I've been using this branch to buy VSP tickets for a bit now, and it's been working, but something I noticed is this: If the wallet uses a single input of For the user, this effectively means that the "fee to purchase a ticket via a VSP" is Here's a link to an example, where the transaction had a fee rate of 854 atoms/byte instead of the normal 10 atoms/byte: https://dcrdata.decred.org/tx/2c1dbddf185ed7c4e5fe567007628d46f07e2f4b19cce8979a4fc542d897492b I believe this is actually worse than leaving toxic, unmixable change from these VSP fees in the wallet as dust, because....the DCR is actually now gone instead of just sitting in the wallet as unmixable (and difficult-to-spend if the wallet doesn't allow sending from unmixed accounts). Ranking the possible outcomes:
A single input of the smallest mix denom is guaranteed to fail the mixability check. Two inputs of the smallest denoms are also going to be mixed, but mixing is going to donate the remaining amount to miners as well, so more inputs doesn't necessarily solve the issue. I don't know how to get to Outcome 1 where the least amount of DCR is wasted. Open to ideas! |
This adds a
MinimumChangeSourceinterface to txauthor as an opt-in extension ofChangeSource. When implemented,NewUnsignedTransactiondrops change below the specified minimum instead of creating a change output, and correctly recalculates the fee estimate without the change output. ExistingChangeSourceimplementations that don't implement it are unaffected (the minimum defaults to zero).p2PKHChangeSourceimplements the new interface. InmixedSplit, the change source now carriesminChange: smallestMixChange(relayFee), so sub-threshold change is dropped at construction time insideNewUnsignedTransactionrather than being nil'd out after the fact. This also fixes a minor fee underestimation in the old code, where the fee was computed assuming a change output that was then removed.CreateVspPaymentis reworked to proactively reserve enough UTXOs for mixable change. The reservation target is now:and if the actual input count drives the real transaction fee above the initial 1-input estimate, additional outputs are reserved iteratively until
change >= minMixableChange. If no more outputs can be reserved, the function proceeds with what it has and drops sub-threshold change to miner fees rather than creating an unmixable UTXO.The iterative approach was motivated by my own real wallet data. I used dcrctl/dcrctlw to decode all VSP fee payment transactions from a mixing wallet (listtransactions filtered to regular sends, minus split transactions identified via each ticket's vin[0].txid).
83% of my VSP fee payments used more than 2 inputs, with a median of ~10 and a maximum of 51 inputs. If the code only reserved 1 input, then in the majority of cases, it would drop the change entirely and fragment the account further.
The commands I used to figure this out are:
The change output guard (
change >= minMixableChange) is also applied to the pre-populatedtx.TxInpath, so callers that supply their own inputs also avoid creating unmixable change. The maximum value absorbed into miner fees in this case is ~0.00265 DCR.