diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 77f4ca5282..49ace5fd06 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -169,12 +169,11 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, if pre.Env.BaseFee != nil { vmContext.BaseFee = new(big.Int).Set(pre.Env.BaseFee) } - // NOTE: this has been removed // If random is defined, add it to the vmContext. - // if pre.Env.Random != nil { - // rnd := common.BigToHash(pre.Env.Random) - // vmContext.Random = &rnd - // } + if pre.Env.Random != nil { + rnd := common.BigToHash(pre.Env.Random) + vmContext.Random = &rnd + } // Calculate the BlobBaseFee var excessBlobGas uint64 if pre.Env.ExcessBlobGas != nil { diff --git a/core/chain_makers.go b/core/chain_makers.go index aeae343220..ae6dd82a00 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -148,7 +148,7 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti // instruction will panic during execution if it attempts to access a block number outside // of the range created by GenerateChain. func (b *BlockGen) AddTx(tx *types.Transaction) { - b.addTx(nil, vm.Config{}, tx) + b.addTx(&BlockChain{chainConfig: params.TestChainConfig}, vm.Config{}, tx) } // AddTxWithChain adds a transaction to the generated block. If no coinbase has @@ -166,7 +166,7 @@ func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) { // been set, the block's coinbase is set to the zero address. // The evm interpreter can be customized with the provided vm config. func (b *BlockGen) AddTxWithVMConfig(tx *types.Transaction, config vm.Config) { - b.addTx(nil, config, tx) + b.addTx(&BlockChain{chainConfig: params.TestChainConfig}, config, tx) } // GetBalance returns the balance of the given address at the generated block. diff --git a/core/evm.go b/core/evm.go index 95f5098eb3..0f7b0a4b0c 100644 --- a/core/evm.go +++ b/core/evm.go @@ -32,9 +32,11 @@ import ( "github.com/ava-labs/subnet-evm/consensus" "github.com/ava-labs/subnet-evm/consensus/misc/eip4844" "github.com/ava-labs/subnet-evm/core/types" + "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/predicate" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/libevm" "github.com/ethereum/go-ethereum/log" "github.com/holiman/uint256" ) @@ -47,6 +49,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. @@ -73,15 +77,16 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common // in header.Extra. // This function is used to create a BlockContext when the header Extra data is not fully formed yet and it's more efficient to pass in predicateResults // directly rather than re-encode the latest results when executing each individaul transaction. -func NewEVMBlockContextWithPredicateResults(header *types.Header, chain ChainContext, author *common.Address, predicateResults *predicate.Results) vm.BlockContext { +func NewEVMBlockContextWithPredicateResults(header *types.Header, chain ChainContext, author *common.Address, predicateResults libevm.PredicateResults) vm.BlockContext { return newEVMBlockContext(header, chain, author, predicateResults) } -func newEVMBlockContext(header *types.Header, chain ChainContext, author *common.Address, predicateResults *predicate.Results) vm.BlockContext { +func newEVMBlockContext(header *types.Header, chain ChainContext, author *common.Address, predicateResults libevm.PredicateResults) vm.BlockContext { var ( beneficiary common.Address baseFee *big.Int blobBaseFee *big.Int + random *common.Hash ) // If we don't have an explicit author (i.e. not mining), extract from the header @@ -96,6 +101,14 @@ func newEVMBlockContext(header *types.Header, chain ChainContext, author *common if header.ExcessBlobGas != nil { blobBaseFee = eip4844.CalcBlobFee(*header.ExcessBlobGas) } + + // Durango enables the Shanghai upgrade of eth, which takes place after the + // Merge upgrade. + isDurango := params.GetExtra(chain.Config()).IsDurango(header.Time) + if isDurango { + random = new(common.Hash) + random.SetBytes(header.Difficulty.Bytes()) + } return vm.BlockContext{ CanTransfer: CanTransfer, Transfer: Transfer, @@ -108,6 +121,7 @@ func newEVMBlockContext(header *types.Header, chain ChainContext, author *common BaseFee: baseFee, BlobBaseFee: blobBaseFee, GasLimit: header.GasLimit, + Random: random, } } diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go index 76f992511c..3ed45e0e30 100644 --- a/core/vm/runtime/env.go +++ b/core/vm/runtime/env.go @@ -49,6 +49,7 @@ func NewEnv(cfg *Config) *vm.EVM { GasLimit: cfg.GasLimit, BaseFee: cfg.BaseFee, BlobBaseFee: cfg.BlobBaseFee, + Random: cfg.Random, } return vm.NewEVM(blockContext, txContext, cfg.State, cfg.ChainConfig, cfg.EVMConfig) diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 15aec2d6b2..3131cd075d 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -256,6 +256,8 @@ func (d *dummyChain) GetHeader(h common.Hash, n uint64) *types.Header { return fakeHeader(n, parentHash) } +func (d *dummyChain) Config() *params.ChainConfig { return params.TestChainConfig } + // 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 1fcff4a00b..be1b454b02 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1036,6 +1036,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 @@ -1050,6 +1051,10 @@ func NewChainContext(ctx context.Context, backend ChainContextBackend) *ChainCon return &ChainContext{ctx: ctx, b: backend} } +func (context *ChainContext) Config() *params.ChainConfig { + return context.b.ChainConfig() +} + func (context *ChainContext) Engine() consensus.Engine { return context.b.Engine() } diff --git a/miner/worker.go b/miner/worker.go index 2aaa836c62..d6a93d0579 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -51,6 +51,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/libevm" "github.com/ethereum/go-ethereum/log" "github.com/holiman/uint256" ) @@ -352,7 +353,11 @@ func (w *worker) applyTransaction(env *environment, tx *types.Transaction, coinb } env.predicateResults.SetTxResults(tx.Hash(), results) - blockContext = core.NewEVMBlockContextWithPredicateResults(env.header, w.chain, &coinbase, env.predicateResults) + var predicateResults libevm.PredicateResults + if env.predicateResults != nil { + predicateResults = env.predicateResults + } + blockContext = core.NewEVMBlockContextWithPredicateResults(env.header, w.chain, &coinbase, predicateResults) } else { blockContext = core.NewEVMBlockContext(env.header, w.chain, &coinbase) } diff --git a/params/config_libevm.go b/params/config_libevm.go index 252aa6b44a..c9576cd8da 100644 --- a/params/config_libevm.go +++ b/params/config_libevm.go @@ -31,12 +31,6 @@ func constructRulesExtra(c *gethparams.ChainConfig, r *gethparams.Rules, cEx *Ch return &rules } rules.AvalancheRules = cEx.GetAvalancheRules(timestamp) - if rules.AvalancheRules.IsDurango || rules.AvalancheRules.IsEtna { - // After Durango, geth rules should be used with isMerge set to true. - // We can't call c.Rules because it will recurse back here. - r.IsShanghai = c.IsShanghai(blockNum, timestamp) - r.IsCancun = c.IsCancun(blockNum, timestamp) - } rules.gethrules = *r // Initialize the stateful precompiles that should be enabled at [blockTimestamp]. diff --git a/params/hooks_libevm.go b/params/hooks_libevm.go index 8340fccd1b..81019b4f8b 100644 --- a/params/hooks_libevm.go +++ b/params/hooks_libevm.go @@ -15,20 +15,13 @@ import ( "github.com/ava-labs/subnet-evm/precompile/precompileconfig" "github.com/ava-labs/subnet-evm/vmerrs" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/libevm" gethparams "github.com/ethereum/go-ethereum/params" ) func (r RulesExtra) JumpTable() interface{} { - switch { - case r.gethrules.IsCancun: - return &vm.SubnetEVMCancunInstructionSet - case r.IsDurango: - return &vm.SubnetEVMDurangoInstructionSet - case r.IsSubnetEVM: - return &vm.SubnetEVMInstructionSet - } + // XXX: This does not account for the any possible differences in EIP-3529 + // Do not merge without verifying. return nil } diff --git a/tests/gen_stenv.go b/tests/gen_stenv.go index 9bcdb5d162..f026b455e3 100644 --- a/tests/gen_stenv.go +++ b/tests/gen_stenv.go @@ -17,12 +17,12 @@ var _ = (*stEnvMarshaling)(nil) func (s stEnv) MarshalJSON() ([]byte, error) { type stEnv struct { Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` - Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"` + Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"optional"` Random *math.HexOrDecimal256 `json:"currentRandom" gencodec:"optional"` GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` - BaseFee *math.HexOrDecimal256 `json:"currentBaseFee" gencodec:"optional"` + BaseFee *math.HexOrDecimal256 `json:"currentBaseFee" gencodec:"optional"` ExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas" gencodec:"optional"` } var enc stEnv @@ -41,12 +41,12 @@ func (s stEnv) MarshalJSON() ([]byte, error) { func (s *stEnv) UnmarshalJSON(input []byte) error { type stEnv struct { Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` - Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"` + Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"optional"` Random *math.HexOrDecimal256 `json:"currentRandom" gencodec:"optional"` GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` - BaseFee *math.HexOrDecimal256 `json:"currentBaseFee" gencodec:"optional"` + BaseFee *math.HexOrDecimal256 `json:"currentBaseFee" gencodec:"optional"` ExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas" gencodec:"optional"` } var dec stEnv @@ -57,10 +57,9 @@ func (s *stEnv) UnmarshalJSON(input []byte) error { return errors.New("missing required field 'currentCoinbase' for stEnv") } s.Coinbase = common.Address(*dec.Coinbase) - if dec.Difficulty == nil { - return errors.New("missing required field 'currentDifficulty' for stEnv") + if dec.Difficulty != nil { + s.Difficulty = (*big.Int)(dec.Difficulty) } - s.Difficulty = (*big.Int)(dec.Difficulty) if dec.Random != nil { s.Random = (*big.Int)(dec.Random) } diff --git a/tests/init.go b/tests/init.go index ce23cf0ce1..1648706da2 100644 --- a/tests/init.go +++ b/tests/init.go @@ -222,6 +222,7 @@ var Forks = map[string]*params.ChainConfig{ IstanbulBlock: big.NewInt(0), BerlinBlock: big.NewInt(0), LondonBlock: big.NewInt(0), + ShanghaiTime: utils.NewUint64(0), CancunTime: utils.NewUint64(0), }, ¶ms.ChainConfigExtra{ diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 8c4f97d8cf..02b7492f25 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -95,7 +95,7 @@ type stPostState struct { //go:generate go run github.com/fjl/gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go type stEnv struct { Coinbase common.Address `json:"currentCoinbase" gencodec:"required"` - Difficulty *big.Int `json:"currentDifficulty" gencodec:"required"` + Difficulty *big.Int `json:"currentDifficulty" gencodec:"optional"` Random *big.Int `json:"currentRandom" gencodec:"optional"` GasLimit uint64 `json:"currentGasLimit" gencodec:"required"` Number uint64 `json:"currentNumber" gencodec:"required"` @@ -300,10 +300,14 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh // Prepare the EVM. txContext := core.NewEVMTxContext(msg) - context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase) + chainCtx := &withChainConfig{nil, config} + context := core.NewEVMBlockContext(block.Header(), chainCtx, &t.json.Env.Coinbase) context.GetHash = vmTestBlockHash context.BaseFee = baseFee - if params.GetExtra(config).IsSubnetEVM(0) && t.json.Env.Random != nil { + context.Random = nil + if config.IsLondon(new(big.Int)) && t.json.Env.Random != nil { + rnd := common.BigToHash(t.json.Env.Random) + context.Random = &rnd context.Difficulty = big.NewInt(0) } if config.IsCancun(new(big.Int), block.Time()) && t.json.Env.ExcessBlobGas != nil { diff --git a/tests/state_test_util_ext.go b/tests/state_test_util_ext.go new file mode 100644 index 0000000000..e0f897eaf5 --- /dev/null +++ b/tests/state_test_util_ext.go @@ -0,0 +1,41 @@ +// (c) 2024, Ava Labs, Inc. +// +// This file is a derived work, based on the go-ethereum library whose original +// notices appear below. +// +// It is distributed under a license compatible with the licensing terms of the +// original code from which it is derived. +// +// Much love to the original authors for their work. +// ********** +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package tests + +import ( + "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/params" +) + +type withChainConfig struct { + core.ChainContext + config *params.ChainConfig +} + +func (w withChainConfig) Config() *params.ChainConfig { + return w.config +}