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 package core
import ( import (
"encoding/binary"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -24,6 +25,7 @@ import (
"github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
) )
// ChainContext supports retrieving headers and consensus parameters from the // 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 // 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], // Cache will initially contain [refHash.parent],
// Then fill up with [refHash.p, refHash.pp, refHash.ppp, ...] // Then fill up with [refHash.p, refHash.pp, refHash.ppp, ...]
var cache []common.Hash 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 { if ref.Number.Uint64() <= n {
// This situation can happen if we're doing tracing and using // This situation can happen if we're doing tracing and using
// block overrides. // block overrides.
return common.Hash{} 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 { // Use the cache if it is within 256 blocks from the head.
header := chain.GetHeader(lastKnownHash, lastKnownNumber) if n >= ref.Number.Uint64()-256 {
if header == nil { // If there's no hash cache yet, make one
break if len(cache) == 0 {
cache = append(cache, ref.ParentHash)
} }
cache = append(cache, header.ParentHash) if idx := ref.Number.Uint64() - n - 1; idx < uint64(len(cache)) {
lastKnownHash = header.ParentHash return cache[idx]
lastKnownNumber = header.Number.Uint64() - 1 }
if n == lastKnownNumber { // No luck in the cache, but we can start iterating from the last element we already know
return lastKnownHash 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{} return common.Hash{}
} }
} }

View file

@ -34,7 +34,7 @@ type (
TransferFunc func(StateDB, common.Address, common.Address, *big.Int) TransferFunc func(StateDB, common.Address, common.Address, *big.Int)
// GetHashFunc returns the n'th block hash in the blockchain // GetHashFunc returns the n'th block hash in the blockchain
// and is used by the BLOCKHASH EVM op code. // 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) { 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 var upper, lower uint64
upper = interpreter.evm.Context.BlockNumber.Uint64() upper = interpreter.evm.Context.BlockNumber.Uint64()
if upper < 257 { evm := interpreter.evm
lower = 0
} else { // 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 lower = upper - 256
} }
if num64 >= lower && num64 < upper { 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 { } else {
num.Clear() num.Clear()
} }

View file

@ -50,7 +50,7 @@ type Config struct {
Random *common.Hash Random *common.Hash
State *state.StateDB State *state.StateDB
GetHashFn func(n uint64) common.Hash GetHashFn vm.GetHashFunc
} }
// sets defaults on the config // sets defaults on the config
@ -90,7 +90,7 @@ func setDefaults(cfg *Config) {
cfg.BlockNumber = new(big.Int) cfg.BlockNumber = new(big.Int)
} }
if cfg.GetHashFn == nil { 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()))) return common.BytesToHash(crypto.Keccak256([]byte(new(big.Int).SetUint64(n).String())))
} }
} }