Introduce variables storage (#5471)

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
pull/5540/head
Fabio Di Fabio 1 year ago committed by GitHub
parent debd53ccc9
commit 8bc939d236
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      CHANGELOG.md
  2. 13
      besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
  3. 172
      besu/src/main/java/org/hyperledger/besu/cli/subcommands/storage/StorageSubCommand.java
  4. 15
      besu/src/main/java/org/hyperledger/besu/controller/BesuController.java
  5. 9
      besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java
  6. 15
      besu/src/test/java/org/hyperledger/besu/RunnerBuilderTest.java
  7. 117
      besu/src/test/java/org/hyperledger/besu/cli/subcommands/storage/StorageSubCommandTest.java
  8. 7
      besu/src/test/java/org/hyperledger/besu/controller/BesuControllerBuilderTest.java
  9. 7
      besu/src/test/java/org/hyperledger/besu/controller/MergeBesuControllerBuilderTest.java
  10. 7
      besu/src/test/java/org/hyperledger/besu/controller/QbftBesuControllerBuilderTest.java
  11. 5
      besu/src/test/java/org/hyperledger/besu/services/BesuEventsImplTest.java
  12. 5
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/blockheaders/NewBlockHeadersSubscriptionServiceTest.java
  13. 2
      ethereum/core/src/jmh/java/org/hyperledger/besu/ethereum/vm/operations/OperationBenchmarkHelper.java
  14. 88
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/VariablesStorage.java
  15. 6
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/StorageProvider.java
  16. 2
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueSegmentIdentifier.java
  17. 140
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueStoragePrefixedKeyBlockchainStorage.java
  18. 23
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueStorageProvider.java
  19. 145
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/VariablesKeyValueStorage.java
  20. 46
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/ExecutionContextTestFixture.java
  21. 25
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryKeyValueStorageProvider.java
  22. 153
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/VariablesStorageHelper.java
  23. 9
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/chain/ChainDataPrunerTest.java
  24. 134
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/chain/DefaultBlockchainTest.java
  25. 103
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueStoragePrefixedKeyBlockchainStorageTest.java
  26. 17
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/worldstate/PrunerTest.java
  27. 5
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckPointBlockImportStepTest.java
  28. 19
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/DataStoreModule.java
  29. 27
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryAgent.java
  30. 7
      ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java
  31. 21
      plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueStorage.java

@ -3,6 +3,8 @@
## 23.4.2
### Breaking Changes
- Move blockchain related variables in a dedicated storage, to pave the way to future optimizations [#5471](https://github.com/hyperledger/besu/pull/5471). The migration is performed automatically at startup,
and in case a rollback is needed, before installing a previous version, the migration can be reverted, using the subcommand `storage revert-variables` with the same configuration use to run Besu.
### Additions and Improvements
- Allow Ethstats connection url to specify ws:// or wss:// scheme. [#5494](https://github.com/hyperledger/besu/issues/5494)

@ -83,6 +83,7 @@ import org.hyperledger.besu.cli.subcommands.ValidateConfigSubCommand;
import org.hyperledger.besu.cli.subcommands.blocks.BlocksSubCommand;
import org.hyperledger.besu.cli.subcommands.operator.OperatorSubCommand;
import org.hyperledger.besu.cli.subcommands.rlp.RLPSubCommand;
import org.hyperledger.besu.cli.subcommands.storage.StorageSubCommand;
import org.hyperledger.besu.cli.util.BesuCommandCustomFactory;
import org.hyperledger.besu.cli.util.CommandLineUtils;
import org.hyperledger.besu.cli.util.ConfigOptionSearchAndRunHandler;
@ -140,6 +141,7 @@ import org.hyperledger.besu.ethereum.permissioning.PermissioningConfigurationBui
import org.hyperledger.besu.ethereum.permissioning.SmartContractPermissioningConfiguration;
import org.hyperledger.besu.ethereum.privacy.storage.keyvalue.PrivacyKeyValueStorageProvider;
import org.hyperledger.besu.ethereum.privacy.storage.keyvalue.PrivacyKeyValueStorageProviderBuilder;
import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProvider;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProviderBuilder;
@ -1598,6 +1600,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
commandLine.addSubcommand(
ValidateConfigSubCommand.COMMAND_NAME,
new ValidateConfigSubCommand(commandLine, commandLine.getOut()));
commandLine.addSubcommand(
StorageSubCommand.COMMAND_NAME, new StorageSubCommand(commandLine.getOut()));
final String generateCompletionSubcommandName = "generate-completion";
commandLine.addSubcommand(
generateCompletionSubcommandName, AutoComplete.GenerateCompletion.class);
@ -2979,6 +2983,15 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
return this.keyValueStorageProvider;
}
/**
* Get the storage provider
*
* @return the storage provider
*/
public StorageProvider getStorageProvider() {
return keyValueStorageProvider(keyValueStorageName);
}
private Optional<PkiBlockCreationConfiguration> maybePkiBlockCreationConfiguration() {
return pkiBlockCreationOptions
.asDomainConfig(commandLine)

@ -0,0 +1,172 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.cli.subcommands.storage;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.hyperledger.besu.cli.subcommands.storage.StorageSubCommand.COMMAND_NAME;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.CHAIN_HEAD_HASH;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.FINALIZED_BLOCK_HASH;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.FORK_HEADS;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.SAFE_BLOCK_HASH;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.SEQ_NO_STORE;
import org.hyperledger.besu.cli.BesuCommand;
import org.hyperledger.besu.cli.util.VersionProvider;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction;
import java.io.PrintWriter;
import org.apache.tuweni.bytes.Bytes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import picocli.CommandLine.Command;
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.ParentCommand;
import picocli.CommandLine.Spec;
/** The Storage sub command. */
@Command(
name = COMMAND_NAME,
description = "This command provides storage related actions.",
mixinStandardHelpOptions = true,
versionProvider = VersionProvider.class,
subcommands = {StorageSubCommand.RevertVariablesStorage.class})
public class StorageSubCommand implements Runnable {
/** The constant COMMAND_NAME. */
public static final String COMMAND_NAME = "storage";
@SuppressWarnings("unused")
@ParentCommand
private BesuCommand parentCommand;
@SuppressWarnings("unused")
@Spec
private CommandSpec spec;
private final PrintWriter out;
/**
* Instantiates a new Storage sub command.
*
* @param out The PrintWriter where the usage will be reported.
*/
public StorageSubCommand(final PrintWriter out) {
this.out = out;
}
@Override
public void run() {
spec.commandLine().usage(out);
}
/** The Hash sub command for password. */
@Command(
name = "revert-variables",
description = "This command revert the modifications done by the variables storage feature.",
mixinStandardHelpOptions = true,
versionProvider = VersionProvider.class)
static class RevertVariablesStorage implements Runnable {
private static final Logger LOG = LoggerFactory.getLogger(RevertVariablesStorage.class);
private static final Bytes VARIABLES_PREFIX = Bytes.of(1);
@SuppressWarnings("unused")
@ParentCommand
private StorageSubCommand parentCommand;
@Override
public void run() {
checkNotNull(parentCommand);
final var storageProvider = getStorageProvider();
revert(storageProvider);
}
private StorageProvider getStorageProvider() {
return parentCommand.parentCommand.getStorageProvider();
}
private void revert(final StorageProvider storageProvider) {
final var variablesStorage = storageProvider.createVariablesStorage();
final var blockchainStorage =
getStorageProvider().getStorageBySegmentIdentifier(KeyValueSegmentIdentifier.BLOCKCHAIN);
final var blockchainUpdater = blockchainStorage.startTransaction();
final var variablesUpdater = variablesStorage.updater();
variablesStorage
.getChainHead()
.ifPresent(
v -> {
setBlockchainVariable(
blockchainUpdater, VARIABLES_PREFIX, CHAIN_HEAD_HASH.getBytes(), v);
LOG.info("Reverted variable storage for key {}", CHAIN_HEAD_HASH);
});
variablesStorage
.getFinalized()
.ifPresent(
v -> {
setBlockchainVariable(
blockchainUpdater, VARIABLES_PREFIX, FINALIZED_BLOCK_HASH.getBytes(), v);
LOG.info("Reverted variable storage for key {}", FINALIZED_BLOCK_HASH);
});
variablesStorage
.getSafeBlock()
.ifPresent(
v -> {
setBlockchainVariable(
blockchainUpdater, VARIABLES_PREFIX, SAFE_BLOCK_HASH.getBytes(), v);
LOG.info("Reverted variable storage for key {}", SAFE_BLOCK_HASH);
});
final var forkHeads = variablesStorage.getForkHeads();
if (!forkHeads.isEmpty()) {
setBlockchainVariable(
blockchainUpdater,
VARIABLES_PREFIX,
FORK_HEADS.getBytes(),
RLP.encode(o -> o.writeList(forkHeads, (val, out) -> out.writeBytes(val))));
LOG.info("Reverted variable storage for key {}", FORK_HEADS);
}
variablesStorage
.getLocalEnrSeqno()
.ifPresent(
v -> {
setBlockchainVariable(blockchainUpdater, Bytes.EMPTY, SEQ_NO_STORE.getBytes(), v);
LOG.info("Reverted variable storage for key {}", SEQ_NO_STORE);
});
variablesUpdater.removeAll();
variablesUpdater.commit();
blockchainUpdater.commit();
}
private void setBlockchainVariable(
final KeyValueStorageTransaction blockchainTransaction,
final Bytes prefix,
final Bytes key,
final Bytes value) {
blockchainTransaction.put(
Bytes.concatenate(prefix, key).toArrayUnsafe(), value.toArrayUnsafe());
}
}
}

@ -36,6 +36,7 @@ import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.p2p.config.SubProtocolConfiguration;
import org.hyperledger.besu.ethereum.storage.StorageProvider;
import java.io.Closeable;
import java.io.IOException;
@ -75,6 +76,7 @@ public class BesuController implements java.io.Closeable {
private final PluginServiceFactory additionalPluginServices;
private final SyncState syncState;
private final EthPeers ethPeers;
private final StorageProvider storageProvider;
/**
* Instantiates a new Besu controller.
@ -111,7 +113,8 @@ public class BesuController implements java.io.Closeable {
final NodeKey nodeKey,
final List<Closeable> closeables,
final PluginServiceFactory additionalPluginServices,
final EthPeers ethPeers) {
final EthPeers ethPeers,
final StorageProvider storageProvider) {
this.protocolSchedule = protocolSchedule;
this.protocolContext = protocolContext;
this.ethProtocolManager = ethProtocolManager;
@ -128,6 +131,7 @@ public class BesuController implements java.io.Closeable {
this.miningParameters = miningParameters;
this.additionalPluginServices = additionalPluginServices;
this.ethPeers = ethPeers;
this.storageProvider = storageProvider;
}
/**
@ -220,6 +224,15 @@ public class BesuController implements java.io.Closeable {
return ethPeers;
}
/**
* Get the storage provider
*
* @return the storage provider
*/
public StorageProvider getStorageProvider() {
return storageProvider;
}
@Override
public void close() {
closeables.forEach(this::tryClose);

@ -42,6 +42,7 @@ import org.hyperledger.besu.ethereum.chain.ChainPrunerConfiguration;
import org.hyperledger.besu.ethereum.chain.DefaultBlockchain;
import org.hyperledger.besu.ethereum.chain.GenesisState;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.chain.VariablesStorage;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
@ -561,11 +562,14 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
final ProtocolSchedule protocolSchedule = createProtocolSchedule();
final GenesisState genesisState = GenesisState.fromConfig(genesisConfig, protocolSchedule);
final VariablesStorage variablesStorage = storageProvider.createVariablesStorage();
final WorldStateStorage worldStateStorage =
storageProvider.createWorldStateStorage(dataStorageConfiguration.getDataStorageFormat());
final BlockchainStorage blockchainStorage =
storageProvider.createBlockchainStorage(protocolSchedule);
storageProvider.createBlockchainStorage(protocolSchedule, variablesStorage);
final MutableBlockchain blockchain =
DefaultBlockchain.createMutable(
@ -773,7 +777,8 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
nodeKey,
closeables,
additionalPluginServices,
ethPeers);
ethPeers,
storageProvider);
}
/**

@ -16,8 +16,9 @@ package org.hyperledger.besu;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider.createInMemoryBlockchain;
import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.BLOCKCHAIN;
import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.VARIABLES;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -163,7 +164,7 @@ public final class RunnerBuilderTest {
.metricsConfiguration(mock(MetricsConfiguration.class))
.vertx(vertx)
.dataDir(dataDir.getRoot())
.storageProvider(mock(KeyValueStorageProvider.class))
.storageProvider(mock(KeyValueStorageProvider.class, RETURNS_DEEP_STUBS))
.rpcEndpointService(new RpcEndpointServiceImpl())
.build();
runner.startEthereumMainLoop();
@ -223,7 +224,7 @@ public final class RunnerBuilderTest {
inMemoryBlockchain.appendBlock(block, gen.receipts(block));
assertThat(
storageProvider
.getStorageBySegmentIdentifier(BLOCKCHAIN)
.getStorageBySegmentIdentifier(VARIABLES)
.get("local-enr-seqno".getBytes(StandardCharsets.UTF_8))
.map(Bytes::of)
.map(NodeRecordFactory.DEFAULT::fromBytes)
@ -265,7 +266,7 @@ public final class RunnerBuilderTest {
.metricsConfiguration(mock(MetricsConfiguration.class))
.vertx(Vertx.vertx())
.dataDir(dataDir.getRoot())
.storageProvider(mock(KeyValueStorageProvider.class))
.storageProvider(mock(KeyValueStorageProvider.class, RETURNS_DEEP_STUBS))
.rpcEndpointService(new RpcEndpointServiceImpl())
.besuPluginContext(mock(BesuPluginContextImpl.class))
.build();
@ -307,7 +308,7 @@ public final class RunnerBuilderTest {
.metricsConfiguration(mock(MetricsConfiguration.class))
.vertx(Vertx.vertx())
.dataDir(dataDir.getRoot())
.storageProvider(mock(KeyValueStorageProvider.class))
.storageProvider(mock(KeyValueStorageProvider.class, RETURNS_DEEP_STUBS))
.rpcEndpointService(new RpcEndpointServiceImpl())
.besuPluginContext(mock(BesuPluginContextImpl.class))
.build();
@ -348,7 +349,7 @@ public final class RunnerBuilderTest {
.metricsConfiguration(mock(MetricsConfiguration.class))
.vertx(Vertx.vertx())
.dataDir(dataDir.getRoot())
.storageProvider(mock(KeyValueStorageProvider.class))
.storageProvider(mock(KeyValueStorageProvider.class, RETURNS_DEEP_STUBS))
.rpcEndpointService(new RpcEndpointServiceImpl())
.besuPluginContext(mock(BesuPluginContextImpl.class))
.build();
@ -390,7 +391,7 @@ public final class RunnerBuilderTest {
.metricsConfiguration(mock(MetricsConfiguration.class))
.vertx(Vertx.vertx())
.dataDir(dataDir.getRoot())
.storageProvider(mock(KeyValueStorageProvider.class))
.storageProvider(mock(KeyValueStorageProvider.class, RETURNS_DEEP_STUBS))
.rpcEndpointService(new RpcEndpointServiceImpl())
.besuPluginContext(mock(BesuPluginContextImpl.class))
.networkingConfiguration(NetworkingConfiguration.create())

@ -0,0 +1,117 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.cli.subcommands.storage;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.FINALIZED_BLOCK_HASH;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.SAFE_BLOCK_HASH;
import static org.hyperledger.besu.ethereum.core.VariablesStorageHelper.assertNoVariablesInStorage;
import static org.hyperledger.besu.ethereum.core.VariablesStorageHelper.assertVariablesPresentInBlockchainStorage;
import static org.hyperledger.besu.ethereum.core.VariablesStorageHelper.getSampleVariableValues;
import static org.hyperledger.besu.ethereum.core.VariablesStorageHelper.populateBlockchainStorage;
import static org.hyperledger.besu.ethereum.core.VariablesStorageHelper.populateVariablesStorage;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.cli.CommandTestAbstract;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.Silent.class)
public class StorageSubCommandTest extends CommandTestAbstract {
@Test
public void storageSubCommandExists() {
parseCommand("storage");
assertThat(commandOutput.toString(UTF_8))
.contains("This command provides storage related actions");
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void storageRevertVariablesSubCommandExists() {
parseCommand("storage", "revert-variables", "--help");
assertThat(commandOutput.toString(UTF_8))
.contains("This command revert the modifications done by the variables storage feature");
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void revertVariables() {
final var kvVariables = new InMemoryKeyValueStorage();
final var kvBlockchain = new InMemoryKeyValueStorage();
when(rocksDBStorageFactory.create(eq(KeyValueSegmentIdentifier.VARIABLES), any(), any()))
.thenReturn(kvVariables);
when(rocksDBStorageFactory.create(eq(KeyValueSegmentIdentifier.BLOCKCHAIN), any(), any()))
.thenReturn(kvBlockchain);
final var variableValues = getSampleVariableValues();
assertNoVariablesInStorage(kvBlockchain);
populateVariablesStorage(kvVariables, variableValues);
parseCommand("storage", "revert-variables");
assertNoVariablesInStorage(kvVariables);
assertVariablesPresentInBlockchainStorage(kvBlockchain, variableValues);
}
@Test
public void revertVariablesWhenSomeVariablesDoNotExist() {
final var kvVariables = new InMemoryKeyValueStorage();
final var kvBlockchain = new InMemoryKeyValueStorage();
when(rocksDBStorageFactory.create(eq(KeyValueSegmentIdentifier.VARIABLES), any(), any()))
.thenReturn(kvVariables);
when(rocksDBStorageFactory.create(eq(KeyValueSegmentIdentifier.BLOCKCHAIN), any(), any()))
.thenReturn(kvBlockchain);
final var variableValues = getSampleVariableValues();
variableValues.remove(FINALIZED_BLOCK_HASH);
variableValues.remove(SAFE_BLOCK_HASH);
assertNoVariablesInStorage(kvBlockchain);
populateVariablesStorage(kvVariables, variableValues);
parseCommand("storage", "revert-variables");
assertNoVariablesInStorage(kvVariables);
assertVariablesPresentInBlockchainStorage(kvBlockchain, variableValues);
}
@Test
public void doesNothingWhenVariablesAlreadyReverted() {
final var kvVariables = new InMemoryKeyValueStorage();
final var kvBlockchain = new InMemoryKeyValueStorage();
when(rocksDBStorageFactory.create(eq(KeyValueSegmentIdentifier.VARIABLES), any(), any()))
.thenReturn(kvVariables);
when(rocksDBStorageFactory.create(eq(KeyValueSegmentIdentifier.BLOCKCHAIN), any(), any()))
.thenReturn(kvBlockchain);
final var variableValues = getSampleVariableValues();
assertNoVariablesInStorage(kvVariables);
populateBlockchainStorage(kvBlockchain, variableValues);
parseCommand("storage", "revert-variables");
assertNoVariablesInStorage(kvVariables);
assertVariablesPresentInBlockchainStorage(kvBlockchain, variableValues);
}
}

@ -45,6 +45,7 @@ import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration;
import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
import org.hyperledger.besu.ethereum.storage.keyvalue.VariablesKeyValueStorage;
import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration;
import org.hyperledger.besu.ethereum.worldstate.DataStorageFormat;
import org.hyperledger.besu.ethereum.worldstate.ImmutableDataStorageConfiguration;
@ -114,10 +115,12 @@ public class BesuControllerBuilderTest {
when(ethashConfigOptions.getFixedDifficulty()).thenReturn(OptionalLong.empty());
when(storageProvider.getStorageBySegmentIdentifier(any()))
.thenReturn(new InMemoryKeyValueStorage());
when(storageProvider.createBlockchainStorage(any()))
when(storageProvider.createBlockchainStorage(any(), any()))
.thenReturn(
new KeyValueStoragePrefixedKeyBlockchainStorage(
new InMemoryKeyValueStorage(), new MainnetBlockHeaderFunctions()));
new InMemoryKeyValueStorage(),
new VariablesKeyValueStorage(new InMemoryKeyValueStorage()),
new MainnetBlockHeaderFunctions()));
when(synchronizerConfiguration.getDownloaderParallelism()).thenReturn(1);
when(synchronizerConfiguration.getTransactionsParallelism()).thenReturn(1);
when(synchronizerConfiguration.getComputationParallelism()).thenReturn(1);

@ -51,6 +51,7 @@ import org.hyperledger.besu.ethereum.mainnet.feemarket.LondonFeeMarket;
import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration;
import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
import org.hyperledger.besu.ethereum.storage.keyvalue.VariablesKeyValueStorage;
import org.hyperledger.besu.ethereum.worldstate.DataStorageFormat;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage;
@ -119,10 +120,12 @@ public class MergeBesuControllerBuilderTest {
when(genesisConfigOptions.getThanosBlockNumber()).thenReturn(OptionalLong.empty());
when(genesisConfigOptions.getTerminalBlockHash()).thenReturn(Optional.of(Hash.ZERO));
when(genesisConfigOptions.getTerminalBlockNumber()).thenReturn(OptionalLong.of(1L));
when(storageProvider.createBlockchainStorage(any()))
when(storageProvider.createBlockchainStorage(any(), any()))
.thenReturn(
new KeyValueStoragePrefixedKeyBlockchainStorage(
new InMemoryKeyValueStorage(), new MainnetBlockHeaderFunctions()));
new InMemoryKeyValueStorage(),
new VariablesKeyValueStorage(new InMemoryKeyValueStorage()),
new MainnetBlockHeaderFunctions()));
when(storageProvider.getStorageBySegmentIdentifier(any()))
.thenReturn(new InMemoryKeyValueStorage());
when(synchronizerConfiguration.getDownloaderParallelism()).thenReturn(1);

@ -47,6 +47,7 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration;
import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
import org.hyperledger.besu.ethereum.storage.keyvalue.VariablesKeyValueStorage;
import org.hyperledger.besu.ethereum.worldstate.DataStorageFormat;
import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage;
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage;
@ -103,10 +104,12 @@ public class QbftBesuControllerBuilderTest {
when(genesisConfigFile.getConfigOptions(any())).thenReturn(genesisConfigOptions);
when(genesisConfigFile.getConfigOptions()).thenReturn(genesisConfigOptions);
when(genesisConfigOptions.getCheckpointOptions()).thenReturn(checkpointConfigOptions);
when(storageProvider.createBlockchainStorage(any()))
when(storageProvider.createBlockchainStorage(any(), any()))
.thenReturn(
new KeyValueStoragePrefixedKeyBlockchainStorage(
new InMemoryKeyValueStorage(), new MainnetBlockHeaderFunctions()));
new InMemoryKeyValueStorage(),
new VariablesKeyValueStorage(new InMemoryKeyValueStorage()),
new MainnetBlockHeaderFunctions()));
when(storageProvider.createWorldStateStorage(DataStorageFormat.FOREST))
.thenReturn(worldStateStorage);
when(worldStateStorage.isWorldStateAvailable(any(), any())).thenReturn(true);

@ -54,6 +54,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
import org.hyperledger.besu.ethereum.storage.keyvalue.VariablesKeyValueStorage;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.data.AddedBlockContext;
@ -114,7 +115,9 @@ public class BesuEventsImplTest {
DefaultBlockchain.createMutable(
gen.genesisBlock(),
new KeyValueStoragePrefixedKeyBlockchainStorage(
new InMemoryKeyValueStorage(), new MainnetBlockHeaderFunctions()),
new InMemoryKeyValueStorage(),
new VariablesKeyValueStorage(new InMemoryKeyValueStorage()),
new MainnetBlockHeaderFunctions()),
new NoOpMetricsSystem(),
0);

@ -35,6 +35,7 @@ import org.hyperledger.besu.ethereum.core.BlockDataGenerator.BlockOptions;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
import org.hyperledger.besu.ethereum.storage.keyvalue.VariablesKeyValueStorage;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
@ -60,7 +61,9 @@ public class NewBlockHeadersSubscriptionServiceTest {
private final BlockDataGenerator gen = new BlockDataGenerator();
private final BlockchainStorage blockchainStorage =
new KeyValueStoragePrefixedKeyBlockchainStorage(
new InMemoryKeyValueStorage(), new MainnetBlockHeaderFunctions());
new InMemoryKeyValueStorage(),
new VariablesKeyValueStorage(new InMemoryKeyValueStorage()),
new MainnetBlockHeaderFunctions());
private final Block genesisBlock = gen.genesisBlock();
private final MutableBlockchain blockchain =
DefaultBlockchain.createMutable(genesisBlock, blockchainStorage, new NoOpMetricsSystem(), 0);

@ -65,7 +65,7 @@ public class OperationBenchmarkHelper {
RocksDBMetricsFactory.PUBLIC_ROCKS_DB_METRICS);
final ExecutionContextTestFixture executionContext =
ExecutionContextTestFixture.builder().keyValueStorage(keyValueStorage).build();
ExecutionContextTestFixture.builder().blockchainKeyValueStorage(keyValueStorage).build();
final MutableBlockchain blockchain = executionContext.getBlockchain();
for (int i = 1; i < 256; i++) {

@ -0,0 +1,88 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.chain;
import static java.nio.charset.StandardCharsets.UTF_8;
import org.hyperledger.besu.datatypes.Hash;
import java.util.Collection;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
public interface VariablesStorage {
enum Keys {
CHAIN_HEAD_HASH("chainHeadHash"),
FORK_HEADS("forkHeads"),
FINALIZED_BLOCK_HASH("finalizedBlockHash"),
SAFE_BLOCK_HASH("safeBlockHash"),
SEQ_NO_STORE("local-enr-seqno");
private final String key;
private final byte[] byteArray;
private final Bytes bytes;
Keys(final String key) {
this.key = key;
this.byteArray = key.getBytes(UTF_8);
this.bytes = Bytes.wrap(byteArray);
}
public byte[] toByteArray() {
return byteArray;
}
public Bytes getBytes() {
return bytes;
}
@Override
public String toString() {
return key;
}
}
Optional<Hash> getChainHead();
Collection<Hash> getForkHeads();
Optional<Hash> getFinalized();
Optional<Hash> getSafeBlock();
Optional<Bytes> getLocalEnrSeqno();
Updater updater();
interface Updater {
void setChainHead(Hash blockHash);
void setForkHeads(Collection<Hash> forkHeadHashes);
void setFinalized(Hash blockHash);
void setSafeBlock(Hash blockHash);
void setLocalEnrSeqno(Bytes nodeRecord);
void removeAll();
void commit();
void rollback();
}
}

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.storage;
import org.hyperledger.besu.ethereum.chain.BlockchainStorage;
import org.hyperledger.besu.ethereum.chain.VariablesStorage;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.worldstate.DataStorageFormat;
import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage;
@ -27,7 +28,10 @@ import java.io.Closeable;
public interface StorageProvider extends Closeable {
BlockchainStorage createBlockchainStorage(ProtocolSchedule protocolSchedule);
VariablesStorage createVariablesStorage();
BlockchainStorage createBlockchainStorage(
ProtocolSchedule protocolSchedule, VariablesStorage variablesStorage);
WorldStateStorage createWorldStateStorage(DataStorageFormat dataStorageFormat);

@ -29,10 +29,10 @@ public enum KeyValueSegmentIdentifier implements SegmentIdentifier {
ACCOUNT_STORAGE_STORAGE(new byte[] {8}, new int[] {2}),
TRIE_BRANCH_STORAGE(new byte[] {9}, new int[] {2}),
TRIE_LOG_STORAGE(new byte[] {10}, new int[] {2}),
VARIABLES(new byte[] {11}), // formerly GOQUORUM_PRIVATE_WORLD_STATE
// previously supported GoQuorum private states
// no longer used but need to be retained for db backward compatibility
GOQUORUM_PRIVATE_WORLD_STATE(new byte[] {11}),
GOQUORUM_PRIVATE_STORAGE(new byte[] {12}),
BACKWARD_SYNC_HEADERS(new byte[] {13}),

@ -14,9 +14,16 @@
*/
package org.hyperledger.besu.ethereum.storage.keyvalue;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.CHAIN_HEAD_HASH;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.FINALIZED_BLOCK_HASH;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.FORK_HEADS;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.SAFE_BLOCK_HASH;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.SEQ_NO_STORE;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.chain.BlockchainStorage;
import org.hyperledger.besu.ethereum.chain.TransactionLocation;
import org.hyperledger.besu.ethereum.chain.VariablesStorage;
import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions;
@ -26,64 +33,61 @@ import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorage;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import com.google.common.collect.Lists;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class KeyValueStoragePrefixedKeyBlockchainStorage implements BlockchainStorage {
private static final Logger LOG =
LoggerFactory.getLogger(KeyValueStoragePrefixedKeyBlockchainStorage.class);
private static final Bytes CHAIN_HEAD_KEY =
Bytes.wrap("chainHeadHash".getBytes(StandardCharsets.UTF_8));
private static final Bytes FORK_HEADS_KEY =
Bytes.wrap("forkHeads".getBytes(StandardCharsets.UTF_8));
private static final Bytes FINALIZED_BLOCK_HASH_KEY =
Bytes.wrap("finalizedBlockHash".getBytes(StandardCharsets.UTF_8));
private static final Bytes SAFE_BLOCK_HASH_KEY =
Bytes.wrap("safeBlockHash".getBytes(StandardCharsets.UTF_8));
@Deprecated(since = "23.4.2", forRemoval = true)
private static final Bytes VARIABLES_PREFIX = Bytes.of(1);
static final Bytes BLOCK_HEADER_PREFIX = Bytes.of(2);
private static final Bytes BLOCK_HEADER_PREFIX = Bytes.of(2);
private static final Bytes BLOCK_BODY_PREFIX = Bytes.of(3);
private static final Bytes TRANSACTION_RECEIPTS_PREFIX = Bytes.of(4);
private static final Bytes BLOCK_HASH_PREFIX = Bytes.of(5);
private static final Bytes TOTAL_DIFFICULTY_PREFIX = Bytes.of(6);
private static final Bytes TRANSACTION_LOCATION_PREFIX = Bytes.of(7);
final KeyValueStorage storage;
final KeyValueStorage blockchainStorage;
final VariablesStorage variablesStorage;
final BlockHeaderFunctions blockHeaderFunctions;
public KeyValueStoragePrefixedKeyBlockchainStorage(
final KeyValueStorage storage, final BlockHeaderFunctions blockHeaderFunctions) {
this.storage = storage;
final KeyValueStorage blockchainStorage,
final VariablesStorage variablesStorage,
final BlockHeaderFunctions blockHeaderFunctions) {
this.blockchainStorage = blockchainStorage;
this.variablesStorage = variablesStorage;
this.blockHeaderFunctions = blockHeaderFunctions;
migrateVariables();
}
@Override
public Optional<Hash> getChainHead() {
return get(VARIABLES_PREFIX, CHAIN_HEAD_KEY).map(this::bytesToHash);
return variablesStorage.getChainHead();
}
@Override
public Collection<Hash> getForkHeads() {
return get(VARIABLES_PREFIX, FORK_HEADS_KEY)
.map(bytes -> RLP.input(bytes).readList(in -> this.bytesToHash(in.readBytes32())))
.orElse(Lists.newArrayList());
return variablesStorage.getForkHeads();
}
@Override
public Optional<Hash> getFinalized() {
return get(VARIABLES_PREFIX, FINALIZED_BLOCK_HASH_KEY).map(this::bytesToHash);
return variablesStorage.getFinalized();
}
@Override
public Optional<Hash> getSafeBlock() {
return get(VARIABLES_PREFIX, SAFE_BLOCK_HASH_KEY).map(this::bytesToHash);
return variablesStorage.getSafeBlock();
}
@Override
@ -121,7 +125,7 @@ public class KeyValueStoragePrefixedKeyBlockchainStorage implements BlockchainSt
@Override
public Updater updater() {
return new Updater(storage.startTransaction());
return new Updater(blockchainStorage.startTransaction(), variablesStorage.updater());
}
private List<TransactionReceipt> rlpDecodeTransactionReceipts(final Bytes bytes) {
@ -133,15 +137,68 @@ public class KeyValueStoragePrefixedKeyBlockchainStorage implements BlockchainSt
}
Optional<Bytes> get(final Bytes prefix, final Bytes key) {
return storage.get(Bytes.concatenate(prefix, key).toArrayUnsafe()).map(Bytes::wrap);
return blockchainStorage.get(Bytes.concatenate(prefix, key).toArrayUnsafe()).map(Bytes::wrap);
}
public void migrateVariables() {
final var blockchainUpdater = updater();
final var variablesUpdater = variablesStorage.updater();
get(VARIABLES_PREFIX, CHAIN_HEAD_HASH.getBytes())
.map(this::bytesToHash)
.ifPresent(
ch -> {
variablesUpdater.setChainHead(ch);
LOG.info("Migrated key {} to variables storage", CHAIN_HEAD_HASH);
});
get(VARIABLES_PREFIX, FINALIZED_BLOCK_HASH.getBytes())
.map(this::bytesToHash)
.ifPresent(
fh -> {
variablesUpdater.setFinalized(fh);
LOG.info("Migrated key {} to variables storage", FINALIZED_BLOCK_HASH);
});
get(VARIABLES_PREFIX, SAFE_BLOCK_HASH.getBytes())
.map(this::bytesToHash)
.ifPresent(
sh -> {
variablesUpdater.setSafeBlock(sh);
LOG.info("Migrated key {} to variables storage", SAFE_BLOCK_HASH);
});
get(VARIABLES_PREFIX, FORK_HEADS.getBytes())
.map(bytes -> RLP.input(bytes).readList(in -> this.bytesToHash(in.readBytes32())))
.ifPresent(
fh -> {
variablesUpdater.setForkHeads(fh);
LOG.info("Migrated key {} to variables storage", FORK_HEADS);
});
get(Bytes.EMPTY, SEQ_NO_STORE.getBytes())
.ifPresent(
sns -> {
variablesUpdater.setLocalEnrSeqno(sns);
LOG.info("Migrated key {} to variables storage", SEQ_NO_STORE);
});
blockchainUpdater.removeVariables();
variablesUpdater.commit();
blockchainUpdater.commit();
}
public static class Updater implements BlockchainStorage.Updater {
private final KeyValueStorageTransaction transaction;
private final KeyValueStorageTransaction blockchainTransaction;
private final VariablesStorage.Updater variablesUpdater;
Updater(final KeyValueStorageTransaction transaction) {
this.transaction = transaction;
Updater(
final KeyValueStorageTransaction blockchainTransaction,
final VariablesStorage.Updater variablesUpdater) {
this.blockchainTransaction = blockchainTransaction;
this.variablesUpdater = variablesUpdater;
}
@Override
@ -178,24 +235,22 @@ public class KeyValueStoragePrefixedKeyBlockchainStorage implements BlockchainSt
@Override
public void setChainHead(final Hash blockHash) {
set(VARIABLES_PREFIX, CHAIN_HEAD_KEY, blockHash);
variablesUpdater.setChainHead(blockHash);
}
@Override
public void setForkHeads(final Collection<Hash> forkHeadHashes) {
final Bytes data =
RLP.encode(o -> o.writeList(forkHeadHashes, (val, out) -> out.writeBytes(val)));
set(VARIABLES_PREFIX, FORK_HEADS_KEY, data);
variablesUpdater.setForkHeads(forkHeadHashes);
}
@Override
public void setFinalized(final Hash blockHash) {
set(VARIABLES_PREFIX, FINALIZED_BLOCK_HASH_KEY, blockHash);
variablesUpdater.setFinalized(blockHash);
}
@Override
public void setSafeBlock(final Hash blockHash) {
set(VARIABLES_PREFIX, SAFE_BLOCK_HASH_KEY, blockHash);
variablesUpdater.setSafeBlock(blockHash);
}
@Override
@ -230,24 +285,35 @@ public class KeyValueStoragePrefixedKeyBlockchainStorage implements BlockchainSt
@Override
public void commit() {
transaction.commit();
blockchainTransaction.commit();
variablesUpdater.commit();
}
@Override
public void rollback() {
transaction.rollback();
variablesUpdater.rollback();
blockchainTransaction.rollback();
}
void set(final Bytes prefix, final Bytes key, final Bytes value) {
transaction.put(Bytes.concatenate(prefix, key).toArrayUnsafe(), value.toArrayUnsafe());
blockchainTransaction.put(
Bytes.concatenate(prefix, key).toArrayUnsafe(), value.toArrayUnsafe());
}
private void remove(final Bytes prefix, final Bytes key) {
transaction.remove(Bytes.concatenate(prefix, key).toArrayUnsafe());
blockchainTransaction.remove(Bytes.concatenate(prefix, key).toArrayUnsafe());
}
private Bytes rlpEncode(final List<TransactionReceipt> receipts) {
return RLP.encode(o -> o.writeList(receipts, TransactionReceipt::writeToWithRevertReason));
}
private void removeVariables() {
remove(VARIABLES_PREFIX, CHAIN_HEAD_HASH.getBytes());
remove(VARIABLES_PREFIX, FINALIZED_BLOCK_HASH.getBytes());
remove(VARIABLES_PREFIX, SAFE_BLOCK_HASH.getBytes());
remove(VARIABLES_PREFIX, FORK_HEADS.getBytes());
remove(Bytes.EMPTY, SEQ_NO_STORE.getBytes());
}
}
}

@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.storage.keyvalue;
import org.hyperledger.besu.ethereum.bonsai.storage.BonsaiWorldStateKeyValueStorage;
import org.hyperledger.besu.ethereum.chain.BlockchainStorage;
import org.hyperledger.besu.ethereum.chain.VariablesStorage;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.storage.StorageProvider;
@ -44,18 +45,6 @@ public class KeyValueStorageProvider implements StorageProvider {
protected final Map<SegmentIdentifier, KeyValueStorage> storageInstances = new HashMap<>();
private final ObservableMetricsSystem metricsSystem;
public KeyValueStorageProvider(
final Function<SegmentIdentifier, KeyValueStorage> storageCreator,
final KeyValueStorage worldStatePreimageStorage,
final boolean segmentIsolationSupported,
final ObservableMetricsSystem metricsSystem) {
this.storageCreator = storageCreator;
this.worldStatePreimageStorage = worldStatePreimageStorage;
this.isWorldStateIterable = segmentIsolationSupported;
this.isWorldStateSnappable = SNAPSHOT_ISOLATION_UNSUPPORTED;
this.metricsSystem = metricsSystem;
}
public KeyValueStorageProvider(
final Function<SegmentIdentifier, KeyValueStorage> storageCreator,
final KeyValueStorage worldStatePreimageStorage,
@ -70,9 +59,17 @@ public class KeyValueStorageProvider implements StorageProvider {
}
@Override
public BlockchainStorage createBlockchainStorage(final ProtocolSchedule protocolSchedule) {
public VariablesStorage createVariablesStorage() {
return new VariablesKeyValueStorage(
getStorageBySegmentIdentifier(KeyValueSegmentIdentifier.VARIABLES));
}
@Override
public BlockchainStorage createBlockchainStorage(
final ProtocolSchedule protocolSchedule, final VariablesStorage variablesStorage) {
return new KeyValueStoragePrefixedKeyBlockchainStorage(
getStorageBySegmentIdentifier(KeyValueSegmentIdentifier.BLOCKCHAIN),
variablesStorage,
ScheduleBasedBlockHeaderFunctions.create(protocolSchedule));
}

@ -0,0 +1,145 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.storage.keyvalue;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.CHAIN_HEAD_HASH;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.FINALIZED_BLOCK_HASH;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.FORK_HEADS;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.SAFE_BLOCK_HASH;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.SEQ_NO_STORE;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.chain.VariablesStorage;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorage;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction;
import java.util.Collection;
import java.util.Optional;
import com.google.common.collect.Lists;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
public class VariablesKeyValueStorage implements VariablesStorage {
final KeyValueStorage variables;
public VariablesKeyValueStorage(final KeyValueStorage variables) {
this.variables = variables;
}
@Override
public Optional<Hash> getChainHead() {
return getVariable(CHAIN_HEAD_HASH).map(this::bytesToHash);
}
@Override
public Collection<Hash> getForkHeads() {
return getVariable(FORK_HEADS)
.map(bytes -> RLP.input(bytes).readList(in -> this.bytesToHash(in.readBytes32())))
.orElse(Lists.newArrayList());
}
@Override
public Optional<Hash> getFinalized() {
return getVariable(FINALIZED_BLOCK_HASH).map(this::bytesToHash);
}
@Override
public Optional<Hash> getSafeBlock() {
return getVariable(SAFE_BLOCK_HASH).map(this::bytesToHash);
}
@Override
public Optional<Bytes> getLocalEnrSeqno() {
return getVariable(SEQ_NO_STORE).map(Bytes::wrap);
}
@Override
public Updater updater() {
return new Updater(variables.startTransaction());
}
private Hash bytesToHash(final Bytes bytes) {
return Hash.wrap(Bytes32.wrap(bytes, 0));
}
Optional<Bytes> getVariable(final Keys key) {
return variables.get(key.toByteArray()).map(Bytes::wrap);
}
public static class Updater implements VariablesStorage.Updater {
private final KeyValueStorageTransaction variablesTransaction;
Updater(final KeyValueStorageTransaction variablesTransaction) {
this.variablesTransaction = variablesTransaction;
}
@Override
public void setChainHead(final Hash blockHash) {
setVariable(CHAIN_HEAD_HASH, blockHash);
}
@Override
public void setForkHeads(final Collection<Hash> forkHeadHashes) {
final Bytes data =
RLP.encode(o -> o.writeList(forkHeadHashes, (val, out) -> out.writeBytes(val)));
setVariable(FORK_HEADS, data);
}
@Override
public void setFinalized(final Hash blockHash) {
setVariable(FINALIZED_BLOCK_HASH, blockHash);
}
@Override
public void setSafeBlock(final Hash blockHash) {
setVariable(SAFE_BLOCK_HASH, blockHash);
}
@Override
public void setLocalEnrSeqno(final Bytes nodeRecord) {
setVariable(SEQ_NO_STORE, nodeRecord);
}
@Override
public void removeAll() {
removeVariable(CHAIN_HEAD_HASH);
removeVariable(FINALIZED_BLOCK_HASH);
removeVariable(SAFE_BLOCK_HASH);
removeVariable(FORK_HEADS);
removeVariable(SEQ_NO_STORE);
}
@Override
public void commit() {
variablesTransaction.commit();
}
@Override
public void rollback() {
variablesTransaction.rollback();
}
void setVariable(final Keys key, final Bytes value) {
variablesTransaction.put(key.toByteArray(), value.toArrayUnsafe());
}
void removeVariable(final Keys key) {
variablesTransaction.remove(key.toByteArray());
}
}
}

@ -27,6 +27,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
import org.hyperledger.besu.ethereum.storage.keyvalue.VariablesKeyValueStorage;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
@ -40,7 +41,8 @@ import java.util.function.Function;
public class ExecutionContextTestFixture {
private final Block genesis;
private final KeyValueStorage keyValueStorage;
private final KeyValueStorage blockchainKeyValueStorage;
private final KeyValueStorage variablesKeyValueStorage;
private final MutableBlockchain blockchain;
private final WorldStateArchive stateArchive;
@ -49,15 +51,20 @@ public class ExecutionContextTestFixture {
private static final GenesisConfigFile genesisConfigFile = GenesisConfigFile.mainnet();
private ExecutionContextTestFixture(
final ProtocolSchedule protocolSchedule, final KeyValueStorage keyValueStorage) {
final ProtocolSchedule protocolSchedule,
final KeyValueStorage blockchainKeyValueStorage,
final KeyValueStorage variablesKeyValueStorage) {
final GenesisState genesisState = GenesisState.fromConfig(genesisConfigFile, protocolSchedule);
this.genesis = genesisState.getBlock();
this.keyValueStorage = keyValueStorage;
this.blockchainKeyValueStorage = blockchainKeyValueStorage;
this.variablesKeyValueStorage = variablesKeyValueStorage;
this.blockchain =
DefaultBlockchain.createMutable(
genesis,
new KeyValueStoragePrefixedKeyBlockchainStorage(
keyValueStorage, new MainnetBlockHeaderFunctions()),
blockchainKeyValueStorage,
new VariablesKeyValueStorage(variablesKeyValueStorage),
new MainnetBlockHeaderFunctions()),
new NoOpMetricsSystem(),
0);
this.stateArchive = createInMemoryWorldStateArchive();
@ -78,8 +85,12 @@ public class ExecutionContextTestFixture {
return genesis;
}
public KeyValueStorage getKeyValueStorage() {
return keyValueStorage;
public KeyValueStorage getBlockchainKeyValueStorage() {
return blockchainKeyValueStorage;
}
public KeyValueStorage getVariablesKeyValueStorage() {
return variablesKeyValueStorage;
}
public MutableBlockchain getBlockchain() {
@ -99,12 +110,17 @@ public class ExecutionContextTestFixture {
}
public static class Builder {
private KeyValueStorage keyValueStorage;
private KeyValueStorage variablesKeyValueStorage;
private KeyValueStorage blockchainKeyValueStorage;
private ProtocolSchedule protocolSchedule;
public Builder keyValueStorage(final KeyValueStorage keyValueStorage) {
this.keyValueStorage = keyValueStorage;
public Builder variablesKeyValueStorage(final KeyValueStorage keyValueStorage) {
this.variablesKeyValueStorage = keyValueStorage;
return this;
}
public Builder blockchainKeyValueStorage(final KeyValueStorage keyValueStorage) {
this.blockchainKeyValueStorage = keyValueStorage;
return this;
}
@ -125,10 +141,14 @@ public class ExecutionContextTestFixture {
EvmConfiguration.DEFAULT)
.createProtocolSchedule();
}
if (keyValueStorage == null) {
keyValueStorage = new InMemoryKeyValueStorage();
if (blockchainKeyValueStorage == null) {
blockchainKeyValueStorage = new InMemoryKeyValueStorage();
}
if (variablesKeyValueStorage == null) {
variablesKeyValueStorage = new InMemoryKeyValueStorage();
}
return new ExecutionContextTestFixture(protocolSchedule, keyValueStorage);
return new ExecutionContextTestFixture(
protocolSchedule, variablesKeyValueStorage, blockchainKeyValueStorage);
}
}
}

@ -19,11 +19,13 @@ import org.hyperledger.besu.ethereum.bonsai.cache.CachedMerkleTrieLoader;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.chain.DefaultBlockchain;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.chain.VariablesStorage;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateKeyValueStorage;
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProvider;
import org.hyperledger.besu.ethereum.storage.keyvalue.VariablesKeyValueStorage;
import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStateKeyValueStorage;
import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStatePreimageKeyValueStorage;
import org.hyperledger.besu.ethereum.worldstate.DataStorageFormat;
@ -44,15 +46,30 @@ public class InMemoryKeyValueStorageProvider extends KeyValueStorageProvider {
}
public static MutableBlockchain createInMemoryBlockchain(final Block genesisBlock) {
return createInMemoryBlockchain(genesisBlock, new MainnetBlockHeaderFunctions());
return createInMemoryBlockchain(genesisBlock, createInMemoryVariablesStorage());
}
public static MutableBlockchain createInMemoryBlockchain(
final Block genesisBlock, final VariablesStorage variablesStorage) {
return createInMemoryBlockchain(
genesisBlock, new MainnetBlockHeaderFunctions(), variablesStorage);
}
public static MutableBlockchain createInMemoryBlockchain(
final Block genesisBlock, final BlockHeaderFunctions blockHeaderFunctions) {
return createInMemoryBlockchain(
genesisBlock, blockHeaderFunctions, createInMemoryVariablesStorage());
}
public static MutableBlockchain createInMemoryBlockchain(
final Block genesisBlock,
final BlockHeaderFunctions blockHeaderFunctions,
final VariablesStorage variablesStorage) {
final InMemoryKeyValueStorage keyValueStorage = new InMemoryKeyValueStorage();
return DefaultBlockchain.createMutable(
genesisBlock,
new KeyValueStoragePrefixedKeyBlockchainStorage(keyValueStorage, blockHeaderFunctions),
new KeyValueStoragePrefixedKeyBlockchainStorage(
keyValueStorage, variablesStorage, blockHeaderFunctions),
new NoOpMetricsSystem(),
0);
}
@ -87,4 +104,8 @@ public class InMemoryKeyValueStorageProvider extends KeyValueStorageProvider {
public static PrivateStateStorage createInMemoryPrivateStateStorage() {
return new PrivateStateKeyValueStorage(new InMemoryKeyValueStorage());
}
public static VariablesStorage createInMemoryVariablesStorage() {
return new VariablesKeyValueStorage(new InMemoryKeyValueStorage());
}
}

@ -0,0 +1,153 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.core;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.CHAIN_HEAD_HASH;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.FINALIZED_BLOCK_HASH;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.FORK_HEADS;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.SAFE_BLOCK_HASH;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.SEQ_NO_STORE;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorage;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
public class VariablesStorageHelper {
public static final Bytes VARIABLES_PREFIX = Bytes.of(1);
public static final Hash SAMPLE_CHAIN_HEAD = Hash.fromHexStringLenient("0x01234");
public static final Hash SAMPLE_FINALIZED = Hash.fromHexStringLenient("0x56789");
public static final Hash SAMPLE_SAFE = Hash.fromHexStringLenient("0xabcde");
public static final Hash FORK1 = Hash.fromHexStringLenient("0xf1357");
public static final Hash FORK2 = Hash.fromHexStringLenient("0xf2468");
public static final Bytes SAMPLE_FORK_HEADS =
RLP.encode(o -> o.writeList(List.of(FORK1, FORK2), (val, out) -> out.writeBytes(val)));
public static final Bytes SAMPLE_ERN_SEQNO = Bytes.fromHexStringLenient("0xabc123");
public static final EnumSet<Keys> NOT_PREFIXED_KEYS = EnumSet.of(SEQ_NO_STORE);
public static Map<Keys, Bytes> getSampleVariableValues() {
final var variableValues = new EnumMap<Keys, Bytes>(Keys.class);
variableValues.put(CHAIN_HEAD_HASH, SAMPLE_CHAIN_HEAD);
variableValues.put(FINALIZED_BLOCK_HASH, SAMPLE_FINALIZED);
variableValues.put(SAFE_BLOCK_HASH, SAMPLE_SAFE);
variableValues.put(FORK_HEADS, SAMPLE_FORK_HEADS);
variableValues.put(SEQ_NO_STORE, SAMPLE_ERN_SEQNO);
return variableValues;
}
public static void assertNoVariablesInStorage(final KeyValueStorage kvStorage) {
assertThat(kvStorage.streamKeys()).isEmpty();
}
public static void assertVariablesPresentInVariablesStorage(
final KeyValueStorage kvVariables, final Map<Keys, Bytes> variableValues) {
assertVariablesPresentInStorage(kvVariables, Bytes.EMPTY, variableValues);
}
public static void assertVariablesPresentInBlockchainStorage(
final KeyValueStorage kvBlockchain, final Map<Keys, Bytes> variableValues) {
assertVariablesPresentInStorage(kvBlockchain, VARIABLES_PREFIX, variableValues);
}
public static void assertVariablesPresentInStorage(
final KeyValueStorage kvStorage, final Bytes prefix, final Map<Keys, Bytes> variableValues) {
variableValues.forEach(
(k, v) ->
assertThat(
kvStorage.get(
Bytes.concatenate(
(NOT_PREFIXED_KEYS.contains(k) ? Bytes.EMPTY : prefix),
k.getBytes())
.toArrayUnsafe()))
.contains(v.toArrayUnsafe()));
}
public static void assertVariablesReturnedByBlockchainStorage(
final KeyValueStoragePrefixedKeyBlockchainStorage blockchainStorage,
final Map<Keys, Bytes> variableValues) {
variableValues.computeIfPresent(
CHAIN_HEAD_HASH,
(k, v) -> {
assertThat(blockchainStorage.getChainHead()).isPresent().contains(bytesToHash(v));
return v;
});
variableValues.computeIfPresent(
FINALIZED_BLOCK_HASH,
(k, v) -> {
assertThat(blockchainStorage.getFinalized()).isPresent().contains(bytesToHash(v));
return v;
});
variableValues.computeIfPresent(
SAFE_BLOCK_HASH,
(k, v) -> {
assertThat(blockchainStorage.getSafeBlock()).isPresent().contains(bytesToHash(v));
return v;
});
variableValues.computeIfPresent(
FORK_HEADS,
(k, v) -> {
assertThat(blockchainStorage.getForkHeads())
.containsExactlyElementsOf(
RLP.input(v).readList(in -> bytesToHash(in.readBytes32())));
return v;
});
}
public static void populateBlockchainStorage(
final KeyValueStorage storage, final Map<Keys, Bytes> variableValues) {
populateStorage(storage, VARIABLES_PREFIX, variableValues);
}
public static void populateVariablesStorage(
final KeyValueStorage storage, final Map<Keys, Bytes> variableValues) {
populateStorage(storage, Bytes.EMPTY, variableValues);
}
public static void populateStorage(
final KeyValueStorage storage, final Bytes prefix, final Map<Keys, Bytes> variableValues) {
populateVariables(storage, prefix, variableValues);
}
public static void populateVariables(
final KeyValueStorage storage, final Bytes prefix, final Map<Keys, Bytes> variableValues) {
final var tx = storage.startTransaction();
variableValues.forEach(
(k, v) -> putVariable(tx, (NOT_PREFIXED_KEYS.contains(k) ? Bytes.EMPTY : prefix), k, v));
tx.commit();
}
public static void putVariable(
final KeyValueStorageTransaction tx, final Bytes prefix, final Keys key, final Bytes value) {
tx.put(Bytes.concatenate(prefix, key.getBytes()).toArrayUnsafe(), value.toArrayUnsafe());
}
public static Hash bytesToHash(final Bytes bytes) {
return Hash.wrap(Bytes32.wrap(bytes, 0));
}
}

@ -21,6 +21,7 @@ import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
import org.hyperledger.besu.ethereum.storage.keyvalue.VariablesKeyValueStorage;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
@ -38,7 +39,9 @@ public class ChainDataPrunerTest {
final BlockDataGenerator gen = new BlockDataGenerator();
final BlockchainStorage blockchainStorage =
new KeyValueStoragePrefixedKeyBlockchainStorage(
new InMemoryKeyValueStorage(), new MainnetBlockHeaderFunctions());
new InMemoryKeyValueStorage(),
new VariablesKeyValueStorage(new InMemoryKeyValueStorage()),
new MainnetBlockHeaderFunctions());
final ChainDataPruner chainDataPruner =
new ChainDataPruner(
blockchainStorage,
@ -74,7 +77,9 @@ public class ChainDataPrunerTest {
final BlockDataGenerator gen = new BlockDataGenerator();
final BlockchainStorage blockchainStorage =
new KeyValueStoragePrefixedKeyBlockchainStorage(
new InMemoryKeyValueStorage(), new MainnetBlockHeaderFunctions());
new InMemoryKeyValueStorage(),
new VariablesKeyValueStorage(new InMemoryKeyValueStorage()),
new MainnetBlockHeaderFunctions());
final ChainDataPruner chainDataPruner =
new ChainDataPruner(
blockchainStorage,

@ -28,6 +28,7 @@ import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
import org.hyperledger.besu.ethereum.storage.keyvalue.VariablesKeyValueStorage;
import org.hyperledger.besu.metrics.MetricsSystemFactory;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration;
@ -52,8 +53,10 @@ public class DefaultBlockchainTest {
final BlockDataGenerator gen = new BlockDataGenerator();
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final Block genesisBlock = gen.genesisBlock();
final DefaultBlockchain blockchain = createMutableBlockchain(kvStore, genesisBlock);
final DefaultBlockchain blockchain =
createMutableBlockchain(kvStore, kvStoreVariables, genesisBlock);
assertBlockDataIsStored(blockchain, genesisBlock, Collections.emptyList());
assertBlockIsHead(blockchain, genesisBlock);
@ -67,11 +70,13 @@ public class DefaultBlockchainTest {
// Write to kv store
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final Block genesisBlock = gen.genesisBlock();
createMutableBlockchain(kvStore, genesisBlock);
createMutableBlockchain(kvStore, kvStoreVariables, genesisBlock);
// Initialize a new blockchain store with kvStore that already contains data
final DefaultBlockchain blockchain = createMutableBlockchain(kvStore, genesisBlock);
final DefaultBlockchain blockchain =
createMutableBlockchain(kvStore, kvStoreVariables, genesisBlock);
assertBlockDataIsStored(blockchain, genesisBlock, Collections.emptyList());
assertBlockIsHead(blockchain, genesisBlock);
@ -85,11 +90,15 @@ public class DefaultBlockchainTest {
// Write to kv store
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final Block genesisBlock = gen.genesisBlock();
createMutableBlockchain(kvStore, genesisBlock);
createMutableBlockchain(kvStore, kvStoreVariables, genesisBlock);
// Initialize a new blockchain store with same kvStore, but different genesis block
assertThatThrownBy(() -> createMutableBlockchain(kvStore, gen.genesisBlock(), "/test/path"))
assertThatThrownBy(
() ->
createMutableBlockchain(
kvStore, kvStoreVariables, gen.genesisBlock(), "/test/path"))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining(
"Supplied genesis block does not match chain data stored in /test/path.\n"
@ -101,13 +110,14 @@ public class DefaultBlockchainTest {
public void initializeReadOnly_withGenesisBlock() {
final BlockDataGenerator gen = new BlockDataGenerator();
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final Block genesisBlock = gen.genesisBlock();
// Write genesis block to storage
createMutableBlockchain(kvStore, genesisBlock);
createMutableBlockchain(kvStore, kvStoreVariables, genesisBlock);
// Create read only chain
final Blockchain blockchain = createBlockchain(kvStore);
final Blockchain blockchain = createBlockchain(kvStore, kvStoreVariables);
assertBlockDataIsStored(blockchain, genesisBlock, Collections.emptyList());
assertBlockIsHead(blockchain, genesisBlock);
@ -118,12 +128,14 @@ public class DefaultBlockchainTest {
public void initializeReadOnly_withSmallChain() {
final BlockDataGenerator gen = new BlockDataGenerator();
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final List<Block> blocks = gen.blockSequence(10);
final List<List<TransactionReceipt>> blockReceipts = new ArrayList<>(blocks.size());
blockReceipts.add(Collections.emptyList());
// Write small chain to storage
final MutableBlockchain mutableBlockchain = createMutableBlockchain(kvStore, blocks.get(0));
final MutableBlockchain mutableBlockchain =
createMutableBlockchain(kvStore, kvStoreVariables, blocks.get(0));
for (int i = 1; i < blocks.size(); i++) {
final Block block = blocks.get(i);
final List<TransactionReceipt> receipts = gen.receipts(block);
@ -132,7 +144,7 @@ public class DefaultBlockchainTest {
}
// Create read only chain
final Blockchain blockchain = createBlockchain(kvStore);
final Blockchain blockchain = createBlockchain(kvStore, kvStoreVariables);
for (int i = 0; i < blocks.size(); i++) {
assertBlockDataIsStored(blockchain, blocks.get(i), blockReceipts.get(i));
@ -148,12 +160,14 @@ public class DefaultBlockchainTest {
gen.setBlockOptionsSupplier(
() -> BlockOptions.create().setDifficulty(Difficulty.of(Long.MAX_VALUE)));
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final List<Block> blocks = gen.blockSequence(10);
final List<List<TransactionReceipt>> blockReceipts = new ArrayList<>(blocks.size());
blockReceipts.add(Collections.emptyList());
// Write small chain to storage
final MutableBlockchain mutableBlockchain = createMutableBlockchain(kvStore, blocks.get(0));
final MutableBlockchain mutableBlockchain =
createMutableBlockchain(kvStore, kvStoreVariables, blocks.get(0));
for (int i = 1; i < blocks.size(); i++) {
final Block block = blocks.get(i);
final List<TransactionReceipt> receipts = gen.receipts(block);
@ -164,7 +178,7 @@ public class DefaultBlockchainTest {
// Create read only chain
final Blockchain blockchain =
DefaultBlockchain.create(
createStorage(kvStore),
createStorage(kvStore, kvStoreVariables),
MetricsSystemFactory.create(MetricsConfiguration.builder().enabled(true).build()),
0);
@ -179,8 +193,8 @@ public class DefaultBlockchainTest {
@Test
public void initializeReadOnly_emptyStorage() {
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
assertThatThrownBy(() -> createBlockchain(kvStore))
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
assertThatThrownBy(() -> createBlockchain(kvStore, kvStoreVariables))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Cannot create Blockchain from empty storage");
}
@ -190,8 +204,10 @@ public class DefaultBlockchainTest {
final BlockDataGenerator gen = new BlockDataGenerator();
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final Block genesisBlock = gen.genesisBlock();
final DefaultBlockchain blockchain = createMutableBlockchain(kvStore, genesisBlock);
final DefaultBlockchain blockchain =
createMutableBlockchain(kvStore, kvStoreVariables, genesisBlock);
final BlockDataGenerator.BlockOptions options =
new BlockDataGenerator.BlockOptions()
@ -218,8 +234,10 @@ public class DefaultBlockchainTest {
final BlockDataGenerator gen = new BlockDataGenerator();
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final Block genesisBlock = gen.genesisBlock();
final DefaultBlockchain blockchain = createMutableBlockchain(kvStore, genesisBlock);
final DefaultBlockchain blockchain =
createMutableBlockchain(kvStore, kvStoreVariables, genesisBlock);
final BlockDataGenerator.BlockOptions options =
new BlockDataGenerator.BlockOptions().setBlockNumber(1L).setParentHash(Hash.ZERO);
@ -234,8 +252,10 @@ public class DefaultBlockchainTest {
final BlockDataGenerator gen = new BlockDataGenerator();
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final Block genesisBlock = gen.genesisBlock();
final DefaultBlockchain blockchain = createMutableBlockchain(kvStore, genesisBlock);
final DefaultBlockchain blockchain =
createMutableBlockchain(kvStore, kvStoreVariables, genesisBlock);
final BlockDataGenerator.BlockOptions options =
new BlockDataGenerator.BlockOptions()
@ -256,7 +276,9 @@ public class DefaultBlockchainTest {
chain.stream().map(gen::receipts).collect(Collectors.toList());
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
final DefaultBlockchain blockchain = createMutableBlockchain(kvStore, chain.get(0));
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final DefaultBlockchain blockchain =
createMutableBlockchain(kvStore, kvStoreVariables, chain.get(0));
for (int i = 1; i < chain.size(); i++) {
blockchain.appendBlock(chain.get(i), blockReceipts.get(i));
}
@ -281,7 +303,9 @@ public class DefaultBlockchainTest {
final List<List<TransactionReceipt>> blockReceipts =
chain.stream().map(gen::receipts).collect(Collectors.toList());
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
final DefaultBlockchain blockchain = createMutableBlockchain(kvStore, chain.get(0));
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final DefaultBlockchain blockchain =
createMutableBlockchain(kvStore, kvStoreVariables, chain.get(0));
// Listen to block events and add the Logs here
List<LogWithMetadata> logsWithMetadata = new ArrayList<>();
@ -358,7 +382,9 @@ public class DefaultBlockchainTest {
final List<List<TransactionReceipt>> blockReceipts =
chain.stream().map(gen::receipts).collect(Collectors.toList());
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
final DefaultBlockchain blockchain = createMutableBlockchain(kvStore, chain.get(0));
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final DefaultBlockchain blockchain =
createMutableBlockchain(kvStore, kvStoreVariables, chain.get(0));
// Listen to block events and add the Logs here
List<LogWithMetadata> logsWithMetadata = new ArrayList<>();
blockchain.observeBlockAdded(event -> logsWithMetadata.addAll(event.getLogsWithMetadata()));
@ -477,7 +503,9 @@ public class DefaultBlockchainTest {
final List<List<TransactionReceipt>> blockReceipts =
chain.stream().map(gen::receipts).collect(Collectors.toList());
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
final DefaultBlockchain blockchain = createMutableBlockchain(kvStore, chain.get(0));
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final DefaultBlockchain blockchain =
createMutableBlockchain(kvStore, kvStoreVariables, chain.get(0));
// Listen to block events and add the Logs here
List<LogWithMetadata> logsWithMetadata = new ArrayList<>();
blockchain.observeBlockAdded(event -> logsWithMetadata.addAll(event.getLogsWithMetadata()));
@ -587,7 +615,9 @@ public class DefaultBlockchainTest {
final List<List<TransactionReceipt>> blockReceipts =
chain.stream().map(gen::receipts).collect(Collectors.toList());
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
final DefaultBlockchain blockchain = createMutableBlockchain(kvStore, chain.get(0));
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final DefaultBlockchain blockchain =
createMutableBlockchain(kvStore, kvStoreVariables, chain.get(0));
// Listen to block events and add the Logs here
List<LogWithMetadata> logsWithMetadata = new ArrayList<>();
blockchain.observeBlockAdded(event -> logsWithMetadata.addAll(event.getLogsWithMetadata()));
@ -658,7 +688,9 @@ public class DefaultBlockchainTest {
final List<List<TransactionReceipt>> blockReceipts =
chain.stream().map(gen::receipts).collect(Collectors.toList());
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
final DefaultBlockchain blockchain = createMutableBlockchain(kvStore, chain.get(0));
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final DefaultBlockchain blockchain =
createMutableBlockchain(kvStore, kvStoreVariables, chain.get(0));
for (int i = 1; i < chain.size(); i++) {
blockchain.appendBlock(chain.get(i), blockReceipts.get(i));
}
@ -697,7 +729,9 @@ public class DefaultBlockchainTest {
final List<List<TransactionReceipt>> blockReceipts =
chain.stream().map(gen::receipts).collect(Collectors.toList());
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
final DefaultBlockchain blockchain = createMutableBlockchain(kvStore, chain.get(0));
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final DefaultBlockchain blockchain =
createMutableBlockchain(kvStore, kvStoreVariables, chain.get(0));
// Listen to block events and add the Logs here
List<LogWithMetadata> logsWithMetadata = new ArrayList<>();
blockchain.observeBlockAdded(event -> logsWithMetadata.addAll(event.getLogsWithMetadata()));
@ -782,8 +816,10 @@ public class DefaultBlockchainTest {
public void blockAddedObserver_removeNonexistentObserver() {
final BlockDataGenerator gen = new BlockDataGenerator();
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final Block genesisBlock = gen.genesisBlock();
final DefaultBlockchain blockchain = createMutableBlockchain(kvStore, genesisBlock);
final DefaultBlockchain blockchain =
createMutableBlockchain(kvStore, kvStoreVariables, genesisBlock);
assertThat(blockchain.removeObserver(7)).isFalse();
}
@ -793,8 +829,10 @@ public class DefaultBlockchainTest {
final BlockDataGenerator gen = new BlockDataGenerator();
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final Block genesisBlock = gen.genesisBlock();
final DefaultBlockchain blockchain = createMutableBlockchain(kvStore, genesisBlock);
final DefaultBlockchain blockchain =
createMutableBlockchain(kvStore, kvStoreVariables, genesisBlock);
final long observerId = blockchain.observeBlockAdded(__ -> {});
assertThat(blockchain.observerCount()).isEqualTo(1);
@ -808,8 +846,10 @@ public class DefaultBlockchainTest {
final BlockDataGenerator gen = new BlockDataGenerator();
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final Block genesisBlock = gen.genesisBlock();
final DefaultBlockchain blockchain = createMutableBlockchain(kvStore, genesisBlock);
final DefaultBlockchain blockchain =
createMutableBlockchain(kvStore, kvStoreVariables, genesisBlock);
assertThatThrownBy(() -> blockchain.observeBlockAdded(null))
.isInstanceOf(NullPointerException.class);
@ -820,8 +860,10 @@ public class DefaultBlockchainTest {
final BlockDataGenerator gen = new BlockDataGenerator();
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final Block genesisBlock = gen.genesisBlock();
final DefaultBlockchain blockchain = createMutableBlockchain(kvStore, genesisBlock);
final DefaultBlockchain blockchain =
createMutableBlockchain(kvStore, kvStoreVariables, genesisBlock);
final long observerId1 = blockchain.observeBlockAdded(__ -> {});
assertThat(blockchain.observerCount()).isEqualTo(1);
@ -847,8 +889,10 @@ public class DefaultBlockchainTest {
final BlockDataGenerator gen = new BlockDataGenerator();
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final Block genesisBlock = gen.genesisBlock();
final DefaultBlockchain blockchain = createMutableBlockchain(kvStore, genesisBlock);
final DefaultBlockchain blockchain =
createMutableBlockchain(kvStore, kvStoreVariables, genesisBlock);
final BlockDataGenerator.BlockOptions options =
new BlockDataGenerator.BlockOptions()
@ -870,8 +914,10 @@ public class DefaultBlockchainTest {
final BlockDataGenerator gen = new BlockDataGenerator();
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final Block genesisBlock = gen.genesisBlock();
final DefaultBlockchain blockchain = createMutableBlockchain(kvStore, genesisBlock);
final DefaultBlockchain blockchain =
createMutableBlockchain(kvStore, kvStoreVariables, genesisBlock);
final BlockDataGenerator.BlockOptions options =
new BlockDataGenerator.BlockOptions()
@ -944,26 +990,40 @@ public class DefaultBlockchainTest {
assertThat(blockchain.getChainHead().getTotalDifficulty()).isEqualTo(td);
}
private BlockchainStorage createStorage(final KeyValueStorage kvStore) {
private BlockchainStorage createStorage(
final KeyValueStorage kvStoreChain, final KeyValueStorage kvStorageVariables) {
return new KeyValueStoragePrefixedKeyBlockchainStorage(
kvStore, new MainnetBlockHeaderFunctions());
kvStoreChain,
new VariablesKeyValueStorage(kvStorageVariables),
new MainnetBlockHeaderFunctions());
}
private DefaultBlockchain createMutableBlockchain(
final KeyValueStorage kvStore, final Block genesisBlock) {
final KeyValueStorage kvStore,
final KeyValueStorage kvStorageVariables,
final Block genesisBlock) {
return (DefaultBlockchain)
DefaultBlockchain.createMutable(
genesisBlock, createStorage(kvStore), new NoOpMetricsSystem(), 0);
genesisBlock, createStorage(kvStore, kvStorageVariables), new NoOpMetricsSystem(), 0);
}
private DefaultBlockchain createMutableBlockchain(
final KeyValueStorage kvStore, final Block genesisBlock, final String dataDirectory) {
final KeyValueStorage kvStore,
final KeyValueStorage kvStorageVariables,
final Block genesisBlock,
final String dataDirectory) {
return (DefaultBlockchain)
DefaultBlockchain.createMutable(
genesisBlock, createStorage(kvStore), new NoOpMetricsSystem(), 0, dataDirectory);
genesisBlock,
createStorage(kvStore, kvStorageVariables),
new NoOpMetricsSystem(),
0,
dataDirectory);
}
private Blockchain createBlockchain(final KeyValueStorage kvStore) {
return DefaultBlockchain.create(createStorage(kvStore), new NoOpMetricsSystem(), 0);
private Blockchain createBlockchain(
final KeyValueStorage kvStore, final KeyValueStorage kvStorageVariables) {
return DefaultBlockchain.create(
createStorage(kvStore, kvStorageVariables), new NoOpMetricsSystem(), 0);
}
}

@ -0,0 +1,103 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.storage.keyvalue;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.FINALIZED_BLOCK_HASH;
import static org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys.SAFE_BLOCK_HASH;
import static org.hyperledger.besu.ethereum.core.VariablesStorageHelper.assertNoVariablesInStorage;
import static org.hyperledger.besu.ethereum.core.VariablesStorageHelper.assertVariablesPresentInVariablesStorage;
import static org.hyperledger.besu.ethereum.core.VariablesStorageHelper.assertVariablesReturnedByBlockchainStorage;
import static org.hyperledger.besu.ethereum.core.VariablesStorageHelper.getSampleVariableValues;
import static org.hyperledger.besu.ethereum.core.VariablesStorageHelper.populateBlockchainStorage;
import static org.hyperledger.besu.ethereum.core.VariablesStorageHelper.populateVariablesStorage;
import static org.mockito.Mockito.mock;
import org.hyperledger.besu.ethereum.chain.VariablesStorage;
import org.hyperledger.besu.ethereum.chain.VariablesStorage.Keys;
import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorage;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
import java.util.Map;
import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class KeyValueStoragePrefixedKeyBlockchainStorageTest {
private final BlockHeaderFunctions blockHeaderFunctions = mock(BlockHeaderFunctions.class);
private KeyValueStorage kvBlockchain;
private KeyValueStorage kvVariables;
private VariablesStorage variablesStorage;
private Map<Keys, Bytes> variableValues;
@BeforeEach
public void setup() {
kvBlockchain = new InMemoryKeyValueStorage();
kvVariables = new InMemoryKeyValueStorage();
variablesStorage = new VariablesKeyValueStorage(kvVariables);
variableValues = getSampleVariableValues();
}
@Test
public void migrationToVariablesStorage() {
populateBlockchainStorage(kvBlockchain, variableValues);
assertNoVariablesInStorage(kvVariables);
final var blockchainStorage =
new KeyValueStoragePrefixedKeyBlockchainStorage(
kvBlockchain, variablesStorage, blockHeaderFunctions);
assertNoVariablesInStorage(kvBlockchain);
assertVariablesPresentInVariablesStorage(kvVariables, variableValues);
assertVariablesReturnedByBlockchainStorage(blockchainStorage, variableValues);
}
@Test
public void migrationToVariablesStorageWhenSomeVariablesDoNotExist() {
variableValues.remove(FINALIZED_BLOCK_HASH);
variableValues.remove(SAFE_BLOCK_HASH);
populateBlockchainStorage(kvBlockchain, variableValues);
assertNoVariablesInStorage(kvVariables);
final var blockchainStorage =
new KeyValueStoragePrefixedKeyBlockchainStorage(
kvBlockchain, variablesStorage, blockHeaderFunctions);
assertNoVariablesInStorage(kvBlockchain);
assertVariablesPresentInVariablesStorage(kvVariables, variableValues);
assertVariablesReturnedByBlockchainStorage(blockchainStorage, variableValues);
}
@Test
public void doesNothingIfVariablesAlreadyMigrated() {
populateVariablesStorage(kvVariables, variableValues);
assertNoVariablesInStorage(kvBlockchain);
final var blockchainStorage =
new KeyValueStoragePrefixedKeyBlockchainStorage(
kvBlockchain, variablesStorage, blockHeaderFunctions);
assertNoVariablesInStorage(kvBlockchain);
assertVariablesPresentInVariablesStorage(kvVariables, variableValues);
assertVariablesReturnedByBlockchainStorage(blockchainStorage, variableValues);
}
}

@ -31,6 +31,7 @@ import org.hyperledger.besu.ethereum.core.BlockDataGenerator.BlockOptions;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
import org.hyperledger.besu.ethereum.storage.keyvalue.VariablesKeyValueStorage;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
import org.hyperledger.besu.testutil.MockExecutorService;
@ -60,7 +61,9 @@ public class PrunerTest {
public void shouldMarkCorrectBlockAndSweep() throws ExecutionException, InterruptedException {
final BlockchainStorage blockchainStorage =
new KeyValueStoragePrefixedKeyBlockchainStorage(
new InMemoryKeyValueStorage(), new MainnetBlockHeaderFunctions());
new InMemoryKeyValueStorage(),
new VariablesKeyValueStorage(new InMemoryKeyValueStorage()),
new MainnetBlockHeaderFunctions());
final MutableBlockchain blockchain =
DefaultBlockchain.createMutable(genesisBlock, blockchainStorage, metricsSystem, 0);
@ -81,7 +84,9 @@ public class PrunerTest {
public void shouldOnlySweepAfterBlockConfirmationPeriodAndRetentionPeriodEnds() {
final BlockchainStorage blockchainStorage =
new KeyValueStoragePrefixedKeyBlockchainStorage(
new InMemoryKeyValueStorage(), new MainnetBlockHeaderFunctions());
new InMemoryKeyValueStorage(),
new VariablesKeyValueStorage(new InMemoryKeyValueStorage()),
new MainnetBlockHeaderFunctions());
final MutableBlockchain blockchain =
DefaultBlockchain.createMutable(genesisBlock, blockchainStorage, metricsSystem, 0);
@ -107,7 +112,9 @@ public class PrunerTest {
public void abortsPruningWhenFullyMarkedBlockNoLongerOnCanonicalChain() {
final BlockchainStorage blockchainStorage =
new KeyValueStoragePrefixedKeyBlockchainStorage(
new InMemoryKeyValueStorage(), new MainnetBlockHeaderFunctions());
new InMemoryKeyValueStorage(),
new VariablesKeyValueStorage(new InMemoryKeyValueStorage()),
new MainnetBlockHeaderFunctions());
final MutableBlockchain blockchain =
DefaultBlockchain.createMutable(genesisBlock, blockchainStorage, metricsSystem, 0);
@ -177,7 +184,9 @@ public class PrunerTest {
public void shouldCleanUpPruningStrategyOnShutdown() throws InterruptedException {
final BlockchainStorage blockchainStorage =
new KeyValueStoragePrefixedKeyBlockchainStorage(
new InMemoryKeyValueStorage(), new MainnetBlockHeaderFunctions());
new InMemoryKeyValueStorage(),
new VariablesKeyValueStorage(new InMemoryKeyValueStorage()),
new MainnetBlockHeaderFunctions());
final MutableBlockchain blockchain =
DefaultBlockchain.createMutable(genesisBlock, blockchainStorage, metricsSystem, 0);

@ -27,6 +27,7 @@ import org.hyperledger.besu.ethereum.core.BlockWithReceipts;
import org.hyperledger.besu.ethereum.eth.sync.fastsync.checkpoint.Checkpoint;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
import org.hyperledger.besu.ethereum.storage.keyvalue.VariablesKeyValueStorage;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
@ -49,7 +50,9 @@ public class CheckPointBlockImportStepTest {
public void setup() {
blockchainStorage =
new KeyValueStoragePrefixedKeyBlockchainStorage(
new InMemoryKeyValueStorage(), new MainnetBlockHeaderFunctions());
new InMemoryKeyValueStorage(),
new VariablesKeyValueStorage(new InMemoryKeyValueStorage()),
new MainnetBlockHeaderFunctions());
blockchain =
DefaultBlockchain.createMutable(
generateBlock(0), blockchainStorage, mock(MetricsSystem.class), 0);

@ -19,6 +19,7 @@ import org.hyperledger.besu.ethereum.chain.BlockchainStorage;
import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
import org.hyperledger.besu.ethereum.storage.keyvalue.VariablesKeyValueStorage;
import org.hyperledger.besu.plugin.services.BesuConfiguration;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorage;
@ -53,6 +54,20 @@ public class DataStoreModule {
List.of(KeyValueSegmentIdentifier.values()),
RocksDBMetricsFactory.PUBLIC_ROCKS_DB_METRICS));
@Provides
@Singleton
@Named("variables")
KeyValueStorage provideVariablesKeyValueStorage(
@Named("KeyValueStorageName") final String keyValueStorageName,
final BesuConfiguration commonConfiguration,
final MetricsSystem metricsSystem) {
return constructKeyValueStorage(
keyValueStorageName,
commonConfiguration,
metricsSystem,
KeyValueSegmentIdentifier.VARIABLES);
}
@Provides
@Singleton
@Named("blockchain")
@ -127,7 +142,9 @@ public class DataStoreModule {
@Singleton
static BlockchainStorage provideBlockchainStorage(
@Named("blockchain") final KeyValueStorage keyValueStorage,
@Named("variables") final KeyValueStorage variablesKeyValueStorage,
final BlockHeaderFunctions blockHashFunction) {
return new KeyValueStoragePrefixedKeyBlockchainStorage(keyValueStorage, blockHashFunction);
return new KeyValueStoragePrefixedKeyBlockchainStorage(
keyValueStorage, new VariablesKeyValueStorage(variablesKeyValueStorage), blockHashFunction);
}
}

@ -15,12 +15,12 @@
package org.hyperledger.besu.ethereum.p2p.discovery;
import static com.google.common.base.Preconditions.checkArgument;
import static java.nio.charset.StandardCharsets.UTF_8;
import org.hyperledger.besu.crypto.Hash;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.cryptoservices.NodeKey;
import org.hyperledger.besu.ethereum.chain.VariablesStorage;
import org.hyperledger.besu.ethereum.forkid.ForkIdManager;
import org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.Packet;
@ -34,12 +34,9 @@ import org.hyperledger.besu.ethereum.p2p.peers.PeerId;
import org.hyperledger.besu.ethereum.p2p.permissions.PeerPermissions;
import org.hyperledger.besu.ethereum.p2p.rlpx.RlpxAgent;
import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier;
import org.hyperledger.besu.nat.NatService;
import org.hyperledger.besu.plugin.data.EnodeURL;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorage;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction;
import org.hyperledger.besu.util.NetworkUtility;
import java.net.InetSocketAddress;
@ -70,7 +67,6 @@ import org.slf4j.LoggerFactory;
*/
public abstract class PeerDiscoveryAgent {
private static final Logger LOG = LoggerFactory.getLogger(PeerDiscoveryAgent.class);
private static final String SEQ_NO_STORE_KEY = "local-enr-seqno";
private static final com.google.common.base.Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
@ -101,7 +97,7 @@ public abstract class PeerDiscoveryAgent {
/* Is discovery enabled? */
private boolean isActive = false;
private final StorageProvider storageProvider;
private final VariablesStorage variablesStorage;
private final Supplier<List<Bytes>> forkIdSupplier;
private String advertisedAddress;
@ -130,7 +126,7 @@ public abstract class PeerDiscoveryAgent {
this.id = nodeKey.getPublicKey().getEncodedBytes();
this.storageProvider = storageProvider;
this.variablesStorage = storageProvider.createVariablesStorage();
this.forkIdManager = forkIdManager;
this.forkIdSupplier = () -> forkIdManager.getForkIdForChainHead().getForkIdAsBytesList();
this.rlpxAgent = rlpxAgent;
@ -187,14 +183,9 @@ public abstract class PeerDiscoveryAgent {
return;
}
final KeyValueStorage keyValueStorage =
storageProvider.getStorageBySegmentIdentifier(KeyValueSegmentIdentifier.BLOCKCHAIN);
final NodeRecordFactory nodeRecordFactory = NodeRecordFactory.DEFAULT;
final Optional<NodeRecord> existingNodeRecord =
keyValueStorage
.get(Bytes.of(SEQ_NO_STORE_KEY.getBytes(UTF_8)).toArray())
.map(Bytes::of)
.map(nodeRecordFactory::fromBytes);
variablesStorage.getLocalEnrSeqno().map(nodeRecordFactory::fromBytes);
final Bytes addressBytes = Bytes.of(InetAddresses.forString(advertisedAddress).getAddress());
final Optional<EnodeURL> maybeEnodeURL = localNode.map(DiscoveryPeer::getEnodeURL);
@ -236,12 +227,10 @@ public abstract class PeerDiscoveryAgent {
.slice(0, 64));
LOG.info("Writing node record to disk. {}", nodeRecord);
final KeyValueStorageTransaction keyValueStorageTransaction =
keyValueStorage.startTransaction();
keyValueStorageTransaction.put(
Bytes.wrap(SEQ_NO_STORE_KEY.getBytes(UTF_8)).toArray(),
nodeRecord.serialize().toArray());
keyValueStorageTransaction.commit();
final var variablesUpdater = variablesStorage.updater();
variablesUpdater.setLocalEnrSeqno(nodeRecord.serialize());
variablesUpdater.commit();
return nodeRecord;
});
localNode

@ -28,6 +28,7 @@ import org.hyperledger.besu.ethereum.blockcreation.IncrementingNonceGenerator;
import org.hyperledger.besu.ethereum.chain.DefaultBlockchain;
import org.hyperledger.besu.ethereum.chain.GenesisState;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.chain.VariablesStorage;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions;
@ -55,6 +56,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
import org.hyperledger.besu.ethereum.storage.keyvalue.VariablesKeyValueStorage;
import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStateKeyValueStorage;
import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStatePreimageKeyValueStorage;
import org.hyperledger.besu.ethereum.worldstate.DefaultWorldStateArchive;
@ -254,9 +256,12 @@ public class RetestethContext {
private static MutableBlockchain createInMemoryBlockchain(
final Block genesisBlock, final BlockHeaderFunctions blockHeaderFunctions) {
final InMemoryKeyValueStorage keyValueStorage = new InMemoryKeyValueStorage();
final VariablesStorage variablesStorage =
new VariablesKeyValueStorage(new InMemoryKeyValueStorage());
return DefaultBlockchain.createMutable(
genesisBlock,
new KeyValueStoragePrefixedKeyBlockchainStorage(keyValueStorage, blockHeaderFunctions),
new KeyValueStoragePrefixedKeyBlockchainStorage(
keyValueStorage, variablesStorage, blockHeaderFunctions),
new NoOpMetricsSystem(),
100);
}

@ -218,24 +218,9 @@ public abstract class RocksDBColumnarKeyValueStorage
}
BlockBasedTableConfig createBlockBasedTableConfig(final RocksDBConfiguration config) {
if (config.isHighSpec()) return createBlockBasedTableConfigHighSpec();
else return createBlockBasedTableConfigDefault(config);
}
private BlockBasedTableConfig createBlockBasedTableConfigHighSpec() {
final LRUCache cache = new LRUCache(ROCKSDB_BLOCKCACHE_SIZE_HIGH_SPEC);
return new BlockBasedTableConfig()
.setFormatVersion(ROCKSDB_FORMAT_VERSION)
.setBlockCache(cache)
.setFilterPolicy(new BloomFilter(10, false))
.setPartitionFilters(true)
.setCacheIndexAndFilterBlocks(false)
.setBlockSize(ROCKSDB_BLOCK_SIZE);
}
private BlockBasedTableConfig createBlockBasedTableConfigDefault(
final RocksDBConfiguration config) {
final LRUCache cache = new LRUCache(config.getCacheCapacity());
final LRUCache cache =
new LRUCache(
config.isHighSpec() ? ROCKSDB_BLOCKCACHE_SIZE_HIGH_SPEC : config.getCacheCapacity());
return new BlockBasedTableConfig()
.setFormatVersion(ROCKSDB_FORMAT_VERSION)
.setBlockCache(cache)

Loading…
Cancel
Save