diff --git a/common/constants.go b/common/constants.go index c923eeea8d..803443a781 100644 --- a/common/constants.go +++ b/common/constants.go @@ -49,6 +49,7 @@ var TIPXDCXDISABLE = big.NewInt(99999999900) var BerlinBlock = big.NewInt(9999999999) var LondonBlock = big.NewInt(9999999999) var MergeBlock = big.NewInt(9999999999) +var ShanghaiBlock = 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 e80dda351f..2bbd3ac06a 100644 --- a/common/constants/constants.go.devnet +++ b/common/constants/constants.go.devnet @@ -49,6 +49,7 @@ var TIPXDCXDISABLE = big.NewInt(15894900) var BerlinBlock = big.NewInt(9999999999) var LondonBlock = big.NewInt(9999999999) var MergeBlock = big.NewInt(9999999999) +var ShanghaiBlock = 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 9a0125e701..3b2811ade9 100644 --- a/common/constants/constants.go.testnet +++ b/common/constants/constants.go.testnet @@ -49,6 +49,7 @@ var TIPXDCXDISABLE = big.NewInt(99999999900) var BerlinBlock = big.NewInt(9999999999) var LondonBlock = big.NewInt(9999999999) var MergeBlock = big.NewInt(9999999999) +var ShanghaiBlock = big.NewInt(9999999999) var TIPXDCXTestnet = big.NewInt(23779191) var IsTestnet bool = false diff --git a/core/asm/asm_test.go b/core/asm/asm_test.go index 92b26b67a5..cd7520ec63 100644 --- a/core/asm/asm_test.go +++ b/core/asm/asm_test.go @@ -22,53 +22,37 @@ import ( "encoding/hex" ) -// Tests disassembling the instructions for valid evm code -func TestInstructionIteratorValid(t *testing.T) { - cnt := 0 - script, _ := hex.DecodeString("61000000") +// Tests disassembling instructions +func TestInstructionIterator(t *testing.T) { + for i, tc := range []struct { + want int + code string + wantErr string + }{ + {2, "61000000", ""}, // valid code + {0, "6100", "incomplete push instruction at 0"}, // invalid code + {2, "5900", ""}, // push0 + {0, "", ""}, // empty - it := NewInstructionIterator(script) - for it.Next() { - cnt++ - } - - if err := it.Error(); err != nil { - t.Errorf("Expected 2, but encountered error %v instead.", err) - } - if cnt != 2 { - t.Errorf("Expected 2, but got %v instead.", cnt) - } -} - -// Tests disassembling the instructions for invalid evm code -func TestInstructionIteratorInvalid(t *testing.T) { - cnt := 0 - script, _ := hex.DecodeString("6100") - - it := NewInstructionIterator(script) - for it.Next() { - cnt++ - } - - if it.Error() == nil { - t.Errorf("Expected an error, but got %v instead.", cnt) - } -} - -// Tests disassembling the instructions for empty evm code -func TestInstructionIteratorEmpty(t *testing.T) { - cnt := 0 - script, _ := hex.DecodeString("") - - it := NewInstructionIterator(script) - for it.Next() { - cnt++ - } - - if err := it.Error(); err != nil { - t.Errorf("Expected 0, but encountered error %v instead.", err) - } - if cnt != 0 { - t.Errorf("Expected 0, but got %v instead.", cnt) + } { + var ( + have int + code, _ = hex.DecodeString(tc.code) + it = NewInstructionIterator(code) + ) + for it.Next() { + have++ + } + var haveErr = "" + if it.Error() != nil { + haveErr = it.Error().Error() + } + if haveErr != tc.wantErr { + t.Errorf("test %d: encountered error: %q want %q", i, haveErr, tc.wantErr) + continue + } + if have != tc.want { + t.Errorf("wrong instruction count, have %d want %d", have, tc.want) + } } } diff --git a/core/vm/eips.go b/core/vm/eips.go index 77dc30a910..48ffb0d691 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -29,7 +29,9 @@ import ( // defined jump tables are not polluted. func EnableEIP(eipNum int, jt *JumpTable) error { switch eipNum { - case 3898: + case 3855: + enable3855(jt) + case 3198: enable3198(jt) case 2200: enable2200(jt) @@ -112,3 +114,20 @@ func opBaseFee(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ callContext.stack.push(baseFee) return nil, nil } + +// enable3855 applies EIP-3855 (PUSH0 opcode) +func enable3855(jt *JumpTable) { + // New opcode + jt[PUSH0] = &operation{ + execute: opPush0, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + } +} + +// opPush0 implements the PUSH0 opcode +func opPush0(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + callContext.stack.push(new(uint256.Int)) + return nil, nil +} diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 29fc4a2cb7..23cc5a03fe 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.IsShanghai: + cfg.JumpTable = &shanghaiInstructionSet case evm.chainRules.IsMerge: cfg.JumpTable = &mergeInstructionSet case evm.chainRules.IsLondon: diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 298ec63ab3..2302a439df 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -53,11 +53,18 @@ var ( berlinInstructionSet = newBerlinInstructionSet() londonInstructionSet = newLondonInstructionSet() mergeInstructionSet = newMergeInstructionSet() + shanghaiInstructionSet = newShanghaiInstructionSet() ) // JumpTable contains the EVM opcodes supported at a given fork. type JumpTable [256]*operation +func newShanghaiInstructionSet() JumpTable { + instructionSet := newMergeInstructionSet() + enable3855(&instructionSet) // PUSH0 instruction + return instructionSet +} + func newMergeInstructionSet() JumpTable { instructionSet := newLondonInstructionSet() instructionSet[PREVRANDAO] = &operation{ diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 3b123f3a49..26523e53ce 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -25,11 +25,7 @@ type OpCode byte // IsPush specifies if an opcode is a PUSH opcode. func (op OpCode) IsPush() bool { - switch op { - case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: - return true - } - return false + return PUSH0 <= op && op <= PUSH32 } // 0x0 range - arithmetic ops. @@ -108,18 +104,19 @@ const ( // 0x50 range - 'storage' and execution. const ( - POP OpCode = 0x50 + iota - MLOAD - MSTORE - MSTORE8 - SLOAD - SSTORE - JUMP - JUMPI - PC - MSIZE - GAS - JUMPDEST + POP OpCode = 0x50 + MLOAD OpCode = 0x51 + MSTORE OpCode = 0x52 + MSTORE8 OpCode = 0x53 + SLOAD OpCode = 0x54 + SSTORE OpCode = 0x55 + JUMP OpCode = 0x56 + JUMPI OpCode = 0x57 + PC OpCode = 0x58 + MSIZE OpCode = 0x59 + GAS OpCode = 0x5a + JUMPDEST OpCode = 0x5b + PUSH0 OpCode = 0x5f ) // 0x60 range - pushes. @@ -300,6 +297,7 @@ var opCodeToString = [256]string{ MSIZE: "MSIZE", GAS: "GAS", JUMPDEST: "JUMPDEST", + PUSH0: "PUSH0", // 0x60 range - push. PUSH1: "PUSH1", @@ -462,6 +460,7 @@ var stringToOp = map[string]OpCode{ "MSIZE": MSIZE, "GAS": GAS, "JUMPDEST": JUMPDEST, + "PUSH0": PUSH0, "PUSH1": PUSH1, "PUSH2": PUSH2, "PUSH3": PUSH3, diff --git a/params/config.go b/params/config.go index 0a46f710f3..933d5f2980 100644 --- a/params/config.go +++ b/params/config.go @@ -498,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 Istanbul: %v BerlinBlock: %v LondonBlock: %v MergeBlock: %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 ShanghaiBlock: %v Engine: %v}", c.ChainId, c.HomesteadBlock, c.DAOForkBlock, @@ -512,6 +512,7 @@ func (c *ChainConfig) String() string { common.BerlinBlock, common.LondonBlock, common.MergeBlock, + common.ShanghaiBlock, engine, ) } @@ -574,6 +575,11 @@ func (c *ChainConfig) IsMerge(num *big.Int) bool { return isForked(common.MergeBlock, num) } +// IsShanghai returns whether num is either equal to the Shanghai fork block or greater. +func (c *ChainConfig) IsShanghai(num *big.Int) bool { + return isForked(common.ShanghaiBlock, num) +} + func (c *ChainConfig) IsTIP2019(num *big.Int) bool { return isForked(common.TIP2019Block, num) } @@ -742,7 +748,7 @@ type Rules struct { IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool IsBerlin, IsLondon bool - IsMerge bool + IsMerge, IsShanghai bool } func (c *ChainConfig) Rules(num *big.Int) Rules { @@ -763,5 +769,6 @@ func (c *ChainConfig) Rules(num *big.Int) Rules { IsBerlin: c.IsBerlin(num), IsLondon: c.IsLondon(num), IsMerge: c.IsMerge(num), + IsShanghai: c.IsShanghai(num), } }