Optional access lists (#2930)

Adds a transaction type which contains an access list, a list of addresses and storage keys that the transaction plans to access. Accesses outside the list are possible, but become more expensive. Intended as a mitigation to contract breakage risks introduced by EIP 2929 and simultaneously a stepping stone toward broader use of access lists in other contexts.
This commit is contained in:
vbuterin 2020-09-02 10:02:59 +08:00 committed by GitHub
parent 12e25bb307
commit c506f8ee11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 133 additions and 1 deletions

View File

@ -147,7 +147,7 @@ A second way to look at the risks is to explore mitigations. First of all, the e
But there are ways to further expand the usability of this pattern. One possibility is to add a `POKE` precompile, which would take an address and a storage key as input and allow transactions that attempt to "rescue" stuck contracts by pre-poking all of the storage slots that they will access. This works even if the address only accepts transactions from the contract, and works in many other contexts with present gas limits. The only case where this will not work would be the case where a transaction call _must_ go from an EOA straight into a specific contract that then sub-calls another contract.
Another option is [EIP-2930](./EIP-2930.md), which would have a similar effect to `POKE` but is more general: it also works for the EOA -> contract -> contract case, and generally should work for all known cases of breakage due to gas cost increases. This option is more complex, though it is arguably a stepping stone toward access lists being used for other use cases (regenesis, account abstraction, SSA all demand access lists).
Another option is [EIP-2930](./eip-2930.md), which would have a similar effect to `POKE` but is more general: it also works for the EOA -> contract -> contract case, and generally should work for all known cases of breakage due to gas cost increases. This option is more complex, though it is arguably a stepping stone toward access lists being used for other use cases (regenesis, account abstraction, SSA all demand access lists).
## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).

132
EIPS/eip-2930.md Normal file
View File

@ -0,0 +1,132 @@
---
eip: 2930
title: Optional access lists
author: Vitalik Buterin (@vbuterin), Martin Swende (@holiman)
discussions-to: https://ethereum-magicians.org/t/eip-2930-optional-access-lists/4561
status: Draft
type: Standards Track
category: Core
created: 2020-08-29
requires: 2718, 2929
---
## Simple Summary
Adds a transaction type which contains an access list, a list of addresses and storage keys that the transaction plans to access. Accesses outside the list are possible, but become more expensive.
## Abstract
We introduce a new [EIP-2718](./eip-2718.md) transaction type, with the format `rlp([3, [nonce, gasPrice, gasLimit, to, value, data, access_list, senderV, senderR, senderS]])`.
The `access_list` specifies a list of addresses and storage keys; these addresses and storage keys are added into the `accessed_addresses` and `accessed_storage_keys` global sets (introduced in [EIP-2929](./eip-2929.md)). A gas cost is charged, though at a discount relative to the cost of accessing outside the list.
## Motivation
This EIP serves two functions:
1. Mitigates contract breakage risks introduced by [EIP-2929](./eip-2929.md), as transactions could pre-specify and pre-pay for the accounts and storage slots that the transaction plans to access; as a result, in the actual execution, the SLOAD and EXT* opcodes would only cost 100 gas: low enough that it would not only prevent breakage due to that EIP but also "unstuck" any contracts that became stuck due to EIP 1884.
2. Introduces the access list format and the logic for handling the format. This logic can later be repurposed for many other purposes, including block-wide witnesses, use in ReGenesis, moving toward static state access over time, and more.
## Specification
### Parameters
| Constant | Value |
| - | - |
| `FORK_BLOCK` | TBD |
| `ACCESS_LIST_STORAGE_KEY_COST` | 1900 |
| `ACCESS_LIST_ADDRESS_COST` | 2400 |
As of `FORK_BLOCK_NUMBER`, we introduce a new transaction type, with the format `rlp([3, [nonce, gasPrice, gasLimit, to, value, data, access_list, senderV, senderR, senderS]])`. The signing hash would be the hash of the RLP of the entire data structure but popping the last three items representing the signature from the inner list: `rlp([3, [nonce, gasPrice, gasLimit, to, value, data, access_list]])`.
For the transaction to be valid, `access_list` must be of type `[[{20 bytes}, [{32 bytes}...]]...]`, where `...` means "zero or more of the thing to the left". For example, the following is a valid access list (all hex strings would in reality be in byte representation):
```
[
[
"0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae",
[
"0x0000000000000000000000000000000000000000000000000000000000000003",
"0x0000000000000000000000000000000000000000000000000000000000000007"
]
],
[
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
[]
]
]
```
At the beginning of execution (ie. at the same time as the `21000 + 4 * zeroes + 12 * nonzeroes` start gas is charged), we charge additional gas for the access list: `ACCESS_LIST_ADDRESS_COST` gas per address and `ACCESS_LIST_STORAGE_KEY_COST` gas per storage key. For example, the above example would be charged `ACCESS_LIST_ADDRESS_COST * 2 + ACCESS_LIST_STORAGE_KEY_COST * 2` gas.
Note that non-unique addresses and storage keys are not disallowed, though they will be charged for multiple times, and aside from the higher gas cost there is no other difference in execution flow or outcome from multiple-inclusion of a value as opposed to the recommended single-inclusion.
The address and storage keys would be immediately loaded into the `accessed_addresses` and `accessed_storage_keys` global sets; this can be done using the following logic (which doubles as a specification-in-code of validation of the RLP-decoded access list)
```python
def process_access_list(access_list) -> Tuple[List[Set[Address], Set[Pair[Address, Bytes32]]], int]:
accessed_addresses = set()
accessed_storage_keys = set()
gas_cost = 0
assert isinstance(access_list, list)
for item in access_list:
assert isinstance(item, list) and len(item) == 2
# Validate and add the address
address = item[0]
assert isinstance(address, bytes) and len(address) == 20
accessed_addresses.add(address)
gas_cost += ACCESS_LIST_ADDRESS_COST
# Validate and add the storage keys
assert isinstance(item[1], list)
for key in item[1]:
assert isinstance(key, bytes) and len(key) == 32
accessed_storage_keys.add((address, key))
gas_cost += ACCESS_LIST_STORAGE_KEY_COST
return (
accessed_addresses,
accessed_storage_keys,
gas_cost
)
```
The access list is NOT charged per-byte fees like tx data is; the per-item costs described above are meant to cover the bandwidth costs of the access list data in addition to the costs of accessing those accounts and storage keys when evaluating the transaction.
## Rationale
### Charging less for accesses in the access list
This is done to encourage transactions to use the access list as much as possible, and because processing transactions is easier when their storage reads are predictable (because clients can pre-load the data from databases and/or ask for witnesses at the time the transaction is received, or at least load the data in parallel).
### Allowing duplicates
This is done because it maximizes simplicity, avoiding questions of what to prevent duplication against: just between two addresses/keys in the access list, between the access list and the tx sender/recipient/newly created contract, other restrictions? Because gas is charged per item, there is no gain and only cost in including a value in the access list twice, so this should not lead to extra chain bloat in practice.
### Signature signs over the entire envelope including the wrapper
This is done to ensure that the transaction cannot be "re-interpreted" as a transaction of a different type.
## Backwards Compatibility
This EIP does make it more gas-expensive to perform "unexpected" SLOADs and account accesses. Because gas is prepaid and so does not affect fixed-gas local calls, it does not break contracts in the way that previous gas cost increases would risk. However, it does make applications that heavily rely on storage access much less economically viable.
## Test Cases
TBD
## Implementation
TBD
## Security Considerations
### Access list generation
Access lists are difficult to construct in real-time in many situations, and this is exacerbated in environments where there is a high time lag between transaction generation and signing or simplicity of the transaction generator is highly valued (eg. either or both may apply in hardware wallets).
However, this EIP proposes only a 10% initial discount to access lists, so there is almost no cost to not bothering with access list generation and only making a simple transaction. The cost of accessing state outside the access list is expected to be ramped up in future hard forks over time as tools are developed and access list generation becomes more mature.
### Transaction size bloating
Average block size will increase as a result of access lists being used. However, the per-byte cost of access lists is `1900 / 32 = 59.375` for storage keys and `2400 / 20 = 120` for addresses, making it much more expensive than calldata; hence, worst-case block size will not increase. Additionally, increases in average block size will be partially compensated for by the ability to pre-fetch storage at time of receiving a transaction and/or load storage in parallel upon receiving a block.
## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).