Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add chain data pruning experimental feature #4686

Merged
merged 40 commits into from
Dec 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
fce33c1
Add chain pruner
wcgcyx Nov 11, 2022
be8f081
Add tests and options
wcgcyx Nov 15, 2022
af59579
Add pruning frequency
wcgcyx Nov 16, 2022
16de0e9
Fix unit tests
wcgcyx Nov 17, 2022
3e67042
Fix chain pruning tests
wcgcyx Nov 17, 2022
0d433a3
Increase minimum blocks to retain
wcgcyx Nov 18, 2022
0e545c5
Skip ancestor check in pruning mode
wcgcyx Nov 21, 2022
4e0e627
Merge branch 'main' into 4476-chain-data-pruning
wcgcyx Nov 21, 2022
9313b02
Insert changelog entry & fix build issue
wcgcyx Nov 21, 2022
3e3596b
Fix unit test
wcgcyx Nov 21, 2022
d0807ff
Merge branch 'main' into 4476-chain-data-pruning
wcgcyx Nov 21, 2022
4c1d792
Fix changelog & address reviews
wcgcyx Nov 22, 2022
95e86a5
Fix minor issues & code cleanup
wcgcyx Nov 24, 2022
a63542a
Separate class for pruning storage
wcgcyx Nov 24, 2022
580942a
Merge branch 'main' into 4476-chain-data-pruning
wcgcyx Nov 24, 2022
328005b
Rename options
wcgcyx Nov 25, 2022
c34154f
Merge branch 'main' into 4476-chain-data-pruning
wcgcyx Nov 25, 2022
64ba456
Move pruning to separate thread
wcgcyx Nov 29, 2022
a42a933
Merge branch 'main' into 4476-chain-data-pruning
wcgcyx Nov 29, 2022
322b327
Merge branch 'main' into 4476-chain-data-pruning
wcgcyx Dec 1, 2022
cd37f0f
Merge branch 'main' into 4476-chain-data-pruning
wcgcyx Dec 2, 2022
ac79611
Update changelog
wcgcyx Dec 2, 2022
51c6c6d
Remove global variable & decrease minimum allowed retaining block
wcgcyx Dec 5, 2022
96c2934
Limit total pruning threads
wcgcyx Dec 5, 2022
cbc460b
Update pruner constructor
wcgcyx Dec 5, 2022
99b273f
Merge branch 'main' into 4476-chain-data-pruning
wcgcyx Dec 5, 2022
cc4648e
Update besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
wcgcyx Dec 6, 2022
8315393
Spotless & Remove unnecessary comments
wcgcyx Dec 6, 2022
5992b66
More clear variable
wcgcyx Dec 6, 2022
5d4da4e
Clear logging
wcgcyx Dec 6, 2022
118178e
Fix bug & use monitored executors
wcgcyx Dec 6, 2022
91102a7
New domain object
wcgcyx Dec 7, 2022
962adb6
Fix tests
wcgcyx Dec 7, 2022
1368c55
Move flag to merge context
wcgcyx Dec 7, 2022
d41661b
Merge branch 'main' into 4476-chain-data-pruning
wcgcyx Dec 7, 2022
2a439f1
Renaming
wcgcyx Dec 8, 2022
d29a286
Rename tx
wcgcyx Dec 9, 2022
e2c088c
Merge branch 'main' into 4476-chain-data-pruning
wcgcyx Dec 9, 2022
25d514d
Fix issues
wcgcyx Dec 9, 2022
571a022
Merge branch 'main' into 4476-chain-data-pruning
jframe Dec 12, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
- Increase the speed of modexp gas execution and execution. [#4780](https://github.com/hyperledger/besu/pull/4780)
- Added experimental CLI options `--Xeth-capability-max` and `--Xeth-capability-min` to specify a range of capabilities to be supported by the Eth protocol. [#4752](https://github.com/hyperledger/besu/pull/4752)
- Set the default curve in the EVMTool, like is done in production operations [#4790](https://github.com/hyperledger/besu/pull/4790)
- Add chain data pruning feature with three experimental CLI options: `--Xchain-pruning-enabled`, `--Xchain-pruning-blocks-retained` and `--Xchain-pruning-frequency` [#4686](https://github.com/hyperledger/besu/pull/4686)

### Bug Fixes
- Fix storage key format for eth_getProof so that it follows the EIP-1474 spec [#4564](https://github.com/hyperledger/besu/pull/4564)
Expand Down
18 changes: 17 additions & 1 deletion besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import org.hyperledger.besu.cli.options.stable.LoggingLevelOption;
import org.hyperledger.besu.cli.options.stable.NodePrivateKeyFileOption;
import org.hyperledger.besu.cli.options.stable.P2PTLSConfigOptions;
import org.hyperledger.besu.cli.options.unstable.ChainPruningOptions;
import org.hyperledger.besu.cli.options.unstable.DnsOptions;
import org.hyperledger.besu.cli.options.unstable.EthProtocolOptions;
import org.hyperledger.besu.cli.options.unstable.EvmOptions;
Expand Down Expand Up @@ -289,6 +290,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
private final PrivacyPluginOptions unstablePrivacyPluginOptions = PrivacyPluginOptions.create();
private final EvmOptions unstableEvmOptions = EvmOptions.create();
private final IpcOptions unstableIpcOptions = IpcOptions.create();
private final ChainPruningOptions unstableChainPruningOptions = ChainPruningOptions.create();

// stable CLI options
private final DataStorageOptions dataStorageOptions = DataStorageOptions.create();
Expand Down Expand Up @@ -1539,6 +1541,7 @@ private void handleUnstableOptions() {
.put("Launcher", unstableLauncherOptions)
.put("EVM Options", unstableEvmOptions)
.put("IPC Options", unstableIpcOptions)
.put("Chain Data Pruning Options", unstableChainPruningOptions)
.build();

UnstableOptionsSubCommand.createUnstableOptions(commandLine, unstableOptions);
Expand Down Expand Up @@ -1776,6 +1779,7 @@ private void validateOptions() {
validateDnsOptionsParams();
ensureValidPeerBoundParams();
validateRpcOptionsParams();
validateChainDataPruningParams();
p2pTLSConfigOptions.checkP2PTLSOptionsDependencies(logger, commandLine);
pkiBlockCreationOptions.checkPkiBlockCreationOptionsDependencies(logger, commandLine);
}
Expand Down Expand Up @@ -1927,6 +1931,17 @@ public void validateRpcOptionsParams() {
}
}

public void validateChainDataPruningParams() {
siladu marked this conversation as resolved.
Show resolved Hide resolved
if (unstableChainPruningOptions.getChainDataPruningEnabled()
&& unstableChainPruningOptions.getChainDataPruningBlocksRetained()
< ChainPruningOptions.DEFAULT_CHAIN_DATA_PRUNING_MIN_BLOCKS_RETAINED) {
throw new ParameterException(
this.commandLine,
"--Xchain-pruning-blocks-retained must be >= "
+ ChainPruningOptions.DEFAULT_CHAIN_DATA_PRUNING_MIN_BLOCKS_RETAINED);
}
}

private GenesisConfigOptions readGenesisConfigOptions() {

try {
Expand Down Expand Up @@ -2184,7 +2199,8 @@ public BesuControllerBuilder getControllerBuilder() {
.reorgLoggingThreshold(reorgLoggingThreshold)
.evmConfiguration(unstableEvmOptions.toDomainObject())
.dataStorageConfiguration(dataStorageOptions.toDomainObject())
.maxPeers(p2PDiscoveryOptionGroup.maxPeers);
.maxPeers(p2PDiscoveryOptionGroup.maxPeers)
.chainPruningConfiguration(unstableChainPruningOptions.toDomainObject());
}

private GraphQLConfiguration graphQLConfiguration() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* 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.options.unstable;

import org.hyperledger.besu.cli.options.CLIOptions;
import org.hyperledger.besu.ethereum.chain.ChainPrunerConfiguration;
import org.hyperledger.besu.util.number.PositiveNumber;

import java.util.Arrays;
import java.util.List;

import picocli.CommandLine;

public class ChainPruningOptions implements CLIOptions<ChainPrunerConfiguration> {
private static final String CHAIN_PRUNING_ENABLED_FLAG = "--Xchain-pruning-enabled";
private static final String CHAIN_PRUNING_BLOCKS_RETAINED_FLAG =
"--Xchain-pruning-blocks-retained";
private static final String CHAIN_PRUNING_FREQUENCY_FLAG = "--Xchain-pruning-frequency";
public static final long DEFAULT_CHAIN_DATA_PRUNING_MIN_BLOCKS_RETAINED = 7200;
public static final int DEFAULT_CHAIN_DATA_PRUNING_FREQUENCY = 256;

@CommandLine.Option(
hidden = true,
names = {CHAIN_PRUNING_ENABLED_FLAG},
description =
"Enable the chain pruner to actively prune old chain data (default: ${DEFAULT-VALUE})")
private final Boolean chainDataPruningEnabled = Boolean.FALSE;

@CommandLine.Option(
hidden = true,
names = {CHAIN_PRUNING_BLOCKS_RETAINED_FLAG},
description =
"The number of recent blocks for which to keep the chain data. Must be >= "
+ DEFAULT_CHAIN_DATA_PRUNING_MIN_BLOCKS_RETAINED
+ " (default: ${DEFAULT-VALUE})")
private final Long chainDataPruningBlocksRetained =
DEFAULT_CHAIN_DATA_PRUNING_MIN_BLOCKS_RETAINED;

@CommandLine.Option(
hidden = true,
names = {CHAIN_PRUNING_FREQUENCY_FLAG},
description =
"The number of blocks added to the chain between two pruning operations. Must be non-negative (default: ${DEFAULT-VALUE})")
private final PositiveNumber chainDataPruningBlocksFrequency =
PositiveNumber.fromInt(DEFAULT_CHAIN_DATA_PRUNING_FREQUENCY);

public static ChainPruningOptions create() {
return new ChainPruningOptions();
}

public Boolean getChainDataPruningEnabled() {
return chainDataPruningEnabled;
}

public Long getChainDataPruningBlocksRetained() {
return chainDataPruningBlocksRetained;
}

@Override
public ChainPrunerConfiguration toDomainObject() {
return new ChainPrunerConfiguration(
chainDataPruningEnabled,
chainDataPruningBlocksRetained,
chainDataPruningBlocksFrequency.getValue());
}

@Override
public List<String> getCLIOptions() {
return Arrays.asList(
CHAIN_PRUNING_ENABLED_FLAG,
chainDataPruningEnabled.toString(),
CHAIN_PRUNING_BLOCKS_RETAINED_FLAG,
chainDataPruningBlocksRetained.toString(),
CHAIN_PRUNING_FREQUENCY_FLAG,
chainDataPruningBlocksFrequency.toString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
import org.hyperledger.besu.ethereum.bonsai.CachedMerkleTrieLoader;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.chain.BlockchainStorage;
import org.hyperledger.besu.ethereum.chain.ChainDataPruner;
import org.hyperledger.besu.ethereum.chain.ChainDataPrunerStorage;
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;
Expand All @@ -51,6 +54,7 @@
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.manager.MergePeerFilter;
import org.hyperledger.besu.ethereum.eth.manager.MonitoredExecutors;
import org.hyperledger.besu.ethereum.eth.manager.snap.SnapProtocolManager;
import org.hyperledger.besu.ethereum.eth.peervalidation.CheckpointBlocksPeerValidator;
import org.hyperledger.besu.ethereum.eth.peervalidation.ClassicForkPeerValidator;
Expand Down Expand Up @@ -140,6 +144,7 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
Collections.emptyList();
protected EvmConfiguration evmConfiguration;
protected int maxPeers;
protected ChainPrunerConfiguration chainPrunerConfiguration = ChainPrunerConfiguration.DEFAULT;

public BesuControllerBuilder storageProvider(final StorageProvider storageProvider) {
this.storageProvider = storageProvider;
Expand Down Expand Up @@ -268,6 +273,12 @@ public BesuControllerBuilder maxPeers(final int maxPeers) {
return this;
}

public BesuControllerBuilder chainPruningConfiguration(
final ChainPrunerConfiguration chainPrunerConfiguration) {
this.chainPrunerConfiguration = chainPrunerConfiguration;
return this;
}

public BesuController build() {
checkNotNull(genesisConfig, "Missing genesis config");
checkNotNull(syncConfig, "Missing sync config");
Expand Down Expand Up @@ -315,6 +326,22 @@ public BesuController build() {
blockchain, worldStateArchive, protocolSchedule, this::createConsensusContext);
validateContext(protocolContext);

if (chainPrunerConfiguration.getChainPruningEnabled()) {
protocolContext
.safeConsensusContext(MergeContext.class)
.ifPresent(
mergeContext -> {
mergeContext.setIsChainPruningEnabled(true);
});
final ChainDataPruner chainDataPruner = createChainPruner(blockchainStorage);
blockchain.observeBlockAdded(chainDataPruner);
LOG.info(
"Chain data pruning enabled with recent blocks retained to be: "
+ chainPrunerConfiguration.getChainPruningBlocksRetained()
+ " and frequency to be: "
+ chainPrunerConfiguration.getChainPruningBlocksFrequency());
}

protocolSchedule.setPublicWorldStateArchiveForPrivacyBlockProcessor(
protocolContext.getWorldStateArchive());

Expand Down Expand Up @@ -651,6 +678,22 @@ private WorldStateArchive createWorldStateArchive(
}
}

private ChainDataPruner createChainPruner(final BlockchainStorage blockchainStorage) {
return new ChainDataPruner(
blockchainStorage,
new ChainDataPrunerStorage(
storageProvider.getStorageBySegmentIdentifier(
KeyValueSegmentIdentifier.CHAIN_PRUNER_STATE)),
chainPrunerConfiguration.getChainPruningBlocksRetained(),
chainPrunerConfiguration.getChainPruningBlocksFrequency(),
MonitoredExecutors.newBoundedThreadPool(
ChainDataPruner.class.getSimpleName(),
1,
1,
ChainDataPruner.MAX_PRUNING_THREAD_QUEUE_SIZE,
metricsSystem));
}

protected List<PeerValidator> createPeerValidators(final ProtocolSchedule protocolSchedule) {
final List<PeerValidator> validators = new ArrayList<>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ public void initMocks() throws Exception {
when(mockControllerBuilder.dataStorageConfiguration(any())).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.evmConfiguration(any())).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.maxPeers(anyInt())).thenReturn(mockControllerBuilder);

when(mockControllerBuilder.chainPruningConfiguration(any())).thenReturn(mockControllerBuilder);
// doReturn used because of generic BesuController
doReturn(mockController).when(mockControllerBuilder).build();
lenient().when(mockController.getProtocolManager()).thenReturn(mockEthProtocolManager);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,10 @@ void fireNewUnverifiedForkchoiceEvent(
void putPayloadById(final PayloadIdentifier payloadId, final Block block);

Optional<Block> retrieveBlockById(final PayloadIdentifier payloadId);

default void setIsChainPruningEnabled(final boolean isChainPruningEnabled) {}

default boolean isChainPruningEnabled() {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ public class PostMergeContext implements MergeContext {
private final AtomicReference<Optional<BlockHeader>> terminalPoWBlock =
new AtomicReference<>(Optional.empty());

// TODO: cleanup - isChainPruningEnabled will not be required after
// https://github.com/hyperledger/besu/pull/4703 is merged.
private boolean isChainPruningEnabled = false;

@VisibleForTesting
PostMergeContext() {
this(Difficulty.ZERO);
Expand Down Expand Up @@ -265,4 +269,14 @@ private static class PayloadTuple {
this.block = block;
}
}

@Override
public void setIsChainPruningEnabled(final boolean isChainPruningEnabled) {
this.isChainPruningEnabled = isChainPruningEnabled;
}

@Override
public boolean isChainPruningEnabled() {
return isChainPruningEnabled;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext)
}

// TODO: post-merge cleanup, this should be unnecessary after merge
if (!mergeCoordinator.latestValidAncestorDescendsFromTerminal(newHead)) {
if (!mergeCoordinator.latestValidAncestorDescendsFromTerminal(newHead)
&& !mergeContext.get().isChainPruningEnabled()) {
logForkchoiceUpdatedCall(INVALID, forkChoice);
return new JsonRpcSuccessResponse(
requestId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,8 @@ public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext)
}

// TODO: post-merge cleanup
if (!mergeCoordinator.latestValidAncestorDescendsFromTerminal(newBlockHeader)) {
if (!mergeCoordinator.latestValidAncestorDescendsFromTerminal(newBlockHeader)
wcgcyx marked this conversation as resolved.
Show resolved Hide resolved
&& !mergeContext.get().isChainPruningEnabled()) {
mergeCoordinator.addBadBlock(block, Optional.empty());
return respondWithInvalid(
reqId,
Expand Down
1 change: 1 addition & 0 deletions ethereum/core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter'
testImplementation 'org.junit.jupiter:junit-jupiter-params'
testImplementation 'org.mockito:mockito-core'
testImplementation 'org.awaitility:awaitility'

testRuntimeOnly 'org.junit.vintage:junit-vintage-engine'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,16 @@ interface Updater {

void removeBlockHash(long blockNumber);

void removeBlockHeader(final Hash blockHash);

void removeBlockBody(final Hash blockHash);

void removeTransactionReceipts(final Hash blockHash);

void removeTransactionLocation(Hash transactionHash);

void removeTotalDifficulty(final Hash blockHash);

void commit();

void rollback();
Expand Down
Loading