From c09145e490ee761629322bcc449cd5306c9bd8bd Mon Sep 17 00:00:00 2001 From: Ivaylo Kirilov Date: Sun, 29 Sep 2019 21:45:03 +0100 Subject: [PATCH] refactor-privacy-storage (#7) * refactor-privacy-storage Signed-off-by: Ivaylo Kirilov --- .../priv/PrivGetTransactionReceipt.java | 26 +-- .../priv/PrivGetTransactionReceiptTest.java | 11 +- ...acyPrecompiledContractIntegrationTest.java | 49 +++--- .../besu/ethereum/core/PrivacyParameters.java | 19 +-- .../privacy/PrivacyPrecompiledContract.java | 21 +-- .../privacy/PrivateStateKeyValueStorage.java | 74 -------- .../ethereum/privacy/PrivateStateStorage.java | 37 ---- .../privacy/PrivateTransactionHandler.java | 3 +- .../PrivateTransactionKeyValueStorage.java | 110 ------------ .../storage/PrivateStateKeyValueStorage.java | 160 ++++++++++++++++++ .../PrivateStateStorage.java} | 23 ++- .../storage/PrivateTransactionMetadata.java | 50 ++++++ .../ethereum/storage/StorageProvider.java | 5 +- .../keyvalue/KeyValueStorageProvider.java | 11 +- .../core/InMemoryStorageProvider.java | 11 +- .../PrivacyPrecompiledContractTest.java | 38 ++--- .../PrivateTransactionHandlerTest.java | 3 +- plugin-api/build.gradle | 2 +- .../services/storage/KeyValueStorage.java | 10 ++ .../RocksDBColumnarKeyValueStorage.java | 21 ++- .../unsegmented/RocksDBKeyValueStorage.java | 18 ++ .../RocksDBColumnarKeyValueStorageTest.java | 42 ++++- .../kvstore/InMemoryKeyValueStorage.java | 9 + .../LimitedInMemoryKeyValueStorage.java | 9 + .../kvstore/SegmentedKeyValueStorage.java | 5 +- .../SegmentedKeyValueStorageAdapter.java | 8 +- .../kvstore/AbstractKeyValueStorageTest.java | 17 ++ 27 files changed, 437 insertions(+), 355 deletions(-) delete mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateKeyValueStorage.java delete mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateStorage.java delete mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionKeyValueStorage.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateStateKeyValueStorage.java rename ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/{PrivateTransactionStorage.java => storage/PrivateStateStorage.java} (61%) create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateTransactionMetadata.java diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetTransactionReceipt.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetTransactionReceipt.java index a8486be0d4..b6fd802979 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetTransactionReceipt.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetTransactionReceipt.java @@ -93,8 +93,8 @@ public class PrivGetTransactionReceipt implements JsonRpcMethod { final long blockNumber = blockchain.getBlockchain().getBlockHeader(blockhash).get().getNumber(); final String publicKey = privacyParameters.getEnclavePublicKey(); - PrivateTransaction privateTransaction; - String privacyGroupId; + final PrivateTransaction privateTransaction; + final String privacyGroupId; try { final ReceiveResponse receiveResponse = getReceiveResponseFromEnclave(transaction, publicKey); LOG.trace("Received transaction information from Enclave"); @@ -123,34 +123,34 @@ public class PrivGetTransactionReceipt implements JsonRpcMethod { LOG.trace("Calculated contractAddress: {}", contractAddress); - BytesValue rlpEncoded = RLP.encode(privateTransaction::writeTo); - Bytes32 txHash = org.hyperledger.besu.crypto.Hash.keccak256(rlpEncoded); + final BytesValue rlpEncoded = RLP.encode(privateTransaction::writeTo); + final Bytes32 txHash = org.hyperledger.besu.crypto.Hash.keccak256(rlpEncoded); LOG.trace("Calculated private transaction hash: {}", txHash); - List events = + final List transactionLogs = privacyParameters - .getPrivateTransactionStorage() - .getEvents(txHash) + .getPrivateStateStorage() + .getTransactionLogs(txHash) .orElse(Collections.emptyList()); LOG.trace("Processed private transaction events"); - BytesValue output = + final BytesValue transactionOutput = privacyParameters - .getPrivateTransactionStorage() - .getOutput(txHash) + .getPrivateStateStorage() + .getTransactionOutput(txHash) .orElse(BytesValue.wrap(new byte[0])); LOG.trace("Processed private transaction output"); - PrivateTransactionReceiptResult result = + final PrivateTransactionReceiptResult result = new PrivateTransactionReceiptResult( contractAddress, privateTransaction.getSender().toString(), privateTransaction.getTo().map(Address::toString).orElse(null), - events, - output, + transactionLogs, + transactionOutput, blockhash, transactionHash, blockNumber, diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetTransactionReceiptTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetTransactionReceiptTest.java index 7a4a7ba67e..3d928bbce1 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetTransactionReceiptTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetTransactionReceiptTest.java @@ -42,8 +42,8 @@ import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.Wei; import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; -import org.hyperledger.besu.ethereum.privacy.PrivateTransactionStorage; import org.hyperledger.besu.ethereum.privacy.Restriction; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; import org.hyperledger.besu.ethereum.rlp.RLP; import org.hyperledger.besu.util.bytes.Bytes32; import org.hyperledger.besu.util.bytes.BytesValue; @@ -154,11 +154,10 @@ public class PrivGetTransactionReceiptTest { final BlockHeader mockBlockHeader = mock(BlockHeader.class); when(blockchain.getBlockHeader(any(Hash.class))).thenReturn(Optional.of(mockBlockHeader)); when(mockBlockHeader.getNumber()).thenReturn(0L); - final PrivateTransactionStorage privateTransactionStorage = - mock(PrivateTransactionStorage.class); - when(privacyParameters.getPrivateTransactionStorage()).thenReturn(privateTransactionStorage); - when(privateTransactionStorage.getEvents(any(Bytes32.class))).thenReturn(Optional.empty()); - when(privateTransactionStorage.getOutput(any(Bytes32.class))).thenReturn(Optional.empty()); + final PrivateStateStorage privateStateStorage = mock(PrivateStateStorage.class); + when(privacyParameters.getPrivateStateStorage()).thenReturn(privateStateStorage); + when(privateStateStorage.getTransactionLogs(any(Bytes32.class))).thenReturn(Optional.empty()); + when(privateStateStorage.getTransactionOutput(any(Bytes32.class))).thenReturn(Optional.empty()); } @Test diff --git a/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java b/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java index 93e6c48bdc..b154a4b74e 100644 --- a/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java +++ b/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java @@ -32,10 +32,9 @@ import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; import org.hyperledger.besu.ethereum.core.WorldUpdater; import org.hyperledger.besu.ethereum.mainnet.SpuriousDragonGasCalculator; -import org.hyperledger.besu.ethereum.privacy.PrivateStateStorage; import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor; -import org.hyperledger.besu.ethereum.privacy.PrivateTransactionStorage; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; import org.hyperledger.besu.ethereum.vm.BlockHashLookup; import org.hyperledger.besu.ethereum.vm.MessageFrame; import org.hyperledger.besu.ethereum.vm.OperationTracer; @@ -85,15 +84,13 @@ public class PrivacyPrecompiledContractIntegrationTest { private static OrionTestHarness testHarness; private static WorldStateArchive worldStateArchive; - private static PrivateTransactionStorage privateTransactionStorage; - private static PrivateTransactionStorage.Updater updater; private static PrivateStateStorage privateStateStorage; private static PrivateStateStorage.Updater storageUpdater; private PrivateTransactionProcessor mockPrivateTxProcessor() { - PrivateTransactionProcessor mockPrivateTransactionProcessor = + final PrivateTransactionProcessor mockPrivateTransactionProcessor = mock(PrivateTransactionProcessor.class); - PrivateTransactionProcessor.Result result = + final PrivateTransactionProcessor.Result result = PrivateTransactionProcessor.Result.successful( null, 0, BytesValue.fromHexString(DEFAULT_OUTPUT), null); when(mockPrivateTransactionProcessor.processTransaction( @@ -126,19 +123,18 @@ public class PrivacyPrecompiledContractIntegrationTest { messageFrame = mock(MessageFrame.class); worldStateArchive = mock(WorldStateArchive.class); - MutableWorldState mutableWorldState = mock(MutableWorldState.class); + final MutableWorldState mutableWorldState = mock(MutableWorldState.class); when(mutableWorldState.updater()).thenReturn(mock(WorldUpdater.class)); when(worldStateArchive.getMutable()).thenReturn(mutableWorldState); when(worldStateArchive.getMutable(any())).thenReturn(Optional.of(mutableWorldState)); - privateTransactionStorage = mock(PrivateTransactionStorage.class); - updater = mock(PrivateTransactionStorage.Updater.class); - when(updater.putTransactionLogs(nullable(Bytes32.class), any())).thenReturn(updater); - when(updater.putTransactionResult(nullable(Bytes32.class), any())).thenReturn(updater); - when(privateTransactionStorage.updater()).thenReturn(updater); privateStateStorage = mock(PrivateStateStorage.class); storageUpdater = mock(PrivateStateStorage.Updater.class); - when(storageUpdater.putPrivateAccountState(nullable(Bytes32.class), any())) + when(storageUpdater.putLatestStateRoot(nullable(Bytes32.class), any())) + .thenReturn(storageUpdater); + when(storageUpdater.putTransactionLogs(nullable(Bytes32.class), any())) + .thenReturn(storageUpdater); + when(storageUpdater.putTransactionResult(nullable(Bytes32.class), any())) .thenReturn(storageUpdater); when(privateStateStorage.updater()).thenReturn(storageUpdater); } @@ -154,26 +150,25 @@ public class PrivacyPrecompiledContractIntegrationTest { } @Test - public void testSendAndReceive() throws Exception { - List publicKeys = testHarness.getPublicKeys(); + public void testSendAndReceive() { + final List publicKeys = testHarness.getPublicKeys(); - String s = new String(VALID_PRIVATE_TRANSACTION_RLP_BASE64, UTF_8); - SendRequest sc = + final String s = new String(VALID_PRIVATE_TRANSACTION_RLP_BASE64, UTF_8); + final SendRequest sc = new SendRequestLegacy(s, publicKeys.get(0), Lists.newArrayList(publicKeys.get(0))); - SendResponse sr = enclave.send(sc); + final SendResponse sr = enclave.send(sc); - PrivacyPrecompiledContract privacyPrecompiledContract = + final PrivacyPrecompiledContract privacyPrecompiledContract = new PrivacyPrecompiledContract( new SpuriousDragonGasCalculator(), publicKeys.get(0), enclave, worldStateArchive, - privateTransactionStorage, privateStateStorage); privacyPrecompiledContract.setPrivateTransactionProcessor(mockPrivateTxProcessor()); - BytesValue actual = + final BytesValue actual = privacyPrecompiledContract.compute(BytesValues.fromBase64(sr.getKey()), messageFrame); assertThat(actual).isEqualTo(BytesValue.fromHexString(DEFAULT_OUTPUT)); @@ -181,11 +176,11 @@ public class PrivacyPrecompiledContractIntegrationTest { @Test public void testNoPrivateKeyError() throws RuntimeException { - List publicKeys = testHarness.getPublicKeys(); + final List publicKeys = testHarness.getPublicKeys(); publicKeys.add("noPrivateKey"); - String s = new String(VALID_PRIVATE_TRANSACTION_RLP_BASE64, UTF_8); - SendRequest sc = new SendRequestLegacy(s, publicKeys.get(0), publicKeys); + final String s = new String(VALID_PRIVATE_TRANSACTION_RLP_BASE64, UTF_8); + final SendRequest sc = new SendRequestLegacy(s, publicKeys.get(0), publicKeys); final Throwable thrown = catchThrowable(() -> enclave.send(sc)); @@ -194,11 +189,11 @@ public class PrivacyPrecompiledContractIntegrationTest { @Test public void testWrongPrivateKeyError() throws RuntimeException { - List publicKeys = testHarness.getPublicKeys(); + final List publicKeys = testHarness.getPublicKeys(); publicKeys.add("noPrivateKenoPrivateKenoPrivateKenoPrivateK"); - String s = new String(VALID_PRIVATE_TRANSACTION_RLP_BASE64, UTF_8); - SendRequest sc = new SendRequestLegacy(s, publicKeys.get(0), publicKeys); + final String s = new String(VALID_PRIVATE_TRANSACTION_RLP_BASE64, UTF_8); + final SendRequest sc = new SendRequestLegacy(s, publicKeys.get(0), publicKeys); final Throwable thrown = catchThrowable(() -> enclave.send(sc)); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/PrivacyParameters.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/PrivacyParameters.java index 5fdab88414..8105256d08 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/PrivacyParameters.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/PrivacyParameters.java @@ -18,8 +18,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import org.hyperledger.besu.crypto.SECP256K1; import org.hyperledger.besu.crypto.SECP256K1.KeyPair; -import org.hyperledger.besu.ethereum.privacy.PrivateStateStorage; -import org.hyperledger.besu.ethereum.privacy.PrivateTransactionStorage; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; import org.hyperledger.besu.ethereum.storage.StorageProvider; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage; @@ -44,10 +43,9 @@ public class PrivacyParameters { private String enclavePublicKey; private File enclavePublicKeyFile; private Optional signingKeyPair = Optional.empty(); + private WorldStateArchive privateWorldStateArchive; private StorageProvider privateStorageProvider; - - private PrivateTransactionStorage privateTransactionStorage; private PrivateStateStorage privateStateStorage; public Integer getPrivacyAddress() { @@ -114,15 +112,6 @@ public class PrivacyParameters { this.privateStorageProvider = privateStorageProvider; } - public PrivateTransactionStorage getPrivateTransactionStorage() { - return privateTransactionStorage; - } - - public void setPrivateTransactionStorage( - final PrivateTransactionStorage privateTransactionStorage) { - this.privateTransactionStorage = privateTransactionStorage; - } - public PrivateStateStorage getPrivateStateStorage() { return privateStateStorage; } @@ -181,16 +170,14 @@ public class PrivacyParameters { final WorldStateArchive privateWorldStateArchive = new WorldStateArchive(privateWorldStateStorage, privatePreimageStorage); - final PrivateTransactionStorage privateTransactionStorage = - storageProvider.createPrivateTransactionStorage(); final PrivateStateStorage privateStateStorage = storageProvider.createPrivateStateStorage(); config.setPrivateWorldStateArchive(privateWorldStateArchive); config.setEnclavePublicKey(enclavePublicKey); config.setEnclavePublicKeyFile(enclavePublicKeyFile); config.setPrivateStorageProvider(storageProvider); - config.setPrivateTransactionStorage(privateTransactionStorage); config.setPrivateStateStorage(privateStateStorage); + if (privateKeyPath != null) { config.setSigningKeyPair(KeyPair.load(privateKeyPath.toFile())); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java index 1eb8ab7549..3cb7375fcf 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java @@ -27,10 +27,9 @@ import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.core.WorldUpdater; import org.hyperledger.besu.ethereum.debug.TraceOptions; import org.hyperledger.besu.ethereum.mainnet.AbstractPrecompiledContract; -import org.hyperledger.besu.ethereum.privacy.PrivateStateStorage; import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor; -import org.hyperledger.besu.ethereum.privacy.PrivateTransactionStorage; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; import org.hyperledger.besu.ethereum.rlp.RLP; import org.hyperledger.besu.ethereum.trie.MerklePatriciaTrie; @@ -49,7 +48,6 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract { private final Enclave enclave; private final String enclavePublicKey; private final WorldStateArchive privateWorldStateArchive; - private final PrivateTransactionStorage privateTransactionStorage; private final PrivateStateStorage privateStateStorage; private PrivateTransactionProcessor privateTransactionProcessor; private static final Hash EMPTY_ROOT_HASH = Hash.wrap(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH); @@ -63,7 +61,6 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract { privacyParameters.getEnclavePublicKey(), new Enclave(privacyParameters.getEnclaveUri()), privacyParameters.getPrivateWorldStateArchive(), - privacyParameters.getPrivateTransactionStorage(), privacyParameters.getPrivateStateStorage()); } @@ -72,13 +69,11 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract { final String publicKey, final Enclave enclave, final WorldStateArchive worldStateArchive, - final PrivateTransactionStorage privateTransactionStorage, final PrivateStateStorage privateStateStorage) { super("Privacy", gasCalculator); this.enclave = enclave; this.enclavePublicKey = publicKey; this.privateWorldStateArchive = worldStateArchive; - this.privateTransactionStorage = privateTransactionStorage; this.privateStateStorage = privateStateStorage; } @@ -97,7 +92,7 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract { final String key = BytesValues.asBase64String(input); final ReceiveRequest receiveRequest = new ReceiveRequest(key, enclavePublicKey); - ReceiveResponse receiveResponse; + final ReceiveResponse receiveResponse; try { receiveResponse = enclave.receive(receiveRequest); } catch (Exception e) { @@ -116,7 +111,7 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract { // get the last world state root hash - or create a new one final Hash lastRootHash = - privateStateStorage.getPrivateAccountState(privacyGroupId).orElse(EMPTY_ROOT_HASH); + privateStateStorage.getLatestStateRoot(privacyGroupId).orElse(EMPTY_ROOT_HASH); final MutableWorldState disposablePrivateState = privateWorldStateArchive.getMutable(lastRootHash).get(); @@ -150,17 +145,15 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract { disposablePrivateState.persist(); final PrivateStateStorage.Updater privateStateUpdater = privateStateStorage.updater(); - privateStateUpdater.putPrivateAccountState(privacyGroupId, disposablePrivateState.rootHash()); - privateStateUpdater.commit(); + privateStateUpdater.putLatestStateRoot(privacyGroupId, disposablePrivateState.rootHash()); final Bytes32 txHash = keccak256(RLP.encode(privateTransaction::writeTo)); - final PrivateTransactionStorage.Updater privateUpdater = privateTransactionStorage.updater(); final LogSeries logs = result.getLogs(); if (!logs.isEmpty()) { - privateUpdater.putTransactionLogs(txHash, result.getLogs()); + privateStateUpdater.putTransactionLogs(txHash, result.getLogs()); } - privateUpdater.putTransactionResult(txHash, result.getOutput()); - privateUpdater.commit(); + privateStateUpdater.putTransactionResult(txHash, result.getOutput()); + privateStateUpdater.commit(); } return result.getOutput(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateKeyValueStorage.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateKeyValueStorage.java deleted file mode 100644 index 014a59c6a1..0000000000 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateKeyValueStorage.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright ConsenSys AG. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.ethereum.privacy; - -import org.hyperledger.besu.ethereum.core.Hash; -import org.hyperledger.besu.plugin.services.storage.KeyValueStorage; -import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction; -import org.hyperledger.besu.util.bytes.Bytes32; -import org.hyperledger.besu.util.bytes.BytesValue; - -import java.util.Optional; - -public class PrivateStateKeyValueStorage implements PrivateStateStorage { - - private final KeyValueStorage keyValueStorage; - - public PrivateStateKeyValueStorage(final KeyValueStorage keyValueStorage) { - this.keyValueStorage = keyValueStorage; - } - - @Override - public Optional getPrivateAccountState(final BytesValue privacyId) { - final byte[] id = privacyId.getArrayUnsafe(); - - if (keyValueStorage.get(id).isPresent()) { - return Optional.of(Hash.wrap(Bytes32.wrap(keyValueStorage.get(id).get()))); - } else { - return Optional.empty(); - } - } - - @Override - public boolean isWorldStateAvailable(final Bytes32 rootHash) { - return false; - } - - @Override - public PrivateStateStorage.Updater updater() { - return new PrivateStateKeyValueStorage.Updater(keyValueStorage.startTransaction()); - } - - public static class Updater implements PrivateStateStorage.Updater { - - private final KeyValueStorageTransaction transaction; - - private Updater(final KeyValueStorageTransaction transaction) { - this.transaction = transaction; - } - - @Override - public PrivateStateStorage.Updater putPrivateAccountState( - final BytesValue privacyId, final Hash privateStateHash) { - transaction.put(privacyId.getArrayUnsafe(), privateStateHash.extractArray()); - return this; - } - - @Override - public void commit() { - transaction.commit(); - } - } -} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateStorage.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateStorage.java deleted file mode 100644 index 9f342985c1..0000000000 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateStorage.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright ConsenSys AG. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.ethereum.privacy; - -import org.hyperledger.besu.ethereum.core.Hash; -import org.hyperledger.besu.util.bytes.Bytes32; -import org.hyperledger.besu.util.bytes.BytesValue; - -import java.util.Optional; - -public interface PrivateStateStorage { - - Optional getPrivateAccountState(BytesValue privacyId); - - boolean isWorldStateAvailable(Bytes32 rootHash); - - PrivateStateStorage.Updater updater(); - - interface Updater { - - PrivateStateStorage.Updater putPrivateAccountState(BytesValue privacyId, Hash privateStateHash); - - void commit(); - } -} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionHandler.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionHandler.java index 456186ec85..9ad354844b 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionHandler.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionHandler.java @@ -28,6 +28,7 @@ import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.mainnet.TransactionValidator; import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import org.hyperledger.besu.ethereum.privacy.markertransaction.PrivateMarkerTransactionFactory; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.util.bytes.BytesValues; @@ -152,7 +153,7 @@ public class PrivateTransactionHandler { public long getSenderNonce(final Address sender, final String privacyGroupId) { return privateStateStorage - .getPrivateAccountState(BytesValues.fromBase64(privacyGroupId)) + .getLatestStateRoot(BytesValues.fromBase64(privacyGroupId)) .map( lastRootHash -> privateWorldStateArchive diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionKeyValueStorage.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionKeyValueStorage.java deleted file mode 100644 index 58a00393c9..0000000000 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionKeyValueStorage.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright ConsenSys AG. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.ethereum.privacy; - -import static java.nio.charset.StandardCharsets.UTF_8; - -import org.hyperledger.besu.ethereum.core.Log; -import org.hyperledger.besu.ethereum.core.LogSeries; -import org.hyperledger.besu.ethereum.rlp.RLP; -import org.hyperledger.besu.plugin.services.storage.KeyValueStorage; -import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction; -import org.hyperledger.besu.util.bytes.Bytes32; -import org.hyperledger.besu.util.bytes.BytesValue; -import org.hyperledger.besu.util.bytes.BytesValues; - -import java.util.List; -import java.util.Optional; - -public class PrivateTransactionKeyValueStorage implements PrivateTransactionStorage { - - private final KeyValueStorage keyValueStorage; - - private static final BytesValue EVENTS_KEY_SUFFIX = BytesValue.of("EVENTS".getBytes(UTF_8)); - private static final BytesValue OUTPUT_KEY_SUFFIX = BytesValue.of("OUTPUT".getBytes(UTF_8)); - - public PrivateTransactionKeyValueStorage(final KeyValueStorage keyValueStorage) { - this.keyValueStorage = keyValueStorage; - } - - @Override - public Optional> getEvents(final Bytes32 transactionHash) { - return get(transactionHash, EVENTS_KEY_SUFFIX).map(this::rlpDecodeLog); - } - - @Override - public Optional getOutput(final Bytes32 transactionHash) { - return get(transactionHash, OUTPUT_KEY_SUFFIX); - } - - @Override - public boolean isPrivateStateAvailable(final Bytes32 transactionHash) { - return get(transactionHash, EVENTS_KEY_SUFFIX).isPresent() - || get(transactionHash, OUTPUT_KEY_SUFFIX).isPresent(); - } - - private List rlpDecodeLog(final BytesValue bytes) { - return RLP.input(bytes).readList(Log::readFrom); - } - - private Optional get(final BytesValue key, final BytesValue keySuffix) { - return keyValueStorage - .get(BytesValues.concatenate(key, keySuffix).getArrayUnsafe()) - .map(BytesValue::wrap); - } - - @Override - public Updater updater() { - return new Updater(keyValueStorage.startTransaction()); - } - - public static class Updater implements PrivateTransactionStorage.Updater { - - private final KeyValueStorageTransaction transaction; - - private Updater(final KeyValueStorageTransaction transaction) { - this.transaction = transaction; - } - - @Override - public PrivateTransactionStorage.Updater putTransactionLogs( - final Bytes32 transactionHash, final LogSeries logs) { - set(transactionHash, EVENTS_KEY_SUFFIX, RLP.encode(logs::writeTo)); - return this; - } - - @Override - public PrivateTransactionStorage.Updater putTransactionResult( - final Bytes32 transactionHash, final BytesValue events) { - set(transactionHash, OUTPUT_KEY_SUFFIX, events); - return this; - } - - private void set(final BytesValue key, final BytesValue keySuffix, final BytesValue value) { - transaction.put( - BytesValues.concatenate(key, keySuffix).getArrayUnsafe(), value.getArrayUnsafe()); - } - - @Override - public void commit() { - transaction.commit(); - } - - @Override - public void rollback() { - transaction.rollback(); - } - } -} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateStateKeyValueStorage.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateStateKeyValueStorage.java new file mode 100644 index 0000000000..2dc93fa146 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateStateKeyValueStorage.java @@ -0,0 +1,160 @@ +/* + * Copyright ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.privacy.storage; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.core.Log; +import org.hyperledger.besu.ethereum.core.LogSeries; +import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; +import org.hyperledger.besu.ethereum.rlp.RLP; +import org.hyperledger.besu.plugin.services.storage.KeyValueStorage; +import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction; +import org.hyperledger.besu.util.bytes.Bytes32; +import org.hyperledger.besu.util.bytes.BytesValue; +import org.hyperledger.besu.util.bytes.BytesValues; + +import java.util.List; +import java.util.Optional; + +public class PrivateStateKeyValueStorage implements PrivateStateStorage { + + @Deprecated + private static final BytesValue EVENTS_KEY_SUFFIX = BytesValue.of("EVENTS".getBytes(UTF_8)); + + private static final BytesValue LOGS_KEY_SUFFIX = BytesValue.of("LOGS".getBytes(UTF_8)); + private static final BytesValue OUTPUT_KEY_SUFFIX = BytesValue.of("OUTPUT".getBytes(UTF_8)); + private static final BytesValue METADATA_KEY_SUFFIX = BytesValue.of("METADATA".getBytes(UTF_8)); + + private final KeyValueStorage keyValueStorage; + + public PrivateStateKeyValueStorage(final KeyValueStorage keyValueStorage) { + this.keyValueStorage = keyValueStorage; + } + + @Override + public Optional getLatestStateRoot(final BytesValue privacyId) { + final byte[] id = privacyId.getArrayUnsafe(); + + if (keyValueStorage.get(id).isPresent()) { + return Optional.of(Hash.wrap(Bytes32.wrap(keyValueStorage.get(id).get()))); + } else { + return Optional.empty(); + } + } + + @Override + public Optional> getTransactionLogs(final Bytes32 transactionHash) { + final Optional> logs = get(transactionHash, LOGS_KEY_SUFFIX).map(this::rlpDecodeLog); + if (logs.isEmpty()) { + return get(transactionHash, EVENTS_KEY_SUFFIX).map(this::rlpDecodeLog); + } + return logs; + } + + @Override + public Optional getTransactionOutput(final Bytes32 transactionHash) { + return get(transactionHash, OUTPUT_KEY_SUFFIX); + } + + @Override + public Optional getTransactionMetadata( + final Bytes32 blockHash, final Bytes32 transactionHash) { + return get(BytesValues.concatenate(blockHash, transactionHash), METADATA_KEY_SUFFIX) + .map( + bytesValue -> + PrivateTransactionMetadata.readFrom(new BytesValueRLPInput(bytesValue, false))); + } + + @Override + public boolean isPrivateStateAvailable(final Bytes32 transactionHash) { + return false; + } + + @Override + public boolean isWorldStateAvailable(final Bytes32 rootHash) { + return false; + } + + private Optional get(final BytesValue key, final BytesValue keySuffix) { + return keyValueStorage + .get(BytesValues.concatenate(key, keySuffix).getArrayUnsafe()) + .map(BytesValue::wrap); + } + + private List rlpDecodeLog(final BytesValue bytes) { + return RLP.input(bytes).readList(Log::readFrom); + } + + @Override + public PrivateStateStorage.Updater updater() { + return new PrivateStateKeyValueStorage.Updater(keyValueStorage.startTransaction()); + } + + public static class Updater implements PrivateStateStorage.Updater { + + private final KeyValueStorageTransaction transaction; + + private Updater(final KeyValueStorageTransaction transaction) { + this.transaction = transaction; + } + + @Override + public Updater putLatestStateRoot(final BytesValue privacyId, final Hash privateStateHash) { + transaction.put(privacyId.getArrayUnsafe(), privateStateHash.extractArray()); + return this; + } + + @Override + public Updater putTransactionLogs(final Bytes32 transactionHash, final LogSeries logs) { + set(transactionHash, LOGS_KEY_SUFFIX, RLP.encode(logs::writeTo)); + return this; + } + + @Override + public Updater putTransactionResult(final Bytes32 transactionHash, final BytesValue events) { + set(transactionHash, OUTPUT_KEY_SUFFIX, events); + return this; + } + + @Override + public Updater putTransactionMetadata( + final Bytes32 blockHash, + final Bytes32 transactionHash, + final PrivateTransactionMetadata metadata) { + set( + BytesValues.concatenate(blockHash, transactionHash), + METADATA_KEY_SUFFIX, + RLP.encode(metadata::writeTo)); + return this; + } + + @Override + public void commit() { + transaction.commit(); + } + + @Override + public void rollback() { + transaction.rollback(); + } + + private void set(final BytesValue key, final BytesValue keySuffix, final BytesValue value) { + transaction.put( + BytesValues.concatenate(key, keySuffix).getArrayUnsafe(), value.getArrayUnsafe()); + } + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionStorage.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateStateStorage.java similarity index 61% rename from ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionStorage.java rename to ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateStateStorage.java index 942206878b..2a9dba6179 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionStorage.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateStateStorage.java @@ -12,8 +12,9 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.ethereum.privacy; +package org.hyperledger.besu.ethereum.privacy.storage; +import org.hyperledger.besu.ethereum.core.Hash; import org.hyperledger.besu.ethereum.core.Log; import org.hyperledger.besu.ethereum.core.LogSeries; import org.hyperledger.besu.util.bytes.Bytes32; @@ -22,22 +23,36 @@ import org.hyperledger.besu.util.bytes.BytesValue; import java.util.List; import java.util.Optional; -public interface PrivateTransactionStorage { +public interface PrivateStateStorage { - Optional> getEvents(Bytes32 transactionHash); + @Deprecated + Optional getLatestStateRoot(BytesValue privacyId); - Optional getOutput(Bytes32 transactionHash); + Optional> getTransactionLogs(Bytes32 transactionHash); + + Optional getTransactionOutput(Bytes32 transactionHash); + + Optional getTransactionMetadata( + Bytes32 blockHash, Bytes32 transactionHash); boolean isPrivateStateAvailable(Bytes32 transactionHash); + boolean isWorldStateAvailable(Bytes32 rootHash); + Updater updater(); interface Updater { + @Deprecated + Updater putLatestStateRoot(BytesValue privacyId, Hash privateStateHash); + Updater putTransactionLogs(Bytes32 transactionHash, LogSeries logs); Updater putTransactionResult(Bytes32 transactionHash, BytesValue events); + Updater putTransactionMetadata( + Bytes32 blockHash, Bytes32 transactionHash, PrivateTransactionMetadata metadata); + void commit(); void rollback(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateTransactionMetadata.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateTransactionMetadata.java new file mode 100644 index 0000000000..cad47508ef --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateTransactionMetadata.java @@ -0,0 +1,50 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.privacy.storage; + +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.rlp.RLPInput; +import org.hyperledger.besu.ethereum.rlp.RLPOutput; + +/** Mined private transaction metadata. */ +public class PrivateTransactionMetadata { + private final Hash stateRoot; + + public PrivateTransactionMetadata(final Hash stateRoot) { + this.stateRoot = stateRoot; + } + + public Hash getStateRoot() { + return stateRoot; + } + + public void writeTo(final RLPOutput out) { + out.startList(); + + out.writeBytesValue(stateRoot); + + out.endList(); + } + + public static PrivateTransactionMetadata readFrom(final RLPInput input) { + input.enterList(); + + final PrivateTransactionMetadata privateTransactionMetadata = + new PrivateTransactionMetadata(Hash.wrap(input.readBytes32())); + + input.leaveList(); + return privateTransactionMetadata; + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/StorageProvider.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/StorageProvider.java index 583f190a0f..2c3c26ac7c 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/StorageProvider.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/StorageProvider.java @@ -16,8 +16,7 @@ package org.hyperledger.besu.ethereum.storage; import org.hyperledger.besu.ethereum.chain.BlockchainStorage; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; -import org.hyperledger.besu.ethereum.privacy.PrivateStateStorage; -import org.hyperledger.besu.ethereum.privacy.PrivateTransactionStorage; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage; import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage; import org.hyperledger.besu.plugin.services.storage.KeyValueStorage; @@ -32,8 +31,6 @@ public interface StorageProvider extends Closeable { WorldStatePreimageStorage createWorldStatePreimageStorage(); - PrivateTransactionStorage createPrivateTransactionStorage(); - PrivateStateStorage createPrivateStateStorage(); KeyValueStorage createPruningStorage(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueStorageProvider.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueStorageProvider.java index 978ca3745d..60ebaa567e 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueStorageProvider.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueStorageProvider.java @@ -17,10 +17,8 @@ package org.hyperledger.besu.ethereum.storage.keyvalue; import org.hyperledger.besu.ethereum.chain.BlockchainStorage; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions; -import org.hyperledger.besu.ethereum.privacy.PrivateStateKeyValueStorage; -import org.hyperledger.besu.ethereum.privacy.PrivateStateStorage; -import org.hyperledger.besu.ethereum.privacy.PrivateTransactionKeyValueStorage; -import org.hyperledger.besu.ethereum.privacy.PrivateTransactionStorage; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateKeyValueStorage; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; import org.hyperledger.besu.ethereum.storage.StorageProvider; import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage; import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage; @@ -71,11 +69,6 @@ public class KeyValueStorageProvider implements StorageProvider { return new WorldStatePreimageKeyValueStorage(worldStatePreimageStorage); } - @Override - public PrivateTransactionStorage createPrivateTransactionStorage() { - return new PrivateTransactionKeyValueStorage(privateTransactionStorage); - } - @Override public PrivateStateStorage createPrivateStateStorage() { return new PrivateStateKeyValueStorage(privateStateStorage); diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryStorageProvider.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryStorageProvider.java index 045b34f816..104c12841f 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryStorageProvider.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryStorageProvider.java @@ -20,10 +20,8 @@ import org.hyperledger.besu.ethereum.chain.MutableBlockchain; import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions; -import org.hyperledger.besu.ethereum.privacy.PrivateStateKeyValueStorage; -import org.hyperledger.besu.ethereum.privacy.PrivateStateStorage; -import org.hyperledger.besu.ethereum.privacy.PrivateTransactionKeyValueStorage; -import org.hyperledger.besu.ethereum.privacy.PrivateTransactionStorage; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateKeyValueStorage; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; import org.hyperledger.besu.ethereum.storage.StorageProvider; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage; import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStateKeyValueStorage; @@ -79,11 +77,6 @@ public class InMemoryStorageProvider implements StorageProvider { return new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage()); } - @Override - public PrivateTransactionStorage createPrivateTransactionStorage() { - return new PrivateTransactionKeyValueStorage(new InMemoryKeyValueStorage()); - } - @Override public PrivateStateStorage createPrivateStateStorage() { return new PrivateStateKeyValueStorage(new InMemoryKeyValueStorage()); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractTest.java index a2b381891a..bcfd22b4e0 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractTest.java @@ -32,10 +32,9 @@ import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; import org.hyperledger.besu.ethereum.core.WorldUpdater; import org.hyperledger.besu.ethereum.mainnet.SpuriousDragonGasCalculator; -import org.hyperledger.besu.ethereum.privacy.PrivateStateStorage; import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor; -import org.hyperledger.besu.ethereum.privacy.PrivateTransactionStorage; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; import org.hyperledger.besu.ethereum.vm.BlockHashLookup; import org.hyperledger.besu.ethereum.vm.MessageFrame; import org.hyperledger.besu.ethereum.vm.OperationTracer; @@ -79,17 +78,17 @@ public class PrivacyPrecompiledContractTest { .extractArray()); private Enclave mockEnclave() { - Enclave mockEnclave = mock(Enclave.class); - ReceiveResponse response = new ReceiveResponse(VALID_PRIVATE_TRANSACTION_RLP_BASE64, ""); + final Enclave mockEnclave = mock(Enclave.class); + final ReceiveResponse response = new ReceiveResponse(VALID_PRIVATE_TRANSACTION_RLP_BASE64, ""); when(mockEnclave.receive(any(ReceiveRequest.class))).thenReturn(response); return mockEnclave; } private PrivateTransactionProcessor mockPrivateTxProcessor() { - PrivateTransactionProcessor mockPrivateTransactionProcessor = + final PrivateTransactionProcessor mockPrivateTransactionProcessor = mock(PrivateTransactionProcessor.class); - LogSeries logs = mock(LogSeries.class); - PrivateTransactionProcessor.Result result = + final LogSeries logs = mock(LogSeries.class); + final PrivateTransactionProcessor.Result result = PrivateTransactionProcessor.Result.successful( logs, 0, BytesValue.fromHexString(DEFAULT_OUTPUT), null); when(mockPrivateTransactionProcessor.processTransaction( @@ -108,29 +107,27 @@ public class PrivacyPrecompiledContractTest { } private Enclave brokenMockEnclave() { - Enclave mockEnclave = mock(Enclave.class); + final Enclave mockEnclave = mock(Enclave.class); when(mockEnclave.receive(any(ReceiveRequest.class))).thenThrow(EnclaveException.class); return mockEnclave; } @Before public void setUp() { - WorldStateArchive worldStateArchive; + final WorldStateArchive worldStateArchive; worldStateArchive = mock(WorldStateArchive.class); - MutableWorldState mutableWorldState = mock(MutableWorldState.class); + final MutableWorldState mutableWorldState = mock(MutableWorldState.class); when(mutableWorldState.updater()).thenReturn(mock(WorldUpdater.class)); when(worldStateArchive.getMutable()).thenReturn(mutableWorldState); when(worldStateArchive.getMutable(any())).thenReturn(Optional.of(mutableWorldState)); - PrivateTransactionStorage privateTransactionStorage = mock(PrivateTransactionStorage.class); - PrivateTransactionStorage.Updater updater = mock(PrivateTransactionStorage.Updater.class); - when(updater.putTransactionLogs(nullable(Bytes32.class), any())).thenReturn(updater); - when(updater.putTransactionResult(nullable(Bytes32.class), any())).thenReturn(updater); - when(privateTransactionStorage.updater()).thenReturn(updater); - - PrivateStateStorage privateStateStorage = mock(PrivateStateStorage.class); - PrivateStateStorage.Updater storageUpdater = mock(PrivateStateStorage.Updater.class); - when(storageUpdater.putPrivateAccountState(nullable(Bytes32.class), any())) + final PrivateStateStorage privateStateStorage = mock(PrivateStateStorage.class); + final PrivateStateStorage.Updater storageUpdater = mock(PrivateStateStorage.Updater.class); + when(storageUpdater.putLatestStateRoot(nullable(Bytes32.class), any())) + .thenReturn(storageUpdater); + when(storageUpdater.putTransactionLogs(nullable(Bytes32.class), any())) + .thenReturn(storageUpdater); + when(storageUpdater.putTransactionResult(nullable(Bytes32.class), any())) .thenReturn(storageUpdater); when(privateStateStorage.updater()).thenReturn(storageUpdater); @@ -140,7 +137,6 @@ public class PrivacyPrecompiledContractTest { publicKey, mockEnclave(), worldStateArchive, - privateTransactionStorage, privateStateStorage); privacyPrecompiledContract.setPrivateTransactionProcessor(mockPrivateTxProcessor()); brokenPrivateTransactionHandler = @@ -149,14 +145,12 @@ public class PrivacyPrecompiledContractTest { publicKey, brokenMockEnclave(), worldStateArchive, - privateTransactionStorage, privateStateStorage); messageFrame = mock(MessageFrame.class); } @Test public void testPrivacyPrecompiledContract() { - final BytesValue actual = privacyPrecompiledContract.compute(key, messageFrame); assertThat(actual).isEqualTo(BytesValue.fromHexString(DEFAULT_OUTPUT)); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionHandlerTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionHandlerTest.java index 7eb8c918a2..b60ce6d9c8 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionHandlerTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionHandlerTest.java @@ -39,6 +39,7 @@ import org.hyperledger.besu.ethereum.core.Wei; import org.hyperledger.besu.ethereum.mainnet.TransactionValidator.TransactionInvalidReason; import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import org.hyperledger.besu.ethereum.privacy.markertransaction.FixedKeySigningPrivateMarkerTransactionFactory; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.util.bytes.BytesValue; import org.hyperledger.besu.util.bytes.BytesValues; @@ -104,7 +105,7 @@ public class PrivateTransactionHandlerTest { public void setUp() throws Exception { PrivateStateStorage privateStateStorage = mock(PrivateStateStorage.class); Hash mockHash = mock(Hash.class); - when(privateStateStorage.getPrivateAccountState(any(BytesValue.class))) + when(privateStateStorage.getLatestStateRoot(any(BytesValue.class))) .thenReturn(Optional.of(mockHash)); WorldStateArchive worldStateArchive = mock(WorldStateArchive.class); Account account = mock(Account.class); diff --git a/plugin-api/build.gradle b/plugin-api/build.gradle index ea380a93f9..c9c16a75a3 100644 --- a/plugin-api/build.gradle +++ b/plugin-api/build.gradle @@ -56,7 +56,7 @@ Calculated : ${currentHash} tasks.register('checkAPIChanges', FileStateChecker) { description = "Checks that the API for the Plugin-API project does not change without deliberate thought" files = sourceSets.main.allJava.files - knownHash = 'f0SPUj4/aLZKyjAGcDYai021dO8pg5xLaNvALEWxoIg=' + knownHash = 'azykDVSB2DOA/rgZyedO8Uwe1eGDGRCMUAZN4FMB3R4=' } check.dependsOn('checkAPIChanges') diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/storage/KeyValueStorage.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/storage/KeyValueStorage.java index 4168f7a45a..5d4fd4a344 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/storage/KeyValueStorage.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/storage/KeyValueStorage.java @@ -19,6 +19,7 @@ import org.hyperledger.besu.plugin.services.exception.StorageException; import java.io.Closeable; import java.util.Optional; +import java.util.Set; import java.util.function.Predicate; /** @@ -70,6 +71,15 @@ public interface KeyValueStorage extends Closeable { */ long removeAllKeysUnless(Predicate retainCondition) throws StorageException; + /** + * Performs an evaluation against each key in the store, returning the set of entries that pass. + * + * @param returnCondition predicate to evaluate each key against, unless the result is {@code + * null}, the key is added to the returned list of keys. + * @return the set of keys that pass the condition. + */ + Set getAllKeysThat(Predicate returnCondition); + /** * Begins a fresh transaction, for sequencing operations for later atomic execution. * diff --git a/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueStorage.java b/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueStorage.java index c864b371f2..a169d492a2 100644 --- a/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueStorage.java +++ b/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueStorage.java @@ -33,11 +33,13 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; import java.util.stream.Collectors; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Sets; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.rocksdb.BlockBasedTableConfig; @@ -161,7 +163,7 @@ public class RocksDBColumnarKeyValueStorage } @Override - public long removeUnless( + public long removeAllEntriesUnless( final ColumnFamilyHandle segmentHandle, final Predicate inUseCheck) { long removedNodeCounter = 0; try (final RocksIterator rocksIterator = db.newIterator(segmentHandle)) { @@ -180,6 +182,23 @@ public class RocksDBColumnarKeyValueStorage return removedNodeCounter; } + @Override + public Set getAllKeysThat( + final ColumnFamilyHandle segmentHandle, final Predicate returnCondition) { + final Set returnedKeys = Sets.newIdentityHashSet(); + try (final RocksIterator rocksIterator = db.newIterator(segmentHandle)) { + rocksIterator.seekToFirst(); + while (rocksIterator.isValid()) { + final byte[] key = rocksIterator.key(); + if (returnCondition.test(key)) { + returnedKeys.add(key); + } + rocksIterator.next(); + } + } + return returnedKeys; + } + @Override public void clear(final ColumnFamilyHandle segmentHandle) { try (final RocksIterator rocksIterator = db.newIterator(segmentHandle)) { diff --git a/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/unsegmented/RocksDBKeyValueStorage.java b/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/unsegmented/RocksDBKeyValueStorage.java index ee0576b268..b858f156af 100644 --- a/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/unsegmented/RocksDBKeyValueStorage.java +++ b/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/unsegmented/RocksDBKeyValueStorage.java @@ -25,9 +25,11 @@ import org.hyperledger.besu.plugin.services.storage.rocksdb.configuration.RocksD import org.hyperledger.besu.services.kvstore.KeyValueStorageTransactionTransitionValidatorDecorator; import java.util.Optional; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; +import com.google.common.collect.Sets; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.rocksdb.BlockBasedTableConfig; @@ -130,6 +132,22 @@ public class RocksDBKeyValueStorage implements KeyValueStorage { return removedNodeCounter; } + @Override + public Set getAllKeysThat(final Predicate returnCondition) { + final Set returnedKeys = Sets.newIdentityHashSet(); + try (final RocksIterator rocksIterator = db.newIterator()) { + rocksIterator.seekToFirst(); + while (rocksIterator.isValid()) { + final byte[] key = rocksIterator.key(); + if (returnCondition.test(key)) { + returnedKeys.add(key); + } + rocksIterator.next(); + } + } + return returnedKeys; + } + @Override public KeyValueStorageTransaction startTransaction() throws StorageException { throwIfClosed(); diff --git a/plugins/rocksdb/src/test/java/org/hyperledger/besu/plugin/services/storage/rocksdb/unsegmented/RocksDBColumnarKeyValueStorageTest.java b/plugins/rocksdb/src/test/java/org/hyperledger/besu/plugin/services/storage/rocksdb/unsegmented/RocksDBColumnarKeyValueStorageTest.java index 796b56549b..3776b9646f 100644 --- a/plugins/rocksdb/src/test/java/org/hyperledger/besu/plugin/services/storage/rocksdb/unsegmented/RocksDBColumnarKeyValueStorageTest.java +++ b/plugins/rocksdb/src/test/java/org/hyperledger/besu/plugin/services/storage/rocksdb/unsegmented/RocksDBColumnarKeyValueStorageTest.java @@ -29,6 +29,7 @@ import org.hyperledger.besu.services.kvstore.SegmentedKeyValueStorageAdapter; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Optional; +import java.util.Set; import org.junit.Rule; import org.junit.Test; @@ -43,7 +44,7 @@ public class RocksDBColumnarKeyValueStorageTest extends AbstractKeyValueStorageT public void twoSegmentsAreIndependent() throws Exception { final SegmentedKeyValueStorage store = createSegmentedStore(); - Transaction tx = store.startTransaction(); + final Transaction tx = store.startTransaction(); tx.put( store.getSegmentIdentifierByName(TestSegment.BAR), bytesFromHexString("0001"), @@ -62,7 +63,7 @@ public class RocksDBColumnarKeyValueStorageTest extends AbstractKeyValueStorageT final ColumnFamilyHandle fooSegment = store.getSegmentIdentifierByName(TestSegment.FOO); final ColumnFamilyHandle barSegment = store.getSegmentIdentifierByName(TestSegment.BAR); - Transaction tx = store.startTransaction(); + final Transaction tx = store.startTransaction(); tx.put(fooSegment, bytesOf(1), bytesOf(1)); tx.put(fooSegment, bytesOf(2), bytesOf(2)); tx.put(fooSegment, bytesOf(3), bytesOf(3)); @@ -71,8 +72,10 @@ public class RocksDBColumnarKeyValueStorageTest extends AbstractKeyValueStorageT tx.put(barSegment, bytesOf(6), bytesOf(6)); tx.commit(); - final long removedFromFoo = store.removeUnless(fooSegment, x -> Arrays.equals(x, bytesOf(3))); - final long removedFromBar = store.removeUnless(barSegment, x -> Arrays.equals(x, bytesOf(4))); + final long removedFromFoo = + store.removeAllEntriesUnless(fooSegment, x -> Arrays.equals(x, bytesOf(3))); + final long removedFromBar = + store.removeAllEntriesUnless(barSegment, x -> Arrays.equals(x, bytesOf(4))); assertThat(removedFromFoo).isEqualTo(2); assertThat(removedFromBar).isEqualTo(2); @@ -86,6 +89,37 @@ public class RocksDBColumnarKeyValueStorageTest extends AbstractKeyValueStorageT assertThat(store.get(barSegment, bytesOf(6))).isEmpty(); } + @Test + public void canGetThroughSegmentIteration() throws Exception { + final SegmentedKeyValueStorage store = createSegmentedStore(); + final ColumnFamilyHandle fooSegment = store.getSegmentIdentifierByName(TestSegment.FOO); + final ColumnFamilyHandle barSegment = store.getSegmentIdentifierByName(TestSegment.BAR); + + final Transaction tx = store.startTransaction(); + tx.put(fooSegment, bytesOf(1), bytesOf(1)); + tx.put(fooSegment, bytesOf(2), bytesOf(2)); + tx.put(fooSegment, bytesOf(3), bytesOf(3)); + tx.put(barSegment, bytesOf(4), bytesOf(4)); + tx.put(barSegment, bytesOf(5), bytesOf(5)); + tx.put(barSegment, bytesOf(6), bytesOf(6)); + tx.commit(); + + final Set gotFromFoo = + store.getAllKeysThat(fooSegment, x -> Arrays.equals(x, bytesOf(3))); + final Set gotFromBar = + store.getAllKeysThat( + barSegment, x -> Arrays.equals(x, bytesOf(4)) || Arrays.equals(x, bytesOf(5))); + final Set gotEmpty = + store.getAllKeysThat(fooSegment, x -> Arrays.equals(x, bytesOf(0))); + + assertThat(gotFromFoo.size()).isEqualTo(1); + assertThat(gotFromBar.size()).isEqualTo(2); + assertThat(gotEmpty).isEmpty(); + + assertThat(gotFromFoo).containsExactlyInAnyOrder(bytesOf(3)); + assertThat(gotFromBar).containsExactlyInAnyOrder(bytesOf(4), bytesOf(5)); + } + public enum TestSegment implements SegmentIdentifier { FOO(new byte[] {1}), BAR(new byte[] {2}); diff --git a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/InMemoryKeyValueStorage.java b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/InMemoryKeyValueStorage.java index 59ae98c626..d66d1a7d71 100644 --- a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/InMemoryKeyValueStorage.java +++ b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/InMemoryKeyValueStorage.java @@ -28,6 +28,7 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Predicate; +import java.util.stream.Collectors; public class InMemoryKeyValueStorage implements KeyValueStorage { @@ -88,6 +89,14 @@ public class InMemoryKeyValueStorage implements KeyValueStorage { } } + @Override + public Set getAllKeysThat(final Predicate returnCondition) { + return hashValueStore.keySet().stream() + .map(BytesValue::getArrayUnsafe) + .filter(returnCondition) + .collect(Collectors.toSet()); + } + @Override public void close() {} diff --git a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/LimitedInMemoryKeyValueStorage.java b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/LimitedInMemoryKeyValueStorage.java index 0b0e6e011a..b8f4b0b6f7 100644 --- a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/LimitedInMemoryKeyValueStorage.java +++ b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/LimitedInMemoryKeyValueStorage.java @@ -28,6 +28,7 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Predicate; +import java.util.stream.Collectors; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; @@ -88,6 +89,14 @@ public class LimitedInMemoryKeyValueStorage implements KeyValueStorage { return initialSize - storage.size(); } + @Override + public Set getAllKeysThat(final Predicate returnCondition) { + return storage.asMap().keySet().stream() + .map(BytesValue::getArrayUnsafe) + .filter(returnCondition) + .collect(Collectors.toSet()); + } + @Override public KeyValueStorageTransaction startTransaction() throws StorageException { return new KeyValueStorageTransactionTransitionValidatorDecorator(new MemoryTransaction()); diff --git a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedKeyValueStorage.java b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedKeyValueStorage.java index b5910f292d..1e775c0396 100644 --- a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedKeyValueStorage.java +++ b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedKeyValueStorage.java @@ -19,6 +19,7 @@ import org.hyperledger.besu.plugin.services.storage.SegmentIdentifier; import java.io.Closeable; import java.util.Optional; +import java.util.Set; import java.util.function.Predicate; /** @@ -48,7 +49,9 @@ public interface SegmentedKeyValueStorage extends Closeable { */ Transaction startTransaction() throws StorageException; - long removeUnless(S segmentHandle, Predicate inUseCheck); + long removeAllEntriesUnless(S segmentHandle, Predicate inUseCheck); + + Set getAllKeysThat(S segmentHandle, Predicate returnCondition); void clear(S segmentHandle); diff --git a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedKeyValueStorageAdapter.java b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedKeyValueStorageAdapter.java index 3489ce92dc..9e07f15d64 100644 --- a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedKeyValueStorageAdapter.java +++ b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedKeyValueStorageAdapter.java @@ -21,6 +21,7 @@ import org.hyperledger.besu.plugin.services.storage.SegmentIdentifier; import java.io.IOException; import java.util.Optional; +import java.util.Set; import java.util.function.Predicate; public class SegmentedKeyValueStorageAdapter implements KeyValueStorage { @@ -51,7 +52,12 @@ public class SegmentedKeyValueStorageAdapter implements KeyValueStorage { @Override public long removeAllKeysUnless(final Predicate retainCondition) throws StorageException { - return storage.removeUnless(segmentHandle, retainCondition); + return storage.removeAllEntriesUnless(segmentHandle, retainCondition); + } + + @Override + public Set getAllKeysThat(final Predicate returnCondition) { + return storage.getAllKeysThat(segmentHandle, returnCondition); } @Override diff --git a/testutil/src/main/java/org/hyperledger/besu/kvstore/AbstractKeyValueStorageTest.java b/testutil/src/main/java/org/hyperledger/besu/kvstore/AbstractKeyValueStorageTest.java index 6a1c798952..f0854a0c15 100644 --- a/testutil/src/main/java/org/hyperledger/besu/kvstore/AbstractKeyValueStorageTest.java +++ b/testutil/src/main/java/org/hyperledger/besu/kvstore/AbstractKeyValueStorageTest.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.function.Function; @@ -86,6 +87,22 @@ public abstract class AbstractKeyValueStorageTest { assertThat(store.containsKey(bytesFromHexString("12"))).isTrue(); } + @Test + public void getAllKeysThat() throws Exception { + final KeyValueStorage store = createStore(); + final KeyValueStorageTransaction tx = store.startTransaction(); + tx.put(bytesFromHexString("0F"), bytesFromHexString("0ABC")); + tx.put(bytesFromHexString("10"), bytesFromHexString("0ABC")); + tx.put(bytesFromHexString("11"), bytesFromHexString("0ABC")); + tx.put(bytesFromHexString("12"), bytesFromHexString("0ABC")); + tx.commit(); + Set keys = store.getAllKeysThat(bv -> BytesValue.wrap(bv).toString().contains("1")); + assertThat(keys.size()).isEqualTo(3); + assertThat(keys) + .containsExactlyInAnyOrder( + bytesFromHexString("10"), bytesFromHexString("11"), bytesFromHexString("12")); + } + @Test public void containsKey() throws Exception { final KeyValueStorage store = createStore();