From 79c460d2c5ab3453b0f5fc72b29b5cdc6fac2210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Negovanovi=C4=87?= Date: Mon, 18 Dec 2023 16:00:07 +0100 Subject: [PATCH 1/3] Introduce PUSH0 opcode --- chain/params.go | 6 +++++- state/runtime/evm/dispatch_table.go | 1 + state/runtime/evm/instructions.go | 10 ++++++++++ state/runtime/evm/opcodes.go | 3 +++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/chain/params.go b/chain/params.go index f82f55935b..5744aa60a4 100644 --- a/chain/params.go +++ b/chain/params.go @@ -92,6 +92,7 @@ const ( EIP158 = "EIP158" EIP155 = "EIP155" Governance = "governance" + EIP3855 = "EIP3855" ) // Forks is map which contains all forks and their starting blocks from genesis @@ -128,6 +129,7 @@ func (f *Forks) At(block uint64) ForksInTime { EIP158: f.IsActive(EIP158, block), EIP155: f.IsActive(EIP155, block), Governance: f.IsActive(Governance, block), + EIP3855: f.IsActive(EIP3855, block), } } @@ -178,7 +180,8 @@ type ForksInTime struct { EIP150, EIP158, EIP155, - Governance bool + Governance, + EIP3855 bool } // AllForksEnabled should contain all supported forks by current edge version @@ -193,4 +196,5 @@ var AllForksEnabled = &Forks{ Istanbul: NewFork(0), London: NewFork(0), Governance: NewFork(0), + EIP3855: NewFork(0), } diff --git a/state/runtime/evm/dispatch_table.go b/state/runtime/evm/dispatch_table.go index e8afeae0f5..4f81f6f748 100644 --- a/state/runtime/evm/dispatch_table.go +++ b/state/runtime/evm/dispatch_table.go @@ -38,6 +38,7 @@ func init() { register(SMOD, handler{opSMod, 2, 5}) register(EXP, handler{opExp, 2, 10}) + register(PUSH0, handler{opPush0, 0, 2}) registerRange(PUSH1, PUSH32, opPush, 3) registerRange(DUP1, DUP16, opDup, 3) registerRange(SWAP1, SWAP16, opSwap, 3) diff --git a/state/runtime/evm/instructions.go b/state/runtime/evm/instructions.go index d2a38374a1..3fa8551016 100644 --- a/state/runtime/evm/instructions.go +++ b/state/runtime/evm/instructions.go @@ -975,6 +975,16 @@ func opJumpi(c *state) { func opJumpDest(c *state) { } +func opPush0(c *state) { + if !c.config.EIP3855 { + c.exit(errOpCodeNotFound) + + return + } + + c.push(zero) +} + func opPush(n int) instruction { return func(c *state) { ins := c.code diff --git a/state/runtime/evm/opcodes.go b/state/runtime/evm/opcodes.go index 409180d4dc..c511d1c3fa 100644 --- a/state/runtime/evm/opcodes.go +++ b/state/runtime/evm/opcodes.go @@ -200,6 +200,9 @@ const ( // JUMPDEST corresponds to a possible jump destination JUMPDEST = 0x5B + // PUSH0 pushes a 0 constant onto the stack + PUSH0 = 0x5F + // PUSH1 pushes a 1-byte value onto the stack PUSH1 = 0x60 From 6f2cd81fce9c8b60961d0b6c804ee34da2d021c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Negovanovi=C4=87?= Date: Wed, 20 Dec 2023 11:05:26 +0100 Subject: [PATCH 2/3] Add instructions unit tests for opPush0, opSub, opMul and opLt --- state/runtime/evm/instructions_test.go | 95 +++++++++++++++++++++----- 1 file changed, 77 insertions(+), 18 deletions(-) diff --git a/state/runtime/evm/instructions_test.go b/state/runtime/evm/instructions_test.go index b0d303ad51..99ff8f43ad 100644 --- a/state/runtime/evm/instructions_test.go +++ b/state/runtime/evm/instructions_test.go @@ -10,6 +10,7 @@ import ( "github.com/0xPolygon/polygon-edge/state/runtime" "github.com/0xPolygon/polygon-edge/types" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var ( @@ -18,47 +19,47 @@ var ( allEnabledForks = chain.AllForksEnabled.At(0) ) -type cases2To1 []struct { - a *big.Int - b *big.Int - c *big.Int +type twoOperandsArithmetic []struct { + a *big.Int + b *big.Int + expectedResult *big.Int } -func test2to1(t *testing.T, f instruction, tests cases2To1) { +func testArithmeticOperation(t *testing.T, f instruction, tests twoOperandsArithmetic) { t.Helper() s, closeFn := getState() defer closeFn() for _, i := range tests { - s.push(i.a) s.push(i.b) + s.push(i.a) f(s) - assert.Equal(t, i.c, s.pop()) + assert.EqualValues(t, i.expectedResult, s.pop()) } } -type cases2ToBool []struct { - a *big.Int - b *big.Int - c bool +type twoOperandsLogical []struct { + a *big.Int + b *big.Int + expectedResult bool } -func test2toBool(t *testing.T, f instruction, tests cases2ToBool) { +func testLogicalOperation(t *testing.T, f instruction, tests twoOperandsLogical) { t.Helper() s, closeFn := getState() defer closeFn() for _, i := range tests { - s.push(i.a) s.push(i.b) + s.push(i.a) f(s) - if i.c { + if i.expectedResult { assert.Equal(t, uint64(1), s.pop().Uint64()) } else { assert.Equal(t, uint64(0), s.pop().Uint64()) @@ -67,22 +68,80 @@ func test2toBool(t *testing.T, f instruction, tests cases2ToBool) { } func TestAdd(t *testing.T) { - test2to1(t, opAdd, cases2To1{ + testArithmeticOperation(t, opAdd, twoOperandsArithmetic{ {one, one, two}, {zero, one, one}, }) } +func TestMul(t *testing.T) { + testArithmeticOperation(t, opMul, twoOperandsArithmetic{ + {two, two, big.NewInt(4)}, + {big.NewInt(3), two, big.NewInt(6)}, + }) +} + +func TestSub(t *testing.T) { + testArithmeticOperation(t, opSub, twoOperandsArithmetic{ + {two, one, one}, + {two, zero, two}, + }) +} + +func TestPush0(t *testing.T) { + t.Run("single push0 success", func(t *testing.T) { + s, closeFn := getState() + s.config = &allEnabledForks + defer closeFn() + + opPush0(s) + assert.Equal(t, zero, s.pop()) + }) + + t.Run("single push0 (EIP-3855 disabled)", func(t *testing.T) { + s, closeFn := getState() + disabledEIP3855Fork := chain.AllForksEnabled.Copy().RemoveFork(chain.EIP3855).At(0) + s.config = &disabledEIP3855Fork + defer closeFn() + + opPush0(s) + assert.Error(t, errOpCodeNotFound, s.err) + }) + + t.Run("within stack size push0", func(t *testing.T) { + s, closeFn := getState() + s.config = &allEnabledForks + defer closeFn() + + for i := 0; i < stackSize; i++ { + opPush0(s) + require.NoError(t, s.err) + } + + for i := 0; i < stackSize; i++ { + require.Equal(t, zero, s.pop()) + } + }) +} + func TestGt(t *testing.T) { - test2toBool(t, opGt, cases2ToBool{ + testLogicalOperation(t, opGt, twoOperandsLogical{ + {one, one, false}, + {one, two, false}, + {two, one, true}, + }) +} + +func TestLt(t *testing.T) { + testLogicalOperation(t, opLt, twoOperandsLogical{ {one, one, false}, - {two, one, false}, {one, two, true}, + {two, one, false}, }) } func TestIsZero(t *testing.T) { - test2toBool(t, opIsZero, cases2ToBool{ + testLogicalOperation(t, opIsZero, twoOperandsLogical{ {one, one, false}, {zero, zero, true}, {two, two, false}, From f42199ebc3501ce9eea7f07de94cb840a28d5f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Negovanovi=C4=87?= <93934272+Stefan-Ethernal@users.noreply.github.com> Date: Wed, 20 Dec 2023 13:33:31 +0100 Subject: [PATCH 3/3] Introduce named fields for op code handlers (#61) --- state/runtime/evm/dispatch_table.go | 154 ++++++++++++++-------------- 1 file changed, 77 insertions(+), 77 deletions(-) diff --git a/state/runtime/evm/dispatch_table.go b/state/runtime/evm/dispatch_table.go index 4f81f6f748..cfe955f35c 100644 --- a/state/runtime/evm/dispatch_table.go +++ b/state/runtime/evm/dispatch_table.go @@ -28,108 +28,108 @@ func registerRange(from, to OpCode, factory func(n int) instruction, gas uint64) func init() { // unsigned arithmetic operations - register(STOP, handler{opStop, 0, 0}) - register(ADD, handler{opAdd, 2, 3}) - register(SUB, handler{opSub, 2, 3}) - register(MUL, handler{opMul, 2, 5}) - register(DIV, handler{opDiv, 2, 5}) - register(SDIV, handler{opSDiv, 2, 5}) - register(MOD, handler{opMod, 2, 5}) - register(SMOD, handler{opSMod, 2, 5}) - register(EXP, handler{opExp, 2, 10}) - - register(PUSH0, handler{opPush0, 0, 2}) + register(STOP, handler{inst: opStop, stack: 0, gas: 0}) + register(ADD, handler{inst: opAdd, stack: 2, gas: 3}) + register(SUB, handler{inst: opSub, stack: 2, gas: 3}) + register(MUL, handler{inst: opMul, stack: 2, gas: 5}) + register(DIV, handler{inst: opDiv, stack: 2, gas: 5}) + register(SDIV, handler{inst: opSDiv, stack: 2, gas: 5}) + register(MOD, handler{inst: opMod, stack: 2, gas: 5}) + register(SMOD, handler{inst: opSMod, stack: 2, gas: 5}) + register(EXP, handler{inst: opExp, stack: 2, gas: 10}) + + register(PUSH0, handler{inst: opPush0, stack: 0, gas: 2}) registerRange(PUSH1, PUSH32, opPush, 3) registerRange(DUP1, DUP16, opDup, 3) registerRange(SWAP1, SWAP16, opSwap, 3) registerRange(LOG0, LOG4, opLog, 375) - register(ADDMOD, handler{opAddMod, 3, 8}) - register(MULMOD, handler{opMulMod, 3, 8}) + register(ADDMOD, handler{inst: opAddMod, stack: 3, gas: 8}) + register(MULMOD, handler{inst: opMulMod, stack: 3, gas: 8}) - register(AND, handler{opAnd, 2, 3}) - register(OR, handler{opOr, 2, 3}) - register(XOR, handler{opXor, 2, 3}) - register(BYTE, handler{opByte, 2, 3}) + register(AND, handler{inst: opAnd, stack: 2, gas: 3}) + register(OR, handler{inst: opOr, stack: 2, gas: 3}) + register(XOR, handler{inst: opXor, stack: 2, gas: 3}) + register(BYTE, handler{inst: opByte, stack: 2, gas: 3}) - register(NOT, handler{opNot, 1, 3}) - register(ISZERO, handler{opIsZero, 1, 3}) + register(NOT, handler{inst: opNot, stack: 1, gas: 3}) + register(ISZERO, handler{inst: opIsZero, stack: 1, gas: 3}) - register(EQ, handler{opEq, 2, 3}) - register(LT, handler{opLt, 2, 3}) - register(GT, handler{opGt, 2, 3}) - register(SLT, handler{opSlt, 2, 3}) - register(SGT, handler{opSgt, 2, 3}) + register(EQ, handler{inst: opEq, stack: 2, gas: 3}) + register(LT, handler{inst: opLt, stack: 2, gas: 3}) + register(GT, handler{inst: opGt, stack: 2, gas: 3}) + register(SLT, handler{inst: opSlt, stack: 2, gas: 3}) + register(SGT, handler{inst: opSgt, stack: 2, gas: 3}) - register(SIGNEXTEND, handler{opSignExtension, 1, 5}) + register(SIGNEXTEND, handler{inst: opSignExtension, stack: 1, gas: 5}) - register(SHL, handler{opShl, 2, 3}) - register(SHR, handler{opShr, 2, 3}) - register(SAR, handler{opSar, 2, 3}) + register(SHL, handler{inst: opShl, stack: 2, gas: 3}) + register(SHR, handler{inst: opShr, stack: 2, gas: 3}) + register(SAR, handler{inst: opSar, stack: 2, gas: 3}) - register(CREATE, handler{opCreate(CREATE), 3, 32000}) - register(CREATE2, handler{opCreate(CREATE2), 4, 32000}) + register(CREATE, handler{inst: opCreate(CREATE), stack: 3, gas: 32000}) + register(CREATE2, handler{inst: opCreate(CREATE2), stack: 4, gas: 32000}) - register(CALL, handler{opCall(CALL), 7, 0}) - register(CALLCODE, handler{opCall(CALLCODE), 7, 0}) - register(DELEGATECALL, handler{opCall(DELEGATECALL), 6, 0}) - register(STATICCALL, handler{opCall(STATICCALL), 6, 0}) + register(CALL, handler{inst: opCall(CALL), stack: 7, gas: 0}) + register(CALLCODE, handler{inst: opCall(CALLCODE), stack: 7, gas: 0}) + register(DELEGATECALL, handler{inst: opCall(DELEGATECALL), stack: 6, gas: 0}) + register(STATICCALL, handler{inst: opCall(STATICCALL), stack: 6, gas: 0}) - register(REVERT, handler{opHalt(REVERT), 2, 0}) - register(RETURN, handler{opHalt(RETURN), 2, 0}) + register(REVERT, handler{inst: opHalt(REVERT), stack: 2, gas: 0}) + register(RETURN, handler{inst: opHalt(RETURN), stack: 2, gas: 0}) // memory - register(MLOAD, handler{opMload, 1, 3}) - register(MSTORE, handler{opMStore, 2, 3}) - register(MSTORE8, handler{opMStore8, 2, 3}) + register(MLOAD, handler{inst: opMload, stack: 1, gas: 3}) + register(MSTORE, handler{inst: opMStore, stack: 2, gas: 3}) + register(MSTORE8, handler{inst: opMStore8, stack: 2, gas: 3}) // store - register(SLOAD, handler{opSload, 1, 0}) - register(SSTORE, handler{opSStore, 2, 0}) + register(SLOAD, handler{inst: opSload, stack: 1, gas: 0}) + register(SSTORE, handler{inst: opSStore, stack: 2, gas: 0}) - register(SHA3, handler{opSha3, 2, 30}) + register(SHA3, handler{inst: opSha3, stack: 2, gas: 30}) - register(POP, handler{opPop, 1, 2}) + register(POP, handler{inst: opPop, stack: 1, gas: 2}) - register(EXTCODEHASH, handler{opExtCodeHash, 1, 0}) + register(EXTCODEHASH, handler{inst: opExtCodeHash, stack: 1, gas: 0}) // context operations - register(ADDRESS, handler{opAddress, 0, 2}) - register(BALANCE, handler{opBalance, 1, 0}) - register(SELFBALANCE, handler{opSelfBalance, 0, 5}) - register(ORIGIN, handler{opOrigin, 0, 2}) - register(CALLER, handler{opCaller, 0, 2}) - register(CALLVALUE, handler{opCallValue, 0, 2}) - register(CALLDATALOAD, handler{opCallDataLoad, 1, 3}) - register(CALLDATASIZE, handler{opCallDataSize, 0, 2}) - register(CODESIZE, handler{opCodeSize, 0, 2}) - register(EXTCODESIZE, handler{opExtCodeSize, 1, 0}) - register(GASPRICE, handler{opGasPrice, 0, 2}) - register(RETURNDATASIZE, handler{opReturnDataSize, 0, 2}) - register(CHAINID, handler{opChainID, 0, 2}) - register(PC, handler{opPC, 0, 2}) - register(MSIZE, handler{opMSize, 0, 2}) - register(GAS, handler{opGas, 0, 2}) - - register(EXTCODECOPY, handler{opExtCodeCopy, 4, 0}) - - register(CALLDATACOPY, handler{opCallDataCopy, 3, 3}) - register(RETURNDATACOPY, handler{opReturnDataCopy, 3, 3}) - register(CODECOPY, handler{opCodeCopy, 3, 3}) + register(ADDRESS, handler{inst: opAddress, stack: 0, gas: 2}) + register(BALANCE, handler{inst: opBalance, stack: 1, gas: 0}) + register(SELFBALANCE, handler{inst: opSelfBalance, stack: 0, gas: 5}) + register(ORIGIN, handler{inst: opOrigin, stack: 0, gas: 2}) + register(CALLER, handler{inst: opCaller, stack: 0, gas: 2}) + register(CALLVALUE, handler{inst: opCallValue, stack: 0, gas: 2}) + register(CALLDATALOAD, handler{inst: opCallDataLoad, stack: 1, gas: 3}) + register(CALLDATASIZE, handler{inst: opCallDataSize, stack: 0, gas: 2}) + register(CODESIZE, handler{inst: opCodeSize, stack: 0, gas: 2}) + register(EXTCODESIZE, handler{inst: opExtCodeSize, stack: 1, gas: 0}) + register(GASPRICE, handler{inst: opGasPrice, stack: 0, gas: 2}) + register(RETURNDATASIZE, handler{inst: opReturnDataSize, stack: 0, gas: 2}) + register(CHAINID, handler{inst: opChainID, stack: 0, gas: 2}) + register(PC, handler{inst: opPC, stack: 0, gas: 2}) + register(MSIZE, handler{inst: opMSize, stack: 0, gas: 2}) + register(GAS, handler{inst: opGas, stack: 0, gas: 2}) + + register(EXTCODECOPY, handler{inst: opExtCodeCopy, stack: 4, gas: 0}) + + register(CALLDATACOPY, handler{inst: opCallDataCopy, stack: 3, gas: 3}) + register(RETURNDATACOPY, handler{inst: opReturnDataCopy, stack: 3, gas: 3}) + register(CODECOPY, handler{inst: opCodeCopy, stack: 3, gas: 3}) // block information - register(BLOCKHASH, handler{opBlockHash, 1, 20}) - register(COINBASE, handler{opCoinbase, 0, 2}) - register(TIMESTAMP, handler{opTimestamp, 0, 2}) - register(NUMBER, handler{opNumber, 0, 2}) - register(DIFFICULTY, handler{opDifficulty, 0, 2}) - register(GASLIMIT, handler{opGasLimit, 0, 2}) - register(BASEFEE, handler{opBaseFee, 0, 2}) + register(BLOCKHASH, handler{inst: opBlockHash, stack: 1, gas: 20}) + register(COINBASE, handler{inst: opCoinbase, stack: 0, gas: 2}) + register(TIMESTAMP, handler{inst: opTimestamp, stack: 0, gas: 2}) + register(NUMBER, handler{inst: opNumber, stack: 0, gas: 2}) + register(DIFFICULTY, handler{inst: opDifficulty, stack: 0, gas: 2}) + register(GASLIMIT, handler{inst: opGasLimit, stack: 0, gas: 2}) + register(BASEFEE, handler{inst: opBaseFee, stack: 0, gas: 2}) - register(SELFDESTRUCT, handler{opSelfDestruct, 1, 0}) + register(SELFDESTRUCT, handler{inst: opSelfDestruct, stack: 1, gas: 0}) // jumps - register(JUMP, handler{opJump, 1, 8}) - register(JUMPI, handler{opJumpi, 2, 10}) - register(JUMPDEST, handler{opJumpDest, 0, 1}) + register(JUMP, handler{inst: opJump, stack: 1, gas: 8}) + register(JUMPI, handler{inst: opJumpi, stack: 2, gas: 10}) + register(JUMPDEST, handler{inst: opJumpDest, stack: 0, gas: 1}) }