diff --git a/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ConsensusRule.java b/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ConsensusRule.java index 494dffa696d..dd121ab18f7 100644 --- a/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ConsensusRule.java +++ b/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ConsensusRule.java @@ -99,6 +99,7 @@ public enum ConsensusRule { RSKIP428("rskip428"), RSKIP434("rskip434"), RSKIP438("rskip438"), + RSKIP445("rskip445"), // From EIP-5656 MCOPY instruction RSKIP454("rskip454"), ; 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 f9de1515e87..52008f8fefd 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/OpCode.java +++ b/rskj-core/src/main/java/org/ethereum/vm/OpCode.java @@ -328,7 +328,10 @@ public enum OpCode { * (0x5b) */ JUMPDEST(0x5b, 0, 0, SPECIAL_TIER), - + /** + * (0x5e) Memory copying instruction + */ + MCOPY(0x5e, 3, 0, VERY_LOW_TIER), /* Push Operations */ /** 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 7b483642fb7..822e9f03185 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/OpCodes.java +++ b/rskj-core/src/main/java/org/ethereum/vm/OpCodes.java @@ -325,6 +325,10 @@ private OpCodes() { * (0x5b) */ static final byte OP_JUMPDEST =0x5b ; + /** + * (0x5e) + */ + public static final byte OP_MCOPY = 0x5e; /* 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 47246e2bbc2..45aa797f4ed 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,25 @@ private long computeDataCopyGas() { return calcMemGas(oldMemSize, newMemSize, copySize); } + private long computeMemoryCopyGas() { + DataWord length = stack.get(stack.size() - 3); + DataWord src = stack.get(stack.size() - 2); + DataWord dst = stack.peek(); + + long copySize = Program.limitToMaxLong(length); + checkSizeArgument(copySize); + + DataWord offset = dst; + if (src.value().compareTo(dst.value()) > 0) { + offset = src; + } + + long newMemSize = memNeeded(offset, copySize); + + // Note: 3 additional units are added outside because of the "Very Low Tier" configuration + return calcMemGas(oldMemSize, newMemSize, copySize); + } + protected void doCODESIZE() { if (computeGas) { if (op == OpCode.EXTCODESIZE) { @@ -1436,6 +1455,25 @@ protected void doJUMPDEST() program.step(); } + protected void doMCOPY() { + if (computeGas) { + gasCost = GasCost.add(gasCost, computeMemoryCopyGas()); + spendOpCodeGas(); + } + + // EXECUTION PHASE + DataWord dst = program.stackPop(); + DataWord src = program.stackPop(); + DataWord length = program.stackPop(); + + if (isLogEnabled) { + hint = "dst: " + dst + " src: " + src + " length: " + length; + } + + program.memoryCopy(dst.intValueSafe(), src.intValueSafe(), length.intValueSafe()); + program.step(); + } + protected void doCREATE(){ if (program.isStaticCall() && program.getActivations().isActive(RSKIP91)) { throw Program.ExceptionHelper.modificationException(program); @@ -1992,6 +2030,12 @@ protected void executeOpcode() { break; case OpCodes.OP_JUMPDEST: doJUMPDEST(); break; + case OpCodes.OP_MCOPY: + if (!activations.isActive(RSKIP445)) { + throw Program.ExceptionHelper.invalidOpCode(program); + } + doMCOPY(); + break; case OpCodes.OP_CREATE: doCREATE(); break; case OpCodes.OP_CREATE2: diff --git a/rskj-core/src/main/java/org/ethereum/vm/program/Program.java b/rskj-core/src/main/java/org/ethereum/vm/program/Program.java index 4ee46950c71..e74019e4fa0 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/program/Program.java +++ b/rskj-core/src/main/java/org/ethereum/vm/program/Program.java @@ -390,6 +390,10 @@ public byte[] memoryChunk(int offset, int size) { return memory.read(offset, size); } + public void memoryCopy(int dst, int src, int length) { + memorySave(dst, memoryChunk(src, length)); + } + /** * Allocates extra memory in the program for * a specified size, calculated from a given offset diff --git a/rskj-core/src/main/resources/expected.conf b/rskj-core/src/main/resources/expected.conf index 23f76d177f1..1bd5a587740 100644 --- a/rskj-core/src/main/resources/expected.conf +++ b/rskj-core/src/main/resources/expected.conf @@ -100,8 +100,9 @@ blockchain = { rskip428 = rskip434 = rskip438 = + rskip445 = rskip454 = - } + } } gc = { enabled = diff --git a/rskj-core/src/main/resources/reference.conf b/rskj-core/src/main/resources/reference.conf index ac1c4afb26c..98515b0872e 100644 --- a/rskj-core/src/main/resources/reference.conf +++ b/rskj-core/src/main/resources/reference.conf @@ -85,6 +85,7 @@ blockchain = { rskip428 = lovell700 rskip434 = arrowhead631 rskip438 = lovell700 + rskip445 = lovell700 rskip454 = lovell700 } } diff --git a/rskj-core/src/test/java/co/rsk/vm/VMExecutionTest.java b/rskj-core/src/test/java/co/rsk/vm/VMExecutionTest.java index 6f2d5b56558..224f2451261 100644 --- a/rskj-core/src/test/java/co/rsk/vm/VMExecutionTest.java +++ b/rskj-core/src/test/java/co/rsk/vm/VMExecutionTest.java @@ -870,6 +870,10 @@ private Program executeCodeWithActivationConfig(String code, int nsteps, Activat return executeCodeWithActivationConfig(compiler.compile(code), nsteps, activations); } + private Program executeCodeWithActivationConfig(String code, DataWord[] stack, int nsteps, ActivationConfig.ForBlock activations) { + return executeCodeWithActivationConfig(compiler.compile(code), stack, nsteps, activations); + } + private Program executeCodeWithActivationConfig(byte[] code, int nsteps, ActivationConfig.ForBlock activations) { VM vm = new VM(vmConfig, precompiledContracts); Program program = new Program(vmConfig, precompiledContracts, blockFactory, activations, code, invoke,null, new HashSet<>(), new BlockTxSignatureCache(new ReceivedTxSignatureCache())); @@ -881,6 +885,21 @@ private Program executeCodeWithActivationConfig(byte[] code, int nsteps, Activat return program; } + private Program executeCodeWithActivationConfig(byte[] code, DataWord[] stack, int nsteps, ActivationConfig.ForBlock activations) { + VM vm = new VM(vmConfig, precompiledContracts); + Program program = new Program(vmConfig, precompiledContracts, blockFactory, activations, code, invoke,null, new HashSet<>(), new BlockTxSignatureCache(new ReceivedTxSignatureCache())); + + for (DataWord element : stack) { + program.stackPush(element); + } + + for (int k = 0; k < nsteps; k++) { + vm.step(program); + } + + return program; + } + private Program playCodeWithActivationConfig(String code, ActivationConfig.ForBlock activations) { return playCodeWithActivationConfig(compiler.compile(code), activations); } @@ -947,4 +966,31 @@ private void executeBASEFEE(ActivationConfig.ForBlock activations) { // See ProgramInvokeMockImpl.getMinimumGasPrice() Assertions.assertEquals(DataWord.valueFromHex("000000000000000000000000000000000000000000000000000003104e60a000"), stack.peek()); } + + @Test + void testMCOPY_WhenActive_ExecutesAsExpected() { + ActivationConfig.ForBlock activations = mock(ActivationConfig.ForBlock.class); + when(activations.isActive(RSKIP445)).thenReturn(true); + + executeMCOPY(activations); + } + + @Test + void testMCOPY_WhenInactive_ExecutesAsExpected() { + ActivationConfig.ForBlock activations = mock(ActivationConfig.ForBlock.class); + when(activations.isActive(RSKIP445)).thenReturn(false); + + Assertions.assertThrows(Program.IllegalOperationException.class, () -> { + executeMCOPY(activations); + }); + } + + private void executeMCOPY(ActivationConfig.ForBlock activations) { + DataWord[] stackValues = {DataWord.ZERO, DataWord.ZERO, DataWord.ZERO}; + Program program = executeCodeWithActivationConfig("MCOPY", stackValues, 1, activations); + Stack stack = program.getStack(); + + Assertions.assertEquals(0, stack.size()); + } + } diff --git a/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyDslTest.java b/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyDslTest.java new file mode 100644 index 00000000000..968c33312a2 --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyDslTest.java @@ -0,0 +1,184 @@ +package co.rsk.vm.opcode; + +import co.rsk.config.TestSystemProperties; +import co.rsk.test.World; +import co.rsk.test.dsl.DslParser; +import co.rsk.test.dsl.DslProcessorException; +import co.rsk.test.dsl.WorldDslProcessor; +import com.typesafe.config.ConfigValueFactory; +import org.ethereum.core.Block; +import org.ethereum.core.Transaction; +import org.ethereum.core.TransactionReceipt; +import org.ethereum.core.util.TransactionReceiptUtil; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.FileNotFoundException; +import java.util.stream.Stream; + +class MCopyDslTest { + + @Test + void testMCOPY_whenNotActivated_behavesAsExpected() throws FileNotFoundException, DslProcessorException { + + // Test Config Setup + + TestSystemProperties configWithRskip445Disabled = new TestSystemProperties(rawConfig -> + rawConfig.withValue("blockchain.config.hardforkActivationHeights.lovell700", ConfigValueFactory.fromAnyRef(-1)) + ); + + // Test Setup + + DslParser parser = DslParser.fromResource("dsl/opcode/mcopy/testRSKIPNotActivatedTest.txt"); + World world = new World(configWithRskip445Disabled); + WorldDslProcessor processor = new WorldDslProcessor(world); + processor.processCommands(parser); + + // Assertions + + assertBlockExistsAndContainsExpectedNumberOfTxs(world, "b01", 1); + assertTxExistsWithExpectedReceiptStatus(world, "txTestMCopy", true); + + assertBlockExistsAndContainsExpectedNumberOfTxs(world, "b02", 1); + assertTxExistsWithExpectedReceiptStatus(world, "txTestMCopyNotActivated", false); + + } + + // Advanced Overwrite Test Cases + // https://github.com/ethereum/execution-spec-tests/blob/c0065176a79f89d93f4c326186fc257ec5b8d5f1/tests/cancun/eip5656_mcopy/test_mcopy.py) + + @Test + void testMCOPY_overwriteCases_behaveAsExpected() throws FileNotFoundException, DslProcessorException { + + DslParser parser = DslParser.fromResource("dsl/opcode/mcopy/testOverwriteCases.txt"); + World world = new World(); + WorldDslProcessor processor = new WorldDslProcessor(world); + processor.processCommands(parser); + + // Assertions + + assertBlockExistsAndContainsExpectedNumberOfTxs(world, "b01", 1); + assertTxExistsWithExpectedReceiptStatus(world, "txTestMCopy", true); + + assertBlockExistsAndContainsExpectedNumberOfTxs(world, "b02", 1); + TransactionReceipt txTestMCopyOKCallReceipt = assertTxExistsWithExpectedReceiptStatus(world, "txTestMCopyOKCall", true); + + // Event Assertions + + Assertions.assertEquals(1, TransactionReceiptUtil.getEventCount(txTestMCopyOKCallReceipt, "ZERO_INPUTS_OK", null)); + Assertions.assertEquals(1, TransactionReceiptUtil.getEventCount(txTestMCopyOKCallReceipt, "SINGLE_BYTE_REWRITE_OK", null)); + Assertions.assertEquals(1, TransactionReceiptUtil.getEventCount(txTestMCopyOKCallReceipt, "FULL_WORD_REWRITE_OK", null)); + Assertions.assertEquals(1, TransactionReceiptUtil.getEventCount(txTestMCopyOKCallReceipt, "SINGLE_BYTE_FWD_OVERWRITE_OK", null)); + Assertions.assertEquals(1, TransactionReceiptUtil.getEventCount(txTestMCopyOKCallReceipt, "FULL_WORD_FWD_OVERWRITE_OK", null)); + Assertions.assertEquals(1, TransactionReceiptUtil.getEventCount(txTestMCopyOKCallReceipt, "MID_WORD_SINGLE_BYTE_REWRITE_OK", null)); + Assertions.assertEquals(1, TransactionReceiptUtil.getEventCount(txTestMCopyOKCallReceipt, "MID_WORD_SINGLE_WORD_REWRITE_OK", null)); + Assertions.assertEquals(1, TransactionReceiptUtil.getEventCount(txTestMCopyOKCallReceipt, "MID_WORD_MULTY_WORD_REWRITE_OK", null)); + Assertions.assertEquals(1, TransactionReceiptUtil.getEventCount(txTestMCopyOKCallReceipt, "TWO_WORDS_FWD_OVERWRITE_OK", null)); + Assertions.assertEquals(1, TransactionReceiptUtil.getEventCount(txTestMCopyOKCallReceipt, "TWO_WORDS_BWD_OVERWRITE_OK", null)); + Assertions.assertEquals(1, TransactionReceiptUtil.getEventCount(txTestMCopyOKCallReceipt, "TWO_WORDS_BWD_OVERWRITE_SINGLE_BYTE_OFFSET_OK", null)); + + Assertions.assertEquals(0, TransactionReceiptUtil.getEventCount(txTestMCopyOKCallReceipt, "ERROR", null)); + + } + + @ParameterizedTest + @MethodSource("provideParametersForMCOPYTestCases") + void testMCOPY_OnEachTestCase_ExecutesAsExpected(String dslFile) throws FileNotFoundException, DslProcessorException { + + DslParser parser = DslParser.fromResource("dsl/opcode/mcopy/" + dslFile); + World world = new World(); + WorldDslProcessor processor = new WorldDslProcessor(world); + processor.processCommands(parser); + + // Assertions + + assertBlockExistsAndContainsExpectedNumberOfTxs(world, "b01", 1); + assertTxExistsWithExpectedReceiptStatus(world, "txTestMCopy", true); + + assertBlockExistsAndContainsExpectedNumberOfTxs(world, "b02", 1); + TransactionReceipt txTestMCopyOKCallReceipt = assertTxExistsWithExpectedReceiptStatus(world, "txTestMCopyOKCall", true); + + // Event Assertions + + Assertions.assertEquals(1, TransactionReceiptUtil.getEventCount(txTestMCopyOKCallReceipt, "OK", null)); + Assertions.assertEquals(0, TransactionReceiptUtil.getEventCount(txTestMCopyOKCallReceipt, "ERROR", null)); + + } + + @Test + void testMCOPY_zeroLengthOutOfBoundsDestination_runsOutOfGasAsExpected() throws FileNotFoundException, DslProcessorException { + + DslParser parser = DslParser.fromResource("dsl/opcode/mcopy/testZeroLengthOutOfBoundsDestination.txt"); + World world = new World(); + WorldDslProcessor processor = new WorldDslProcessor(world); + processor.processCommands(parser); + + // Assertions + + assertBlockExistsAndContainsExpectedNumberOfTxs(world, "b01", 1); + assertTxExistsWithExpectedReceiptStatus(world, "txTestMCopy", true); + + assertBlockExistsAndContainsExpectedNumberOfTxs(world, "b02", 1); + TransactionReceipt txTestMCopyOKCallReceipt = assertTxExistsWithExpectedReceiptStatus(world, "txTestMCopyOKCall", false); + + // Event Assertions + + Assertions.assertEquals(0, TransactionReceiptUtil.getEventCount(txTestMCopyOKCallReceipt, "OK", null)); + Assertions.assertEquals(0, TransactionReceiptUtil.getEventCount(txTestMCopyOKCallReceipt, "ERROR", null)); + + } + + private static Stream provideParametersForMCOPYTestCases() { + return Stream.of( + + // Basic Test Cases from 1 to 4 on EIP + Arguments.of("testCopying32BytesFromOffset32toOffset0.txt"), + Arguments.of("testCopying32BytesFromOffset0toOffset0.txt"), + Arguments.of("testCopying8BytesFromOffset1toOffset0.txt"), + Arguments.of("testCopying8BytesFromOffset0toOffset1.txt"), + + // Full Memory Copy/Rewrite/Clean Tests + Arguments.of("testFullMemoryClean.txt"), + Arguments.of("testFullMemoryCopy.txt"), + Arguments.of("testFullMemoryCopyOffset.txt"), + Arguments.of("testFullMemoryRewrite.txt"), + + // Memory Extension Tests + Arguments.of("testOutOfBoundsMemoryExtension.txt"), + Arguments.of("testSingleByteMemoryExtension.txt"), + Arguments.of("testSingleWordMemoryExtension.txt"), + Arguments.of("testSingleWordMinusOneByteMemoryExtension.txt"), + Arguments.of("testSingleWordPlusOneByteMemoryExtension.txt") + + ); + } + + private static void assertBlockExistsAndContainsExpectedNumberOfTxs(World world, String blockName, int expectedNumberOfTxs) { + Block block = world.getBlockByName(blockName); + Assertions.assertNotNull(block); + Assertions.assertEquals(expectedNumberOfTxs, block.getTransactionsList().size()); + } + + private static TransactionReceipt assertTxExistsWithExpectedReceiptStatus(World world, String txName, boolean mustBeSuccessful) { + Transaction tx = world.getTransactionByName(txName); + Assertions.assertNotNull(tx); + + TransactionReceipt txReceipt = world.getTransactionReceiptByName(txName); + Assertions.assertNotNull(txReceipt); + byte[] creationStatus = txReceipt.getStatus(); + Assertions.assertNotNull(creationStatus); + + if (mustBeSuccessful) { + Assertions.assertEquals(1, creationStatus.length); + Assertions.assertEquals(1, creationStatus[0]); + } else { + Assertions.assertEquals(0, creationStatus.length); + } + + return txReceipt; + } + +} 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 00000000000..0ca771c89ee --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyGasTest.java @@ -0,0 +1,171 @@ +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; + +class MCopyGasTest { + + 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("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 += 32; + } + + program.stackPush(DataWord.valueOf(length)); // Mind the stack order!! + program.stackPush(DataWord.valueOf(src)); + program.stackPush(DataWord.valueOf(dst)); + + // When + executeProgram(vm, program); + + // 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), + 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, 51) + ); + } + + @ParameterizedTest + @MethodSource("provideParametersForMCOPYOutOfGasExceptionTest") + void testMCopy_ShouldThrowOutOfGasException(String[] initMemory, String dst, String src, String 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.valueFromHex(length)); // Mind the stack order!! + program.stackPush(DataWord.valueFromHex(src)); + program.stackPush(DataWord.valueFromHex(dst)); + + // Then + 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 provideParametersForMCOPYOutOfGasExceptionTest() { + + String[] emptyMemory = new String[]{}; + String[] existentMemory = new String[]{ + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f", + "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f", + "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "a0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf", + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf", + "e0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" + }; + + String maxSingleByte = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"; // 2**256 - 1 + String maxMinusOneSingleByte = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"; // 2**256 - 2 + String halfMaxSingleByte = "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"; // 2**255 - 1 + + return Stream.of( + + // From an empty memory + Arguments.of(emptyMemory, maxSingleByte, "00", "01"), + Arguments.of(emptyMemory, maxMinusOneSingleByte, "00", "01"), + Arguments.of(emptyMemory, halfMaxSingleByte, "00", "01"), + Arguments.of(emptyMemory, "00", maxSingleByte, "01"), + Arguments.of(emptyMemory, "00", maxMinusOneSingleByte, "01"), + Arguments.of(emptyMemory, "00", halfMaxSingleByte, "01"), + Arguments.of(emptyMemory, "00", "00", maxSingleByte), + Arguments.of(emptyMemory, "00", "00", maxMinusOneSingleByte), + Arguments.of(emptyMemory, "00", "00", halfMaxSingleByte), + + // From existent memory + Arguments.of(existentMemory, maxSingleByte, "00", "01"), + Arguments.of(existentMemory, maxMinusOneSingleByte, "00", "01"), + Arguments.of(existentMemory, halfMaxSingleByte, "00", "01"), + Arguments.of(existentMemory, "00", maxSingleByte, "01"), + Arguments.of(existentMemory, "00", maxMinusOneSingleByte, "01"), + Arguments.of(existentMemory, "00", halfMaxSingleByte, "01"), + Arguments.of(existentMemory, "00", "00", maxSingleByte), + Arguments.of(existentMemory, "00", "00", maxMinusOneSingleByte), + Arguments.of(existentMemory, "00", "00", halfMaxSingleByte) + + ); + } + + 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"); + } + } + +} \ No newline at end of file diff --git a/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyInputTest.java b/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyInputTest.java new file mode 100644 index 00000000000..8a8d5996a9c --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyInputTest.java @@ -0,0 +1,103 @@ +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; + +class MCopyInputTest { + + 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("provideParametersForOOGCases") + void testMCopy_ShouldThrowOOGException(String[] initMemory, long dst, long 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 + 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 provideParametersForOOGCases() { + return Stream.of( + // Special Border Cases + 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), + // Max Memory Limits + Arguments.of(new String[]{}, Program.MAX_MEMORY, 0, 1L), + Arguments.of(new String[]{}, 0, Program.MAX_MEMORY, 1L), + Arguments.of(new String[]{}, 0, 0, Program.MAX_MEMORY + 1), + // Negative Length + Arguments.of(new String[]{}, 0, 0, -1L) + ); + } + + 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"); + } + } + +} \ No newline at end of file diff --git a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java index 821167ab1dc..74e9a37ade3 100644 --- a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java +++ b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java @@ -126,6 +126,7 @@ class ActivationConfigTest { " rskip428: lovell700", " rskip434: arrowhead631", " rskip438: lovell700", + " rskip445: lovell700", " rskip454: lovell700", "}" )); diff --git a/rskj-core/src/test/resources/dsl/opcode/mcopy/testCopying32BytesFromOffset0toOffset0.txt b/rskj-core/src/test/resources/dsl/opcode/mcopy/testCopying32BytesFromOffset0toOffset0.txt new file mode 100644 index 00000000000..f9fbaaeb9ac --- /dev/null +++ b/rskj-core/src/test/resources/dsl/opcode/mcopy/testCopying32BytesFromOffset0toOffset0.txt @@ -0,0 +1,112 @@ +comment + +// CONTRACT CODE +// Corresponds to https://eips.ethereum.org/EIPS/eip-5656 (second case on Test Cases section) + +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +contract TestMCopy { + constructor() {} + + event OK(); + event ERROR(); + + function checkMCopy() external { + bytes32 value = 0x0101010101010101010101010101010101010101010101010101010101010101; + bytes32 result = getCopiedValue(value); + + if (result == value) { + emit OK(); + } else { + emit ERROR(); + } + } + + function getCopiedValue(bytes32 value) public pure returns (bytes32 x) { + assembly { + mstore(0, value) // Store given value at offset 0 in memory + mcopy(0, 0, 32) // Use MCOPY to copy 32 bytes starting at offset 0 to offset 0 in memory + x := mload(0) // Returns the word at offset 0 + } + } + +} + +// DESCRIPTION + +This contract contains two functions: checkMCopy, and getCopiedValue. + +* checkMCopy simply checks the result of the memory copying against an expected value and: + - If returned value matches the expected one, then the OK event is emitted + - ERROR event is emitted otherwise. + +* getCopiedValue manage the memory by storing, copying and reading values as follows: + - First it stores a value to memory on offset 0 + - Then uses MCOPY to copy 32 bytes starting on offset 0 to offset 0 + - Finally it returns the word stored on offset 0 + +// CONTRACT BYTECODE + +6080604052348015600e575f80fd5b506101df8061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80638aa8ef4f146100385780638c2bcab914610068575b5f80fd5b610052600480360381019061004d9190610156565b610072565b60405161005f9190610190565b60405180910390f35b610070610084565b005b5f815f5260205f805e5f519050919050565b5f7f01010101010101010101010101010101010101010101010101010101010101015f1b90505f6100b482610072565b90508181036100ee577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a161011b565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b5050565b5f80fd5b5f819050919050565b61013581610123565b811461013f575f80fd5b50565b5f813590506101508161012c565b92915050565b5f6020828403121561016b5761016a61011f565b5b5f61017884828501610142565b91505092915050565b61018a81610123565b82525050565b5f6020820190506101a35f830184610181565b9291505056fea26469706673582212205d8e61d94ecb994ef2270b32ef0a57105e8d88621a047de9f0440fd6db25ab3864736f6c634300081a0033 + +// CONTRACT CALL + +- checkMCopy() + + 8c2bcab9 + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TestMCopy contract +transaction_build txTestMCopy + sender acc1 + receiverAddress 00 + value 0 + data 6080604052348015600e575f80fd5b506101df8061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80638aa8ef4f146100385780638c2bcab914610068575b5f80fd5b610052600480360381019061004d9190610156565b610072565b60405161005f9190610190565b60405180910390f35b610070610084565b005b5f815f5260205f805e5f519050919050565b5f7f01010101010101010101010101010101010101010101010101010101010101015f1b90505f6100b482610072565b90508181036100ee577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a161011b565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b5050565b5f80fd5b5f819050919050565b61013581610123565b811461013f575f80fd5b50565b5f813590506101508161012c565b92915050565b5f6020828403121561016b5761016a61011f565b5b5f61017884828501610142565b91505092915050565b61018a81610123565b82525050565b5f6020820190506101a35f830184610181565b9291505056fea26469706673582212205d8e61d94ecb994ef2270b32ef0a57105e8d88621a047de9f0440fd6db25ab3864736f6c634300081a0033 + gas 200000 + build + +# Create block to hold txTestMCopy transaction +block_build b01 + parent g00 + transactions txTestMCopy + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Check txTestMCopy succeeded +assert_tx_success txTestMCopy + +# Create transaction to execute checkMCopy() method +transaction_build txTestMCopyOKCall + sender acc1 + nonce 1 + contract txTestMCopy + value 0 + data 8c2bcab9 + gas 30000 + build + +# Create block to hold txTestMCopyOKCall transaction +block_build b02 + parent b01 + transactions txTestMCopyOKCall + gasLimit 30000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 + +# Check txTestMCopyOKCall succeeded +assert_tx_success txTestMCopyOKCall \ No newline at end of file diff --git a/rskj-core/src/test/resources/dsl/opcode/mcopy/testCopying32BytesFromOffset32toOffset0.txt b/rskj-core/src/test/resources/dsl/opcode/mcopy/testCopying32BytesFromOffset32toOffset0.txt new file mode 100644 index 00000000000..c99cf2c012e --- /dev/null +++ b/rskj-core/src/test/resources/dsl/opcode/mcopy/testCopying32BytesFromOffset32toOffset0.txt @@ -0,0 +1,112 @@ +comment + +// CONTRACT CODE +// Corresponds to https://eips.ethereum.org/EIPS/eip-5656 (first case on Test Cases section) + +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +contract TestMCopy { + constructor() {} + + event OK(); + event ERROR(); + + function checkMCopy() external { + bytes32 value = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; + bytes32 result = getCopiedValue(value); + + if (result == value) { + emit OK(); + } else { + emit ERROR(); + } + } + + function getCopiedValue(bytes32 value) public pure returns (bytes32 x) { + assembly { + mstore(32, value) // Store given value at offset 32 in memory + mcopy(0, 32, 32) // Use MCOPY to copy 32 bytes starting at offset 32 to offset 0 in memory + x := mload(0) // Returns the word at offset 0 + } + } + +} + +// DESCRIPTION + +This contract contains two functions: checkMCopy, and getCopiedValue. + +* checkMCopy simply checks the result of the memory copying against an expected value and: + - If returned value matches the expected one, then the OK event is emitted + - ERROR event is emitted otherwise. + +* getCopiedValue manage the memory by storing, copying and reading values as follows: + - First it stores a value to memory on offset 0 + - Then uses MCOPY to copy 32 bytes starting on offset 32 to offset 0 + - Finally it returns the word stored on offset 0 + +// CONTRACT BYTECODE + +6080604052348015600e575f80fd5b506101df8061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80638aa8ef4f146100385780638c2bcab914610068575b5f80fd5b610052600480360381019061004d9190610156565b610072565b60405161005f9190610190565b60405180910390f35b610070610085565b005b5f816020526020805f5e5f519050919050565b5f7e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f6100b482610072565b90508181036100ee577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a161011b565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b5050565b5f80fd5b5f819050919050565b61013581610123565b811461013f575f80fd5b50565b5f813590506101508161012c565b92915050565b5f6020828403121561016b5761016a61011f565b5b5f61017884828501610142565b91505092915050565b61018a81610123565b82525050565b5f6020820190506101a35f830184610181565b9291505056fea2646970667358221220ff9bc6049530cc617c26029a72bb0e926abf7c4420de2d7030377857a017f9a064736f6c634300081a0033 + +// CONTRACT CALL + +- checkMCopy() + + 8c2bcab9 + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TestMCopy contract +transaction_build txTestMCopy + sender acc1 + receiverAddress 00 + value 0 + data 6080604052348015600e575f80fd5b506101df8061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80638aa8ef4f146100385780638c2bcab914610068575b5f80fd5b610052600480360381019061004d9190610156565b610072565b60405161005f9190610190565b60405180910390f35b610070610085565b005b5f816020526020805f5e5f519050919050565b5f7e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f6100b482610072565b90508181036100ee577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a161011b565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b5050565b5f80fd5b5f819050919050565b61013581610123565b811461013f575f80fd5b50565b5f813590506101508161012c565b92915050565b5f6020828403121561016b5761016a61011f565b5b5f61017884828501610142565b91505092915050565b61018a81610123565b82525050565b5f6020820190506101a35f830184610181565b9291505056fea2646970667358221220ff9bc6049530cc617c26029a72bb0e926abf7c4420de2d7030377857a017f9a064736f6c634300081a0033 + gas 200000 + build + +# Create block to hold txTestMCopy transaction +block_build b01 + parent g00 + transactions txTestMCopy + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Check txTestMCopy succeeded +assert_tx_success txTestMCopy + +# Create transaction to execute checkMCopy() method +transaction_build txTestMCopyOKCall + sender acc1 + nonce 1 + contract txTestMCopy + value 0 + data 8c2bcab9 + gas 30000 + build + +# Create block to hold txTestMCopyOKCall transaction +block_build b02 + parent b01 + transactions txTestMCopyOKCall + gasLimit 30000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 + +# Check txTestMCopyOKCall succeeded +assert_tx_success txTestMCopyOKCall \ No newline at end of file diff --git a/rskj-core/src/test/resources/dsl/opcode/mcopy/testCopying8BytesFromOffset0toOffset1.txt b/rskj-core/src/test/resources/dsl/opcode/mcopy/testCopying8BytesFromOffset0toOffset1.txt new file mode 100644 index 00000000000..92c03097ff6 --- /dev/null +++ b/rskj-core/src/test/resources/dsl/opcode/mcopy/testCopying8BytesFromOffset0toOffset1.txt @@ -0,0 +1,113 @@ +comment + +// CONTRACT CODE +// Corresponds to https://eips.ethereum.org/EIPS/eip-5656 (fourth case on Test Cases section) + +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +contract TestMCopy { + constructor() {} + + event OK(); + event ERROR(); + + function checkMCopy() external { + bytes32 expected = 0x0000010203040506070000000000000000000000000000000000000000000000; + bytes32 result = getCopiedValue(); + + if (result == expected) { + emit OK(); + } else { + emit ERROR(); + } + } + + function getCopiedValue() public pure returns (bytes32 x) { + bytes32 initialValue = 0x0001020304050607080000000000000000000000000000000000000000000000; + assembly { + mstore(0, initialValue) // Initialize memory with a word at offset 0 + mcopy(1, 0, 8) // Use MCOPY to copy 8 bytes starting from offset 0 to offset 1 in memory + x := mload(0) // Returns the word starting at offset 0 + } + } + +} + +// DESCRIPTION + +This contract contains two functions: checkMCopy, and getCopiedValue. + +* checkMCopy simply checks the result of the memory copying against an expected value and: + - If returned value matches the expected one, then the OK event is emitted + - ERROR event is emitted otherwise. + +* getCopiedValue manage the memory by storing, copying and reading values as follows: + - First it stores a value to memory on offset 0 + - Then uses MCOPY to copy 8 bytes starting on offset 0 to offset 1 + - Finally it returns the word stored on offset 0 + +// CONTRACT BYTECODE + +6080604052348015600e575f80fd5b506101968061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c806389448792146100385780638c2bcab914610056575b5f80fd5b610040610060565b60405161004d9190610147565b60405180910390f35b61005e610097565b005b5f807e010203040506070800000000000000000000000000000000000000000000005f1b9050805f5260085f60015e5f5191505090565b5f7d0102030405060700000000000000000000000000000000000000000000005f1b90505f6100c4610060565b90508181036100fe577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a161012b565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b5050565b5f819050919050565b6101418161012f565b82525050565b5f60208201905061015a5f830184610138565b9291505056fea2646970667358221220ee4f3cb5e305d93f003d812f3ee4a054eed8063c496be56de4312410213220b964736f6c634300081a0033 + +// CONTRACT CALL + +- checkMCopy() + + 8c2bcab9 + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TestMCopy contract +transaction_build txTestMCopy + sender acc1 + receiverAddress 00 + value 0 + data 6080604052348015600e575f80fd5b506101968061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c806389448792146100385780638c2bcab914610056575b5f80fd5b610040610060565b60405161004d9190610147565b60405180910390f35b61005e610097565b005b5f807e010203040506070800000000000000000000000000000000000000000000005f1b9050805f5260085f60015e5f5191505090565b5f7d0102030405060700000000000000000000000000000000000000000000005f1b90505f6100c4610060565b90508181036100fe577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a161012b565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b5050565b5f819050919050565b6101418161012f565b82525050565b5f60208201905061015a5f830184610138565b9291505056fea2646970667358221220ee4f3cb5e305d93f003d812f3ee4a054eed8063c496be56de4312410213220b964736f6c634300081a0033 + gas 200000 + build + +# Create block to hold txTestMCopy transaction +block_build b01 + parent g00 + transactions txTestMCopy + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Check txTestMCopy succeeded +assert_tx_success txTestMCopy + +# Create transaction to execute checkMCopy() method +transaction_build txTestMCopyOKCall + sender acc1 + nonce 1 + contract txTestMCopy + value 0 + data 8c2bcab9 + gas 30000 + build + +# Create block to hold txTestMCopyOKCall transaction +block_build b02 + parent b01 + transactions txTestMCopyOKCall + gasLimit 30000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 + +# Check txTestMCopyOKCall succeeded +assert_tx_success txTestMCopyOKCall \ No newline at end of file diff --git a/rskj-core/src/test/resources/dsl/opcode/mcopy/testCopying8BytesFromOffset1toOffset0.txt b/rskj-core/src/test/resources/dsl/opcode/mcopy/testCopying8BytesFromOffset1toOffset0.txt new file mode 100644 index 00000000000..1f33081f337 --- /dev/null +++ b/rskj-core/src/test/resources/dsl/opcode/mcopy/testCopying8BytesFromOffset1toOffset0.txt @@ -0,0 +1,113 @@ +comment + +// CONTRACT CODE +// Corresponds to https://eips.ethereum.org/EIPS/eip-5656 (third case on Test Cases section) + +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +contract TestMCopy { + constructor() {} + + event OK(); + event ERROR(); + + function checkMCopy() external { + bytes32 expected = 0x0102030405060708080000000000000000000000000000000000000000000000; + bytes32 result = getCopiedValue(); + + if (result == expected) { + emit OK(); + } else { + emit ERROR(); + } + } + + function getCopiedValue() public pure returns (bytes32 x) { + bytes32 initialValue = 0x0001020304050607080000000000000000000000000000000000000000000000; + assembly { + mstore(0, initialValue) // Initialize memory with a word at offset 0 + mcopy(0, 1, 8) // Use MCOPY to copy 8 bytes starting from offset 1 to offset 0 in memory + x := mload(0) // Returns the word starting at offset 0 + } + } + +} + +// DESCRIPTION + +This contract contains two functions: checkMCopy, and getCopiedValue. + +* checkMCopy simply checks the result of the memory copying against an expected value and: + - If returned value matches the expected one, then the OK event is emitted + - ERROR event is emitted otherwise. + +* getCopiedValue manage the memory by storing, copying and reading values as follows: + - First it stores a value to memory on offset 0 + - Then uses MCOPY to copy 8 bytes starting on offset 1 to offset 0 + - Finally it returns the word stored on offset 0 + +// CONTRACT BYTECODE + +6080604052348015600e575f80fd5b506101988061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c806389448792146100385780638c2bcab914610056575b5f80fd5b610040610060565b60405161004d9190610149565b60405180910390f35b61005e610097565b005b5f807e010203040506070800000000000000000000000000000000000000000000005f1b9050805f52600860015f5e5f5191505090565b5f7f01020304050607080800000000000000000000000000000000000000000000005f1b90505f6100c6610060565b9050818103610100577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a161012d565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b5050565b5f819050919050565b61014381610131565b82525050565b5f60208201905061015c5f83018461013a565b9291505056fea26469706673582212204f708ad743bdd487256dc87ae669c08786cb50977047b5963de97e2830c132aa64736f6c634300081a0033 + +// CONTRACT CALL + +- checkMCopy() + + 8c2bcab9 + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TestMCopy contract +transaction_build txTestMCopy + sender acc1 + receiverAddress 00 + value 0 + data 6080604052348015600e575f80fd5b506101988061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c806389448792146100385780638c2bcab914610056575b5f80fd5b610040610060565b60405161004d9190610149565b60405180910390f35b61005e610097565b005b5f807e010203040506070800000000000000000000000000000000000000000000005f1b9050805f52600860015f5e5f5191505090565b5f7f01020304050607080800000000000000000000000000000000000000000000005f1b90505f6100c6610060565b9050818103610100577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a161012d565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b5050565b5f819050919050565b61014381610131565b82525050565b5f60208201905061015c5f83018461013a565b9291505056fea26469706673582212204f708ad743bdd487256dc87ae669c08786cb50977047b5963de97e2830c132aa64736f6c634300081a0033 + gas 200000 + build + +# Create block to hold txTestMCopy transaction +block_build b01 + parent g00 + transactions txTestMCopy + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Check txTestMCopy succeeded +assert_tx_success txTestMCopy + +# Create transaction to execute checkMCopy() method +transaction_build txTestMCopyOKCall + sender acc1 + nonce 1 + contract txTestMCopy + value 0 + data 8c2bcab9 + gas 30000 + build + +# Create block to hold txTestMCopyOKCall transaction +block_build b02 + parent b01 + transactions txTestMCopyOKCall + gasLimit 30000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 + +# Check txTestMCopyOKCall succeeded +assert_tx_success txTestMCopyOKCall \ No newline at end of file diff --git a/rskj-core/src/test/resources/dsl/opcode/mcopy/testFullMemoryClean.txt b/rskj-core/src/test/resources/dsl/opcode/mcopy/testFullMemoryClean.txt new file mode 100644 index 00000000000..00a21c4457e --- /dev/null +++ b/rskj-core/src/test/resources/dsl/opcode/mcopy/testFullMemoryClean.txt @@ -0,0 +1,137 @@ +comment + +// CONTRACT CODE +// + +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +contract TestMCopy { + constructor() {} + + event OK(); + event ERROR(); + + function checkMCopy() external { + if (testFullMemoryClean()) { + emit OK(); + } else { + emit ERROR(); + } + } + + function testFullMemoryClean() public pure returns (bool status) { + + bytes32 zeroed = 0x0000000000000000000000000000000000000000000000000000000000000000; + + uint memSizeBefore; + uint memSizeAfter; + + assembly { + + mstore(0, 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f) + mstore(32, 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f) + mstore(64, 0x404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f) + mstore(96, 0x606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f) + mstore(128, 0x808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f) + mstore(160, 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf) + mstore(192, 0xc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf) + mstore(224, 0xe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff) // ... Initialize Memory + + memSizeBefore := msize() // Get memory size before the changes + mcopy(0, 256, 256) // Use MCOPY to copy 256 bytes starting from offset 256 to offset 0 in memory + memSizeAfter := msize() // Get memory size after the changes + + status := eq(mload(0), zeroed) + status := and(status, eq(mload(32), zeroed)) + status := and(status, eq(mload(64), zeroed)) + status := and(status, eq(mload(96), zeroed)) + status := and(status, eq(mload(128), zeroed)) + status := and(status, eq(mload(160), zeroed)) + status := and(status, eq(mload(192), zeroed)) + status := and(status, eq(mload(224), zeroed)) // All words should contain only 0s (they're cleared) + + } + + status = status && memSizeBefore == 256 && memSizeAfter == 512; // New memory size is the double of the initial size + + } + +} + +// DESCRIPTION + +This contract contains two types of functions: + +* checkMCopy runs the tes function and based on its return value: + - Emits an OK event if it returned true. + - Emits an ERROR otherwise. + +* testFullMemoryClean initialize, modifies and checks memory and then: + - Returns true if all the checks passed. + - Returns false otherwise. + +// CONTRACT BYTECODE + +6080604052348015600e575f80fd5b506102d18061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c806318d79be0146100385780638c2bcab914610056575b5f80fd5b610040610060565b60405161004d9190610282565b60405180910390f35b61005e6101fb565b005b5f805f801b90505f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f527f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f6020527f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f6040527f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f6060527f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f6080527fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf60a0527fc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf60c0527fe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff60e052599150610100805f5e599050825f511493508260205114841693508260405114841693508260605114841693508260805114841693508260a05114841693508260c05114841693508260e05114841693508380156101e6575061010082145b80156101f3575061020081145b935050505090565b610203610060565b15610239577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a1610266565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b565b5f8115159050919050565b61027c81610268565b82525050565b5f6020820190506102955f830184610273565b9291505056fea2646970667358221220f5efb928b226b33069cc5084e89551d5d7260bc1d9e5639f849fa8ded0ee8f0e64736f6c634300081a0033 + +// CONTRACT CALL + +- checkMCopy() + + 8c2bcab9 + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TestMCopy contract +transaction_build txTestMCopy + sender acc1 + receiverAddress 00 + value 0 + data 6080604052348015600e575f80fd5b506102d18061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c806318d79be0146100385780638c2bcab914610056575b5f80fd5b610040610060565b60405161004d9190610282565b60405180910390f35b61005e6101fb565b005b5f805f801b90505f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f527f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f6020527f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f6040527f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f6060527f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f6080527fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf60a0527fc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf60c0527fe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff60e052599150610100805f5e599050825f511493508260205114841693508260405114841693508260605114841693508260805114841693508260a05114841693508260c05114841693508260e05114841693508380156101e6575061010082145b80156101f3575061020081145b935050505090565b610203610060565b15610239577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a1610266565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b565b5f8115159050919050565b61027c81610268565b82525050565b5f6020820190506102955f830184610273565b9291505056fea2646970667358221220f5efb928b226b33069cc5084e89551d5d7260bc1d9e5639f849fa8ded0ee8f0e64736f6c634300081a0033 + gas 300000 + build + +# Create block to hold txTestMCopy transaction +block_build b01 + parent g00 + transactions txTestMCopy + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Check txTestMCopy succeeded +assert_tx_success txTestMCopy + +# Create transaction to execute checkMCopy() method +transaction_build txTestMCopyOKCall + sender acc1 + nonce 1 + contract txTestMCopy + value 0 + data 8c2bcab9 + gas 30000 + build + +# Create block to hold txTestMCopyOKCall transaction +block_build b02 + parent b01 + transactions txTestMCopyOKCall + gasLimit 30000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 + +# Check txTestMCopyOKCall succeeded +assert_tx_success txTestMCopyOKCall \ No newline at end of file diff --git a/rskj-core/src/test/resources/dsl/opcode/mcopy/testFullMemoryCopy.txt b/rskj-core/src/test/resources/dsl/opcode/mcopy/testFullMemoryCopy.txt new file mode 100644 index 00000000000..d025db1a235 --- /dev/null +++ b/rskj-core/src/test/resources/dsl/opcode/mcopy/testFullMemoryCopy.txt @@ -0,0 +1,153 @@ +comment + +// CONTRACT CODE +// + +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +contract TestMCopy { + constructor() {} + + event OK(); + event ERROR(); + + function checkMCopy() external { + if (testFullMemoryCopy()) { + emit OK(); + } else { + emit ERROR(); + } + } + + function testFullMemoryCopy() public pure returns (bool status) { + + bytes32 word1 = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; + bytes32 word2 = 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f; + bytes32 word3 = 0x404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f; + bytes32 word4 = 0x606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f; + bytes32 word5 = 0x808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f; + bytes32 word6 = 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf; + bytes32 word7 = 0xc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf; + bytes32 word8 = 0xe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff; + + uint memSizeBefore; + uint memSizeAfter; + + assembly { + + mstore(0, word1) + mstore(32, word2) + mstore(64, word3) + mstore(96, word4) + mstore(128, word5) + mstore(160, word6) + mstore(192, word7) + mstore(224, word8) // ... Initialize Memory + + memSizeBefore := msize() // Get memory size before the changes + mcopy(256, 0, 256) // Use MCOPY to copy 256 bytes starting from offset 0 to offset 256 in memory + memSizeAfter := msize() // Get memory size after the changes + + status := eq(mload(0), word1) + status := and(status, eq(mload(32), word2)) + status := and(status, eq(mload(64), word3)) + status := and(status, eq(mload(96), word4)) + status := and(status, eq(mload(128), word5)) + status := and(status, eq(mload(160), word6)) + status := and(status, eq(mload(192), word7)) + status := and(status, eq(mload(224), word8)) // ... Old memory stays as it was + + status := and(status, eq(mload(256), word1)) + status := and(status, eq(mload(288), word2)) + status := and(status, eq(mload(320), word3)) + status := and(status, eq(mload(352), word4)) + status := and(status, eq(mload(384), word5)) + status := and(status, eq(mload(416), word6)) + status := and(status, eq(mload(448), word7)) + status := and(status, eq(mload(480), word8)) // ... And new entries contain expected values + + } + + status = status && memSizeBefore == 256 && memSizeAfter == 512; // New memory size is the double of the initial size + + } + +} + +// DESCRIPTION + +This contract contains two types of functions: + +* checkMCopy runs the tes function and based on its return value: + - Emits an OK event if it returned true. + - Emits an ERROR otherwise. + +* testFullMemoryCopy initialize, modifies and checks memory and then: + - Returns true if all the checks passed. + - Returns false otherwise. + +// CONTRACT BYTECODE + +6080604052348015600e575f80fd5b506103548061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80638c2bcab914610038578063c0b80c4f14610042575b5f80fd5b610040610060565b005b61004a6100cd565b6040516100579190610305565b60405180910390f35b6100686100cd565b1561009e577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16100cb565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b565b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f7f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f5f1b90505f7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f5f1b90505f7fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf5f1b90505f7fc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf5f1b90505f7fe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff5f1b90505f80895f52886020528760405286606052856080528460a0528360c0528260e0525991506101005f6101005e599050895f51149a5088602051148b169a5087604051148b169a5086606051148b169a5085608051148b169a508460a051148b169a508360c051148b169a508260e051148b169a508961010051148b169a508861012051148b169a508761014051148b169a508661016051148b169a508561018051148b169a50846101a051148b169a50836101c051148b169a50826101e051148b169a508a80156102cf575061010082145b80156102dc575061020081145b9a505050505050505050505090565b5f8115159050919050565b6102ff816102eb565b82525050565b5f6020820190506103185f8301846102f6565b9291505056fea26469706673582212206300c7f37f94930d3bc96bba8b03c281abe4667f5dc4b92938d6479b051a62cb64736f6c634300081a0033 + +// CONTRACT CALL + +- checkMCopy() + + 8c2bcab9 + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TestMCopy contract +transaction_build txTestMCopy + sender acc1 + receiverAddress 00 + value 0 + data 6080604052348015600e575f80fd5b506103548061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80638c2bcab914610038578063c0b80c4f14610042575b5f80fd5b610040610060565b005b61004a6100cd565b6040516100579190610305565b60405180910390f35b6100686100cd565b1561009e577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16100cb565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b565b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f7f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f5f1b90505f7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f5f1b90505f7fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf5f1b90505f7fc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf5f1b90505f7fe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff5f1b90505f80895f52886020528760405286606052856080528460a0528360c0528260e0525991506101005f6101005e599050895f51149a5088602051148b169a5087604051148b169a5086606051148b169a5085608051148b169a508460a051148b169a508360c051148b169a508260e051148b169a508961010051148b169a508861012051148b169a508761014051148b169a508661016051148b169a508561018051148b169a50846101a051148b169a50836101c051148b169a50826101e051148b169a508a80156102cf575061010082145b80156102dc575061020081145b9a505050505050505050505090565b5f8115159050919050565b6102ff816102eb565b82525050565b5f6020820190506103185f8301846102f6565b9291505056fea26469706673582212206300c7f37f94930d3bc96bba8b03c281abe4667f5dc4b92938d6479b051a62cb64736f6c634300081a0033 + gas 400000 + build + +# Create block to hold txTestMCopy transaction +block_build b01 + parent g00 + transactions txTestMCopy + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Check txTestMCopy succeeded +assert_tx_success txTestMCopy + +# Create transaction to execute checkMCopy() method +transaction_build txTestMCopyOKCall + sender acc1 + nonce 1 + contract txTestMCopy + value 0 + data 8c2bcab9 + gas 30000 + build + +# Create block to hold txTestMCopyOKCall transaction +block_build b02 + parent b01 + transactions txTestMCopyOKCall + gasLimit 30000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 + +# Check txTestMCopyOKCall succeeded +assert_tx_success txTestMCopyOKCall \ No newline at end of file diff --git a/rskj-core/src/test/resources/dsl/opcode/mcopy/testFullMemoryCopyOffset.txt b/rskj-core/src/test/resources/dsl/opcode/mcopy/testFullMemoryCopyOffset.txt new file mode 100644 index 00000000000..c27a5d21f40 --- /dev/null +++ b/rskj-core/src/test/resources/dsl/opcode/mcopy/testFullMemoryCopyOffset.txt @@ -0,0 +1,164 @@ +comment + +// CONTRACT CODE +// + +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +contract TestMCopy { + constructor() {} + + event OK(); + event ERROR(); + + function checkMCopy() external { + if (testFullMemoryCopyOffset()) { + emit OK(); + } else { + emit ERROR(); + } + } + + function testFullMemoryCopyOffset() public pure returns (bool status) { + + bytes32 word1 = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; + bytes32 word2 = 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f; + bytes32 word3 = 0x404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f; + bytes32 word4 = 0x606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f; + bytes32 word5 = 0x808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f; + bytes32 word6 = 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf; + bytes32 word7 = 0xc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf; + bytes32 word8 = 0xe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff; + + bytes32 zeroed = 0x0000000000000000000000000000000000000000000000000000000000000000; + + uint memSizeBefore; + uint memSizeAfter; + + assembly { + + mstore(0, word1) + mstore(32, word2) + mstore(64, word3) + mstore(96, word4) + mstore(128, word5) + mstore(160, word6) + mstore(192, word7) + mstore(224, word8) // ... Initialize Memory + + memSizeBefore := msize() // Get memory size before the changes + mcopy(512, 0, 256) // Use MCOPY to copy 256 bytes starting from offset 0 to offset 512 in memory + memSizeAfter := msize() // Get memory size after the changes + + status := eq(mload(0), word1) + status := and(status, eq(mload(32), word2)) + status := and(status, eq(mload(64), word3)) + status := and(status, eq(mload(96), word4)) + status := and(status, eq(mload(128), word5)) + status := and(status, eq(mload(160), word6)) + status := and(status, eq(mload(192), word7)) + status := and(status, eq(mload(224), word8)) // ... Old memory stays as it was + + status := and(status, eq(mload(256), zeroed)) + status := and(status, eq(mload(288), zeroed)) + status := and(status, eq(mload(320), zeroed)) + status := and(status, eq(mload(352), zeroed)) + status := and(status, eq(mload(384), zeroed)) + status := and(status, eq(mload(416), zeroed)) + status := and(status, eq(mload(448), zeroed)) + status := and(status, eq(mload(480), zeroed)) // ... The new offset words are all 0s + + status := and(status, eq(mload(512), word1)) + status := and(status, eq(mload(544), word2)) + status := and(status, eq(mload(576), word3)) + status := and(status, eq(mload(608), word4)) + status := and(status, eq(mload(640), word5)) + status := and(status, eq(mload(672), word6)) + status := and(status, eq(mload(704), word7)) + status := and(status, eq(mload(736), word8)) // ... And new entries contain expected values + + } + + status = status && memSizeBefore == 256 && memSizeAfter == 768; // New memory size is the double of the initial size, plus the offset + + } + +} + +// DESCRIPTION + +This contract contains two types of functions: + +* checkMCopy runs the tes function and based on its return value: + - Emits an OK event if it returned true. + - Emits an ERROR otherwise. + +* testFullMemoryCopyOffset initialize, modifies and checks memory and then: + - Returns true if all the checks passed. + - Returns false otherwise. + +// CONTRACT BYTECODE + +6080604052348015600e575f80fd5b506103ab8061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c8063555ef883146100385780638c2bcab914610056575b5f80fd5b610040610060565b60405161004d919061035c565b60405180910390f35b61005e6102d5565b005b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f7f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f5f1b90505f7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f5f1b90505f7fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf5f1b90505f7fc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf5f1b90505f7fe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff5f1b90505f805f1b90505f808a5f52896020528860405287606052866080528560a0528460c0528360e0525991506101005f6102005e5990508a5f51149b5089602051148c169b5088604051148c169b5087606051148c169b5086608051148c169b508560a051148c169b508460c051148c169b508360e051148c169b508261010051148c169b508261012051148c169b508261014051148c169b508261016051148c169b508261018051148c169b50826101a051148c169b50826101c051148c169b50826101e051148c169b508a61020051148c169b508961022051148c169b508861024051148c169b508761026051148c169b508661028051148c169b50856102a051148c169b50846102c051148c169b50836102e051148c169b508b80156102b8575061010082145b80156102c5575061030081145b9b50505050505050505050505090565b6102dd610060565b15610313577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a1610340565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b565b5f8115159050919050565b61035681610342565b82525050565b5f60208201905061036f5f83018461034d565b9291505056fea2646970667358221220c4e94d0d017a821439be881bd7ae577b35556e64a80392c127a557d02e3656eb64736f6c634300081a0033 + +// CONTRACT CALL + +- checkMCopy() + + 8c2bcab9 + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TestMCopy contract +transaction_build txTestMCopy + sender acc1 + receiverAddress 00 + value 0 + data 6080604052348015600e575f80fd5b506103ab8061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c8063555ef883146100385780638c2bcab914610056575b5f80fd5b610040610060565b60405161004d919061035c565b60405180910390f35b61005e6102d5565b005b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f7f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f5f1b90505f7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f5f1b90505f7fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf5f1b90505f7fc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf5f1b90505f7fe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff5f1b90505f805f1b90505f808a5f52896020528860405287606052866080528560a0528460c0528360e0525991506101005f6102005e5990508a5f51149b5089602051148c169b5088604051148c169b5087606051148c169b5086608051148c169b508560a051148c169b508460c051148c169b508360e051148c169b508261010051148c169b508261012051148c169b508261014051148c169b508261016051148c169b508261018051148c169b50826101a051148c169b50826101c051148c169b50826101e051148c169b508a61020051148c169b508961022051148c169b508861024051148c169b508761026051148c169b508661028051148c169b50856102a051148c169b50846102c051148c169b50836102e051148c169b508b80156102b8575061010082145b80156102c5575061030081145b9b50505050505050505050505090565b6102dd610060565b15610313577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a1610340565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b565b5f8115159050919050565b61035681610342565b82525050565b5f60208201905061036f5f83018461034d565b9291505056fea2646970667358221220c4e94d0d017a821439be881bd7ae577b35556e64a80392c127a557d02e3656eb64736f6c634300081a0033 + gas 400000 + build + +# Create block to hold txTestMCopy transaction +block_build b01 + parent g00 + transactions txTestMCopy + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Check txTestMCopy succeeded +assert_tx_success txTestMCopy + +# Create transaction to execute checkMCopy() method +transaction_build txTestMCopyOKCall + sender acc1 + nonce 1 + contract txTestMCopy + value 0 + data 8c2bcab9 + gas 30000 + build + +# Create block to hold txTestMCopyOKCall transaction +block_build b02 + parent b01 + transactions txTestMCopyOKCall + gasLimit 30000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 + +# Check txTestMCopyOKCall succeeded +assert_tx_success txTestMCopyOKCall \ No newline at end of file diff --git a/rskj-core/src/test/resources/dsl/opcode/mcopy/testFullMemoryRewrite.txt b/rskj-core/src/test/resources/dsl/opcode/mcopy/testFullMemoryRewrite.txt new file mode 100644 index 00000000000..ae4995423dd --- /dev/null +++ b/rskj-core/src/test/resources/dsl/opcode/mcopy/testFullMemoryRewrite.txt @@ -0,0 +1,144 @@ +comment + +// CONTRACT CODE +// + +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +contract TestMCopy { + constructor() {} + + event OK(); + event ERROR(); + + function checkMCopy() external { + if (testFullMemoryRewrite()) { + emit OK(); + } else { + emit ERROR(); + } + } + + function testFullMemoryRewrite() public pure returns (bool status) { + + bytes32 word1 = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; + bytes32 word2 = 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f; + bytes32 word3 = 0x404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f; + bytes32 word4 = 0x606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f; + bytes32 word5 = 0x808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f; + bytes32 word6 = 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf; + bytes32 word7 = 0xc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf; + bytes32 word8 = 0xe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff; + + uint memSizeBefore; + uint memSizeAfter; + + assembly { + + mstore(0, word1) + mstore(32, word2) + mstore(64, word3) + mstore(96, word4) + mstore(128, word5) + mstore(160, word6) + mstore(192, word7) + mstore(224, word8) // ... Initialize Memory + + memSizeBefore := msize() // Get memory size before the changes + mcopy(0, 0, 256) // Use MCOPY to copy 256 bytes starting from offset 0 to offset 0 in memory + memSizeAfter := msize() // Get memory size after the changes + + status := eq(mload(0), word1) + status := and(status, eq(mload(32), word2)) + status := and(status, eq(mload(64), word3)) + status := and(status, eq(mload(96), word4)) + status := and(status, eq(mload(128), word5)) + status := and(status, eq(mload(160), word6)) + status := and(status, eq(mload(192), word7)) + status := and(status, eq(mload(224), word8)) // ... Everything stays as it was + + } + + status = status && memSizeBefore == 256 && memSizeAfter == 256; // Memory size is the same as it was before + + } + +} + +// DESCRIPTION + +This contract contains two types of functions: + +* checkMCopy runs the tes function and based on its return value: + - Emits an OK event if it returned true. + - Emits an ERROR otherwise. + +* testFullMemoryRewrite initialize, modifies and checks memory and then: + - Returns true if all the checks passed. + - Returns false otherwise. + +// CONTRACT BYTECODE + +6080604052348015600e575f80fd5b506103028061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80633a05a424146100385780638c2bcab914610056575b5f80fd5b610040610060565b60405161004d91906102b3565b60405180910390f35b61005e61022c565b005b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f7f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f5f1b90505f7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f5f1b90505f7fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf5f1b90505f7fc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf5f1b90505f7fe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff5f1b90505f80895f52886020528760405286606052856080528460a0528360c0528260e0525991506101005f805e599050895f51149a5088602051148b169a5087604051148b169a5086606051148b169a5085608051148b169a508460a051148b169a508360c051148b169a508260e051148b169a508a8015610210575061010082145b801561021d575061010081145b9a505050505050505050505090565b610234610060565b1561026a577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a1610297565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b565b5f8115159050919050565b6102ad81610299565b82525050565b5f6020820190506102c65f8301846102a4565b9291505056fea2646970667358221220403c015d6168a20ce36383a9cbd22e79e8f007dcf460a1a87e6bbff01226f61464736f6c634300081a0033 + +// CONTRACT CALL + +- checkMCopy() + + 8c2bcab9 + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TestMCopy contract +transaction_build txTestMCopy + sender acc1 + receiverAddress 00 + value 0 + data 6080604052348015600e575f80fd5b506103028061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80633a05a424146100385780638c2bcab914610056575b5f80fd5b610040610060565b60405161004d91906102b3565b60405180910390f35b61005e61022c565b005b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f7f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f5f1b90505f7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f5f1b90505f7fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf5f1b90505f7fc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf5f1b90505f7fe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff5f1b90505f80895f52886020528760405286606052856080528460a0528360c0528260e0525991506101005f805e599050895f51149a5088602051148b169a5087604051148b169a5086606051148b169a5085608051148b169a508460a051148b169a508360c051148b169a508260e051148b169a508a8015610210575061010082145b801561021d575061010081145b9a505050505050505050505090565b610234610060565b1561026a577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a1610297565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b565b5f8115159050919050565b6102ad81610299565b82525050565b5f6020820190506102c65f8301846102a4565b9291505056fea2646970667358221220403c015d6168a20ce36383a9cbd22e79e8f007dcf460a1a87e6bbff01226f61464736f6c634300081a0033 + gas 300000 + build + +# Create block to hold txTestMCopy transaction +block_build b01 + parent g00 + transactions txTestMCopy + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Check txTestMCopy succeeded +assert_tx_success txTestMCopy + +# Create transaction to execute checkMCopy() method +transaction_build txTestMCopyOKCall + sender acc1 + nonce 1 + contract txTestMCopy + value 0 + data 8c2bcab9 + gas 30000 + build + +# Create block to hold txTestMCopyOKCall transaction +block_build b02 + parent b01 + transactions txTestMCopyOKCall + gasLimit 30000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 + +# Check txTestMCopyOKCall succeeded +assert_tx_success txTestMCopyOKCall \ No newline at end of file diff --git a/rskj-core/src/test/resources/dsl/opcode/mcopy/testOutOfBoundsMemoryExtension.txt b/rskj-core/src/test/resources/dsl/opcode/mcopy/testOutOfBoundsMemoryExtension.txt new file mode 100644 index 00000000000..925c2d77aca --- /dev/null +++ b/rskj-core/src/test/resources/dsl/opcode/mcopy/testOutOfBoundsMemoryExtension.txt @@ -0,0 +1,148 @@ +comment + +// CONTRACT CODE +// + +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +contract TestMCopy { + constructor() {} + + event OK(); + event ERROR(); + + function checkMCopy() external { + if (testOutOfBoundsMemoryExtension()) { + emit OK(); + } else { + emit ERROR(); + } + } + + function testOutOfBoundsMemoryExtension() public pure returns (bool status) { + + bytes32 word1 = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; + bytes32 word2 = 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f; + bytes32 word3 = 0x404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f; + bytes32 word4 = 0x606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f; + bytes32 word5 = 0x808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f; + bytes32 word6 = 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf; + bytes32 word7 = 0xc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf; + bytes32 word8 = 0xe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff; + + bytes32 zeroed = 0x0000000000000000000000000000000000000000000000000000000000000000; + + uint memSizeBefore; + uint memSizeAfter; + + assembly { + + mstore(0, word1) + mstore(32, word2) + mstore(64, word3) + mstore(96, word4) + mstore(128, word5) + mstore(160, word6) + mstore(192, word7) + mstore(224, word8) // ... Initialize Memory + + memSizeBefore := msize() // Get memory size before the changes + mcopy(256, 256, 1) // Use MCOPY to copy 1 byte starting from offset 256 to offset 256 in memory + memSizeAfter := msize() // Get memory size after the changes + + status := eq(mload(0), word1) + status := and(status, eq(mload(32), word2)) + status := and(status, eq(mload(64), word3)) + status := and(status, eq(mload(96), word4)) + status := and(status, eq(mload(128), word5)) + status := and(status, eq(mload(160), word6)) + status := and(status, eq(mload(192), word7)) + status := and(status, eq(mload(224), word8)) // ... Old memory stays as it was + + status := and(status, eq(mload(256), zeroed)) // Last (and new) word will contain all 0s + + } + + status = status && memSizeBefore == 256 && memSizeAfter == 288; // New memory size is the initial size plus 32 additional bytes + + } + +} + +// DESCRIPTION + +This contract contains two types of functions: + +* checkMCopy runs the tes function and based on its return value: + - Emits an OK event if it returned true. + - Emits an ERROR otherwise. + +* testOutOfBoundsMemoryExtension initialize, modifies and checks memory and then: + - Returns true if all the checks passed. + - Returns false otherwise. + +// CONTRACT BYTECODE + +6080604052348015600e575f80fd5b506103148061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80638c2bcab9146100385780638e97cccf14610042575b5f80fd5b610040610060565b005b61004a6100cd565b60405161005791906102c5565b60405180910390f35b6100686100cd565b1561009e577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16100cb565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b565b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f7f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f5f1b90505f7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f5f1b90505f7fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf5f1b90505f7fc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf5f1b90505f7fe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff5f1b90505f805f1b90505f808a5f52896020528860405287606052866080528560a0528460c0528360e0525991506001610100805e5990508a5f51149b5089602051148c169b5088604051148c169b5087606051148c169b5086608051148c169b508560a051148c169b508460c051148c169b508360e051148c169b508261010051148c169b508b801561028e575061010082145b801561029b575061012081145b9b50505050505050505050505090565b5f8115159050919050565b6102bf816102ab565b82525050565b5f6020820190506102d85f8301846102b6565b9291505056fea2646970667358221220158e838124016e3e2b38988de977bc64e257f5f39460a26e461104d0e815677d64736f6c634300081a0033 + +// CONTRACT CALL + +- checkMCopy() + + 8c2bcab9 + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TestMCopy contract +transaction_build txTestMCopy + sender acc1 + receiverAddress 00 + value 0 + data 6080604052348015600e575f80fd5b506103148061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80638c2bcab9146100385780638e97cccf14610042575b5f80fd5b610040610060565b005b61004a6100cd565b60405161005791906102c5565b60405180910390f35b6100686100cd565b1561009e577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16100cb565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b565b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f7f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f5f1b90505f7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f5f1b90505f7fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf5f1b90505f7fc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf5f1b90505f7fe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff5f1b90505f805f1b90505f808a5f52896020528860405287606052866080528560a0528460c0528360e0525991506001610100805e5990508a5f51149b5089602051148c169b5088604051148c169b5087606051148c169b5086608051148c169b508560a051148c169b508460c051148c169b508360e051148c169b508261010051148c169b508b801561028e575061010082145b801561029b575061012081145b9b50505050505050505050505090565b5f8115159050919050565b6102bf816102ab565b82525050565b5f6020820190506102d85f8301846102b6565b9291505056fea2646970667358221220158e838124016e3e2b38988de977bc64e257f5f39460a26e461104d0e815677d64736f6c634300081a0033 + gas 300000 + build + +# Create block to hold txTestMCopy transaction +block_build b01 + parent g00 + transactions txTestMCopy + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Check txTestMCopy succeeded +assert_tx_success txTestMCopy + +# Create transaction to execute checkMCopy() method +transaction_build txTestMCopyOKCall + sender acc1 + nonce 1 + contract txTestMCopy + value 0 + data 8c2bcab9 + gas 30000 + build + +# Create block to hold txTestMCopyOKCall transaction +block_build b02 + parent b01 + transactions txTestMCopyOKCall + gasLimit 30000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 + +# Check txTestMCopyOKCall succeeded +assert_tx_success txTestMCopyOKCall \ No newline at end of file diff --git a/rskj-core/src/test/resources/dsl/opcode/mcopy/testOverwriteCases.txt b/rskj-core/src/test/resources/dsl/opcode/mcopy/testOverwriteCases.txt new file mode 100644 index 00000000000..92195d262d4 --- /dev/null +++ b/rskj-core/src/test/resources/dsl/opcode/mcopy/testOverwriteCases.txt @@ -0,0 +1,308 @@ +comment + +// CONTRACT CODE +// + +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +contract TestMCopy { + constructor() {} + + event ZERO_INPUTS_OK(); + event SINGLE_BYTE_REWRITE_OK(); + event FULL_WORD_REWRITE_OK(); + event SINGLE_BYTE_FWD_OVERWRITE_OK(); + event FULL_WORD_FWD_OVERWRITE_OK(); + event MID_WORD_SINGLE_BYTE_REWRITE_OK(); + event MID_WORD_SINGLE_WORD_REWRITE_OK(); + event MID_WORD_MULTY_WORD_REWRITE_OK(); + event TWO_WORDS_FWD_OVERWRITE_OK(); + event TWO_WORDS_BWD_OVERWRITE_OK(); + event TWO_WORDS_BWD_OVERWRITE_SINGLE_BYTE_OFFSET_OK(); + + event ERROR(); + + function runOverwriteCases() external { + if (testZeroInputs()) { + emit ZERO_INPUTS_OK(); + if (testSingleByteRewrite()) { + emit SINGLE_BYTE_REWRITE_OK(); + if (testFullWordRewrite()) { + emit FULL_WORD_REWRITE_OK(); + if (testSingleByteFwdOverwrite()) { + emit SINGLE_BYTE_FWD_OVERWRITE_OK(); + if (testFullWordFwdOverwrite()) { + emit FULL_WORD_FWD_OVERWRITE_OK(); + if (testMidWordSingleByteRewrite()) { + emit MID_WORD_SINGLE_BYTE_REWRITE_OK(); + if (testMidWordSingleWordRewrite()) { + emit MID_WORD_SINGLE_WORD_REWRITE_OK(); + if (testMidWordMultiWordRewrite()) { + emit MID_WORD_MULTY_WORD_REWRITE_OK(); + if (testTwoWordsFwdOverwrite()) { + emit TWO_WORDS_FWD_OVERWRITE_OK(); + if (testTwoWordsBwdOverwrite()) { + emit TWO_WORDS_BWD_OVERWRITE_OK(); + if (testTwoWordsBwdOverwriteSingleByteOffset()) { + emit TWO_WORDS_BWD_OVERWRITE_SINGLE_BYTE_OFFSET_OK(); + return; + } + } + } + } + } + } + } + } + } + } + } + + emit ERROR(); + } + + function testZeroInputs() public pure returns (bool status) { + bytes32 initialValue = 0x0001020304050607080900000000000000000000000000000000000000000000; + bytes32 result; + assembly { + mstore(0, initialValue) // Initialize memory with a word at offset 0 + mcopy(0, 0, 0) // Use MCOPY to copy 0 bytes starting from offset 0 to offset 0 in memory + result := mload(0) // Assigns the word starting at offset 0 to result + } + status = (result == initialValue); + } + + function testSingleByteRewrite() public pure returns (bool status) { + bytes32 initialValue = 0x0001020304050607080900000000000000000000000000000000000000000000; + bytes32 result; + assembly { + mstore(0, initialValue) // Initialize memory with a word at offset 0 + mcopy(0, 0, 1) // Use MCOPY to copy 1 byte starting from offset 0 to offset 0 in memory + result := mload(0) // Assigns the word starting at offset 0 to result + } + status = (result == initialValue); + } + + function testFullWordRewrite() public pure returns (bool status) { + bytes32 initialValue = 0x0001020304050607080900000000000000000000000000000000000000000000; + bytes32 result; + assembly { + mstore(0, initialValue) // Initialize memory with a word at offset 0 + mcopy(0, 0, 32) // Use MCOPY to copy 32 bytes starting from offset 0 to offset 0 in memory + result := mload(0) // Assigns the word starting at offset 0 to result + } + status = (result == initialValue); + } + + function testSingleByteFwdOverwrite() public pure returns (bool status) { + bytes32 initialValue = 0x0001020304050607080900000000000000000000000000000000000000000000; + bytes32 expectedValue = 0x0000020304050607080900000000000000000000000000000000000000000000; + bytes32 result; + assembly { + mstore(0, initialValue) // Initialize memory with a word at offset 0 + mcopy(1, 0, 1) // Use MCOPY to copy 1 byte starting from offset 0 to offset 1 in memory + result := mload(0) // Assigns the word starting at offset 0 to result + } + status = (result == expectedValue); + } + + function testFullWordFwdOverwrite() public pure returns (bool status) { + bytes32 initialValue = 0x0001020304050607080900000000000000000000000000000000000000000000; + bytes32 expectedValue = 0x0000010203040506070809000000000000000000000000000000000000000000; + bytes32 result; + assembly { + mstore(0, initialValue) // Initialize memory with a word at offset 0 + mcopy(1, 0, 32) // Use MCOPY to copy 32 bytes starting from offset 0 to offset 1 in memory + result := mload(0) // Assigns the word starting at offset 0 to result + } + status = (result == expectedValue); + } + + function testMidWordSingleByteRewrite() public pure returns (bool status) { + bytes32 initialValue = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; + bytes32 expectedValue = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; + bytes32 result; + assembly { + mstore(0, initialValue) // Initialize memory with a word at offset 0 + mcopy(17, 17, 1) // Use MCOPY to copy 1 byte starting from offset 17 to offset 17 in memory + result := mload(0) // Assigns the word starting at offset 0 to result + } + status = (result == expectedValue); + } + + function testMidWordSingleWordRewrite() public pure returns (bool status) { + bytes32 word1 = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; + bytes32 word2 = 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f; + bytes32 expectedValue = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; + bytes32 result; + assembly { + mstore(0, word1) // Initialize memory with a word at offset 0 + mstore(32, word2) // Add more memory with a word at offset 32 + mcopy(17, 17, 32) // Use MCOPY to copy 32 bytes starting from offset 17 to offset 17 in memory + result := mload(0) // Assigns the word starting at offset 0 to result + } + status = (result == expectedValue); + } + + function testMidWordMultiWordRewrite() public pure returns (bool status) { + bytes32 word1 = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; + bytes32 word2 = 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f; + bytes32 word3 = 0x404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f; + bytes32 result1; + bytes32 result2; + bytes32 result3; + assembly { + mstore(0, word1) // Initialize memory with a word at offset 0 + mstore(32, word2) // Add more memory with a word at offset 32 + mstore(64, word3) // Add more memory with a word at offset 64 + mcopy(17, 17, 64) // Use MCOPY to copy 64 bytes starting from offset 17 to offset 17 in memory + result1 := mload(0) // Assigns the word starting at offset 0 to result1 + result2 := mload(32) // Assigns the word starting at offset 32 to result2 + result3 := mload(64) // Assigns the word starting at offset 64 to result3 + } + status = (result1 == word1 && result2 == word2 && result3 == word3); + } + + function testTwoWordsFwdOverwrite() public pure returns (bool status) { + bytes32 word1 = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; + bytes32 word2 = 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f; + bytes32 word3 = 0x404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f; + bytes32 expectedValue1 = 0x000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f; + bytes32 expectedValue2 = 0x101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f; + bytes32 expectedValue3 = 0x303132333435363738393a3b3c3d3e3f505152535455565758595a5b5c5d5e5f; + bytes32 result1; + bytes32 result2; + bytes32 result3; + assembly { + mstore(0, word1) // Initialize memory with a word at offset 0 + mstore(32, word2) // Add more memory with a word at offset 32 + mstore(64, word3) // Add more memory with a word at offset 64 + mcopy(16, 0, 64) // Use MCOPY to copy 64 bytes starting from offset 0 to offset 16 in memory + result1 := mload(0) // Assigns the word starting at offset 0 to result1 + result2 := mload(32) // Assigns the word starting at offset 32 to result2 + result3 := mload(64) // Assigns the word starting at offset 64 to result3 + } + status = (result1 == expectedValue1 && result2 == expectedValue2 && result3 == expectedValue3); + } + + function testTwoWordsBwdOverwrite() public pure returns (bool status) { + bytes32 word1 = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; + bytes32 word2 = 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f; + bytes32 word3 = 0x404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f; + bytes32 expectedValue1 = 0x000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f; + bytes32 expectedValue2 = 0x101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f; + bytes32 expectedValue3 = 0x303132333435363738393a3b3c3d3e3f505152535455565758595a5b5c5d5e5f; + bytes32 result1; + bytes32 result2; + bytes32 result3; + assembly { + mstore(0, word1) // Initialize memory with a word at offset 0 + mstore(32, word2) // Add more memory with a word at offset 32 + mstore(64, word3) // Add more memory with a word at offset 64 + mcopy(16, 0, 64) // Use MCOPY to copy 64 bytes starting from offset 0 to offset 16 in memory + result1 := mload(0) // Assigns the word starting at offset 0 to result1 + result2 := mload(32) // Assigns the word starting at offset 32 to result2 + result3 := mload(64) // Assigns the word starting at offset 64 to result3 + } + status = (result1 == expectedValue1 && result2 == expectedValue2 && result3 == expectedValue3); + } + + function testTwoWordsBwdOverwriteSingleByteOffset() public pure returns (bool status) { + bytes32 word1 = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; + bytes32 word2 = 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f; + bytes32 word3 = 0x404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f; + bytes32 expectedValue1 = 0x000102030405060708090a0b0c0d0e101112131415161718191a1b1c1d1e1f20; + bytes32 expectedValue2 = 0x2122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40; + bytes32 expectedValue3 = 0x4142434445464748494a4b4c4d4e4f4f505152535455565758595a5b5c5d5e5f; + bytes32 result1; + bytes32 result2; + bytes32 result3; + assembly { + mstore(0, word1) // Initialize memory with a word at offset 0 + mstore(32, word2) // Add more memory with a word at offset 32 + mstore(64, word3) // Add more memory with a word at offset 64 + mcopy(15, 16, 64) // Use MCOPY to copy 64 bytes starting from offset 16 to offset 15 in memory + result1 := mload(0) // Assigns the word starting at offset 0 to result1 + result2 := mload(32) // Assigns the word starting at offset 32 to result2 + result3 := mload(64) // Assigns the word starting at offset 64 to result3 + } + status = (result1 == expectedValue1 && result2 == expectedValue2 && result3 == expectedValue3); + } + +} + +// DESCRIPTION + +This contract contains two types of functions: + +* runAdvancedCases runs the individual tests (failing fast) and: + - Emits an OK event for each test that was successfully executed. + - Emits an ERROR as soon as some test fails. + +* test manage the memory by storing, copying and reading values as needed for the case. + +// CONTRACT BYTECODE + +6080604052348015600e575f80fd5b50610bcf8061001c5f395ff3fe608060405234801561000f575f80fd5b50600436106100b1575f3560e01c8063960958e51161006f578063960958e514610155578063ab6707f514610173578063af9fafca14610191578063c16527b5146101af578063dde848ce146101cd578063e67db72b146101eb576100b1565b80624aa044146100b5578063190fef25146100d35780632b803126146100f15780632bccb6691461010f5780633f2a93c51461012d578063942e95561461014b575b5f80fd5b6100bd610209565b6040516100ca9190610b80565b60405180910390f35b6100db610335565b6040516100e89190610b80565b60405180910390f35b6100f9610462565b6040516101069190610b80565b60405180910390f35b61011761058e565b6040516101249190610b80565b60405180910390f35b6101356105f2565b6040516101429190610b80565b60405180910390f35b6101536106aa565b005b61015d61095b565b60405161016a9190610b80565b60405180910390f35b61017b6109ea565b6040516101889190610b80565b60405180910390f35b610199610a26565b6040516101a69190610b80565b60405180910390f35b6101b7610a89565b6040516101c49190610b80565b60405180910390f35b6101d5610aec565b6040516101e29190610b80565b60405180910390f35b6101f3610b29565b6040516102009190610b80565b60405180910390f35b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f7e0102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f5f1b90505f7f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f5f1b90505f7f303132333435363738393a3b3c3d3e3f505152535455565758595a5b5c5d5e5f5f1b90505f805f885f52876020528660405260405f60105e5f51925060205191506040519050858314801561031c57508482145b801561032757508381145b995050505050505050505090565b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f7e0102030405060708090a0b0c0d0e101112131415161718191a1b1c1d1e1f205f1b90505f7f2122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f405f1b90505f7f4142434445464748494a4b4c4d4e4f4f505152535455565758595a5b5c5d5e5f5f1b90505f805f885f52876020528660405260406010600f5e5f51925060205191506040519050858314801561044957508482145b801561045457508381145b995050505050505050505090565b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f7e0102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f5f1b90505f7f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f5f1b90505f7f303132333435363738393a3b3c3d3e3f505152535455565758595a5b5c5d5e5f5f1b90505f805f885f52876020528660405260405f60105e5f51925060205191506040519050858314801561057557508482145b801561058057508381145b995050505050505050505090565b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f825f5260016011805e5f519050818114935050505090565b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f805f855f52846020528360405260406011805e5f51925060205191506040519050858314801561069457508482145b801561069f57508381145b965050505050505090565b6106b26109ea565b1561092c577f6f4d0d238fff0175b16cb83021c4b659ea083bffe875b6af90747bb8de77808860405160405180910390a16106eb610b29565b1561092b577f73d3f9a883b7d82f7ff3e14f66d5f8c3aa4479c9023a0767a806ef70cc47230360405160405180910390a1610724610aec565b1561092a577fdd983e940817071ed50d8f612cc4b6e3b38422b4f9db00e8afcf97e941b9f58860405160405180910390a161075d610a89565b15610929577f50c03b0095f7485c8af592a8df05acddc51459fc47678cc80643748a5045d8d060405160405180910390a1610796610a26565b15610928577f44c6943968f28dcc072b103b38a00777b96ca694433fa6f1582fd8bef8ee290e60405160405180910390a16107cf61058e565b15610927577ff8501af18168a96bcaf17f9d8ea891cfd963ed0e605cb681ef58b0dad711047d60405160405180910390a161080861095b565b15610926577f4a28baee79ceb4fc5da8fdbb3a482c7700a0f5406ba8e2849741bcd49e88f74160405160405180910390a16108416105f2565b15610925577fa6d79b32f273252d1504fbeea7f68c57633aa4fdc721493f9d435ac9795f28b460405160405180910390a161087a610209565b15610924577fd8c4eae993ca99cd0e2e16294175b57c2e3854b69c736fc9ba638eb9bb5abfe660405160405180910390a16108b3610462565b15610923577fbdd8f7a05848307eab4cdca691861ed1955df075139db8c6ea0a5f7d7631bd8160405160405180910390a16108ec610335565b15610922577f964126d4996bcf352c3ffa136cee5e184ee5353ada375004bc8ea03a44f4c9b660405160405180910390a1610959565b5b5b5b5b5b5b5b5b5b5b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b565b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f835f528260205260206011805e5f51905081811494505050505090565b5f807e010203040506070809000000000000000000000000000000000000000000005f1b90505f815f525f805f5e5f5190508181149250505090565b5f807e010203040506070809000000000000000000000000000000000000000000005f1b90505f7d0102030405060708090000000000000000000000000000000000000000005f1b90505f825f5260205f60015e5f519050818114935050505090565b5f807e010203040506070809000000000000000000000000000000000000000000005f1b90505f7d0203040506070809000000000000000000000000000000000000000000005f1b90505f825f5260015f60015e5f519050818114935050505090565b5f807e010203040506070809000000000000000000000000000000000000000000005f1b90505f815f5260205f805e5f5190508181149250505090565b5f807e010203040506070809000000000000000000000000000000000000000000005f1b90505f815f5260015f805e5f5190508181149250505090565b5f8115159050919050565b610b7a81610b66565b82525050565b5f602082019050610b935f830184610b71565b9291505056fea26469706673582212200e7955b8113336ae846ffeb55d541f54584c0fc5800e2bebf58caea89643135664736f6c634300081a0033 + +// CONTRACT CALL + +- runOverwriteCases() + + 942e9556 + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TestMCopy contract +transaction_build txTestMCopy + sender acc1 + receiverAddress 00 + value 0 + data 6080604052348015600e575f80fd5b50610bcf8061001c5f395ff3fe608060405234801561000f575f80fd5b50600436106100b1575f3560e01c8063960958e51161006f578063960958e514610155578063ab6707f514610173578063af9fafca14610191578063c16527b5146101af578063dde848ce146101cd578063e67db72b146101eb576100b1565b80624aa044146100b5578063190fef25146100d35780632b803126146100f15780632bccb6691461010f5780633f2a93c51461012d578063942e95561461014b575b5f80fd5b6100bd610209565b6040516100ca9190610b80565b60405180910390f35b6100db610335565b6040516100e89190610b80565b60405180910390f35b6100f9610462565b6040516101069190610b80565b60405180910390f35b61011761058e565b6040516101249190610b80565b60405180910390f35b6101356105f2565b6040516101429190610b80565b60405180910390f35b6101536106aa565b005b61015d61095b565b60405161016a9190610b80565b60405180910390f35b61017b6109ea565b6040516101889190610b80565b60405180910390f35b610199610a26565b6040516101a69190610b80565b60405180910390f35b6101b7610a89565b6040516101c49190610b80565b60405180910390f35b6101d5610aec565b6040516101e29190610b80565b60405180910390f35b6101f3610b29565b6040516102009190610b80565b60405180910390f35b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f7e0102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f5f1b90505f7f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f5f1b90505f7f303132333435363738393a3b3c3d3e3f505152535455565758595a5b5c5d5e5f5f1b90505f805f885f52876020528660405260405f60105e5f51925060205191506040519050858314801561031c57508482145b801561032757508381145b995050505050505050505090565b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f7e0102030405060708090a0b0c0d0e101112131415161718191a1b1c1d1e1f205f1b90505f7f2122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f405f1b90505f7f4142434445464748494a4b4c4d4e4f4f505152535455565758595a5b5c5d5e5f5f1b90505f805f885f52876020528660405260406010600f5e5f51925060205191506040519050858314801561044957508482145b801561045457508381145b995050505050505050505090565b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f7e0102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f5f1b90505f7f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f5f1b90505f7f303132333435363738393a3b3c3d3e3f505152535455565758595a5b5c5d5e5f5f1b90505f805f885f52876020528660405260405f60105e5f51925060205191506040519050858314801561057557508482145b801561058057508381145b995050505050505050505090565b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f825f5260016011805e5f519050818114935050505090565b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f805f855f52846020528360405260406011805e5f51925060205191506040519050858314801561069457508482145b801561069f57508381145b965050505050505090565b6106b26109ea565b1561092c577f6f4d0d238fff0175b16cb83021c4b659ea083bffe875b6af90747bb8de77808860405160405180910390a16106eb610b29565b1561092b577f73d3f9a883b7d82f7ff3e14f66d5f8c3aa4479c9023a0767a806ef70cc47230360405160405180910390a1610724610aec565b1561092a577fdd983e940817071ed50d8f612cc4b6e3b38422b4f9db00e8afcf97e941b9f58860405160405180910390a161075d610a89565b15610929577f50c03b0095f7485c8af592a8df05acddc51459fc47678cc80643748a5045d8d060405160405180910390a1610796610a26565b15610928577f44c6943968f28dcc072b103b38a00777b96ca694433fa6f1582fd8bef8ee290e60405160405180910390a16107cf61058e565b15610927577ff8501af18168a96bcaf17f9d8ea891cfd963ed0e605cb681ef58b0dad711047d60405160405180910390a161080861095b565b15610926577f4a28baee79ceb4fc5da8fdbb3a482c7700a0f5406ba8e2849741bcd49e88f74160405160405180910390a16108416105f2565b15610925577fa6d79b32f273252d1504fbeea7f68c57633aa4fdc721493f9d435ac9795f28b460405160405180910390a161087a610209565b15610924577fd8c4eae993ca99cd0e2e16294175b57c2e3854b69c736fc9ba638eb9bb5abfe660405160405180910390a16108b3610462565b15610923577fbdd8f7a05848307eab4cdca691861ed1955df075139db8c6ea0a5f7d7631bd8160405160405180910390a16108ec610335565b15610922577f964126d4996bcf352c3ffa136cee5e184ee5353ada375004bc8ea03a44f4c9b660405160405180910390a1610959565b5b5b5b5b5b5b5b5b5b5b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b565b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f835f528260205260206011805e5f51905081811494505050505090565b5f807e010203040506070809000000000000000000000000000000000000000000005f1b90505f815f525f805f5e5f5190508181149250505090565b5f807e010203040506070809000000000000000000000000000000000000000000005f1b90505f7d0102030405060708090000000000000000000000000000000000000000005f1b90505f825f5260205f60015e5f519050818114935050505090565b5f807e010203040506070809000000000000000000000000000000000000000000005f1b90505f7d0203040506070809000000000000000000000000000000000000000000005f1b90505f825f5260015f60015e5f519050818114935050505090565b5f807e010203040506070809000000000000000000000000000000000000000000005f1b90505f815f5260205f805e5f5190508181149250505090565b5f807e010203040506070809000000000000000000000000000000000000000000005f1b90505f815f5260015f805e5f5190508181149250505090565b5f8115159050919050565b610b7a81610b66565b82525050565b5f602082019050610b935f830184610b71565b9291505056fea26469706673582212200e7955b8113336ae846ffeb55d541f54584c0fc5800e2bebf58caea89643135664736f6c634300081a0033 + gas 750000 + build + +# Create block to hold txTestMCopy transaction +block_build b01 + parent g00 + transactions txTestMCopy + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Check txTestMCopy succeeded +assert_tx_success txTestMCopy + +# Create transaction to execute checkMCopy() method +transaction_build txTestMCopyOKCall + sender acc1 + nonce 1 + contract txTestMCopy + value 0 + data 942e9556 + gas 100000 + build + +# Create block to hold txTestMCopyOKCall transaction +block_build b02 + parent b01 + transactions txTestMCopyOKCall + gasLimit 100000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 + +# Check txTestMCopyOKCall succeeded +assert_tx_success txTestMCopyOKCall \ No newline at end of file diff --git a/rskj-core/src/test/resources/dsl/opcode/mcopy/testRSKIPNotActivatedTest.txt b/rskj-core/src/test/resources/dsl/opcode/mcopy/testRSKIPNotActivatedTest.txt new file mode 100644 index 00000000000..c9af386c3da --- /dev/null +++ b/rskj-core/src/test/resources/dsl/opcode/mcopy/testRSKIPNotActivatedTest.txt @@ -0,0 +1,111 @@ +comment + +// CONTRACT CODE + +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +import "hardhat/console.sol"; + +contract TestMCopy { + constructor() {} + + event OK(); + event ERROR(); + event LOG(bytes32 l); + + function checkMCopy() external { + bytes32 expected = 0x0000000000000000000000000000000000000000000000000000000000000077; + bytes32 result = getCopiedValue(); + + if (result == expected) { + emit OK(); + } else { + emit ERROR(); + } + } + + function getCopiedValue() public pure returns (bytes32 x) { + assembly { + mstore(0x20, 0x77) // Store 0x77 at word 1 in memory + mcopy(0, 0x20, 0x20) // Use MCOPY to copy value at word 1 to word 0 in memory + x := mload(0) // Returns the value at word 0 + } + } + +} + +// DESCRIPTION + +This contract contains two functions: checkMCopy, and getCopiedValue. + +* checkMCopy simply checks the result of the memory copying against an expected value and: + - If returned value matches the expected one, then the OK event is emitted + - ERROR event is emitted otherwise. + +* getCopiedValue manage the memory by storing, copying and reading values as follows: + - First it stores a value to memory on word 1 + - Then uses MCOPY to copy the value on word 1 to word 0 + - Finally it returns the value stored on word 0 + +// CONTRACT BYTECODE + +6080604052348015600e575f80fd5b506101548061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c806389448792146100385780638c2bcab914610056575b5f80fd5b610040610060565b60405161004d9190610105565b60405180910390f35b61005e610072565b005b5f60776020526020805f5e5f51905090565b5f60775f1b90505f610082610060565b90508181036100bc577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16100e9565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b5050565b5f819050919050565b6100ff816100ed565b82525050565b5f6020820190506101185f8301846100f6565b9291505056fea26469706673582212206199acac4caca83f6a604881008d178a60fad43ca326fb08e555757e3a0161da64736f6c634300081a0033 + +// CONTRACT CALL + +- checkMCopy() + + 8c2bcab9 + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TestMCopy contract +transaction_build txTestMCopy + sender acc1 + receiverAddress 00 + value 0 + data 6080604052348015600e575f80fd5b506101548061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c806389448792146100385780638c2bcab914610056575b5f80fd5b610040610060565b60405161004d9190610105565b60405180910390f35b61005e610072565b005b5f60776020526020805f5e5f51905090565b5f60775f1b90505f610082610060565b90508181036100bc577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16100e9565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b5050565b5f819050919050565b6100ff816100ed565b82525050565b5f6020820190506101185f8301846100f6565b9291505056fea26469706673582212206199acac4caca83f6a604881008d178a60fad43ca326fb08e555757e3a0161da64736f6c634300081a0033 + gas 200000 + build + +# Create block to hold txTestMCopy transaction +block_build b01 + parent g00 + transactions txTestMCopy + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Check txTestMCopy succeded +assert_tx_success txTestMCopy + +# Create transaction to execute checkMCopy() method +transaction_build txTestMCopyNotActivated + sender acc1 + nonce 1 + contract txTestMCopy + value 0 + data 8c2bcab9 + gas 30000 + build + +# Create block to hold txTestMCopyNotActivated transaction +block_build b02 + parent b01 + transactions txTestMCopyNotActivated + gasLimit 30000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 \ No newline at end of file diff --git a/rskj-core/src/test/resources/dsl/opcode/mcopy/testSingleByteMemoryExtension.txt b/rskj-core/src/test/resources/dsl/opcode/mcopy/testSingleByteMemoryExtension.txt new file mode 100644 index 00000000000..d0ee3d1b08e --- /dev/null +++ b/rskj-core/src/test/resources/dsl/opcode/mcopy/testSingleByteMemoryExtension.txt @@ -0,0 +1,148 @@ +comment + +// CONTRACT CODE +// + +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +contract TestMCopy { + constructor() {} + + event OK(); + event ERROR(); + + function checkMCopy() external { + if (testSingleByteMemoryExtension()) { + emit OK(); + } else { + emit ERROR(); + } + } + + function testSingleByteMemoryExtension() public pure returns (bool status) { + + bytes32 word1 = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; + bytes32 word2 = 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f; + bytes32 word3 = 0x404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f; + bytes32 word4 = 0x606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f; + bytes32 word5 = 0x808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f; + bytes32 word6 = 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf; + bytes32 word7 = 0xc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf; + bytes32 word8 = 0xe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff; + + bytes32 newWord = 0x0100000000000000000000000000000000000000000000000000000000000000; + + uint memSizeBefore; + uint memSizeAfter; + + assembly { + + mstore(0, word1) + mstore(32, word2) + mstore(64, word3) + mstore(96, word4) + mstore(128, word5) + mstore(160, word6) + mstore(192, word7) + mstore(224, word8) // ... Initialize Memory + + memSizeBefore := msize() // Get memory size before the changes + mcopy(256, 1, 1) // Use MCOPY to copy 1 byte starting from offset 1 to offset 256 in memory + memSizeAfter := msize() // Get memory size after the changes + + status := eq(mload(0), word1) + status := and(status, eq(mload(32), word2)) + status := and(status, eq(mload(64), word3)) + status := and(status, eq(mload(96), word4)) + status := and(status, eq(mload(128), word5)) + status := and(status, eq(mload(160), word6)) + status := and(status, eq(mload(192), word7)) + status := and(status, eq(mload(224), word8)) // ... Old memory stays as it was + + status := and(status, eq(mload(256), newWord)) // Last (and new) word will contain the copied byte and the rest will be all 0s + + } + + status = status && memSizeBefore == 256 && memSizeAfter == 288; // New memory size is the initial size plus 32 additional bytes + + } + +} + +// DESCRIPTION + +This contract contains two types of functions: + +* checkMCopy runs the tes function and based on its return value: + - Emits an OK event if it returned true. + - Emits an ERROR otherwise. + +* testSingleByteMemoryExtension initialize, modifies and checks memory and then: + - Returns true if all the checks passed. + - Returns false otherwise. + +// CONTRACT BYTECODE + +6080604052348015600e575f80fd5b506103348061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80638c2bcab914610038578063b8e40c3114610042575b5f80fd5b610040610060565b005b61004a6100cd565b60405161005791906102e5565b60405180910390f35b6100686100cd565b1561009e577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16100cb565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b565b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f7f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f5f1b90505f7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f5f1b90505f7fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf5f1b90505f7fc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf5f1b90505f7fe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff5f1b90505f7f01000000000000000000000000000000000000000000000000000000000000005f1b90505f808a5f52896020528860405287606052866080528560a0528460c0528360e0525991506001806101005e5990508a5f51149b5089602051148c169b5088604051148c169b5087606051148c169b5086608051148c169b508560a051148c169b508460c051148c169b508360e051148c169b508261010051148c169b508b80156102ae575061010082145b80156102bb575061012081145b9b50505050505050505050505090565b5f8115159050919050565b6102df816102cb565b82525050565b5f6020820190506102f85f8301846102d6565b9291505056fea2646970667358221220a1f31ba0c8bc3a6d1fa5855df4a6cebad6d0d3907398139c9a3ae656ef82ea9664736f6c634300081a0033 + +// CONTRACT CALL + +- checkMCopy() + + 8c2bcab9 + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TestMCopy contract +transaction_build txTestMCopy + sender acc1 + receiverAddress 00 + value 0 + data 6080604052348015600e575f80fd5b506103348061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80638c2bcab914610038578063b8e40c3114610042575b5f80fd5b610040610060565b005b61004a6100cd565b60405161005791906102e5565b60405180910390f35b6100686100cd565b1561009e577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16100cb565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b565b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f7f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f5f1b90505f7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f5f1b90505f7fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf5f1b90505f7fc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf5f1b90505f7fe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff5f1b90505f7f01000000000000000000000000000000000000000000000000000000000000005f1b90505f808a5f52896020528860405287606052866080528560a0528460c0528360e0525991506001806101005e5990508a5f51149b5089602051148c169b5088604051148c169b5087606051148c169b5086608051148c169b508560a051148c169b508460c051148c169b508360e051148c169b508261010051148c169b508b80156102ae575061010082145b80156102bb575061012081145b9b50505050505050505050505090565b5f8115159050919050565b6102df816102cb565b82525050565b5f6020820190506102f85f8301846102d6565b9291505056fea2646970667358221220a1f31ba0c8bc3a6d1fa5855df4a6cebad6d0d3907398139c9a3ae656ef82ea9664736f6c634300081a0033 + gas 300000 + build + +# Create block to hold txTestMCopy transaction +block_build b01 + parent g00 + transactions txTestMCopy + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Check txTestMCopy succeeded +assert_tx_success txTestMCopy + +# Create transaction to execute checkMCopy() method +transaction_build txTestMCopyOKCall + sender acc1 + nonce 1 + contract txTestMCopy + value 0 + data 8c2bcab9 + gas 30000 + build + +# Create block to hold txTestMCopyOKCall transaction +block_build b02 + parent b01 + transactions txTestMCopyOKCall + gasLimit 30000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 + +# Check txTestMCopyOKCall succeeded +assert_tx_success txTestMCopyOKCall \ No newline at end of file diff --git a/rskj-core/src/test/resources/dsl/opcode/mcopy/testSingleWordMemoryExtension.txt b/rskj-core/src/test/resources/dsl/opcode/mcopy/testSingleWordMemoryExtension.txt new file mode 100644 index 00000000000..3793d842680 --- /dev/null +++ b/rskj-core/src/test/resources/dsl/opcode/mcopy/testSingleWordMemoryExtension.txt @@ -0,0 +1,148 @@ +comment + +// CONTRACT CODE +// + +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +contract TestMCopy { + constructor() {} + + event OK(); + event ERROR(); + + function checkMCopy() external { + if (testSingleWordMemoryExtension()) { + emit OK(); + } else { + emit ERROR(); + } + } + + function testSingleWordMemoryExtension() public pure returns (bool status) { + + bytes32 word1 = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; + bytes32 word2 = 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f; + bytes32 word3 = 0x404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f; + bytes32 word4 = 0x606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f; + bytes32 word5 = 0x808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f; + bytes32 word6 = 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf; + bytes32 word7 = 0xc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf; + bytes32 word8 = 0xe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff; + + bytes32 newWord = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20; + + uint memSizeBefore; + uint memSizeAfter; + + assembly { + + mstore(0, word1) + mstore(32, word2) + mstore(64, word3) + mstore(96, word4) + mstore(128, word5) + mstore(160, word6) + mstore(192, word7) + mstore(224, word8) // ... Initialize Memory + + memSizeBefore := msize() // Get memory size before the changes + mcopy(256, 1, 32) // Use MCOPY to copy 32 bytes starting from offset 1 to offset 256 in memory + memSizeAfter := msize() // Get memory size after the changes + + status := eq(mload(0), word1) + status := and(status, eq(mload(32), word2)) + status := and(status, eq(mload(64), word3)) + status := and(status, eq(mload(96), word4)) + status := and(status, eq(mload(128), word5)) + status := and(status, eq(mload(160), word6)) + status := and(status, eq(mload(192), word7)) + status := and(status, eq(mload(224), word8)) // ... Old memory stays as it was + + status := and(status, eq(mload(256), newWord)) // Last (and new) word will contain the last 31 bytes from the first word, plus the first byte of the second word + + } + + status = status && memSizeBefore == 256 && memSizeAfter == 288; // New memory size is the initial size plus 32 additional bytes + + } + +} + +// DESCRIPTION + +This contract contains two types of functions: + +* checkMCopy runs the tes function and based on its return value: + - Emits an OK event if it returned true. + - Emits an ERROR otherwise. + +* testSingleWordMemoryExtension initialize, modifies and checks memory and then: + - Returns true if all the checks passed. + - Returns false otherwise. + +// CONTRACT BYTECODE + +6080604052348015600e575f80fd5b506103358061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80638c2bcab914610038578063a42a7a8414610042575b5f80fd5b610040610060565b005b61004a6100cd565b60405161005791906102e6565b60405180910390f35b6100686100cd565b1561009e577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16100cb565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b565b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f7f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f5f1b90505f7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f5f1b90505f7fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf5f1b90505f7fc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf5f1b90505f7fe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff5f1b90505f7f0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f205f1b90505f808a5f52896020528860405287606052866080528560a0528460c0528360e052599150602060016101005e5990508a5f51149b5089602051148c169b5088604051148c169b5087606051148c169b5086608051148c169b508560a051148c169b508460c051148c169b508360e051148c169b508261010051148c169b508b80156102af575061010082145b80156102bc575061012081145b9b50505050505050505050505090565b5f8115159050919050565b6102e0816102cc565b82525050565b5f6020820190506102f95f8301846102d7565b9291505056fea264697066735822122019824cc688e40f146d6e6896e474c3ff854d45ece1b135fba692a677d36354b064736f6c634300081a0033 + +// CONTRACT CALL + +- checkMCopy() + + 8c2bcab9 + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TestMCopy contract +transaction_build txTestMCopy + sender acc1 + receiverAddress 00 + value 0 + data 6080604052348015600e575f80fd5b506103358061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80638c2bcab914610038578063a42a7a8414610042575b5f80fd5b610040610060565b005b61004a6100cd565b60405161005791906102e6565b60405180910390f35b6100686100cd565b1561009e577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16100cb565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b565b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f7f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f5f1b90505f7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f5f1b90505f7fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf5f1b90505f7fc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf5f1b90505f7fe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff5f1b90505f7f0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f205f1b90505f808a5f52896020528860405287606052866080528560a0528460c0528360e052599150602060016101005e5990508a5f51149b5089602051148c169b5088604051148c169b5087606051148c169b5086608051148c169b508560a051148c169b508460c051148c169b508360e051148c169b508261010051148c169b508b80156102af575061010082145b80156102bc575061012081145b9b50505050505050505050505090565b5f8115159050919050565b6102e0816102cc565b82525050565b5f6020820190506102f95f8301846102d7565b9291505056fea264697066735822122019824cc688e40f146d6e6896e474c3ff854d45ece1b135fba692a677d36354b064736f6c634300081a0033 + gas 300000 + build + +# Create block to hold txTestMCopy transaction +block_build b01 + parent g00 + transactions txTestMCopy + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Check txTestMCopy succeeded +assert_tx_success txTestMCopy + +# Create transaction to execute checkMCopy() method +transaction_build txTestMCopyOKCall + sender acc1 + nonce 1 + contract txTestMCopy + value 0 + data 8c2bcab9 + gas 30000 + build + +# Create block to hold txTestMCopyOKCall transaction +block_build b02 + parent b01 + transactions txTestMCopyOKCall + gasLimit 30000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 + +# Check txTestMCopyOKCall succeeded +assert_tx_success txTestMCopyOKCall \ No newline at end of file diff --git a/rskj-core/src/test/resources/dsl/opcode/mcopy/testSingleWordMinusOneByteMemoryExtension.txt b/rskj-core/src/test/resources/dsl/opcode/mcopy/testSingleWordMinusOneByteMemoryExtension.txt new file mode 100644 index 00000000000..88df6dc78e8 --- /dev/null +++ b/rskj-core/src/test/resources/dsl/opcode/mcopy/testSingleWordMinusOneByteMemoryExtension.txt @@ -0,0 +1,148 @@ +comment + +// CONTRACT CODE +// + +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +contract TestMCopy { + constructor() {} + + event OK(); + event ERROR(); + + function checkMCopy() external { + if (testSingleWordMinusOneByteMemoryExtension()) { + emit OK(); + } else { + emit ERROR(); + } + } + + function testSingleWordMinusOneByteMemoryExtension() public pure returns (bool status) { + + bytes32 word1 = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; + bytes32 word2 = 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f; + bytes32 word3 = 0x404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f; + bytes32 word4 = 0x606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f; + bytes32 word5 = 0x808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f; + bytes32 word6 = 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf; + bytes32 word7 = 0xc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf; + bytes32 word8 = 0xe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff; + + bytes32 newWord = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f00; + + uint memSizeBefore; + uint memSizeAfter; + + assembly { + + mstore(0, word1) + mstore(32, word2) + mstore(64, word3) + mstore(96, word4) + mstore(128, word5) + mstore(160, word6) + mstore(192, word7) + mstore(224, word8) // ... Initialize Memory + + memSizeBefore := msize() // Get memory size before the changes + mcopy(256, 1, 31) // Use MCOPY to copy 31 bytes starting from offset 1 to offset 256 in memory + memSizeAfter := msize() // Get memory size after the changes + + status := eq(mload(0), word1) + status := and(status, eq(mload(32), word2)) + status := and(status, eq(mload(64), word3)) + status := and(status, eq(mload(96), word4)) + status := and(status, eq(mload(128), word5)) + status := and(status, eq(mload(160), word6)) + status := and(status, eq(mload(192), word7)) + status := and(status, eq(mload(224), word8)) // ... Old memory stays as it was + + status := and(status, eq(mload(256), newWord)) // Last (and new) word will contain the last 31 bytes from the first word and the last byte will be 00 + + } + + status = status && memSizeBefore == 256 && memSizeAfter == 288; // New memory size is the initial size plus 32 additional bytes + + } + +} + +// DESCRIPTION + +This contract contains two types of functions: + +* checkMCopy runs the tes function and based on its return value: + - Emits an OK event if it returned true. + - Emits an ERROR otherwise. + +* testSingleWordMinusOneByteMemoryExtension initialize, modifies and checks memory and then: + - Returns true if all the checks passed. + - Returns false otherwise. + +// CONTRACT BYTECODE + +6080604052348015600e575f80fd5b506103358061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c806318235dcc146100385780638c2bcab914610056575b5f80fd5b610040610060565b60405161004d91906102e6565b60405180910390f35b61005e61025f565b005b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f7f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f5f1b90505f7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f5f1b90505f7fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf5f1b90505f7fc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf5f1b90505f7fe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff5f1b90505f7f0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f005f1b90505f808a5f52896020528860405287606052866080528560a0528460c0528360e052599150601f60016101005e5990508a5f51149b5089602051148c169b5088604051148c169b5087606051148c169b5086608051148c169b508560a051148c169b508460c051148c169b508360e051148c169b508261010051148c169b508b8015610242575061010082145b801561024f575061012081145b9b50505050505050505050505090565b610267610060565b1561029d577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16102ca565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b565b5f8115159050919050565b6102e0816102cc565b82525050565b5f6020820190506102f95f8301846102d7565b9291505056fea264697066735822122047a49a13689ca3e813987e614c0df5b097f5fb0befb034e7d9e64750e196694864736f6c634300081a0033 + +// CONTRACT CALL + +- checkMCopy() + + 8c2bcab9 + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TestMCopy contract +transaction_build txTestMCopy + sender acc1 + receiverAddress 00 + value 0 + data 6080604052348015600e575f80fd5b506103358061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c806318235dcc146100385780638c2bcab914610056575b5f80fd5b610040610060565b60405161004d91906102e6565b60405180910390f35b61005e61025f565b005b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f7f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f5f1b90505f7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f5f1b90505f7fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf5f1b90505f7fc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf5f1b90505f7fe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff5f1b90505f7f0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f005f1b90505f808a5f52896020528860405287606052866080528560a0528460c0528360e052599150601f60016101005e5990508a5f51149b5089602051148c169b5088604051148c169b5087606051148c169b5086608051148c169b508560a051148c169b508460c051148c169b508360e051148c169b508261010051148c169b508b8015610242575061010082145b801561024f575061012081145b9b50505050505050505050505090565b610267610060565b1561029d577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16102ca565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b565b5f8115159050919050565b6102e0816102cc565b82525050565b5f6020820190506102f95f8301846102d7565b9291505056fea264697066735822122047a49a13689ca3e813987e614c0df5b097f5fb0befb034e7d9e64750e196694864736f6c634300081a0033 + gas 300000 + build + +# Create block to hold txTestMCopy transaction +block_build b01 + parent g00 + transactions txTestMCopy + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Check txTestMCopy succeeded +assert_tx_success txTestMCopy + +# Create transaction to execute checkMCopy() method +transaction_build txTestMCopyOKCall + sender acc1 + nonce 1 + contract txTestMCopy + value 0 + data 8c2bcab9 + gas 30000 + build + +# Create block to hold txTestMCopyOKCall transaction +block_build b02 + parent b01 + transactions txTestMCopyOKCall + gasLimit 30000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 + +# Check txTestMCopyOKCall succeeded +assert_tx_success txTestMCopyOKCall \ No newline at end of file diff --git a/rskj-core/src/test/resources/dsl/opcode/mcopy/testSingleWordPlusOneByteMemoryExtension.txt b/rskj-core/src/test/resources/dsl/opcode/mcopy/testSingleWordPlusOneByteMemoryExtension.txt new file mode 100644 index 00000000000..1d2efc7d24a --- /dev/null +++ b/rskj-core/src/test/resources/dsl/opcode/mcopy/testSingleWordPlusOneByteMemoryExtension.txt @@ -0,0 +1,151 @@ +comment + +// CONTRACT CODE +// + +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +contract TestMCopy { + constructor() {} + + event OK(); + event ERROR(); + + function checkMCopy() external { + if (testSingleWordPlusOneByteMemoryExtension()) { + emit OK(); + } else { + emit ERROR(); + } + } + + function testSingleWordPlusOneByteMemoryExtension() public pure returns (bool status) { + + bytes32 word1 = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; + bytes32 word2 = 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f; + bytes32 word3 = 0x404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f; + bytes32 word4 = 0x606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f; + bytes32 word5 = 0x808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f; + bytes32 word6 = 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf; + bytes32 word7 = 0xc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf; + bytes32 word8 = 0xe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff; + + bytes32 secondLastValue = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20; + bytes32 lastValue = 0x2100000000000000000000000000000000000000000000000000000000000000; + + uint memSizeBefore; + uint memSizeAfter; + + assembly { + + mstore(0, word1) + mstore(32, word2) + mstore(64, word3) + mstore(96, word4) + mstore(128, word5) + mstore(160, word6) + mstore(192, word7) + mstore(224, word8) // ... Initialize Memory + + memSizeBefore := msize() // Get memory size before the changes + mcopy(256, 1, 33) // Use MCOPY to copy 33 bytes starting from offset 1 to offset 256 in memory + memSizeAfter := msize() // Get memory size after the changes + + status := eq(mload(0), word1) + status := and(status, eq(mload(32), word2)) + status := and(status, eq(mload(64), word3)) + status := and(status, eq(mload(96), word4)) + status := and(status, eq(mload(128), word5)) + status := and(status, eq(mload(160), word6)) + status := and(status, eq(mload(192), word7)) + status := and(status, eq(mload(224), word8)) // ... Old memory stays as it was + + status := and(status, eq(mload(256), secondLastValue)) // Second-last word will contain the last 31 bytes from the first word, plus the first byte of the second word + + status := and(status, eq(mload(288), lastValue)) // Last word will contain the second byte from the second word, and the rest will be all 0s + + } + + status = status && memSizeBefore == 256 && memSizeAfter == 320; // New memory size is the initial size plus 64 additional bytes + + } + +} + +// DESCRIPTION + +This contract contains two types of functions: + +* checkMCopy runs the tes function and based on its return value: + - Emits an OK event if it returned true. + - Emits an ERROR otherwise. + +* testSingleWordPlusOneByteMemoryExtension initialize, modifies and checks memory and then: + - Returns true if all the checks passed. + - Returns false otherwise. + +// CONTRACT BYTECODE + +6080604052348015600e575f80fd5b506103668061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80631802a516146100385780638c2bcab914610056575b5f80fd5b610040610060565b60405161004d9190610317565b60405180910390f35b61005e610290565b005b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f7f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f5f1b90505f7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f5f1b90505f7fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf5f1b90505f7fc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf5f1b90505f7fe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff5f1b90505f7f0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f205f1b90505f7f21000000000000000000000000000000000000000000000000000000000000005f1b90505f808b5f528a6020528960405288606052876080528660a0528560c0528460e052599150602160016101005e5990508b5f51149c508a602051148d169c5089604051148d169c5088606051148d169c5087608051148d169c508660a051148d169c508560c051148d169c508460e051148d169c508361010051148d169c508261012051148d169c508c8015610272575061010082145b801561027f575061014081145b9c5050505050505050505050505090565b610298610060565b156102ce577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16102fb565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b565b5f8115159050919050565b610311816102fd565b82525050565b5f60208201905061032a5f830184610308565b9291505056fea264697066735822122069f4ef6b0ae12a6ae1a9d7d028c4d25928b8081ff24888c4c714aeeb1937fc1264736f6c634300081a0033 + +// CONTRACT CALL + +- checkMCopy() + + 8c2bcab9 + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TestMCopy contract +transaction_build txTestMCopy + sender acc1 + receiverAddress 00 + value 0 + data 6080604052348015600e575f80fd5b506103668061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80631802a516146100385780638c2bcab914610056575b5f80fd5b610040610060565b60405161004d9190610317565b60405180910390f35b61005e610290565b005b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f5f1b90505f7f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f5f1b90505f7f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f5f1b90505f7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f5f1b90505f7fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf5f1b90505f7fc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf5f1b90505f7fe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff5f1b90505f7f0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f205f1b90505f7f21000000000000000000000000000000000000000000000000000000000000005f1b90505f808b5f528a6020528960405288606052876080528660a0528560c0528460e052599150602160016101005e5990508b5f51149c508a602051148d169c5089604051148d169c5088606051148d169c5087608051148d169c508660a051148d169c508560c051148d169c508460e051148d169c508361010051148d169c508261012051148d169c508c8015610272575061010082145b801561027f575061014081145b9c5050505050505050505050505090565b610298610060565b156102ce577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16102fb565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b565b5f8115159050919050565b610311816102fd565b82525050565b5f60208201905061032a5f830184610308565b9291505056fea264697066735822122069f4ef6b0ae12a6ae1a9d7d028c4d25928b8081ff24888c4c714aeeb1937fc1264736f6c634300081a0033 + gas 300000 + build + +# Create block to hold txTestMCopy transaction +block_build b01 + parent g00 + transactions txTestMCopy + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Check txTestMCopy succeeded +assert_tx_success txTestMCopy + +# Create transaction to execute checkMCopy() method +transaction_build txTestMCopyOKCall + sender acc1 + nonce 1 + contract txTestMCopy + value 0 + data 8c2bcab9 + gas 30000 + build + +# Create block to hold txTestMCopyOKCall transaction +block_build b02 + parent b01 + transactions txTestMCopyOKCall + gasLimit 30000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 + +# Check txTestMCopyOKCall succeeded +assert_tx_success txTestMCopyOKCall \ No newline at end of file diff --git a/rskj-core/src/test/resources/dsl/opcode/mcopy/testZeroLengthOutOfBoundsDestination.txt b/rskj-core/src/test/resources/dsl/opcode/mcopy/testZeroLengthOutOfBoundsDestination.txt new file mode 100644 index 00000000000..096eaef8cb8 --- /dev/null +++ b/rskj-core/src/test/resources/dsl/opcode/mcopy/testZeroLengthOutOfBoundsDestination.txt @@ -0,0 +1,112 @@ +comment + +// CONTRACT CODE +// + +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +contract TestMCopy { + constructor() {} + + event OK(); + event ERROR(); + + function checkMCopy() external { + if (testZeroLengthOutOfBoundsDestination()) { + emit OK(); + } else { + emit ERROR(); + } + } + + function testZeroLengthOutOfBoundsDestination() public pure returns (bool status) { + + bytes32 word1 = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; + uint256 dst = (2 ** 256) - 1; + + assembly { + mstore(0, word1) // ... Initialize Memory + mcopy(dst, 1, 33) // Use MCOPY to copy 0 bytes starting from offset 0 to offset (2^256 - 1) in memory + } + status = true; // This line must not be reached as OOG Exception should occur when trying to execute MCOPY + } + +} + +// DESCRIPTION + +This contract contains two functions: checkMCopy, and testZeroLengthOutOfBoundsDestination. + +* checkMCopy simply checks the result of the memory copying against an expected value and: + - If returned value is true, then the OK event is emitted. + - ERROR event is emitted otherwise. + +* testZeroLengthOutOfBoundsDestination manage the memory by initializing the memory, and then executing MCOPY with the corresponding values as follows: + - First it stores a value to memory on offset 0 + - Then uses MCOPY to copy (2 ** 256) - 1 bytes starting on offset 0 to offset 0 + - Finally it returns true. (This line must never be reached, is added simply to avoid misleading and ugly code). + +Executing checkMCopy must never emit any event as executing the MCOPY instruction with the given parameters will produce an Out Of Gas exception. + +// CONTRACT BYTECODE + +6080604052348015600e575f80fd5b506101928061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80638c2bcab914610038578063cdae7c4114610042575b5f80fd5b610040610060565b005b61004a6100cd565b6040516100579190610143565b60405180910390f35b6100686100cd565b1561009e577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16100cb565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b565b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9050815f5260216001825e60019250505090565b5f8115159050919050565b61013d81610129565b82525050565b5f6020820190506101565f830184610134565b9291505056fea264697066735822122060f880beef67959f26009305aa48e5f6abb8519686cf1974841b6873d619dfb964736f6c634300081a0033 + +// CONTRACT CALL + +- checkMCopy() + + 8c2bcab9 + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TestMCopy contract +transaction_build txTestMCopy + sender acc1 + receiverAddress 00 + value 0 + data 6080604052348015600e575f80fd5b506101928061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80638c2bcab914610038578063cdae7c4114610042575b5f80fd5b610040610060565b005b61004a6100cd565b6040516100579190610143565b60405180910390f35b6100686100cd565b1561009e577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16100cb565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b565b5f807e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9050815f5260216001825e60019250505090565b5f8115159050919050565b61013d81610129565b82525050565b5f6020820190506101565f830184610134565b9291505056fea264697066735822122060f880beef67959f26009305aa48e5f6abb8519686cf1974841b6873d619dfb964736f6c634300081a0033 + gas 200000 + build + +# Create block to hold txTestMCopy transaction +block_build b01 + parent g00 + transactions txTestMCopy + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Check txTestMCopy succeeded +assert_tx_success txTestMCopy + +# Create transaction to execute checkMCopy() method +transaction_build txTestMCopyOKCall + sender acc1 + nonce 1 + contract txTestMCopy + value 0 + data 8c2bcab9 + gas 30000 + build + +# Create block to hold txTestMCopyOKCall transaction +block_build b02 + parent b01 + transactions txTestMCopyOKCall + gasLimit 30000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 \ No newline at end of file