Skip to content

EVM: Store EIP-4788 history in block-global state#5430

Open
mpapierski wants to merge 2 commits into
casper-network:feat-evmfrom
mpapierski:evm-eip-4788-storage-improvements
Open

EVM: Store EIP-4788 history in block-global state#5430
mpapierski wants to merge 2 commits into
casper-network:feat-evmfrom
mpapierski:evm-eip-4788-storage-improvements

Conversation

@mpapierski

Copy link
Copy Markdown
Collaborator

Summary

This PR replaces repeated EIP-4788 bytecode execution with a native, Global State-backed implementation.

Previously, each block executed the beacon-roots contract to update its EVM storage, and every lookup executed the deployed bytecode and performed storage reads. We now write the parent block hash directly into a fixed-size ring buffer in Casper Global State and serve canonical calls through the EVM precompile handler.

What changed

A new BlockGlobalAddr::BlockParentHash { slot } key stores an atomic (timestamp, block_hash) tuple. The slot is derived using the EIP-4788 rule:

timestamp_seconds % HISTORY_BUFFER_LENGTH

The write occurs at the same pre-transaction point as the previous EVM system call and uses the same parent hash. Casper’s millisecond block timestamp is converted to EVM seconds before deriving the slot.

Direct CALL and STATICCALL operations to the EIP-4788 address are handled natively. The implementation:

  • Requires exactly 32 bytes of calldata.
  • Rejects zero timestamps and values larger than u64.
  • Reads the tuple through the current tracking copy.
  • Verifies that the stored timestamp exactly matches the requested timestamp.
  • Returns the raw 32-byte hash or reverts on missing or overwritten entries.

The lookup does not use locally retained block history, so full-history and fast-synced nodes execute against the same Merkleized Global State.

Preserving EVM behavior

The EIP-4788 bytecode and code hash remain deployed for EXTCODE* compatibility.

Only calls that use the predeploy’s own storage context are intercepted. DELEGATECALL and CALLCODE continue to execute the deployed bytecode because they run it against the caller’s storage context. Replacing those calls with the native Global State lookup would change normal EVM semantics.

Crate ownership and compatibility

EIP-specific constants, bytecode, hashing, slot derivation, and tuple encoding now live in casper-storage. casper-types retains only the generic block-global key representation.

Existing BlockGlobalAddr and Key::BlockGlobal encodings remain backward compatible. Legacy block-global addresses are still one byte, and existing block-global keys remain 33 bytes.

Gas behavior

The native lookup charges no additional interpreted-bytecode or SLOAD gas. Ordinary call and address-access costs still apply, and the address is not initially warmed.

Activation

This change assumes activation before a network has populated the old EIP-4788 contract-storage ring. If that assumption does not hold, the existing bounded ring must be migrated during activation.

Testing

Coverage includes:

  • Legacy key serialization compatibility.
  • Ring-buffer writes and overwrites.
  • Reads from a freshly committed state root.
  • More than 8,191 writes.
  • Valid native lookups and zero hashes.
  • Missing and overwritten timestamps.
  • Malformed and oversized calldata.
  • Native bytecode bypass.
  • Block-level writes when no transactions are present.

Replace beacon-roots system calls with atomic ring-buffer writes through
TrackingCopyExt. Handle direct CALL and STATICCALL lookups natively while
preserving bytecode for EXTCODE, DELEGATECALL, and CALLCODE semantics.

Move EIP-4788 helpers to casper-storage and retain legacy key encoding.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants