From 659e93182f8f56423f1b0362bf32714dd56bb844 Mon Sep 17 00:00:00 2001 From: Nazaret Garcia Date: Fri, 22 Nov 2024 19:07:09 -0300 Subject: [PATCH 01/12] Add gas cost --- rskj-core/src/main/java/org/ethereum/vm/VM.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/rskj-core/src/main/java/org/ethereum/vm/VM.java b/rskj-core/src/main/java/org/ethereum/vm/VM.java index 7ae5fdd73d..6d34421ac5 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/VM.java +++ b/rskj-core/src/main/java/org/ethereum/vm/VM.java @@ -1438,11 +1438,14 @@ protected void doJUMPDEST() protected void doMCOPY() { if (computeGas) { - // See "Gas Cost" section on EIP 5656 - // gas cost = 3 * (length + 31) + memory expansion cost + very low long length = stack.get(stack.size() - 3).longValue(); long newMemSize = memNeeded(stack.peek(), length); - long cost = 3 * (length + 31) + calcMemGas(oldMemSize, newMemSize, 0) + 3; // TODO -> Check copy size + + // See "Gas Cost" section on EIP 5656 for reference + long copiedWords = (length + 31) / 32; + long memoryExpansionCost = calcMemGas(oldMemSize, newMemSize, copiedWords); + long copyGasCost = 3 * copiedWords + memoryExpansionCost; + long cost = 3 + copyGasCost; // 3 is the fixed gas cost for very low mem usage gasCost = GasCost.add(gasCost, cost); spendOpCodeGas(); From f17ab249ad27480bb0f73236358c1bd5bbc04cff Mon Sep 17 00:00:00 2001 From: Nazaret Garcia Date: Tue, 26 Nov 2024 12:11:20 -0300 Subject: [PATCH 02/12] Updating gas calculations --- .../src/main/java/org/ethereum/vm/VM.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/rskj-core/src/main/java/org/ethereum/vm/VM.java b/rskj-core/src/main/java/org/ethereum/vm/VM.java index 6d34421ac5..f69e53d6bc 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/VM.java +++ b/rskj-core/src/main/java/org/ethereum/vm/VM.java @@ -767,6 +767,16 @@ private long computeDataCopyGas() { return calcMemGas(oldMemSize, newMemSize, copySize); } + private long computeMemoryCopyGas() { + DataWord length = stack.get(stack.size() - 3); + DataWord offset = stack.peek(); + long copiedWords = (length.longValue() + 31) / 32; + long copySize = Program.limitToMaxLong(length); + checkSizeArgument(copySize); + long newMemSize = memNeeded(offset, copySize); + return GasCost.MEMORY * copiedWords + calcMemGas(oldMemSize, newMemSize, copySize) + GasCost.MEMORY; + } + protected void doCODESIZE() { if (computeGas) { if (op == OpCode.EXTCODESIZE) { @@ -1438,16 +1448,7 @@ protected void doJUMPDEST() protected void doMCOPY() { if (computeGas) { - long length = stack.get(stack.size() - 3).longValue(); - long newMemSize = memNeeded(stack.peek(), length); - - // See "Gas Cost" section on EIP 5656 for reference - long copiedWords = (length + 31) / 32; - long memoryExpansionCost = calcMemGas(oldMemSize, newMemSize, copiedWords); - long copyGasCost = 3 * copiedWords + memoryExpansionCost; - long cost = 3 + copyGasCost; // 3 is the fixed gas cost for very low mem usage - - gasCost = GasCost.add(gasCost, cost); + gasCost = GasCost.add(gasCost, computeMemoryCopyGas()); spendOpCodeGas(); } From 4f3b0454d87135f1263809f03e891b090b2220a4 Mon Sep 17 00:00:00 2001 From: Nazaret Garcia Date: Thu, 28 Nov 2024 23:14:19 -0300 Subject: [PATCH 03/12] Adding gas tests WIP --- .../java/co/rsk/vm/opcode/MCopyGasTest.java | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 rskj-core/src/test/java/co/rsk/vm/opcode/MCopyGasTest.java diff --git a/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyGasTest.java b/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyGasTest.java new file mode 100644 index 0000000000..7d7aa6637a --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyGasTest.java @@ -0,0 +1,96 @@ +package co.rsk.vm.opcode; + +import co.rsk.config.TestSystemProperties; +import co.rsk.config.VmConfig; +import co.rsk.peg.BridgeSupportFactory; +import co.rsk.peg.RepositoryBtcBlockStoreWithCache; +import co.rsk.vm.BytecodeCompiler; +import org.ethereum.config.blockchain.upgrades.ActivationConfig; +import org.ethereum.core.*; +import org.ethereum.vm.DataWord; +import org.ethereum.vm.PrecompiledContracts; +import org.ethereum.vm.VM; +import org.ethereum.vm.program.Program; +import org.ethereum.vm.program.invoke.ProgramInvokeMockImpl; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.HashSet; +import java.util.stream.Stream; + +import static co.rsk.net.utils.TransactionUtils.createTransaction; +import static org.ethereum.config.blockchain.upgrades.ConsensusRule.RSKIP445; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class MCopyGasTest { + + private ActivationConfig.ForBlock activationConfig; + private ProgramInvokeMockImpl invoke = new ProgramInvokeMockImpl(); + private BytecodeCompiler compiler = new BytecodeCompiler(); + + private final TestSystemProperties config = new TestSystemProperties(); + private final VmConfig vmConfig = config.getVmConfig(); + private final SignatureCache signatureCache = new BlockTxSignatureCache(new ReceivedTxSignatureCache()); + private final BlockFactory blockFactory = new BlockFactory(config.getActivationConfig()); + private final Transaction transaction = createTransaction(); + private final PrecompiledContracts precompiledContracts = new PrecompiledContracts( + config, + new BridgeSupportFactory( + new RepositoryBtcBlockStoreWithCache.Factory( + config.getNetworkConstants().getBridgeConstants().getBtcParams()), + config.getNetworkConstants().getBridgeConstants(), + config.getActivationConfig(), signatureCache), signatureCache); + + @BeforeEach + void setup() { + activationConfig = mock(ActivationConfig.ForBlock.class); + when(activationConfig.isActive(RSKIP445)).thenReturn(true); + } + + @ParameterizedTest + @MethodSource("provideParametersForMCOPYGasTest") + void testMCopy_ShouldConsumeTheCorrectAmountOfGas(String[] initMemory, int dst, int src, int length, int expectedGasUsage) { + // Given + byte[] code = compiler.compile("MCOPY"); + VM vm = new VM(vmConfig, precompiledContracts); + + Program program = new Program(vmConfig, precompiledContracts, blockFactory, activationConfig, code, invoke, transaction, new HashSet<>(), new BlockTxSignatureCache(new ReceivedTxSignatureCache())); + + int address = 0; + for (String entry : initMemory) { + program.memorySave(DataWord.valueOf(address), DataWord.valueFromHex(entry)); + address += 1; + } + + program.stackPush(DataWord.valueOf(dst)); + program.stackPush(DataWord.valueOf(src)); + program.stackPush(DataWord.valueOf(length)); + + // When + try { + while (!program.isStopped()) { + vm.step(program); + } + } catch(Program.StackTooSmallException e) { + Assertions.fail("Stack too small exception"); + } + + // Then + Assertions.assertEquals(0, program.getStack().size()); + Assertions.assertEquals(expectedGasUsage, program.getResult().getGasUsed()); + } + + private static Stream provideParametersForMCOPYGasTest() { + return Stream.of( + Arguments.of(new String[]{ "0000000000000000000000000000000000000000000000000000000000000000", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" }, 0, 32, 32, 6), + Arguments.of(new String[]{ "0101010101010101010101010101010101010101010101010101010101010101" }, 0, 0, 32, 6), + Arguments.of(new String[]{ "0001020304050607080000000000000000000000000000000000000000000000" }, 0, 1, 8, 6), + Arguments.of(new String[]{ "0001020304050607080000000000000000000000000000000000000000000000" }, 1, 0, 8, 6) + ); + } + +} From 18e820642afacc2fe65cd4f5502b6e45154175bf Mon Sep 17 00:00:00 2001 From: Nazaret Garcia Date: Fri, 29 Nov 2024 15:27:22 -0300 Subject: [PATCH 04/12] Updating opcode cost --- rskj-core/src/main/java/org/ethereum/vm/OpCode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rskj-core/src/main/java/org/ethereum/vm/OpCode.java b/rskj-core/src/main/java/org/ethereum/vm/OpCode.java index 38e0867d3d..3c7872997a 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/OpCode.java +++ b/rskj-core/src/main/java/org/ethereum/vm/OpCode.java @@ -331,7 +331,7 @@ public enum OpCode { /** * (0x5e) Memory copying instruction */ - MCOPY(0x5e, 3, 0, BASE_TIER), + MCOPY(0x5e, 3, 0, ZERO_TIER), // TODO UPDATE RSKIP /* Push Operations */ /** From 55a7afcdb6883eb09e294b50b30a284327a29b03 Mon Sep 17 00:00:00 2001 From: Nazaret Garcia Date: Fri, 29 Nov 2024 15:28:05 -0300 Subject: [PATCH 05/12] Updating compute gas calculations --- rskj-core/src/main/java/org/ethereum/vm/VM.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rskj-core/src/main/java/org/ethereum/vm/VM.java b/rskj-core/src/main/java/org/ethereum/vm/VM.java index f69e53d6bc..ab4060b985 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/VM.java +++ b/rskj-core/src/main/java/org/ethereum/vm/VM.java @@ -770,11 +770,10 @@ private long computeDataCopyGas() { private long computeMemoryCopyGas() { DataWord length = stack.get(stack.size() - 3); DataWord offset = stack.peek(); - long copiedWords = (length.longValue() + 31) / 32; long copySize = Program.limitToMaxLong(length); checkSizeArgument(copySize); long newMemSize = memNeeded(offset, copySize); - return GasCost.MEMORY * copiedWords + calcMemGas(oldMemSize, newMemSize, copySize) + GasCost.MEMORY; + return calcMemGas(oldMemSize, newMemSize, copySize) + GasCost.MEMORY; } protected void doCODESIZE() { From a41462cafad49f0028a401e6983a6f309cf505cf Mon Sep 17 00:00:00 2001 From: Nazaret Garcia Date: Fri, 29 Nov 2024 15:28:30 -0300 Subject: [PATCH 06/12] Updating tests WIP --- .../test/java/co/rsk/vm/opcode/MCopyGasTest.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyGasTest.java b/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyGasTest.java index 7d7aa6637a..0c920c7650 100644 --- a/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyGasTest.java +++ b/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyGasTest.java @@ -63,12 +63,12 @@ void testMCopy_ShouldConsumeTheCorrectAmountOfGas(String[] initMemory, int dst, int address = 0; for (String entry : initMemory) { program.memorySave(DataWord.valueOf(address), DataWord.valueFromHex(entry)); - address += 1; + address += 32; } - program.stackPush(DataWord.valueOf(dst)); + program.stackPush(DataWord.valueOf(length)); // Mind the stack order!! program.stackPush(DataWord.valueOf(src)); - program.stackPush(DataWord.valueOf(length)); + program.stackPush(DataWord.valueOf(dst)); // When try { @@ -86,10 +86,11 @@ void testMCopy_ShouldConsumeTheCorrectAmountOfGas(String[] initMemory, int dst, private static Stream provideParametersForMCOPYGasTest() { return Stream.of( - Arguments.of(new String[]{ "0000000000000000000000000000000000000000000000000000000000000000", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" }, 0, 32, 32, 6), - Arguments.of(new String[]{ "0101010101010101010101010101010101010101010101010101010101010101" }, 0, 0, 32, 6), - Arguments.of(new String[]{ "0001020304050607080000000000000000000000000000000000000000000000" }, 0, 1, 8, 6), - Arguments.of(new String[]{ "0001020304050607080000000000000000000000000000000000000000000000" }, 1, 0, 8, 6) + Arguments.of(new String[]{ "0000000000000000000000000000000000000000000000000000000000000000", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" }, 0, 32, 32, 6), + Arguments.of(new String[]{ "0101010101010101010101010101010101010101010101010101010101010101" }, 0, 0, 32, 6), + Arguments.of(new String[]{ "0001020304050607080000000000000000000000000000000000000000000000" }, 0, 1, 8, 6), + Arguments.of(new String[]{ "0001020304050607080000000000000000000000000000000000000000000000" }, 1, 0, 8, 6), + Arguments.of(new String[]{ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f", "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f", "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", "a0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf", "c0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf", "e0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" }, 256, 256, 1, 9) ); } From 916c4a5faf43fecaa583e82e7c498ab71fa3f033 Mon Sep 17 00:00:00 2001 From: Nazaret Garcia Date: Fri, 29 Nov 2024 22:06:19 -0300 Subject: [PATCH 07/12] Ading full memory clean test --- rskj-core/src/test/java/co/rsk/vm/opcode/MCopyGasTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyGasTest.java b/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyGasTest.java index 0c920c7650..d75615563a 100644 --- a/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyGasTest.java +++ b/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyGasTest.java @@ -90,7 +90,8 @@ private static Stream provideParametersForMCOPYGasTest() { Arguments.of(new String[]{ "0101010101010101010101010101010101010101010101010101010101010101" }, 0, 0, 32, 6), Arguments.of(new String[]{ "0001020304050607080000000000000000000000000000000000000000000000" }, 0, 1, 8, 6), Arguments.of(new String[]{ "0001020304050607080000000000000000000000000000000000000000000000" }, 1, 0, 8, 6), - Arguments.of(new String[]{ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f", "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f", "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", "a0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf", "c0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf", "e0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" }, 256, 256, 1, 9) + Arguments.of(new String[]{ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f", "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f", "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", "a0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf", "c0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf", "e0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" }, 256, 256, 1, 9), + Arguments.of(new String[]{ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f", "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f", "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", "a0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf", "c0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf", "e0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" }, 0, 256, 256, 27) ); } From 06baee86def2d057071df9e628b019c9f9c90588 Mon Sep 17 00:00:00 2001 From: Nazaret Garcia Date: Mon, 2 Dec 2024 13:57:28 -0300 Subject: [PATCH 08/12] Improving code --- rskj-core/src/test/java/co/rsk/vm/opcode/MCopyGasTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyGasTest.java b/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyGasTest.java index d75615563a..750733d1e7 100644 --- a/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyGasTest.java +++ b/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyGasTest.java @@ -29,9 +29,9 @@ public class MCopyGasTest { private ActivationConfig.ForBlock activationConfig; - private ProgramInvokeMockImpl invoke = new ProgramInvokeMockImpl(); - private BytecodeCompiler compiler = new BytecodeCompiler(); + private final ProgramInvokeMockImpl invoke = new ProgramInvokeMockImpl(); + private final BytecodeCompiler compiler = new BytecodeCompiler(); private final TestSystemProperties config = new TestSystemProperties(); private final VmConfig vmConfig = config.getVmConfig(); private final SignatureCache signatureCache = new BlockTxSignatureCache(new ReceivedTxSignatureCache()); From ac87cf656b3d671b8bf0a0d7e24f5eb17e213c85 Mon Sep 17 00:00:00 2001 From: Nazaret Garcia Date: Mon, 2 Dec 2024 13:58:18 -0300 Subject: [PATCH 09/12] Adding input tests --- .../java/co/rsk/vm/opcode/MCopyBoundTest.java | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 rskj-core/src/test/java/co/rsk/vm/opcode/MCopyBoundTest.java diff --git a/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyBoundTest.java b/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyBoundTest.java new file mode 100644 index 0000000000..45756cf249 --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyBoundTest.java @@ -0,0 +1,94 @@ +package co.rsk.vm.opcode; + +import co.rsk.config.TestSystemProperties; +import co.rsk.config.VmConfig; +import co.rsk.peg.BridgeSupportFactory; +import co.rsk.peg.RepositoryBtcBlockStoreWithCache; +import co.rsk.vm.BytecodeCompiler; +import org.ethereum.config.blockchain.upgrades.ActivationConfig; +import org.ethereum.core.*; +import org.ethereum.vm.DataWord; +import org.ethereum.vm.PrecompiledContracts; +import org.ethereum.vm.VM; +import org.ethereum.vm.program.Program; +import org.ethereum.vm.program.invoke.ProgramInvokeMockImpl; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.HashSet; +import java.util.stream.Stream; + +import static co.rsk.net.utils.TransactionUtils.createTransaction; +import static org.ethereum.config.blockchain.upgrades.ConsensusRule.RSKIP445; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class MCopyBoundTest { + + private ActivationConfig.ForBlock activationConfig; + + private final ProgramInvokeMockImpl invoke = new ProgramInvokeMockImpl(); + private final BytecodeCompiler compiler = new BytecodeCompiler(); + private final TestSystemProperties config = new TestSystemProperties(); + private final VmConfig vmConfig = config.getVmConfig(); + private final SignatureCache signatureCache = new BlockTxSignatureCache(new ReceivedTxSignatureCache()); + private final BlockFactory blockFactory = new BlockFactory(config.getActivationConfig()); + private final Transaction transaction = createTransaction(); + private final PrecompiledContracts precompiledContracts = new PrecompiledContracts( + config, + new BridgeSupportFactory( + new RepositoryBtcBlockStoreWithCache.Factory( + config.getNetworkConstants().getBridgeConstants().getBtcParams()), + config.getNetworkConstants().getBridgeConstants(), + config.getActivationConfig(), signatureCache), signatureCache); + + @BeforeEach + void setup() { + activationConfig = mock(ActivationConfig.ForBlock.class); + when(activationConfig.isActive(RSKIP445)).thenReturn(true); + } + + @ParameterizedTest + @MethodSource("provideParametersForMCOPYBoundTest") + void testMCopy_ShouldThrowOOGException(String[] initMemory, int dst, int src, long length) { + // Given + byte[] code = compiler.compile("MCOPY"); + VM vm = new VM(vmConfig, precompiledContracts); + + Program program = new Program(vmConfig, precompiledContracts, blockFactory, activationConfig, code, invoke, transaction, new HashSet<>(), new BlockTxSignatureCache(new ReceivedTxSignatureCache())); + + int address = 0; + for (String entry : initMemory) { + program.memorySave(DataWord.valueOf(address), DataWord.valueFromHex(entry)); + address += 32; + } + + program.stackPush(DataWord.valueOf(length)); // Mind the stack order!! + program.stackPush(DataWord.valueOf(src)); + program.stackPush(DataWord.valueOf(dst)); + + // Then + try { + while (!program.isStopped()) { + vm.step(program); + } + Assertions.fail("OutOfGasException should be thrown!"); + } catch(Program.StackTooSmallException e) { + Assertions.fail("Stack too small exception"); + } catch(Program.OutOfGasException e) { + Assertions.assertTrue(e.getMessage().contains("Not enough gas for 'MCOPY' operation")); + } + } + + private static Stream provideParametersForMCOPYBoundTest() { + return Stream.of( + Arguments.of(new String[]{ "0000000000000000000000000000000000000000000000000000000000000000" }, 0, 0, -1), + Arguments.of(new String[]{}, 0, 0, -(2 * (Long.MAX_VALUE / 3))), + Arguments.of(new String[]{}, 0, 0, Integer.MAX_VALUE + 1L) + ); + } + +} From 92f3b3719b4187367d3e6a4b26c5edb8bbfd4dbc Mon Sep 17 00:00:00 2001 From: Nazaret Garcia Date: Wed, 4 Dec 2024 12:21:49 -0300 Subject: [PATCH 10/12] Applying suggestions --- .../src/main/java/org/ethereum/vm/OpCode.java | 2 +- .../src/main/java/org/ethereum/vm/VM.java | 2 +- ...CopyBoundTest.java => MCopyInputTest.java} | 28 ++++++++++--------- 3 files changed, 17 insertions(+), 15 deletions(-) rename rskj-core/src/test/java/co/rsk/vm/opcode/{MCopyBoundTest.java => MCopyInputTest.java} (89%) diff --git a/rskj-core/src/main/java/org/ethereum/vm/OpCode.java b/rskj-core/src/main/java/org/ethereum/vm/OpCode.java index 3c7872997a..4991803dff 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/OpCode.java +++ b/rskj-core/src/main/java/org/ethereum/vm/OpCode.java @@ -331,7 +331,7 @@ public enum OpCode { /** * (0x5e) Memory copying instruction */ - MCOPY(0x5e, 3, 0, ZERO_TIER), // TODO UPDATE RSKIP + MCOPY(0x5e, 3, 0, ZERO_TIER), /* Push Operations */ /** diff --git a/rskj-core/src/main/java/org/ethereum/vm/VM.java b/rskj-core/src/main/java/org/ethereum/vm/VM.java index ab4060b985..064183b278 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/VM.java +++ b/rskj-core/src/main/java/org/ethereum/vm/VM.java @@ -773,7 +773,7 @@ private long computeMemoryCopyGas() { long copySize = Program.limitToMaxLong(length); checkSizeArgument(copySize); long newMemSize = memNeeded(offset, copySize); - return calcMemGas(oldMemSize, newMemSize, copySize) + GasCost.MEMORY; + return GasCost.add(calcMemGas(oldMemSize, newMemSize, copySize), GasCost.MEMORY); } protected void doCODESIZE() { diff --git a/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyBoundTest.java b/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyInputTest.java similarity index 89% rename from rskj-core/src/test/java/co/rsk/vm/opcode/MCopyBoundTest.java rename to rskj-core/src/test/java/co/rsk/vm/opcode/MCopyInputTest.java index 45756cf249..27d9627b1e 100644 --- a/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyBoundTest.java +++ b/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyInputTest.java @@ -26,7 +26,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -public class MCopyBoundTest { +public class MCopyInputTest { private ActivationConfig.ForBlock activationConfig; @@ -52,7 +52,7 @@ void setup() { } @ParameterizedTest - @MethodSource("provideParametersForMCOPYBoundTest") + @MethodSource("provideParametersForOOGCases") void testMCopy_ShouldThrowOOGException(String[] initMemory, int dst, int src, long length) { // Given byte[] code = compiler.compile("MCOPY"); @@ -71,19 +71,11 @@ void testMCopy_ShouldThrowOOGException(String[] initMemory, int dst, int src, lo program.stackPush(DataWord.valueOf(dst)); // Then - try { - while (!program.isStopped()) { - vm.step(program); - } - Assertions.fail("OutOfGasException should be thrown!"); - } catch(Program.StackTooSmallException e) { - Assertions.fail("Stack too small exception"); - } catch(Program.OutOfGasException e) { - Assertions.assertTrue(e.getMessage().contains("Not enough gas for 'MCOPY' operation")); - } + Program.OutOfGasException ex = Assertions.assertThrows(Program.OutOfGasException.class, () -> executeProgram(vm, program)); + Assertions.assertTrue(ex.getMessage().contains("Not enough gas for 'MCOPY' operation")); } - private static Stream provideParametersForMCOPYBoundTest() { + private static Stream provideParametersForOOGCases() { return Stream.of( Arguments.of(new String[]{ "0000000000000000000000000000000000000000000000000000000000000000" }, 0, 0, -1), Arguments.of(new String[]{}, 0, 0, -(2 * (Long.MAX_VALUE / 3))), @@ -91,4 +83,14 @@ private static Stream provideParametersForMCOPYBoundTest() { ); } + private static void executeProgram(VM vm, Program program) { + try { + while (!program.isStopped()) { + vm.step(program); + } + } catch(Program.StackTooSmallException e) { + Assertions.fail("Stack too small exception"); + } + } + } From fcc92946bdcfacc7fa0bbd753c9d09cc56b42275 Mon Sep 17 00:00:00 2001 From: Nazaret Garcia Date: Thu, 5 Dec 2024 18:38:34 -0300 Subject: [PATCH 11/12] Updating visibility --- rskj-core/src/main/java/org/ethereum/vm/OpCodes.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rskj-core/src/main/java/org/ethereum/vm/OpCodes.java b/rskj-core/src/main/java/org/ethereum/vm/OpCodes.java index ff7a4af7f1..822e9f0318 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/OpCodes.java +++ b/rskj-core/src/main/java/org/ethereum/vm/OpCodes.java @@ -328,7 +328,7 @@ private OpCodes() { /** * (0x5e) */ - static final byte OP_MCOPY = 0x5e; + public static final byte OP_MCOPY = 0x5e; /* Push Operations */ /** From be19790e0f2de5a31c1929fb355aa62cf2971a0f Mon Sep 17 00:00:00 2001 From: Nazaret Garcia Date: Mon, 9 Dec 2024 09:51:45 -0300 Subject: [PATCH 12/12] Adjusting gas cost --- rskj-core/src/main/java/org/ethereum/vm/OpCode.java | 2 +- rskj-core/src/main/java/org/ethereum/vm/VM.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/rskj-core/src/main/java/org/ethereum/vm/OpCode.java b/rskj-core/src/main/java/org/ethereum/vm/OpCode.java index 4991803dff..52008f8fef 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/OpCode.java +++ b/rskj-core/src/main/java/org/ethereum/vm/OpCode.java @@ -331,7 +331,7 @@ public enum OpCode { /** * (0x5e) Memory copying instruction */ - MCOPY(0x5e, 3, 0, ZERO_TIER), + MCOPY(0x5e, 3, 0, VERY_LOW_TIER), /* Push Operations */ /** diff --git a/rskj-core/src/main/java/org/ethereum/vm/VM.java b/rskj-core/src/main/java/org/ethereum/vm/VM.java index 064183b278..93ce25885a 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/VM.java +++ b/rskj-core/src/main/java/org/ethereum/vm/VM.java @@ -773,7 +773,8 @@ private long computeMemoryCopyGas() { long copySize = Program.limitToMaxLong(length); checkSizeArgument(copySize); long newMemSize = memNeeded(offset, copySize); - return GasCost.add(calcMemGas(oldMemSize, newMemSize, copySize), GasCost.MEMORY); + // Note: 3 additional units are added outside because of the "Very Low Tier" configuration + return calcMemGas(oldMemSize, newMemSize, copySize); } protected void doCODESIZE() {