From b74a7f2133147aa9b82d58e4eaad596a465755ba Mon Sep 17 00:00:00 2001 From: phillip Yun Date: Tue, 4 Feb 2025 19:19:45 +0900 Subject: [PATCH] add Xchain-pruning-blocks-retained must be >= epochlength (#7963) (#8140) Signed-off-by: philosup --- .../org/hyperledger/besu/cli/BesuCommand.java | 38 ++++++-- .../hyperledger/besu/cli/BesuCommandTest.java | 87 +++++++++++++++++++ 2 files changed, 118 insertions(+), 7 deletions(-) diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index 8a39f9bfc1d..b628e420cf1 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -1587,13 +1587,37 @@ private void validateRpcWsOptions() { } private void validateChainDataPruningParams() { - if (unstableChainPruningOptions.getChainDataPruningEnabled() - && unstableChainPruningOptions.getChainDataPruningBlocksRetained() - < unstableChainPruningOptions.getChainDataPruningBlocksRetainedLimit()) { - throw new ParameterException( - this.commandLine, - "--Xchain-pruning-blocks-retained must be >= " - + unstableChainPruningOptions.getChainDataPruningBlocksRetainedLimit()); + Long chainDataPruningBlocksRetained = + unstableChainPruningOptions.getChainDataPruningBlocksRetained(); + if (unstableChainPruningOptions.getChainDataPruningEnabled()) { + final GenesisConfigOptions genesisConfigOptions = readGenesisConfigOptions(); + if (chainDataPruningBlocksRetained + < unstableChainPruningOptions.getChainDataPruningBlocksRetainedLimit()) { + throw new ParameterException( + this.commandLine, + "--Xchain-pruning-blocks-retained must be >= " + + unstableChainPruningOptions.getChainDataPruningBlocksRetainedLimit()); + } else if (genesisConfigOptions.isPoa()) { + Long epochLength = 0L; + String consensusMechanism = ""; + if (genesisConfigOptions.isIbft2()) { + epochLength = genesisConfigOptions.getBftConfigOptions().getEpochLength(); + consensusMechanism = "IBFT2"; + } else if (genesisConfigOptions.isQbft()) { + epochLength = genesisConfigOptions.getQbftConfigOptions().getEpochLength(); + consensusMechanism = "QBFT"; + } else if (genesisConfigOptions.isClique()) { + epochLength = genesisConfigOptions.getCliqueConfigOptions().getEpochLength(); + consensusMechanism = "Clique"; + } + if (chainDataPruningBlocksRetained < epochLength) { + throw new ParameterException( + this.commandLine, + String.format( + "--Xchain-pruning-blocks-retained(%d) must be >= epochlength(%d) for %s", + chainDataPruningBlocksRetained, epochLength, consensusMechanism)); + } + } } } diff --git a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java index 515ad59e3dc..733efd9162c 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java @@ -2598,4 +2598,91 @@ void helpOutputShouldDisplayCorrectDefaultValues() { assertThat(errorOutputString).isEmpty(); } + + @Test + void chainPruningEnabledWithPOAShouldFailWhenChainPruningBlocksRetainedValueLessThanEpochLength() + throws IOException { + JsonObject genesis = GENESIS_VALID_JSON; + + // for QBFT + genesis.getJsonObject("config").put("qbft", new JsonObject().put("epochlength", 25000)); + final Path genesisFileQBFT = createFakeGenesisFile(genesis); + parseCommand( + "--genesis-file", + genesisFileQBFT.toString(), + "--Xchain-pruning-enabled=true", + "--Xchain-pruning-blocks-retained=7200", + "--version-compatibility-protection=false"); + assertThat(commandErrorOutput.toString(UTF_8)) + .contains("--Xchain-pruning-blocks-retained(7200) must be >= epochlength(25000) for QBFT"); + commandErrorOutput.reset(); + + // for IBFT2 + genesis.getJsonObject("config").put("ibft2", new JsonObject().put("epochlength", 20000)); + genesis.getJsonObject("config").remove("qbft"); + final Path genesisFileIBFT = createFakeGenesisFile(genesis); + parseCommand( + "--genesis-file", + genesisFileIBFT.toString(), + "--Xchain-pruning-enabled=true", + "--Xchain-pruning-blocks-retained=7200", + "--version-compatibility-protection=false"); + assertThat(commandErrorOutput.toString(UTF_8)) + .contains("--Xchain-pruning-blocks-retained(7200) must be >= epochlength(20000) for IBFT2"); + commandErrorOutput.reset(); + + // for Clique + genesis.getJsonObject("config").put("clique", new JsonObject().put("epochlength", 10000)); + genesis.getJsonObject("config").remove("ibft2"); + final Path genesisFileClique = createFakeGenesisFile(genesis); + parseCommand( + "--genesis-file", + genesisFileClique.toString(), + "--Xchain-pruning-enabled=true", + "--Xchain-pruning-blocks-retained=7200", + "--version-compatibility-protection=false"); + assertThat(commandErrorOutput.toString(UTF_8)) + .contains( + "--Xchain-pruning-blocks-retained(7200) must be >= epochlength(10000) for Clique"); + } + + @Test + void chainPruningEnabledWithPOA() throws IOException { + JsonObject genesis = GENESIS_VALID_JSON; + // for QBFT + genesis.getJsonObject("config").put("qbft", new JsonObject().put("epochlength", 25000)); + final Path genesisFileForQBFT = createFakeGenesisFile(genesis); + parseCommand( + "--genesis-file", + genesisFileForQBFT.toString(), + "--Xchain-pruning-enabled=true", + "--Xchain-pruning-blocks-retained=25000", + "--version-compatibility-protection=false"); + assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); + + // for IBFT2 + genesis.getJsonObject("config").put("ibft2", new JsonObject().put("epochlength", 20000)); + genesis.getJsonObject("config").remove("qbft"); + final Path genesisFileIBFT = createFakeGenesisFile(genesis); + parseCommand( + "--genesis-file", + genesisFileIBFT.toString(), + "--Xchain-pruning-enabled=true", + "--Xchain-pruning-blocks-retained=20000", + "--version-compatibility-protection=false"); + + assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); + + // for Clique + genesis.getJsonObject("config").put("clique", new JsonObject().put("epochlength", 10000)); + genesis.getJsonObject("config").remove("ibft2"); + final Path genesisFileClique = createFakeGenesisFile(genesis); + parseCommand( + "--genesis-file", + genesisFileClique.toString(), + "--Xchain-pruning-enabled=true", + "--Xchain-pruning-blocks-retained=10000", + "--version-compatibility-protection=false"); + assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); + } }