From cf9c661c5466c6fbe006eee090e6c568fe2f94ae Mon Sep 17 00:00:00 2001 From: Nazaret Garcia Date: Fri, 27 Sep 2024 15:01:48 -0300 Subject: [PATCH] Adding basic cases --- .../java/co/rsk/vm/opcode/MCopyDslTest.java | 166 ++++++++++++++++++ ...testCopying32BytesFromOffset0toOffset0.txt | 112 ++++++++++++ .../testCopying8BytesFromOffset0toOffset1.txt | 113 ++++++++++++ .../testCopying8BytesFromOffset1toOffset0.txt | 113 ++++++++++++ 4 files changed, 504 insertions(+) create mode 100644 rskj-core/src/test/resources/dsl/opcode/mcopy/testCopying32BytesFromOffset0toOffset0.txt create mode 100644 rskj-core/src/test/resources/dsl/opcode/mcopy/testCopying8BytesFromOffset0toOffset1.txt create mode 100644 rskj-core/src/test/resources/dsl/opcode/mcopy/testCopying8BytesFromOffset1toOffset0.txt 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 index 5c2b5106d20..8b0b4107f69 100644 --- a/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyDslTest.java +++ b/rskj-core/src/test/java/co/rsk/vm/opcode/MCopyDslTest.java @@ -17,6 +17,8 @@ public class MCopyDslTest { + // Activation Config Tests + @Test void testMCOPY_whenNotActivated_behavesAsExpected() throws FileNotFoundException, DslProcessorException { @@ -74,6 +76,8 @@ void testMCOPY_whenNotActivated_behavesAsExpected() throws FileNotFoundException } + // Basic Usage Cases Tests + @Test void testMCOPY_testCase1_behavesAsExpected() throws FileNotFoundException, DslProcessorException { @@ -128,4 +132,166 @@ void testMCOPY_testCase1_behavesAsExpected() throws FileNotFoundException, DslPr } + @Test + void testMCOPY_testCase2_behavesAsExpected() throws FileNotFoundException, DslProcessorException { + + DslParser parser = DslParser.fromResource("dsl/opcode/mcopy/testCopying32BytesFromOffset0toOffset0.txt"); + World world = new World(); + WorldDslProcessor processor = new WorldDslProcessor(world); + processor.processCommands(parser); + + // Assertions + + // There's one block (b01) containing only 1 transaction + Block block1 = world.getBlockByName("b01"); + Assertions.assertNotNull(block1); + Assertions.assertEquals(1, block1.getTransactionsList().size()); + + // There's a transaction called txTestMCopy + Transaction txTestMCopy = world.getTransactionByName("txTestMCopy"); + Assertions.assertNotNull(txTestMCopy); + + // Transaction txTestMCopy has a transaction receipt + TransactionReceipt txTestMCopyReceipt = world.getTransactionReceiptByName("txTestMCopy"); + Assertions.assertNotNull(txTestMCopyReceipt); + + // Transaction txTestMCopy has been processed correctly + byte[] creationStatus = txTestMCopyReceipt.getStatus(); + Assertions.assertNotNull(creationStatus); + Assertions.assertEquals(1, creationStatus.length); + Assertions.assertEquals(1, creationStatus[0]); + + // There's one block (b02) containing only 1 transaction + Block block2 = world.getBlockByName("b02"); + Assertions.assertNotNull(block2); + Assertions.assertEquals(1, block2.getTransactionsList().size()); + + // There's a transaction called txTestMCopyOKCall + Transaction txTestMCopyOKCall = world.getTransactionByName("txTestMCopyOKCall"); + Assertions.assertNotNull(txTestMCopyOKCall); + + // Transaction txTestMCopyOKCall has a transaction receipt + TransactionReceipt txTestMCopyOKCallReceipt = world.getTransactionReceiptByName("txTestMCopyOKCall"); + Assertions.assertNotNull(txTestMCopyOKCallReceipt); + + // Transaction txTestMCopyOKCall has been processed correctly + byte[] txTestMCopyOKCallCreationStatus = txTestMCopyOKCallReceipt.getStatus(); + Assertions.assertNotNull(txTestMCopyOKCallCreationStatus); + Assertions.assertEquals(1, txTestMCopyOKCallCreationStatus.length); + Assertions.assertEquals(1, txTestMCopyOKCallCreationStatus[0]); + + // Check events + Assertions.assertEquals(1, TransactionReceiptUtil.getEventCount(txTestMCopyOKCallReceipt, "OK", null)); + Assertions.assertEquals(0, TransactionReceiptUtil.getEventCount(txTestMCopyOKCallReceipt, "ERROR", null)); + + } + + @Test + void testMCOPY_testCase3_behavesAsExpected() throws FileNotFoundException, DslProcessorException { + + DslParser parser = DslParser.fromResource("dsl/opcode/mcopy/testCopying8BytesFromOffset1toOffset0.txt"); + World world = new World(); + WorldDslProcessor processor = new WorldDslProcessor(world); + processor.processCommands(parser); + + // Assertions + + // There's one block (b01) containing only 1 transaction + Block block1 = world.getBlockByName("b01"); + Assertions.assertNotNull(block1); + Assertions.assertEquals(1, block1.getTransactionsList().size()); + + // There's a transaction called txTestMCopy + Transaction txTestMCopy = world.getTransactionByName("txTestMCopy"); + Assertions.assertNotNull(txTestMCopy); + + // Transaction txTestMCopy has a transaction receipt + TransactionReceipt txTestMCopyReceipt = world.getTransactionReceiptByName("txTestMCopy"); + Assertions.assertNotNull(txTestMCopyReceipt); + + // Transaction txTestMCopy has been processed correctly + byte[] creationStatus = txTestMCopyReceipt.getStatus(); + Assertions.assertNotNull(creationStatus); + Assertions.assertEquals(1, creationStatus.length); + Assertions.assertEquals(1, creationStatus[0]); + + // There's one block (b02) containing only 1 transaction + Block block2 = world.getBlockByName("b02"); + Assertions.assertNotNull(block2); + Assertions.assertEquals(1, block2.getTransactionsList().size()); + + // There's a transaction called txTestMCopyOKCall + Transaction txTestMCopyOKCall = world.getTransactionByName("txTestMCopyOKCall"); + Assertions.assertNotNull(txTestMCopyOKCall); + + // Transaction txTestMCopyOKCall has a transaction receipt + TransactionReceipt txTestMCopyOKCallReceipt = world.getTransactionReceiptByName("txTestMCopyOKCall"); + Assertions.assertNotNull(txTestMCopyOKCallReceipt); + + // Transaction txTestMCopyOKCall has been processed correctly + byte[] txTestMCopyOKCallCreationStatus = txTestMCopyOKCallReceipt.getStatus(); + Assertions.assertNotNull(txTestMCopyOKCallCreationStatus); + Assertions.assertEquals(1, txTestMCopyOKCallCreationStatus.length); + Assertions.assertEquals(1, txTestMCopyOKCallCreationStatus[0]); + + // Check events + Assertions.assertEquals(1, TransactionReceiptUtil.getEventCount(txTestMCopyOKCallReceipt, "OK", null)); + Assertions.assertEquals(0, TransactionReceiptUtil.getEventCount(txTestMCopyOKCallReceipt, "ERROR", null)); + + } + + @Test + void testMCOPY_testCase4_behavesAsExpected() throws FileNotFoundException, DslProcessorException { + + DslParser parser = DslParser.fromResource("dsl/opcode/mcopy/testCopying8BytesFromOffset0toOffset1.txt"); + World world = new World(); + WorldDslProcessor processor = new WorldDslProcessor(world); + processor.processCommands(parser); + + // Assertions + + // There's one block (b01) containing only 1 transaction + Block block1 = world.getBlockByName("b01"); + Assertions.assertNotNull(block1); + Assertions.assertEquals(1, block1.getTransactionsList().size()); + + // There's a transaction called txTestMCopy + Transaction txTestMCopy = world.getTransactionByName("txTestMCopy"); + Assertions.assertNotNull(txTestMCopy); + + // Transaction txTestMCopy has a transaction receipt + TransactionReceipt txTestMCopyReceipt = world.getTransactionReceiptByName("txTestMCopy"); + Assertions.assertNotNull(txTestMCopyReceipt); + + // Transaction txTestMCopy has been processed correctly + byte[] creationStatus = txTestMCopyReceipt.getStatus(); + Assertions.assertNotNull(creationStatus); + Assertions.assertEquals(1, creationStatus.length); + Assertions.assertEquals(1, creationStatus[0]); + + // There's one block (b02) containing only 1 transaction + Block block2 = world.getBlockByName("b02"); + Assertions.assertNotNull(block2); + Assertions.assertEquals(1, block2.getTransactionsList().size()); + + // There's a transaction called txTestMCopyOKCall + Transaction txTestMCopyOKCall = world.getTransactionByName("txTestMCopyOKCall"); + Assertions.assertNotNull(txTestMCopyOKCall); + + // Transaction txTestMCopyOKCall has a transaction receipt + TransactionReceipt txTestMCopyOKCallReceipt = world.getTransactionReceiptByName("txTestMCopyOKCall"); + Assertions.assertNotNull(txTestMCopyOKCallReceipt); + + // Transaction txTestMCopyOKCall has been processed correctly + byte[] txTestMCopyOKCallCreationStatus = txTestMCopyOKCallReceipt.getStatus(); + Assertions.assertNotNull(txTestMCopyOKCallCreationStatus); + Assertions.assertEquals(1, txTestMCopyOKCallCreationStatus.length); + Assertions.assertEquals(1, txTestMCopyOKCallCreationStatus[0]); + + // Check events + Assertions.assertEquals(1, TransactionReceiptUtil.getEventCount(txTestMCopyOKCallReceipt, "OK", null)); + Assertions.assertEquals(0, TransactionReceiptUtil.getEventCount(txTestMCopyOKCallReceipt, "ERROR", null)); + + } + } 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..21f9112adfe --- /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 1200000 + 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 6500000 + 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..9aadc4e0a46 --- /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 1200000 + 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 6500000 + 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..8b4ed86b53f --- /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 1200000 + 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 6500000 + 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