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

Add EIP: Block-level Warming #9246

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions EIPS/eip-9999.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
---
eip: 9999
title: Block-level warming
description: Warm addresses and storage keys over the duration of a block
author: Toni Wahrstätter (@nerolation), Alex Stokes (@ralexstokes), Ansgar Dietrichs (@adietrichs)
discussions-to: ""

Check failure on line 6 in EIPS/eip-9999.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

preamble header `discussions-to` is not a valid URL

error[preamble-discussions-to]: preamble header `discussions-to` is not a valid URL --> EIPS/eip-9999.md:6:16 | 6 | discussions-to: "" | ^^^ relative URL without a base | = help: see https://ethereum.github.io/eipw/preamble-discussions-to/

Check failure on line 6 in EIPS/eip-9999.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

preamble header `discussions-to` should point to a thread on ethereum-magicians.org

error[preamble-re-discussions-to]: preamble header `discussions-to` should point to a thread on ethereum-magicians.org --> EIPS/eip-9999.md:6:16 | 6 | discussions-to: "" | ^^^ required pattern was not matched | = info: the pattern in question: `^https://ethereum-magicians.org/t/[^/]+/[0-9]+$` = help: see https://ethereum.github.io/eipw/preamble-re-discussions-to/

Check failure on line 6 in EIPS/eip-9999.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

preamble header `discussions-to` is not a valid URL

error[preamble-discussions-to]: preamble header `discussions-to` is not a valid URL --> EIPS/eip-9999.md:6:16 | 6 | discussions-to: "" | ^^^ relative URL without a base | = help: see https://ethereum.github.io/eipw/preamble-discussions-to/

Check failure on line 6 in EIPS/eip-9999.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

preamble header `discussions-to` should point to a thread on ethereum-magicians.org

error[preamble-re-discussions-to]: preamble header `discussions-to` should point to a thread on ethereum-magicians.org --> EIPS/eip-9999.md:6:16 | 6 | discussions-to: "" | ^^^ required pattern was not matched | = info: the pattern in question: `^https://ethereum-magicians.org/t/[^/]+/[0-9]+$` = help: see https://ethereum.github.io/eipw/preamble-re-discussions-to/
status: Draft
type: Standards Track
category: Core
created: 2025-01-15
---

## Abstract

This proposal introduces block-level address and storage key warming, allowing accessed addresses and storage keys to maintain their "warm" status throughout an entire block's execution. Accessed slots can be effectively cached at the block level, allowing for this opimization.

## Motivation

Currently, the EVM's storage slot warming mechanism operates at the transaction level, requiring each transaction to "warm up" slots independently, even when accessing the same storage locations within the same block. This design does not take advantage of the fact that modern node implementations can effectively cache storage access patterns at the block level. By extending the slot warming duration to the block level, we can:

1. Reduce redundant warming costs for frequently accessed slots
2. Better align gas costs with actual computational overhead
3. Improve overall network throughput without compromising security.

As of Jan. 2025, the empirically messured efficiency gains are around 5%.


## Specification

### Mechanics

When a storage slot is accessed within a block:
1. The first access to a slot in a block incurs the cold access cost as of [EIP-2929](./eip-2929).

Check failure on line 33 in EIPS/eip-9999.md

View workflow job for this annotation

GitHub Actions / Markdown Linter

Lists should be surrounded by blank lines [Context: "1. The first access to a slot ..."]

EIPS/eip-9999.md:33 MD032/blanks-around-lists Lists should be surrounded by blank lines [Context: "1. The first access to a slot ..."]

Check failure on line 33 in EIPS/eip-9999.md

View workflow job for this annotation

GitHub Actions / Markdown Linter

Lists should be surrounded by blank lines [Context: "1. The first access to a slot ..."]

EIPS/eip-9999.md:33 MD032/blanks-around-lists Lists should be surrounded by blank lines [Context: "1. The first access to a slot ..."]
nerolation marked this conversation as resolved.
Show resolved Hide resolved
2. All subsequent accesses to the same slot within the same block incur only the warm access cost as of [EIP-2929](./eip-2929).
nerolation marked this conversation as resolved.
Show resolved Hide resolved
3. The warm/cold status resets at block boundaries

### Block Processing

1. At the start of each block:
* Initialize two empty sets `block_level_accessed_addresses` and `block_level_accessed_storage_keys`
nerolation marked this conversation as resolved.
Show resolved Hide resolved
2. For each transaction in the block:
* Before processing storage access:
* Check if touched address is in `block_level_accessed_addresses`
* If yes: charge `GAS_WARM_ACCESS`
nerolation marked this conversation as resolved.
Show resolved Hide resolved
* If no:
* Charge `GAS_COLD_ACCOUNT_ACCESS`
nerolation marked this conversation as resolved.
Show resolved Hide resolved
* Add address to `block_level_accessed_addresses`
* Check if storage key is in `block_level_accessed_storage_keys`
* If yes: charge `GAS_WARM_ACCESS`
nerolation marked this conversation as resolved.
Show resolved Hide resolved
* If no:
* Charge `GAS_COLD_SLOAD`
nerolation marked this conversation as resolved.
Show resolved Hide resolved
* Add storage key to `block_level_accessed_storage_keys`


### Implementation Details

The implementation modifies the block execution process to maintain block-level sets of accessed addresses and storage slots.

#### Block-Level Storage Management

```python
def apply_body(...):
# Initialize block-level tracking sets
block_level_accessed_addresses = set()
block_level_accessed_storage_keys = set()
nerolation marked this conversation as resolved.
Show resolved Hide resolved

for i, tx in enumerate(map(decode_transaction, transactions)):
# Create environment with block-level context
env = vm.Environment(
# ... other parameters ...
block_level_accessed_addresses=block_level_accessed_addresses,
block_level_accessed_storage_keys=block_level_accessed_storage_keys
)

# Process transaction and update block-level sets
gas_used, accessed_addresses, accessed_storage_keys, logs, error = process_transaction(env, tx)
block_level_accessed_addresses += accessed_addresses
block_level_accessed_storage_keys += accessed_storage_keys
```

This code shows how block-level slot warming is implemented at the execution layer. The `block_level_accessed_addresses` and `block_level_accessed_storage_keys` sets are maintained throughout the block's execution and passed to each transaction's environment.

#### Transaction Processing

```python
def process_transaction(env: vm.Environment, tx: Transaction) -> Tuple[Uint, Tuple[Log, ...], Optional[Exception]]:
preaccessed_addresses = set()
preaccessed_storage_keys = set()

# Add block-level pre-accessed slots
preaccessed_addresses.add(env.block_level_accessed_addresses)
preaccessed_storage_keys.add(env.block_level_accessed_storage_keys)

# Handle access lists from transaction
...
```

This adds the block-level-accessed addresses and storage keys to the preaccessed addresses and storage keys.
As a result, from the perspective of a transaction, block-level accessed addresses and storage keys are treated the same as precompiles or the coinbase address.
nerolation marked this conversation as resolved.
Show resolved Hide resolved

```python
def process_message_call(message: Message, env: Environment) -> MessageCallOutput:
return MessageCallOutput(
# ... other fields ...
accessed_addresses=evm.accessed_addresses,
accessed_storage_keys=evm.accessed_storage_keys
)
```

The message call processing tracks accessed addresses and storage keys during execution, which are then propagated back up to the transaction level and ultimately to the block level.

## Rationale

The proposal builds on several key observations:

1. **Caching Efficiency**: Modern Ethereum clients already implement sophisticated caching mechanisms at the block level. Extending address and storage key warming to match this caching behavior better aligns gas costs with actual computational costs.
nerolation marked this conversation as resolved.
Show resolved Hide resolved

2. **Backward Compatibility**: The worst-case scenario for any transaction remains unchanged - it will never pay more than the current cold access cost for its first access to a slot.

3. **First Access Warms For All**: Under the proposed mechanism, the first transaction in a block that accesses and warms multiple addresses or storage slots bears the entire cost of warming, even if subsequent transactions benefit from those already-warmed slots without incurring additional costs. This approach is deemed acceptable because early execution within a block—where warming typically occurs—is generally occupied for transactions placed by professional builders aiming for top-of-block execution. Since the resulting cost discrepancy is relatively minor, a more complex cost-sharing model is avoided in favor of maintaining simplicity.
nerolation marked this conversation as resolved.
Show resolved Hide resolved
An alternative to the one-warms-for-all mechanism involves distributing the warming costs across all accesses within a block. In this approach, each transaction must at least be able to pay the cold access cost. Once all transactions in the block are executed, the total warming costs are evenly distributed, and transaction originators are refunded accordingly.
nerolation marked this conversation as resolved.
Show resolved Hide resolved

4. **Alternative Block Warming Windows**: Instead of applying warming at the block level, more advanced multi-block warming approaches can be considered. Potential options include:
nerolation marked this conversation as resolved.
Show resolved Hide resolved
* Warming addresses and storage keys over the duration of an epoch
* Using a ring buffer spanning `x` blocks

Since the Execution Layer (EL) currently operates without regard to epoch boundaries, it may be preferable to maintain this design. Therefore, the second option, involving a ring buffer of size x, could be more suitable.

## Backwards Compatibility

This change is not backward compatible and requires a hard fork activation. However, it does not introduce any breaking changes for existing contracts, as:

1. All existing transactions will continue to work as before
2. Gas costs will only decrease, never increase
3. Contract logic depending on gas costs will remain valid, as the worst-case scenario is unchanged

## Reference Implementation

TBD, first draft here:

Check failure on line 139 in EIPS/eip-9999.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

non-relative link or image

error[markdown-rel-links]: non-relative link or image --> EIPS/eip-9999.md | 139 | TBD, first draft here: | = help: see https://ethereum.github.io/eipw/markdown-rel-links/

Check failure on line 139 in EIPS/eip-9999.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

non-relative link or image

error[markdown-rel-links]: non-relative link or image --> EIPS/eip-9999.md | 139 | TBD, first draft here: | = help: see https://ethereum.github.io/eipw/markdown-rel-links/
https://github.com/nerolation/execution-specs/tree/block-level-warming

## Security Considerations

1. **Memory Usage**: The set of warm slots is still bounded by block gas limit / minimum gas cost per slot access, preventing DoS attacks through excessive memory usage.

3. **MEV Considerations**: Block producers can optimize transaction ordering to maximize slot warming benefits for themselves. Third-party block builders can backrun transactions that warmed specific addresses or storage keys.

## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).
Loading