From 461995d44c41c9b69886ce0e1ed5d53b4861755c Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Sun, 7 Nov 2021 14:12:57 +0100 Subject: [PATCH 01/12] core: implement eip-4399 random opcode --- cmd/evm/internal/t8ntool/execution.go | 2 ++ cmd/evm/internal/t8ntool/gen_stenv.go | 6 +++++ consensus/beacon/consensus.go | 6 +---- core/blockchain_reader.go | 6 +++++ core/evm.go | 1 + core/genesis.go | 2 +- core/vm/evm.go | 1 + core/vm/instructions.go | 6 +++++ core/vm/instructions_test.go | 34 +++++++++++++++++++++++++++ core/vm/interpreter.go | 3 +++ core/vm/jump_table.go | 12 ++++++++++ core/vm/opcodes.go | 3 ++- eth/catalyst/api.go | 9 +++++++ params/config.go | 1 + tests/gen_stenv.go | 15 ++++++++---- tests/state_test_util.go | 18 +++++++++++--- 16 files changed, 110 insertions(+), 15 deletions(-) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index dfdde4217396d..28af534ec0bbd 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -67,6 +67,7 @@ type ommer struct { type stEnv struct { Coinbase common.Address `json:"currentCoinbase" gencodec:"required"` Difficulty *big.Int `json:"currentDifficulty"` + Random *big.Int `json:"currentRandom"` ParentDifficulty *big.Int `json:"parentDifficulty"` GasLimit uint64 `json:"currentGasLimit" gencodec:"required"` Number uint64 `json:"currentNumber" gencodec:"required"` @@ -81,6 +82,7 @@ type stEnv struct { type stEnvMarshaling struct { Coinbase common.UnprefixedAddress Difficulty *math.HexOrDecimal256 + Random *math.HexOrDecimal256 ParentDifficulty *math.HexOrDecimal256 GasLimit math.HexOrDecimal64 Number math.HexOrDecimal64 diff --git a/cmd/evm/internal/t8ntool/gen_stenv.go b/cmd/evm/internal/t8ntool/gen_stenv.go index 1bb3c6a46b0c5..a6d774cdabcf0 100644 --- a/cmd/evm/internal/t8ntool/gen_stenv.go +++ b/cmd/evm/internal/t8ntool/gen_stenv.go @@ -18,6 +18,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) { type stEnv struct { Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"` + Random *math.HexOrDecimal256 `json:"currentRandom"` ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"` GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` @@ -31,6 +32,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) { var enc stEnv enc.Coinbase = common.UnprefixedAddress(s.Coinbase) enc.Difficulty = (*math.HexOrDecimal256)(s.Difficulty) + enc.Random = (*math.HexOrDecimal256)(s.Random) enc.ParentDifficulty = (*math.HexOrDecimal256)(s.ParentDifficulty) enc.GasLimit = math.HexOrDecimal64(s.GasLimit) enc.Number = math.HexOrDecimal64(s.Number) @@ -48,6 +50,7 @@ func (s *stEnv) UnmarshalJSON(input []byte) error { type stEnv struct { Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"` + Random *math.HexOrDecimal256 `json:"currentRandom"` ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"` GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` @@ -69,6 +72,9 @@ func (s *stEnv) UnmarshalJSON(input []byte) error { if dec.Difficulty != nil { s.Difficulty = (*big.Int)(dec.Difficulty) } + if dec.Random != nil { + s.Random = (*big.Int)(dec.Random) + } if dec.ParentDifficulty != nil { s.ParentDifficulty = (*big.Int)(dec.ParentDifficulty) } diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 9467fea67bef7..1fd7deb872fb4 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -43,7 +43,6 @@ var ( // error types into the consensus package. var ( errTooManyUncles = errors.New("too many uncles") - errInvalidMixDigest = errors.New("invalid mix digest") errInvalidNonce = errors.New("invalid nonce") errInvalidUncleHash = errors.New("invalid uncle hash") ) @@ -182,10 +181,7 @@ func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, pa if len(header.Extra) > 32 { return fmt.Errorf("extra-data longer than 32 bytes (%d)", len(header.Extra)) } - // Verify the seal parts. Ensure the mixhash, nonce and uncle hash are the expected value. - if header.MixDigest != (common.Hash{}) { - return errInvalidMixDigest - } + // Verify the seal parts. Ensure the nonce and uncle hash are the expected value. if header.Nonce != beaconNonce { return errInvalidNonce } diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index 9e966df4e0134..4be3e91d958f7 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -349,6 +349,12 @@ func (bc *BlockChain) GetVMConfig() *vm.Config { return &bc.vmConfig } +// SetVMConfig sets the vm config. +// Warning: might not be threadsafe with other components +func (bc *BlockChain) SetVMConfig(config vm.Config) { + bc.vmConfig = config +} + // SetTxLookupLimit is responsible for updating the txlookup limit to the // original one stored in db if the new mismatches with the old one. func (bc *BlockChain) SetTxLookupLimit(limit uint64) { diff --git a/core/evm.go b/core/evm.go index 6c67fc43762ce..edbe7b7a4b489 100644 --- a/core/evm.go +++ b/core/evm.go @@ -61,6 +61,7 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common Difficulty: new(big.Int).Set(header.Difficulty), BaseFee: baseFee, GasLimit: header.GasLimit, + Random: header.MixDigest, } } diff --git a/core/genesis.go b/core/genesis.go index 557440d08aa16..1d17f298a4fb3 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -294,7 +294,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { if g.GasLimit == 0 { head.GasLimit = params.GenesisGasLimit } - if g.Difficulty == nil { + if g.Difficulty == nil && g.Mixhash == (common.Hash{}) { head.Difficulty = params.GenesisDifficulty } if g.Config != nil && g.Config.IsLondon(common.Big0) { diff --git a/core/vm/evm.go b/core/vm/evm.go index 2c7880b3bf385..6fb7bc470b86b 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -75,6 +75,7 @@ type BlockContext struct { Time *big.Int // Provides information for TIME Difficulty *big.Int // Provides information for DIFFICULTY BaseFee *big.Int // Provides information for BASEFEE + Random common.Hash // Provides information for RANDOM } // TxContext provides the EVM with information about a transaction. diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 4eda3bf5317f0..db507c481100e 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -477,6 +477,12 @@ func opDifficulty(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) return nil, nil } +func opRandom(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + v := new(uint256.Int).SetBytes((interpreter.evm.Context.Random.Bytes())) + scope.Stack.push(v) + return nil, nil +} + func opGasLimit(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.Context.GasLimit)) return nil, nil diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index e67acd83271f2..1bf6aadfc3ae9 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -21,6 +21,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "math/big" "testing" "github.com/ethereum/go-ethereum/common" @@ -654,3 +655,36 @@ func TestCreate2Addreses(t *testing.T) { } } } + +func TestRandom(t *testing.T) { + type testcase struct { + name string + random common.Hash + } + + for _, tt := range []testcase{ + {name: "empty hash", random: common.Hash{}}, + {name: "1", random: common.Hash{0}}, + {name: "emptyCodeHash", random: emptyCodeHash}, + {name: "hash(0x010203)", random: crypto.Keccak256Hash([]byte{0x01, 0x02, 0x03})}, + } { + var ( + env = NewEVM(BlockContext{Random: tt.random}, TxContext{}, nil, params.TestChainConfig, Config{RandomOpcode: true}) + stack = newstack() + pc = uint64(0) + evmInterpreter = env.interpreter + ) + opRandom(&pc, evmInterpreter, &ScopeContext{nil, stack, nil}) + if len(stack.data) != 1 { + t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data)) + } + actual := stack.pop() + expected, overflow := uint256.FromBig(new(big.Int).SetBytes(tt.random.Bytes())) + if overflow { + t.Errorf("Testcase %v: invalid overflow", tt.name) + } + if actual.Cmp(expected) != 0 { + t.Errorf("Testcase %v: expected %x, got %x", tt.name, expected, actual) + } + } +} diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 1660e3ce0ff1f..83e20640f40ae 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -29,6 +29,7 @@ type Config struct { Debug bool // Enables debugging Tracer EVMLogger // Opcode logger NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls) + RandomOpcode bool // Enables the random opcode EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages JumpTable *JumpTable // EVM instruction table, automatically populated if unset @@ -69,6 +70,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { // If jump table was not initialised we set the default one. if cfg.JumpTable == nil { switch { + case cfg.RandomOpcode: + cfg.JumpTable = &mergeInstructionSet case evm.chainRules.IsLondon: cfg.JumpTable = &londonInstructionSet case evm.chainRules.IsBerlin: diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 6dea5d81f33cb..eef3b53d8c668 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -54,6 +54,7 @@ var ( istanbulInstructionSet = newIstanbulInstructionSet() berlinInstructionSet = newBerlinInstructionSet() londonInstructionSet = newLondonInstructionSet() + mergeInstructionSet = newMergeInstructionSet() ) // JumpTable contains the EVM opcodes supported at a given fork. @@ -77,6 +78,17 @@ func validate(jt JumpTable) JumpTable { return jt } +func newMergeInstructionSet() JumpTable { + instructionSet := newLondonInstructionSet() + instructionSet[RANDOM] = &operation{ + execute: opRandom, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + } + return validate(instructionSet) +} + // newLondonInstructionSet returns the frontier, homestead, byzantium, // contantinople, istanbul, petersburg, berlin and london instructions. func newLondonInstructionSet() JumpTable { diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index a1833e5109668..e16db6fcb954f 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -99,6 +99,7 @@ const ( CHAINID OpCode = 0x46 SELFBALANCE OpCode = 0x47 BASEFEE OpCode = 0x48 + RANDOM OpCode = 0x44 // Same as DIFFICULTY ) // 0x50 range - 'storage' and execution. @@ -275,7 +276,7 @@ var opCodeToString = map[OpCode]string{ COINBASE: "COINBASE", TIMESTAMP: "TIMESTAMP", NUMBER: "NUMBER", - DIFFICULTY: "DIFFICULTY", + DIFFICULTY: "DIFFICULTY", // TODO (MariusVanDerWijden) rename to RANDOM post merge GASLIMIT: "GASLIMIT", CHAINID: "CHAINID", SELFBALANCE: "SELFBALANCE", diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 3c0b6d9e43fc8..72a4b0a99a967 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -134,6 +134,7 @@ type blockExecutionEnv struct { func (env *blockExecutionEnv) commitTransaction(tx *types.Transaction, coinbase common.Address) error { vmconfig := *env.chain.GetVMConfig() + vmconfig.RandomOpcode = true snap := env.state.Snapshot() receipt, err := core.ApplyTransaction(env.chain.Config(), env.chain, &coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, vmconfig) if err != nil { @@ -276,6 +277,9 @@ func (api *ConsensusAPI) ExecutePayloadV1(params ExecutableDataV1) (ExecutePaylo if td.Cmp(ttd) < 0 { return api.invalid(), fmt.Errorf("can not execute payload on top of block with low td got: %v threshold %v", td, ttd) } + conf := api.eth.BlockChain().GetVMConfig() + conf.RandomOpcode = true + api.eth.BlockChain().SetVMConfig(*conf) if err := api.eth.BlockChain().InsertBlockWithoutSetHead(block); err != nil { return api.invalid(), err } @@ -318,7 +322,11 @@ func (api *ConsensusAPI) assembleBlock(parentHash common.Hash, params *PayloadAt GasLimit: parent.GasLimit(), // Keep the gas limit constant in this prototype Extra: []byte{}, // TODO (MariusVanDerWijden) properly set extra data Time: params.Timestamp, + MixDigest: params.Random, } + conf := api.eth.BlockChain().GetVMConfig() + conf.RandomOpcode = true + api.eth.BlockChain().SetVMConfig(*conf) if config := api.eth.BlockChain().Config(); config.IsLondon(header.Number) { header.BaseFee = misc.CalcBaseFee(config, parent.Header()) } @@ -432,6 +440,7 @@ func ExecutableDataToBlock(params ExecutableDataV1) (*types.Block, error) { Time: params.Timestamp, BaseFee: params.BaseFeePerGas, Extra: params.ExtraData, + MixDigest: params.Random, // TODO (MariusVanDerWijden) add params.Random to header once required } block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */) diff --git a/params/config.go b/params/config.go index 36482f2380a41..18c367bbea30a 100644 --- a/params/config.go +++ b/params/config.go @@ -672,6 +672,7 @@ type Rules struct { // Rules ensures c's ChainID is not nil. func (c *ChainConfig) Rules(num *big.Int) Rules { + // TODO (MariusVanDerWijden) replace isMerge after the merge once we know the merge block chainID := c.ChainID if chainID == nil { chainID = new(big.Int) diff --git a/tests/gen_stenv.go b/tests/gen_stenv.go index ecf7af8503825..29fbce121385b 100644 --- a/tests/gen_stenv.go +++ b/tests/gen_stenv.go @@ -17,7 +17,8 @@ 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"` @@ -26,6 +27,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) { var enc stEnv enc.Coinbase = common.UnprefixedAddress(s.Coinbase) enc.Difficulty = (*math.HexOrDecimal256)(s.Difficulty) + enc.Random = (*math.HexOrDecimal256)(s.Random) enc.GasLimit = math.HexOrDecimal64(s.GasLimit) enc.Number = math.HexOrDecimal64(s.Number) enc.Timestamp = math.HexOrDecimal64(s.Timestamp) @@ -37,7 +39,8 @@ 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"` @@ -51,10 +54,12 @@ 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) + } + if dec.Random != nil { + s.Random = (*big.Int)(dec.Random) } - s.Difficulty = (*big.Int)(dec.Difficulty) if dec.GasLimit == nil { return errors.New("missing required field 'currentGasLimit' for stEnv") } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index f7fb08bfbc8d7..8a5920f295886 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -80,7 +80,8 @@ type stPostState struct { 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"` Timestamp uint64 `json:"currentTimestamp" gencodec:"required"` @@ -90,6 +91,7 @@ type stEnv struct { type stEnvMarshaling struct { Coinbase common.UnprefixedAddress Difficulty *math.HexOrDecimal256 + Random *math.HexOrDecimal256 GasLimit math.HexOrDecimal64 Number math.HexOrDecimal64 Timestamp math.HexOrDecimal64 @@ -218,8 +220,12 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase) context.GetHash = vmTestBlockHash context.BaseFee = baseFee + if t.json.Env.Random != nil { + vmconfig.RandomOpcode = true + context.Random = common.BigToHash(t.json.Env.Random) + context.Difficulty = nil + } evm := vm.NewEVM(context, txContext, statedb, config, vmconfig) - // Execute the message. snapshot := statedb.Snapshot() gaspool := new(core.GasPool) @@ -268,7 +274,7 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc, snapshotter boo } func (t *StateTest) genesis(config *params.ChainConfig) *core.Genesis { - return &core.Genesis{ + genesis := &core.Genesis{ Config: config, Coinbase: t.json.Env.Coinbase, Difficulty: t.json.Env.Difficulty, @@ -277,6 +283,12 @@ func (t *StateTest) genesis(config *params.ChainConfig) *core.Genesis { Timestamp: t.json.Env.Timestamp, Alloc: t.json.Pre, } + if t.json.Env.Random != nil { + // Post-Merge + genesis.Mixhash = common.BigToHash(t.json.Env.Random) + genesis.Difficulty = nil + } + return genesis } func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int) (core.Message, error) { From 62e9c1a8d88fa78e57e69ae261fbc487a6838a12 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 20 Dec 2021 10:57:06 +0100 Subject: [PATCH 02/12] core: make vmconfig threadsafe --- core/blockchain.go | 9 +++++++-- core/blockchain_reader.go | 8 ++++++-- eth/catalyst/api.go | 1 - params/config.go | 1 - 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 85591931ee42b..662553ad0871a 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -209,6 +209,7 @@ type BlockChain struct { processor Processor // Block transaction processor interface forker *ForkChoice vmConfig vm.Config + vmConfigMu *sync.RWMutex } // NewBlockChain returns a fully initialised block chain using information @@ -245,6 +246,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par futureBlocks: futureBlocks, engine: engine, vmConfig: vmConfig, + vmConfigMu: &sync.RWMutex{}, } bc.forker = NewForkChoice(bc, shouldPreserve) bc.validator = NewBlockValidator(chainConfig, bc, engine) @@ -1572,6 +1574,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool) statedb.StartPrefetcher("chain") activeState = statedb + bc.vmConfigMu.RLock() + vmConfig := bc.vmConfig + bc.vmConfigMu.RUnlock() // If we have a followup block, run that against the current state to pre-cache // transactions and probabilistically some of the account/storage trie nodes. var followupInterrupt uint32 @@ -1580,7 +1585,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool) throwaway, _ := state.New(parent.Root, bc.stateCache, bc.snaps) go func(start time.Time, followup *types.Block, throwaway *state.StateDB, interrupt *uint32) { - bc.prefetcher.Prefetch(followup, throwaway, bc.vmConfig, &followupInterrupt) + bc.prefetcher.Prefetch(followup, throwaway, vmConfig, &followupInterrupt) blockPrefetchExecuteTimer.Update(time.Since(start)) if atomic.LoadUint32(interrupt) == 1 { @@ -1592,7 +1597,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool) // Process block using the parent state as reference point substart := time.Now() - receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig) + receipts, logs, usedGas, err := bc.processor.Process(block, statedb, vmConfig) if err != nil { bc.reportBlock(block, receipts, err) atomic.StoreUint32(&followupInterrupt, 1) diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index 4be3e91d958f7..f630a72f0ae65 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -346,12 +346,16 @@ func (bc *BlockChain) Genesis() *types.Block { // GetVMConfig returns the block chain VM config. func (bc *BlockChain) GetVMConfig() *vm.Config { - return &bc.vmConfig + bc.vmConfigMu.RLock() + defer bc.vmConfigMu.RUnlock() + copy := bc.vmConfig + return © } // SetVMConfig sets the vm config. -// Warning: might not be threadsafe with other components func (bc *BlockChain) SetVMConfig(config vm.Config) { + bc.vmConfigMu.Lock() + defer bc.vmConfigMu.Unlock() bc.vmConfig = config } diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 72a4b0a99a967..82358c12f6201 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -441,7 +441,6 @@ func ExecutableDataToBlock(params ExecutableDataV1) (*types.Block, error) { BaseFee: params.BaseFeePerGas, Extra: params.ExtraData, MixDigest: params.Random, - // TODO (MariusVanDerWijden) add params.Random to header once required } block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */) if block.Hash() != params.BlockHash { diff --git a/params/config.go b/params/config.go index 18c367bbea30a..36482f2380a41 100644 --- a/params/config.go +++ b/params/config.go @@ -672,7 +672,6 @@ type Rules struct { // Rules ensures c's ChainID is not nil. func (c *ChainConfig) Rules(num *big.Int) Rules { - // TODO (MariusVanDerWijden) replace isMerge after the merge once we know the merge block chainID := c.ChainID if chainID == nil { chainID = new(big.Int) From 4a4948b7ac6e61bdd6c3c25ccac11f67714a889f Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 20 Dec 2021 11:07:51 +0100 Subject: [PATCH 03/12] core: miner: pass vmConfig by value not reference --- core/blockchain_reader.go | 5 ++--- eth/api_backend.go | 3 ++- eth/catalyst/api.go | 6 +++--- miner/worker.go | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index f630a72f0ae65..b4cdfa7cb2549 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -345,11 +345,10 @@ func (bc *BlockChain) Genesis() *types.Block { } // GetVMConfig returns the block chain VM config. -func (bc *BlockChain) GetVMConfig() *vm.Config { +func (bc *BlockChain) GetVMConfig() vm.Config { bc.vmConfigMu.RLock() defer bc.vmConfigMu.RUnlock() - copy := bc.vmConfig - return © + return bc.vmConfig } // SetVMConfig sets the vm config. diff --git a/eth/api_backend.go b/eth/api_backend.go index 6577ac1e1af49..398bb22106e76 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -204,7 +204,8 @@ func (b *EthAPIBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int { func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error) { vmError := func() error { return nil } if vmConfig == nil { - vmConfig = b.eth.blockchain.GetVMConfig() + conf := b.eth.blockchain.GetVMConfig() + vmConfig = &conf } txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(header, b.eth.BlockChain(), nil) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 82358c12f6201..3dd8a6573a7bb 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -133,7 +133,7 @@ type blockExecutionEnv struct { } func (env *blockExecutionEnv) commitTransaction(tx *types.Transaction, coinbase common.Address) error { - vmconfig := *env.chain.GetVMConfig() + vmconfig := env.chain.GetVMConfig() vmconfig.RandomOpcode = true snap := env.state.Snapshot() receipt, err := core.ApplyTransaction(env.chain.Config(), env.chain, &coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, vmconfig) @@ -279,7 +279,7 @@ func (api *ConsensusAPI) ExecutePayloadV1(params ExecutableDataV1) (ExecutePaylo } conf := api.eth.BlockChain().GetVMConfig() conf.RandomOpcode = true - api.eth.BlockChain().SetVMConfig(*conf) + api.eth.BlockChain().SetVMConfig(conf) if err := api.eth.BlockChain().InsertBlockWithoutSetHead(block); err != nil { return api.invalid(), err } @@ -326,7 +326,7 @@ func (api *ConsensusAPI) assembleBlock(parentHash common.Hash, params *PayloadAt } conf := api.eth.BlockChain().GetVMConfig() conf.RandomOpcode = true - api.eth.BlockChain().SetVMConfig(*conf) + api.eth.BlockChain().SetVMConfig(conf) if config := api.eth.BlockChain().Config(); config.IsLondon(header.Number) { header.BaseFee = misc.CalcBaseFee(config, parent.Header()) } diff --git a/miner/worker.go b/miner/worker.go index 2c576ad082727..1a772abe4c5fd 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -773,7 +773,7 @@ func (w *worker) updateSnapshot() { func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Address) ([]*types.Log, error) { snap := w.current.state.Snapshot() - receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, *w.chain.GetVMConfig()) + receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, w.chain.GetVMConfig()) if err != nil { w.current.state.RevertToSnapshot(snap) return nil, err From 2e1b39988f61576b9880aaeff40250c1da83844c Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 21 Dec 2021 16:25:16 +0100 Subject: [PATCH 04/12] all: enable 4399 by Rules --- core/blockchain_reader.go | 13 ++----------- core/evm.go | 1 + core/state_transition.go | 2 +- core/vm/evm.go | 3 ++- core/vm/runtime/runtime.go | 6 +++--- eth/api_backend.go | 3 +-- eth/catalyst/api.go | 11 ++--------- eth/tracers/js/tracer.go | 2 +- eth/tracers/native/4byte.go | 2 +- internal/ethapi/api.go | 3 ++- miner/worker.go | 2 +- params/config.go | 6 ++++-- 12 files changed, 21 insertions(+), 33 deletions(-) diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index b4cdfa7cb2549..9e966df4e0134 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -345,17 +345,8 @@ func (bc *BlockChain) Genesis() *types.Block { } // GetVMConfig returns the block chain VM config. -func (bc *BlockChain) GetVMConfig() vm.Config { - bc.vmConfigMu.RLock() - defer bc.vmConfigMu.RUnlock() - return bc.vmConfig -} - -// SetVMConfig sets the vm config. -func (bc *BlockChain) SetVMConfig(config vm.Config) { - bc.vmConfigMu.Lock() - defer bc.vmConfigMu.Unlock() - bc.vmConfig = config +func (bc *BlockChain) GetVMConfig() *vm.Config { + return &bc.vmConfig } // SetTxLookupLimit is responsible for updating the txlookup limit to the diff --git a/core/evm.go b/core/evm.go index edbe7b7a4b489..c6002b13b0dc8 100644 --- a/core/evm.go +++ b/core/evm.go @@ -62,6 +62,7 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common BaseFee: baseFee, GasLimit: header.GasLimit, Random: header.MixDigest, + IsPostMerge: header.Difficulty.Cmp(common.Big0) == 0, } } diff --git a/core/state_transition.go b/core/state_transition.go index 135a9c6dbe85f..fb4c841ca5f8a 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -310,7 +310,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { } // Set up the initial access list. - if rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber); rules.IsBerlin { + if rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber, st.evm.Context.IsPostMerge); rules.IsBerlin { st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList()) } var ( diff --git a/core/vm/evm.go b/core/vm/evm.go index 6fb7bc470b86b..6399049c859bf 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -76,6 +76,7 @@ type BlockContext struct { Difficulty *big.Int // Provides information for DIFFICULTY BaseFee *big.Int // Provides information for BASEFEE Random common.Hash // Provides information for RANDOM + IsPostMerge bool // Set if we are post-merge } // TxContext provides the EVM with information about a transaction. @@ -132,7 +133,7 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig StateDB: statedb, Config: config, chainConfig: chainConfig, - chainRules: chainConfig.Rules(blockCtx.BlockNumber), + chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.IsPostMerge), } evm.interpreter = NewEVMInterpreter(evm, config) return evm diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 103ce3e175ffb..d046fb73de73a 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -118,7 +118,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { vmenv = NewEnv(cfg) sender = vm.AccountRef(cfg.Origin) ) - if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsBerlin { + if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.IsPostMerge); rules.IsBerlin { cfg.State.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil) } cfg.State.CreateAccount(address) @@ -150,7 +150,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { vmenv = NewEnv(cfg) sender = vm.AccountRef(cfg.Origin) ) - if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsBerlin { + if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.IsPostMerge); rules.IsBerlin { cfg.State.PrepareAccessList(cfg.Origin, nil, vm.ActivePrecompiles(rules), nil) } // Call the code with the given configuration. @@ -176,7 +176,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er sender := cfg.State.GetOrNewStateObject(cfg.Origin) statedb := cfg.State - if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsBerlin { + if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.IsPostMerge); rules.IsBerlin { statedb.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil) } // Call the code with the given configuration. diff --git a/eth/api_backend.go b/eth/api_backend.go index 398bb22106e76..6577ac1e1af49 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -204,8 +204,7 @@ func (b *EthAPIBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int { func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error) { vmError := func() error { return nil } if vmConfig == nil { - conf := b.eth.blockchain.GetVMConfig() - vmConfig = &conf + vmConfig = b.eth.blockchain.GetVMConfig() } txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(header, b.eth.BlockChain(), nil) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 3dd8a6573a7bb..d4cf9f7ca2c20 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -133,10 +133,9 @@ type blockExecutionEnv struct { } func (env *blockExecutionEnv) commitTransaction(tx *types.Transaction, coinbase common.Address) error { - vmconfig := env.chain.GetVMConfig() - vmconfig.RandomOpcode = true + vmConfig := env.chain.GetVMConfig() snap := env.state.Snapshot() - receipt, err := core.ApplyTransaction(env.chain.Config(), env.chain, &coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, vmconfig) + receipt, err := core.ApplyTransaction(env.chain.Config(), env.chain, &coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *vmConfig) if err != nil { env.state.RevertToSnapshot(snap) return err @@ -277,9 +276,6 @@ func (api *ConsensusAPI) ExecutePayloadV1(params ExecutableDataV1) (ExecutePaylo if td.Cmp(ttd) < 0 { return api.invalid(), fmt.Errorf("can not execute payload on top of block with low td got: %v threshold %v", td, ttd) } - conf := api.eth.BlockChain().GetVMConfig() - conf.RandomOpcode = true - api.eth.BlockChain().SetVMConfig(conf) if err := api.eth.BlockChain().InsertBlockWithoutSetHead(block); err != nil { return api.invalid(), err } @@ -324,9 +320,6 @@ func (api *ConsensusAPI) assembleBlock(parentHash common.Hash, params *PayloadAt Time: params.Timestamp, MixDigest: params.Random, } - conf := api.eth.BlockChain().GetVMConfig() - conf.RandomOpcode = true - api.eth.BlockChain().SetVMConfig(conf) if config := api.eth.BlockChain().Config(); config.IsLondon(header.Number) { header.BaseFee = misc.CalcBaseFee(config, parent.Header()) } diff --git a/eth/tracers/js/tracer.go b/eth/tracers/js/tracer.go index b8e035e6f3f46..205458773470a 100644 --- a/eth/tracers/js/tracer.go +++ b/eth/tracers/js/tracer.go @@ -697,7 +697,7 @@ func (jst *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Ad jst.ctx["block"] = env.Context.BlockNumber.Uint64() jst.dbWrapper.db = env.StateDB // Update list of precompiles based on current block - rules := env.ChainConfig().Rules(env.Context.BlockNumber) + rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.IsPostMerge) jst.activePrecompiles = vm.ActivePrecompiles(rules) // Compute intrinsic gas diff --git a/eth/tracers/native/4byte.go b/eth/tracers/native/4byte.go index e60e82de479a0..5c9e465874ec7 100644 --- a/eth/tracers/native/4byte.go +++ b/eth/tracers/native/4byte.go @@ -83,7 +83,7 @@ func (t *fourByteTracer) CaptureStart(env *vm.EVM, from common.Address, to commo t.env = env // Update list of precompiles based on current block - rules := env.ChainConfig().Rules(env.Context.BlockNumber) + rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.IsPostMerge) t.activePrecompiles = vm.ActivePrecompiles(rules) // Save the outer calldata also diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 65e34752bf41d..eff73301772f1 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1432,8 +1432,9 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH } else { to = crypto.CreateAddress(args.from(), uint64(*args.Nonce)) } + isPostMerge := header.Difficulty.Cmp(common.Big0) == 0 // Retrieve the precompiles since they don't need to be added to the access list - precompiles := vm.ActivePrecompiles(b.ChainConfig().Rules(header.Number)) + precompiles := vm.ActivePrecompiles(b.ChainConfig().Rules(header.Number, isPostMerge)) // Create an initial tracer prevTracer := logger.NewAccessListTracer(nil, args.from(), to, precompiles) diff --git a/miner/worker.go b/miner/worker.go index 1a772abe4c5fd..2c576ad082727 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -773,7 +773,7 @@ func (w *worker) updateSnapshot() { func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Address) ([]*types.Log, error) { snap := w.current.state.Snapshot() - receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, w.chain.GetVMConfig()) + receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, *w.chain.GetVMConfig()) if err != nil { w.current.state.RevertToSnapshot(snap) return nil, err diff --git a/params/config.go b/params/config.go index 36482f2380a41..7f52472ec9dcd 100644 --- a/params/config.go +++ b/params/config.go @@ -267,7 +267,7 @@ var ( AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}} TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil} - TestRules = TestChainConfig.Rules(new(big.Int)) + TestRules = TestChainConfig.Rules(new(big.Int), false) ) // TrustedCheckpoint represents a set of post-processed trie roots (CHT and @@ -668,10 +668,11 @@ type Rules struct { IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool IsBerlin, IsLondon bool + IsMerge bool } // Rules ensures c's ChainID is not nil. -func (c *ChainConfig) Rules(num *big.Int) Rules { +func (c *ChainConfig) Rules(num *big.Int, isMerge bool) Rules { chainID := c.ChainID if chainID == nil { chainID = new(big.Int) @@ -688,5 +689,6 @@ func (c *ChainConfig) Rules(num *big.Int) Rules { IsIstanbul: c.IsIstanbul(num), IsBerlin: c.IsBerlin(num), IsLondon: c.IsLondon(num), + IsMerge: isMerge, } } From ea26584bc900d6a925755ac28f1515e6a9a3d39e Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 21 Dec 2021 16:33:58 +0100 Subject: [PATCH 05/12] core: remove diff (f) --- core/blockchain.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 662553ad0871a..85591931ee42b 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -209,7 +209,6 @@ type BlockChain struct { processor Processor // Block transaction processor interface forker *ForkChoice vmConfig vm.Config - vmConfigMu *sync.RWMutex } // NewBlockChain returns a fully initialised block chain using information @@ -246,7 +245,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par futureBlocks: futureBlocks, engine: engine, vmConfig: vmConfig, - vmConfigMu: &sync.RWMutex{}, } bc.forker = NewForkChoice(bc, shouldPreserve) bc.validator = NewBlockValidator(chainConfig, bc, engine) @@ -1574,9 +1572,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool) statedb.StartPrefetcher("chain") activeState = statedb - bc.vmConfigMu.RLock() - vmConfig := bc.vmConfig - bc.vmConfigMu.RUnlock() // If we have a followup block, run that against the current state to pre-cache // transactions and probabilistically some of the account/storage trie nodes. var followupInterrupt uint32 @@ -1585,7 +1580,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool) throwaway, _ := state.New(parent.Root, bc.stateCache, bc.snaps) go func(start time.Time, followup *types.Block, throwaway *state.StateDB, interrupt *uint32) { - bc.prefetcher.Prefetch(followup, throwaway, vmConfig, &followupInterrupt) + bc.prefetcher.Prefetch(followup, throwaway, bc.vmConfig, &followupInterrupt) blockPrefetchExecuteTimer.Update(time.Since(start)) if atomic.LoadUint32(interrupt) == 1 { @@ -1597,7 +1592,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool) // Process block using the parent state as reference point substart := time.Now() - receipts, logs, usedGas, err := bc.processor.Process(block, statedb, vmConfig) + receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig) if err != nil { bc.reportBlock(block, receipts, err) atomic.StoreUint32(&followupInterrupt, 1) From 4d59679fa052cb04528f60f8ffe5e9fe4f0b6d6a Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 21 Dec 2021 16:46:00 +0100 Subject: [PATCH 06/12] tests: set proper difficulty (f) --- tests/state_test_util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 8a5920f295886..a66c21f437029 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -223,7 +223,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh if t.json.Env.Random != nil { vmconfig.RandomOpcode = true context.Random = common.BigToHash(t.json.Env.Random) - context.Difficulty = nil + context.Difficulty = big.NewInt(0) } evm := vm.NewEVM(context, txContext, statedb, config, vmconfig) // Execute the message. From 7d72217f87f3235fcb521be9df0d26be1ee307b4 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 21 Dec 2021 16:54:37 +0100 Subject: [PATCH 07/12] smaller diff (f) --- core/vm/instructions_test.go | 2 +- core/vm/interpreter.go | 3 +-- tests/state_test_util.go | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 1bf6aadfc3ae9..ce9673bc91bd4 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -669,7 +669,7 @@ func TestRandom(t *testing.T) { {name: "hash(0x010203)", random: crypto.Keccak256Hash([]byte{0x01, 0x02, 0x03})}, } { var ( - env = NewEVM(BlockContext{Random: tt.random}, TxContext{}, nil, params.TestChainConfig, Config{RandomOpcode: true}) + env = NewEVM(BlockContext{Random: tt.random, IsPostMerge: true}, TxContext{}, nil, params.TestChainConfig, Config{}) stack = newstack() pc = uint64(0) evmInterpreter = env.interpreter diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 83e20640f40ae..21e3c914e139c 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -29,7 +29,6 @@ type Config struct { Debug bool // Enables debugging Tracer EVMLogger // Opcode logger NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls) - RandomOpcode bool // Enables the random opcode EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages JumpTable *JumpTable // EVM instruction table, automatically populated if unset @@ -70,7 +69,7 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { // If jump table was not initialised we set the default one. if cfg.JumpTable == nil { switch { - case cfg.RandomOpcode: + case evm.chainRules.IsMerge: cfg.JumpTable = &mergeInstructionSet case evm.chainRules.IsLondon: cfg.JumpTable = &londonInstructionSet diff --git a/tests/state_test_util.go b/tests/state_test_util.go index a66c21f437029..1bbd9f4a1b93b 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -221,7 +221,6 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh context.GetHash = vmTestBlockHash context.BaseFee = baseFee if t.json.Env.Random != nil { - vmconfig.RandomOpcode = true context.Random = common.BigToHash(t.json.Env.Random) context.Difficulty = big.NewInt(0) } From e1a30c1b97a8fe19cdb4d90ece42650f38079328 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Wed, 29 Dec 2021 16:34:40 +0100 Subject: [PATCH 08/12] eth/catalyst: nit --- eth/catalyst/api.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index d4cf9f7ca2c20..1c3d65a1ce79e 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -133,9 +133,9 @@ type blockExecutionEnv struct { } func (env *blockExecutionEnv) commitTransaction(tx *types.Transaction, coinbase common.Address) error { - vmConfig := env.chain.GetVMConfig() + vmConfig := *env.chain.GetVMConfig() snap := env.state.Snapshot() - receipt, err := core.ApplyTransaction(env.chain.Config(), env.chain, &coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *vmConfig) + receipt, err := core.ApplyTransaction(env.chain.Config(), env.chain, &coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, vmConfig) if err != nil { env.state.RevertToSnapshot(snap) return err From b80b6a12e81676ec0828d5e81a13b6c08700e784 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Wed, 5 Jan 2022 11:04:00 +0100 Subject: [PATCH 09/12] core: make RANDOM a pointer which is only set post-merge --- core/evm.go | 7 +++++-- core/state_transition.go | 2 +- core/vm/evm.go | 5 ++--- core/vm/instructions_test.go | 2 +- core/vm/opcodes.go | 2 +- core/vm/runtime/runtime.go | 6 +++--- eth/tracers/js/tracer.go | 2 +- eth/tracers/native/4byte.go | 2 +- tests/state_test_util.go | 9 +++++---- 9 files changed, 20 insertions(+), 17 deletions(-) diff --git a/core/evm.go b/core/evm.go index c6002b13b0dc8..536ac673e6a62 100644 --- a/core/evm.go +++ b/core/evm.go @@ -40,6 +40,7 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common var ( beneficiary common.Address baseFee *big.Int + random *common.Hash ) // If we don't have an explicit author (i.e. not mining), extract from the header @@ -51,6 +52,9 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common if header.BaseFee != nil { baseFee = new(big.Int).Set(header.BaseFee) } + if header.Difficulty.Cmp(common.Big0) == 0 { + random = &header.MixDigest + } return vm.BlockContext{ CanTransfer: CanTransfer, Transfer: Transfer, @@ -61,8 +65,7 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common Difficulty: new(big.Int).Set(header.Difficulty), BaseFee: baseFee, GasLimit: header.GasLimit, - Random: header.MixDigest, - IsPostMerge: header.Difficulty.Cmp(common.Big0) == 0, + Random: random, } } diff --git a/core/state_transition.go b/core/state_transition.go index fb4c841ca5f8a..05d5633075b98 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -310,7 +310,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { } // Set up the initial access list. - if rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber, st.evm.Context.IsPostMerge); rules.IsBerlin { + if rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber, st.evm.Context.Random != nil); rules.IsBerlin { st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList()) } var ( diff --git a/core/vm/evm.go b/core/vm/evm.go index 6399049c859bf..dd55618bf8125 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -75,8 +75,7 @@ type BlockContext struct { Time *big.Int // Provides information for TIME Difficulty *big.Int // Provides information for DIFFICULTY BaseFee *big.Int // Provides information for BASEFEE - Random common.Hash // Provides information for RANDOM - IsPostMerge bool // Set if we are post-merge + Random *common.Hash // Provides information for RANDOM } // TxContext provides the EVM with information about a transaction. @@ -133,7 +132,7 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig StateDB: statedb, Config: config, chainConfig: chainConfig, - chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.IsPostMerge), + chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil), } evm.interpreter = NewEVMInterpreter(evm, config) return evm diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index ce9673bc91bd4..36589a1269574 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -669,7 +669,7 @@ func TestRandom(t *testing.T) { {name: "hash(0x010203)", random: crypto.Keccak256Hash([]byte{0x01, 0x02, 0x03})}, } { var ( - env = NewEVM(BlockContext{Random: tt.random, IsPostMerge: true}, TxContext{}, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{Random: &tt.random}, TxContext{}, nil, params.TestChainConfig, Config{}) stack = newstack() pc = uint64(0) evmInterpreter = env.interpreter diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index e16db6fcb954f..ba70fa09d486c 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -95,11 +95,11 @@ const ( TIMESTAMP OpCode = 0x42 NUMBER OpCode = 0x43 DIFFICULTY OpCode = 0x44 + RANDOM OpCode = 0x44 // Same as DIFFICULTY GASLIMIT OpCode = 0x45 CHAINID OpCode = 0x46 SELFBALANCE OpCode = 0x47 BASEFEE OpCode = 0x48 - RANDOM OpCode = 0x44 // Same as DIFFICULTY ) // 0x50 range - 'storage' and execution. diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index d046fb73de73a..7861fb92dba31 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -118,7 +118,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { vmenv = NewEnv(cfg) sender = vm.AccountRef(cfg.Origin) ) - if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.IsPostMerge); rules.IsBerlin { + if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil); rules.IsBerlin { cfg.State.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil) } cfg.State.CreateAccount(address) @@ -150,7 +150,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { vmenv = NewEnv(cfg) sender = vm.AccountRef(cfg.Origin) ) - if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.IsPostMerge); rules.IsBerlin { + if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil); rules.IsBerlin { cfg.State.PrepareAccessList(cfg.Origin, nil, vm.ActivePrecompiles(rules), nil) } // Call the code with the given configuration. @@ -176,7 +176,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er sender := cfg.State.GetOrNewStateObject(cfg.Origin) statedb := cfg.State - if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.IsPostMerge); rules.IsBerlin { + if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil); rules.IsBerlin { statedb.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil) } // Call the code with the given configuration. diff --git a/eth/tracers/js/tracer.go b/eth/tracers/js/tracer.go index 205458773470a..30c5c2cf149a0 100644 --- a/eth/tracers/js/tracer.go +++ b/eth/tracers/js/tracer.go @@ -697,7 +697,7 @@ func (jst *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Ad jst.ctx["block"] = env.Context.BlockNumber.Uint64() jst.dbWrapper.db = env.StateDB // Update list of precompiles based on current block - rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.IsPostMerge) + rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil) jst.activePrecompiles = vm.ActivePrecompiles(rules) // Compute intrinsic gas diff --git a/eth/tracers/native/4byte.go b/eth/tracers/native/4byte.go index 5c9e465874ec7..ad1d89071c521 100644 --- a/eth/tracers/native/4byte.go +++ b/eth/tracers/native/4byte.go @@ -83,7 +83,7 @@ func (t *fourByteTracer) CaptureStart(env *vm.EVM, from common.Address, to commo t.env = env // Update list of precompiles based on current block - rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.IsPostMerge) + rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil) t.activePrecompiles = vm.ActivePrecompiles(rules) // Save the outer calldata also diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 1bbd9f4a1b93b..0f8e48625fcb5 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -81,11 +81,11 @@ type stPostState struct { type stEnv struct { Coinbase common.Address `json:"currentCoinbase" gencodec:"required"` Difficulty *big.Int `json:"currentDifficulty" gencodec:"optional"` - Random *big.Int `json:"currentRandom" gencodec:"optional"` + Random *big.Int `json:"currentRandom" gencodec:"optional"` GasLimit uint64 `json:"currentGasLimit" gencodec:"required"` Number uint64 `json:"currentNumber" gencodec:"required"` Timestamp uint64 `json:"currentTimestamp" gencodec:"required"` - BaseFee *big.Int `json:"currentBaseFee" gencodec:"optional"` + BaseFee *big.Int `json:"currentBaseFee" gencodec:"optional"` } type stEnvMarshaling struct { @@ -221,7 +221,8 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh context.GetHash = vmTestBlockHash context.BaseFee = baseFee if t.json.Env.Random != nil { - context.Random = common.BigToHash(t.json.Env.Random) + rnd := common.BigToHash(t.json.Env.Random) + context.Random = &rnd context.Difficulty = big.NewInt(0) } evm := vm.NewEVM(context, txContext, statedb, config, vmconfig) @@ -285,7 +286,7 @@ func (t *StateTest) genesis(config *params.ChainConfig) *core.Genesis { if t.json.Env.Random != nil { // Post-Merge genesis.Mixhash = common.BigToHash(t.json.Env.Random) - genesis.Difficulty = nil + genesis.Difficulty = common.Big0 } return genesis } From c73dc4c30ee1e7d3e904261981866132ebde5cfe Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 6 Jan 2022 10:06:23 +0100 Subject: [PATCH 10/12] cmd/evm/internal/t8ntool: fix t8n tracing of 4399 --- cmd/evm/internal/t8ntool/execution.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 28af534ec0bbd..874685f15ea04 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -141,6 +141,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) } + // If random is defined, add it to the vmContext. + if pre.Env.Random != nil { + rnd := common.BigToHash(pre.Env.Random) + vmContext.Random = &rnd + } // If DAO is supported/enabled, we need to handle it here. In geth 'proper', it's // done in StateProcessor.Process(block, ...), right before transactions are applied. if chainConfig.DAOForkSupport && From 72896f447d8994e397a0347a49c912700fe4e4e9 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 6 Jan 2022 16:03:35 +0100 Subject: [PATCH 11/12] tests: set difficulty --- tests/state_test_util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 0f8e48625fcb5..4fd3cf76b2108 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -286,7 +286,7 @@ func (t *StateTest) genesis(config *params.ChainConfig) *core.Genesis { if t.json.Env.Random != nil { // Post-Merge genesis.Mixhash = common.BigToHash(t.json.Env.Random) - genesis.Difficulty = common.Big0 + genesis.Difficulty = big.NewInt(0) } return genesis } From 8253191d4c7808dbb52c92aaab820f807a677b97 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Fri, 7 Jan 2022 19:21:54 +0100 Subject: [PATCH 12/12] cmd/evm/internal/t8ntool: check that baserules are london before applying the merge chainrules --- cmd/evm/internal/t8ntool/transition.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index 11d71e4ce55d5..097f9ce65c556 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -252,6 +252,10 @@ func Transition(ctx *cli.Context) error { return NewError(ErrorConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section")) } } + // Sanity check, to not `panic` in state_transition + if prestate.Env.Random != nil && !chainConfig.IsLondon(big.NewInt(int64(prestate.Env.Number))) { + return NewError(ErrorConfig, errors.New("can only apply RANDOM on top of London chainrules")) + } if env := prestate.Env; env.Difficulty == nil { // If difficulty was not provided by caller, we need to calculate it. switch {