-
Notifications
You must be signed in to change notification settings - Fork 19
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
EOFCREATE
: Don't hash the init-container
#162
Comments
A relevant code is CREATE3: https://github.com/Vectorized/solady/blob/main/src/utils/CREATE3.sol Should ask for feedback from library authors. |
If we decide to take away initcontainer hashing (or change it somehow), we need to revisit and ask for an update to the EOF considerations for Verkle EIPs. A link a tentative PR which handles this (and a thread about initcontainer hashing) here.
|
A different perspective on this: the hash of the init container locks in the semantics, not the exact code contents of the contract. This doesn't prevent future rewrites of the code because they will be semantics-preserving or inherently breaking regardless of code observability. Code observability should be removed to the extent that CODECOPY/EXTCODECOPY can cause semantics-preserving rewrites to become breaking changes indirectly. In my opinion, it should be totally okay to rewrite code even when the address is a witness of the original code on the account. In fact, it's a good thing that there's a way to recover CODECOPY via this witness in a way that doesn't risk being broken by rewrites! I also think this ability to provide on-chain proof of the code/semantics of an account is an important primitive that we shouldn't get rid of. |
Thank you for this perspective. We're gathering inputs on this one, so this feedback is very useful.
Do the Solution 1 & 2 above still qualify as getting rid of? Note that |
Because of Solution 2 would work if there was a way to trace back to a "root" deployer whose address was computed with codehash. If EOFCREATE doesn't do that (and CREATE2 is "removed" via EOF), I don't think it would be possible to get a root deployer like that because creation transactions don't use the codehash. I think the current state where the code hash is directly included is better though, because it takes a single hash to compute the address rather than a tracing process involving multiple hashes. Additionally, you may only care about proving the code of an account ignoring the code of its deployer, and if you have to trace it back to the deployer you are not able to do that. Overall I think including code hash into the address directly is significantly better. |
A note about CREATE3.sol and similar patterns: this is often used to deploy contracts via CREATE2 at a deterministic address that doesn't depend on the creation parameters (or only some of them). For example Uniswap v3 does this here:
This kind of use case is actually natively addressed by EOFCREATE! The workaround will no longer be needed because the input is not included in the address formula. The other side to this is that users of CREATE2 that do care about the creation parameters will need a new way to validate them. Either a trusted factory mixes them into the salt, or the contract exposes getters. There is another potential use case for CREATE3.sol, which is to deploy at a deterministic address that is fully independent of the creation code. This other use case is not only not addressed by EOFCREATE, it becomes impossible to strictly implement under EOF, although it is easy to work around by deploying a proxy instead. I don't know how common this use case is honestly. More context here: https://github.com/moodysalem/EIPs/blob/46350bb/EIPS/eip-3171.md#motivation |
Analysis of input data for create addressIt is preferable the inputs be less than 136 bytes (the keccak256 block size).
|
Interesting. I was thinking about extending solution 2 to hash also inputs provided to
I think the pattern may be for user to hash inputs and combine them with the salt. |
What about chained EOF creates going deep? In this case would the "code address" of the second level create be the code address of the parent? If the code address is the address if the topmost container... not so much. Because then the index could be re-used at different depths to cause different contracts to be deployed at the same address based on call data (although returncontract can do that more cleanly). Or is it the "sender address" that gets updated in nested EOFCREATES? Either way we need tests for this scenario. |
O: O: O: O: |
Is this a problem though? seems OK to me. We have the a deterministic address to deploy D / E at, but the code itself (contents) is not in the witness |
Wouldn't this scheme work?
The combination Amending @chfast's examples above: O: O: O: O: E no longer equal to D Any further nested EOFCREATE would have different |
EDIT: this post is likely some kind of misunderstanding on my part. We mentioned originally that code address is the address of the outer-most EOFCREATE, but actually, a scheme where code_address changes during each EOFCREATE seems to avoid address conflicts better... Having revisited @chfast 's example after a while, I think by O: CALL A This already makes D != E or putting it differently:
However, this only changes the way one runs into the D==E conflict: O: CALL C (deployed above) but initcodes used for D and E are different (at different nesting depth in A) |
...or wait, maybe we actually do want the |
It doesn't sound right for nested EOFCREATE to have |
The problem stems from the fact that we're swapping the code executing at the context of C - during init it is the initcode, after it is the initcode's subcontainer (RETURNCONTRACTed). This makes the two instances of
|
From my perspective in case of EOFCREATE nested in outer EOFCREATE initcode, the inner EOFCREATE's initcode doesn't "live at C" at all, it has almost nothing to do with C. C is an address that will be deployed (or not) when outer EOFCREATE finishes. But this is a bit of bikeshedding. I agree |
Yeah, I see your point here. But actually the bikeshedding is useful. I revisited the option with "code_address doesn't change on EOFCREATE + during_init" with this new perspective and now it seems to me it works too, I must've made a mistake somewhere yesterday, PTAL: O: CALL A O: CALL C (deployed above) O: CALL C (deployed above) In this version |
Yes, I like this version more. Seems to work and not conflict on deeper nesting levels, too. O: CALL A |
This seems to work. The approach seems equivalent to a list of indices pointing at a deeply nested subcontainer of I find it hard to reason about though. I think if you see So the init containers for each of these contracts are:
There seems to be some redundancy here:
So an alternative could be to remove
Note that EOFCREATE in a DELEGATECALL context always results in |
Since it looks like we may have solved this issue I'll resurface my previous comment. With this change we would be losing the ability to make on-chain proofs about the behavior of an account without a trusted factory (although it would be recoverable with a zk-coprocessor). I do think we need to consider whether it's okay to remove that, or if it's a primitive that applications are relying on. I'm currently weakly leaning towards probably okay to remove. |
Can you clarify what kind of a behavior proof? Just that |
Yeah that works if you have a trusted/known factory. This is probably enough. |
I like this variant, too. I would reframe it as we'd have 2 different schemes depending on whether EOFCREATE is called inside initcode:
|
The create address derivation for
EOFCREATE
is based onCREATE2
.where the
sender_address
is the logical address of the contract invokingEOFCREATE
.We identified that the
keccak256(init-container)
goes against the "code non-observability" because it locks in the contents of the init-container e.g. preventing re-writing it in some future upgrade.It also seems unnecessary expensive:
EOFCREATE
can only pick up one of the deploy-time sub-containers.Solution 1: Use sub-container index
The create address is already bound to the "sender address", code is immutable (no
SELFDESTRUCT
) so replacing the hash of the sub-container with just its index may be enough.Solution 2: Use code's address + sub-container index
The
CREATE2
scheme uses the "sender address" with may not be the address of the code (seeDELEGATECALL
). I'm not sure if this is desired property forCREATE2
. But forEOFCREATE
this looks to be a problem. A contract may deploy different contract usingDELEGATECALL
proxy: forEOFCREATE
inside aDELEGATECALL
the same sub-container index will point to different sub-container. To fix this we can replace/combine the physical code address:keccak256(code_address + salt + sub-container-index)
keccak256(sender_address + code_address + salt + sub-container-index)
The text was updated successfully, but these errors were encountered: