Skip to content

Instantly share code, notes, and snippets.

@axic
Created March 22, 2023 15:54
Show Gist options
  • Save axic/85c0ed993b63f8087adf1ed8c521d790 to your computer and use it in GitHub Desktop.
Save axic/85c0ed993b63f8087adf1ed8c521d790 to your computer and use it in GitHub Desktop.
--- 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