Repository navigation
[#4887] Let the host serve BLOCKHASH from the EIP-2935 history contract - #89
Closed
isaacdoidge wants to merge 3 commits into
Closed
isaacdoidge wants to merge 3 commits into
isaacdoidge wants to merge 3 commits into
Conversation
Add `BlockHashSource` to the EVM configuration (`Cfg::block_hash_source`,
`CfgEnv::block_hash_source`). `Database`, the default, keeps the upstream
read through `Database::block_hash`. `HistoryContract { floor }` reads slot
`n % HISTORY_SERVE_WINDOW` of the history contract instead: a non-zero slot
is the hash, a zero slot below `floor` is a zero hash, and a zero slot at or
above `floor` is a host error (`ContextError::Custom`).
The slot is read with the new `JournalTr::storage_value_unwarmed`, which
returns the journal's present value before falling back to the database,
because the current block's history write stays in the journal until the
block is finalized. The read neither warms nor journals the account or the
slot, so BLOCKHASH keeps its fixed 20 gas (Ethereum mainnet's pricing, not
EIP-7709's added storage charge) and leaves the gas of later reads unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A probe contract executes BLOCKHASH(n) and then calls the history contract's `get(n)` twice, measuring the gas of each call. The tests cover a hash written by the same block's history write while it is still in the journal (with a different stale value in the database), a hash in the database only, a zero slot below and at the floor, the default database source, and the heights the opcode answers without a host read. The gas of the two `get` calls shows that the BLOCKHASH read warmed neither the contract's account nor its slot. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hash.
With `BlockHashSource::HistoryContract { floor }`, a zero slot for a block
at or above `floor` is a height the history contract should hold but does
not. The host now refers it to `Database::block_hash`, whose result or
error is the answer; a database error is stored as `ContextError::Db` and
fails the transaction as `EVMError::Database`. A host whose database
cannot serve block hashes thereby fails the transaction with a typed
database error. A zero slot below `floor` is still a zero hash.
The tests check both outcomes of the database read, at the floor, above
it, and for a slot that wraps.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
aregng
reviewed
Nov 13, 2026
| }; | ||
| if !value.is_zero() { | ||
| Some(B256::from(value)) | ||
| } else if requested_number < floor { |
There was a problem hiding this comment.
if requested_number < floor { can be done early to avoid journal read.
Collaborator
Author
|
Closed unmerged. Entropy-Foundation/smr-moonshot#5001 serves BLOCKHASH from the EIP-2935 history contract through the existing Database::block_hash hook in smr-moonshot's EvmDB, so no revm change is needed. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
cfg::BlockHashSourceselects whereHost::block_hashreads from.Databaseis the default and callsDatabase::block_hash, as upstream does.HistoryContract { floor }reads slotn % HISTORY_SERVE_WINDOWof the EIP-2935 history contract atHISTORY_STORAGE_ADDRESS.Cfggainsblock_hash_source(), andCfgEnvgains the field plusset_block_hash_source/with_block_hash_source.HistoryContract { floor }:floorreturns zero, because no EVM block recorded that height;flooris a height the contract should hold but does not, and is referred toDatabase::block_hash, whose result or error is the answer. A database error is stored asContextError::Dband surfaces asEVMError::Database. The node's database serves no block hashes, so on SupraEVM this fails the block, as every database error does.JournalTr::storage_value_unwarmedreturns a slot's present value without loading or warming the account or slot and without a journal entry. It reads the journal's state first, then treats an account created in the journal as having empty storage (assloaddoes), then falls back to the database. The journal is read first because the current block's history write stays in the journal until the block's state is finalized.blockhashopcode, its 256-block range and its 20 gas are unchanged. The read does not warm the slot, so later accesses to the history contract are priced as before. This keeps Ethereum mainnet'sBLOCKHASHcost rather than EIP-7709's (Draft) extra storage charge.Motivation
smr-moonshot#4887: serve
BLOCKHASHfrom the history ring that smr-moonshot#4885 writes before every block, so that the opcode no longer depends on the node's certified-block table and its pruning floor. The node selectsHistoryContractwith the height of the block that applied the EVM genesis asfloor. The node-side change is smr-moonshot#5001, which pins this branch; its pin moves to the tag this PR is released in.Notes for review
JournalTr::storage_value_unwarmedis a required method. It is implemented forJournaland for theBackendinexamples/cheatcode_inspector.history_storage.rshelpers are nowpub(crate)so the new test module can reuse them.Test plan
cargo fmt --all --checkRUSTFLAGS=-Dwarnings cargo clippy --workspace --all-targets --all-featurescargo check --target riscv32imac-unknown-none-elf --no-default-features(and-p op-revm)cargo check --no-default-features -p revm --features=serdeRUSTDOCFLAGS="--cfg docsrs -D warnings" cargo doc --workspace --all-features --no-deps --document-private-itemscargo nextest run --workspace: all-features 569 passed; default features 567;--no-default-features567cargo test --workspace --all-features --dochistory_block_hash):BLOCKHASH, the history contract's account and slot are still cold (the firstgetcosts 4500 gas more than the second);EVMError::Databasewhen the database'sblock_hashfails, including across the slot wrap;Database::block_hash;storage_value_unwarmed.load_account+sloadfails both not-warming checks.cargo hack --feature-powerset,zepter, thekzg-rsno_std entry, forge/genesis-bytecode jobs (no features or Solidity changed).🤖 Generated with Claude Code