implement hash reads from smart contract

This commit is contained in:
Guillaume Ballet 2024-01-22 12:19:54 +01:00
parent b4460a8f74
commit 6164aadba8
4 changed files with 45 additions and 28 deletions

View file

@ -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{}
}
}

View file

@ -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) {

View file

@ -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()
}

View file

@ -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())))
}
}