diff --git a/core/evm.go b/core/evm.go index c4801dc797..abb5570f1d 100644 --- a/core/evm.go +++ b/core/evm.go @@ -17,6 +17,7 @@ package core import ( + "encoding/binary" "math/big" "github.com/ethereum/go-ethereum/common" @@ -24,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/params" ) // ChainContext supports retrieving headers and consensus parameters from the @@ -89,40 +91,51 @@ func NewEVMTxContext(msg *Message) vm.TxContext { } // GetHashFn returns a GetHashFunc which retrieves header hashes by number -func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash { +func GetHashFn(ref *types.Header, chain ChainContext) vm.GetHashFunc { // Cache will initially contain [refHash.parent], // Then fill up with [refHash.p, refHash.pp, refHash.ppp, ...] var cache []common.Hash - return func(n uint64) common.Hash { + return func(n uint64, statedb vm.StateDB, eip2935 bool) common.Hash { if ref.Number.Uint64() <= n { // This situation can happen if we're doing tracing and using // block overrides. return common.Hash{} } - // If there's no hash cache yet, make one - if len(cache) == 0 { - cache = append(cache, ref.ParentHash) - } - if idx := ref.Number.Uint64() - n - 1; idx < uint64(len(cache)) { - return cache[idx] - } - // No luck in the cache, but we can start iterating from the last element we already know - lastKnownHash := cache[len(cache)-1] - lastKnownNumber := ref.Number.Uint64() - uint64(len(cache)) - for { - header := chain.GetHeader(lastKnownHash, lastKnownNumber) - if header == nil { - break + // Use the cache if it is within 256 blocks from the head. + if n >= ref.Number.Uint64()-256 { + // If there's no hash cache yet, make one + if len(cache) == 0 { + cache = append(cache, ref.ParentHash) } - cache = append(cache, header.ParentHash) - lastKnownHash = header.ParentHash - lastKnownNumber = header.Number.Uint64() - 1 - if n == lastKnownNumber { - return lastKnownHash + if idx := ref.Number.Uint64() - n - 1; idx < uint64(len(cache)) { + return cache[idx] + } + // No luck in the cache, but we can start iterating from the last element we already know + lastKnownHash := cache[len(cache)-1] + lastKnownNumber := ref.Number.Uint64() - uint64(len(cache)) + + for { + header := chain.GetHeader(lastKnownHash, lastKnownNumber) + if header == nil { + break + } + cache = append(cache, header.ParentHash) + lastKnownHash = header.ParentHash + lastKnownNumber = header.Number.Uint64() - 1 + if n == lastKnownNumber { + return lastKnownHash + } + } + } else { + if eip2935 { + var key common.Hash + binary.BigEndian.PutUint64(key[24:], n) + return statedb.GetState(params.HistoryStorageAddress, key) } } + return common.Hash{} } } diff --git a/core/vm/evm.go b/core/vm/evm.go index 088b18aaa4..879fb11749 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -34,7 +34,7 @@ type ( TransferFunc func(StateDB, common.Address, common.Address, *big.Int) // GetHashFunc returns the n'th block hash in the blockchain // and is used by the BLOCKHASH EVM op code. - GetHashFunc func(uint64) common.Hash + GetHashFunc func(uint64, StateDB, bool) common.Hash ) func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 56ff350201..ff14fb295a 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -441,13 +441,17 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( } var upper, lower uint64 upper = interpreter.evm.Context.BlockNumber.Uint64() - if upper < 257 { - lower = 0 - } else { + evm := interpreter.evm + + // After Prague, the values preceding FORKNUM will be 0, + // as requested in EIP-2935. So it's fine to allow 0 as + // the lower bound. + if !evm.chainRules.IsPrague && upper >= 257 { lower = upper - 256 } + if num64 >= lower && num64 < upper { - num.SetBytes(interpreter.evm.Context.GetHash(num64).Bytes()) + num.SetBytes(evm.Context.GetHash(num64, evm.StateDB, evm.chainRules.IsPrague).Bytes()) } else { num.Clear() } diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index abb0a20e24..3e1d78470f 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -50,7 +50,7 @@ type Config struct { Random *common.Hash State *state.StateDB - GetHashFn func(n uint64) common.Hash + GetHashFn vm.GetHashFunc } // sets defaults on the config @@ -90,7 +90,7 @@ func setDefaults(cfg *Config) { cfg.BlockNumber = new(big.Int) } if cfg.GetHashFn == nil { - cfg.GetHashFn = func(n uint64) common.Hash { + cfg.GetHashFn = func(n uint64, _ vm.StateDB, _ bool) common.Hash { return common.BytesToHash(crypto.Keccak256([]byte(new(big.Int).SetUint64(n).String()))) } }