From a31489541d79b963bda43fabcf6c276df8cf871a Mon Sep 17 00:00:00 2001 From: Gerui Wang Date: Sat, 2 Mar 2024 18:45:28 +0800 Subject: [PATCH] implement EIP-4399, PREVRANDAO opcode. and remove unused LondonBlock and BerlinBlock in `params/config.go` (already defined in constants.go) --- common/constants.go | 1 + common/constants/constants.go.devnet | 1 + common/constants/constants.go.testnet | 1 + core/evm.go | 9 ++++++- core/vm/evm.go | 1 + core/vm/instructions.go | 11 +++++++++ core/vm/instructions_test.go | 34 +++++++++++++++++++++++++++ core/vm/interpreter.go | 4 +++- core/vm/jump_table.go | 12 ++++++++++ core/vm/opcodes.go | 4 +++- params/config.go | 27 +++++++++++---------- 11 files changed, 89 insertions(+), 16 deletions(-) diff --git a/common/constants.go b/common/constants.go index 7020e8fd68..349706679b 100644 --- a/common/constants.go +++ b/common/constants.go @@ -47,6 +47,7 @@ var TIPXDCXCancellationFee = big.NewInt(38383838) var TIPXDCXCancellationFeeTestnet = big.NewInt(38383838) var BerlinBlock = big.NewInt(9999999999) var LondonBlock = big.NewInt(9999999999) +var MergeBlock = big.NewInt(9999999999) var TIPXDCXTestnet = big.NewInt(38383838) var IsTestnet bool = false diff --git a/common/constants/constants.go.devnet b/common/constants/constants.go.devnet index 58351e020b..d5df25b477 100644 --- a/common/constants/constants.go.devnet +++ b/common/constants/constants.go.devnet @@ -47,6 +47,7 @@ var TIPXDCXCancellationFee = big.NewInt(225000) var TIPXDCXCancellationFeeTestnet = big.NewInt(225000) var BerlinBlock = big.NewInt(9999999999) var LondonBlock = big.NewInt(9999999999) +var MergeBlock = big.NewInt(9999999999) var TIPXDCXTestnet = big.NewInt(0) var IsTestnet bool = false diff --git a/common/constants/constants.go.testnet b/common/constants/constants.go.testnet index eae07aa116..5b173297fe 100644 --- a/common/constants/constants.go.testnet +++ b/common/constants/constants.go.testnet @@ -47,6 +47,7 @@ var TIPXDCXCancellationFee = big.NewInt(23779191) var TIPXDCXCancellationFeeTestnet = big.NewInt(23779191) var BerlinBlock = big.NewInt(9999999999) var LondonBlock = big.NewInt(9999999999) +var MergeBlock = big.NewInt(9999999999) var TIPXDCXTestnet = big.NewInt(23779191) var IsTestnet bool = false diff --git a/core/evm.go b/core/evm.go index f3fb1a2349..bc1c72ce5b 100644 --- a/core/evm.go +++ b/core/evm.go @@ -23,17 +23,23 @@ import ( "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" + "github.com/XinFinOrg/XDPoSChain/crypto" ) // NewEVMContext creates a new context for use in the EVM. func NewEVMContext(msg Message, header *types.Header, chain consensus.ChainContext, author *common.Address) vm.Context { // If we don't have an explicit author (i.e. not mining), extract from the header - var beneficiary common.Address + var ( + beneficiary common.Address + random common.Hash + ) if author == nil { beneficiary, _ = chain.Engine().Author(header) // Ignore error, we're past header validation } else { beneficiary = *author } + // since xdpos chain do not use difficulty and mixdigest, we use hash of the block number as random + random = crypto.Keccak256Hash(header.Number.Bytes()) return vm.Context{ CanTransfer: CanTransfer, Transfer: Transfer, @@ -45,6 +51,7 @@ func NewEVMContext(msg Message, header *types.Header, chain consensus.ChainConte Difficulty: new(big.Int).Set(header.Difficulty), GasLimit: header.GasLimit, GasPrice: new(big.Int).Set(msg.GasPrice()), + Random: &random, } } diff --git a/core/vm/evm.go b/core/vm/evm.go index 576fbdd094..227d38412c 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -107,6 +107,7 @@ type Context struct { BlockNumber *big.Int // Provides information for NUMBER Time *big.Int // Provides information for TIME Difficulty *big.Int // Provides information for DIFFICULTY + Random *common.Hash // Provides information for PREVRANDAO } // EVM is the Ethereum Virtual Machine base object and provides diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 73173b9039..a020654440 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -477,6 +477,17 @@ func opDifficulty(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) return nil, nil } +func opRandom(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + var v *uint256.Int + if interpreter.evm.Context.Random != nil { + v = new(uint256.Int).SetBytes((interpreter.evm.Context.Random.Bytes())) + } else { // if context random is not set, use emptyCodeHash as default + v = new(uint256.Int).SetBytes(emptyCodeHash.Bytes()) + } + callContext.stack.push(v) + return nil, nil +} + func opGasLimit(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { callContext.stack.push(new(uint256.Int).SetUint64(interpreter.evm.GasLimit)) return nil, nil diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 1f63d2b040..200aa14772 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -21,6 +21,7 @@ import ( "encoding/json" "fmt" "log" + "math/big" "os" "testing" @@ -661,3 +662,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(Context{Random: &tt.random}, nil, nil, params.TestChainConfig, Config{}) + stack = newstack() + pc = uint64(0) + evmInterpreter = NewEVMInterpreter(env, env.vmConfig) + ) + opRandom(&pc, evmInterpreter, &callCtx{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 92bed8e708..29fc4a2cb7 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -93,6 +93,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 evm.chainRules.IsMerge: + cfg.JumpTable = &mergeInstructionSet case evm.chainRules.IsLondon: cfg.JumpTable = &londonInstructionSet case evm.chainRules.IsBerlin: @@ -262,7 +264,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( } pc++ } - + if err == errStopToken { err = nil // clear stop token error } diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 68b13fbe2f..298ec63ab3 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -52,11 +52,23 @@ var ( istanbulInstructionSet = newIstanbulInstructionSet() berlinInstructionSet = newBerlinInstructionSet() londonInstructionSet = newLondonInstructionSet() + mergeInstructionSet = newMergeInstructionSet() ) // JumpTable contains the EVM opcodes supported at a given fork. type JumpTable [256]*operation +func newMergeInstructionSet() JumpTable { + instructionSet := newLondonInstructionSet() + instructionSet[PREVRANDAO] = &operation{ + execute: opRandom, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + } + return instructionSet +} + // newLondonInstructionSet returns the frontier, homestead, byzantium, // constantinople, istanbul, petersburg, berlin and london instructions. func newLondonInstructionSet() JumpTable { diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 69b0436db4..3b123f3a49 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -98,6 +98,8 @@ const ( TIMESTAMP OpCode = 0x42 NUMBER OpCode = 0x43 DIFFICULTY OpCode = 0x44 + RANDOM OpCode = 0x44 // Same as DIFFICULTY + PREVRANDAO OpCode = 0x44 // Same as DIFFICULTY GASLIMIT OpCode = 0x45 CHAINID OpCode = 0x46 SELFBALANCE OpCode = 0x47 @@ -277,7 +279,7 @@ var opCodeToString = [256]string{ COINBASE: "COINBASE", TIMESTAMP: "TIMESTAMP", NUMBER: "NUMBER", - DIFFICULTY: "DIFFICULTY", + DIFFICULTY: "DIFFICULTY", // TODO rename to PREVRANDAO post merge GASLIMIT: "GASLIMIT", CHAINID: "CHAINID", SELFBALANCE: "SELFBALANCE", diff --git a/params/config.go b/params/config.go index a75f7a4053..3462e4dc2c 100644 --- a/params/config.go +++ b/params/config.go @@ -362,8 +362,6 @@ type ChainConfig struct { ByzantiumBlock *big.Int `json:"byzantiumBlock,omitempty"` // Byzantium switch block (nil = no fork, 0 = already on byzantium) ConstantinopleBlock *big.Int `json:"constantinopleBlock,omitempty"` // Constantinople switch block (nil = no fork, 0 = already activated) - BerlinBlock *big.Int `json:"berlinBlock,omitempty"` // Berlin switch block (nil = no fork, 0 = already on berlin) - LondonBlock *big.Int `json:"londonBlock,omitempty"` // London switch block (nil = no fork, 0 = already on london) // Various consensus engines Ethash *EthashConfig `json:"ethash,omitempty"` @@ -500,7 +498,7 @@ func (c *ChainConfig) String() string { default: engine = "unknown" } - return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v BerlinBlock: %v LondonBlock: %v Engine: %v}", + return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Istanbul: %v BerlinBlock: %v LondonBlock: %v MergeBlock: %v Engine: %v}", c.ChainId, c.HomesteadBlock, c.DAOForkBlock, @@ -510,8 +508,10 @@ func (c *ChainConfig) String() string { c.EIP158Block, c.ByzantiumBlock, c.ConstantinopleBlock, - c.BerlinBlock, - c.LondonBlock, + common.TIPXDCXCancellationFee, + common.BerlinBlock, + common.LondonBlock, + common.MergeBlock, engine, ) } @@ -568,6 +568,12 @@ func (c *ChainConfig) IsLondon(num *big.Int) bool { return isForked(common.LondonBlock, num) } +// IsMerge returns whether num is either equal to the Merge fork block or greater. +// Different from Geth which uses `block.difficulty != nil` +func (c *ChainConfig) IsMerge(num *big.Int) bool { + return isForked(common.MergeBlock, num) +} + func (c *ChainConfig) IsTIP2019(num *big.Int) bool { return isForked(common.TIP2019Block, num) } @@ -669,13 +675,6 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *Confi if isForkIncompatible(c.ConstantinopleBlock, newcfg.ConstantinopleBlock, head) { return newCompatError("Constantinople fork block", c.ConstantinopleBlock, newcfg.ConstantinopleBlock) } - if isForkIncompatible(c.BerlinBlock, newcfg.BerlinBlock, head) { - return newCompatError("Berlin fork block", c.BerlinBlock, newcfg.BerlinBlock) - } - if isForkIncompatible(c.LondonBlock, newcfg.LondonBlock, head) { - return newCompatError("London fork block", c.LondonBlock, newcfg.LondonBlock) - } - return nil } @@ -744,6 +743,7 @@ type Rules struct { IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool IsBerlin, IsLondon bool + IsMerge bool } func (c *ChainConfig) Rules(num *big.Int) Rules { @@ -762,6 +762,7 @@ func (c *ChainConfig) Rules(num *big.Int) Rules { IsPetersburg: c.IsPetersburg(num), IsIstanbul: c.IsIstanbul(num), IsBerlin: c.IsBerlin(num), - IsLondon: c.IsLondon(num), + IsLondon: c.IsLondon(num), + IsMerge: c.IsMerge(num), } }