From fd7e3df784e95b7de57925b26e7ddace63eb253a Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas Date: Sun, 2 Feb 2025 11:10:56 +0200 Subject: [PATCH] feat(opcodes/eip5656/mcopy): add mcopy, test --- x/evm/core/vm/instructions.go | 32 ++++++++++++++++++++++++++++++ x/evm/core/vm/instructions_test.go | 25 +++++++++++++++++++++++ x/evm/core/vm/jump_table.go | 8 ++++++++ x/evm/core/vm/opcodes.go | 3 +++ 4 files changed, 68 insertions(+) diff --git a/x/evm/core/vm/instructions.go b/x/evm/core/vm/instructions.go index 46409ce63d..cabf749a45 100644 --- a/x/evm/core/vm/instructions.go +++ b/x/evm/core/vm/instructions.go @@ -522,6 +522,38 @@ func opMstore8(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] return nil, nil } +// opMcopy implements EIP-5656 MCOPY operation +// +// Stack Input: +// [ length | top ] -- number of bytes to copy +// [ srcOffset | second ] -- source memory offset +// [ destOffset | third ] -- destination memory offset +// +// Operation Flow: +// 1. Pop parameters from stack: +// - destination offset +// - source offset +// - length +// 2. Convert stack items to uint64 +// 3. Copy memory from source to destination +func opMcopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + var ( + destOffset = scope.Stack.Pop() + srcOffset = scope.Stack.Pop() + length = scope.Stack.Pop() + ) + + // Convert stack items to integers + destOffsetInt := destOffset.Uint64() + srcOffsetInt := srcOffset.Uint64() + lengthInt := length.Uint64() + + // Copy memory + scope.Memory.Set(destOffsetInt, lengthInt, scope.Memory.GetPtr(int64(srcOffsetInt), int64(lengthInt))) + + return nil, nil +} + func opSload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { loc := scope.Stack.Peek() hash := common.Hash(loc.Bytes32()) diff --git a/x/evm/core/vm/instructions_test.go b/x/evm/core/vm/instructions_test.go index 30406b66f1..b174ac82fc 100644 --- a/x/evm/core/vm/instructions_test.go +++ b/x/evm/core/vm/instructions_test.go @@ -564,6 +564,31 @@ func TestOpMstore(t *testing.T) { } } +func TestOpMcopy(t *testing.T) { + var ( + env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) + stack, err = NewStack() + mem = NewMemory() + evmInterpreter = NewEVMInterpreter(env, env.Config) + pc = uint64(0) + ) + require.NoError(t, err) + + env.interpreter = evmInterpreter + mem.Resize(100) + mem.Set(10, 3, []byte{0x01, 0x02, 0x03}) + + stack.Push(new(uint256.Int).SetUint64(3)) // length + stack.Push(new(uint256.Int).SetUint64(10)) // srcOffset + stack.Push(new(uint256.Int).SetUint64(20)) // destOffset + + opMcopy(&pc, evmInterpreter, &ScopeContext{mem, stack, nil}) + + expected := []byte{0x01, 0x02, 0x03} + actual := mem.GetCopy(20, 3) + require.Equal(t, expected, actual) +} + func BenchmarkOpMstore(bench *testing.B) { var ( env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) diff --git a/x/evm/core/vm/jump_table.go b/x/evm/core/vm/jump_table.go index ffe33bb014..f958c9a69a 100644 --- a/x/evm/core/vm/jump_table.go +++ b/x/evm/core/vm/jump_table.go @@ -576,6 +576,14 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(2, 0), maxStack: maxStack(2, 0), }, + MCOPY: { + execute: opMcopy, + constantGas: GasFastestStep, + dynamicGas: gasMStore, + minStack: minStack(3, 0), + maxStack: maxStack(3, 0), + memorySize: memoryMStore, + }, SLOAD: { execute: opSload, constantGas: params.SloadGasFrontier, diff --git a/x/evm/core/vm/opcodes.go b/x/evm/core/vm/opcodes.go index 7e3a870242..bd71166b81 100644 --- a/x/evm/core/vm/opcodes.go +++ b/x/evm/core/vm/opcodes.go @@ -119,6 +119,7 @@ const ( MSIZE OpCode = 0x59 GAS OpCode = 0x5a JUMPDEST OpCode = 0x5b + MCOPY OpCode = 0x5e PUSH0 OpCode = 0x5f ) @@ -301,6 +302,7 @@ var opCodeToString = map[OpCode]string{ MSIZE: "MSIZE", GAS: "GAS", JUMPDEST: "JUMPDEST", + MCOPY: "MCOPY", PUSH0: "PUSH0", // 0x60 range - push. @@ -465,6 +467,7 @@ var stringToOp = map[string]OpCode{ "MSIZE": MSIZE, "GAS": GAS, "JUMPDEST": JUMPDEST, + "MCOPY": MCOPY, "PUSH0": PUSH0, "PUSH1": PUSH1, "PUSH2": PUSH2,