Improvement of ETH/ERC20 shielding design

You’ll note that the shielding flow in the app has been updated for better UX and security.

In this topic, I’d like to explain the historical shielding process for ETH/ERC20 tokens: what the original design and drawbacks were, the new improvements, and what we’re ultimately working towards. Additionally, we believe that an exploit was carried out last week that took advantage of a user who had a timed out shield request under the original design. This post will provide contextual information about the mechanism, detail the exploit, and explain the improvements - which are now live after the maintenance done last week.

Drawbacks of the original design

The original design was not ideal since it relied too much on the 2 hour window.

Specifically, there was a number of temporary addresses used for shielding requests, assigned in a round-robin fashion. These temporary addresses were in part designed to aid the user; each contained funds to cover the smart contract costs of the shielding action. However, the design came with its own set of limitations. Each shielding request could only own a temporary address within a specific time window (2 hours in this case). A temporary address could then be assigned to an incoming shielding request after the time window closed, or after the shielding process completed within the time window. At the beginning of a shield, the system would snapshot the existing amount held by the temporary address and watch out for an incoming transaction (its amount) sent by a user to the temporary address, in order to calculate the shielding amount.

That’s why we have always strongly suggested sending from a personal address that a user has direct control over, especially now that so many crypto platforms are experiencing delays due to high traffic. In the app, users had to confirm that they were sending from a personal address in order to continue, though there was no way to force them to do so. While this rather convoluted and inflexible process was and is overdue for improvements, instructions given on the shielding screen, when followed, would ensure the overall goal was successful.

If a user completed the request within the 2 hour window, funds would arrive safely because the beneficiary address would not yet be rotated and assigned to another requester (this beneficiary address is one of the set of temporary addresses mentioned above).

Unfortunately, there was recently a user who fell outside the window by several hours. Prior to this point, even timed out ETH/ERC20 shielding requests had been successfully filled before their temporary addresses were assigned to new requests. Via snapshotting as detailed above, and with help from vigilant users who reported any delays quickly, timed out shields could be retried with new temporary addresses to continue the original shielding process. This time, however, it is highly probable that an attack occurred which caused the user’s funds to be received by someone else.

Luck or malicious attack?

After an intensive investigation of the flow of funds as well as the behavior of the beneficiary account (a.k.a attacker’s account), we believe that it was likely a deliberate attack.

  1. The attacker continually created shielding requests to obtain a large number of temporary addresses and assign them to his fake requests.

  2. He then monitored all incoming pending transactions to these addresses over the Ethereum network.

  3. Once he detected one, he kept creating shield requests continually until he obtained a shielding address corresponding to the detected pending transaction. That also means the pending transaction corresponded to a timed out shield request, and the shielding address was available for rotation. At that point, when the funds arrived, the shield request of the attacker processed. In other words, the attacker took the funds of the user who had the timed out shielding request.

Medium-term improvements

As you may know, we paused the ETH/ERC20 shield feature for a week to work on improvements. It prevents similar attacks from occurring in the future, works towards a greater degree of decentralization, and more generally hopes to improve the overall shielding experience.

In this design, each Incognito account has a dedicated shielding address for all its shields. Funds may be sent to this address at any time without worrying about the address timing out. Once the funds land in the shielding address, a background job will kick in and start processing the shielding request.

This means that the shielding fees will be paid by the user; the system will deduct a part of the shielded funds to pay smart contract interaction fees. Depending on the gas price at the time of shielding, the system will calculate the fees in ETH. If ERC20 tokens are being shielded, e.g. DAI, the system will convert the ETH fee to DAI, then deduct the fees in DAI from the original funds.

This is now live. The latest app version is 4.3.8, please update your app in case you don’t have it yet. If you have any questions about the new shielding flow, feel free to comment below this topic or reach out to @support.

A longer-term decentralized solution

The medium-term design may help prevent attacks like the one described above, but a system that has any centralized elements always carries risk. A fully decentralized solution necessitates the complete removal of centralized parties (i.e., the temporary addresses). Work is already being done to this end, with more details to follow.

In the near future, a user will be able to shield ETH/ERC20 tokens right from his personal wallet, e.g. Metamask, Trust Wallet, etc. – and will no longer need to send funds to a temporary address managed by the Incognito team. A detailed timeline and specification will be published on the forum soon. The code will be completely open source.

24 Likes

Thanks for posting this!

Within the last several months I’ve had several shielding transactions fail despite it being confirmed on the blockchain within minutes. I have to wait until it times out and then click “retry”. In the case of a malicious actor it seems that the retry didn’t work for this user. This is very, very concerning that these transactions of mine might have been at risk even though the transaction was within the 2 hour window.

Can you clarify if the user shielded the coins within the 2 hour window or not?

1 Like

Greetings, just this morning I provided some Eth to my provide, but the balance does not reflect what I provided.

Can you assist me with this please

Thanks

Hey @marko, in the old design, if the funds land in the shielding address within a 2-hour window, it would be processed and safe for sure. In the case of the user mentioned in the post, the funds only landed in the shielding address 2 days later since the initialization of the shielding request which caused the unexpected situation.

In the new design that went live a few days ago, each Incognito account has a dedicated shielding address, funds may be sent to this address at any time without worrying about the address timing out. This might help prevent the problem from occurring in the future.

We’re also working on a decentralized solution already, the ETA for this is in 1 month, stay tuned!

Things I’ve described above are for ETH/ERC20 tokens shielding. I guess you talked about non-ethereum tokens shielding like BTC, XMR, etc, correct?. If so, for these tokens, the design is slightly different which every shielding request would have a one-time shielding address that is only used once and doesn’t rotate to assign for other shields. For that reason, the app also supports a “Resume” button that allows users to retry a timed-out shielding themselves safely.

5 Likes

Hi @Heart8reak, please create a ticket for @ Support team via DM on the forum. They will help you out!
Also, we’ve improved the Provide in Incognito app v4.3.9, please update your app (if haven’t) to have a better experience for the future provision. Thanks.

2 Likes

Awesome!

Will create one now

Thank you

It’s good that people cannot exploit the old flow. Under the current design does the ETH/ERC20 address change after one successful shield or will that address be permanently tied to an incognito address (thus meaning someone needs to create another incognito keychain in order to get a new shielding address)?

Hey @duc,

After this upgrade, I wanted to unshield some ERC-20 assets for the first time. I’m really surprised about the unshielding fee. To me, if this isn’t intentional, there may be a bug. I know and use your API to estimate the unshielding fees. Up to now, there was no problem. However, this time the fee (~$350) that the app requests is much higher than the fee (~$150) that your old(?) API requests.

Either you have improved :slight_smile: both shielding and “unshielding”, or there is a bug. Could you provide us with some details?

Thanks.

1 Like

hmm… I see @abduraman, I guess it’s a problem with fees estimation, working on the issue today. Thanks for pointing it out!

3 Likes

Hey @duc,

I think there is one more problem in the new Shielding flow: dynamic fee. At the unshielding flow, the fee is known by the user and she adjusts amount accordingly. However, at the new Shielding flow, the fee is not fixed. This is a problem. Today I wanted to shield some ETH. When I got the address (ok, it is always the same address anymore), the estimated fee was ~0.0037 ETH. However, when my transaction was completed, ~0.0075 ETH was cut. I understand the issue but I don’t think this is acceptable solution. The user should know how much she will get after the shielding. Such an uncertainty is unacceptable, right? If possible, could you provide us with a fixed or time-framed** fee solution?

** I mean a hybrid solution of old and new flows. There will be no time frame for deposit but will a 2-hour time frame for the shielding fee.

Thanks.

2 Likes

Hey @abduraman, I think your suggestion does make sense, will be working with the team today to discuss the inconvenience.

And just wanted to update you and the community, we’re also already working on the decentralized shielding process as mentioned in the topic, the ETA is in a week from now. In the process, a user can shield (or transfer funds) directly from an ETH/ERC20 crypto wallet without a need for an intermediary so the problem with the fee difference between actual and estimation will no longer exist.

4 Likes

In that case, can’t we shield our assets from any wallets (i.e. exchange wallets) that we don’t have its private key? If we can, how will the fee estimation work for those wallets? @duc

1 Like

We will still support both options, the current one for those who want to shield funds from exchanges and the decentralized one for those who willing to shield directly from their personal wallets. Of course, we will discuss and fix the problem you mentioned earlier.

6 Likes

@duc I was curious if you could answer my question. I’ve been waiting to shield but I don’t know if it’s safe (edit: private) to proceed.

Is an ethereum shielding address changed upon a successful shield or do I need to generate a new keychain to obtain a different shielding address?

Hey @marko, sorry for the late response, for some reason I missed your question here.

The temporary address (that you send funds to on the Incognito app) doesn’t help with privacy at all (or anonymity for more correctly). The addresses are only helpful for making the shielding process more convenient. You can only obtain privacy for transfers or anonymity for trades AFTER your funds land in Incognito.

For Ethereum-based tokens (ETH/ERC20), in order to avoid the unexpected mistake with expired temporary address as described in the topic, every incognito account has a dedicated shielding address and never changes even if the shield has been successful. Another option to make a shielding in a decentralized manner right from user’s personal wallet, e.g. Metamask, Trust Wallet will be released next week. You can now give it a try at [Staging app] ETH/ERC20 tokens decentralized shielding

For non-Ethereum based tokens like BTC, XMR, etc… the shielding address is still changed upon a successful shield as before. Additionally, once a user encounters an expiry situation, he/she can extend the expiry time by clicking the “Resume” button on the shielding detail screen.

5 Likes