From bd652afb676c02b47ea74972698cfbc27d35f547 Mon Sep 17 00:00:00 2001 From: frederico leal Date: Mon, 30 Sep 2024 11:51:42 +0200 Subject: [PATCH 1/2] Adding the structure for transient storage opcodes - Adding scratch from the DSL tests and contracts for tests - The tests are still failing, I am investigating to find the root cause - Adding more logic to the map structures - Finishing test validation for basic scenarios of TLOAD/TSTORE - Now we need to add more scenarios to validate that the memory isn't shared and erased in the end of the transaction. --- .../org/ethereum/db/MutableRepository.java | 11 +- .../db/TransientStorageRepositoryCreator.java | 36 ++++ .../src/main/java/org/ethereum/vm/VM.java | 31 +-- .../java/org/ethereum/vm/program/Program.java | 27 ++- .../vm/opcode/TransientStorageDslTest.java | 99 +++++++-- .../upgrades/ActivationConfigTest.java | 1 + .../tload_tstore_basic_contract.txt | 198 ++++++++++++++---- 7 files changed, 325 insertions(+), 78 deletions(-) create mode 100644 rskj-core/src/main/java/org/ethereum/db/TransientStorageRepositoryCreator.java diff --git a/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java b/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java index 42dd7336b25..57279db7dfb 100644 --- a/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java +++ b/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java @@ -25,7 +25,11 @@ import co.rsk.crypto.Keccak256; import co.rsk.db.MutableTrieCache; import co.rsk.db.MutableTrieImpl; -import co.rsk.trie.*; +import co.rsk.trie.IterationElement; +import co.rsk.trie.MutableTrie; +import co.rsk.trie.Trie; +import co.rsk.trie.TrieKeySlice; +import co.rsk.trie.TrieStore; import com.google.common.annotations.VisibleForTesting; import org.ethereum.core.AccountState; import org.ethereum.core.Repository; @@ -37,7 +41,10 @@ import javax.annotation.Nonnull; import java.math.BigInteger; -import java.util.*; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Optional; +import java.util.Set; public class MutableRepository implements Repository { private static final Logger logger = LoggerFactory.getLogger("repository"); diff --git a/rskj-core/src/main/java/org/ethereum/db/TransientStorageRepositoryCreator.java b/rskj-core/src/main/java/org/ethereum/db/TransientStorageRepositoryCreator.java new file mode 100644 index 00000000000..c03541fbe6f --- /dev/null +++ b/rskj-core/src/main/java/org/ethereum/db/TransientStorageRepositoryCreator.java @@ -0,0 +1,36 @@ +/* + * This file is part of RskJ + * Copyright (C) 2024 RSK Labs Ltd. + * (derived from ethereumJ library, Copyright (c) 2016 ) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package org.ethereum.db; + +import co.rsk.db.MutableTrieImpl; +import co.rsk.trie.Trie; +import co.rsk.trie.TrieStoreImpl; +import org.ethereum.core.Repository; +import org.ethereum.datasource.HashMapDB; + +public class TransientStorageRepositoryCreator { + + private TransientStorageRepositoryCreator() { + } + + public static Repository createNewTransientStorage() { + return new MutableRepository(new MutableTrieImpl(new TrieStoreImpl(new HashMapDB()), new Trie())); + } +} 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 6c5adc79246..7bed8d184ef 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/VM.java +++ b/rskj-core/src/main/java/org/ethereum/vm/VM.java @@ -1327,30 +1327,30 @@ else if (oldValue != null && newValue.isZero()) { } protected void doTLOAD(){ - DataWord key = program.stackPop(); - DataWord address = program.getOwnerAddress(); - if (isLogEnabled) { - logger.info("Executing TLOAD with parameters: address={} | key = {}", address, key); + logger.info("Executing TLOAD with parameters: key = {}", key); } + DataWord val = program.transientStorageLoad(key); - program.transientStorageSave(key, address); - // key could be returned to the pool, but storageLoad semantics should be checked + if (val == null) { + val = DataWord.ZERO; + } + + program.stackPush(val); + // key could be returned to the pool, but transientStorageLoad semantics should be checked // to make sure storageLoad always gets a copy, not a reference. program.step(); } protected void doTSTORE(){ - + DataWord address = program.stackPop(); DataWord value = program.stackPop(); - DataWord address = program.getOwnerAddress(); - DataWord key = DataWord.ZERO; if (isLogEnabled) { - logger.info("Executing TSTORE with parameters: address={} | key = {} | value = {}", address, key, value); + logger.info("Executing TSTORE with parameters: address={} | value = {}", address, value); } - program.transientStorageLoad(address, key, value); + program.transientStorageSave(address, value); program.step(); } @@ -1967,9 +1967,16 @@ protected void executeOpcode() { break; case OpCodes.OP_SSTORE: doSSTORE(); break; - case OpCodes.OP_TLOAD: doTLOAD(); + case OpCodes.OP_TLOAD: + if (!activations.isActive(RSKIP446)) { + throw Program.ExceptionHelper.invalidOpCode(program); + } + doTLOAD(); break; case OpCodes.OP_TSTORE: doTSTORE(); + if (!activations.isActive(RSKIP446)) { + throw Program.ExceptionHelper.invalidOpCode(program); + } break; case OpCodes.OP_JUMP: doJUMP(); break; diff --git a/rskj-core/src/main/java/org/ethereum/vm/program/Program.java b/rskj-core/src/main/java/org/ethereum/vm/program/Program.java index 2029b0c6f3b..73043c7a642 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/program/Program.java +++ b/rskj-core/src/main/java/org/ethereum/vm/program/Program.java @@ -40,6 +40,7 @@ import org.ethereum.core.SignatureCache; import org.ethereum.core.Transaction; import org.ethereum.crypto.HashUtil; +import org.ethereum.db.TransientStorageRepositoryCreator; import org.ethereum.util.ByteUtil; import org.ethereum.util.FastByteComparisons; import org.ethereum.vm.DataWord; @@ -72,8 +73,10 @@ import java.math.BigInteger; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.Map; import java.util.Optional; import java.util.Set; @@ -95,7 +98,6 @@ public class Program { private static final Logger logger = LoggerFactory.getLogger("VM"); private static final Logger gasLogger = LoggerFactory.getLogger("gas"); - public static final long MAX_MEMORY = (1<<30); //Max size for stack checks @@ -115,6 +117,7 @@ public class Program { private final Stack stack; private final Memory memory; private final Storage storage; + private final Map transientStorages; private byte[] returnDataBuffer; private final ProgramResult result = new ProgramResult(); @@ -178,6 +181,7 @@ public Program( this.stack = setupProgramListener(new Stack()); this.stack.ensureCapacity(1024); // faster? this.storage = setupProgramListener(new Storage(programInvoke)); + this.transientStorages = new HashMap<>(); this.deletedAccountsInBlock = new HashSet<>(deletedAccounts); this.signatureCache = signatureCache; precompile(); @@ -453,6 +457,16 @@ public Repository getStorage() { return this.storage; } + public Repository getTransientStorage(RskAddress addr) { + Repository current = transientStorages.get(addr); + if(current == null) { + current = TransientStorageRepositoryCreator.createNewTransientStorage(); + transientStorages.put(addr, current); + } + return current; + } + + @SuppressWarnings("ThrowableResultOfMethodCallIgnored") public void createContract(DataWord value, DataWord memStart, DataWord memSize) { RskAddress senderAddress = new RskAddress(getOwnerAddress()); @@ -985,8 +999,10 @@ private void storageSave(byte[] key, byte[] val) { getStorage().addStorageRow(getOwnerRskAddress(), keyWord, valWord); } - public void transientStorageSave(DataWord key, DataWord address) { - + public void transientStorageSave(DataWord key, DataWord value) { + RskAddress addr = getOwnerRskAddress(); + Repository storage = getTransientStorage(addr); + storage.addStorageRow(addr, key, value); } private RskAddress getOwnerRskAddress() { @@ -1099,8 +1115,11 @@ public DataWord storageLoad(DataWord key) { return getStorage().getStorageValue(getOwnerRskAddress(), key); } - public void transientStorageLoad(DataWord address, DataWord key, DataWord value) { + public DataWord transientStorageLoad(DataWord key) { + RskAddress addr = getOwnerRskAddress(); + Repository currentTransientStorage = getTransientStorage(addr); + return currentTransientStorage.getStorageValue(addr, key); } public DataWord getPrevHash() { 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 1eec762bb0f..0a5b23b7467 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 @@ -28,6 +28,7 @@ import org.ethereum.core.Block; import org.ethereum.core.Transaction; import org.ethereum.core.TransactionReceipt; +import org.ethereum.core.util.TransactionReceiptUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -35,6 +36,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; public class TransientStorageDslTest { @@ -45,23 +47,59 @@ void testTransientStorageOpcodesExecutionsWithRSKIPActivated() throws FileNotFou WorldDslProcessor processor = new WorldDslProcessor(world); processor.processCommands(parser); - String contractCreationTx = "txTestTransientStorageContract"; - Transaction contractTransaction = world.getTransactionByName("txTestTransientStorageContract"); - assertNotNull(contractTransaction); + String mainContractTransientStorageCreationTxName = "txTestTransientStorageContract"; + assertTransactionReceiptWithStatus(world, mainContractTransientStorageCreationTxName, "b01", true); - Block bestBlock = world.getBlockByName("b03"); - Assertions.assertEquals(1, bestBlock.getTransactionsList().size()); - TransactionReceipt contractTransactionReceipt = world.getTransactionReceiptByName(contractCreationTx); + String secondaryContractTransientStorageCreationTxName = "txTestTransientStorageOtherContract"; + assertTransactionReceiptWithStatus(world, secondaryContractTransientStorageCreationTxName, "b02", true); - assertNotNull(contractTransactionReceipt); - byte[] status = contractTransactionReceipt.getStatus(); - assertNotNull(status); - assertEquals(1, status.length); - assertEquals(1, status[0]); + String checkingOpcodesTxName = "txTestTransientStorageOpCodes"; + TransactionReceipt txReceipt = assertTransactionReceiptWithStatus(world, checkingOpcodesTxName, "b03", true); + Assertions.assertEquals(1, TransactionReceiptUtil.getEventCount(txReceipt, "OK", null)); + + String checkingOpcodesTxName2 = "txTestTransientStorageOpCodesOtherValue"; + TransactionReceipt txReceipt2 = assertTransactionReceiptWithStatus(world, checkingOpcodesTxName2, "b04", true); + Assertions.assertEquals(1, TransactionReceiptUtil.getEventCount(txReceipt2, "OK", null)); + } + + @Test + void testTransientStorageOpcodesShareMemorySameTransaction() throws FileNotFoundException, DslProcessorException { + DslParser parser = DslParser.fromResource("dsl/transaction_storage_rskip446/tload_tstore_basic_contract.txt"); + World world = new World(); + WorldDslProcessor processor = new WorldDslProcessor(world); + processor.processCommands(parser); + + String mainContractTransientStorageCreationTxName = "txTestTransientStorageContract"; + assertTransactionReceiptWithStatus(world, mainContractTransientStorageCreationTxName, "b01", true); + + String secondaryContractTransientStorageCreationTxName = "txTestTransientStorageOtherContract"; + assertTransactionReceiptWithStatus(world, secondaryContractTransientStorageCreationTxName, "b02", true); + + String checkingOpcodesTxName = "txTestTransientStorageNestedTransactionShareMemory"; + TransactionReceipt txReceipt = assertTransactionReceiptWithStatus(world, checkingOpcodesTxName, "b05", true); + Assertions.assertEquals(1, TransactionReceiptUtil.getEventCount(txReceipt, "OK", null)); + } + + @Test + void testTransientStorageOpcodesDoesntShareMemoryFromOtherContract() throws FileNotFoundException, DslProcessorException { + DslParser parser = DslParser.fromResource("dsl/transaction_storage_rskip446/tload_tstore_basic_contract.txt"); + World world = new World(); + WorldDslProcessor processor = new WorldDslProcessor(world); + processor.processCommands(parser); + + String mainContractTransientStorageCreationTxName = "txTestTransientStorageContract"; + assertTransactionReceiptWithStatus(world, mainContractTransientStorageCreationTxName, "b01", true); + + String secondaryContractTransientStorageCreationTxName = "txTestTransientStorageOtherContract"; + assertTransactionReceiptWithStatus(world, secondaryContractTransientStorageCreationTxName, "b02", true); + + String checkingOpcodesTxName = "txTestTransientStorageNestedTransactionOtherContractDoesntShareMemory"; + TransactionReceipt txReceipt = assertTransactionReceiptWithStatus(world, checkingOpcodesTxName, "b06", true); + Assertions.assertEquals(1, TransactionReceiptUtil.getEventCount(txReceipt, "ERROR", new String[]{"bytes32"})); } @Test - void testTransientStorageOpcodesExecutionsWithRSKIPDeactivated() throws FileNotFoundException, DslProcessorException { + void testTransientStorageOpcodesExecutionFailsWithRSKIPDeactivated() throws FileNotFoundException, DslProcessorException { TestSystemProperties rskip446Disabled = new TestSystemProperties(rawConfig -> rawConfig.withValue("blockchain.config.hardforkActivationHeights.lovell700", ConfigValueFactory.fromAnyRef(-1)) ); @@ -71,19 +109,36 @@ void testTransientStorageOpcodesExecutionsWithRSKIPDeactivated() throws FileNotF WorldDslProcessor processor = new WorldDslProcessor(world); processor.processCommands(parser); - String contractCreationTx = "txTestTransientStorageContract"; - Transaction contractTransaction = world.getTransactionByName("txTestTransientStorageContract"); - assertNotNull(contractTransaction); + String mainContractTransientStorageCreationTxName = "txTestTransientStorageContract"; + assertTransactionReceiptWithStatus(world, mainContractTransientStorageCreationTxName, "b01", true); + + String secondaryContractTransientStorageCreationTxName = "txTestTransientStorageOtherContract"; + assertTransactionReceiptWithStatus(world, secondaryContractTransientStorageCreationTxName, "b02", true); + + String checkingOpcodesTxName = "txTestTransientStorageOpCodes"; + assertTransactionReceiptWithStatus(world, checkingOpcodesTxName, "b03", false); + } - Block bestBlock = world.getBlockByName("b03"); - Assertions.assertEquals(1, bestBlock.getTransactionsList().size()); - TransactionReceipt contractTransactionReceipt = world.getTransactionReceiptByName(contractCreationTx); + private static TransactionReceipt assertTransactionReceiptWithStatus(World world, String txName, String blockName, boolean withSuccess) { + Transaction txCreation = world.getTransactionByName(txName); + assertNotNull(txCreation); - assertNotNull(contractTransactionReceipt); - byte[] status = contractTransactionReceipt.getStatus(); + Block block = world.getBlockByName(blockName); + assertEquals(1, block.getTransactionsList().size()); + + TransactionReceipt txReceipt = world.getTransactionReceiptByName(txName); + assertNotNull(txReceipt); + + byte[] status = txReceipt.getStatus(); assertNotNull(status); - assertEquals(1, status.length); - assertEquals(1, status[0]); + + if(withSuccess) { + assertEquals(1, status.length); + assertEquals(1, status[0]); + } else { + assertEquals(0, status.length); + } + return txReceipt; } } diff --git a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java index 167d30f3f8b..760742b20cf 100644 --- a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java +++ b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java @@ -126,6 +126,7 @@ class ActivationConfigTest { " rskip434: arrowhead631", " rskip428: lovell700", " rskip438: lovell700", + " rskip446: lovell700", "}" )); diff --git a/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/tload_tstore_basic_contract.txt b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/tload_tstore_basic_contract.txt index e8312fc8cb4..0607af0de7a 100644 --- a/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/tload_tstore_basic_contract.txt +++ b/rskj-core/src/test/resources/dsl/transaction_storage_rskip446/tload_tstore_basic_contract.txt @@ -1,55 +1,108 @@ comment // CONTRACT CODE - pragma solidity ^0.8.24; contract TestTransientStorage { constructor() {} event OK(); - event ERROR(); + event ERROR(bytes32); - function checkTStore() external { - bytes32 valueToSave = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; - address contractAddress = address(this); + function checkTransientStorage(bytes32 key, bytes32 value) external { + bytes32 valueLoaded; assembly { - tstore(contractAddress, valueToSave) // Use TSTORE to save the value at the contract address on the transient storage + tstore(key, value) + valueLoaded := tload(key) + } + if (valueLoaded == value) { + emit OK(); + } else { + emit ERROR(valueLoaded); } } - function checkTLoad() external { - bytes32 valueSaved = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; + function checkDataEqualsTheValuePassed(bytes32 key, bytes32 valueExpected) external { bytes32 valueLoaded; - address contractAddress = address(this); - assembly { - valueLoaded := tload(contractAddress) // Use TLOAD to load the value into the temporary variable + assembly { + valueLoaded := tload(key) } - if (valueLoaded == valueSaved) { + if (valueLoaded == valueExpected) { emit OK(); } else { - emit ERROR(); + emit ERROR(valueLoaded); + } + } + + function checkIfNestedTransactionSameContractShareMemory(bytes32 key, bytes32 value) external { + assembly { + tstore(key, value) + } + this.checkDataEqualsTheValuePassed(key, value); + } + + function checkIfNestedTransactionFromOtherContractShareMemory(bytes32 key, bytes32 value) external { + assembly { + tstore(key, value) + } + new TestTransientStorageOtherContract().checkDataEqualsTheValuePassed(key, value); + } +} + +contract TestTransientStorageOtherContract{ + constructor() {} + + event OK(); + event ERROR(bytes32); + + function checkDataEqualsTheValuePassed(bytes32 key, bytes32 valueExpected) external { + bytes32 valueLoaded; + assembly { + valueLoaded := tload(key) + } + if (valueLoaded == valueExpected) { + emit OK(); + } else { + emit ERROR(valueLoaded); } } } // DESCRIPTION -This contract contains two functions: checkTStore and checkTLoad. +TestTransientStorage has the following functions: -* checkTStore simply checks if the transaction finished with success and it's expected to have saved the value on the address +* checkTransientStorage simply checks if the TSTORE and TLOAD functions are working properly. It stores a value in the transient storage and then loads it. If the loaded value is the same as the stored value, it emits an OK event. Otherwise, it emits an ERROR event. -* checkTLoad simply checks if the transaction finished with success and check if the value loaded is the same as the value saved +* checkDataEqualsTheValuePassed simply checks if the transaction TLOAD doesn't load a value that was not stored in the transient storage. It loads a value from the transient storage, if we haven't loaded with a nested transaction it will return 0 otherwise it will return the value stored. +If the value loaded is the same as the value passed as a parameter, it emits an OK event. Otherwise, it emits an ERROR event. +* checkIfNestedTransactionSameContractShareMemory stores a value in the transient storage and then calls a function from the same contract that loads the value from the transient storage. This is to check if the transient storage is shared between nested transactions. + +* checkIfNestedTransactionFromOtherContractShareMemory stores a value in the transient storage and then calls a function from another contract that loads the value from the transient storage. This is to check if the transient storage is not shared between transactions. + +TestTransientStorageOtherContract has the following functions: + +* checkDataEqualsTheValuePassed simply checks if the transaction TLOAD doesn't load a value that was not stored in the transient storage. It loads a value from the transient storage, if we haven't loaded with a nested transaction it will return 0 otherwise it will return the value stored. +If the value loaded is the same as the value passed as a parameter, it emits an OK event. Otherwise, it emits an ERROR event. // CONTRACT BYTECODE -608060405234801561000f575f80fd5b5061014a8061001d5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c8063a36ca99d14610038578063d5ad4e0b14610042575b5f80fd5b61004061004c565b005b61004a61007c565b005b5f7e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f30905081815d5050565b5f7e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f80309050805c91508282036100e2577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a161010f565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b50505056fea2646970667358221220c97a07f12090a982975e295ffa092efe648f43916b098afb2523daffb7e42df464736f6c63430008180033 +TestTransientStorage: 6080604052348015600e575f80fd5b506105718061001c5f395ff3fe608060405234801561000f575f80fd5b506004361061004a575f3560e01c80635dbdd0ce1461004e5780637b70ef4e1461006a5780637dccab6614610086578063f5cc67a1146100a2575b5f80fd5b610068600480360381019061006391906102fc565b6100be565b005b610084600480360381019061007f91906102fc565b61012d565b005b6100a0600480360381019061009b91906102fc565b6101c1565b005b6100bc60048036038101906100b791906102fc565b61023b565b005b80825d3073ffffffffffffffffffffffffffffffffffffffff16637dccab6683836040518363ffffffff1660e01b81526004016100fc929190610349565b5f604051808303815f87803b158015610113575f80fd5b505af1158015610125573d5f803e3d5ffd5b505050505050565b80825d60405161013c906102b8565b604051809103905ff080158015610155573d5f803e3d5ffd5b5073ffffffffffffffffffffffffffffffffffffffff16637dccab6683836040518363ffffffff1660e01b8152600401610190929190610349565b5f604051808303815f87803b1580156101a7575f80fd5b505af11580156101b9573d5f803e3d5ffd5b505050505050565b5f825c90508181036101fe577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a1610236565b7f2fe0d3bd8bbb632441eefda46ec52f1ddda3a9e827d14c40f422037320f28bd98160405161022d9190610370565b60405180910390a15b505050565b5f81835d825c905081810361027b577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16102b3565b7f2fe0d3bd8bbb632441eefda46ec52f1ddda3a9e827d14c40f422037320f28bd9816040516102aa9190610370565b60405180910390a15b505050565b6101b28061038a83390190565b5f80fd5b5f819050919050565b6102db816102c9565b81146102e5575f80fd5b50565b5f813590506102f6816102d2565b92915050565b5f8060408385031215610312576103116102c5565b5b5f61031f858286016102e8565b9250506020610330858286016102e8565b9150509250929050565b610343816102c9565b82525050565b5f60408201905061035c5f83018561033a565b610369602083018461033a565b9392505050565b5f6020820190506103835f83018461033a565b9291505056fe6080604052348015600e575f80fd5b506101968061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c80637dccab661461002d575b5f80fd5b610047600480360381019061004291906100fa565b610049565b005b5f825c9050818103610086577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16100be565b7f2fe0d3bd8bbb632441eefda46ec52f1ddda3a9e827d14c40f422037320f28bd9816040516100b59190610147565b60405180910390a15b505050565b5f80fd5b5f819050919050565b6100d9816100c7565b81146100e3575f80fd5b50565b5f813590506100f4816100d0565b92915050565b5f80604083850312156101105761010f6100c3565b5b5f61011d858286016100e6565b925050602061012e858286016100e6565b9150509250929050565b610141816100c7565b82525050565b5f60208201905061015a5f830184610138565b9291505056fea2646970667358221220299e78f210bb50b25e55a7d3d761eb697663f501d6b877c5f17e651d5c7111fb64736f6c634300081a0033a2646970667358221220d99f2c2087ad3b0f2283262f95bb66f6becb8b8538af83c1a9a571716f0abb1b64736f6c634300081a0033 + +TestTransientStorageOtherContract: 6080604052348015600e575f80fd5b506101968061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c80637dccab661461002d575b5f80fd5b610047600480360381019061004291906100fa565b610049565b005b5f825c9050818103610086577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16100be565b7f2fe0d3bd8bbb632441eefda46ec52f1ddda3a9e827d14c40f422037320f28bd9816040516100b59190610147565b60405180910390a15b505050565b5f80fd5b5f819050919050565b6100d9816100c7565b81146100e3575f80fd5b50565b5f813590506100f4816100d0565b92915050565b5f80604083850312156101105761010f6100c3565b5b5f61011d858286016100e6565b925050602061012e858286016100e6565b9150509250929050565b610141816100c7565b82525050565b5f60208201905061015a5f830184610138565b9291505056fea2646970667358221220299e78f210bb50b25e55a7d3d761eb697663f501d6b877c5f17e651d5c7111fb64736f6c634300081a0033 -// CONTRACT CALL +// CONTRACT CALLS -- checkTStore() -> a36ca99d -- checkTLoad() -> d5ad4e0b +TestTransientStorage: + + 7dccab66: checkDataEqualsTheValuePassed(bytes32,bytes32) + 7b70ef4e: checkIfNestedTransactionFromOtherContractShareMemory(bytes32,bytes32) + 5dbdd0ce: checkIfNestedTransactionSameContractShareMemory(bytes32,bytes32) + f5cc67a1: checkTransientStorage(bytes32,bytes32) + +TestTransientStorageOtherContract: +- 7dccab66 -> checkDataEqualsTheValuePassed(bytes32,bytes32) end @@ -61,8 +114,8 @@ transaction_build txTestTransientStorageContract sender acc1 receiverAddress 00 value 0 - data 608060405234801561000f575f80fd5b5061014a8061001d5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c8063a36ca99d14610038578063d5ad4e0b14610042575b5f80fd5b61004061004c565b005b61004a61007c565b005b5f7e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f30905081815d5050565b5f7e0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f5f1b90505f80309050805c91508282036100e2577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a161010f565b7f1c9c433b57013295d61f5c5738f5e2cb1de70bb5ba5b2896edfa8efae345965e60405160405180910390a15b50505056fea2646970667358221220c97a07f12090a982975e295ffa092efe648f43916b098afb2523daffb7e42df464736f6c63430008180033 - gas 1200000 + data 6080604052348015600e575f80fd5b506105718061001c5f395ff3fe608060405234801561000f575f80fd5b506004361061004a575f3560e01c80635dbdd0ce1461004e5780637b70ef4e1461006a5780637dccab6614610086578063f5cc67a1146100a2575b5f80fd5b610068600480360381019061006391906102fc565b6100be565b005b610084600480360381019061007f91906102fc565b61012d565b005b6100a0600480360381019061009b91906102fc565b6101c1565b005b6100bc60048036038101906100b791906102fc565b61023b565b005b80825d3073ffffffffffffffffffffffffffffffffffffffff16637dccab6683836040518363ffffffff1660e01b81526004016100fc929190610349565b5f604051808303815f87803b158015610113575f80fd5b505af1158015610125573d5f803e3d5ffd5b505050505050565b80825d60405161013c906102b8565b604051809103905ff080158015610155573d5f803e3d5ffd5b5073ffffffffffffffffffffffffffffffffffffffff16637dccab6683836040518363ffffffff1660e01b8152600401610190929190610349565b5f604051808303815f87803b1580156101a7575f80fd5b505af11580156101b9573d5f803e3d5ffd5b505050505050565b5f825c90508181036101fe577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a1610236565b7f2fe0d3bd8bbb632441eefda46ec52f1ddda3a9e827d14c40f422037320f28bd98160405161022d9190610370565b60405180910390a15b505050565b5f81835d825c905081810361027b577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16102b3565b7f2fe0d3bd8bbb632441eefda46ec52f1ddda3a9e827d14c40f422037320f28bd9816040516102aa9190610370565b60405180910390a15b505050565b6101b28061038a83390190565b5f80fd5b5f819050919050565b6102db816102c9565b81146102e5575f80fd5b50565b5f813590506102f6816102d2565b92915050565b5f8060408385031215610312576103116102c5565b5b5f61031f858286016102e8565b9250506020610330858286016102e8565b9150509250929050565b610343816102c9565b82525050565b5f60408201905061035c5f83018561033a565b610369602083018461033a565b9392505050565b5f6020820190506103835f83018461033a565b9291505056fe6080604052348015600e575f80fd5b506101968061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c80637dccab661461002d575b5f80fd5b610047600480360381019061004291906100fa565b610049565b005b5f825c9050818103610086577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16100be565b7f2fe0d3bd8bbb632441eefda46ec52f1ddda3a9e827d14c40f422037320f28bd9816040516100b59190610147565b60405180910390a15b505050565b5f80fd5b5f819050919050565b6100d9816100c7565b81146100e3575f80fd5b50565b5f813590506100f4816100d0565b92915050565b5f80604083850312156101105761010f6100c3565b5b5f61011d858286016100e6565b925050602061012e858286016100e6565b9150509250929050565b610141816100c7565b82525050565b5f60208201905061015a5f830184610138565b9291505056fea2646970667358221220299e78f210bb50b25e55a7d3d761eb697663f501d6b877c5f17e651d5c7111fb64736f6c634300081a0033a2646970667358221220d99f2c2087ad3b0f2283262f95bb66f6becb8b8538af83c1a9a571716f0abb1b64736f6c634300081a0033 + gas 1000000 build # Create block to hold txTestTransientStorageContract transaction @@ -77,21 +130,20 @@ block_connect b01 # Check b01 is best block assert_best b01 -# Create transaction to execute checkTStore() method -transaction_build txTestTStore +# Create transaction to deploy TestTransientStorageOtherContract contract +transaction_build txTestTransientStorageOtherContract sender acc1 nonce 1 - contract txTestTransientStorageContract + receiverAddress 00 value 0 - data a36ca99d - gas 30000 + data 6080604052348015600e575f80fd5b506101968061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c80637dccab661461002d575b5f80fd5b610047600480360381019061004291906100fa565b610049565b005b5f825c9050818103610086577fd48fe2800bace8f5ca2450feacbd6efc681b1cd0115019bb49fa529b6171bf6760405160405180910390a16100be565b7f2fe0d3bd8bbb632441eefda46ec52f1ddda3a9e827d14c40f422037320f28bd9816040516100b59190610147565b60405180910390a15b505050565b5f80fd5b5f819050919050565b6100d9816100c7565b81146100e3575f80fd5b50565b5f813590506100f4816100d0565b92915050565b5f80604083850312156101105761010f6100c3565b5b5f61011d858286016100e6565b925050602061012e858286016100e6565b9150509250929050565b610141816100c7565b82525050565b5f60208201905061015a5f830184610138565b9291505056fea2646970667358221220299e78f210bb50b25e55a7d3d761eb697663f501d6b877c5f17e651d5c7111fb64736f6c634300081a0033 + gas 1000000 build -# Create block to hold txTestTStore transaction +# Create block to hold txTestTransientStorageOtherContract transaction block_build b02 parent b01 - transactions txTestTStore - gasLimit 6500000 + transactions txTestTransientStorageOtherContract build # Connect block @@ -100,25 +152,95 @@ block_connect b02 # Check b02 is best block assert_best b02 -# Create transaction to execute checkTLoad() method -transaction_build txTestTLoad +# Create transaction to execute checkTransientStorage(bytes32 key, bytes32 value) method +transaction_build txTestTransientStorageOpCodes sender acc1 nonce 2 contract txTestTransientStorageContract value 0 - data d5ad4e0b - gas 30000 + data f5cc67a131000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000 + gas 200000 build -# Create block to hold txTestTLoad transaction +# Create block to hold txTestTransientStorageOpCodes transaction block_build b03 parent b02 - transactions txTestTLoad - gasLimit 6500000 + transactions txTestTransientStorageOpCodes + gasLimit 350000 build # Connect block block_connect b03 # Check b02 is best block -assert_best b03 \ No newline at end of file +assert_best b03 + +# Create transaction to execute checkTransientStorage(bytes32 key, bytes32 value) method +transaction_build txTestTransientStorageOpCodesOtherValue + sender acc1 + nonce 3 + contract txTestTransientStorageContract + value 0 + data f5cc67a132000000000000000000000000000000000000000000000000000000000000003132333435000000000000000000000000000000000000000000000000000000 + gas 200000 + build + +# Create block to hold txTestTransientStorageOpCodesOtherValue transaction +block_build b04 + parent b03 + transactions txTestTransientStorageOpCodesOtherValue + gasLimit 350000 + build + +# Connect block +block_connect b04 + +# Check b02 is best block +assert_best b04 + +# Create transaction to execute checkIfNestedTransactionSameContractShareMemory(bytes32 key, bytes32 value) method +transaction_build txTestTransientStorageNestedTransactionShareMemory + sender acc1 + nonce 4 + contract txTestTransientStorageContract + value 0 + data f5cc67a131300000000000000000000000000000000000000000000000000000000000003230000000000000000000000000000000000000000000000000000000000000 + gas 200000 + build + +# Create block to hold txTestTransientStorageNestedTransactionShareMemory transaction +block_build b05 + parent b04 + transactions txTestTransientStorageNestedTransactionShareMemory + gasLimit 350000 + build + +# Connect block +block_connect b05 + +# Check b02 is best block +assert_best b05 + +# Create transaction to execute checkIfNestedTransactionFromOtherContractShareMemory(bytes32 key, bytes32 value) method +transaction_build txTestTransientStorageNestedTransactionOtherContractDoesntShareMemory + sender acc1 + nonce 5 + contract txTestTransientStorageContract + value 0 + data 7b70ef4e31300000000000000000000000000000000000000000000000000000000000003230000000000000000000000000000000000000000000000000000000000000 + gas 200000 + build + +# Create block to hold txTestTransientStorageNestedTransactionOtherContractDoesntShareMemory transaction +block_build b06 + parent b05 + transactions txTestTransientStorageNestedTransactionOtherContractDoesntShareMemory + gasLimit 350000 + build + +# Connect block +block_connect b06 + +# Check b02 is best block +assert_best b06 + From 81cef20c306cef724d7837f95b2be9ac3e34157a Mon Sep 17 00:00:00 2001 From: frederico leal Date: Mon, 21 Oct 2024 13:34:41 +0200 Subject: [PATCH 2/2] Refactor from code to get advantage from the MutableRepository - In order to get advantage from the whole flow of tracking, rollback and commits already present in the MutableRepository. We refactored a bit the logic of transient storage, now it's simpler. - The task isn't done yet, more tests needs to be added to validate that this is working properly. --- .../java/org/ethereum/core/Repository.java | 2 +- .../ethereum/core/TransientRepository.java | 40 +++++++++++ .../org/ethereum/db/MutableRepository.java | 66 ++++++++++++++++++- .../java/org/ethereum/vm/program/Program.java | 22 +------ .../java/org/ethereum/vm/program/Storage.java | 28 ++++++++ 5 files changed, 134 insertions(+), 24 deletions(-) create mode 100644 rskj-core/src/main/java/org/ethereum/core/TransientRepository.java diff --git a/rskj-core/src/main/java/org/ethereum/core/Repository.java b/rskj-core/src/main/java/org/ethereum/core/Repository.java index 0cf1bfa183d..38b10af2f68 100644 --- a/rskj-core/src/main/java/org/ethereum/core/Repository.java +++ b/rskj-core/src/main/java/org/ethereum/core/Repository.java @@ -27,7 +27,7 @@ import java.math.BigInteger; -public interface Repository extends RepositorySnapshot { +public interface Repository extends RepositorySnapshot, TransientRepository { Trie getTrie(); /** diff --git a/rskj-core/src/main/java/org/ethereum/core/TransientRepository.java b/rskj-core/src/main/java/org/ethereum/core/TransientRepository.java new file mode 100644 index 00000000000..eee1f29d4ab --- /dev/null +++ b/rskj-core/src/main/java/org/ethereum/core/TransientRepository.java @@ -0,0 +1,40 @@ +/* + * This file is part of RskJ + * Copyright (C) 2024 RSK Labs Ltd. + * (derived from ethereumJ library, Copyright (c) 2016 ) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package org.ethereum.core; + +import co.rsk.core.RskAddress; +import org.ethereum.vm.DataWord; + +import javax.annotation.Nullable; + +public interface TransientRepository { + + void addTransientStorageRow(RskAddress addr, DataWord key, DataWord value); + + void addTransientStorageBytes(RskAddress addr, DataWord key, byte[] value); + + void clearTransientStorage(); + + @Nullable + DataWord getTransientStorageValue(RskAddress addr, DataWord key); + + @Nullable + byte[] getTransientStorageBytes(RskAddress addr, DataWord key); +} \ No newline at end of file diff --git a/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java b/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java index 57279db7dfb..b3b00667002 100644 --- a/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java +++ b/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java @@ -30,16 +30,19 @@ import co.rsk.trie.Trie; import co.rsk.trie.TrieKeySlice; import co.rsk.trie.TrieStore; +import co.rsk.trie.TrieStoreImpl; import com.google.common.annotations.VisibleForTesting; import org.ethereum.core.AccountState; import org.ethereum.core.Repository; import org.ethereum.crypto.HashUtil; import org.ethereum.crypto.Keccak256Helper; +import org.ethereum.datasource.HashMapDB; import org.ethereum.vm.DataWord; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.math.BigInteger; import java.util.HashSet; import java.util.Iterator; @@ -54,21 +57,32 @@ public class MutableRepository implements Repository { private final TrieKeyMapper trieKeyMapper; private final MutableTrie mutableTrie; + private MutableTrie transientTrie; private final IReadWrittenKeysTracker tracker; public MutableRepository(TrieStore trieStore, Trie trie) { - this(new MutableTrieImpl(trieStore, trie)); + this(new MutableTrieImpl(trieStore, trie), new MutableTrieImpl(new TrieStoreImpl(new HashMapDB()), new Trie())); } public MutableRepository(MutableTrie mutableTrie) { + this(mutableTrie, new MutableTrieImpl(new TrieStoreImpl(new HashMapDB()), new Trie())); + } + + public MutableRepository(MutableTrie mutableTrie, IReadWrittenKeysTracker tracker) { + this(mutableTrie, new MutableTrieImpl(new TrieStoreImpl(new HashMapDB()), new Trie()), tracker); + } + + public MutableRepository(MutableTrie mutableTrie, MutableTrie transientTrie) { this.trieKeyMapper = new TrieKeyMapper(); this.mutableTrie = mutableTrie; + this.transientTrie = transientTrie; this.tracker = new DummyReadWrittenKeysTracker(); } - public MutableRepository(MutableTrie mutableTrie, IReadWrittenKeysTracker tracker) { + public MutableRepository(MutableTrie mutableTrie, MutableTrie transientTrie, IReadWrittenKeysTracker tracker) { this.trieKeyMapper = new TrieKeyMapper(); this.mutableTrie = mutableTrie; + this.transientTrie = transientTrie; this.tracker = tracker; } @@ -334,7 +348,7 @@ public synchronized Set getAccountsKeys() { // To start tracking, a new repository is created, with a MutableTrieCache in the middle @Override public synchronized Repository startTracking() { - return new MutableRepository(new MutableTrieCache(mutableTrie), tracker); + return new MutableRepository(new MutableTrieCache(mutableTrie), new MutableTrieCache(transientTrie), tracker); } @Override @@ -345,11 +359,13 @@ public void save() { @Override public synchronized void commit() { mutableTrie.commit(); + transientTrie.commit(); } @Override public synchronized void rollback() { mutableTrie.rollback(); + transientTrie.rollback(); } @Override @@ -413,4 +429,48 @@ private Optional internalGetValueHash(byte[] key) { tracker.addNewReadKey(new ByteArrayWrapper(key)); return mutableTrie.getValueHash(key); } + + @Override + public void addTransientStorageRow(RskAddress addr, DataWord key, DataWord value) { + addTransientStorageBytes(addr, key, value.getByteArrayForStorage()); + } + + @Override + public void addTransientStorageBytes(RskAddress addr, DataWord key, byte[] value) { + byte[] triekey = trieKeyMapper.getAccountStorageKey(addr, key); + + // Special case: if the value is an empty vector, we pass "null" which commands the trie to remove the item. + // Note that if the call comes from addStorageRow(), this method will already have replaced 0 by null, so the + // conversion here only applies if this is called directly. If suppose this only occurs in tests, but it can + // also occur in precompiled contracts that store data directly using this method. + if (value == null || value.length == 0) { + transientTrie.put(triekey, null); + } else { + transientTrie.put(triekey, value); + } + } + + @Override + public void clearTransientStorage() { + this.transientTrie = new MutableTrieImpl(new TrieStoreImpl(new HashMapDB()), new Trie()); + } + + @Nullable + @Override + public DataWord getTransientStorageValue(RskAddress addr, DataWord key) { + byte[] triekey = trieKeyMapper.getAccountStorageKey(addr, key); + byte[] value = transientTrie.get(triekey); + if (value == null) { + return null; + } + + return DataWord.valueOf(value); + } + + @Nullable + @Override + public byte[] getTransientStorageBytes(RskAddress addr, DataWord key) { + byte[] triekey = trieKeyMapper.getAccountStorageKey(addr, key); + return transientTrie.get(triekey); + } } diff --git a/rskj-core/src/main/java/org/ethereum/vm/program/Program.java b/rskj-core/src/main/java/org/ethereum/vm/program/Program.java index 73043c7a642..87443ab6b84 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/program/Program.java +++ b/rskj-core/src/main/java/org/ethereum/vm/program/Program.java @@ -40,7 +40,6 @@ import org.ethereum.core.SignatureCache; import org.ethereum.core.Transaction; import org.ethereum.crypto.HashUtil; -import org.ethereum.db.TransientStorageRepositoryCreator; import org.ethereum.util.ByteUtil; import org.ethereum.util.FastByteComparisons; import org.ethereum.vm.DataWord; @@ -117,7 +116,6 @@ public class Program { private final Stack stack; private final Memory memory; private final Storage storage; - private final Map transientStorages; private byte[] returnDataBuffer; private final ProgramResult result = new ProgramResult(); @@ -181,7 +179,6 @@ public Program( this.stack = setupProgramListener(new Stack()); this.stack.ensureCapacity(1024); // faster? this.storage = setupProgramListener(new Storage(programInvoke)); - this.transientStorages = new HashMap<>(); this.deletedAccountsInBlock = new HashSet<>(deletedAccounts); this.signatureCache = signatureCache; precompile(); @@ -457,16 +454,6 @@ public Repository getStorage() { return this.storage; } - public Repository getTransientStorage(RskAddress addr) { - Repository current = transientStorages.get(addr); - if(current == null) { - current = TransientStorageRepositoryCreator.createNewTransientStorage(); - transientStorages.put(addr, current); - } - return current; - } - - @SuppressWarnings("ThrowableResultOfMethodCallIgnored") public void createContract(DataWord value, DataWord memStart, DataWord memSize) { RskAddress senderAddress = new RskAddress(getOwnerAddress()); @@ -1000,9 +987,7 @@ private void storageSave(byte[] key, byte[] val) { } public void transientStorageSave(DataWord key, DataWord value) { - RskAddress addr = getOwnerRskAddress(); - Repository storage = getTransientStorage(addr); - storage.addStorageRow(addr, key, value); + getStorage().addTransientStorageRow(getOwnerRskAddress(), key, value); } private RskAddress getOwnerRskAddress() { @@ -1116,10 +1101,7 @@ public DataWord storageLoad(DataWord key) { } public DataWord transientStorageLoad(DataWord key) { - RskAddress addr = getOwnerRskAddress(); - Repository currentTransientStorage = getTransientStorage(addr); - - return currentTransientStorage.getStorageValue(addr, key); + return getStorage().getTransientStorageValue(getOwnerRskAddress(), key); } public DataWord getPrevHash() { diff --git a/rskj-core/src/main/java/org/ethereum/vm/program/Storage.java b/rskj-core/src/main/java/org/ethereum/vm/program/Storage.java index 836e4d4122a..99802802b9b 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/program/Storage.java +++ b/rskj-core/src/main/java/org/ethereum/vm/program/Storage.java @@ -30,6 +30,7 @@ import org.ethereum.vm.program.listener.ProgramListener; import org.ethereum.vm.program.listener.ProgramListenerAware; +import javax.annotation.Nullable; import java.math.BigInteger; import java.util.Iterator; import java.util.Set; @@ -223,4 +224,31 @@ public byte[] getRoot() { public void updateAccountState(RskAddress addr, AccountState accountState) { throw new UnsupportedOperationException(); } + + @Override + public void addTransientStorageRow(RskAddress addr, DataWord key, DataWord value) { + repository.addTransientStorageRow(addr, key, value); + } + + @Override + public void addTransientStorageBytes(RskAddress addr, DataWord key, byte[] value) { + repository.addTransientStorageBytes(addr, key, value); + } + + @Override + public void clearTransientStorage() { + repository.clearTransientStorage(); + } + + @Nullable + @Override + public DataWord getTransientStorageValue(RskAddress addr, DataWord key) { + return repository.getTransientStorageValue(addr, key); + } + + @Nullable + @Override + public byte[] getTransientStorageBytes(RskAddress addr, DataWord key) { + return repository.getTransientStorageBytes(addr, key); + } }