Skip to content
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

Removing/Fixing/Encrypting monero's timelocks #78

Open
TheCharlatan opened this issue Oct 21, 2020 · 54 comments
Open

Removing/Fixing/Encrypting monero's timelocks #78

TheCharlatan opened this issue Oct 21, 2020 · 54 comments

Comments

@TheCharlatan
Copy link

Following discussions during a #monero-research-lab meeting, see logs here, there seems to be some support for removing monero's timelock implementation. This issue should open further discussion on this topic. There are future use cases, for example payment channels and payment channel networks, that require some form of timelock, as described in the DLSAG paper.

Monero's timelock is used with the unlock_time field, which is available in every transaction. By default it is always 0. A user can choose to either set it to a block height, indicating until which block all the outputs of the transaction remained locked, or a timestamp, indicating until what time all the outputs should be locked. A user receiving an output in a timelocked transaction can only spend this output once it has expired.

The unlock_time field has a host of problems. I detailed most of them in a series of blogposts: https://thecharlatan.ch/Monero-Unlock-Time-Privacy/.

The problems can be summarised in short:

  1. 98% of its usage does not have semantic meaning. These are unlock_time values between 1 and 15 that have been expired shortly after genesis. Their usage does not lock up any monero. This weird pattern potentially allows linking these transactions to a single service.
  2. Actual legitimate usage is really low. The 2% are a total of 195 transactions in the past 1'000'000 blocks that use the block-based locktime. Exactly one transaction used the time-based locktime, a test transaction by Mitchell.
  3. The ring member selection does not take the timelock values into account. An output mined a long time ago, but only recently expired is treated the same as any other output mined with it.
  4. The timelock locks an entire transaction. Not just the recipient's outputs, but also any change or outputs of other recipients. If the lock is moved to a single output, change outputs become distinguishable.
  5. The value range is very big, allowing to lock transactions until close to the heat death of the universe.

The unlock_time can be encrypted, which solves all these issues, but costs about a doubling in transaction verification time and a significant increase in transaction size. With current usage being so low, this is a high price to pay. If a legitimate use case arises the price becomes more acceptable though.

Alternatively, there are a number of things that can be done to improve the situation without increasing transaction size or verification time:

  1. Use a more compact representation. Sensible values can be conveyed in as little as two bytes - a significant improvement over the current varint, which can represent up to a 8-byte/64-bit number
  2. Only use block_height based unlock_time
  3. Repair the ring selection by taking the unlock_time of selected outputs into account
  4. Forbid the unlock_time to be set in the past apart from the 0 value

Even with these improvements, usage patterns can still emerge and leak significant information, for example identifying transactions from a special wallet that always sets the unlock_time 150 blocks into the future. I therefore support removing the field. If an actual use case appears in the future, timelocks canbe re-added in a fashion best fitting this use case (and hopefully encrypted).

@SamsungGalaxyPlayer
Copy link

I'm currently cautiously for the removal of the timelock field. In no case would I recommend adding encrypted timelocks in current form.

@apertamono
Copy link

Yes, it makes sense to remove the timelock field and not to re-introduce it in encrypted form until we know what kind of precision is needed for a relevant use case.

@iamamyth
Copy link

I've long disliked the current unlock_time semantics, precisely because they try to anticipate a use case by being overly generic and complicated, as you've stated. If the implementation were sound, but unused, I could see a case for preserving it, but, given that it has serious flaws, I favor scrapping unlock_time.

@apertamono
Copy link

Just wondering, and this isn't a reason to keep it, but are there any practical objections to take into account, e.g. tools like explorers expecting the timelock field?

@iamamyth
Copy link

I think it makes sense to keep the field in any existing RPC interfaces, as unlock times did (on rare occasion) exist for old blocks. So, there wouldn't be a compatibility problem. But, the RPC documentation should be updated to reflect the fact that the value will be fixed for all blocks after a certain point. And, care must be taken not to introduce an identically named field with alternate semantics later, as that could be a recipe for serious problems in existing clients (the clients could, of course, alter their interpretation of the field based on height, but that approach seems more likely to introduce errors).

@ghost
Copy link

ghost commented May 20, 2021

We used the timelocks as a way to verify the amount in the donation fund aeonix/aeon#215. So not totally useless, yet also still very confusing how to use it. Would be better to just lock for n blocks rather than so many different behaviors. If there is a timestamp preferred better to have a separate function lock_time lock_blocks. Then no confusion.

@b-g-goodell
Copy link
Contributor

b-g-goodell commented May 21, 2021 via email

@iamamyth
Copy link

I think the issue wouldn't be whether the feature has a possible use, but whether that use justifies the overall code complexity (and also, relatedly, whether removing it likely increases long-term complexity due to likelihood of future re-introduction). Proving wallet balance via timelocks doesn't seem terribly compelling to me, one can achieve a similar end by simply executing periodic send-to-self transactions and publishing a viewkey. I suppose one can argue that timelocks actually improve privacy in this scenario, as they allow the daemon to reject guaranteed timelocked transactions, a database of which could be assembled for analysis, similar to mining pool payout data (note this argument also applies to encrypted timelocks).

Atomic swaps could require timelocks, but, to my knowledge, a lot of other pieces would have to fall into place for such a scenario to arise (i.e. no current atomic swap proposals use them on the Monero chain, depending instead on the alternate chain's timelock implementation).

@UkoeHB
Copy link

UkoeHB commented Sep 4, 2021

Unlock times have another drawback: they don't play well with deterministic ring member selection (see Issue #84).

Since any output could potentially be locked, if you deterministically select a locked output to be a decoy, then you have to rewind tx building to try a new ring member generation seed. That alone isn’t catastrophic, but binning makes it a big problem. What if the local outputs around the output you want to spend are all locked? There might not be enough decoy outputs available to make a bin with the real spend in it. This opens a serious attack vector.

There are a few solutions:

  • Use hidden timelocks. Costly, but effective.
  • Only append locked outputs to the set of unlocked on-chain outputs (for ring members selection) when they are unlocked.
    • Possible poison attack: make many poison outputs that unlock at the same time.
  • Deprecate timelocks altogether (as recommended by this issue), and use the 'append at the end' method for coinbase outputs (which have a 60-block lock time enforced by consensus).

@Hueristic
Copy link

* Use hidden timelocks. Costly, but effective.

Effective?
Wouldn't this just increase cost of the attack as well which would not put it out of scope of some adversaries?

@UkoeHB
Copy link

UkoeHB commented Sep 5, 2021

Effective?
Wouldn't this just increase cost of the attack as well which would not put it out of scope of some adversaries?

Hidden timelocks are hidden, there are no attack vectors. They would be enforced by range proofs.

@j-berman
Copy link

j-berman commented Sep 22, 2021

Current Status

Discussion on what to do with Monero's current timelock feature is ongoing. In the last MRL meeting, there seemed to be some support for deprecating the feature in either the hard fork after next, and/or in the hard fork that would bring the next major tx protocol change (such as Lelantus/Seraphis/Triptych). Deprecation is supported because of the feature's potential to introduce privacy issues, and its lack of compelling use cases. There also seems to be support that if a compelling use case is known that utilizes the feature, then it could be brought back (or kept).

In the rest of this post, I'll:

  • present my view of pros/cons for deprecating vs. keeping as-is vs. encrypting
  • highlight known use cases for Monero's timelocks
  • highlight how the proposed protocols/implementations for atomic swaps and payment channels on Monero work around the Monero timelock's limitations

Pros/Cons of Deprecating/Keeping/Encrypting

Deprecating

Pros

  • eliminates issues of privacy arising using timelocks (discussed in greater detail here)
  • allows for a safe/smooth implementation of "binning", which seems to be a generally supported upgrade for the decoy selection algorithm (discussed in greater detail here (6.2),Ring Binning #84, and Decoy Selection Algorithm - Areas to Improve #86)
  • eliminates griefing vector for malicious party to send unspendable outputs to a service provider who does not correctly check unlock time (described here)

Cons

  • gets rid of the known niche use cases discussed below
  • makes it more difficult to bring back if some compelling use case emerges

Keeping as is (& enforcing sensible minimum as proposed here)

Pros

  • enables niche use cases discussed below
  • researchers/programmers may be more inclined to consider the feature for some compelling use case in the future

Cons

  • maintains some issues of privacy arising using timelocks (discussed in greater detail here)
  • makes a "binning" strategy in the decoy selection algorithm marginally more dangerous/complex/challenging to implement (reasoning highlighted here)
  • maintains griefing vector for malicious party to send unspendable outputs to a service provider who does not correctly check unlock time (described here)

Encrypting

Pros

  • eliminates issues of privacy arising using timelocks (discussed in greater detail here)
  • allows for a safe/smooth binning implementation
  • enables niche use cases discussed below
  • researchers/programmers may be more inclined to consider the feature for some compelling use case in the future

Cons

  • roughly doubles transaction verification time and is a significant increase in transaction size
  • development time/adds some complexity to the code & consensus
  • the use cases for the feature aren't very compelling yet
  • maintains griefing vector for malicious party to send unspendable outputs to a service provider who does not correctly check unlock time (described here)

Known use cases

Proof of unspent funds

As highlighted above, timelocks can be (and have been) used to have an entity prove they have unspent funds.

The prover constructs a tx where the amount of funds are locked in an output until block X, then the prover provides the tx key for the transaction before block X to a verifier.

Also highlighted above, a prover could instead re-construct tx's and provide tx keys (or a view key) on request, without needing to lock outputs at all. However, this has a privacy issue where publicly revealed outputs are more likely known spent via this method (e.g. you had this much Monero then, and likely re-sent that output to yourself in this tx = link from this tx to prior tx = known spent output on chain).

Subscription model

This is a creative idea with some known privacy/UX/technical challenges shared by @moneromooo-monero in #monero-dev. Let's say a user wants to sign up for a subscription service, has all the Monero up-front ready to go for the service, wants the option to pay monthly, and wants the option to cancel the subscription at any time.

A user could sign (and not broadcast) a number of transactions that each have 2 inputs. 1 input is a normal output, and the other input in each transaction is an output that is locked until the start date of each month's billing cycle. The user could then send all the transactions over to the service provider. The service provider could then broadcast each transaction at the start of each billing cycle. Before each transaction is broadcast, the user has the option to spend the normal output in all the transactions sent to the service provider, which would effectively render the pre-signed transactions impossible to broadcast to the network, thereby "canceling" the subscription.

The privacy issues with this approach are:

  • unless there is a pool of a bunch of locked outputs (that are locked until the same exact date of the start of each billing cycle) that the user can use to construct the transactions in advance, the service provider would be able to deduce which output is the one being spent in each transaction (by seeing which output is locked). Therefore linkability to a prior tx is broken.
  • transactions constructed this way would have a particular trace on chain (their rings would reference outputs way in the past, rather than recent outputs). This affects sender/receiver privacy, and affects other transactions on chain.

UX challenges:

  • user would need to submit an equivalent number of transactions to create outputs that lock until the specified dates.
  • user would need to re-do the process once all pre-signed transactions have been broadcast.

An alternative way of supporting a subscription model would be for a wallet to support an "auto-pay" feature that would send out payments to intended recipients when the wallet is running.

Extreme budgeting/exec payouts

Similar to the concept of "vesting" and/or restricted stock, you could send an output to someone that does not allow them to use it until a later date. However, unlike restricted stock, the sender can't reclaim the the funds after sent.

Misunderstood non-use cases for Monero's timelocks

In the latest MRL meeting and in ensuing conversation, there was also some confusion over the use cases that Monero's current timelocks have (in atomic swaps/payment channels), so summarizing what I've surmised since then: Monero's timelocks as currently designed are limited to niche use cases, and are not useful for atomic swaps and payment channels AFAIU. The critical ingredient for swaps/channels is the ability for a recipient to claim the output before the timelock expires, and if not, the output is refunded to the sender. However, Monero's timelocks do not enable this. Monero's timelocks strictly lock an output for a specified period of time, preventing the recipient from spending the output until the timelock expires.

Atomic Swaps

@iamamyth appears correct in saying the current swap implementation only relies on Bitcoin's HTLCs, and not Monero's timelocks. The protocol uses adaptor signatures to work around Monero timelock limitations. From the COMIT paper:

Within this work we present our current efforts on cross-chain atomic swaps
using adaptor signatures. In particular, we show how adaptor signatures can be
employed to swap between Monero and Bitcoin. Notably, the former does not
support scripts or timelocks.

Also from Daniel over at COMIT:

we are not building on top of [Monero's timelocks] at the moment and I don't think there are any plans to do so. So I don't think we would be advocates for needing it (at least at the current point in time).

Payment Channels

DLSAG: Non-Interactive Refund Transactions For Interoperable Payment Channels in Monero

This would essentially be a fundamental change to Monero's tx protocol that would allow for refundable transactions, and would need a hard fork to support. Additionally, it's what introduces the concept of hidden timelocks in the first place. Basically, as I understand it, timelocks as currently constructed are not useful for the payment channels proposed in this paper without a separate hard fork that would allow for someone to claim an output before the timelock expires.

PayMo: Payment Channels For Monero

One of the main challenges that we face in constructing a fully-compatible [payment channel] for Monero is the lack of a time-lock functionality of its scripting language. To “simulate” this functionality off-chain, our constructions will resort to the usage of time-lock puzzles.

The basic idea behind time-lock puzzles is that a recipient would take a pre-determined amount of time to solve the puzzle off-chain, such that they can spend the output with the solution to the puzzle. It's a workaround to simulate the expected "refund" mechanism of timelocks, the mechanism that Monero's timelocks don't presently support.

EDIT: modified the "keep as is" option to "keep as is (& enforce a sensible minimum)"
EDIT2: clarified PayMo
EDIT3: added griefing vector described here

@Rucknium
Copy link

@j-berman For the subscription model, this approach may yield some insight:

https://bitcoincashresearch.org/t/introducing-arcc-allowable-revocable-contract-chain-system-for-bitcoin-cash/522

@r4v3r23
Copy link

r4v3r23 commented Sep 22, 2021

Subscription model makes no sense - no subscription I've ever had locks X amount of payments ahead of time.

@UkoeHB
Copy link

UkoeHB commented Sep 22, 2021

Subscription model makes no sense - no subscription I've ever had locks X amount of payments ahead of time.

The point is - how can you have scheduled payments that can be canceled (i.e. without pre-paying all months) in Monero? The method he described is one of the few available methods, even if not very appealing UX.

@iamamyth
Copy link

I agree that the subscription use case makes very little sense: The subscriber needs to record which transaction to broadcast in order to cancel the subscription, meaning there must be client-side support for subscription tracking and cancellation. If the client must perform such bookkeeping, it seems easier for it to simply record addresses and frequencies of subscriptions, and auto-pay at the appropriate time.

The following is not, and should not be considered, investment advice: I think the "vesting" concept strains credulity, as vesting schemes involve selling an issuer's contractual right in a common enterprise. There's no common enterprise here, and minting Monero has a cost, so why would anyone deliberately lock it away? The act of locking itself introduces a negative return, which isn't really the case with unvested stock.

@r4v3r23
Copy link

r4v3r23 commented Sep 22, 2021

Subscription model makes no sense - no subscription I've ever had locks X amount of payments ahead of time.

The point is - how can you have scheduled payments that can be canceled (i.e. without pre-paying all months) in Monero? The method he described is one of the few available methods, even if not very appealing UX.

Features should reflect real world habits and use cases. No need for complicated UX to solve a nonissue - if you have the funds for a yearly subscription upfront you usually get a discount to pay in full. This is one instance where the market decides.

@UkoeHB
Copy link

UkoeHB commented Sep 22, 2021

Features should reflect real world habits and use cases.

Ok... academically, it is our responsibility to enumerate theoretical use cases. After enumerating them, then you can assess practicality and usability. Which you have been doing, but I want to be clear that @j-berman is not in the wrong for discussing subscriptions.

@r4v3r23
Copy link

r4v3r23 commented Sep 22, 2021

I understand, but no one sets aside a year worth of payments upfront. Otherwise they'd just pay in full.

The idea of recurring payments is interesting, but I don't think locking up funds for future payments is the solution.

@iamamyth
Copy link

I think you're missing an important downside to timelocks: They require recipients to check the timelock status of received funds. I've seen cases where service providers fail to implement such checks and end up with effectively unspendable funds.

@j-berman
Copy link

To be clear, I'm also in favor of deprecating Monero's timelocks as is. I think the privacy/safety benefits far outweigh the potential niche use cases. I'm just trying to give as much weight as possible to the counter-arguments. I figure it's best to be cautious in deprecating a feature. Perhaps we could give it another month or so, and if no compelling counter-arguments emerge, we could more officially pencil it in for deprecation in a later hard fork (not this hard fork, but the one after, or the one with the tx protocol change). Most seem strongly in favor of removing it when it's brought up for discussion in #monero-research-lab/#monero-dev as well.

In any case, I also agree the subscription model described (and "vesting" concept described) is (are) probably not useful in practice. Perhaps the ideas may inspire other ideas and seemed worth sharing to be comprehensive. You could in theory combine the concepts to have a "cancelable" payout schedule for whatever purpose, however, I believe the core of the privacy/UX challenges of that use case remain, and there are probably better ways to support that use case anyway. I would agree that delayed cancelable payouts (with significant privacy/UX challenges) probably not compelling enough to support keeping the timelock feature when weighed against the cons.

So far it seems the "proof of unspent funds" feature may be the most compelling use case for keeping Monero's timelocks as is, though I personally feel the alternative methods of achieving this use case today are suitable. Fleshing out some alternative methods of achieving this use case:

Simply provide a tx key for a single new on-chain tx

A prover submits a new tx to themselves with an amount they want to prove, then provides a verifier with the tx key/address pair.

In @yorha-0x 's case for example, I'm not seeing why not simply say to the prover: "on future date X, please send yourself a tx containing the amount of funds you're in control of, and give us the tx key to verify." The drawback to this approach versus using a timelock is that a verifier can't be certain that the prover doesn't spend the output after the tx entered the chain, and before some later date. But is that really necessary?

As far as holding an entity accountable, I don't see why there is practical value to a verifier in ensuring the prover remains in control of the funds for some small period after the date the output was constructed. I also don't see major institutions (such as exchanges) locking up outputs for meaningful periods of time for the timelock use case to be useful.

get_reserve_proof / check_reserve_proof

This method allows a prover to prove control of unspent funds without an on-chain tx, and allows the verifier to know exactly when the outputs are spent in the future.

It's offered by the reference wallet and is documented here and explained further here. Copied from monero-wallet-cli's description of the command (and that 2nd link):

Command usage: 
  get_reserve_proof (all|<amount>) [<message>]

Command description: 
  Generate a signature proving that you own at least this much, optionally with a challenge string <message>.
  If 'all' is specified, you prove the entire sum of all of your existing accounts' balances.
  Otherwise, you prove the reserve of the smallest possible amount above <amount> available in your current account.

Basically the proof is composed of all (or < amount >'s worth of) the unspent outputs in a wallet, the key images for those outputs, key image signatures to prove the outputs correspond to the key images, and shared secrets to know the outputs were sent to a given address.

The drawback (or pro, depending on context I guess) with this approach is that a verifier will know exactly when the outputs are spent in the future. There are some (imperfect) mitigations to this drawback, but I feel it would be out of scope of this issue to get deep into it.

Conclusion

I don't think the timelock adds much practical value compared to alternative methods of proving unspent funds.

@ghost
Copy link

ghost commented Sep 24, 2021

That is good. Timelock proof of funds is one use case but that is confusing because it is not the intended use. The reserve proof is better. Optionally privacy for spent outputs could be added.

I can believe vesting would be a useful tool in what @j-berman says. Proving you have a vested holding in monero for a set duration of time in order to perform some role.

@carrington1859
Copy link

carrington1859 commented Oct 2, 2021

Regarding the get_reserve_proof method and the drawbacks, it seems that if you were being audited you would first consolidate you inputs and then go through a churning process if you didn't want the reserve proof to impact you past/future privacy.

I've seen no compelling case for keeping timelocks.

@Minthos
Copy link

Minthos commented Oct 4, 2021

I keep most of my stack timelocked at all times to force myself to hodl and protect against the $5 wrench attack. I agree the interface is clunky but I would prefer to keep the functionality.

@meordeuw
Copy link

meordeuw commented Jan 27, 2023

Hi

I have to express my opinion because I think it would be an important loss for Monero if this frozen transaction feature was removed.
I agree with the fact that this feature is currently misused and that it negatively impacts privacy, but to solve this problem it is sufficient to make a change that will discourage creation of frozen transactions. For example we could require that frozen transaction have 2 times higher fee. Then this feature would be used only when it is really needed and it would not be misused. Alternatively, we can require minimum freeze time (for example 20 block after current block). That would also eliminate misusing. And then this feature would no longer have a significant impact on privacy.

I will describe two use cases which are very useful to me.

First use case - tax law.
In my country, the tax law requires that I pay percentage tax each year from all my assets (wealth tax) including cryptocurrencies. But if I froze my Monero for 10 years (send a transaction with future arrival time 10 years later), I don't have to pay tax from my Monero for 10 years. I have to pay tax only once 10 years later instead of 10 times (each year) - but this is much smaller tax. During those 10 years I don't own Monero so I don't have to pay tax. This is not cheating, this is legal. Even if I wanted to pay tax, I can't because I don't have money. If I frozen 1000000000USD in Monero for 100 years, i wouldn't have to pay huge tax for my entire life. It would be really stupid and immoral law if it was required. Someone malicious could send such frozen transaction to my Monero address (or malware on my computer) and I would go to the jail because I don't have money to pay tax - nonsense.
Some people freeze their cryptocurrency for 5 years because they know that 5 years later they will be living in country where there is no cryptocurrency tax. Other people froze their funds because they don't want to pay tax and don't want to commit crime by hiding crypto from government and they consider moving to another country with no tax if the cryptocurrency value increases significantly.
Some people froze crypto for retirement and thanks to that they don't have to pay wealth tax their entire life each year.
Some people hope that tax law will be changed when their Monero unfreeze.

Second use case - bankruptcy law.
In my country, when I have debt and I can not pay the debt, then I declare bankruptcy and that means that I lose all my assets and all my debts are forgiven. This means that I would also lose all my Monero (if it wouldn't be frozen!). If there is frozen Monero transaction in blockchain with freeze time 10 years into future, that means I still don't own this Monero. I will own this Monero only 10 years later. If I declare bankruptcy now (not 10 years later) I don't have to give my Monero, because I can't! And my debts still are forgiven now.
Knowing that I can freeze my Monero, I can sleep peacefully, knowing that I will not lose them if I fell into debts.
But if this feature were deleted, I would not be able to sleep peacefully anymore holding Monero. I would have to convert my Monero into Bitcoin, because in case of debt collection, I couldn't use centralized exchange to convert because of KYC.

Please don't remove this feature. Even if the fee for frozen transaction was 100 times higher, I would still be using it. Converting Monero to Bitcoin in order to freeze Bitcoin is much more expensive. And I need Monero privacy features. I really need this feature.

@tevador
Copy link

tevador commented Jan 27, 2023

If there is frozen Monero transaction in blockchain with freeze time 10 years into future, that means I still don't own this Monero. I will own this Monero only 10 years later.

I'm not a lawyer, but this sounds dubious. There are fiat-denominated term deposits which work in a similar way and I'm pretty sure the depositor is still considered to be the owner although they can't withdraw the funds.

In any case, helping you avoid paying taxes/debts is definitely not something that would be considered a legitimate use case when deciding to deprecate the "time-lock" feature.

@meordeuw
Copy link

meordeuw commented Jan 27, 2023

I'm not a lawyer, but this sounds dubious. There are fiat-denominated term deposits which work in a similar way and I'm pretty sure the depositor is still considered to be the owner although they can't withdraw the funds.

I'm just saying what law is in my country. I don't say that this law is everywhere. I'm also not 100% sure that this is true so I'm not telling the name of my country, because I don't want that someone go to jail if I'm mistaken and I don't want to have fiscal control, but I'm using time locked transactions and I'm not paying taxes legally. And I'm convinced that this is not tax avoidance or debt avoidance. In my country it is commonly believed that this is fair and not immoral and not unfair.
People know that if they lock their money, they don't have to pay taxes. This applies not only to cryptocurrencies but also to fiat money for retirement. Our government provides special system for freezing money for retirement and people who use them don't have to pay tax (completely legally). And debt collector in our country also can not take this money which was frozen for retirement.
People in our country live peacefully knowing that our money frozen for retirement is safe (even if we have debt). People who froze Bitcoin for retirement in our country also live peacefully. People using Monero also should have such possibility. I know that maybe in other countries law is different and maybe this causes privacy issue, but i think that higher fee for frozen transaction is best trade-off. It will be used only rarely when it is really needed.

@unknowntrojan
Copy link

Has there been any notable increase in usage of timelocks, or is the feature just as niche as when this issue was first posted? Have some of the discussed usecases moved off-chain? From what I've seen discussion has basically stopped around the topic for the time being. I believe the discussion around timelocks should go for a second round, to determine whether it would benefit Monero to leave it alone, remove it, or change it in a way that alleviates some of the issues raised and allows us to implement some of the things the original timelocks were unsuited for.

@jeffro256
Copy link

jeffro256 commented Oct 3, 2023

After studying the decoy selection algorithm in wallet2, @Rucknium and I made an observation that further warrants the removal of transaction timelocks. Because of the possible existence of timelocked transactions, wallet2::get_outs picks more decoys than necessary for each ring (assuming some may be locked and/or have EC points not in the main subgroup), according to a gamma distribution, but does so without replacement. Then, after the daemon returns the information about all picked decoys, the wallet filters out the time-locked and/or malformed outputs, then picks against those outputs according to uniform random distribution, also without replacement.

The effect of choosing M elements of D elements according to some distribution without replacement, then choosing N elements from those M elements uniformly at random has the effect of distorting the distribution of your N picks towards a uniform random distribution. What this means for users is that sender-privacy is slightly degraded depending on how noticeable this statistical effect is.

To this, one could respond with a reasonable question: "Why not just change that one specific wallet implementation?" Certain solutions have different downsides, but all downsides could disappear if all outputs on-chain before a certain height are usable for decoy selection or known to be unusable ahead of time. In other words, making solid non-fingerprinting decoy selection implementations is much, much easier if time-locked outputs are not something that we have to worry about.

Let's say that we forked/changed relay rules to ban unlock times in future transactions. Assuming that we still honor the unlock times of past transactions, we obviously still need to account for timelocked transactions in future decoy selection algorithms. However, since we know that only ~0.01% of transactions used unlock_time, we could reasonably store a static list of every custom-locked output index to wallets when selecting decoys, with the information only taking up a couple dozen kilobytes, many times smaller than the RCT output distribution. Then, when doing the actual decoy selection, we pick exactly the number of decoys we need, skipping over unusable outputs, which we know ahead of time.

It is not currently possible to know which outputs are unusable before/during decoy selection without adding more database tables
and RPC calls since the list of custom-locked outputs may grow arbitrarily large.

@johnr365
Copy link

johnr365 commented Feb 10, 2024

@jeffro256 - thanks for the summary, ie:

"What this means for users is that sender-privacy is slightly degraded depending on how noticeable this statistical effect is."

My understanding is that time locked transactions are currently very limited in use.

In terms of the actual privacy implications for the network and transactions currently, is there a way to quantify this?

For example, of x% of transactions implicated, we see a privacy degradation of y?

@dimalinux
Copy link

@jeffro256 - thanks for the summary, ie:
In terms of the actual privacy implications for the network and transactions currently, is there a way to quantify this?

I assume the privacy implications right now are negligible. The question is what will happen if someone, knowing this issue, decides to start spamming the network with time locked outputs for some time period X before a payment that they know is forthcoming. The decoy selection algorithm favors recent outputs, so they might be able to create a privacy impact that is quantifiable for some reasonable cost in fees.

@jeffro256
Copy link

@dimalinux Yes you would have to be the wallet's node and if you created a ton of time-locked outputs and the user makes an RPC request which contains all locked outputs except for one, there's a near guarantee that that user owns that output.

For example, of x% of transactions implicated, we see a privacy degradation of y?

I haven't done any kind of analysis like that unfortunately.

@Rucknium
Copy link

Rucknium commented Feb 14, 2024

I haven't done any kind of analysis like that unfortunately.

I just finished doing an analysis like that.

I'm using data on transactions since the last hard fork (August 2022) until the first week of this year (2024). The number of non-coinbase transactions with custom unlock times is:

unlock_time number of txs share
1 106 2.81
2 7 0.19
3 2777 73.74
5 1 0.03
6 4 0.11
9 112 2.97
10 478 12.69
12 1 0.03
20 1 0.03
1e+04 - 5e+08 265 7.04
1.4e+09 - 1.4e+12 14 0.37

These transactions make up about 0.036% of the 10.38 million transactions on the blockchain in this period. Remember that when unlock_time is less than 5,000,000, consensus rules interpret it as absolute block height. (When it is greater than 5,000,000, it is unix epoch time.) 93% of these custom unlock times have no effect since they are literally at block height 12 and below, i.e. blocks in 2014.

Privacy impact

I performed the same analysis for some of the unlock_times that I did for nonstandrad fees to evaluate the impact on the privacy of users who are creating transactions with these nonstandard unlock times. The Positive Predictive Value (PPV) is the probability that the real spend can be guessed. Please read my Discussion Note: Formula for Accuracy of Guessing Monero Real Spends Using Fungibility Defects for more info about how an adversary can attempt to guess the real spend when a wallet is producing nonstandard transactions.

unlock_time PPV beta mu_C
3 93.9 0.023 93.6
9 70.7 0.0009 68.7
10 34.3 0.004 29.9

The probability of guessing the real spend is as high as 94% and as low as 34%. With ring size 16, the probability of guessing the real spend randomly when the probability is equal is 1/16 = 6.25%.

Numbers revised Feb 14, 2024 21:00 UTC. In a previous version I accidentally excluded some transactions.

@expiredhotdog
Copy link

expiredhotdog commented Feb 14, 2024

I know most of the discussion is regarding Monero-style timelocks, but I'd like to bring up the possibility of implementing Bitcoin-style timelocks. For those unaware, nLockTime prevents a signed transaction from being accepted into a block before time/blockheight X (meaning, nLockTime has no connections to the UTXO set whatsoever), as opposed to Monero's timelocks which directly prohibit outputs in a mined transaction from being spent in a later transaction before time/blockheight X.

nLockTime would enable Monero-first atomic swaps, simple & robust payment channels (even a Lightning Network clone, maybe?), and other layer-2 systems.

nLockTime has much fewer privacy issues than Monero's system, and yet is much more powerful. There are still some privacy considerations, but I believe they could be overcome at relatively little cost. I just wanted to potentially start a discussion on this, so I won't go into possibly-unncessary details on that.

jeffro256 added a commit to jeffro256/monero that referenced this issue Feb 24, 2024
Related to monero-project/research-lab#78

Added a relay rule that enforces the `unlock_time` field is equal to 0 for non-coinbase transactions.

UIs changed:
* Removed `locked_transfer` and `locked_sweep_all` commands from `monero-wallet-cli`

APIs changed:
* Removed `unlock_time` parameters from `wallet2` transfer methods
* Wallet RPC transfer endpoints send error codes when requested unlock time is not 0
* Removed `unlock_time` parameters from `construct_tx*` cryptonote core functions
jeffro256 added a commit to jeffro256/monero that referenced this issue Apr 29, 2024
Related to monero-project/research-lab#78

Added a relay rule that enforces the `unlock_time` field is equal to 0 for non-coinbase transactions.

UIs changed:
* Removed `locked_transfer` and `locked_sweep_all` commands from `monero-wallet-cli`

APIs changed:
* Removed `unlock_time` parameters from `wallet2` transfer methods
* Wallet RPC transfer endpoints send error codes when requested unlock time is not 0
* Removed `unlock_time` parameters from `construct_tx*` cryptonote core functions

@tobtoht: undo rebase changes tx.dsts -> tx_dsts
@xmrrmxntom
Copy link

@Minthos @Cactii1

A Plea to Restore a Crucial Feature in XMR

As a computer science student and a long-time follower of XMR & Dr. Daniel Kim (sweetwater.consulting), I'm compelled to share my thoughts on a feature that I believe is important to the value proposition of XMR. I've created an account specifically to express my disappointment and frustration with the removal of the locked_transfers and locked_sweep_all feature.

A Personal Journey with XMR

I've been following the XMR project since 2018, and its value proposition was evident to me even back then. However, I wasn't technical enough to fully appreciate its features. This year, I became proficient enough to run a full node and use the CLI, which is when I discovered the locked_transfers and locked_sweep_all features. I immediately utilized them, and they have been invaluable to me.

As someone who has impulsively sold assets like NVIDIA, BTC, and TSLA before they reached their full potential, I've come to realize that XMR is a long-term play that will appreciate in value over the next 5-20 years. The ability to lock transactions for an extended period has been a game-changer for me, allowing me to make sacrifices that my future self will appreciate.

The Value of Locked Transactions

The locked_transfers and locked_sweep_all feature was a unique selling point for XMR, offering me the ability to make long-term commitments to the blockchain. By removing this feature, we're depriving users of a valuable tool that would help them appreciate the embedded on-chain hodl properties of the XMR blockchain.

A Call to Action

I urge the XMR community to reconsider the removal of this feature and to implement safeguards to prevent similar decisions in the future. Specifically, I request:

  1. Reinstatement of the feature: Bring back the locked_transfers and locked_sweep_all feature to allow users to make long-term commitments to the blockchain.
  2. Safeguards for feature deprecation: Establish a formal, non-trivial process or procedure to determine whether a feature should be deprecated, ensuring that user feedback and concerns are taken into account and valued.
  3. Improvement or alternative: If the feature cannot be reinstated, explore alternative solutions that would provide similar functionality and benefits to users.

Conclusion

As more users join the XMR community, they will come to appreciate the unique properties of the blockchain. I firmly believe that the locked_transfers and locked_sweep_all feature helps users HODL with the long-term success of XMR. I hope that my plea will be heard, and we can work together to restore this important feature.

A Final Appeal

I've gone from hearing about XMR as the real privacy-focused vision of BTC, to buying some XMR on an exchange, to self-custodying on Exodus wallet, and finally to downloading and running the CLI. XMR is beautiful, and it's idealistic. Please keep or reimplement this feature.

https://reddit.com/r/Monero/comments/mwrm6g/how_to_lock_send_future_monero_to_yourself_with/

This was the post and feature that motivated me to dedicate a weekend last semester to read the documentation, compile from source, and use the CLI.

…Please keep this feature…

@nahuhh
Copy link

nahuhh commented Sep 22, 2024

Monero (the blockchain) has 1 job. Digital currency.

Gambling on its fiat valuation is completely unrelated to Monero and is very poor reasoning to reinstate what is, among other things, a privacy harming feature.

If you want to force yourself to HODL:
A) learn impulse control
B) store funds in a paper wallet
C) store funds in a multisig
D) store funds with a custodian

@j-berman
Copy link

I've hit a hiccup in wallet side scanning for fcmp++ because of timelocks. TL;DR they can slow down full wallet scanning under fcmp++.

Context

fcmp++ works by proving you own an output (or "enote") within a special merkle tree called a curve tree. All valid, spendable (i.e. unlocked) outputs across the entire chain compose the leaves of the tree. When constructing the full-chain membership proof, the user proves their output is a member of the tree, without revealing which output it is. To construct the proof, a wallet needs the output's path in the tree (from leaf to root) at the latest block. The state of the tree changes every block, so an output's complete path in the tree changes every block as well.

I'm currently working on building the tree locally as the wallet syncs, so that wallets can update received outputs' changing paths while syncing (and can therefore construct fcmp++ txs without leaving any statistical trace to the daemon which output is being spent when the user constructs an fcmp++ tx).

In order to build the tree correctly, clients need to grow the tree with outputs that unlock each block.

The problem

Thanks to timelocks, an output's unlock block can be any block far in the future, so clients need to take special effort to keep track of outputs by unlock block.

Solution A

Clients download all outputs that unlock in a block in addition to outputs created in a block. This is bad because it's almost 2x'ing the amount of data clients download in order to sync.

Solution B

Clients locally keep a cache of locked outputs, and then remove from the cache upon inclusion in the tree. The main problem with this is thanks to timelocks, it's a potentially unbounded cache that clients need to store.

Solution B++

Keep track of valid normally locked (i.e. not timelocked) outputs in a persistent cache in the client, then remove from the cache upon inclusion in the tree. Modify /getblocks.bin to support returning timelocked outputs as they unlock in a block. This means clients will have to download almost 2x the data for timelocked outpus. Note: this solution could also be tweaked to avoid needing to re-download coinbase outputs.


If we remove timelocks at consensus, it closes the door to enabling excess scanning cost to clients via timelocks, and full wallets can reasonably keep a local cache of all locked outputs.

Note that keeping a locked outputs cache in the client would increase the client's space footprint by n * 72 bytes, where n is all currently locked outputs as of the latest block.

P.S. also open to any ideas on other solutions.

@UkoeHB
Copy link

UkoeHB commented Oct 16, 2024

Keep track of valid normally locked (i.e. not timelocked) outputs in a persistent cache in the client, then remove from the cache upon inclusion in the tree.

Why would this be needed if you are just growing the tree with every block received?

Modify /getblocks.bin to support returning timelocked outputs as they unlock in a block.

This seems the best option to me. Let the daemon keep track of which block an enote will unlock in. The main problem is potential DoS by locking many enotes to a single block. If timelocks are deprecated then this is no longer as big an issue because 'known DoS targets' can be hard-coded (if there are any).

@jeffro256
Copy link

Why would this be needed if you are just growing the tree with every block received?

You can't include transaction outputs inside the merkle tree until the moment that it is valid to spend them. So even for normal transaction outputs, you have to keep them around for 10 blocks before insertion. If they were added to the tree before, then the validators wouldn't be able to determine whether you were spending a "locked" output or an "unlocked" output. All they can tell is whether that transaction output is an element contained in the tree, or not.

Modify /getblocks.bin to support returning timelocked outputs as they unlock in a block.

The problem here for me is that this isn't easily verifiable to the wallet. Normally all content returned by /getblocks.bin (except for new output indices which will be not needed post-FCMP) can be hashed and validated as belonging to that block ID. This wouldn't be the case for transaction outputs of previous blocks, unless a sister/brother transaction ID merkle proof was included per-output. One of the main reasons for building the FCMP tree client-side is so that daemon trust is minimized.

@jeffro256
Copy link

Clients locally keep a cache of locked outputs, and then remove from the cache upon inclusion in the tree. The main problem with this is thanks to timelocks, it's a potentially unbounded cache that clients need to store.

I think solution B is fine give the current usage of Monero timelocks is niche and the fact that there is currently a relay rule in place to prevent future transactions from including an unlock time. We could initiate a soft fork before the FCMP hard fork to completely cement the rule in-place, or we could decide to not honor time-locked transaction outputs past height X during the building of the FCMP tree. Both of these would allow us to know the exact size of the wallet cache before deploying the code.

@j-berman
Copy link

Good point on the relay rule -- I also generally agree solution B will be acceptable in combo with a guarantee we'll know the size of the wallet cache before deploying.

We could initiate a soft fork before the FCMP hard fork to completely cement the rule in-place

This seems a cleaner break to me, rather than setting a precedent of voiding prior expectations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests