-
-
Save axic/85c0ed993b63f8087adf1ed8c521d790 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- unified_eof.md 2023-03-22 16:51:55.000000000 +0100 | |
+++ mega_eof.md 2023-03-22 16:51:36.000000000 +0100 | |
@@ -1,9 +1,11 @@ | |
-# Unified EOF specification | |
+# "Mega EOF Endgame" Specification | |
[toc] | |
## Preface | |
+**This document describes all the changes which we previously dicussed titles as EOF 1.1 and EOF 2.0. Those changes do not have an EIP yet.** | |
+ | |
This unified specification should be used as a guide to understand the various changes the EVM Object Format is proposing. The individual EIPs still remain the official specification and should confusion arise those are to be consulted: | |
- 📃[EIP-3540](https://eips.ethereum.org/EIPS/eip-3540): EOF - EVM Object Format v1 [_history_](https://github.com/ethereum/EIPs/commits/master/EIPS/eip-3540.md) | |
- 📃[EIP-3670](https://eips.ethereum.org/EIPS/eip-3670): EOF - Code Validation [_history_](https://github.com/ethereum/EIPs/commits/master/EIPS/eip-3670.md) | |
@@ -11,6 +13,8 @@ | |
- 📃[EIP-4750](https://eips.ethereum.org/EIPS/eip-4750): EOF - Functions [_history_](https://github.com/ethereum/EIPs/commits/master/EIPS/eip-4750.md) | |
- 📃[EIP-5450](https://eips.ethereum.org/EIPS/eip-5450): EOF - Stack Validation [_history_](https://github.com/ethereum/EIPs/commits/master/EIPS/eip-5450.md) | |
- 📃[EIP-6206](https://eips.ethereum.org/EIPS/eip-6206): EOF - JUMPF instruction [_history_](https://github.com/ethereum/EIPs/commits/master/EIPS/eip-6026.md) | |
+- TBA: EOF - Contract Creation | |
+- TBA: EOF - Restrict code and gas introspection | |
## Container | |
@@ -18,12 +22,18 @@ | |
``` | |
container := header, body | |
-header := magic, version, kind_types, types_size, kind_code, num_code_sections, code_size+, kind_data, data_size, terminator | |
-body := types_section, code_section+, data_section | |
+header := | |
+ magic, version, | |
+ kind_types, types_size, | |
+ kind_code, num_code_sections, code_size+, | |
+ kind_data, data_size, | |
+ [kind_container, num_container_sections, container_size+,] | |
+ terminator | |
+body := types_section, code_section+, data_section, container_section* | |
types_section := (inputs, outputs, max_stack_height)+ | |
``` | |
-_note: `,` is a concatenation operator and `+` should be interpreted as "one or more" of the preceding item_ | |
+_note: `,` is a concatenation operator, `+` should be interpreted as "one or more" of the preceding item, and `*` should be interpreted as "zero or more" of the preceding item__ | |
While EOF is extensible, in this document we discuss the first version, EOFv1. | |
@@ -40,6 +50,9 @@ | |
| code_size | 2 bytes | 0x0001-0xFFFF | 16-bit unsigned big-endian integer denoting the length of the code section content | | |
| kind_data | 1 byte | 0x03 | kind marker for data size section | | |
| data_size | 2 bytes | 0x0000-0xFFFF | 16-bit unsigned big-endian integer denoting the length of the data section content | | |
+| kind_container | 1 byte | 0x04 | kind marker for container size section | | |
+| num_container_sections | 2 bytes | 0x0001-0x00FF | 16-bit unsigned big-endian integer denoting the number of the container sections | | |
+| container_size | 2 bytes | 0x0001-0xFFFF | 16-bit unsigned big-endian integer denoting the length of the container section content | | |
| terminator | 1 byte | 0x00 | marks the end of the header | | |
#### Body | |
@@ -52,6 +65,7 @@ | |
| max_stack_height | 2 bytes | 0x0000-0x03FF | maximum number of elements ever placed onto the stack by the code section | | |
| code_section | variable | n/a | arbitrary sequence of bytes | | |
| data_section | variable | n/a | arbitrary sequence of bytes | | |
+| container_section | variable | n/a | arbitrary sequence of bytes | | |
### Container Validation | |
@@ -63,7 +77,20 @@ | |
- the number of code sections must be equal to `types_size / 4` | |
- the number of code sections must not exceed 1024 | |
- `code_size` may not be 0 | |
-- the total size of the container must be `13 + 2*num_code_sections + types_size + code_size[0] + ... + code_size[num_code_sections-1] + data_size` | |
+- the number of container sections must not exceed 256 | |
+- `container_size` may not be 0 | |
+- the total size of the container without container sections must be `13 + 2*num_code_sections + types_size + code_size[0] + ... + code_size[num_code_sections-1] + data_size` | |
+- the total size of the container with at least one container section must be `16 + 2*num_code_sections + types_size + code_size[0] + ... + code_size[num_code_sections-1] + data_size + 2*num_container_sections + container_size[0] + ... + container_size[num_container_sections-1]` | |
+ | |
+## Transaction Types | |
+ | |
+Extend `BlobTransaction` of [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) by adding a new field `initcodes: List[ByteList[MAX_INITCODE_SIZE], 32]`. | |
+ | |
+The `initcodes` can only be accessed via the `CREATE4` instruction (see below). Furthermore it is possible to have a standardised contract creator contract (i.e. written in EVM, but existing at a known address, such as precompiles), which eliminates the need to have a special create transaction (because anyone can call it via a regular transaction). See the appendix below for such a contract. | |
+ | |
+TODO: Decide size limit and cost of this. | |
+ | |
+TODO: We do not want to validate these blobs prior to use to avoid mempool attacks. | |
## Execution Semantics | |
@@ -74,8 +101,10 @@ | |
- Execution starts at the first byte of code section 0, and `pc` is set to 0. | |
- `pc` is scoped to the executing code section | |
- If `pc` exceeds the size of the code section in context, execution aborts with failure. | |
-- `CODECOPY`/`CODESIZE`/`EXTCODECOPY`/`EXTCODESIZE`/`EXTCODEHASH` operate on the entire container. | |
-- The instructions `CALLCODE`, `SELFDESTRUCT`, `JUMP`, `JUMPI`, `PC` are deprecated and therefore result in an exceptional abort. | |
+- The instructions `CALL`, `CALLCODE`, `DELEGATECALL`, `SELFDESTRUCT`, `JUMP`, `JUMPI`, `PC`, `CREATE`, `CREATE2`, `CODESIZE`, `CODECOPY`, `EXTCODESIZE`, `EXTCODECOPY`, `EXTCODEHASH`, `GAS` are deprecated and therefore result in an exceptional abort. | |
+- If the target account of `EXTCODECOPY` is an EOF contract, then it will copy 0 bytes. | |
+- If the target account of `EXTCODEHASH` is an EOF contract, then it will return `0x9dbf3648db8210552e9c4f75c6a1c3057c0ca432043bd648be15fe7be05646f5` (the hash of `EF00`, as if that would be the code). | |
+- If the target account of `EXTCODESIZE` is an EOF contract, then it will return 2. | |
- The instruction `JUMPDEST` is renamed to `NOP` and remains charging 1 gas without any effect. | |
- Note: jumpdest-analysis is not performed anymore. | |
- Perform validation on initcode. | |
@@ -85,6 +114,12 @@ | |
- If validation fails, all creation gas is deducted, similar to exceptional abort during initcode execution, and push 0 to the stack if not part of a create tx. | |
- See validation rules below. | |
- EOF contract may not deploy legacy code | |
+- Legacy contract may not deploy EOF code | |
+- If a `DELEGATECALL` crosses an EOF<>legacy boundary, the it returns 0 to signal failure (i.e. legacy->EOF and EOF->legacy `DELEGATECALL`s are disallowed). | |
+- Introduce a replacement of `CALL` and `DELEGATECALL` in EOF, with two differences to legacy: | |
+ - The `gas_limit` input is removed. | |
+ - The `ouput_offset` and `output_size` is removed. | |
+ - The `gas_limit` will be set to `(gas_left / 64) * 63` (aka as if the caller used `gas()` in place of `gas_limit`). | |
### New Behavior | |
@@ -123,6 +158,51 @@ | |
- if `1024 <= len(stack) + types[idx].max_stack_height`, execution results in an exceptional halt | |
- set `current_code_idx` to `section` | |
- set `pc = 0` | |
+- `CREATE3 (0xf6)` instruction | |
+ - deduct x gas | |
+ - read uint8 operand `initcontainer_index` | |
+ - pops `value`, `salt`, `data_offset`, `data_size` from the stack | |
+ - validate initcode EOF container | |
+ - executes the referenced container in "initcode-mode" | |
+ - calculate `new_address` as `keccak256(0xff || sender || salt || keccak256(initcontainer) || aux_data)[12:]` | |
+ - an unsuccesful execution of initcode results in pushing `0` onto the stack | |
+ - can populate returndata if execution reverted | |
+ - a successful execution returns a `container_index` and `aux_data` | |
+ - load container from the index | |
+ - validate EOF format (header + total size) of the container | |
+ - concatenate data section with aux data and update data size in the header | |
+ - validate EOF sections (e.g. types, codes) | |
+ - set `state[new_address].code` to the updated container | |
+ - push `new_address` onto the stack | |
+ - `RETURN` and `STOP` are not allowed (abort execution) | |
+- `CREATE4 (0xf7)` instruction | |
+ - Works the same as `CREATE3` except: | |
+ - pops one more value from the stack: `tx_initcode_index` | |
+ - takes the initcode container from the transaction `initcodes` array | |
+ - fails (returns 0 on the stack) if such initcode does not exist in the transaction | |
+- `RETURNCONTRACT (0xf8)` instruction | |
+ - loads `uint8` immediate `container_index` | |
+ - pops two values from the stack: `aux_data_offset`, `aux_data_size` | |
+ - cost 0 gas + possible memory expansion for aux data | |
+ - returns `container_index` and aux data to the caller | |
+ - instruction exceptionally aborts if invoked not in "initcode mode" | |
+- `DATALOAD` | |
+ - deduct 3 gas | |
+ - pop one value, `offset`, from the stack | |
+ - read `[offset, offset+32]` from the data section of the active container and push the value to the stack | |
+- `DATALOADN` | |
+ - deduct 2 gas | |
+ - like `DATALOAD`, but takes the offset as a 16-bit immediate value and not from the stack | |
+ - TODO: add clarification for validation | |
+- `DATASIZE` | |
+ - deduct 2 gas | |
+ - push the size of the data section of the active container to the stack | |
+- `DATACOPY` | |
+ - deduct 3 gas | |
+ - pops `mem_offset`, `offset`, `size` from the stack | |
+ - perform memory expansion to `mem_offset + size` and deduct memory expansion cost | |
+ - deduct `3 * (size + 31) // 32` gas for copying | |
+ - read `[offset, offset+size]` from the data section of the active container and write it to memory starting at offset `mem_offset` | |
## Code Validation | |
@@ -143,3 +223,26 @@ | |
- maximum data stack must not exceed 1024 | |
- `types[section].max_stack_height` must match the maximum stack height observed during validation | |
- no section may have more than 127 inputs or outputs | |
\ No newline at end of file | |
+ | |
+## Appendix: Creator Contract | |
+ | |
+```solidity | |
+/// Takes [index][salt][aux_data] as input, | |
+/// creates contract and returns the address or failure otherwise | |
+ | |
+/// aux_data.length can be 0, but the first 2 words are mandatory | |
+let size := calldatasize() | |
+if lt(size, 64) { revert(0, 0) } | |
+ | |
+let initcode_index := calldataload(0) | |
+let salt := calldataload(32) | |
+ | |
+let aux_size := sub(size, 64) | |
+calldatacopy(0, 64, aux_size) | |
+ | |
+let ret := create4(initcode_index, callvalue(), salt, 0, aux_size) | |
+if iszero(ret) { revert(0, 0) } | |
+ | |
+mstore(0, ret) | |
+return(0, 32) | |
+``` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment