EVM Object Format (EOF) removes the possibility to create contracts using CREATE
or CREATE2
instructions. We introduce a new/replacement method in form of pair of instructions : EOFCREATE
and RETURNCODE
to provide a way to create contracts using EOF containers.
This EIP uses terminology from the EIP-3540 which introduces the EOF format.
EOF aims to remove code observability, which is a prerequisite to legacy EVM contract creation logic using legacy-style create transactions, CREATE
or CREATE2
, because both the initcode and code are available to the EVM and can be manipulated. On the same premise, EOF removes opcodes like CODECOPY
and EXTCODECOPY
, introducing EOF subcontainers as a replacement to cater for factory contracts creating other contracts.
The new instructions introduced in this EIP operate on EOF containers enabling factory contract use case that legacy EVM has.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
Wherever not explicitly listed, the rules of EOF contract creation, as well as the EOFCREATE
instruction, should be identical or analogous to those of CREATE2
instruction. This includes but is not limited to:
accessed_addresses
and address collision (EIP-684 and EIP-2929)EOFCREATE
initcode - memory, account context etc.value
argument)Constant | Value |
---|---|
TX_CREATE_COST |
Defined as 32000 in the Ethereum Execution Layer Specs |
STACK_DEPTH_LIMIT |
Defined as 1024 in the Ethereum Execution Layer Specs |
GAS_CODE_DEPOSIT |
Defined as 200 in the Ethereum Execution Layer Specs |
MAX_CODE_SIZE |
Defined as 24576 in EIP-170 |
We introduce two new instructions on the same block number EIP-3540 is activated on:
EOFCREATE
(0xec
)RETURNCODE
(0xee
)If the code is legacy bytecode, any of these instructions result in an exceptional halt. (Note: This means no change to behaviour.)
CREATE
, CREATE2
are made obsolete and rejected by validation in EOF contracts. They are only available in legacy contracts.CREATE
and CREATE2
have EOF code as initcode (starting with EF00
magic)In the context of legacy bytecode execution any of these instructions (EOFCREATE
, RETURNCODE
) result in an exceptional halt. (Note: This means no change to behaviour.)
In EOF EVM, new bytecode is introduced to the state by means of InitcodeTransaction
delivering an EOF container (initcontainer
). Such a container may include arbitrarily deeply nesting subcontainers. The initcontainer
and its subcontainers are recursively validated according to all the validation rules applicable for the EOF version in question. Next, the 0th code section of the initcontainer
is executed and may eventually call a RETURNCODE
instruction, which will refer to a subcontainer to be finally deployed to an address.
InitcodeTransactions
are defined in detail in EIP-7873.
EOFCREATE
instruction is in turn a replacement of the CREATE
and CREATE2
legacy instructions allowing factory contracts to create other contracts. The main difference to the InitcodeTransaction
is that the initcontainer
is selected to be one of the subcontainers of the EOF container calling EOFCREATE
(and not one of transaction.initcodes
). It is worth noting that no validation is performed at this point, as it has already been done when the factory contract containing EOFCREATE
was deployed.
Details on each instruction follow in the next sections.
EOFCREATE
TX_CREATE_COST
gasstatic-mode
.initcontainer_index
, encoded as 8-bit unsigned valuesalt
, input_offset
, input_size
, value
from the operand stack[input_offset, input_size]
initcontainer_index
in the container from which EOFCREATE
is executedinitcontainer
be that EOF containerSTACK_DEPTH_LIMIT
and that caller balance is enough to transfer value
[input_offset:input_size]
is used as calldatasender
account's noncenew_address
as keccak256(0xff || sender32 || salt)[12:]
, where sender32
is the sender address left-padded to 32 bytes with zeros0
onto the stackREVERT
edRETURNCODE{deploy_container_index}(aux_data_offset, aux_data_size)
instruction (see below). After that:deploy_container_index
in the container from which RETURNCODE
is executed(aux_data_offset, aux_data_offset + aux_data_size)
memory segment and update data size in the header if needed.MAX_CODE_SIZE
instruction exceptionally abortsstate[new_address].code
to the updated deploy containernew_address
onto the stackGAS_CODE_DEPOSIT * deployed_code_size
gasRETURNCODE
deploy_container_index
, encoded as 8-bit unsigned valueaux_data_offset
, aux_data_size
referring to memory section that will be appended to deployed container's datadeploy_container_index
and aux_data
are used to construct deployed contract (see above)We extend code section validation rules (as defined in EIP-3670).
EOFCREATE
initcontainer_index
must be less than num_container_sections
EOFCREATE
the subcontainer pointed to by initcontainer_index
must have its len(data_section)
equal data_size
, i.e. data section content is exactly as the size declared in the header (see Data section lifecycle)EOFCREATE
the subcontainer pointed to by initcontainer_index
must not contain either a RETURN
or STOP
instructionRETURNCODE
deploy_container_index
must be less than num_container_sections
RETURNCODE
the subcontainer pointed to deploy_container_index
must not contain a RETURNCODE
instructionRETURNCODE
and either of RETURN
or STOP
RETURNCODE
and EOFCREATE
RJUMP
, RJUMPI
and RJUMPV
immediate argument value (jump destination relative offset) validation: code section is invalid in case offset points to the byte directly following either EOFCREATE
or RETURNCODE
instruction.For an EOF container which has not yet been deployed, the data_section
is only a portion of the final data_section
after deployment.
Let's define it as pre_deploy_data_section
and as pre_deploy_data_size
the data_size
declared in that container's header.
pre_deploy_data_size >= len(pre_deploy_data_section)
, which anticipates more data to be appended to the pre_deploy_data_section
during the process of deploying.
pre_deploy_data_section
| |
\___________pre_deploy_data_size______/
For a deployed EOF container, the final data_section
becomes:
pre_deploy_data_section | static_aux_data | dynamic_aux_data
| | | |
| \___________aux_data___________/
| | |
\___________pre_deploy_data_size______/ |
| |
\________________________data_size_______________________/
where:
aux_data
is the data which is appended to pre_deploy_data_section
on RETURNCODE
instruction.static_aux_data
is a subrange of aux_data
, which size is known before RETURNCODE
and equals pre_deploy_data_size - len(pre_deploy_data_section)
.dynamic_aux_data
is the remainder of aux_data
.data_size
in the deployed container header is updated to be equal len(data_section)
.
Summarizing, there are pre_deploy_data_size
bytes in the final data section which are guaranteed to exist before the EOF container is deployed and len(dynamic_aux_data)
bytes which are known to exist only after.
This impacts the validation and behavior of data-section-accessing instructions: DATALOAD
, DATALOADN
, and DATACOPY
, see EIP-7480.
The data section is appended to during contract creation and also its size needs to be updated in the header. Alternative designs were considered, where:
All of these alternatives either complicated the otherwise simple data structures or took away useful features (like the dynamically sized portion of the data section).
keccak256(initcontainer)
in the new_address
hashing schemenew_address = keccak256(0xff || sender || salt || keccak256(initcontainer))[12:]
was originally proposed as the way to calculate the address of newly created contract, similar, but not exactly equal, to what CREATE2
uses.
This alternative however goes against code non-observability, because it locks in the contents of the initcontainer e.g. preventing re-writing it in some future upgrade. It also seems unnecessarily expensive: EOFCREATE
can only pick up one of its subcontainers, yet the hash value would need to be recalculated on every execution of EOFCREATE
.
Other ways of removing code observability, yet keeping some form of witness of the code, were considered. However, keeping only sender
and salt
allows the implementer of the factory contract (i.e. one containing the EOFCREATE
instruction) to include the code witness via the salt
anyway, if that's necessary for the particular use case. Therefore, keeping the new_address
formula minimal is the most flexible approach with better separation of concerns.
Leaving the keccak256(initcontainer)
out of the new_address
hash has also the benefit of making the new_address
independent of the metadata section (proposed for the EOF in a separate EIP), which is a desired property. Unfortunately, if a factory wants to opt into committing to a particular initcontainer
, it needs to include it in the salt
, and that will include the metadata section.
EOFCREATE
stack argument orderEXT*CALL
instructions from EIP-7069 have had their stack argument order changed, as compared to that of legacy instructions *CALL
. We follow the same change to have EOFCREATE
stack arg order match those of EXTCALL
.
This change poses no risk to backwards compatibility, as it is introduced at the same time EIP-3540 is. The new instructions are not introduced for legacy bytecode (code which is not EOF formatted), and the contract creation options do not change for legacy bytecode.
CREATE
and CREATE2
calls with EF00
initcode fail early without executing the initcode. Previously, in both cases the initcode execution would begin and fail on the first undefined instruction EF
.
Creation transaction, CREATE
and CREATE2
cannot have its code starting with 0xEF
, but such cases are covered already in EIP-3541. However, new cases must be added where CREATE
or CREATE2
have its initcode being (validly or invalidly) EOF formatted:
Initcode | Expected result |
---|---|
0xEF |
initcode starts execution and fails |
0xEF01 |
initcode starts execution and fails |
0xEF5f |
initcode starts execution and fails |
0xEF00 |
CREATE / CREATE2 fails early, returns 0 and keeps sender nonce intact |
0xEF0001 |
as above |
valid EOFv1 container | as above |
It is the EOF InitcodeTransaction
(specified in EIP-7873) which needs a detailed review and discussion as that is where external unverified code enters the state. Among others:
Copyright and related rights waived via CC0.