research-lab
research-lab copied to clipboard
Eliminating the 10-block-lock
Eliminating the 10-block-lock
This is a proposal for eliminating Monero's 10-block-lock, which prevents users from spending outputs until they are 10 blocks old. The proposal has two significant and clear drawbacks, but I believe it is the best possible solution given all the constraints.
If you are reading this, please first look at it academically before dismissing it out of hand. Yes, the drawbacks may be too severe, but before passing final judgement I would like a useful discussion.
Motivation for 10-block-locks
In my view, there is one main reason for needing 10-block-locks in Monero.
Outputs in the PoW 'reorg zone' (the most recent ~1-10 blocks) can be completely removed from the chain by a reorg, because any output that can be reorged can be double-spent. If an attacker spams the network with double-spends of his own outputs, then on a regular basis 'naturally occurring' reorgs will cause some of his transactions to be replaced by his double-spends of those transactions' inputs. If an output created by the first group is used as a decoy by a random person, then that person's transaction will become invalid after the reorg.
In other words, decoys are vulnerable to the 'confirmation' problem. A tx author should only use decoys that are 'strongly confirmed', i.e. decoys that are buried below the 'reorg zone'.
Since all txs need strongly confirmed decoys, outputs in the 'reorg zone' will never be used as decoys. Hence, any output in the 'reorg zone' referenced by a tx input's ring signature must be that input's real spend. The tx author may be fine with revealing their real spend. However, if that output is used as a decoy in other users' txs, then since observers know it has been spent, they will know it is a decoy in those other users' txs. This weakens those users' ring signatures. To prevent that weakening, the 10-block-lock time is enforced. This way all decoys in a tx input are plausibly the real spend.
Proposal
- Allow transactions to have inputs with zero decoys if those inputs are very young (recently added to the chain).
- After a grace period, remove outputs from the pool of eligible ring members if they have been spent in a zero-decoy input.
This offers a compromise between fast-spends and minimizing the damage those fast-spends can do to ring signature effectiveness.
// constants
DEFAULT_SPENDABLE_AGE = 10;
INPUT_ELIGIBILITY_GRACE_BLOCKS = 20;
ZERO_DECOY_ELIGIBLE_ZONE = DEFAULT_SPENDABLE_AGE + INPUT_ELIGIBILITY_GRACE_BLOCKS; //30
PROVABLY_SPENT_DECOY_ELIGIBLE_ZONE = ZERO_DECOY_ELIGIBLE_ZONE + INPUT_ELIGIBILITY_GRACE_BLOCKS; //50
fn validate_input_age_zero_decoy(block_height, input_height) -> bool
{
// only outputs in the zero-decoy eligible zone can be spent with zero decoys
if (block_height - input_height > ZERO_DECOY_ELIGIBLE_ZONE)
return false;
else
return true;
}
fn validate_input_age(block_height, input_height, input) -> bool
{
// only outputs that reached the default spendable age can be referenced in a membership proof with > 0 decoys (IF they are not 'provably spent and older than the provably spent eligible zone')
if (block_height - input_height <= DEFAULT_SPENDABLE_AGE ||
(block_height - input_height > PROVABLY_SPENT_DECOY_ELIGIBLE_ZONE && is_provably_spent(input))
)
return false;
else
return true;
}
- Why are there grace blocks?
A transaction that lands in the tx pool may not be added to a block right away. If a tx spends a zero-decoy input that is 10 blocks old at the time of construction, it can't even be added to the in-progress block if there is no grace period.
If a tx with a normal input references a provably-spent input that is 20 blocks old, and provably-spent in a tx that is 10 blocks old, then it can't be added to the in-progress block without a grace period on provably-spent ring members.
When constructing a transaction, if a real-spend is within the 10-block-lock, then spend it as a zero-decoy input. Otherwise use a ring signature like normal (however, the tx author should avoid using decoys that are already provably spent). The grace blocks should not be abused to make zero-decoy inputs that are older than 10 blocks.
Drawbacks
There are two main drawbacks to this proposal.
- When a transaction is constructed, any decoys selected from the most recent ~20 eligible blocks may become provably spent.
- Since input-type-eligibility changes as a function of the block height where a tx gets mined, if a tx lingers in the tx pool for longer than the 20-block grace period, it may become invalid.
- Any tx with a zero-decoy input would become invalid.
- Any tx with a normal input that references a provably-spent output would become invalid. If deterministic binning is implemented, it may be impossible for deterministic references to work across the eligibility-barrier (i.e. because output ordering/indexing would change within blocks after provably-spent outputs are removed). In that case, txs with normal inputs would always become invalid at the end of the grace period.
Note that Seraphis membership proof delegation allows tx authors to always complete membership proofs right before submitting their txs, which would make slow txs like multisig/atomic swaps compatible with this proposal (otherwise the grace period would usually expire before a tx can be submitted).
Looking at the future
If Monero implemented an ideal membership proof that could reference all outputs in the chain, then this proposal could be greatly simplified to 'allow zero-decoy inputs if the inputs are < 30 blocks old'.
Eliminating the 10-block-lock would be a quantum leap in the usability of Monero. Having to wait 20 minutes to spend isn't something that would be expected of a currency that is positioning itself as the next generation of money. If this is possible with Seraphis, it's a design goal that should be prioritized.
As for @UkoeHB's proposal. I have a question, what if the zero-decoy input actually pre-selects decoy outputs, including outputs within the <20 block zone, this way if the tx lingers for longer than the grace period it automatically converts itself into a standard tx?
Since all Monero transactions must have at least 2 outputs, it would make sense to at least have a 2-member ring consisting of both outputs. This would avoid having provably spent outputs*. Note that the confirmation problem does not apply in this case, because the whole ring consists of outputs that were created by the user submitting the transactions.
So there would be 2 options:
- Reference 11 ring members older than 10 blocks as usual.
- Reference a previous
txid, in which case the ring is formed by all outputs of that transaction.
* In rare cases, we could still have provably spent outputs if both transactions outputs are spent this way.
The 10-blocks lock is a big UX problem for normal users, which cannot spend coins they just received and are forced to wait 20 minutes; and for projects like Haveno, built on multisignature transactions (more details about how the lock negatively affects Haveno in our dedicated issue: https://github.com/haveno-dex/haveno/issues/203).
We strongly support the removal of the 10-blocks lock and we agree it should be a priority.
As for @UkoeHB's proposal. I have a question, what if the zero-decoy input actually pre-selects decoy outputs, including outputs within the <20 block zone, this way if the tx lingers for longer than the grace period it automatically converts itself into a standard tx? @LocalMonero
Once a zero-decoy signature has been made, the link between output and key image is guaranteed. Adding decoys later cannot hide that connection.
Since all Monero transactions must have at least 2 outputs, it would make sense to at least have a 2-member ring consisting of both outputs. @tevador
This is an interesting idea. However, the spent output could not be removed from the decoy pool. Zero-decoy vs. 1-decoy is not a huge gap, and would still weaken other ring signatures that use the referenced outputs as decoys.
My goal is to prevent weakening ring signatures even if a huge proportion of outputs are zero-decoy (e.g. due to an adversary).
This is super exciting if its actually viable.
My goal is to prevent weakening ring signatures even if a huge proportion of outputs are zero-decoy (e.g. due to an adversary).
My question: Hypothetically, let's say after this change 50% of transactions are spent as 0-decoy within 10 blocks. That would reduce the number of potential outputs to select as decoys by half compared to the case where we keep the lock time, no?
It seems like reducing the available outputs for decoy selection would have some effect on the effectiveness of ring signatures -- can we quantify it?
It seems like reducing the available outputs for decoy selection would have some effect on the effectiveness of ring signatures -- can we quantify it?
My intuition is reducing the total number of available decoys only affects the total depth/breadth of the transaction graph. It does not affect the average number of times a given non-zero-decoy output would be used as a ring member (it should be ~ equal to the ring size if tx volume is constant).
Once the graph is sufficiently large, further increases in size have negligible impact on privacy, so a reduction in size may also be negligible.
I think the biggest risk is breaking the "privacy by default" property of Monero. With this change, it is theoretically possible to have a very active wallet with an entirely traceable history. Worse yet, exchanges may start requiring deposits to be zero-decoy transactions etc. It's a slippery slope that ends up down there with zcash.
Once the graph is sufficiently large
Do we have an idea for what counts as sufficiently large
Worse yet, exchanges may start requiring deposits to be zero-decoy transactions etc
In order to accommodate this request, a user who has coins >10 blocks old would first have to self send which would mitigate the information gain? At least somewhat.
Hmm, although, that makes me wonder. You receive a TX with 10 decoys, then you spend that output as a 0-decoy TX --- wouldn't that also allow an observer to make an inference about which output in the previous 10-decoy TX was the real spend?
I think the biggest risk is breaking the "privacy by default" property of Monero. With this change, it is theoretically possible to have a very active wallet with an entirely traceable history. Worse yet, exchanges may start requiring deposits to be zero-decoy transactions etc. It's a slippery slope that ends up down there with zcash.
so much this.
Additionally, this issue is something that can be "solved" with output management.
I think the biggest risk is breaking the "privacy by default" property of Monero.
Then another solution needs to be found. Nobody says that the 10-block-lock should be eliminated at all costs. Just that it's a high-priority goal.
Additionally, this issue is something that can be "solved" with output management.
Details? Are you talking about generating txs with outputs for yourself? This doesn't solve any problem apart from sending multiple transfers that lock all your outputs in a row, such as to pay for two cups of coffee in the span of 5 minutes. Even then, it's an awful hacky solution that no average person should be expected to do, nor is it desirable to bloat the blockchain with transactions heavier than need be even if this functionality is abstracted away into the wallets that will automatically generate and send out these outputs on a regular basis, using up the device's resources, and they'd have to do it ahead of time too.
@LocalMonero one can prepare in many cases to avoid having to deal with this problem. It doesn't solve for all cases though, hence "solved." Haveno is an example case that's difficult/impossible to plan around. You can't realistically tell someone who needs to put money in a 2/3 multisig to confirm a trade that they should have thought ahead :/
You can't realistically tell anyone that they should have thought ahead in the modern instant economy and call your product the next generation of money. Waiting 20 minutes to spend is not something to "hack around", it's something to solve. Spending unconfirmed coins is a massively useful application, ranging from instant payments between trusted parties to hot<->cold wallet management for exchanges to non-custodial service platform flow optimizations.
I'm quite interested in debating the merits of the idea @tevador introduced. I'm also still wrapping my head around the implications, so please don't take these points as gospel.
Benefits of Tevador's method:
- Fewer things are completely known to be spent, preventing them from being completely ruled out.
- People using the Monero wallet for high-frequency transactions will at least get some transaction graph privacy.
- Better protections for decoys that are selected within this 10-20 block window.
Benefits of the originally-proposed method:
- Lazily-spent outputs are filtered out quickly.
- Better protections for decoys that are selected later after block 20.
Disadvantages of both methods:
- Encourages lazy/sloppy output use, where many exchanges, mining pools, and other services will see no meaningful reason to follow best-practices of cycling through different outputs and batching transactions.
- Adds complexity compared to the current method.
- If a 20 block grace period is selected and there is a typical 10-block lock window, that would reduce privacy somewhat for transactions spent between 10-20 blocks.
Recommended improvements:
- Impose a noticeable additional fee on instant send transactions to discourage sloppy use. Make it 10x more for example. That way exchanges and services which are making regular transactions have an incentive to not be apathetic towards privacy.
I think the biggest risk is breaking the "privacy by default" property of Monero. With this change, it is theoretically possible to have a very active wallet with an entirely traceable history. Worse yet, exchanges may start requiring deposits to be zero-decoy transactions etc. It's a slippery slope that ends up down there with zcash.
This is by far the biggest drawback. New users may not be aware of the traceability of their transactions unless they research it themselves. Easy to use default privacy, without any gotchas, is one of the biggest reasons monero is so useful.
A solution that doesn't jeopardize this would be preferable.
A solution that doesn't jeopardize this would be preferable.
I'd even go so far as to say that only a solution that doesn't jeopardize would be acceptable.
I agree, that this limitation in every day usage is probably the biggest limitation, already starting when you show someone how a wallet works and get him to download one onto their device. While receiving is literally instant at almost no cost, most want to try themselves sending some back to you which obviously is only possible after 10 blocks.
Still I wouldn't think sacrificing universal privacy would be a good option and maybe with new protocols like Seraphis with possible ring sizes it would be feasible to drop the requirement anyway by selecting a bunch of decoys from very recent transactions, maybe even unconfirmed. The latter might allow for instant-chaining in tx pool which is common on Bitcoin.
However, this would for sure require much bigger ring sizes and most probably even more research on decoy selection.
This doesn't solve any problem apart from sending multiple transfers that lock all your outputs in a row... Even then, it's an awful hacky solution that no average person should be expected to do.
That was sorta my point, hence the use of quotation marks. I don't think the current proposed solution solves it either. To me, it's equally as hacky - it's essentially "hey, well, in order to make monero more like bitcoin, we can remove some of the things that make monero monero."
I whole heartedly agree that the 10 block lock is annoying and at odds with our ideal of money of the future, but this kind of solution seems like ... not a solution.
Hell, it's entirely possible that at some point the network hashrate is so high that it makes reasonable sense to reduce the 10 block lock to something lower... again, this isn't really a solution, but it's a hack that, in my opinion, doesn't chip away at monero's core protocol for the sake of convenience.
I mean, at the surface, I kinda get it... like, these 0decoy transactions would essentially be treated as a pass-through on a tx graph. Like, yeah, tx A is linked to B right here, so it would just be clumped. So A -> B -> C would essentially be A -> C. So the default privacy for the user would depend on the users prior activity to ensure the privacy of the given transaction?
And B wouldn't be a candidate used as a ring member for other users.
So basically an attacker could flood the chain with 0decoy transactions, unless there's some limit on how many can be included per block, and then.... and then..... and then....
I dunno. To me, its like there's this well-oiled machine. And we're debating whether we should remove this cog over here because, well, is it really necessary all of the time? I dunno.
Ultimately I hope my point thats being driven home is that, yes, I think this is an issue, but I don't think this is a solution. But I haven't done any academics on the topic. Need some simulations and demonstrations and confabulations and orchestrations and all the ions.
@LocalMonero @AlejandroGuariguata the idea as written is to effectively allow for transactions "without" ring signatures within a 20 (or so) block window. There are going to be severe privacy drawbacks with this if unaddressed.
This writeup describes some methods for mitigating these issues, though allowing this user experience is strictly worse for privacy, and even with mitigations, it's certainly problematic.
I'm not one to barge around saying that this privacy/UX tradeoff is completely horrible; clearly some applications like Haveno would benefit from not having to wait 10 blocks. However, the implications are worrying, even without getting into slippery slopes resulting from 2 classes of transactions. Earning this UX improvement comes at a privacy cost, period.
Earning this UX improvement comes at a privacy cost, period.
It comes at a privacy cost as far as you know. The point of this MRL topic is to find ways to solve this problem without an unacceptable privacy cost. Hopefully, as this issue gains more traction we'll have more people looking into this problem and coming up with potential solutions.
I've been holding off replying since we'd need to hear from researchers and OGs like articmine, othe, Isthmus, the Noethers, luigi, etc on this one., but it struck me as unviable to begin with. Looks like an attack vector for a sufficiently well funded party and it's in place for a reason. As long as we're saying "there's no point to even researching chainable payments because of the possibility that it could harm privacy or segment outputs", I'd say we ought to be applying to same standard to any other decision, and we already know that zec shielded vs non-shielded does harm its privacy in practice. Monero's also an actual target, especially with real legitimacy and such low fees.
There does exist another alternative here. It doesn't entirely solve the problem, but I think it would get us pretty far. I even already have beta code for it which I intended to publicize soon, and have run it by a few people over the years.
At the moment, lots of e.g. pools will pay multiple pool members at a time with as few transactions as possible by including multiple destinations into the transactions. Meanwhile the majority of wallet software is creating transactions with two outputs - one going to the real recipient and one going back to the sender as either change or a dummy zero amount output to remain indistinguishable from txs with change. If you look at Isthmus's plot of the distribution of number of outputs generated by transactions (2-16) from ... 2019 or so, you can see maybe 20% have 16, maybe 60% have 2, and then there's a bunch of them in between.
At the moment we're using our lack of knowledge about the sources of these differently formed transactions as a proxy assumption for indistinguishability, but chances are, these populations are somewhat segmentable already.
Suppose we solve this problem at the same time as providing balances which are more likely to be liquidly spendable by creating a consensus rule that transactions always must have the Bulletproofs maximum of 16 outputs, and use, say, 6 of those extra outputs each transaction to decompose the change which is being sent back to oneself rather than lumping it into a singular change output which gets locked each transaction?
My team has done some analysis of this over the past couple years - I've had two CS grads work on it very lightly - and we found it actually allowed us to make an enhancement to the code quality around create_tx related to resolving the conflation of specification of the 'change addr' and the 'dests' passed in - this is incidental - but the analysis showed us that indeed the 16 output transactions do increase the size of each tx slightly, but that tx size increase does result in a higher payout to miners, as well as as plugging one of the last remaining fungibility gaps we have, aside from, I'd say, ring sigs themselves.
What this would do is allow a wallet to accumulate "coins" and "notes" of different denominations in the form of multiple change outputs over time and for subsequent transactions, at the very least, the wallet software may have available to it some outputs to use at that time which are not already locked up in another transaction.
I recognize there is quite a lot of refinement which can be done in terms of selecting the denominations of the change outputs we're creating. At the moment our first-pass code only really just splits up the change into the set of outputs. I recall the wallet tx creation output selection algo already handles multiple existent outputs well.
By the way I suggest padding to 16 outs on txs rather than randomizing the number of outputs on txs because I believe this ends up with optimal privacy, since, for example, if we only randomize with an upper bound k of 16 minus the number of non-padding outputs which itself clearly varies across the userbase, you still end up with the question of how you harmonize your randomization with the networks', so 16 seems optimal.
What this suggestion does not solve is the fact that an output you have just received would remain locked until confirmation.
There are some concerns I'm probably leaving out here but the actual code change to do this is pretty straightforward and modular, with only that dests modification I mentioned before required to mainline-crypto-core itself.
I first thought of this a couple years ago, but wasn't able to release the work publicly yet (soon[tm]) but I believe Monerujo is also working on something that sounds like this called PocketChange.
My initial concern with @paulshapiro's suggestion is that combining large numbers of inputs is also not great for privacy. If you have a TX that creates 16 outputs, then later recombine 6 of them, you'll have 6 separate ring sigs that each contain an output from a single previous TX. I think this would be a pretty strong heuristic.
I suppose one thing that might be done, is to have the decoy selection algorithm sometimes select decoys that also match this pattern; taking 6 outputs from a single TX and including them 1 per ring as decoys.
I'm not sure how well this would work with ring size 11/15 but maybe with Seraphis.
@paulshapiro thanks for sharing your interesting thoughts! However, I have a similar concern like @MoneroArbo since there are plenty of "mini" and "micro" tx, just think of pretty common uses paying for a VPN, VPS, domain or such. Splitting the change into 15 would mean for a user having 1 XMR as one single output having it split up into 30 after just two transactions, so you'd have to combine a pretty large bunch of them at some point and its most probably small output would be split up again into 15 even smaller ones. Combining a large bunch of (previously split up outputs yourself) is imo a high privacy concern, since you have a clear pattern in all your rings.
We'd need some research on that, with some simulations. Maybe instead of having at least two outputs, three would be better, already mitigating a lot of instant spending problems, since your change outputs would grow over time and combining would not impact privacy so much?
@janowitz small correction, unless changed the standard wallet implementation prefers to create 2-input TXs when possible so in those cases the number of outputs would stay the same: 2 inputs consumed, 2 change outputs created. I think that's actually better though; it only makes sense to break a single output into change so many times. At some point you need to receive outputs that increase your balance.
Also they actually suggested 6 change outputs, not 15, and I think even that suggestion was arbitrary. The rest would be dummies, like the current empty output that's created when you have transactions with no change.
PS maybe this is too off topic since it's not technically about eliminating the lock?
All apparently valid and good observations, @MoneroArbo and @janowitz. I did not anticipate the output-group linkability thing, which seems obvious in retrospect, but again, likely is still an issue in many cases (people are certainly already combining multiple outputs in ways that are output-group-linkable). So it seems we should do some simulations and check how things turn out. The concern of accumulating and having to combine a huge number of outputs to accomplish a given transaction is there regardless, and should be managed at the output selection level, where the algo decides whether it wants to receive more change and/or combine multiple inputs for a given tx to manage that.
However, the 3-output solution doesn't seem to really address the fungibility concerns for all the other users who need to send to more than one destination in a given transaction.
I would agree that the granular discussion should be taken to a new thread, from which this thread should be referenced.
is there anything wacky that the new keys with all their different abilities can somehow tackle this issue? Or some key we didn't know we needed in order to get at this?
like, you send a blob that is a pre-transaction. It has everything but the ring members or some of them. Like, you send over ... gah I think im talking about payment channels ultimately. Because the question becomes well how does the sender guarantee that they won't craft their own transaction to counter the pre-transaction they sent over.
but we could use some key so the receiver could know "ok this pre-transaction block checks out, the sender definitely has the monero needed for the transaction, but its obviously locked up in the 10 block lock because ring sigs are sooo much fun, so once that 10 block thing passes, this transaction blob will fire off".
i guess that doesn't permit chaining, per se. .. well it could, but then you end up in a weird payment channel thing.
somewhere im not.... scatterbrained.....
and also, in general, this might be a thing that is best solved on the mythical second layer.
The most important aspect of Monero is privacy. As far as I can tell this proposal would, in some way or the other, reduce the privacy or even could become a possible attack vector against Monero's privacy. That is a no go, even if it is implemented as an optional feature.
If this could be achieved without implications, it would be a great UX improvement and should be embraced.
In the meantime there might be a possible 'higher layer' feature which could be developed or made more accessible to users as a (partial) workaround to lessen the impact.
I do not comment here much, but I am also a long time Monero user. This suggestion on it's face seems like a good idea for the reasons stated, but I think we must be EXTREMELY careful tinkering with the elements that make up the fundamental security of Monero's privacy. A great deal of import is recently been put on Monero's use as CASH which is understandable since fungibility allows cash like transactions more than many other base properties of a crypto currency.
But we MUST be extremely careful not to break Monero's privacy. That is a tradeoff (particularly in the name of convenience) that I think is entirely not worth it.
I look forward to the input of some of the longer standing Monero community cryptographers and researchers on this very interesting, but potentially dangerous(?) idea.
Everyone involved in the conversation until now agrees that Monero's privacy shouldn't be lessened by the removal of the 10-blocks lock. Please let's keep that in mind when commenting. What we need is to find a way to make it happen without impacting the privacy of Monero users. Not easy and not clear if it will be possible, but as far as i can see we all agree it would be a huge improvement.