Skip to content

Commit

Permalink
Introduce variables storage (hyperledger#5471)
Browse files Browse the repository at this point in the history
Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
  • Loading branch information
fab-10 authored and eum602 committed Nov 3, 2023
1 parent f43acf0 commit 4cf481b
Show file tree
Hide file tree
Showing 31 changed files with 1,177 additions and 169 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
13 changes: 13 additions & 0 deletions besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
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;
Expand Down Expand Up @@ -140,6 +141,7 @@
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;
Expand Down Expand Up @@ -1598,6 +1600,8 @@ private void addSubCommands(final InputStream in) {
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);
Expand Down Expand Up @@ -2979,6 +2983,15 @@ private KeyValueStorageProvider keyValueStorageProvider(final String name) {
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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand All @@ -128,6 +131,7 @@ public class BesuController implements java.io.Closeable {
this.miningParameters = miningParameters;
this.additionalPluginServices = additionalPluginServices;
this.ethPeers = ethPeers;
this.storageProvider = storageProvider;
}

/**
Expand Down Expand Up @@ -220,6 +224,15 @@ public EthPeers getEthPeers() {
return ethPeers;
}

/**
* Get the storage provider
*
* @return the storage provider
*/
public StorageProvider getStorageProvider() {
return storageProvider;
}

@Override
public void close() {
closeables.forEach(this::tryClose);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
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;
Expand Down Expand Up @@ -561,11 +562,14 @@ public BesuController build() {

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(
Expand Down Expand Up @@ -773,7 +777,8 @@ public BesuController build() {
nodeKey,
closeables,
additionalPluginServices,
ethPeers);
ethPeers,
storageProvider);
}

/**
Expand Down
15 changes: 8 additions & 7 deletions besu/src/test/java/org/hyperledger/besu/RunnerBuilderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@

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;

Expand Down Expand Up @@ -163,7 +164,7 @@ public void enodeUrlShouldHaveAdvertisedHostWhenDiscoveryDisabled() {
.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();
Expand Down Expand Up @@ -223,7 +224,7 @@ public void movingAcrossProtocolSpecsUpdatesNodeRecord() {
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)
Expand Down Expand Up @@ -265,7 +266,7 @@ public void whenEngineApiAddedListensOnDefaultPort() {
.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();
Expand Down Expand Up @@ -307,7 +308,7 @@ public void whenEngineApiAddedWebSocketReadyOnSamePort() {
.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();
Expand Down Expand Up @@ -348,7 +349,7 @@ public void whenEngineApiAddedEthSubscribeAvailable() {
.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();
Expand Down Expand Up @@ -390,7 +391,7 @@ public void noEngineApiNoServiceForMethods() {
.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())
Expand Down
Loading

0 comments on commit 4cf481b

Please sign in to comment.