From f42093b2278b6994ddb854fc6fc1095335c1bbe9 Mon Sep 17 00:00:00 2001 From: frederico leal Date: Sat, 23 Nov 2024 20:11:53 +0100 Subject: [PATCH 1/4] Added tests for dynamic reentrant contexts - Added scenarios for dynamic executions with revert and invalid --- .../vm/opcode/TransientStorageDslTest.java | 90 ++++++++++ ...y_context_revert_or_invalid_undoes_all.txt | 154 ++++++++++++++++++ ...before_revert_or_invalid_has_no_effect.txt | 144 ++++++++++++++++ ...y_context_manipulate_in_reentrant_call.txt | 136 ++++++++++++++++ ...y_context_tload_after_reentrant_tstore.txt | 133 +++++++++++++++ ...cy_context_tstore_after_reentrant_call.txt | 131 +++++++++++++++ 6 files changed, 788 insertions(+) create mode 100644 rskj-core/src/test/resources/dsl/transaction_storage_rskip446/dynamic_reentrancy_context_revert_or_invalid_undoes_all.txt create mode 100644 rskj-core/src/test/resources/dsl/transaction_storage_rskip446/dynamic_reentrancy_context_tstore_before_revert_or_invalid_has_no_effect.txt create mode 100644 rskj-core/src/test/resources/dsl/transaction_storage_rskip446/reentrancy_context_manipulate_in_reentrant_call.txt create mode 100644 rskj-core/src/test/resources/dsl/transaction_storage_rskip446/reentrancy_context_tload_after_reentrant_tstore.txt create mode 100644 rskj-core/src/test/resources/dsl/transaction_storage_rskip446/reentrancy_context_tstore_after_reentrant_call.txt diff --git a/rskj-core/src/test/java/co/rsk/vm/opcode/TransientStorageDslTest.java b/rskj-core/src/test/java/co/rsk/vm/opcode/TransientStorageDslTest.java index c6ad737821..587fef5d36 100644 --- a/rskj-core/src/test/java/co/rsk/vm/opcode/TransientStorageDslTest.java +++ b/rskj-core/src/test/java/co/rsk/vm/opcode/TransientStorageDslTest.java @@ -341,6 +341,96 @@ void testDynamicExecutionStaticCallSubcallCantUseTstore() throws FileNotFoundExc Assertions.assertEquals(2, TransactionReceiptUtil.getEventCount(txReceipt, "OK", null)); } + @Test + void testDynamicReentrancyContextsTstoreBeforeRevertOrInvalidHasNoEffect() throws FileNotFoundException, DslProcessorException { + DslParser parser = DslParser.fromResource("dsl/transaction_storage_rskip446/dynamic_reentrancy_context_tstore_before_revert_or_invalid_has_no_effect.txt"); + World world = new World(); + WorldDslProcessor processor = new WorldDslProcessor(world); + processor.processCommands(parser); + + String txTstorageDynamicReentrancyContextContract = "txTstorageDynamicReentrancyContextContract"; + assertTransactionReceiptWithStatus(world, txTstorageDynamicReentrancyContextContract, "b01", true); + + String txTestReentrantContextRevert = "txTestReentrantContextRevert"; + TransactionReceipt txReceipt = assertTransactionReceiptWithStatus(world, txTestReentrantContextRevert, "b02", true); + Assertions.assertEquals(3, TransactionReceiptUtil.getEventCount(txReceipt, "OK", null)); + + String txTestReentrantContextInvalid = "txTestReentrantContextInvalid"; + assertTransactionReceiptWithStatus(world, txTestReentrantContextInvalid, "b03", false); + } + + @Test + void testDynamicReentrancyContextsRevertOrInvalidUndoesAll() throws FileNotFoundException, DslProcessorException { + DslParser parser = DslParser.fromResource("dsl/transaction_storage_rskip446/dynamic_reentrancy_context_revert_or_invalid_undoes_all.txt"); + World world = new World(); + WorldDslProcessor processor = new WorldDslProcessor(world); + processor.processCommands(parser); + + String txTstorageDynamicReentrancyContextContract = "txTstorageDynamicReentrancyContextContract"; + assertTransactionReceiptWithStatus(world, txTstorageDynamicReentrancyContextContract, "b01", true); + + String txTestReentrantContextRevert = "txTestReentrantContextRevert"; + TransactionReceipt txReceipt = assertTransactionReceiptWithStatus(world, txTestReentrantContextRevert, "b02", true); + Assertions.assertEquals(5, TransactionReceiptUtil.getEventCount(txReceipt, "OK", null)); + + String txTestReentrantContextInvalid = "txTestReentrantContextInvalid"; + assertTransactionReceiptWithStatus(world, txTestReentrantContextInvalid, "b03", false); + } + + @Test + void testReentrancyContextsTstoreAfterReentrantCall() throws FileNotFoundException, DslProcessorException { + DslParser parser = DslParser.fromResource("dsl/transaction_storage_rskip446/reentrancy_context_tstore_after_reentrant_call.txt"); + World world = new World(); + WorldDslProcessor processor = new WorldDslProcessor(world); + processor.processCommands(parser); + + String txTstorageReentrancyContextTestContract = "txTstorageReentrancyContextTestContract"; + assertTransactionReceiptWithStatus(world, txTstorageReentrancyContextTestContract, "b01", true); + + String txTstoreInReentrantCall = "txTstoreInReentrantCall"; + TransactionReceipt txReceipt = assertTransactionReceiptWithStatus(world, txTstoreInReentrantCall, "b02", true); + + String txCheckValuesStoredInTstorage = "txCheckValuesStoredInTstorage"; + txReceipt = assertTransactionReceiptWithStatus(world, txCheckValuesStoredInTstorage, "b03", true); + Assertions.assertEquals(3, TransactionReceiptUtil.getEventCount(txReceipt, "OK", null)); + } + + @Test + void testReentrancyContextsTloadAfterReentrantTstore() throws FileNotFoundException, DslProcessorException { + DslParser parser = DslParser.fromResource("dsl/transaction_storage_rskip446/reentrancy_context_tload_after_reentrant_tstore.txt"); + World world = new World(); + WorldDslProcessor processor = new WorldDslProcessor(world); + processor.processCommands(parser); + + String txTstorageReentrancyContextTestContract = "txTstorageReentrancyContextTestContract"; + assertTransactionReceiptWithStatus(world, txTstorageReentrancyContextTestContract, "b01", true); + + String txTloadAfterReentrantTstore = "txTloadAfterReentrantTstore"; + TransactionReceipt txReceipt = assertTransactionReceiptWithStatus(world, txTloadAfterReentrantTstore, "b02", true); + + String txCheckValuesStoredInTstorage = "txCheckValuesStoredInTstorage"; + txReceipt = assertTransactionReceiptWithStatus(world, txCheckValuesStoredInTstorage, "b03", true); + Assertions.assertEquals(3, TransactionReceiptUtil.getEventCount(txReceipt, "OK", null)); + } + + @Test + void testReentrancyContextsManipulateInReentrantCall() throws FileNotFoundException, DslProcessorException { + DslParser parser = DslParser.fromResource("dsl/transaction_storage_rskip446/reentrancy_context_manipulate_in_reentrant_call.txt"); + World world = new World(); + WorldDslProcessor processor = new WorldDslProcessor(world); + processor.processCommands(parser); + + String txTstorageReentrancyContextTestContract = "txTstorageReentrancyContextTestContract"; + assertTransactionReceiptWithStatus(world, txTstorageReentrancyContextTestContract, "b01", true); + + String txManipulateInReentrantCall = "txManipulateInReentrantCall"; + TransactionReceipt txReceipt = assertTransactionReceiptWithStatus(world, txManipulateInReentrantCall, "b02", true); + + String txCheckValuesStoredInTstorage = "txCheckValuesStoredInTstorage"; + txReceipt = assertTransactionReceiptWithStatus(world, txCheckValuesStoredInTstorage, "b03", true); + Assertions.assertEquals(4, TransactionReceiptUtil.getEventCount(txReceipt, "OK", null)); + } + private static TransactionReceipt assertTransactionReceiptWithStatus(World world, String txName, String blockName, boolean withSuccess) { Transaction txCreation = world.getTransactionByName(txName); assertNotNull(txCreation); diff --git a/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/dynamic_reentrancy_context_revert_or_invalid_undoes_all.txt b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/dynamic_reentrancy_context_revert_or_invalid_undoes_all.txt new file mode 100644 index 0000000000..23f19e6fa2 --- /dev/null +++ b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/dynamic_reentrancy_context_revert_or_invalid_undoes_all.txt @@ -0,0 +1,154 @@ +comment + +// CONTRACT CODE + +pragma solidity ^0.8.24; + +contract TstorageDynamicReentrancyContext { + bool reentrant = false; + + constructor() { + } + + event OK(); + event ERROR(string, uint256); + + function opcodeUndoesAll(uint256 opcodeSelector) external { + bytes memory data = abi.encodeWithSignature("opcodeUndoesAll(uint256)", opcodeSelector); + uint256 fail; + uint256 valueLoadedFromFEBefore; + uint256 valueLoadedFromFFBefore; + uint256 valueLoadedFromFEAfter; + uint256 valueLoadedFromFFAfter; + + assembly { + let reentrantValue := sload(reentrant.slot) + switch reentrantValue + case 0 { + sstore(reentrant.slot, true) + + tstore(0xFE, 0x100) + tstore(0xFF, 0x101) + valueLoadedFromFEBefore := tload(0xFE) + valueLoadedFromFFBefore := tload(0xFF) + + mstore(0, 2) + switch opcodeSelector + case 0 { + fail := call(gas(), address(), 0, add(data, 0x20), mload(data), 0, 0) + } + case 1 { + fail := call(gas(), address(), 0, add(data, 0x20), mload(data), 0, 0) + } + valueLoadedFromFEAfter := tload(0xFE) + valueLoadedFromFFAfter := tload(0xFF) + + } + default { + tstore(0xFE, 0x201) + tstore(0xFE, 0x202) + tstore(0xFF, 0x201) + tstore(0xFF, 0x202) + switch opcodeSelector + case 0 { + revert(0, 0) + } + case 1 { + invalid() + } + } + } + checkReturnValueExpected(fail, 'Checking result callee execution fails', 0); + checkReturnValueExpected(valueLoadedFromFEBefore, 'Checking value from tload FE before', 0x100); + checkReturnValueExpected(valueLoadedFromFEAfter, 'Checking value from tload FE after', 0x100); + checkReturnValueExpected(valueLoadedFromFFBefore, 'Checking value from tload FF before', 0x101); + checkReturnValueExpected(valueLoadedFromFFAfter, 'Checking value from tload FF after', 0x101); + } + + function checkReturnValueExpected(uint256 valueReceived, string memory message, uint256 expectedValue) private { + if( valueReceived == expectedValue){ + emit OK(); + } else { + emit ERROR(message, valueReceived); + } + } +} + +// CONTRACT BYTECODE + +TstorageDynamicReentrancyContext: 60806040525f805f6101000a81548160ff021916908315150217905550348015610027575f80fd5b506104e4806100355f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c80630b35f95c1461002d575b5f80fd5b610047600480360381019061004291906102f3565b610049565b005b5f8160405160240161005b919061032d565b6040516020818303038152906040527f0b35f95c000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f805f805f8054805f81146101225761020160fe5d61020260fe5d61020160ff5d61020260ff5d885f8114610116576001811461011a5761011c565b5f80fd5bfe5b50610183565b60015f5561010060fe5d61010160ff5d60fe5c955060ff5c945060025f52885f8114610155576001811461016857610177565b5f808a5160208c015f305af19750610177565b5f808a5160208c015f305af197505b5060fe5c935060ff5c92505b50506101a885604051806060016040528060268152602001610421602691395f610245565b6101cd8460405180606001604052806023815260200161046960239139610100610245565b6101f28260405180606001604052806022815260200161044760229139610100610245565b6102178360405180606001604052806023815260200161048c60239139610101610245565b61023c816040518060600160405280602281526020016103ff60229139610101610245565b50505050505050565b80830361027d577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16102b7565b7fc9e730d5b570f89e168eb8c3d29f8c396b957e540af248c95c9519ac47c2c69f82846040516102ae9291906103d0565b60405180910390a15b505050565b5f80fd5b5f819050919050565b6102d2816102c0565b81146102dc575f80fd5b50565b5f813590506102ed816102c9565b92915050565b5f60208284031215610308576103076102bc565b5b5f610315848285016102df565b91505092915050565b610327816102c0565b82525050565b5f6020820190506103405f83018461031e565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f5b8381101561037d578082015181840152602081019050610362565b5f8484015250505050565b5f601f19601f8301169050919050565b5f6103a282610346565b6103ac8185610350565b93506103bc818560208601610360565b6103c581610388565b840191505092915050565b5f6040820190508181035f8301526103e88185610398565b90506103f7602083018461031e565b939250505056fe436865636b696e672076616c75652066726f6d20746c6f6164204646206166746572436865636b696e6720726573756c742063616c6c656520657865637574696f6e206661696c73436865636b696e672076616c75652066726f6d20746c6f6164204645206166746572436865636b696e672076616c75652066726f6d20746c6f6164204645206265666f7265436865636b696e672076616c75652066726f6d20746c6f6164204646206265666f7265a264697066735822122014d79ec53d86680fbb087e64cebf51f7c1e829ea7683527c492cf3316530d26964736f6c63430008180033 + +0b35f95c: opcodeUndoesAll(uint256) + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TstorageDynamicReentrancyContext contract +transaction_build txTstorageDynamicReentrancyContextContract + sender acc1 + receiverAddress 00 + value 0 + data 60806040525f805f6101000a81548160ff021916908315150217905550348015610027575f80fd5b506104e4806100355f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c80630b35f95c1461002d575b5f80fd5b610047600480360381019061004291906102f3565b610049565b005b5f8160405160240161005b919061032d565b6040516020818303038152906040527f0b35f95c000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f805f805f8054805f81146101225761020160fe5d61020260fe5d61020160ff5d61020260ff5d885f8114610116576001811461011a5761011c565b5f80fd5bfe5b50610183565b60015f5561010060fe5d61010160ff5d60fe5c955060ff5c945060025f52885f8114610155576001811461016857610177565b5f808a5160208c015f305af19750610177565b5f808a5160208c015f305af197505b5060fe5c935060ff5c92505b50506101a885604051806060016040528060268152602001610421602691395f610245565b6101cd8460405180606001604052806023815260200161046960239139610100610245565b6101f28260405180606001604052806022815260200161044760229139610100610245565b6102178360405180606001604052806023815260200161048c60239139610101610245565b61023c816040518060600160405280602281526020016103ff60229139610101610245565b50505050505050565b80830361027d577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16102b7565b7fc9e730d5b570f89e168eb8c3d29f8c396b957e540af248c95c9519ac47c2c69f82846040516102ae9291906103d0565b60405180910390a15b505050565b5f80fd5b5f819050919050565b6102d2816102c0565b81146102dc575f80fd5b50565b5f813590506102ed816102c9565b92915050565b5f60208284031215610308576103076102bc565b5b5f610315848285016102df565b91505092915050565b610327816102c0565b82525050565b5f6020820190506103405f83018461031e565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f5b8381101561037d578082015181840152602081019050610362565b5f8484015250505050565b5f601f19601f8301169050919050565b5f6103a282610346565b6103ac8185610350565b93506103bc818560208601610360565b6103c581610388565b840191505092915050565b5f6040820190508181035f8301526103e88185610398565b90506103f7602083018461031e565b939250505056fe436865636b696e672076616c75652066726f6d20746c6f6164204646206166746572436865636b696e6720726573756c742063616c6c656520657865637574696f6e206661696c73436865636b696e672076616c75652066726f6d20746c6f6164204645206166746572436865636b696e672076616c75652066726f6d20746c6f6164204645206265666f7265436865636b696e672076616c75652066726f6d20746c6f6164204646206265666f7265a264697066735822122014d79ec53d86680fbb087e64cebf51f7c1e829ea7683527c492cf3316530d26964736f6c63430008180033 + gas 1000000 + build + +# Create block to hold txTstorageDynamicReentrancyContextContract transaction +block_build b01 + parent g00 + transactions txTstorageDynamicReentrancyContextContract + gasLimit 1200000 + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Create transaction to execute txTestReentrantContextRevert transaction +transaction_build txTestReentrantContextRevert + sender acc1 + nonce 1 + contract txTstorageDynamicReentrancyContextContract + value 0 + data 0b35f95c0000000000000000000000000000000000000000000000000000000000000000 + gas 300000 + build + +# Create block to hold txTestReentrantContextRevert transaction +block_build b02 + parent b01 + transactions txTestReentrantContextRevert + gasLimit 350000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 + +# Create transaction to execute txTestReentrantContextInvalid transaction +transaction_build txTestReentrantContextInvalid transaction + sender acc1 + nonce 2 + contract txTstorageDynamicReentrancyContextContract + value 0 + data 0b35f95c0000000000000000000000000000000000000000000000000000000000000001 + gas 1000000 + build + +# Create block to hold txTestReentrantContextInvalid transaction +block_build b03 + parent b02 + transactions txTestReentrantContextInvalid + gasLimit 1200000 + build + +# Connect block +block_connect b03 + +# Check b03 is best block +assert_best b03 \ No newline at end of file diff --git a/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/dynamic_reentrancy_context_tstore_before_revert_or_invalid_has_no_effect.txt b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/dynamic_reentrancy_context_tstore_before_revert_or_invalid_has_no_effect.txt new file mode 100644 index 0000000000..68e2c9996b --- /dev/null +++ b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/dynamic_reentrancy_context_tstore_before_revert_or_invalid_has_no_effect.txt @@ -0,0 +1,144 @@ +comment + +// CONTRACT CODE + +pragma solidity ^0.8.24; + +contract TstorageDynamicReentrancyContext { + bool reentrant = false; + + constructor() { + } + + event OK(); + event ERROR(string, uint256); + + function tstoreBeforeOpcodeHasNoEffect(uint256 opcodeSelector) external { + bytes memory data = abi.encodeWithSignature("tstoreBeforeOpcodeHasNoEffect(uint256)", opcodeSelector); + uint256 fail; + uint256 valueLoadedFromFFBefore; + uint256 valueLoadedFromFFAfter; + + assembly { + let reentrantValue := sload(reentrant.slot) + switch reentrantValue + case 0 { + sstore(reentrant.slot, true) + + tstore(0xFF, 0x100) + valueLoadedFromFFBefore := tload(0xFF) + + mstore(0, 2) + switch opcodeSelector + case 0 { + fail := call(gas(), address(), 0, add(data, 0x20), mload(data), 0, 0) + } + case 1 { + fail := call(0xFFFF, address(), 0, add(data, 0x20), mload(data), 0, 0) + } + valueLoadedFromFFAfter := tload(0xFF) + + } + default { + tstore(0xFF, 0x101) + switch opcodeSelector + case 0 { + revert(0, 0) + } + case 1 { + invalid() + } + } + } + checkReturnValueExpected(fail, 'Checking result callee execution fails', 0); + checkReturnValueExpected(valueLoadedFromFFBefore, 'Checking value from tload FF before', 0x100); + checkReturnValueExpected(valueLoadedFromFFAfter, 'Checking value from tload FF after', 0x100); + } + + function checkReturnValueExpected(uint256 valueReceived, string memory message, uint256 expectedValue) private { + if( valueReceived == expectedValue){ + emit OK(); + } else { + emit ERROR(message, valueReceived); + } + } +} + +// CONTRACT BYTECODE + +TstorageDynamicReentrancyContext: 60806040525f805f6101000a81548160ff021916908315150217905550348015610027575f80fd5b50610431806100355f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c8063902b3cc81461002d575b5f80fd5b61004760048036038101906100429190610285565b610049565b005b5f8160405160240161005b91906102bf565b6040516020818303038152906040527f902b3cc8000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f805f8054805f811461010e5761010160ff5d865f8114610102576001811461010657610108565b5f80fd5bfe5b50610161565b60015f5561010060ff5d60ff5c935060025f52865f811461013657600181146101495761015a565b5f80885160208a015f305af1955061015a565b5f80885160208a015f3061fffff195505b5060ff5c92505b5050610186836040518060600160405280602681526020016103b3602691395f6101d7565b6101ab826040518060600160405280602381526020016103d9602391396101006101d7565b6101d081604051806060016040528060228152602001610391602291396101006101d7565b5050505050565b80830361020f577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a1610249565b7fc9e730d5b570f89e168eb8c3d29f8c396b957e540af248c95c9519ac47c2c69f8284604051610240929190610362565b60405180910390a15b505050565b5f80fd5b5f819050919050565b61026481610252565b811461026e575f80fd5b50565b5f8135905061027f8161025b565b92915050565b5f6020828403121561029a5761029961024e565b5b5f6102a784828501610271565b91505092915050565b6102b981610252565b82525050565b5f6020820190506102d25f8301846102b0565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f5b8381101561030f5780820151818401526020810190506102f4565b5f8484015250505050565b5f601f19601f8301169050919050565b5f610334826102d8565b61033e81856102e2565b935061034e8185602086016102f2565b6103578161031a565b840191505092915050565b5f6040820190508181035f83015261037a818561032a565b905061038960208301846102b0565b939250505056fe436865636b696e672076616c75652066726f6d20746c6f6164204646206166746572436865636b696e6720726573756c742063616c6c656520657865637574696f6e206661696c73436865636b696e672076616c75652066726f6d20746c6f6164204646206265666f7265a26469706673582212209e3cc9ab852f09a8d8d7e1daa468813b380a77f02875c4641d7e0e9942b30c1464736f6c63430008180033 + +902b3cc8: tstoreBeforeOpcodeHasNoEffect(uint256) + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TstorageDynamicReentrancyContext contract +transaction_build txTstorageDynamicReentrancyContextContract + sender acc1 + receiverAddress 00 + value 0 + data 60806040525f805f6101000a81548160ff021916908315150217905550348015610027575f80fd5b50610431806100355f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c8063902b3cc81461002d575b5f80fd5b61004760048036038101906100429190610285565b610049565b005b5f8160405160240161005b91906102bf565b6040516020818303038152906040527f902b3cc8000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f805f8054805f811461010e5761010160ff5d865f8114610102576001811461010657610108565b5f80fd5bfe5b50610161565b60015f5561010060ff5d60ff5c935060025f52865f811461013657600181146101495761015a565b5f80885160208a015f305af1955061015a565b5f80885160208a015f3061fffff195505b5060ff5c92505b5050610186836040518060600160405280602681526020016103b3602691395f6101d7565b6101ab826040518060600160405280602381526020016103d9602391396101006101d7565b6101d081604051806060016040528060228152602001610391602291396101006101d7565b5050505050565b80830361020f577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a1610249565b7fc9e730d5b570f89e168eb8c3d29f8c396b957e540af248c95c9519ac47c2c69f8284604051610240929190610362565b60405180910390a15b505050565b5f80fd5b5f819050919050565b61026481610252565b811461026e575f80fd5b50565b5f8135905061027f8161025b565b92915050565b5f6020828403121561029a5761029961024e565b5b5f6102a784828501610271565b91505092915050565b6102b981610252565b82525050565b5f6020820190506102d25f8301846102b0565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f5b8381101561030f5780820151818401526020810190506102f4565b5f8484015250505050565b5f601f19601f8301169050919050565b5f610334826102d8565b61033e81856102e2565b935061034e8185602086016102f2565b6103578161031a565b840191505092915050565b5f6040820190508181035f83015261037a818561032a565b905061038960208301846102b0565b939250505056fe436865636b696e672076616c75652066726f6d20746c6f6164204646206166746572436865636b696e6720726573756c742063616c6c656520657865637574696f6e206661696c73436865636b696e672076616c75652066726f6d20746c6f6164204646206265666f7265a26469706673582212209e3cc9ab852f09a8d8d7e1daa468813b380a77f02875c4641d7e0e9942b30c1464736f6c63430008180033 + gas 1000000 + build + +# Create block to hold txTstorageDynamicReentrancyContextContract transaction +block_build b01 + parent g00 + transactions txTstorageDynamicReentrancyContextContract + gasLimit 1200000 + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Create transaction to execute txTestReentrantContextRevert transaction +transaction_build txTestReentrantContextRevert + sender acc1 + nonce 1 + contract txTstorageDynamicReentrancyContextContract + value 0 + data 902b3cc80000000000000000000000000000000000000000000000000000000000000000 + gas 300000 + build + +# Create block to hold txTestReentrantContextRevert transaction +block_build b02 + parent b01 + transactions txTestReentrantContextRevert + gasLimit 350000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 + +# Create transaction to execute txTestReentrantContextInvalid transaction +transaction_build txTestReentrantContextInvalid transaction + sender acc1 + nonce 2 + contract txTstorageDynamicReentrancyContextContract + value 0 + data 902b3cc80000000000000000000000000000000000000000000000000000000000000001 + gas 1000000 + build + +# Create block to hold txTestReentrantContextInvalid transaction +block_build b03 + parent b02 + transactions txTestReentrantContextInvalid + gasLimit 1200000 + build + +# Connect block +block_connect b03 + +# Check b03 is best block +assert_best b03 \ No newline at end of file diff --git a/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/reentrancy_context_manipulate_in_reentrant_call.txt b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/reentrancy_context_manipulate_in_reentrant_call.txt new file mode 100644 index 0000000000..8cc9993873 --- /dev/null +++ b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/reentrancy_context_manipulate_in_reentrant_call.txt @@ -0,0 +1,136 @@ +comment + +// CONTRACT CODE + +pragma solidity ^0.8.24; + +contract TstorageReentrancyContextTest { + bool reentrant = false; + uint256 success; + uint256 valueLoadedAfterSubcall; + uint256 valueLoadedBeforeSubcall; + uint256 valueLoadedInSubcall; + + constructor() { + } + + event OK(); + event ERROR(string, uint256); + + function manipulateInReentrantCall() external { + bytes memory data = abi.encodeWithSignature("manipulateInReentrantCall()"); + + assembly { + let reentrantValue := sload(reentrant.slot) + switch reentrantValue + case 0 { + sstore(reentrant.slot, true) + + tstore(0xFF, 0x100) + sstore(valueLoadedBeforeSubcall.slot, tload(0xFF)) + mstore(0, 2) + sstore(success.slot, call(gas(), address(), 0, add(data, 0x20), mload(data), 0, 0)) + sstore(valueLoadedAfterSubcall.slot, tload(0xFF)) + } + default { + tstore(0xFF, 0x101) + sstore(valueLoadedInSubcall.slot, tload(0xFF)) + } + } + } + + function checkValuesStoredInTstorage() external { + checkReturnValueExpected(valueLoadedBeforeSubcall, 'Checking value from tload before subcall', 0x100); + checkReturnValueExpected(success, 'Checking result callee execution is with success', 1); + checkReturnValueExpected(valueLoadedInSubcall, 'Checking value from tload inside subcall', 0x101); + checkReturnValueExpected(valueLoadedAfterSubcall, 'Checking value from tload after subcall ', 0x101); + } + + function checkReturnValueExpected(uint256 valueReceived, string memory message, uint256 expectedValue) private { + if( valueReceived == expectedValue){ + emit OK(); + } else { + emit ERROR(message, valueReceived); + } + } +} + +// CONTRACT BYTECODE + +TstorageReentrancyContextTest: 60806040525f805f6101000a81548160ff021916908315150217905550348015610027575f80fd5b506103de806100355f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80631ff644ef146100385780635f6813d114610042575b5f80fd5b61004061004c565b005b61004a61011c565b005b5f6040516024016040516020818303038152906040527f1ff644ef000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f54805f81146100ed5761010160ff5d60ff5c600455610117565b60015f5561010060ff5d60ff5c60035560025f525f808451602086015f305af160015560ff5c6002555b505050565b610143600354604051806060016040528060288152602001610301602891396101006101b9565b6101696001546040518060600160405280603081526020016103296030913960016101b9565b610190600454604051806060016040528060288152602001610381602891396101016101b9565b6101b7600254604051806060016040528060288152602001610359602891396101016101b9565b565b8083036101f1577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a161022b565b7fc9e730d5b570f89e168eb8c3d29f8c396b957e540af248c95c9519ac47c2c69f82846040516102229291906102d2565b60405180910390a15b505050565b5f81519050919050565b5f82825260208201905092915050565b5f5b8381101561026757808201518184015260208101905061024c565b5f8484015250505050565b5f601f19601f8301169050919050565b5f61028c82610230565b610296818561023a565b93506102a681856020860161024a565b6102af81610272565b840191505092915050565b5f819050919050565b6102cc816102ba565b82525050565b5f6040820190508181035f8301526102ea8185610282565b90506102f960208301846102c3565b939250505056fe436865636b696e672076616c75652066726f6d20746c6f6164206265666f72652073756263616c6c436865636b696e6720726573756c742063616c6c656520657865637574696f6e20697320776974682073756363657373436865636b696e672076616c75652066726f6d20746c6f61642061667465722073756263616c6c20436865636b696e672076616c75652066726f6d20746c6f616420696e736964652073756263616c6ca26469706673582212205eaa2b45243abfc23eb1044a5fe2ff7c133a9d27dc7ee1836eed96d02a8919ea64736f6c63430008180033 + +5f6813d1: checkValuesStoredInTstorage() +1ff644ef: manipulateInReentrantCall() + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TstorageReentrancyContextTest contract +transaction_build txTstorageReentrancyContextTestContract + sender acc1 + receiverAddress 00 + value 0 + data 60806040525f805f6101000a81548160ff021916908315150217905550348015610027575f80fd5b506103de806100355f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80631ff644ef146100385780635f6813d114610042575b5f80fd5b61004061004c565b005b61004a61011c565b005b5f6040516024016040516020818303038152906040527f1ff644ef000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f54805f81146100ed5761010160ff5d60ff5c600455610117565b60015f5561010060ff5d60ff5c60035560025f525f808451602086015f305af160015560ff5c6002555b505050565b610143600354604051806060016040528060288152602001610301602891396101006101b9565b6101696001546040518060600160405280603081526020016103296030913960016101b9565b610190600454604051806060016040528060288152602001610381602891396101016101b9565b6101b7600254604051806060016040528060288152602001610359602891396101016101b9565b565b8083036101f1577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a161022b565b7fc9e730d5b570f89e168eb8c3d29f8c396b957e540af248c95c9519ac47c2c69f82846040516102229291906102d2565b60405180910390a15b505050565b5f81519050919050565b5f82825260208201905092915050565b5f5b8381101561026757808201518184015260208101905061024c565b5f8484015250505050565b5f601f19601f8301169050919050565b5f61028c82610230565b610296818561023a565b93506102a681856020860161024a565b6102af81610272565b840191505092915050565b5f819050919050565b6102cc816102ba565b82525050565b5f6040820190508181035f8301526102ea8185610282565b90506102f960208301846102c3565b939250505056fe436865636b696e672076616c75652066726f6d20746c6f6164206265666f72652073756263616c6c436865636b696e6720726573756c742063616c6c656520657865637574696f6e20697320776974682073756363657373436865636b696e672076616c75652066726f6d20746c6f61642061667465722073756263616c6c20436865636b696e672076616c75652066726f6d20746c6f616420696e736964652073756263616c6ca26469706673582212205eaa2b45243abfc23eb1044a5fe2ff7c133a9d27dc7ee1836eed96d02a8919ea64736f6c63430008180033 + gas 1000000 + build + +# Create block to hold txTstorageReentrancyContextTestContract transaction +block_build b01 + parent g00 + transactions txTstorageReentrancyContextTestContract + gasLimit 1200000 + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Create transaction to execute txManipulateInReentrantCall transaction +transaction_build txManipulateInReentrantCall + sender acc1 + nonce 1 + contract txTstorageReentrancyContextTestContract + value 0 + data 1ff644ef + gas 300000 + build + +# Create block to hold txManipulateInReentrantCall transaction +block_build b02 + parent b01 + transactions txManipulateInReentrantCall + gasLimit 350000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 + +# Create transaction to execute txCheckValuesStoredInTstorage transaction +transaction_build txCheckValuesStoredInTstorage transaction + sender acc1 + nonce 2 + contract txTstorageReentrancyContextTestContract + value 0 + data 5f6813d1 + gas 300000 + build + +# Create block to hold txCheckValuesStoredInTstorage transaction +block_build b03 + parent b02 + transactions txCheckValuesStoredInTstorage + gasLimit 350000 + build + +# Connect block +block_connect b03 + +# Check b03 is best block +assert_best b03 \ No newline at end of file diff --git a/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/reentrancy_context_tload_after_reentrant_tstore.txt b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/reentrancy_context_tload_after_reentrant_tstore.txt new file mode 100644 index 0000000000..e807c54741 --- /dev/null +++ b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/reentrancy_context_tload_after_reentrant_tstore.txt @@ -0,0 +1,133 @@ +comment + +// CONTRACT CODE + +pragma solidity ^0.8.24; + +contract TstorageReentrancyContextTest { + bool reentrant = false; + uint256 success; + uint256 valueLoadedAfterSubcall; + uint256 valueLoadedBeforeSubcall; + + constructor() { + } + + event OK(); + event ERROR(string, uint256); + + function tLoadAfterReentrantTstore() external { + bytes memory data = abi.encodeWithSignature("tLoadAfterReentrantTstore()"); + + assembly { + let reentrantValue := sload(reentrant.slot) + switch reentrantValue + case 0 { + sstore(reentrant.slot, true) + + tstore(0xFF, 0x100) + sstore(valueLoadedBeforeSubcall.slot, tload(0xFF)) + mstore(0, 2) + sstore(success.slot, call(gas(), address(), 0, add(data, 0x20), mload(data), 0, 0)) + sstore(valueLoadedAfterSubcall.slot, tload(0xFF)) + } + default { + tstore(0xFF, 0x101) + } + } + } + + function checkValuesStoredInTstorageCase() external { + checkReturnValueExpected(valueLoadedBeforeSubcall, 'Checking value from tload before subcall', 0x100); + checkReturnValueExpected(success, 'Checking result callee execution is with success', 1); + checkReturnValueExpected(valueLoadedAfterSubcall, 'Checking value from tload after subcall ', 0x101); + } + + function checkReturnValueExpected(uint256 valueReceived, string memory message, uint256 expectedValue) private { + if( valueReceived == expectedValue){ + emit OK(); + } else { + emit ERROR(message, valueReceived); + } + } +} + +// CONTRACT BYTECODE + +TstorageReentrancyContextTest: 60806040525f805f6101000a81548160ff021916908315150217905550348015610027575f80fd5b50610389806100355f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80635ecc5be5146100385780635f6813d114610042575b5f80fd5b61004061004c565b005b61004a610116565b005b5f6040516024016040516020818303038152906040527f5ecc5be5000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f54805f81146100e75761010160ff5d610111565b60015f5561010060ff5d60ff5c60035560025f525f808451602086015f305af160015560ff5c6002555b505050565b61013d6003546040518060600160405280602881526020016102d46028913961010061018c565b6101636001546040518060600160405280603081526020016102fc60309139600161018c565b61018a60025460405180606001604052806028815260200161032c6028913961010161018c565b565b8083036101c4577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16101fe565b7fc9e730d5b570f89e168eb8c3d29f8c396b957e540af248c95c9519ac47c2c69f82846040516101f59291906102a5565b60405180910390a15b505050565b5f81519050919050565b5f82825260208201905092915050565b5f5b8381101561023a57808201518184015260208101905061021f565b5f8484015250505050565b5f601f19601f8301169050919050565b5f61025f82610203565b610269818561020d565b935061027981856020860161021d565b61028281610245565b840191505092915050565b5f819050919050565b61029f8161028d565b82525050565b5f6040820190508181035f8301526102bd8185610255565b90506102cc6020830184610296565b939250505056fe436865636b696e672076616c75652066726f6d20746c6f6164206265666f72652073756263616c6c436865636b696e6720726573756c742063616c6c656520657865637574696f6e20697320776974682073756363657373436865636b696e672076616c75652066726f6d20746c6f61642061667465722073756263616c6c20a264697066735822122051c2d1c8d7ba9ee8ee19a56961f0bb6c1a00450d360d92729994011b727dafac64736f6c63430008180033 + +5f6813d1: checkValuesStoredInTstorage() +5ecc5be5: tLoadAfterReentrantTstore() + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TstorageReentrancyContextTest contract +transaction_build txTstorageReentrancyContextTestContract + sender acc1 + receiverAddress 00 + value 0 + data 60806040525f805f6101000a81548160ff021916908315150217905550348015610027575f80fd5b50610389806100355f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80635ecc5be5146100385780635f6813d114610042575b5f80fd5b61004061004c565b005b61004a610116565b005b5f6040516024016040516020818303038152906040527f5ecc5be5000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f54805f81146100e75761010160ff5d610111565b60015f5561010060ff5d60ff5c60035560025f525f808451602086015f305af160015560ff5c6002555b505050565b61013d6003546040518060600160405280602881526020016102d46028913961010061018c565b6101636001546040518060600160405280603081526020016102fc60309139600161018c565b61018a60025460405180606001604052806028815260200161032c6028913961010161018c565b565b8083036101c4577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16101fe565b7fc9e730d5b570f89e168eb8c3d29f8c396b957e540af248c95c9519ac47c2c69f82846040516101f59291906102a5565b60405180910390a15b505050565b5f81519050919050565b5f82825260208201905092915050565b5f5b8381101561023a57808201518184015260208101905061021f565b5f8484015250505050565b5f601f19601f8301169050919050565b5f61025f82610203565b610269818561020d565b935061027981856020860161021d565b61028281610245565b840191505092915050565b5f819050919050565b61029f8161028d565b82525050565b5f6040820190508181035f8301526102bd8185610255565b90506102cc6020830184610296565b939250505056fe436865636b696e672076616c75652066726f6d20746c6f6164206265666f72652073756263616c6c436865636b696e6720726573756c742063616c6c656520657865637574696f6e20697320776974682073756363657373436865636b696e672076616c75652066726f6d20746c6f61642061667465722073756263616c6c20a264697066735822122051c2d1c8d7ba9ee8ee19a56961f0bb6c1a00450d360d92729994011b727dafac64736f6c63430008180033 + gas 1000000 + build + +# Create block to hold txTstorageReentrancyContextTestContract transaction +block_build b01 + parent g00 + transactions txTstorageReentrancyContextTestContract + gasLimit 1200000 + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Create transaction to execute txTloadAfterReentrantTstorel transaction +transaction_build txTloadAfterReentrantTstore + sender acc1 + nonce 1 + contract txTstorageReentrancyContextTestContract + value 0 + data 5ecc5be5 + gas 300000 + build + +# Create block to hold txTloadAfterReentrantTstore transaction +block_build b02 + parent b01 + transactions txTloadAfterReentrantTstore + gasLimit 350000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 + +# Create transaction to execute txCheckValuesStoredInTstorage transaction +transaction_build txCheckValuesStoredInTstorage transaction + sender acc1 + nonce 2 + contract txTstorageReentrancyContextTestContract + value 0 + data 5f6813d1 + gas 300000 + build + +# Create block to hold txCheckValuesStoredInTstorage transaction +block_build b03 + parent b02 + transactions txCheckValuesStoredInTstorage + gasLimit 350000 + build + +# Connect block +block_connect b03 + +# Check b03 is best block +assert_best b03 \ No newline at end of file diff --git a/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/reentrancy_context_tstore_after_reentrant_call.txt b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/reentrancy_context_tstore_after_reentrant_call.txt new file mode 100644 index 0000000000..9206a0fc71 --- /dev/null +++ b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/reentrancy_context_tstore_after_reentrant_call.txt @@ -0,0 +1,131 @@ +comment + +// CONTRACT CODE +pragma solidity ^0.8.24; + +contract TstorageReentrancyContextTest { + bool reentrant = false; + uint256 success; + uint256 valueLoadedAfterSubcall; + uint256 valueLoadedInsideSubcall; + + constructor() { + } + + event OK(); + event ERROR(string, uint256); + + function tstoreInReentrantCall() external { + bytes memory data = abi.encodeWithSignature("tstoreInReentrantCall()"); + + assembly { + let reentrantValue := sload(reentrant.slot) + switch reentrantValue + case 0 { + sstore(reentrant.slot, true) + + tstore(0, 0x100) + mstore(0, 2) + sstore(success.slot, call(gas(), address(), 0, add(data, 0x20), mload(data), 0, 0)) + sstore(valueLoadedAfterSubcall.slot, tload(0)) + } + default { + sstore(valueLoadedInsideSubcall.slot, tload(0)) + } + } + } + + function checkValuesStoredInTstorage() external { + checkReturnValueExpected(valueLoadedInsideSubcall, 'Checking value from tload inside subcall', 0x100); + checkReturnValueExpected(success, 'Checking result callee execution is with success', 1); + checkReturnValueExpected(valueLoadedAfterSubcall, 'Checking value from tload after subcall ', 0x100); + } + + function checkReturnValueExpected(uint256 valueReceived, string memory message, uint256 expectedValue) private { + if( valueReceived == expectedValue){ + emit OK(); + } else { + emit ERROR(message, valueReceived); + } + } +} + +// CONTRACT BYTECODE + +TstorageReentrancyContextTest: 60806040525f805f6101000a81548160ff021916908315150217905550348015610027575f80fd5b50610380806100355f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c806321b76bb4146100385780635f6813d114610042575b5f80fd5b61004061004c565b005b61004a61010d565b005b5f6040516024016040516020818303038152906040527f21b76bb4000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f54805f81146100e6575f5c600355610108565b60015f556101005f5d60025f525f808451602086015f305af16001555f5c6002555b505050565b61013460035460405180606001604052806028815260200161032360289139610100610183565b61015a6001546040518060600160405280603081526020016102cb603091396001610183565b6101816002546040518060600160405280602881526020016102fb60289139610100610183565b565b8083036101bb577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16101f5565b7fc9e730d5b570f89e168eb8c3d29f8c396b957e540af248c95c9519ac47c2c69f82846040516101ec92919061029c565b60405180910390a15b505050565b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015610231578082015181840152602081019050610216565b5f8484015250505050565b5f601f19601f8301169050919050565b5f610256826101fa565b6102608185610204565b9350610270818560208601610214565b6102798161023c565b840191505092915050565b5f819050919050565b61029681610284565b82525050565b5f6040820190508181035f8301526102b4818561024c565b90506102c3602083018461028d565b939250505056fe436865636b696e6720726573756c742063616c6c656520657865637574696f6e20697320776974682073756363657373436865636b696e672076616c75652066726f6d20746c6f61642061667465722073756263616c6c20436865636b696e672076616c75652066726f6d20746c6f616420696e736964652073756263616c6ca2646970667358221220aa4b50d0af36690596f713b131408b71aefedd3207a7ddee4c16a2924f23811464736f6c63430008180033 + +5f6813d1: checkValuesStoredInTstorage() +21b76bb4: tstoreInReentrantCall() + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TstorageReentrancyContextTest contract +transaction_build txTstorageReentrancyContextTestContract + sender acc1 + receiverAddress 00 + value 0 + data 60806040525f805f6101000a81548160ff021916908315150217905550348015610027575f80fd5b50610380806100355f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c806321b76bb4146100385780635f6813d114610042575b5f80fd5b61004061004c565b005b61004a61010d565b005b5f6040516024016040516020818303038152906040527f21b76bb4000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f54805f81146100e6575f5c600355610108565b60015f556101005f5d60025f525f808451602086015f305af16001555f5c6002555b505050565b61013460035460405180606001604052806028815260200161032360289139610100610183565b61015a6001546040518060600160405280603081526020016102cb603091396001610183565b6101816002546040518060600160405280602881526020016102fb60289139610100610183565b565b8083036101bb577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16101f5565b7fc9e730d5b570f89e168eb8c3d29f8c396b957e540af248c95c9519ac47c2c69f82846040516101ec92919061029c565b60405180910390a15b505050565b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015610231578082015181840152602081019050610216565b5f8484015250505050565b5f601f19601f8301169050919050565b5f610256826101fa565b6102608185610204565b9350610270818560208601610214565b6102798161023c565b840191505092915050565b5f819050919050565b61029681610284565b82525050565b5f6040820190508181035f8301526102b4818561024c565b90506102c3602083018461028d565b939250505056fe436865636b696e6720726573756c742063616c6c656520657865637574696f6e20697320776974682073756363657373436865636b696e672076616c75652066726f6d20746c6f61642061667465722073756263616c6c20436865636b696e672076616c75652066726f6d20746c6f616420696e736964652073756263616c6ca2646970667358221220aa4b50d0af36690596f713b131408b71aefedd3207a7ddee4c16a2924f23811464736f6c63430008180033 + gas 1000000 + build + +# Create block to hold txTstorageReentrancyContextTestContract transaction +block_build b01 + parent g00 + transactions txTstorageReentrancyContextTestContract + gasLimit 1200000 + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Create transaction to execute txTstoreInReentrantCall transaction +transaction_build txTstoreInReentrantCall + sender acc1 + nonce 1 + contract txTstorageReentrancyContextTestContract + value 0 + data 21b76bb4 + gas 300000 + build + +# Create block to hold txTstoreInReentrantCall transaction +block_build b02 + parent b01 + transactions txTstoreInReentrantCall + gasLimit 350000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 + +# Create transaction to execute txCheckValuesStoredInTstorage transaction +transaction_build txCheckValuesStoredInTstorage transaction + sender acc1 + nonce 2 + contract txTstorageReentrancyContextTestContract + value 0 + data 5f6813d1 + gas 300000 + build + +# Create block to hold txCheckValuesStoredInTstorage transaction +block_build b03 + parent b02 + transactions txCheckValuesStoredInTstorage + gasLimit 350000 + build + +# Connect block +block_connect b03 + +# Check b03 is best block +assert_best b03 \ No newline at end of file From 13afb227f40f98a691558495ac66d870d6a4846a Mon Sep 17 00:00:00 2001 From: frederico leal Date: Mon, 25 Nov 2024 21:14:01 +0100 Subject: [PATCH 2/4] Added another scenarios of test with multiple reentrant calls - All the tests from the EIP for reentrancy contexts are covered now --- .../vm/opcode/TransientStorageDslTest.java | 43 ++++ ...undoes_tstorage_after_successfull_call.txt | 200 ++++++++++++++++++ ..._call_then_tload_return_in_static_call.txt | 149 +++++++++++++ 3 files changed, 392 insertions(+) create mode 100644 rskj-core/src/test/resources/dsl/transaction_storage_rskip446/dynamic_reentrancy_context_revert_or_invalid_undoes_tstorage_after_successfull_call.txt create mode 100644 rskj-core/src/test/resources/dsl/transaction_storage_rskip446/reentrancy_context_tstore_in_call_then_tload_return_in_static_call.txt diff --git a/rskj-core/src/test/java/co/rsk/vm/opcode/TransientStorageDslTest.java b/rskj-core/src/test/java/co/rsk/vm/opcode/TransientStorageDslTest.java index 587fef5d36..71a762f555 100644 --- a/rskj-core/src/test/java/co/rsk/vm/opcode/TransientStorageDslTest.java +++ b/rskj-core/src/test/java/co/rsk/vm/opcode/TransientStorageDslTest.java @@ -377,6 +377,31 @@ void testDynamicReentrancyContextsRevertOrInvalidUndoesAll() throws FileNotFound assertTransactionReceiptWithStatus(world, txTestReentrantContextInvalid, "b03", false); } + @Test + void testDynamicReentrancyContextsRevertOrInvalidUndoesTstorageAfterSuccessfullCall() throws FileNotFoundException, DslProcessorException { + DslParser parser = DslParser.fromResource("dsl/transaction_storage_rskip446/dynamic_reentrancy_context_revert_or_invalid_undoes_tstorage_after_successfull_call.txt"); + World world = new World(); + WorldDslProcessor processor = new WorldDslProcessor(world); + processor.processCommands(parser); + + String txTstorageDynamicReentrancyContextContract = "txTstorageDynamicReentrancyContextContract"; + assertTransactionReceiptWithStatus(world, txTstorageDynamicReentrancyContextContract, "b01", true); + + String txTstoreInDoubleReentrantCallWithRevert = "txTstoreInDoubleReentrantCallWithRevert"; + assertTransactionReceiptWithStatus(world, txTstoreInDoubleReentrantCallWithRevert, "b02", true); + + String txCheckValuesStoredInTstorageForRevert = "txCheckValuesStoredInTstorageForRevert"; + TransactionReceipt txReceipt = assertTransactionReceiptWithStatus(world, txCheckValuesStoredInTstorageForRevert, "b03", true); + Assertions.assertEquals(4, TransactionReceiptUtil.getEventCount(txReceipt, "OK", null)); + + String txTstoreInDoubleReentrantCallWithInvalid = "txTstoreInDoubleReentrantCallWithInvalid"; + assertTransactionReceiptWithStatus(world, txTstoreInDoubleReentrantCallWithInvalid, "b04", false); + + String txCheckValuesStoredInTstorageForInvalid = "txCheckValuesStoredInTstorageForInvalid"; + txReceipt = assertTransactionReceiptWithStatus(world, txCheckValuesStoredInTstorageForInvalid, "b05", true); + Assertions.assertEquals(4, TransactionReceiptUtil.getEventCount(txReceipt, "OK", null)); + } + @Test void testReentrancyContextsTstoreAfterReentrantCall() throws FileNotFoundException, DslProcessorException { DslParser parser = DslParser.fromResource("dsl/transaction_storage_rskip446/reentrancy_context_tstore_after_reentrant_call.txt"); @@ -431,6 +456,24 @@ void testReentrancyContextsManipulateInReentrantCall() throws FileNotFoundExcept Assertions.assertEquals(4, TransactionReceiptUtil.getEventCount(txReceipt, "OK", null)); } + @Test + void testReentrancyContextsTstoreInCallThenTloadReturnInStaticCall() throws FileNotFoundException, DslProcessorException { + DslParser parser = DslParser.fromResource("dsl/transaction_storage_rskip446/reentrancy_context_tstore_in_call_then_tload_return_in_static_call.txt"); + World world = new World(); + WorldDslProcessor processor = new WorldDslProcessor(world); + processor.processCommands(parser); + + String txTstorageReentrancyContextTestContract = "txTstorageReentrancyContextTestContract"; + assertTransactionReceiptWithStatus(world, txTstorageReentrancyContextTestContract, "b01", true); + + String txTstorageInReentrantCallTest = "txTstorageInReentrantCallTest"; + TransactionReceipt txReceipt = assertTransactionReceiptWithStatus(world, txTstorageInReentrantCallTest, "b02", true); + + String txCheckValuesStoredInTstorage = "txCheckValuesStoredInTstorage"; + txReceipt = assertTransactionReceiptWithStatus(world, txCheckValuesStoredInTstorage, "b03", true); + Assertions.assertEquals(5, TransactionReceiptUtil.getEventCount(txReceipt, "OK", null)); + } + private static TransactionReceipt assertTransactionReceiptWithStatus(World world, String txName, String blockName, boolean withSuccess) { Transaction txCreation = world.getTransactionByName(txName); assertNotNull(txCreation); diff --git a/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/dynamic_reentrancy_context_revert_or_invalid_undoes_tstorage_after_successfull_call.txt b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/dynamic_reentrancy_context_revert_or_invalid_undoes_tstorage_after_successfull_call.txt new file mode 100644 index 0000000000..75f8a46b6f --- /dev/null +++ b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/dynamic_reentrancy_context_revert_or_invalid_undoes_tstorage_after_successfull_call.txt @@ -0,0 +1,200 @@ +comment + +// CONTRACT CODE + +pragma solidity ^0.8.24; + +contract TstorageDynamicReentrancyContext { + uint256 reentrantCounter = 0; + uint256 valueLoadedFromFFBefore; + uint256 valueLoadedFromFFAfter; + uint256 resultFirstReentrantCall; + uint256 resultSecondReentrantCall; + + constructor() { + } + + event OK(); + event ERROR(string, uint256); + + function opcodeUndoesTstorageAfterSuccessfullCall(uint256 opcodeSelector) external { + bytes memory data = abi.encodeWithSignature("opcodeUndoesTstorageAfterSuccessfullCall(uint256)", opcodeSelector); + + assembly { + let reentrantValue := sload(reentrantCounter.slot) + switch reentrantValue + case 1 { + // +1 to the counter so it goes to the other case from reentrant call + reentrantValue := add(reentrantValue, 1) + sstore(reentrantCounter.slot, reentrantValue) + // Switch to select if failure will be caused by opcode revert or invalid opcode + switch opcodeSelector + case 0 { + sstore(resultSecondReentrantCall.slot, call(gas(), address(), 0, add(data, 0x20), mload(data), 0, 0)) + revert(0, 32) + } + case 1 { + sstore(resultSecondReentrantCall.slot, call(0xFFFF, address(), 0, add(data, 0x20), mload(data), 0, 0)) + invalid() + } + } + case 2 { + tstore(0xFF, 0x101) + } + default { + // +1 to the counter so it goes to the other case from reentrant call + reentrantValue := add(reentrantValue, 1) + sstore(reentrantCounter.slot, reentrantValue) + + // Setup the conditions to be tested + tstore(0xFF, 0x100) + sstore(valueLoadedFromFFBefore.slot, tload(0xFF)) + sstore(resultFirstReentrantCall.slot, call(gas(), address(), 0, add(data, 0x20), mload(data), 0, 0)) // saves result from the call so we can check later + sstore(valueLoadedFromFFAfter.slot, tload(0xFF)) + } + } + } + + function checkValuesStoredInTstorage() external { + checkReturnValueExpected(resultFirstReentrantCall, 'Checking result callee execution resultFirstReentrantCall is failed', 0); + checkReturnValueExpected(resultSecondReentrantCall, 'Checking result callee execution resultSecondReentrantCall is failed', 0); + checkReturnValueExpected(valueLoadedFromFFBefore, 'Checking value from tload FF before', 0x100); + checkReturnValueExpected(valueLoadedFromFFAfter, 'Checking value from tload FF after', 0x100); + } + + function checkReturnValueExpected(uint256 valueReceived, string memory message, uint256 expectedValue) private { + if( valueReceived == expectedValue){ + emit OK(); + } else { + emit ERROR(message, valueReceived); + } + } +} + +// CONTRACT BYTECODE + +TstorageDynamicReentrancyContext: 60806040525f8055348015610012575f80fd5b506104de806100205f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80634abdbf22146100385780635f6813d114610054575b5f80fd5b610052600480360381019061004d91906102d1565b61005e565b005b61005c610189565b005b5f81604051602401610070919061030b565b6040516020818303038152906040527f4abdbf22000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f548060018114610131576002811461017c57600182019150815f5561010060ff5d60ff5c6001555f808451602086015f305af160035560ff5c600255610183565b600182019150815f55835f811461014f576001811461016357610176565b5f808551602087015f305af160045560205ffd5b5f808551602087015f3061fffff1600455fe5b50610183565b61010160ff5d5b50505050565b6101ae6003546040518060800160405280604381526020016103ff604391395f610223565b6101d3600454604051806080016040528060448152602001610442604491395f610223565b6101fa60015460405180606001604052806023815260200161048660239139610100610223565b6102216002546040518060600160405280602281526020016103dd60229139610100610223565b565b80830361025b577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a1610295565b7fc9e730d5b570f89e168eb8c3d29f8c396b957e540af248c95c9519ac47c2c69f828460405161028c9291906103ae565b60405180910390a15b505050565b5f80fd5b5f819050919050565b6102b08161029e565b81146102ba575f80fd5b50565b5f813590506102cb816102a7565b92915050565b5f602082840312156102e6576102e561029a565b5b5f6102f3848285016102bd565b91505092915050565b6103058161029e565b82525050565b5f60208201905061031e5f8301846102fc565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f5b8381101561035b578082015181840152602081019050610340565b5f8484015250505050565b5f601f19601f8301169050919050565b5f61038082610324565b61038a818561032e565b935061039a81856020860161033e565b6103a381610366565b840191505092915050565b5f6040820190508181035f8301526103c68185610376565b90506103d560208301846102fc565b939250505056fe436865636b696e672076616c75652066726f6d20746c6f6164204646206166746572436865636b696e6720726573756c742063616c6c656520657865637574696f6e20726573756c7446697273745265656e7472616e7443616c6c206973206661696c6564436865636b696e6720726573756c742063616c6c656520657865637574696f6e20726573756c745365636f6e645265656e7472616e7443616c6c206973206661696c6564436865636b696e672076616c75652066726f6d20746c6f6164204646206265666f7265a2646970667358221220ddbb9e72364b1b79e927453462ad5f3aa80de1475c599317f83708e1ef5ca17b64736f6c63430008180033 + +Function hashes + +4abdbf22: opcodeUndoesTstorageAfterSuccessfullCall(uint256) +5f6813d1: checkValuesStoredInTstorage() + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TstorageDynamicReentrancyContext contract +transaction_build txTstorageDynamicReentrancyContextContract + sender acc1 + receiverAddress 00 + value 0 + data 60806040525f8055348015610012575f80fd5b506104de806100205f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80634abdbf22146100385780635f6813d114610054575b5f80fd5b610052600480360381019061004d91906102d1565b61005e565b005b61005c610189565b005b5f81604051602401610070919061030b565b6040516020818303038152906040527f4abdbf22000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f548060018114610131576002811461017c57600182019150815f5561010060ff5d60ff5c6001555f808451602086015f305af160035560ff5c600255610183565b600182019150815f55835f811461014f576001811461016357610176565b5f808551602087015f305af160045560205ffd5b5f808551602087015f3061fffff1600455fe5b50610183565b61010160ff5d5b50505050565b6101ae6003546040518060800160405280604381526020016103ff604391395f610223565b6101d3600454604051806080016040528060448152602001610442604491395f610223565b6101fa60015460405180606001604052806023815260200161048660239139610100610223565b6102216002546040518060600160405280602281526020016103dd60229139610100610223565b565b80830361025b577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a1610295565b7fc9e730d5b570f89e168eb8c3d29f8c396b957e540af248c95c9519ac47c2c69f828460405161028c9291906103ae565b60405180910390a15b505050565b5f80fd5b5f819050919050565b6102b08161029e565b81146102ba575f80fd5b50565b5f813590506102cb816102a7565b92915050565b5f602082840312156102e6576102e561029a565b5b5f6102f3848285016102bd565b91505092915050565b6103058161029e565b82525050565b5f60208201905061031e5f8301846102fc565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f5b8381101561035b578082015181840152602081019050610340565b5f8484015250505050565b5f601f19601f8301169050919050565b5f61038082610324565b61038a818561032e565b935061039a81856020860161033e565b6103a381610366565b840191505092915050565b5f6040820190508181035f8301526103c68185610376565b90506103d560208301846102fc565b939250505056fe436865636b696e672076616c75652066726f6d20746c6f6164204646206166746572436865636b696e6720726573756c742063616c6c656520657865637574696f6e20726573756c7446697273745265656e7472616e7443616c6c206973206661696c6564436865636b696e6720726573756c742063616c6c656520657865637574696f6e20726573756c745365636f6e645265656e7472616e7443616c6c206973206661696c6564436865636b696e672076616c75652066726f6d20746c6f6164204646206265666f7265a2646970667358221220ddbb9e72364b1b79e927453462ad5f3aa80de1475c599317f83708e1ef5ca17b64736f6c63430008180033 + gas 1000000 + build + +# Create block to hold txTstorageDynamicReentrancyContextContract transaction +block_build b01 + parent g00 + transactions txTstorageDynamicReentrancyContextContract + gasLimit 1200000 + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Create transaction to execute txTstoreInDoubleReentrantCallWithRevert transaction +transaction_build txTstoreInDoubleReentrantCallWithRevert + sender acc1 + nonce 1 + contract txTstorageDynamicReentrancyContextContract + value 0 + data 4abdbf220000000000000000000000000000000000000000000000000000000000000000 + gas 300000 + build + +# Create block to hold txTstoreInDoubleReentrantCallWithRevert transaction +block_build b02 + parent b01 + transactions txTstoreInDoubleReentrantCallWithRevert + gasLimit 350000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 + +# Create transaction to execute txCheckValuesStoredInTstorageForRevert transaction +transaction_build txCheckValuesStoredInTstorageForRevert transaction + sender acc1 + nonce 2 + contract txTstorageDynamicReentrancyContextContract + value 0 + data 5f6813d1 + gas 300000 + build + +# Create block to hold txCheckValuesStoredInTstorageForRevert transaction +block_build b03 + parent b02 + transactions txCheckValuesStoredInTstorageForRevert + gasLimit 350000 + build + +# Connect block +block_connect b03 + +# Check b03 is best block +assert_best b03 + +# Create transaction to execute txTstoreInDoubleReentrantCallWithInvalid transaction +transaction_build txTstoreInDoubleReentrantCallWithInvalid + sender acc1 + nonce 3 + contract txTstorageDynamicReentrancyContextContract + value 0 + data 4abdbf220000000000000000000000000000000000000000000000000000000000000001 + gas 300000 + build + +# Create block to hold txTstoreInDoubleReentrantCallWithInvalid transaction +block_build b04 + parent b03 + transactions txTstoreInDoubleReentrantCallWithInvalid + gasLimit 350000 + build + +# Connect block +block_connect b04 + +# Check b04 is best block +assert_best b04 + +# Create transaction to execute txCheckValuesStoredInTstorageForInvalid transaction +transaction_build txCheckValuesStoredInTstorageForInvalid transaction + sender acc1 + nonce 4 + contract txTstorageDynamicReentrancyContextContract + value 0 + data 5f6813d1 + gas 300000 + build + +# Create block to hold txCheckValuesStoredInTstorageForInvalid transaction +block_build b05 + parent b04 + transactions txCheckValuesStoredInTstorageForInvalid + gasLimit 350000 + build + +# Connect block +block_connect b05 + +# Check b05 is best block +assert_best b05 \ No newline at end of file diff --git a/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/reentrancy_context_tstore_in_call_then_tload_return_in_static_call.txt b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/reentrancy_context_tstore_in_call_then_tload_return_in_static_call.txt new file mode 100644 index 0000000000..68bbcf8c04 --- /dev/null +++ b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/reentrancy_context_tstore_in_call_then_tload_return_in_static_call.txt @@ -0,0 +1,149 @@ +comment + +// CONTRACT CODE + +pragma solidity ^0.8.24; + +contract TstorageReentrancyContext { + uint256 reentrantCounter = 0; + uint256 valueLoadedFromFFBeforeDefaultCall; + uint256 valueLoadedFromFEAfterCall; + uint256 valueLoadedFromMloadAfterSecondReentrantCall; + uint256 resultFirstReentrantCall; + uint256 resultSecondReentrantStaticCall; + + constructor() { + } + + event OK(); + event ERROR(string, uint256); + + function tstoreInCallThenTloadReturnInStaticCall() external { + bytes memory data = abi.encodeWithSignature("tstoreInCallThenTloadReturnInStaticCall()"); + + assembly { + let reentrantValue := sload(reentrantCounter.slot) + switch reentrantValue + case 1 { + // +1 to the counter so it goes to the other case from reentrant call + reentrantValue := add(reentrantValue, 1) + sstore(reentrantCounter.slot, reentrantValue) + tstore(0xFE, 0x101) + sstore(resultSecondReentrantStaticCall.slot, staticcall(gas(), address(), add(data, 0x20), mload(data), 0, 32)) + sstore(valueLoadedFromMloadAfterSecondReentrantCall.slot, mload(0)) + } + case 2 { + let value := tload(0xFE) + mstore(0, value) + return(0, 32) + } + default { + // +1 to the counter so it goes to the other case from reentrant call + reentrantValue := add(reentrantValue, 1) + sstore(reentrantCounter.slot, reentrantValue) + + // Setup the conditions to be tested + tstore(0xFF, 0x100) + sstore(valueLoadedFromFFBeforeDefaultCall.slot, tload(0xFF)) + sstore(resultFirstReentrantCall.slot, call(gas(), address(), 0, add(data, 0x20), mload(data), 0, 0)) // saves result from the call so we can check later + sstore(valueLoadedFromFEAfterCall.slot, tload(0xFE)) + } + } + } + + function checkValuesStoredInTstorage() external { + checkReturnValueExpected(resultFirstReentrantCall, 'Checking result callee execution resultFirstReentrantCall is success', 1); + checkReturnValueExpected(resultSecondReentrantStaticCall, 'Checking result callee execution resultSecondReentrantCall is success', 1); + checkReturnValueExpected(valueLoadedFromFFBeforeDefaultCall, 'Checking value from tload FF before default call', 0x100); + checkReturnValueExpected(valueLoadedFromMloadAfterSecondReentrantCall, 'Checking value from FE after second reentrant call', 0x101); + checkReturnValueExpected(valueLoadedFromFEAfterCall, 'Checking value from tload FE after defaul call', 0x101); + } + + function checkReturnValueExpected(uint256 valueReceived, string memory message, uint256 expectedValue) private { + if( valueReceived == expectedValue){ + emit OK(); + } else { + emit ERROR(message, valueReceived); + } + } +} + +// CONTRACT BYTECODE + +TstorageReentrancyContext: 60806040525f8055348015610012575f80fd5b506104a5806100205f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c8063586c69f3146100385780635f6813d114610042575b5f80fd5b61004061004c565b005b61004a61014c565b005b5f6040516024016040516020818303038152906040527f586c69f3000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f548060018114610114576002811461013c57600182019150815f5561010060ff5d60ff5c6001555f808451602086015f305af160045560fe5c600255610147565b600182019150815f5561010160fe5d60205f845160208601305afa6005555f51600355610147565b60fe5c805f5260205ff35b505050565b6101726004546040518060800160405280604481526020016103ce60449139600161020f565b61019860055460405180608001604052806045815260200161035760459139600161020f565b6101bf6001546040518060600160405280603081526020016104126030913961010061020f565b6101e660035460405180606001604052806032815260200161039c6032913961010161020f565b61020d6002546040518060600160405280602e8152602001610442602e913961010161020f565b565b808303610247577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a1610281565b7fc9e730d5b570f89e168eb8c3d29f8c396b957e540af248c95c9519ac47c2c69f8284604051610278929190610328565b60405180910390a15b505050565b5f81519050919050565b5f82825260208201905092915050565b5f5b838110156102bd5780820151818401526020810190506102a2565b5f8484015250505050565b5f601f19601f8301169050919050565b5f6102e282610286565b6102ec8185610290565b93506102fc8185602086016102a0565b610305816102c8565b840191505092915050565b5f819050919050565b61032281610310565b82525050565b5f6040820190508181035f83015261034081856102d8565b905061034f6020830184610319565b939250505056fe436865636b696e6720726573756c742063616c6c656520657865637574696f6e20726573756c745365636f6e645265656e7472616e7443616c6c2069732073756363657373436865636b696e672076616c75652066726f6d204645206166746572207365636f6e64207265656e7472616e742063616c6c436865636b696e6720726573756c742063616c6c656520657865637574696f6e20726573756c7446697273745265656e7472616e7443616c6c2069732073756363657373436865636b696e672076616c75652066726f6d20746c6f6164204646206265666f72652064656661756c742063616c6c436865636b696e672076616c75652066726f6d20746c6f61642046452061667465722064656661756c2063616c6ca2646970667358221220fb81ff9160d8fbb79af6b7a0900873b90768b55016a5ae78f0763585a925688064736f6c63430008180033 + +5f6813d1: checkValuesStoredInTstorage() +586c69f3: tstoreInCallThenTloadReturnInStaticCall() + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TstorageReentrancyContextTest contract +transaction_build txTstorageReentrancyContextTestContract + sender acc1 + receiverAddress 00 + value 0 + data 60806040525f8055348015610012575f80fd5b506104a5806100205f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c8063586c69f3146100385780635f6813d114610042575b5f80fd5b61004061004c565b005b61004a61014c565b005b5f6040516024016040516020818303038152906040527f586c69f3000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f548060018114610114576002811461013c57600182019150815f5561010060ff5d60ff5c6001555f808451602086015f305af160045560fe5c600255610147565b600182019150815f5561010160fe5d60205f845160208601305afa6005555f51600355610147565b60fe5c805f5260205ff35b505050565b6101726004546040518060800160405280604481526020016103ce60449139600161020f565b61019860055460405180608001604052806045815260200161035760459139600161020f565b6101bf6001546040518060600160405280603081526020016104126030913961010061020f565b6101e660035460405180606001604052806032815260200161039c6032913961010161020f565b61020d6002546040518060600160405280602e8152602001610442602e913961010161020f565b565b808303610247577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a1610281565b7fc9e730d5b570f89e168eb8c3d29f8c396b957e540af248c95c9519ac47c2c69f8284604051610278929190610328565b60405180910390a15b505050565b5f81519050919050565b5f82825260208201905092915050565b5f5b838110156102bd5780820151818401526020810190506102a2565b5f8484015250505050565b5f601f19601f8301169050919050565b5f6102e282610286565b6102ec8185610290565b93506102fc8185602086016102a0565b610305816102c8565b840191505092915050565b5f819050919050565b61032281610310565b82525050565b5f6040820190508181035f83015261034081856102d8565b905061034f6020830184610319565b939250505056fe436865636b696e6720726573756c742063616c6c656520657865637574696f6e20726573756c745365636f6e645265656e7472616e7443616c6c2069732073756363657373436865636b696e672076616c75652066726f6d204645206166746572207365636f6e64207265656e7472616e742063616c6c436865636b696e6720726573756c742063616c6c656520657865637574696f6e20726573756c7446697273745265656e7472616e7443616c6c2069732073756363657373436865636b696e672076616c75652066726f6d20746c6f6164204646206265666f72652064656661756c742063616c6c436865636b696e672076616c75652066726f6d20746c6f61642046452061667465722064656661756c2063616c6ca2646970667358221220fb81ff9160d8fbb79af6b7a0900873b90768b55016a5ae78f0763585a925688064736f6c63430008180033 + gas 1000000 + build + +# Create block to hold txTstorageReentrancyContextTestContract transaction +block_build b01 + parent g00 + transactions txTstorageReentrancyContextTestContract + gasLimit 1200000 + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Create transaction to execute txTstorageInReentrantCallTest transaction +transaction_build txTstorageInReentrantCallTest + sender acc1 + nonce 1 + contract txTstorageReentrancyContextTestContract + value 0 + data 586c69f3 + gas 300000 + build + +# Create block to hold txTstorageInReentrantCallTest transaction +block_build b02 + parent b01 + transactions txTstorageInReentrantCallTest + gasLimit 350000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 + +# Create transaction to execute txCheckValuesStoredInTstorage transaction +transaction_build txCheckValuesStoredInTstorage transaction + sender acc1 + nonce 2 + contract txTstorageReentrancyContextTestContract + value 0 + data 5f6813d1 + gas 300000 + build + +# Create block to hold txCheckValuesStoredInTstorage transaction +block_build b03 + parent b02 + transactions txCheckValuesStoredInTstorage + gasLimit 350000 + build + +# Connect block +block_connect b03 + +# Check b03 is best block +assert_best b03 \ No newline at end of file From 498cd951bd3ab08563444311de61e9e876d848fe Mon Sep 17 00:00:00 2001 From: frederico leal Date: Tue, 3 Dec 2024 09:02:27 +0100 Subject: [PATCH 3/4] Added gas cost calculation for TLOAD and TSTORE - Added basic scenario of gas measure costs for Transient Storage - Stil needed to add more scenarios of tests for run out of gas --- .../main/java/org/ethereum/vm/GasCost.java | 3 + .../src/main/java/org/ethereum/vm/VM.java | 14 +- .../vm/opcode/TransientStorageDslTest.java | 15 ++ .../tstorage_gas_measure_tests.txt | 130 ++++++++++++++++++ 4 files changed, 158 insertions(+), 4 deletions(-) create mode 100644 rskj-core/src/test/resources/dsl/transaction_storage_rskip446/tstorage_gas_measure_tests.txt diff --git a/rskj-core/src/main/java/org/ethereum/vm/GasCost.java b/rskj-core/src/main/java/org/ethereum/vm/GasCost.java index c9da2ebc09..c9c786841f 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/GasCost.java +++ b/rskj-core/src/main/java/org/ethereum/vm/GasCost.java @@ -61,6 +61,9 @@ public class GasCost { public static final long REFUND_SSTORE = 15000; public static final long CREATE = 32000; + public static final long TLOAD = 100; + public static final long TSTORE = 100; + public static final long JUMPDEST = 1; public static final long CREATE_DATA_BYTE = 5; public static final long CALL = 700; 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 0c4d3f999d..6802028fab 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/VM.java +++ b/rskj-core/src/main/java/org/ethereum/vm/VM.java @@ -1340,8 +1340,11 @@ else if (oldValue != null && newValue.isZero()) { } protected void doTLOAD(){ - //TODO: Gas cost calculation will be done here and also shared contexts verifications for - // different types of calls + if (computeGas) { + gasCost = GasCost.TLOAD; + spendOpCodeGas(); + } + DataWord key = program.stackPop(); if (isLogEnabled) { logger.info("Executing TLOAD with parameters: key = {}", key); @@ -1359,8 +1362,11 @@ protected void doTLOAD(){ } protected void doTSTORE(){ - //TODO: Gas cost calculation will be done here and also shared contexts verifications for - // different types of calls + if (computeGas) { + gasCost = GasCost.TSTORE; + spendOpCodeGas(); + } + if (program.isStaticCall()) { throw Program.ExceptionHelper.modificationException(program); } diff --git a/rskj-core/src/test/java/co/rsk/vm/opcode/TransientStorageDslTest.java b/rskj-core/src/test/java/co/rsk/vm/opcode/TransientStorageDslTest.java index 71a762f555..f4a059ef63 100644 --- a/rskj-core/src/test/java/co/rsk/vm/opcode/TransientStorageDslTest.java +++ b/rskj-core/src/test/java/co/rsk/vm/opcode/TransientStorageDslTest.java @@ -474,6 +474,21 @@ void testReentrancyContextsTstoreInCallThenTloadReturnInStaticCall() throws File Assertions.assertEquals(5, TransactionReceiptUtil.getEventCount(txReceipt, "OK", null)); } + @Test + void testTransientStorageGasMeasureTests() throws FileNotFoundException, DslProcessorException { + DslParser parser = DslParser.fromResource("dsl/transaction_storage_rskip446/tstorage_gas_measure_tests.txt"); + World world = new World(); + WorldDslProcessor processor = new WorldDslProcessor(world); + processor.processCommands(parser); + + String txTstorageGasMeasureTestContract = "txTstorageGasMeasureTestContract"; + assertTransactionReceiptWithStatus(world, txTstorageGasMeasureTestContract, "b01", true); + + String txCheckGasMeasures = "txCheckGasMeasures"; + TransactionReceipt txReceipt = assertTransactionReceiptWithStatus(world, txCheckGasMeasures, "b02", true); + Assertions.assertEquals(4, TransactionReceiptUtil.getEventCount(txReceipt, "OK", null)); + } + private static TransactionReceipt assertTransactionReceiptWithStatus(World world, String txName, String blockName, boolean withSuccess) { Transaction txCreation = world.getTransactionByName(txName); assertNotNull(txCreation); diff --git a/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/tstorage_gas_measure_tests.txt b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/tstorage_gas_measure_tests.txt new file mode 100644 index 0000000000..93afe26062 --- /dev/null +++ b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/tstorage_gas_measure_tests.txt @@ -0,0 +1,130 @@ +comment + +// CONTRACT CODE +pragma solidity ^0.8.24; + +contract TstorageGasMeasureTestCases { + uint public gasUsed; + + constructor() + { + + } + + event OK(); + event ERROR(string, uint256); + + function checkGasMeasures() external { + // Checking tload gas cost + uint gasBefore; + uint gasAfter; + assembly { + gasBefore := gas() + let value := tload(0x10) + gasAfter := gas() + } + gasUsed = gasBefore - gasAfter; + checkReturnValueExpected(gasUsed, 'Checking tload gas spent is the expected', 110); + + // Checking tstore and tload gas cost + assembly { + gasBefore := gas() + tstore(0x10, 0x10) + let value := tload(0x10) + gasAfter := gas() + } + gasUsed = gasBefore - gasAfter; + checkReturnValueExpected(gasUsed, 'Checking tstore and tload gas spent is the expected', 216); + + // Checking tstore in a cold slot + assembly { + gasBefore := gas() + tstore(0xFF, 0x10) + gasAfter := gas() + } + gasUsed = gasBefore - gasAfter; + checkReturnValueExpected(gasUsed, 'Checking tstore cold gas spent is the expected', 113); + + // Checking tstore warm + assembly { + gasBefore := gas() + tstore(0xFF, 0x10) + tstore(0xFF, 0x11) + gasAfter := gas() + } + gasUsed = gasBefore - gasAfter; + checkReturnValueExpected(gasUsed, 'Checking tstore warm gas spent is the expected', 219); + } + + function checkReturnValueExpected(uint256 valueReceived, string memory message, uint256 expectedValue) private { + if( valueReceived == expectedValue){ + emit OK(); + } else { + emit ERROR(message, valueReceived); + } + } +} +// DESCRIPTION + +TstorageGasMeasureTestCases has the following functions: + + + +// CONTRACT BYTECODE + +TstorageGasMeasureTestCases: 6080604052348015600e575f5ffd5b506104098061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c806307e5ab1e14610038578063592cdadd14610042575b5f5ffd5b610040610060565b005b61004a610171565b6040516100579190610205565b60405180910390f35b5f5f5a915060105c5a9150508082610078919061024b565b5f819055506100a25f5460405180606001604052806028815260200161037e60289139606e610176565b5a91506010805d60105c5a91505080826100bc919061024b565b5f819055506100e65f5460405180606001604052806033815260200161034b6033913960d8610176565b5a9150601060ff5d5a905080826100fd919061024b565b5f819055506101275f546040518060600160405280602e81526020016103a6602e91396071610176565b5a9150601060ff5d601160ff5d5a90508082610143919061024b565b5f8190555061016d5f546040518060600160405280602e815260200161031d602e913960db610176565b5050565b5f5481565b8083036101ae577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16101e8565b7fc9e730d5b570f89e168eb8c3d29f8c396b957e540af248c95c9519ac47c2c69f82846040516101df9291906102ee565b60405180910390a15b505050565b5f819050919050565b6101ff816101ed565b82525050565b5f6020820190506102185f8301846101f6565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f610255826101ed565b9150610260836101ed565b92508282039050818111156102785761027761021e565b5b92915050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f6102c08261027e565b6102ca8185610288565b93506102da818560208601610298565b6102e3816102a6565b840191505092915050565b5f6040820190508181035f83015261030681856102b6565b905061031560208301846101f6565b939250505056fe436865636b696e67207473746f7265207761726d20676173207370656e7420697320746865206578706563746564436865636b696e67207473746f726520616e6420746c6f616420676173207370656e7420697320746865206578706563746564436865636b696e6720746c6f616420676173207370656e7420697320746865206578706563746564436865636b696e67207473746f726520636f6c6420676173207370656e7420697320746865206578706563746564a2646970667358221220b17e2b2108ab28708c0fb481438a67aaae5b4c10a6e07af3125fe9d609eb498c64736f6c634300081c0033 + +function hashes: + +- 07e5ab1e: checkGasMeasures() +- 592cdadd: gasUsed() + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TstorageGasMeasureTestCases contract +transaction_build txTstorageGasMeasureTestContract + sender acc1 + receiverAddress 00 + value 0 + data 6080604052348015600e575f5ffd5b506104098061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c806307e5ab1e14610038578063592cdadd14610042575b5f5ffd5b610040610060565b005b61004a610171565b6040516100579190610205565b60405180910390f35b5f5f5a915060105c5a9150508082610078919061024b565b5f819055506100a25f5460405180606001604052806028815260200161037e60289139606e610176565b5a91506010805d60105c5a91505080826100bc919061024b565b5f819055506100e65f5460405180606001604052806033815260200161034b6033913960d8610176565b5a9150601060ff5d5a905080826100fd919061024b565b5f819055506101275f546040518060600160405280602e81526020016103a6602e91396071610176565b5a9150601060ff5d601160ff5d5a90508082610143919061024b565b5f8190555061016d5f546040518060600160405280602e815260200161031d602e913960db610176565b5050565b5f5481565b8083036101ae577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16101e8565b7fc9e730d5b570f89e168eb8c3d29f8c396b957e540af248c95c9519ac47c2c69f82846040516101df9291906102ee565b60405180910390a15b505050565b5f819050919050565b6101ff816101ed565b82525050565b5f6020820190506102185f8301846101f6565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f610255826101ed565b9150610260836101ed565b92508282039050818111156102785761027761021e565b5b92915050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f6102c08261027e565b6102ca8185610288565b93506102da818560208601610298565b6102e3816102a6565b840191505092915050565b5f6040820190508181035f83015261030681856102b6565b905061031560208301846101f6565b939250505056fe436865636b696e67207473746f7265207761726d20676173207370656e7420697320746865206578706563746564436865636b696e67207473746f726520616e6420746c6f616420676173207370656e7420697320746865206578706563746564436865636b696e6720746c6f616420676173207370656e7420697320746865206578706563746564436865636b696e67207473746f726520636f6c6420676173207370656e7420697320746865206578706563746564a2646970667358221220b17e2b2108ab28708c0fb481438a67aaae5b4c10a6e07af3125fe9d609eb498c64736f6c634300081c0033 + gas 1000000 + build + +# Create block to hold txTstorageGasMeasureTestContract transaction +block_build b01 + parent g00 + transactions txTstorageGasMeasureTestContract + gasLimit 1200000 + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Create transaction to execute txCheckGasMeasures function +transaction_build txCheckGasMeasures + sender acc1 + nonce 1 + contract txTstorageGasMeasureTestContract + value 0 + data 07e5ab1e + gas 350000 + build + +# Create block to hold txCheckGasMeasures transaction +block_build b02 + parent b01 + transactions txCheckGasMeasures + gasLimit 400000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 \ No newline at end of file From d86e88b04c93952ea0b77522e0031464047d02e5 Mon Sep 17 00:00:00 2001 From: frederico leal Date: Wed, 4 Dec 2024 01:22:09 +0100 Subject: [PATCH 4/4] Added gas cost calculation for TLOAD and TSTORE - Added basic scenario of gas measure costs for Transient Storage - Added scenarios of run out of gas exception loops - Still missing some tests with gas consumption for some execution contexts --- .../src/main/java/org/ethereum/vm/VM.java | 14 ++-- .../vm/opcode/TransientStorageDslTest.java | 66 +++++++++++++-- .../tstorage_gas_measure_tests.txt | 4 - ...tstore_and_tload_loop_until_out_of_gas.txt | 84 +++++++++++++++++++ .../tstore_loop_until_out_of_gas.txt | 79 +++++++++++++++++ ...de_address_space_loop_until_out_of_gas.txt | 83 ++++++++++++++++++ 6 files changed, 310 insertions(+), 20 deletions(-) create mode 100644 rskj-core/src/test/resources/dsl/transaction_storage_rskip446/tstore_and_tload_loop_until_out_of_gas.txt create mode 100644 rskj-core/src/test/resources/dsl/transaction_storage_rskip446/tstore_loop_until_out_of_gas.txt create mode 100644 rskj-core/src/test/resources/dsl/transaction_storage_rskip446/tstore_wide_address_space_loop_until_out_of_gas.txt 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 6802028fab..946b0f838e 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/VM.java +++ b/rskj-core/src/main/java/org/ethereum/vm/VM.java @@ -63,37 +63,37 @@ /** * The Ethereum Virtual Machine (EVM) is responsible for initialization * and executing a transaction on a contract. - * + * It is a quasi-Turing-complete machine; the quasi qualification * comes from the fact that the computation is intrinsically bounded * through a parameter, gas, which limits the total amount of computation done. - * + * The EVM is a simple stack-based architecture. The word size of the machine * (and thus size of stack item) is 256-bit. This was chosen to facilitate * the SHA3-256 hash scheme and elliptic-curve computations. The memory model * is a simple word-addressed byte array. The stack has an unlimited size. * The machine also has an independent storage model; this is similar in concept * to the memory but rather than a byte array, it is a word-addressable word array. - * + * Unlike memory, which is volatile, storage is non volatile and is * maintained as part of the system state. All locations in both storage * and memory are well-defined initially as zero. - * + * The machine does not follow the standard von Neumann architecture. * Rather than storing program code in generally-accessible memory or storage, * it is stored separately in a virtual ROM interactable only though * a specialised instruction. - * + * The machine can have exceptional execution for several reasons, * including stack underflows and invalid instructions. These unambiguously * and validly result in immediate halting of the machine with all state changes * left intact. The one piece of exceptional execution that does not leave * state changes intact is the out-of-gas (OOG) exception. - * + * Here, the machine halts immediately and reports the issue to * the execution agent (either the transaction processor or, recursively, * the spawning execution environment) and which will deal with it separately. - * + * @author Roman Mandeleil * @since 01.06.2014 */ diff --git a/rskj-core/src/test/java/co/rsk/vm/opcode/TransientStorageDslTest.java b/rskj-core/src/test/java/co/rsk/vm/opcode/TransientStorageDslTest.java index f4a059ef63..b47b3eb9a5 100644 --- a/rskj-core/src/test/java/co/rsk/vm/opcode/TransientStorageDslTest.java +++ b/rskj-core/src/test/java/co/rsk/vm/opcode/TransientStorageDslTest.java @@ -33,11 +33,11 @@ import org.junit.jupiter.api.Test; import java.io.FileNotFoundException; +import java.math.BigInteger; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; class TransientStorageDslTest { @@ -413,10 +413,10 @@ void testReentrancyContextsTstoreAfterReentrantCall() throws FileNotFoundExcepti assertTransactionReceiptWithStatus(world, txTstorageReentrancyContextTestContract, "b01", true); String txTstoreInReentrantCall = "txTstoreInReentrantCall"; - TransactionReceipt txReceipt = assertTransactionReceiptWithStatus(world, txTstoreInReentrantCall, "b02", true); + assertTransactionReceiptWithStatus(world, txTstoreInReentrantCall, "b02", true); String txCheckValuesStoredInTstorage = "txCheckValuesStoredInTstorage"; - txReceipt = assertTransactionReceiptWithStatus(world, txCheckValuesStoredInTstorage, "b03", true); + TransactionReceipt txReceipt = assertTransactionReceiptWithStatus(world, txCheckValuesStoredInTstorage, "b03", true); Assertions.assertEquals(3, TransactionReceiptUtil.getEventCount(txReceipt, "OK", null)); } @@ -431,10 +431,10 @@ void testReentrancyContextsTloadAfterReentrantTstore() throws FileNotFoundExcept assertTransactionReceiptWithStatus(world, txTstorageReentrancyContextTestContract, "b01", true); String txTloadAfterReentrantTstore = "txTloadAfterReentrantTstore"; - TransactionReceipt txReceipt = assertTransactionReceiptWithStatus(world, txTloadAfterReentrantTstore, "b02", true); + assertTransactionReceiptWithStatus(world, txTloadAfterReentrantTstore, "b02", true); String txCheckValuesStoredInTstorage = "txCheckValuesStoredInTstorage"; - txReceipt = assertTransactionReceiptWithStatus(world, txCheckValuesStoredInTstorage, "b03", true); + TransactionReceipt txReceipt = assertTransactionReceiptWithStatus(world, txCheckValuesStoredInTstorage, "b03", true); Assertions.assertEquals(3, TransactionReceiptUtil.getEventCount(txReceipt, "OK", null)); } @@ -449,10 +449,10 @@ void testReentrancyContextsManipulateInReentrantCall() throws FileNotFoundExcept assertTransactionReceiptWithStatus(world, txTstorageReentrancyContextTestContract, "b01", true); String txManipulateInReentrantCall = "txManipulateInReentrantCall"; - TransactionReceipt txReceipt = assertTransactionReceiptWithStatus(world, txManipulateInReentrantCall, "b02", true); + assertTransactionReceiptWithStatus(world, txManipulateInReentrantCall, "b02", true); String txCheckValuesStoredInTstorage = "txCheckValuesStoredInTstorage"; - txReceipt = assertTransactionReceiptWithStatus(world, txCheckValuesStoredInTstorage, "b03", true); + TransactionReceipt txReceipt = assertTransactionReceiptWithStatus(world, txCheckValuesStoredInTstorage, "b03", true); Assertions.assertEquals(4, TransactionReceiptUtil.getEventCount(txReceipt, "OK", null)); } @@ -467,10 +467,10 @@ void testReentrancyContextsTstoreInCallThenTloadReturnInStaticCall() throws File assertTransactionReceiptWithStatus(world, txTstorageReentrancyContextTestContract, "b01", true); String txTstorageInReentrantCallTest = "txTstorageInReentrantCallTest"; - TransactionReceipt txReceipt = assertTransactionReceiptWithStatus(world, txTstorageInReentrantCallTest, "b02", true); + assertTransactionReceiptWithStatus(world, txTstorageInReentrantCallTest, "b02", true); String txCheckValuesStoredInTstorage = "txCheckValuesStoredInTstorage"; - txReceipt = assertTransactionReceiptWithStatus(world, txCheckValuesStoredInTstorage, "b03", true); + TransactionReceipt txReceipt = assertTransactionReceiptWithStatus(world, txCheckValuesStoredInTstorage, "b03", true); Assertions.assertEquals(5, TransactionReceiptUtil.getEventCount(txReceipt, "OK", null)); } @@ -489,6 +489,54 @@ void testTransientStorageGasMeasureTests() throws FileNotFoundException, DslProc Assertions.assertEquals(4, TransactionReceiptUtil.getEventCount(txReceipt, "OK", null)); } + @Test + void testTstoreLoopUntilOutOfGas() throws FileNotFoundException, DslProcessorException { + DslParser parser = DslParser.fromResource("dsl/transaction_storage_rskip446/tstore_loop_until_out_of_gas.txt"); + World world = new World(); + WorldDslProcessor processor = new WorldDslProcessor(world); + processor.processCommands(parser); + + String txTstoreLoopUntilOutOfGasContract = "txTstoreLoopUntilOutOfGasContract"; + assertTransactionReceiptWithStatus(world, txTstoreLoopUntilOutOfGasContract, "b01", true); + + String txRunTstoreUntilOutOfGas = "txRunTstoreUntilOutOfGas"; + TransactionReceipt txReceipt = assertTransactionReceiptWithStatus(world, txRunTstoreUntilOutOfGas, "b02", false); + long txRunOutOfGas = new BigInteger(1, txReceipt.getGasUsed()).longValue(); + assertEquals(300000, txRunOutOfGas); // Assert that it consumed all the gas configured in the transaction + } + + @Test + void testTstoreWideAddressSpaceLoopUntilOutOfGas() throws FileNotFoundException, DslProcessorException { + DslParser parser = DslParser.fromResource("dsl/transaction_storage_rskip446/tstore_wide_address_space_loop_until_out_of_gas.txt"); + World world = new World(); + WorldDslProcessor processor = new WorldDslProcessor(world); + processor.processCommands(parser); + + String txTstoreWideAddressSpaceLoopUntilOutOfGasContract = "txTstoreWideAddressSpaceLoopUntilOutOfGasContract"; + assertTransactionReceiptWithStatus(world, txTstoreWideAddressSpaceLoopUntilOutOfGasContract, "b01", true); + + String txRunTstoreWideAddressSpaceUntilOutOfGas = "txRunTstoreWideAddressSpaceUntilOutOfGas"; + TransactionReceipt txReceipt = assertTransactionReceiptWithStatus(world, txRunTstoreWideAddressSpaceUntilOutOfGas, "b02", false); + long txRunOutOfGas = new BigInteger(1, txReceipt.getGasUsed()).longValue(); + assertEquals(500000, txRunOutOfGas); // Assert that it consumed all the gas configured in the transaction + } + + @Test + void testTstoreAndTloadLoopUntilOutOfGas() throws FileNotFoundException, DslProcessorException { + DslParser parser = DslParser.fromResource("dsl/transaction_storage_rskip446/tstore_and_tload_loop_until_out_of_gas.txt"); + World world = new World(); + WorldDslProcessor processor = new WorldDslProcessor(world); + processor.processCommands(parser); + + String txTstoreAndTloadLoopUntilOutOfGasContract = "txTstoreAndTloadLoopUntilOutOfGasContract"; + assertTransactionReceiptWithStatus(world, txTstoreAndTloadLoopUntilOutOfGasContract, "b01", true); + + String txRunTstoreAndTloadUntilOutOfGas = "txRunTstoreAndTloadUntilOutOfGas"; + TransactionReceipt txReceipt = assertTransactionReceiptWithStatus(world, txRunTstoreAndTloadUntilOutOfGas, "b02", false); + long txRunOutOfGas = new BigInteger(1, txReceipt.getGasUsed()).longValue(); + assertEquals(700000, txRunOutOfGas); // Assert that it consumed all the gas configured in the transaction + } + private static TransactionReceipt assertTransactionReceiptWithStatus(World world, String txName, String blockName, boolean withSuccess) { Transaction txCreation = world.getTransactionByName(txName); assertNotNull(txCreation); diff --git a/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/tstorage_gas_measure_tests.txt b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/tstorage_gas_measure_tests.txt index 93afe26062..c560ad5aef 100644 --- a/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/tstorage_gas_measure_tests.txt +++ b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/tstorage_gas_measure_tests.txt @@ -66,10 +66,6 @@ contract TstorageGasMeasureTestCases { } // DESCRIPTION -TstorageGasMeasureTestCases has the following functions: - - - // CONTRACT BYTECODE TstorageGasMeasureTestCases: 6080604052348015600e575f5ffd5b506104098061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c806307e5ab1e14610038578063592cdadd14610042575b5f5ffd5b610040610060565b005b61004a610171565b6040516100579190610205565b60405180910390f35b5f5f5a915060105c5a9150508082610078919061024b565b5f819055506100a25f5460405180606001604052806028815260200161037e60289139606e610176565b5a91506010805d60105c5a91505080826100bc919061024b565b5f819055506100e65f5460405180606001604052806033815260200161034b6033913960d8610176565b5a9150601060ff5d5a905080826100fd919061024b565b5f819055506101275f546040518060600160405280602e81526020016103a6602e91396071610176565b5a9150601060ff5d601160ff5d5a90508082610143919061024b565b5f8190555061016d5f546040518060600160405280602e815260200161031d602e913960db610176565b5050565b5f5481565b8083036101ae577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16101e8565b7fc9e730d5b570f89e168eb8c3d29f8c396b957e540af248c95c9519ac47c2c69f82846040516101df9291906102ee565b60405180910390a15b505050565b5f819050919050565b6101ff816101ed565b82525050565b5f6020820190506102185f8301846101f6565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f610255826101ed565b9150610260836101ed565b92508282039050818111156102785761027761021e565b5b92915050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f6102c08261027e565b6102ca8185610288565b93506102da818560208601610298565b6102e3816102a6565b840191505092915050565b5f6040820190508181035f83015261030681856102b6565b905061031560208301846101f6565b939250505056fe436865636b696e67207473746f7265207761726d20676173207370656e7420697320746865206578706563746564436865636b696e67207473746f726520616e6420746c6f616420676173207370656e7420697320746865206578706563746564436865636b696e6720746c6f616420676173207370656e7420697320746865206578706563746564436865636b696e67207473746f726520636f6c6420676173207370656e7420697320746865206578706563746564a2646970667358221220b17e2b2108ab28708c0fb481438a67aaae5b4c10a6e07af3125fe9d609eb498c64736f6c634300081c0033 diff --git a/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/tstore_and_tload_loop_until_out_of_gas.txt b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/tstore_and_tload_loop_until_out_of_gas.txt new file mode 100644 index 0000000000..2ab80ad6ce --- /dev/null +++ b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/tstore_and_tload_loop_until_out_of_gas.txt @@ -0,0 +1,84 @@ +comment + +// CONTRACT CODE +pragma solidity ^0.8.24; + +contract TstoreAndTloadLoopUntilOutOfGas { + + constructor() + { + + } + + function runTstoreAndTloadUntilOutOfGas() external { + // Run tstore and tload in loop until out of gas + for (uint256 i = 0; i < 1000000; i++) { + assembly { + let gasValue := gas() + tstore(gasValue, gas()) + let loadedValue := tload(gasValue) + pop(0) + } + } + } +} + +// DESCRIPTION + +// CONTRACT BYTECODE + +TstoreAndTloadLoopUntilOutOfGas: 6080604052348015600e575f80fd5b5060888061001b5f395ff3fe6080604052348015600e575f80fd5b50600436106026575f3560e01c8063ae1978d714602a575b5f80fd5b60306032565b005b5f5b620f4240811015604f575a5a815d5080806001019150506034565b5056fea2646970667358221220db37ed7fb5e1de866d127a5da76e02d895a6014e4de7965e57f66ae94ad5d1d764736f6c63430008180033 + +function hashes: + +- ae1978d7: runTstoreAndTloadUntilOutOfGas() + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TstoreWideAddressSpaceLoopUntilOutOfGas contract +transaction_build txTstoreAndTloadLoopUntilOutOfGasContract + sender acc1 + receiverAddress 00 + value 0 + data 6080604052348015600e575f80fd5b5060888061001b5f395ff3fe6080604052348015600e575f80fd5b50600436106026575f3560e01c8063ae1978d714602a575b5f80fd5b60306032565b005b5f5b620f4240811015604f575a5a815d5080806001019150506034565b5056fea2646970667358221220db37ed7fb5e1de866d127a5da76e02d895a6014e4de7965e57f66ae94ad5d1d764736f6c63430008180033 + gas 1000000 + build + +# Create block to hold txTstoreAndTloadLoopUntilOutOfGasContract transaction +block_build b01 + parent g00 + transactions txTstoreAndTloadLoopUntilOutOfGasContract + gasLimit 1200000 + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Create transaction to execute txRunTstoreUntilOutOfGas function +transaction_build txRunTstoreAndTloadUntilOutOfGas + sender acc1 + nonce 1 + contract txTstoreAndTloadLoopUntilOutOfGasContract + value 0 + data ae1978d7 + gas 700000 + build + +# Create block to hold txRunTstoreAndTloadUntilOutOfGas transaction +block_build b02 + parent b01 + transactions txRunTstoreAndTloadUntilOutOfGas + gasLimit 750000 + 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/transaction_storage_rskip446/tstore_loop_until_out_of_gas.txt b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/tstore_loop_until_out_of_gas.txt new file mode 100644 index 0000000000..62d3361b6b --- /dev/null +++ b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/tstore_loop_until_out_of_gas.txt @@ -0,0 +1,79 @@ +comment + +// CONTRACT CODE +pragma solidity ^0.8.24; + +contract TstoreLoopUntilOutOfGas { + + constructor() + { + } + + function runTstoreUntilOutOfGas() external { + // Run tstore in loop until out of gas + for (uint256 i = 0; i < 1000000; i++) { + assembly { + tstore(gas(), gas()) + } + } + } +} +// DESCRIPTION + +// CONTRACT BYTECODE + +TstoreLoopUntilOutOfGas: 6080604052348015600e575f80fd5b5060868061001b5f395ff3fe6080604052348015600e575f80fd5b50600436106026575f3560e01c80637b17abde14602a575b5f80fd5b60306032565b005b5f5b620f4240811015604d575a5a5d80806001019150506034565b5056fea2646970667358221220460f7f44d313897b5627f933b4969a97d228645b5447a4ea286119f6cc66155964736f6c63430008180033 + +function hashes: + +- 7b17abde: runTstoreUntilOutOfGas() + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TstoreLoopUntilOutOfGas contract +transaction_build txTstoreLoopUntilOutOfGasContract + sender acc1 + receiverAddress 00 + value 0 + data 6080604052348015600e575f80fd5b5060868061001b5f395ff3fe6080604052348015600e575f80fd5b50600436106026575f3560e01c80637b17abde14602a575b5f80fd5b60306032565b005b5f5b620f4240811015604d575a5a5d80806001019150506034565b5056fea2646970667358221220460f7f44d313897b5627f933b4969a97d228645b5447a4ea286119f6cc66155964736f6c63430008180033 + gas 1000000 + build + +# Create block to hold txTstoreLoopUntilOutOfGasContract transaction +block_build b01 + parent g00 + transactions txTstoreLoopUntilOutOfGasContract + gasLimit 1200000 + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Create transaction to execute txRunTstoreUntilOutOfGas function +transaction_build txRunTstoreUntilOutOfGas + sender acc1 + nonce 1 + contract txTstoreLoopUntilOutOfGasContract + value 0 + data 7b17abde + gas 300000 + build + +# Create block to hold txRunTstoreUntilOutOfGas transaction +block_build b02 + parent b01 + transactions txRunTstoreUntilOutOfGas + gasLimit 350000 + 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/transaction_storage_rskip446/tstore_wide_address_space_loop_until_out_of_gas.txt b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/tstore_wide_address_space_loop_until_out_of_gas.txt new file mode 100644 index 0000000000..68b0e91fba --- /dev/null +++ b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/tstore_wide_address_space_loop_until_out_of_gas.txt @@ -0,0 +1,83 @@ +comment + +// CONTRACT CODE +pragma solidity ^0.8.24; + +contract TstoreWideAddressSpaceLoopUntilOutOfGas { + + constructor() + { + } + + function runTstoreWideAddressSpaceUntilOutOfGas() external { + // Run tstore in loop until out of gas, using a wide address space + for (uint256 i = 0; i < 1000000; i++) { + assembly { + let pcValue := codesize() + let shiftedPc := shl(pcValue, 1) + let addResult := add(shiftedPc, gas()) + tstore(addResult, gas()) + } + } + } +} + +// DESCRIPTION + +// CONTRACT BYTECODE + +TstoreWideAddressSpaceLoopUntilOutOfGas: 6080604052348015600e575f80fd5b5060918061001b5f395ff3fe6080604052348015600e575f80fd5b50600436106026575f3560e01c806309fdcd3f14602a575b5f80fd5b60306032565b005b5f5b620f4240811015605857386001811b5a81015a815d50505080806001019150506034565b5056fea264697066735822122098cf088d672ad1d70c2d2a0edbbba6202aff9669702ae83d6e9eb1050fc6622864736f6c63430008180033 + +function hashes: + +- 09fdcd3f: runTstoreWideAddressSpaceUntilOutOfGas() + +end + +# Create and fund new account +account_new acc1 10000000 + +# Create transaction to deploy TstoreWideAddressSpaceLoopUntilOutOfGas contract +transaction_build txTstoreWideAddressSpaceLoopUntilOutOfGasContract + sender acc1 + receiverAddress 00 + value 0 + data 6080604052348015600e575f80fd5b5060918061001b5f395ff3fe6080604052348015600e575f80fd5b50600436106026575f3560e01c806309fdcd3f14602a575b5f80fd5b60306032565b005b5f5b620f4240811015605857386001811b5a81015a815d50505080806001019150506034565b5056fea264697066735822122098cf088d672ad1d70c2d2a0edbbba6202aff9669702ae83d6e9eb1050fc6622864736f6c63430008180033 + gas 1000000 + build + +# Create block to hold txTstoreWideAddressSpaceLoopUntilOutOfGasContract transaction +block_build b01 + parent g00 + transactions txTstoreWideAddressSpaceLoopUntilOutOfGasContract + gasLimit 1200000 + build + +# Connect block +block_connect b01 + +# Check b01 is best block +assert_best b01 + +# Create transaction to execute txRunTstoreUntilOutOfGas function +transaction_build txRunTstoreWideAddressSpaceUntilOutOfGas + sender acc1 + nonce 1 + contract txTstoreWideAddressSpaceLoopUntilOutOfGasContract + value 0 + data 09fdcd3f + gas 500000 + build + +# Create block to hold txRunTstoreWideAddressSpaceUntilOutOfGas transaction +block_build b02 + parent b01 + transactions txRunTstoreWideAddressSpaceUntilOutOfGas + gasLimit 550000 + build + +# Connect block +block_connect b02 + +# Check b02 is best block +assert_best b02 \ No newline at end of file