How to deal with the Ethereum Replay Attack

The upcoming Ethereum hard fork is scheduled for block 1920000 and it is expected to activate on Wednesday July, 20th.

While every hard fork poses a challenge for exchanges and ETH holders this particular one has nasty side effects. It’s driven by ideology, highly controversial and some groups already declared their intentions to keep maintaining the old chain. Since it will be listed at least one exchange we have to consider the chance that the tokens of the old chain, ETHC (ETH Classic), might have at least some value.

With two chains co-existing every ETH owner will suddenly have the same amount of ETHC on the other chain. However, the two chains won’t part ways completely in the future. Every transaction that happens on the ETH network is a valid transaction on the ETHC network and vice versa. When every transaction gets relayed to both networks then they will move funds on both chains in the same way unless they involve the ETH from the Dark DAO which did not leave the Dark DAO on the old chain.

For an exchange, this means that every ETH withdrawal will also move the same amount of ETHC out of its wallet on the other chain. Users could exploit that by depositing from an address that doesn’t have ETHC and withdraw ETH again, receiving ETHC as well until the exchanges ETHC wallet is drained.

For the normal user it could mean that he continues to use ETH for some time and in a couple weeks he notices that he should have some ETHC from the fork which now have a non-zero value. When he tries to sell them he notices that he in fact doesn’t have any ETHC any more because every ETH transaction he did got replayed and moved ETHC out of his wallet as well.

There are some things we can do to avoid that.

Protocol-level mitigation

This would be the cleanest solution for the replay problem and would fix it for all users. Of course it has its own set of problems, in case of a real protocol change, instead of just a nonce offset, all libraries would need updates as well for example.

Split your funds

Try to send different transactions until you succeed:

Use a smart contract to make the split:

However, the initial split is not enough.

When an exchange does the split it is fine for the moment but the operator has to refill his hot wallet from deposits after some time to pay for withdrawals. This refill transaction is (without extra measures) replayable again and gives his hot wallet a balance on the other chain, hence all withdrawals he processed up to that balance will be replayable on that chain. Here is a complete example of the procedure:

To be safe from that attack the exchange has to make sure that the refill transaction is not replayable on the other chain.

Solution: Use a fork oracle

I’d like to propose the use of a fork oracle contract:

(kudos to D-Nice who wrote a similar contract 2 days ago, Jeff Coleman who brought up the idea to use the dark Dao balance and Nick Johnson who suggested to move the state code into a function and deploy the contract already before the fork)

This contract will permanently lock in whether it resides on the forked chain or not. Other contracts can use the value of its variable ‘forked’ to alter their behaviour and find out on what chain they are.

This oracle is deployed as 0x2bd2326c993dfaef84f696526064ff22eba5b362 and can be used in smart contracts already.

We can use that to write another contract that will forward funds only when it’s on the correct chain. Since every value transfer to it has different outcomes depending on the chain it happens on it can be used to defeat the replay attack.

Deploy the contract on both networks and every time you will send funds to it it will only transfer value on the intended network.

There are different solutions possible how to design a contract like this. This particular design allows you to use it with normal value transfers only but uses up all the supplied gas on the other network for each transfer which could be considered suboptimal.

When you can call functions on a contract

This contract has one function called split. Call it with 2 parameters, the address it should forward value to on the forked chain and the address it should forward to on the old chain. It will forward the value your transaction carries.

It’s already deployed as 0xaa1a6e3e6ef20068f7f8d8c835d2d22fd5116444 on both chains. Follow the link for the ABI. Disclaimer: Always verify the source code of a contract before you send value to it to understand how it will behave.

Feedback is welcome.

Ps. I’m aware that probably only exchanges will go to lengths like this and most users just expect the network to work. They will suffer losses because of the replay attack. I’m urging the client devs to include a protocol-level solution in the hard fork. But there is probably not enough time left.

Edit: Jeffrey Wilcke and Tjaden Hess pointed out that the mentioned replay protection was only used in the testnet before, not for a hard fork. I’ve corrected the paragraph.

Edit 2: I’ve added a second contract ‘ReplaySafeSplit’ to the article.