Skip to content

Commit

Permalink
Update EIP-7620: Rename and update
Browse files Browse the repository at this point in the history
Merged by EIP-Bot.
  • Loading branch information
pdobacz authored Mar 7, 2024
1 parent 6aed382 commit 73fbb29
Showing 1 changed file with 52 additions and 42 deletions.
94 changes: 52 additions & 42 deletions EIPS/eip-7620.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
eip: 7620
title: EOF Contract Creation
description: Introduce `CREATE3`, `CREATE4`, `RETURNCONTRACT` instructions along with a new `InitcodeTransaction` transaction
description: Introduce `EOFCREATE`, `TXCREATE`, `RETURNCONTRACT` instructions along with a new `InitcodeTransaction` transaction
author: Alex Beregszaszi (@axic), Paweł Bylica (@chfast), Andrei Maiboroda (@gumb0), Piotr Dobaczewski (@pdobacz)
discussions-to: https://ethereum-magicians.org/t/eip-7620-eof-contract-creation-instructions/18625
status: Draft
Expand All @@ -13,7 +13,7 @@ requires: 170, 1559, 2028, 2718, 3540, 3541, 3670, 3860

## Abstract

EVM Object Format (EOF) removes the possibility to create contracts using creation transactions (with an empty `to` field), `CREATE` or `CREATE2` instructions. We introduce three new instructions: `CREATE3`, `CREATE4` and `RETURNCONTRACT`, as well as a new transaction type (`InitcodeTransaction`) to provide a way to create contracts using EOF containers.
EVM Object Format (EOF) removes the possibility to create contracts using creation transactions (with an empty `to` field), `CREATE` or `CREATE2` instructions. We introduce three new instructions: `EOFCREATE`, `TXCREATE` and `RETURNCONTRACT`, as well as a new transaction type (`InitcodeTransaction`) to provide a way to create contracts using EOF containers.

## Motivation

Expand All @@ -33,39 +33,49 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S
| - | - |
| `INITCODE_TX_TYPE` | `Bytes1(0x04)` |
| `MAX_INITCODE_COUNT` | `256` |
| `GKECCAK256WORD` | Defined as `6` in the Ethereum Yellow Paper |
| `GCREATE` | Defined as `32000` in the Ethereum Yellow Paper |
| `GCODEDEPOSIT` | Defined as `200` in the Ethereum Yellow Paper |
| `GTXDATAZERO` | Defined as `4` in the Ethereum Yellow Paper |
| `GTXDATANONZERO` | Defined as `16` in the Ethereum Yellow Paper |
| `GAS_KECCAK256_WORD` | Defined as `6` in the [Ethereum Execution Layer Specs](https://github.com/ethereum/execution-specs/blob/0f9e4345b60d36c23fffaa69f70cf9cdb975f4ba/src/ethereum/shanghai/vm/gas.py#L37C1-L37C19) |
| `TX_CREATE_COST` | Defined as `32000` in the [Ethereum Execution Layer Specs](https://github.com/ethereum/execution-specs/blob/0f9e4345b60d36c23fffaa69f70cf9cdb975f4ba/src/ethereum/shanghai/fork_types.py#L42) |
| `GAS_CODE_DEPOSIT` | Defined as `200` in the [Ethereum Execution Layer Specs](https://github.com/ethereum/execution-specs/blob/0f9e4345b60d36c23fffaa69f70cf9cdb975f4ba/src/ethereum/shanghai/vm/gas.py#L44) |
| `TX_DATA_COST_PER_ZERO` | Defined as `4` in the [Ethereum Execution Layer Specs](https://github.com/ethereum/execution-specs/blob/0f9e4345b60d36c23fffaa69f70cf9cdb975f4ba/src/ethereum/shanghai/fork_types.py#L41) |
| `TX_DATA_COST_PER_NON_ZERO` | Defined as `16` in the [Ethereum Execution Layer Specs](https://github.com/ethereum/execution-specs/blob/0f9e4345b60d36c23fffaa69f70cf9cdb975f4ba/src/ethereum/shanghai/fork_types.py#L40) |
| `INITCODE_WORD_COST` | Defined as `2` in [EIP-3860](./eip-3860.md) |
| `MAX_INITCODE_SIZE` | Defined as `2 * MAX_CODE_SIZE` in [EIP-3860](./eip-3860.md) |
| `MAX_CODE_SIZE` | Defined as `24576` in [EIP-170](./eip-170.md) |

We introduce three new instructions on the same block number [EIP-3540](./eip-3540.md) is activated on:

1. `CREATE3` (`0xec`)
2. `CREATE4` (`0xed`)
1. `EOFCREATE` (`0xec`)
2. `TXCREATE` (`0xed`)
3. `RETURNCONTRACT` (`0xee`)

### Transaction Types

Introduce new transaction `InitcodeTransaction` (type `INITCODE_TX_TYPE`) which extends [EIP-1559](./eip-1559.md) (type 2) transaction by adding a new field `initcodes: List[ByteList[MAX_INITCODE_SIZE], MAX_INITCODE_COUNT]`.

The `initcodes` can only be accessed via the `CREATE4` instruction (see below), therefore `InitcodeTransactions` are intended to be sent to contracts including `CREATE4` in their execution.
The `initcodes` can only be accessed via the `TXCREATE` instruction (see below), therefore `InitcodeTransactions` are intended to be sent to contracts including `TXCREATE` in their execution.

We introduce a standardised Creator Contract (i.e. written in EVM, but existing at a known address, such as precompiles), which eliminates the need to have create transactions with empty `to`. Deployment of the Creator Contract will require an irregular state change at EOF activation block. Note that such introduction of the Creator Contract is needed, because only EOF contracts can create EOF contracts. See below for Creator Contract code.

#### Gas schedule

Each `initcodes` item data costs the same as calldata (`GTXDATANONZERO` gas for non-zero bytes, `GTXDATAZERO` for zero bytes -- see [EIP-2028](./eip-2028.md)). The intrinsic gas of an `InitcodeTransaction` is extended by the sum of all those items' costs.
Each `initcodes` item data costs the same as calldata (`TX_DATA_COST_PER_NON_ZERO` gas for non-zero bytes, `TX_DATA_COST_PER_ZERO` for zero bytes -- see [EIP-2028](./eip-2028.md)). The intrinsic gas of an `InitcodeTransaction` is extended by the sum of all those items' costs. Using the conventions from [`calculate_intrinsic_cost` in Ethereum Execution Layer Specs](https://github.com/ethereum/execution-specs/blob/0f9e4345b60d36c23fffaa69f70cf9cdb975f4ba/src/ethereum/shanghai/fork.py#L687), the additional cost is calculated as:

```
initcode_cost = 0
for initcode in tx.initcodes:
for byte in initcode:
if byte == 0:
initcode_cost += TX_DATA_COST_PER_ZERO
else:
initcode_cost += TX_DATA_COST_PER_NON_ZERO
```

#### Transaction validation

- `InitcodeTransaction` is invalid if there are more than `MAX_INITCODE_COUNT` entries in `initcodes`, or if any one exceeds `MAX_INITCODE_SIZE`.
- `InitcodeTransaction` is invalid if the `to` is `nil`.

Under transaction validation rules `initcodes` are not validated for conforming to the EOF specification. They are only validated when accessed via `CREATE4`. This avoids potential DoS attacks of the mempool. If during the execution of an `InitcodeTransaction` no `CREATE4` instruction is called, such transaction is still valid.
Under transaction validation rules `initcodes` are not validated for conforming to the EOF specification. They are only validated when accessed via `TXCREATE`. This avoids potential DoS attacks of the mempool. If during the execution of an `InitcodeTransaction` no `TXCREATE` instruction is called, such transaction is still valid.

#### RLP and signature

Expand Down Expand Up @@ -93,43 +103,43 @@ The [EIP-2718](./eip-2718.md) `ReceiptPayload` for this transaction is `rlp([sta

#### Overview of the new contract creation flow

In EOF EVM, new bytecode is delivered inside a special field in an `InitcodeTransaction` in the form of EOF containers. Such EOF containers may contain arbitrarily deeply nesting subcontainers. A target contract of an `InitcodeTransaction` may execute `CREATE4` instruction(s), and each execution refers to one such EOF container (the `initcontainer`). 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 `RETURNCONTRACT` instruction, which will refer to a subcontainer to be finally deployed to an address.
In EOF EVM, new bytecode is delivered inside a special field in an `InitcodeTransaction` in the form of EOF containers. Such EOF containers may contain arbitrarily deeply nesting subcontainers. A target contract of an `InitcodeTransaction` may execute `TXCREATE` instruction(s), and each execution refers to one such EOF container (the `initcontainer`). 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 `RETURNCONTRACT` instruction, which will refer to a subcontainer to be finally deployed to an address.

As such, `InitcodeTransaction` and `CREATE4` are an EOF replacement of a legacy create transaction.
As such, `InitcodeTransaction` and `TXCREATE` are an EOF replacement of a legacy create transaction.

`CREATE3` instruction is in turn a replacement of the `CREATE` and `CREATE2` legacy instructions allowing factory contracts to create other contracts. The main difference to `CREATE4` is that the `initcontainer` is selected to be one of the subcontainers of the EOF container calling `CREATE3`. It is worth noting that no validation is performed at this point, as it has already been done when the factory contract containing `CREATE3` was deployed.
`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 `TXCREATE` is that the `initcontainer` is selected to be one of the subcontainers of the EOF container calling `EOFCREATE`. 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.

#### `CREATE3`
#### `EOFCREATE`

- deduct `GCREATE` gas
- deduct `TX_CREATE_COST` gas
- read immediate operand `initcontainer_index`, encoded as 8-bit unsigned value
- pop `value`, `salt`, `data_offset`, `data_size` from the operand stack
- load initcode EOF subcontainer at `initcontainer_index` in the container from which `CREATE3` is executed
- deduct `GKECCAK256WORD * ((initcontainer_size + 31) // 32)` gas (hashing charge)
- load initcode EOF subcontainer at `initcontainer_index` in the container from which `EOFCREATE` is executed
- deduct `GAS_KECCAK256_WORD * ((initcontainer_size + 31) // 32)` gas (hashing charge)
- follow steps in the [Initcontainer execution](#initcontainer-execution) below
- deduct `GCODEDEPOSIT * deployed_code_size` gas
- deduct `GAS_CODE_DEPOSIT * deployed_code_size` gas

#### `CREATE4`
#### `TXCREATE`

- deduct `GCREATE` gas
- deduct `TX_CREATE_COST` gas
- pop `tx_initcode_hash`, `value`, `salt`, `data_offset`, `data_size` from the operand stack
- load initcode EOF container from the transaction `initcodes` array which hashes to `tx_initcode_hash`
- fails (returns 0 on the stack) if such initcode does not exist in the transaction, or if called from a transaction of `TransactionType` other than `INITCODE_TX_TYPE`
- caller's nonce is not updated and gas for initcode execution is not consumed. Only `CREATE4` constant gas was consumed
- caller's nonce is not updated and gas for initcode execution is not consumed. Only `TXCREATE` constant gas was consumed
- deduct `INITCODE_WORD_COST * ((initcontainer_size + 31) // 32)` gas
- **validate the initcode container and all its subcontainers recursively**
- in addition to this, check if the initcode container has 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](#data-section-lifecycle))
- fails (returns 0 on the stack) if any of those was invalid
- caller’s nonce is not updated and gas for initcode execution is not consumed. Only `CREATE4` constant and [EIP-3860](./eip-3860.md) gas were consumed
- deduct `GKECCAK256WORD * ((initcontainer_size + 31) // 32)` gas (hashing charge)
- caller’s nonce is not updated and gas for initcode execution is not consumed. Only `TXCREATE` constant and [EIP-3860](./eip-3860.md) gas were consumed
- deduct `GAS_KECCAK256_WORD * ((initcontainer_size + 31) // 32)` gas (hashing charge)
- follow steps in the [Initcontainer execution](#initcontainer-execution) below
- deduct `GCODEDEPOSIT * deployed_code_size` gas
- deduct `GAS_CODE_DEPOSIT * deployed_code_size` gas

#### Initcontainer execution

These steps are common for `CREATE3` and `CREATE4`:
These steps are common for `EOFCREATE` and `TXCREATE`:

- execute the container in "initcode-mode" and deduct gas for execution
- calculate `new_address` as `keccak256(0xff || sender || salt || keccak256(initcontainer))[12:]`
Expand All @@ -148,18 +158,18 @@ These steps are common for `CREATE3` and `CREATE4`:
- read immediate operand `deploy_container_index`, encoded as 8-bit unsigned value
- pop two values from the operand stack: `aux_data_offset`, `aux_data_size` referring to memory section that will be appended to deployed container's data
- cost 0 gas + possible memory expansion for aux data
- ends initcode frame execution and returns control to CREATE3/4 caller frame where `deploy_container_index` and `aux_data` are used to construct deployed contract (see above)
- ends initcode frame execution and returns control to EOFCREATE/4 caller frame where `deploy_container_index` and `aux_data` are used to construct deployed contract (see above)
- instruction exceptionally aborts if after the appending, data section size would overflow the maximum data section size or underflow (i.e. be less than data section size declared in the header)
- instruction exceptionally aborts if invoked not in "initcode-mode"

### Code Validation

We extend code section validation rules (as defined in [EIP-3670](./eip-3670.md)).

1. `CREATE3` `initcontainer_index` must be less than `num_container_sections`
1. `CREATE3` 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](#data-section-lifecycle))
1. `EOFCREATE` `initcontainer_index` must be less than `num_container_sections`
1. `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](#data-section-lifecycle))
2. `RETURNCONTRACT` `deploy_container_index` must be less than `num_container_sections`
3. `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 `CREATE3` or `RETURNCONTRACT` instruction.
3. `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 `RETURNCONTRACT` instruction.

### Data Section Lifecycle

Expand All @@ -180,7 +190,7 @@ pre_deploy_data_section | static_aux_data | dynamic_aux_data
| | | |
| \___________aux_data___________/
| | |
\___________pre_deploy_data_size______/ |
\___________pre_deploy_data_size______/ |
| |
\________________________data_size_______________________/
```
Expand Down Expand Up @@ -213,14 +223,14 @@ let salt := calldataload(32)
let init_data_size := sub(size, 64)
calldatacopy(0, 64, init_data_size)
let ret := create4(tx_initcode_index, callvalue(), salt, 0, init_data_size)
let ret := txcreate(tx_initcode_index, callvalue(), salt, 0, init_data_size)
if iszero(ret) { revert(0, 0) }
mstore(0, ret)
return(0, 32)
// Helper to compile this with existing Solidity (with --strict-assembly mode)
function create4(a, b, c, d, e) -> f {
function txcreate(a, b, c, d, e) -> f {
f := verbatim_5i_1o(hex"ed", a, b, c, d, e)
}
Expand All @@ -237,7 +247,7 @@ The data section is appended to during contract creation and also its size needs

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).

`CREATE4` has two "light" failure modes in case the initcontainer is not present and in case the EOF validation is unsuccessful. An alternative design where both cases led to a "hard" failure (consuming the entire gas available) was considered. We decided to have the more granular and forgiving failure modes in order to align the gas costs incurred to the actual work the EVM performs.
`TXCREATE` has two "light" failure modes in case the initcontainer is not present and in case the EOF validation is unsuccessful. An alternative design where both cases led to a "hard" failure (consuming the entire gas available) was considered. We decided to have the more granular and forgiving failure modes in order to align the gas costs incurred to the actual work the EVM performs.

EOF contract creation requires the Creator Contract be introduced via a state change, because neither legacy contracts nor create transactions can deploy EOF code. The alternative approach which was to continue using legacy creation would still rely on fetching the *initcode* from memory and not satisfy the requirement of code non-observability.

Expand All @@ -260,15 +270,15 @@ Creation transaction, `CREATE` and `CREATE2` cannot have its *code* starting wit
| `0xEF0001` | as above |
| valid EOFv1 container | as above |

Since EOF contract validation happens for all EOF containers during `CREATE4`, the following cases must be tested:
Since EOF contract validation happens for all EOF containers during `TXCREATE`, the following cases must be tested:

- `CREATE4` references a valid EOF `initcontainer` having only valid subcontainers
- `CREATE4` references legacy code, contract creation fails
- `CREATE4` references an invalid EOF `initcontainer`, contract creation fails
- `CREATE4` references a valid EOF `initcontainer` having an invalid EOF subcontainer somewhere in the subcontainer tree, contract creation fails
- `CREATE4` references a valid EOF `initcontainer` having legacy code somewhere in the subcontainer tree, contract creation fails
- `TXCREATE` references a valid EOF `initcontainer` having only valid subcontainers
- `TXCREATE` references legacy code, contract creation fails
- `TXCREATE` references an invalid EOF `initcontainer`, contract creation fails
- `TXCREATE` references a valid EOF `initcontainer` having an invalid EOF subcontainer somewhere in the subcontainer tree, contract creation fails
- `TXCREATE` references a valid EOF `initcontainer` having legacy code somewhere in the subcontainer tree, contract creation fails

Cases for initcode calling "nested" `CREATE3` or `CREATE4` in various combinations
Cases for initcode calling "nested" `EOFCREATE` or `TXCREATE` in various combinations

## Security Considerations

Expand Down

0 comments on commit 73fbb29

Please sign in to comment.