(Reposting here my comment from the Medium article)
I suspect that, for the overwhelming majority of contracts and security inspectors, this will never be an issue. Even before better tooling comes along, one will be able to quickly tell the “common case” of contracts that have been deployed with CREATE “all the way down”, without inspecting creation code. Just look up the contract’s creator, try a few small integer nonces (1–255 will catch virtually all cases), see if keccak256(rlp([creator, nonce])) returns the contract’s address, repeat the same for creator recursively. One can do this manually or it’s an easy utility to write over existing info in blockchain explorers.
E.g., the test in Solidity (given contract and creator addresses) is: contract== keccak256(abi.encodePacked(byte(0xd6), byte(0x94), address(creator), byte(nonce)) . If it passes, the contract was created with CREATE, not with CREATE2.
Neither the init code nor the salt can change for the same address.
ETH2.0 phase 0 has nothing resembling an EVM, so CREATE2 is pretty irrelevant for at least 12-18 months.
It’s not a bug in the sense that the spec allows it, so all clients are meeting the spec correctly. We can argue about whether it is a good spec, which is a matter of taste/opinion, and I don’t expect to get total consensus on that point.
Preventing redeployments is better but in its absence, at least being able to (programmatically) detect that one is interacting with a redeployed contract is a good security check.
As discussed earlier, even the same contract bytecode redeployed with different initial values may exploit users; so EXTCODEHASH is apparently of limited use. But if we think that this slightly increases the assurance level (i.e. same code but maybe not data), why was only the hash of init_code but not that of contract bytecode included in CREATE2 address generation - what am I missing?
I don’t get your question. What do you mean with contract bytecode? The actual deployed bytecode? This doesn’t help in your case because we can still alter the storage. So if you really want it to work you should basically calculate a merkle root of the deployed contract storage, calculate the deployed bytecode and put those in the hash input.
But this creates some weird edge cases. What if the deployed bytecode depends on the current address?
Sorry, yes, this is what I meant - to-be-deployed bytecode along with the deploy/init code.
Correct, this is the point about EXTCODEHASH not being sufficient (discussed earlier in this thread) because it captures only code not storage. But isn’t this better than only capturing init_code in the address calculation?
Not sure I understand your suggestion on hashing deployed contract storage - are you saying that we can capture the storage state that will be initialised by the init_code (i.e. within the constructor)? I suppose as long as init_code is not dependent on external state, this aspect is already captured in its hash, i.e. any different storage initialisations in the init_code will change its hash, correct? But changes in external data dependencies (e.g. from other contract state, block state) if present in init_code won’t be detected and cannot be captured here I think.
If you care only about the deployed bytecode then you can use EXTCODEHASH to verify that the deployed bytecode is indeed the code which you expect.
I agree that if the function is not dependent on external factors then you can indeed precalculate what code will be deployed. (However, realize that in terms of gas costs this essentially will mean running the constructor twice to get the deployed bytecode before hashing it!). Be careful though - you can do some tricks using ORIGIN, CALLVALUE or ADDRESS. In what ADDRESS should we run the to-be-deployed contract?
EDIT: In the end this all boils down to how the code has been deployed. If you don’t want redeploys you make a guard at the point where it was created, which in my opinion solves all problems.
You can effectively do this with proxy contracts pre-Constantinople. The idea is that users know to look at a contract and identify that it’s a proxy, and when it can change. Now users have another thing to look for.
I agree that it would be better if contracts couldn’t be brought back after being destroyed. But I think it’s not as dire as “invalidat[ing] the value prop”. It is definitely happening, so at this point the best thing to do is to help with the education campaign to make sure people know what to look for.
I don’t think that’s an apt analogy. It is reasonably obvious to any professional developer when linked contract addys can be updated. And I can typically query the linked address on-chain whenever I like. If the address hasn’t changed, I can be confident the linked code hasn’t either.
This is totally different. No reasonable developer would assume this is possible (I surely didn’t). I think this qualifies as “dire.”
edit: In other words, where in the Solidity source does it say “this code is subject to replacement at any time?” What’s the line of code for that?
edit2: not for nothin’ but simply saying “but you could ALWAYS swap out code; this is happening” does not inspire confidence… (in Ethereum, not you. )
How so? If my intention is to never redeploy my own contract, there IS no problem. That’s not what we’re talking about. We’re talking about the situation where I DO want to redeploy my contract (for nefarious reasons) and the users of my contract don’t know that.
What is the motivation for allowing zombie contracts? I don’t see it in the EIP:
Allows interactions to (actually or counterfactually in channels) be made with addresses that do not exist yet on-chain but can be relied on to only possibly eventually contain code that has been created by a particular piece of init code. Important for state-channel use cases that involve counterfactual interactions with contracts.
None of this seems to imply that creation has to be allowed to take place multiple times. What am I missing
As for defensive programming, it is optimistic to assume that it will be the norm given the perverse incentives inherent to (high) gas costs
Then it should be your job to communicate this to your users and it’s the users’ job to check the security of the contract they are interacting with. Of course you cannot expect that users are programmers and can read EVM - but in that case, they should delegate this check to other people they trust to check the security for the contract.
This makes no sense because it can be a feature. Etherscan should not put a “big red warning” on it - a small yellow warning might be fine, just like how they warn on older solidity versions in contracts verified.
Yes. Even in contracts. You can EXTCODECOPY and check that there is not a single byte (not in a PUSH operation) which equals 0xff.
None of that makes sense. This attack vector is available to the OWNER of the contract. If my intention is to replace my own contract to nefariously do something, why on earth would I warn my users about it?
And none of this solves the problem. See my “Company XYZ” scenario above.
This is insane. I think once this vulnerability is more widely known, the Constantinople upgrade is going to make a lot of new Bitcoin programmers, unfortunately. I mean, just personally, I don’t know that I can tell my clients to build on Ethereum when I know this giant turd is floating around out there. It’s just a matter of time before this gets exploited in a big and spectacular way. Sure, my clients’ contracts will be unaffected, but Ethereum will have lost all credibility (rightly so).