diff --git a/core/evm.go b/core/evm.go index abb5570f1d..70895690ec 100644 --- a/core/evm.go +++ b/core/evm.go @@ -36,6 +36,8 @@ type ChainContext interface { // GetHeader returns the header corresponding to the hash/number argument pair. GetHeader(common.Hash, uint64) *types.Header + + Config() *params.ChainConfig } // NewEVMBlockContext creates a new context for use in the EVM. @@ -103,39 +105,52 @@ func GetHashFn(ref *types.Header, chain ChainContext) vm.GetHashFunc { return common.Hash{} } - // 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) - } - 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 + var eip2935Ancestor *types.Header + + // If there's no hash cache yet, make one and fill + // it with all 256 hashes. We have to get all blocks + // since for eip2935 we need to query the 256th + // ancestor to find out if it had eip2935 enabled. + if len(cache) == 0 { + cache = append(cache, ref.ParentHash) + lastKnownHash := cache[len(cache)-1] lastKnownNumber := ref.Number.Uint64() - uint64(len(cache)) + limit := uint64(257) + if ref.Number.Uint64() < limit { + limit = ref.Number.Uint64() + } - for { + for ref.Number.Uint64()-lastKnownNumber < limit { header := chain.GetHeader(lastKnownHash, lastKnownNumber) if header == nil { break } + if lastKnownNumber == n { + eip2935Ancestor = header + } 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) } } + // Check if the 256th ancestor had already activated eip 2935. + // If the ancestor is nil, then this is the case of a testnet + // that forked within 256 blocks of the genesis. + if eip2935Ancestor != nil && chain.Config().IsPrague(eip2935Ancestor.Number, eip2935Ancestor.Time) { + var key common.Hash + binary.BigEndian.PutUint64(key[24:], n) + return statedb.GetState(params.HistoryStorageAddress, key) + } + + // if the 256th ancestor did not have eip2935 enabled, try + // to get the value from the cache. + if idx := ref.Number.Uint64() - n - 1; idx < uint64(len(cache)) { + return cache[idx] + } + + // something went wrong while building the cache return common.Hash{} } } diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index e71760bb23..d569b20587 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -247,6 +247,14 @@ func (d *dummyChain) GetHeader(h common.Hash, n uint64) *types.Header { return fakeHeader(n, parentHash) } +func (d *dummyChain) Config() *params.ChainConfig { + return nil +} + +func (d *dummyChain) GetHeaderByNumber(n uint64) *types.Header { + return d.GetHeader(common.Hash{}, n) +} + // TestBlockhash tests the blockhash operation. It's a bit special, since it internally // requires access to a chain reader. func TestBlockhash(t *testing.T) { diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index ee479d7139..98d149f09a 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1043,6 +1043,7 @@ func (diff *BlockOverrides) Apply(blockCtx *vm.BlockContext) { type ChainContextBackend interface { Engine() consensus.Engine HeaderByNumber(context.Context, rpc.BlockNumber) (*types.Header, error) + ChainConfig() *params.ChainConfig } // ChainContext is an implementation of core.ChainContext. It's main use-case @@ -1071,6 +1072,10 @@ func (context *ChainContext) GetHeader(hash common.Hash, number uint64) *types.H return header } +func (context *ChainContext) Config() *params.ChainConfig { + return context.b.ChainConfig() +} + func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, overrides *StateOverride, blockOverrides *BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) { if err := overrides.Apply(state); err != nil { return nil, err