Skip to content
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

Update EIP-4762: reworked gas schedule from interop #8550

Merged
merged 15 commits into from
Jun 27, 2024
120 changes: 64 additions & 56 deletions EIPS/eip-4762.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
eip: 4762
title: Statelessness gas cost changes
description: Changes the gas schedule to reflect the costs of creating a witness by requiring clients update their database layout to match.
author: Guillaume Ballet (@gballet), Vitalik Buterin (@vbuterin), Dankrad Feist (@dankrad)
author: Guillaume Ballet (@gballet), Vitalik Buterin (@vbuterin), Dankrad Feist (@dankrad), Ignacio Hagopian (@jsign), Tanishq Jasoria (@tanishqjasoria), Gajinder Singh (@g11tech)
discussions-to: https://ethereum-magicians.org/t/eip-4762-statelessness-gas-cost-changes/8714
status: Draft
type: Standards Track
Expand Down Expand Up @@ -39,47 +39,42 @@ We define access events as follows. When an access event takes place, the access

#### Access events for account headers

When a non-precompile address is the target of a `CALL`, `CALLCODE`, `DELEGATECALL`, `SELFDESTRUCT`, `EXTCODESIZE`, or `EXTCODECOPY` opcode, or is the target address of a contract creation whose initcode starts execution, process these access events:
When:

```
(address, 0, VERSION_LEAF_KEY)
(address, 0, CODE_SIZE_LEAF_KEY)
```
1. a non-precompile address is the target of a `*CALL`, `SELFDESTRUCT`, `EXTCODESIZE`, or `EXTCODECOPY` opcode,
2. a non-precompile address is the target address of a contract creation whose initcode starts execution,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3. any address is the target of the `BALANCE` opcode
4. a _deployed_ contract calls `CODECOPY`
gballet marked this conversation as resolved.
Show resolved Hide resolved

If a call is value-bearing (ie. it transfers nonzero wei), whether or not the callee is a precompile, process these two access events:
process this access events:
gballet marked this conversation as resolved.
Show resolved Hide resolved

```
(caller_address, 0, BALANCE_LEAF_KEY)
(callee_address, 0, BALANCE_LEAF_KEY)
(address, 0, BASIC_DATA_LEAF_KEY)
```

When a contract is created, process these access events:

```
(contract_address, 0, VERSION_LEAF_KEY)
(contract_address, 0, NONCE_LEAF_KEY)
(contract_address, 0, BALANCE_LEAF_KEY)
gballet marked this conversation as resolved.
Show resolved Hide resolved
(contract_address, 0, CODE_KECCAK_LEAF_KEY)
(contract_address, 0, CODE_SIZE_LEAF_KEY)
```
Note: a non-value-bearing `SELFDESTRUCT` or `*CALL`, targetting a precompile, will not cause the `BASIC_DATA_LEAF_KEY` to be added to the witness.

If the `BALANCE` opcode is called targeting some address, process this access event:
If a `*CALL` or `SELFDESTRUCT` is value-bearing (ie. it transfers nonzero wei), whether or not the callee is a precompile, process this additional access event:

```
(address, 0, BALANCE_LEAF_KEY)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we also touch the VERSION thus BASIC_DATA_LEAF_KEY?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: add version

(origin, 0, BASIC_DATA_LEAF_KEY)
```

If the `SELFDESTRUCT` opcode is called by some caller_address targeting some target_address (regardless of whether it’s value-bearing or not), process access events of the form:
Note: when checking for the existence of the callee, the existence check is done by validating that there is an extension-and-suffix tree at the corresponding stem, and does not rely on `CODEHASH_LEAF_KEY`.

When calling `EXTCODEHASH` on a non-precompile target, process the access event:

```
(caller_address, 0, BALANCE_LEAF_KEY)
(target_address, 0, BALANCE_LEAF_KEY)
(address, 0, CODEHASH_LEAF_KEY)
```

If the `EXTCODEHASH` opcode is called targeting some address, process an access event of the form:
Note that precompiles are excluded, as their hashes are known to the client.

When a contract is created, process these access events:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


```
(address, 0, CODEHASH_LEAF_KEY)
(contract_address, 0, BASIC_DATA_LEAF_KEY)
(contract_address, 0, CODEHASH_LEAF_KEY)
```

#### Access events for storage
Expand Down Expand Up @@ -120,34 +115,29 @@ We define **write events** as follows. Note that when a write takes place, an ac

#### Write events for account headers

When a nonzero-balance-sending `CALL` or `SELFDESTRUCT` with a given sender and recipient takes place, process these write events:
When a nonzero-balance-sending `*CALL` or `SELFDESTRUCT` with a given sender and recipient takes place, process these write events:

```
(sender, 0, BALANCE_LEAF_KEY)
(recipient, 0, BALANCE_LEAF_KEY)
(origin, 0, BASIC_DATA_LEAF_KEY)
(target, 0, BASIC_DATA_LEAF_KEY)
```

When a contract creation is initialized, process these write events:
if no account exists at `callee_address`, also process:

```
(contract_address, 0, VERSION_LEAF_KEY)
(contract_address, 0, NONCE_LEAF_KEY)
(target, 0, CODEHASH_LEAF_KEY)
```

Only if the value sent with the creation is nonzero, also process:
When a contract creation is initialized, process these write events:

```
(contract_address, 0, BALANCE_LEAF_KEY)
(contract_address, 0, BASIC_DATA_LEAF_KEY)
```

When a contract is created, process these write events:

```
(contract_address, 0, VERSION_LEAF_KEY)
(contract_address, 0, NONCE_LEAF_KEY)
(contract_address, 0, BALANCE_LEAF_KEY)
(contract_address, 0, CODE_KECCAK_LEAF_KEY)
(contract_address, 0, CODE_SIZE_LEAF_KEY)
(contract_address, 0, CODEHASH_LEAF_KEY)
```

#### Write events for storage
Expand All @@ -174,36 +164,31 @@ When a contract is created, process the write events:

For `i` in `0 ... (len(code)+30)//31`.

Note: since no access list existed for code up until this EIP, note that no warm costs are charged for code accesses.

### Transactions

#### Access events

For a transaction, make these access events:

```
(tx.origin, 0, VERSION_LEAF_KEY)
(tx.origin, 0, BALANCE_LEAF_KEY)
(tx.origin, 0, NONCE_LEAF_KEY)
(tx.origin, 0, CODE_SIZE_LEAF_KEY)
(tx.origin, 0, CODE_KECCAK_LEAF_KEY)
(tx.target, 0, VERSION_LEAF_KEY)
(tx.target, 0, BALANCE_LEAF_KEY)
(tx.target, 0, NONCE_LEAF_KEY)
(tx.target, 0, CODE_SIZE_LEAF_KEY)
(tx.target, 0, CODE_KECCAK_LEAF_KEY)
(tx.origin, 0, BASIC_DATA_LEAF_KEY)
(tx.origin, 0, CODEHASH_LEAF_KEY)
Comment on lines +176 to +177
Copy link
Contributor

@jsign jsign Jun 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Edit: comment quoting code is not correct, I meant L173 (tx.target, 0, BASIC_DATA_LEAF_KEY)]

If we have a tx (non-value-bearing) targeting a precompile or system contract, we don't need to touch BASIC_DATA. (i.e: stateless client knows the code-size)

We can still argue that this rule should be avoided to avoid a border case. But considering we didn't do any similar simplification for precompiles/system contracts in other places in the EIP (i.e. we avoid adding in the witness for them as much as possible), then maybe not doing it here is actually adding a border case (we have to remember this is an exception), not removing it.

No strong opinions, just surfacing this.
cc @gballet @g11tech @gabrocheleau @tanishqjasoria @matkt

(tx.target, 0, BASIC_DATA_LEAF_KEY)
gballet marked this conversation as resolved.
Show resolved Hide resolved
(tx.target, 0, CODEHASH_LEAF_KEY)
```

#### Write events

```
(tx.origin, 0, NONCE_LEAF_KEY)
(tx.origin, 0, BASIC_DATA_LEAF_KEY)
```

if `value` is non-zero:
If `value` is non-zero:

```
(tx.origin, 0, BALANCE_LEAF_KEY)
(tx.target, 0, BALANCE_LEAF_KEY)
(tx.target, 0, BASIC_DATA_LEAF_KEY)
```

### Witness gas costs
Expand All @@ -216,15 +201,15 @@ Remove the following gas costs:

Reduce gas cost:

* `CREATE` to 1000
* `CREATE`/`CREATE2` to 1000

|Constant |Value|
|-|-|
|`WITNESS_BRANCH_COST` |1900|
|`WITNESS_BRANCH_COST`|1900|
|`WITNESS_CHUNK_COST` |200|
|`SUBTREE_EDIT_COST` |3000|
|`CHUNK_EDIT_COST` |500|
|`CHUNK_FILL_COST` |6200|
|`CHUNK_EDIT_COST` |500|
|`CHUNK_FILL_COST` |6200|

When executing a transaction, maintain four sets:

Expand All @@ -251,6 +236,13 @@ When a **write** event of `(address, sub_key, leaf_key)` occurs, perform the fol

Note that tree keys can no longer be emptied: only the values `0...2**256-1` can be written to a tree key, and 0 is distinct from `None`. Once a tree key is changed from `None` to not-`None`, it can never go back to `None`.

Note that values should only be added to the witness if there is sufficient gas to cover their associated event costs.
gballet marked this conversation as resolved.
Show resolved Hide resolved

`CREATE*` and `*CALL` reserve 1/64th of the gas before the nested execution. In order to match the behavior of this charge with the pre-fork behavior of access lists:

* this minimum 1/64th gas reservation is checked **AFTER** charging the witness costs when performing a `CALL`, `CODECALL`, `DELEGATECALL` or`STATICCALL`
* this 1/64th of the gas is subtracted **BEFORE** charging the witness costs when performing a `CREATE` or `CREATE2`

### Replacement for access lists

We replace [EIP-2930](./eip-2930.md) access lists with an SSZ structure of the form:
Expand All @@ -268,6 +260,22 @@ class AccessSubtree(Container):
elements: BitVector[256]
```

### Block-level operations

None of:

* Precompile accounts, system contract accounts and slots of a system contract that are accessed during a system call,
* The coinbase account
* Withdrawal accounts

are warm at the start of a transaction.

Note: When (and only when) calling a system contract _via a system call_, the code chunks and account should not appear in the witness.

### Account abstraction

TODO : still waiting on a final decision between 7702 and 3074

## Rationale

### Gas reform
Expand Down
40 changes: 20 additions & 20 deletions EIPS/eip-6800.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
eip: 6800
title: Ethereum state using a unified verkle tree
description: This introduces a new Verkle state tree alongside the existing MPT.
author: Vitalik Buterin (@vbuterin), Dankrad Feist (@dankrad), Kevaundray Wedderburn (@kevaundray), Guillaume Ballet (@gballet), Piper Merriam (@pipermerriam), Gottfried Herold (@GottfriedHerold)
author: Vitalik Buterin (@vbuterin), Dankrad Feist (@dankrad), Kevaundray Wedderburn (@kevaundray), Guillaume Ballet (@gballet), Piper Merriam (@pipermerriam), Gottfried Herold (@GottfriedHerold), Ignacio Hagopian (@jsign), Tanishq Jasoria (@tanishqjasoria), Gajinder Singh (@g11tech), Danno Ferrin (@shemnon)
discussions-to: https://ethereum-magicians.org/t/proposed-verkle-tree-scheme-for-ethereum-state/5805
status: Draft
type: Standards Track
Expand Down Expand Up @@ -115,11 +115,8 @@ Instead of a two-layer structure as in the Patricia tree, in the Verkle tree we

| Parameter | Value |
| --------------------- | ------- |
| VERSION_LEAF_KEY | 0 |
| BALANCE_LEAF_KEY | 1 |
| NONCE_LEAF_KEY | 2 |
| CODE_KECCAK_LEAF_KEY | 3 |
| CODE_SIZE_LEAF_KEY | 4 |
| BASIC_DATA_LEAF_KEY | 0 |
| CODE_KECCAK_LEAF_KEY | 1 |
| HEADER_STORAGE_OFFSET | 64 |
| CODE_OFFSET | 128 |
| VERKLE_NODE_WIDTH | 256 |
Expand Down Expand Up @@ -157,25 +154,28 @@ def get_tree_key(address: Address32, tree_index: int, sub_index: int):
bytes([sub_index])
)

def get_tree_key_for_version(address: Address32):
return get_tree_key(address, 0, VERSION_LEAF_KEY)

def get_tree_key_for_balance(address: Address32):
return get_tree_key(address, 0, BALANCE_LEAF_KEY)

def get_tree_key_for_nonce(address: Address32):
return get_tree_key(address, 0, NONCE_LEAF_KEY)
def get_tree_key_for_basic_data(address: Address32):
return get_tree_key(address, 0, BASIC_DATA_LEAF_KEY)

# Backwards compatibility for EXTCODEHASH
def get_tree_key_for_code_keccak(address: Address32):
return get_tree_key(address, 0, CODE_KECCAK_LEAF_KEY)

# Backwards compatibility for EXTCODESIZE
def get_tree_key_for_code_size(address: Address32):
return get_tree_key(address, 0, CODE_SIZE_LEAF_KEY)
```

When any account header field is set, the `version` is also set to zero. The `code_keccak` and `code_size` fields are set upon contract creation.
An account's `version`, `balance`, `nonce` and `code_size` fields are packed in the value found at `BASIC_DATA_LEAF_KEY`:

| Name | Offset | Size |
| ----------- | ------ | ---- |
| `version` | 0 | 1 |
| `code_size` | 5 | 3 |
| `nonce` | 8 | 8 |
| `balance` | 16 | 16 |

Bytes `1..4` are reserved for future use.

Note: the code size is stored on 3 bytes. To allow for an extension to 4 bytes without changing the account version is possible, reserved byte #4 should be allocated last.

When any account header field is set, the `version` field is also set to zero. The `code_keccak` and `code_size` fields are set upon contract or EoA creation.
gballet marked this conversation as resolved.
Show resolved Hide resolved

#### Code

Expand Down Expand Up @@ -239,7 +239,7 @@ Note that storage slots in the same size `VERKLE_NODE_WIDTH` range (ie. a range

#### Fork

TODO - see specific EIP
Described in [EIP-7612](./eip-7612.md).

#### Access events

Expand Down
Loading