This writeup lists issues related to contract redeployment and vague specifications, making a proposal to precisely define the expected behavior in such instances.
Contract deployment
According to EIP684, If a contract creation is attempted, due to either a creation transaction or the CREATE/CREATE2 opcode, and the destination address already has either nonzero nonce, or nonempty code, then the creation throws immediately. Specifically the prerequisites for a successful deployment includes:
account.nonce == 0
account.CodeHash == 0000000000000000000000000000000000000000000000000000000000000000 || account.CodeHash ==
c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
It means the balance
and storage
presence is not checked upon the contract deployment and the latter brings some confusion.
It’s a pretty common scenario supported by Ethereum protocol that the destination address is pre-funded and contract deployment happens later. The non-zero balance is regarded as valid, therefore, the contract deployment will proceed normally. The behaviour in this case is clearly defined and not the main toic of this writeup.
What if the destination address has non-empty storage? One might wonder if it even possible for this situation to occur. The answer is Yes: we do have a few on mainnet (see appendix).
This kind of account can be specified as follow:
account.nonce == 0
account.CodeHash == 0000000000000000000000000000000000000000000000000000000000000000 || account.CodeHash ==
c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
account.StorageRoot != 0000000000000000000000000000000000000000000000000000000000000000
&&account.StorageRoot != 56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421
Since storage slots can only be created while the contract itself is running (within constructor or deployed code), and a requirement is that there is no deployed code, it follows that these slots can only have been set within the constructor, and that the constructor returned empty code while executing.
After EIP-158
, the nonce of a new contract became set to 1, therefore it also follows that any such contract was depoyed before EIP-158
was deployed.
Redeployment indicates that the address has been deployed with a contract, but still meets the conditions of deployment. When such an address is deployed successfully with contract code again, this situation is called redeployment.
Prerequisites for redeployment
- The account was deployed before EIP158, with zero nonce and empty code
- The destination address of new deployment is same with the old one
The first condition is totally possible as described in background section. The main focus will be the second condition.
The destination address calculation
There are two formulas in Ethereum protocol for contract address calculation:
Keccak256(rlp{sender, nonce})[12:]
Keccak256(0xff ++ address ++ salt ++ keccak256(init_code))[12:]
The target account discussed in the writeup was created before EIP158 by using the first formula. Next we will prove that the same address cannot be calculated unless hash collision happens.
It’s impossible to calculate the same address with formula1
In order to produce a same address with formula1, the same sender
and nonce
pair must be held.
- If the deployer is an EOA, the same deployment nonce is not available
- If the deployer is a contract(via
create
opcode), the deployment nonce might be available if the contract can be self-destroyed, but the same deployment address is not available after destruction as the contract itself must be created by EOA or the contract created by EOA recursively.
It’s impossible to calculate the same address with formula2
According to EIP1014, the addresses created with this formula2 cannot collide with addresses created using the formula1, as 0xff
can only be a starting byte for RLP for data many petabytes long.
Hash collision
According to the analysis from Peter Davies:
Gary's analysis seems correct to, but has missed a subtle point, which makes this complicated. The KECCAK collision required is only 160 bits, not the full 256 bits. 160 bit collisions are acheivable and cost around ~$10 billion
The address hash collision is theoretically possible. Therefore we can conclude it’s impossible to calculate the same destination address, unless hash collision happens with non-trivial computation cost.
After the analysis above, we know it’s possible to encounter contract re-deployment if hash-collision. To eliminate confusion and make all clients behave consistently in case of redeployment. we need precisely define the specification.
Here is what I propose as the expected behaviors:
- Contract deployment to an account with non-zero nonce: the create-operation fails. (No change)
- Contract deployment to an account with non-empty code: the create-operation fails. (No change)
- Contract deployment to an account with non-empty balance: the balance is inherited (No change)
- Contract deployment to an account with non-empty storage: the storage is retained (Change: previously the storage was deleted)
Rationale
The reason for choosing to retain the leftover storages:
- There are some extra cost/complexities to delete the leftover storage, especially when verkle is enabled
- Account reset notion (if storage is totally discarded) is a very non-intuitive notion, and should be removed.
An acceptable alternative, would be to reject contract deployement if storage is non-empty.
The list of re-deployable accounts with non-empty storage on ethereum mainnet:
- f468bcbc4a0bfdb06336e773382c5202e674db71
- d8253352f6044cfe55bcc0748c3fa37b7df81f98
- 5983c6ac846dcf85fbbc4303f43eb91c379f79ae
- de425ad4b8d2d9e0e12f65cbcd6d55f447b44083
- 50b1497068bae652df3562eb8ea7677ff84477fa
- 8398ff6c618e9515468c1c4b198d53666cbe8462
- 6f156dbf8ed30e53f7c9df73144e69f65cbb7e94
- 2c081ed1949d7dd9447f9d96e509befe576d4461
- db7c577b93baeb56dab50af4d6f86f99a06b96a2
- 14725085d004f1b10ee07234a4ab28c5ad2a7b9e
- ae3703584494ade958ad27ec2d289b7a67c19e90
- 7d6ae067de8d44ae1a08750e7d626d61a623c44a
- 4d149eb99bdeefc1f858f8fd22289c6beae99f2c
- 361d7a60b43587c7f6bba4f9fd9642747f65210a
- b619f45637c39ca49a41ac64c11637a0a194455e
- 5071cb62aa170b7f66b26cae8004d90e6078bb1e
- add92e0650457c5db0c4c08cbf7ca580175d33d2
- 3311c08066580cb906a7287b6786e504c2ebd09f
- 02820e4bee488c40f7455fdca53125565148708f
- e62dc49c92fa799033644d2a9afd7e3babe5a80a
- 5cc182fabfb81a056b6080d4200bc5150673d06f
- f4a835ec1364809003de3925685f24cd360bdffe
- fc4465f84b29a1f8794dc753f41bef1f4b025ed2
- 40490c9c468622d5c89646d6f3097f8eaf80c411
- a21b22389bfc1cd6bc7ba19a4fc96adc3d0fe074
- 59ec0410867828e3b8c23dd8a29d9796ef523b17
- 19272418753b90d9a3e3efc8430b1612c55fcb3a
- fee7707fa4b8c0a923a0e40399db3e7ce26069c6