From 647750c201dcd06fac51028bfb93e6242c2e222b Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Wed, 28 Feb 2024 20:12:51 +1000 Subject: [PATCH 1/4] Acceptance tests to use bonsai (#6619) * use given config or else default bonsai Signed-off-by: Sally MacFarlane --------- Signed-off-by: Sally MacFarlane --- .../tests/acceptance/dsl/node/ProcessBesuNodeRunner.java | 7 +++---- .../tests/acceptance/dsl/node/ThreadBesuNodeRunner.java | 8 +++++++- .../node/configuration/BesuNodeConfigurationBuilder.java | 2 +- .../tests/acceptance/AbstractPreexistingNodeTest.java | 3 +++ 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java index 2b80ebea568..d85076ee28b 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java @@ -25,7 +25,6 @@ import org.hyperledger.besu.ethereum.p2p.rlpx.connections.netty.TLSConfiguration; import org.hyperledger.besu.ethereum.permissioning.PermissioningConfiguration; import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; -import org.hyperledger.besu.ethereum.worldstate.ImmutableDataStorageConfiguration; import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration; import org.hyperledger.besu.plugin.services.metrics.MetricCategory; import org.hyperledger.besu.tests.acceptance.dsl.StaticNodesUtils; @@ -114,9 +113,9 @@ public void startNode(final BesuNode node) { params.addAll( DataStorageOptions.fromConfig( - ImmutableDataStorageConfiguration.builder() - .from(DataStorageConfiguration.DEFAULT_FOREST_CONFIG) - .build()) + node.getDataStorageConfiguration() == null + ? DataStorageConfiguration.DEFAULT_BONSAI_CONFIG + : node.getDataStorageConfiguration()) .getCLIOptions()); if (node.getMiningParameters().isMiningEnabled()) { diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java index c4b36df2a92..0104e1ded76 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java @@ -207,6 +207,12 @@ public void startNode(final BesuNode node) { final PluginTransactionValidatorFactory pluginTransactionValidatorFactory = getPluginTransactionValidatorFactory(besuPluginContext); + + final DataStorageConfiguration dataStorageConfiguration = + node.getDataStorageConfiguration() == null + ? DataStorageConfiguration.DEFAULT_BONSAI_CONFIG + : node.getDataStorageConfiguration(); + builder .synchronizerConfiguration(new SynchronizerConfiguration.Builder().build()) .dataDirectory(node.homeDirectory()) @@ -215,7 +221,7 @@ public void startNode(final BesuNode node) { .nodeKey(new NodeKey(new KeyPairSecurityModule(KeyPairUtil.loadKeyPair(dataDir)))) .metricsSystem(metricsSystem) .transactionPoolConfiguration(txPoolConfig) - .dataStorageConfiguration(DataStorageConfiguration.DEFAULT_FOREST_CONFIG) + .dataStorageConfiguration(dataStorageConfiguration) .ethProtocolConfiguration(EthProtocolConfiguration.defaultConfig()) .clock(Clock.systemUTC()) .isRevertReasonEnabled(node.isRevertReasonEnabled()) diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java index 1a9a16f36f5..d83743ff447 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java @@ -75,7 +75,7 @@ public class BesuNodeConfigurationBuilder { private Optional permissioningConfiguration = Optional.empty(); private ApiConfiguration apiConfiguration = ImmutableApiConfiguration.builder().build(); private DataStorageConfiguration dataStorageConfiguration = - DataStorageConfiguration.DEFAULT_FOREST_CONFIG; + DataStorageConfiguration.DEFAULT_BONSAI_CONFIG; private String keyFilePath = null; private boolean devMode = true; private GenesisConfigurationProvider genesisConfigProvider = ignore -> Optional.empty(); diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/AbstractPreexistingNodeTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/AbstractPreexistingNodeTest.java index a407b389e44..c7af349ee4f 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/AbstractPreexistingNodeTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/AbstractPreexistingNodeTest.java @@ -19,6 +19,7 @@ import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase; import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.BesuNodeConfigurationBuilder; @@ -70,6 +71,8 @@ protected BesuNodeConfigurationBuilder configureNode( return nodeBuilder .devMode(false) .dataPath(hostDataPath) + .dataStorageConfiguration( + DataStorageConfiguration.DEFAULT_FOREST_CONFIG) // existing db is forest .genesisConfigProvider((nodes) -> Optional.of(genesisData)) .jsonRpcEnabled(); } From 0e3d2f47d7d7f6d354e8b747318358d032f5007b Mon Sep 17 00:00:00 2001 From: Simon Dudley Date: Thu, 29 Feb 2024 06:45:19 +1000 Subject: [PATCH 2/4] Clique block period transition (#6596) Add BFT-style transitions to Clique, modelled with ForksSchedule Add ability to transition the blockperiodseconds config. --------- Signed-off-by: Jason Frame Signed-off-by: Simon Dudley Co-authored-by: Jason Frame --- CHANGELOG.md | 1 + .../clique/CliqueMiningAcceptanceTest.java | 122 ++++++++++++++++++ .../CliqueBesuControllerBuilder.java | 9 +- config/build.gradle | 2 + .../org/hyperledger/besu/config/BftFork.java | 3 +- .../besu/config/CliqueConfigOptions.java | 59 ++------- .../hyperledger/besu/config/CliqueFork.java | 66 ++++++++++ .../org/hyperledger/besu/config/Fork.java | 27 ++++ .../besu/config/JsonCliqueConfigOptions.java | 90 +++++++++++++ .../besu/config/JsonGenesisConfigOptions.java | 6 +- .../besu/config/StubGenesisConfigOptions.java | 4 +- .../besu/config/TransitionsConfigOptions.java | 29 +++-- .../besu/config/CliqueConfigOptionsTest.java | 4 +- .../besu/config/GenesisConfigOptionsTest.java | 5 +- .../clique/CliqueForksSchedulesFactory.java | 49 +++++++ .../clique/CliqueProtocolSchedule.java | 35 +++-- .../blockcreation/CliqueBlockScheduler.java | 16 ++- .../clique/CliqueProtocolScheduleTest.java | 29 ++++- .../blockcreation/CliqueBlockCreatorTest.java | 2 + .../CliqueBlockSchedulerTest.java | 72 ++++++++++- .../CliqueMinerExecutorTest.java | 2 + ...Factory.java => ForksScheduleFactory.java} | 33 +++-- ...est.java => ForksScheduleFactoryTest.java} | 24 ++-- .../ibft/IbftForksSchedulesFactory.java | 4 +- .../qbft/QbftForksSchedulesFactory.java | 4 +- .../blockcreation/DefaultBlockScheduler.java | 17 ++- .../blockcreation/PoWMinerExecutorTest.java | 2 +- 27 files changed, 584 insertions(+), 132 deletions(-) create mode 100644 config/src/main/java/org/hyperledger/besu/config/CliqueFork.java create mode 100644 config/src/main/java/org/hyperledger/besu/config/Fork.java create mode 100644 config/src/main/java/org/hyperledger/besu/config/JsonCliqueConfigOptions.java create mode 100644 consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueForksSchedulesFactory.java rename consensus/common/src/main/java/org/hyperledger/besu/consensus/common/{bft/BftForksScheduleFactory.java => ForksScheduleFactory.java} (64%) rename consensus/common/src/test/java/org/hyperledger/besu/consensus/common/{bft/BftForksScheduleFactoryTest.java => ForksScheduleFactoryTest.java} (80%) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf9338d4f6e..eccec60f258 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Additions and Improvements - Extend `Blockchain` service [#6592](https://github.com/hyperledger/besu/pull/6592) +- Add bft-style blockperiodseconds transitions to Clique [#6596](https://github.com/hyperledger/besu/pull/6596) - RocksDB database metadata refactoring [#6555](https://github.com/hyperledger/besu/pull/6555) - Make layered txpool aware of minGasPrice and minPriorityFeePerGas dynamic options [#6611](https://github.com/hyperledger/besu/pull/6611) diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/clique/CliqueMiningAcceptanceTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/clique/CliqueMiningAcceptanceTest.java index c2048c61d9c..c70bc470b19 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/clique/CliqueMiningAcceptanceTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/clique/CliqueMiningAcceptanceTest.java @@ -14,14 +14,23 @@ */ package org.hyperledger.besu.tests.acceptance.clique; +import static java.util.stream.Collectors.joining; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.data.Percentage.withPercentage; + import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBaseJunit5; import org.hyperledger.besu.tests.acceptance.dsl.account.Account; import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode; import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.genesis.GenesisConfigurationFactory.CliqueOptions; import java.io.IOException; +import java.math.BigInteger; +import java.util.List; +import java.util.Map; +import java.util.Optional; import org.junit.jupiter.api.Test; +import org.web3j.protocol.core.DefaultBlockParameter; public class CliqueMiningAcceptanceTest extends AcceptanceTestBaseJunit5 { @@ -123,4 +132,117 @@ public void shouldStillMineWhenANodeFailsAndHasSufficientValidators() throws IOE cluster.verifyOnActiveNodes(clique.blockIsCreatedByProposer(minerNode1)); cluster.verifyOnActiveNodes(clique.blockIsCreatedByProposer(minerNode2)); } + + @Test + public void shouldMineBlocksWithBlockPeriodAccordingToTransitions() throws IOException { + + final var cliqueOptions = new CliqueOptions(3, CliqueOptions.DEFAULT.epochLength(), true); + final BesuNode minerNode = besu.createCliqueNode("miner1", cliqueOptions); + + // setup transitions + final Map decreasePeriodTo2_Transition = + Map.of("block", 3, "blockperiodseconds", 2); + final Map decreasePeriodTo1_Transition = + Map.of("block", 4, "blockperiodseconds", 1); + final Map increasePeriodTo2_Transition = + Map.of("block", 6, "blockperiodseconds", 2); + + final Optional initialGenesis = + minerNode.getGenesisConfigProvider().create(List.of(minerNode)); + final String genesisWithTransitions = + prependTransitionsToCliqueOptions( + initialGenesis.orElseThrow(), + List.of( + decreasePeriodTo2_Transition, + decreasePeriodTo1_Transition, + increasePeriodTo2_Transition)); + minerNode.setGenesisConfig(genesisWithTransitions); + + // Mine 6 blocks + cluster.start(minerNode); + minerNode.verify(blockchain.reachesHeight(minerNode, 5)); + + // Assert the block period decreased/increased after each transition + final long block1Timestamp = getTimestampForBlock(minerNode, 1); + final long block2Timestamp = getTimestampForBlock(minerNode, 2); + final long block3Timestamp = getTimestampForBlock(minerNode, 3); + final long block4Timestamp = getTimestampForBlock(minerNode, 4); + final long block5Timestamp = getTimestampForBlock(minerNode, 5); + final long block6Timestamp = getTimestampForBlock(minerNode, 6); + assertThat(block2Timestamp - block1Timestamp).isCloseTo(3, withPercentage(20)); + assertThat(block3Timestamp - block2Timestamp).isCloseTo(2, withPercentage(20)); + assertThat(block4Timestamp - block3Timestamp).isCloseTo(1, withPercentage(20)); + assertThat(block5Timestamp - block4Timestamp).isCloseTo(1, withPercentage(20)); + assertThat(block6Timestamp - block5Timestamp).isCloseTo(2, withPercentage(20)); + } + + private long getTimestampForBlock(final BesuNode minerNode, final int blockNumber) { + return minerNode + .execute( + ethTransactions.block(DefaultBlockParameter.valueOf(BigInteger.valueOf(blockNumber)))) + .getTimestamp() + .longValue(); + } + + private String prependTransitionsToCliqueOptions( + final String originalOptions, final List> transitions) { + final StringBuilder stringBuilder = + new StringBuilder() + .append(formatCliqueTransitionsOptions(transitions)) + .append(",\n") + .append(quote("clique")) + .append(": {"); + + return originalOptions.replace(quote("clique") + ": {", stringBuilder.toString()); + } + + private String formatCliqueTransitionsOptions(final List> transitions) { + final StringBuilder stringBuilder = new StringBuilder(); + + stringBuilder.append(quote("transitions")); + stringBuilder.append(": {\n"); + stringBuilder.append(quote("clique")); + stringBuilder.append(": ["); + final String formattedTransitions = + transitions.stream().map(this::formatTransition).collect(joining(",\n")); + stringBuilder.append(formattedTransitions); + stringBuilder.append("\n]"); + stringBuilder.append("}\n"); + + return stringBuilder.toString(); + } + + private String quote(final Object value) { + return '"' + value.toString() + '"'; + } + + private String formatTransition(final Map transition) { + final StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("{"); + String formattedTransition = + transition.keySet().stream() + .map(key -> formatKeyValues(key, transition.get(key))) + .collect(joining(",")); + stringBuilder.append(formattedTransition); + stringBuilder.append("}"); + return stringBuilder.toString(); + } + + private String formatKeyValues(final Object... keyOrValue) { + if (keyOrValue.length % 2 == 1) { + // An odd number of strings cannot form a set of key-value pairs + throw new IllegalArgumentException("Must supply key-value pairs"); + } + final StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < keyOrValue.length; i += 2) { + if (i > 0) { + stringBuilder.append(", "); + } + final String key = keyOrValue[i].toString(); + final Object value = keyOrValue[i + 1]; + final String valueStr = value instanceof String ? quote(value) : value.toString(); + stringBuilder.append(String.format("\n%s: %s", quote(key), valueStr)); + } + return stringBuilder.toString(); + } } diff --git a/besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java index b28c2741603..20f89629da2 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java @@ -19,6 +19,7 @@ import org.hyperledger.besu.config.CliqueConfigOptions; import org.hyperledger.besu.consensus.clique.CliqueBlockInterface; import org.hyperledger.besu.consensus.clique.CliqueContext; +import org.hyperledger.besu.consensus.clique.CliqueForksSchedulesFactory; import org.hyperledger.besu.consensus.clique.CliqueMiningTracker; import org.hyperledger.besu.consensus.clique.CliqueProtocolSchedule; import org.hyperledger.besu.consensus.clique.blockcreation.CliqueBlockScheduler; @@ -27,6 +28,7 @@ import org.hyperledger.besu.consensus.clique.jsonrpc.CliqueJsonRpcMethods; import org.hyperledger.besu.consensus.common.BlockInterface; import org.hyperledger.besu.consensus.common.EpochManager; +import org.hyperledger.besu.consensus.common.ForksSchedule; import org.hyperledger.besu.consensus.common.validator.blockbased.BlockValidatorProvider; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.ethereum.ProtocolContext; @@ -52,19 +54,19 @@ public class CliqueBesuControllerBuilder extends BesuControllerBuilder { private Address localAddress; private EpochManager epochManager; - private long secondsBetweenBlocks; private boolean createEmptyBlocks = true; private final BlockInterface blockInterface = new CliqueBlockInterface(); + private ForksSchedule forksSchedule; @Override protected void prepForBuild() { localAddress = Util.publicKeyToAddress(nodeKey.getPublicKey()); final CliqueConfigOptions cliqueConfig = configOptionsSupplier.get().getCliqueConfigOptions(); final long blocksPerEpoch = cliqueConfig.getEpochLength(); - secondsBetweenBlocks = cliqueConfig.getBlockPeriodSeconds(); createEmptyBlocks = cliqueConfig.getCreateEmptyBlocks(); epochManager = new EpochManager(blocksPerEpoch); + forksSchedule = CliqueForksSchedulesFactory.create(configOptionsSupplier.get()); } @Override @@ -92,7 +94,7 @@ protected MiningCoordinator createMiningCoordinator( clock, protocolContext.getConsensusContext(CliqueContext.class).getValidatorProvider(), localAddress, - secondsBetweenBlocks), + forksSchedule), epochManager, createEmptyBlocks, ethProtocolManager.ethContext().getScheduler()); @@ -113,6 +115,7 @@ protected MiningCoordinator createMiningCoordinator( protected ProtocolSchedule createProtocolSchedule() { return CliqueProtocolSchedule.create( configOptionsSupplier.get(), + forksSchedule, nodeKey, privacyParameters, isRevertReasonEnabled, diff --git a/config/build.gradle b/config/build.gradle index acfbc83f43f..ae6b2ff4c4f 100644 --- a/config/build.gradle +++ b/config/build.gradle @@ -38,6 +38,8 @@ dependencies { implementation 'info.picocli:picocli' implementation 'io.tmio:tuweni-bytes' implementation 'io.tmio:tuweni-units' + implementation "org.immutables:value-annotations" + annotationProcessor "org.immutables:value" testImplementation project(':testutil') diff --git a/config/src/main/java/org/hyperledger/besu/config/BftFork.java b/config/src/main/java/org/hyperledger/besu/config/BftFork.java index 2b2031cea45..7e9c408bac6 100644 --- a/config/src/main/java/org/hyperledger/besu/config/BftFork.java +++ b/config/src/main/java/org/hyperledger/besu/config/BftFork.java @@ -28,7 +28,7 @@ import org.apache.tuweni.bytes.Bytes; /** The Bft fork. */ -public class BftFork { +public class BftFork implements Fork { /** The constant FORK_BLOCK_KEY. */ public static final String FORK_BLOCK_KEY = "block"; @@ -59,6 +59,7 @@ public BftFork(final ObjectNode forkConfigRoot) { * * @return the fork block */ + @Override public long getForkBlock() { return JsonUtil.getLong(forkConfigRoot, FORK_BLOCK_KEY) .orElseThrow( diff --git a/config/src/main/java/org/hyperledger/besu/config/CliqueConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/CliqueConfigOptions.java index c0ba496f892..6b8cb77eb16 100644 --- a/config/src/main/java/org/hyperledger/besu/config/CliqueConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/CliqueConfigOptions.java @@ -1,5 +1,5 @@ /* - * Copyright ConsenSys AG. + * 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 @@ -12,75 +12,42 @@ * * SPDX-License-Identifier: Apache-2.0 */ + package org.hyperledger.besu.config; import java.util.Map; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.common.collect.ImmutableMap; - -/** The Clique config options. */ -public class CliqueConfigOptions { - - /** The constant DEFAULT. */ - public static final CliqueConfigOptions DEFAULT = - new CliqueConfigOptions(JsonUtil.createEmptyObjectNode()); - - private static final long DEFAULT_EPOCH_LENGTH = 30_000; - private static final int DEFAULT_BLOCK_PERIOD_SECONDS = 15; - private static final boolean DEFAULT_CREATE_EMPTY_BLOCKS = true; +import org.immutables.value.Value; - private final ObjectNode cliqueConfigRoot; - - /** - * Instantiates a new Clique config options. - * - * @param cliqueConfigRoot the clique config root - */ - CliqueConfigOptions(final ObjectNode cliqueConfigRoot) { - this.cliqueConfigRoot = cliqueConfigRoot; - } +/** Configuration options for the Clique consensus mechanism. */ +@Value.Immutable +public interface CliqueConfigOptions { /** * The number of blocks in an epoch. * * @return the epoch length */ - public long getEpochLength() { - return JsonUtil.getLong(cliqueConfigRoot, "epochlength", DEFAULT_EPOCH_LENGTH); - } + long getEpochLength(); /** * Gets block period seconds. * * @return the block period seconds */ - public int getBlockPeriodSeconds() { - return JsonUtil.getPositiveInt( - cliqueConfigRoot, "blockperiodseconds", DEFAULT_BLOCK_PERIOD_SECONDS); - } + int getBlockPeriodSeconds(); /** - * Whether the creation of empty blocks is allowed. + * Gets create empty blocks. * - * @return the create empty block status + * @return whether empty blocks are permitted */ - public boolean getCreateEmptyBlocks() { - return JsonUtil.getBoolean(cliqueConfigRoot, "createemptyblocks", DEFAULT_CREATE_EMPTY_BLOCKS); - } + boolean getCreateEmptyBlocks(); /** - * As map. + * A map of the config options. * * @return the map */ - Map asMap() { - return ImmutableMap.of( - "epochLength", - getEpochLength(), - "blockPeriodSeconds", - getBlockPeriodSeconds(), - "createemptyblocks", - getCreateEmptyBlocks()); - } + Map asMap(); } diff --git a/config/src/main/java/org/hyperledger/besu/config/CliqueFork.java b/config/src/main/java/org/hyperledger/besu/config/CliqueFork.java new file mode 100644 index 00000000000..f1e9850b3d5 --- /dev/null +++ b/config/src/main/java/org/hyperledger/besu/config/CliqueFork.java @@ -0,0 +1,66 @@ +/* + * 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.config; + +import java.util.OptionalInt; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** The Clique fork. */ +public class CliqueFork implements Fork { + + /** The constant FORK_BLOCK_KEY. */ + public static final String FORK_BLOCK_KEY = "block"; + + /** The constant BLOCK_PERIOD_SECONDS_KEY. */ + public static final String BLOCK_PERIOD_SECONDS_KEY = "blockperiodseconds"; + + /** The Fork config root. */ + protected final ObjectNode forkConfigRoot; + + /** + * Instantiates a new Clique fork. + * + * @param forkConfigRoot the fork config root + */ + @JsonCreator + public CliqueFork(final ObjectNode forkConfigRoot) { + this.forkConfigRoot = forkConfigRoot; + } + + /** + * Gets fork block. + * + * @return the fork block + */ + @Override + public long getForkBlock() { + return JsonUtil.getLong(forkConfigRoot, FORK_BLOCK_KEY) + .orElseThrow( + () -> + new IllegalArgumentException( + "Fork block not specified for Clique fork in custom forks")); + } + + /** + * Gets block period seconds. + * + * @return the block period seconds + */ + public OptionalInt getBlockPeriodSeconds() { + return JsonUtil.getPositiveInt(forkConfigRoot, BLOCK_PERIOD_SECONDS_KEY); + } +} diff --git a/config/src/main/java/org/hyperledger/besu/config/Fork.java b/config/src/main/java/org/hyperledger/besu/config/Fork.java new file mode 100644 index 00000000000..442dca3079f --- /dev/null +++ b/config/src/main/java/org/hyperledger/besu/config/Fork.java @@ -0,0 +1,27 @@ +/* + * 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.config; + +/** A "custom hard fork" used for Proof of Authority network Transitions */ +public interface Fork { + + /** + * The block number at which the fork occurs. + * + * @return the block number at which the fork occurs + */ + long getForkBlock(); +} diff --git a/config/src/main/java/org/hyperledger/besu/config/JsonCliqueConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/JsonCliqueConfigOptions.java new file mode 100644 index 00000000000..977542b877c --- /dev/null +++ b/config/src/main/java/org/hyperledger/besu/config/JsonCliqueConfigOptions.java @@ -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.config; + +import java.util.Map; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.ImmutableMap; + +/** The Clique config options. */ +public class JsonCliqueConfigOptions implements CliqueConfigOptions { + + private static final long DEFAULT_EPOCH_LENGTH = 30_000; + private static final int DEFAULT_BLOCK_PERIOD_SECONDS = 15; + private static final boolean DEFAULT_CREATE_EMPTY_BLOCKS = true; + + private final ObjectNode cliqueConfigRoot; + + /** The constant DEFAULT. */ + public static final JsonCliqueConfigOptions DEFAULT = + new JsonCliqueConfigOptions(JsonUtil.createEmptyObjectNode()); + + /** + * Instantiates a new Clique config options. + * + * @param cliqueConfigRoot the clique config root + */ + JsonCliqueConfigOptions(final ObjectNode cliqueConfigRoot) { + this.cliqueConfigRoot = cliqueConfigRoot; + } + + /** + * The number of blocks in an epoch. + * + * @return the epoch length + */ + @Override + public long getEpochLength() { + return JsonUtil.getLong(cliqueConfigRoot, "epochlength", DEFAULT_EPOCH_LENGTH); + } + + /** + * Gets block period seconds. + * + * @return the block period seconds + */ + @Override + public int getBlockPeriodSeconds() { + return JsonUtil.getPositiveInt( + cliqueConfigRoot, "blockperiodseconds", DEFAULT_BLOCK_PERIOD_SECONDS); + } + + /** + * Whether the creation of empty blocks is allowed. + * + * @return the create empty block status + */ + @Override + public boolean getCreateEmptyBlocks() { + return JsonUtil.getBoolean(cliqueConfigRoot, "createemptyblocks", DEFAULT_CREATE_EMPTY_BLOCKS); + } + + /** + * As map. + * + * @return the map + */ + @Override + public Map asMap() { + return ImmutableMap.of( + "epochLength", + getEpochLength(), + "blockPeriodSeconds", + getBlockPeriodSeconds(), + "createemptyblocks", + getCreateEmptyBlocks()); + } +} diff --git a/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java index c2c8dad513a..d2ce8401fc6 100644 --- a/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java @@ -180,10 +180,10 @@ public CheckpointConfigOptions getCheckpointOptions() { } @Override - public CliqueConfigOptions getCliqueConfigOptions() { + public JsonCliqueConfigOptions getCliqueConfigOptions() { return JsonUtil.getObjectNode(configRoot, CLIQUE_CONFIG_KEY) - .map(CliqueConfigOptions::new) - .orElse(CliqueConfigOptions.DEFAULT); + .map(JsonCliqueConfigOptions::new) + .orElse(JsonCliqueConfigOptions.DEFAULT); } @Override diff --git a/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java index bdff4497280..d3705487cd4 100644 --- a/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java @@ -129,8 +129,8 @@ public CheckpointConfigOptions getCheckpointOptions() { } @Override - public CliqueConfigOptions getCliqueConfigOptions() { - return CliqueConfigOptions.DEFAULT; + public JsonCliqueConfigOptions getCliqueConfigOptions() { + return JsonCliqueConfigOptions.DEFAULT; } @Override diff --git a/config/src/main/java/org/hyperledger/besu/config/TransitionsConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/TransitionsConfigOptions.java index 0b6687efce0..6a2125aa805 100644 --- a/config/src/main/java/org/hyperledger/besu/config/TransitionsConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/TransitionsConfigOptions.java @@ -51,7 +51,7 @@ public TransitionsConfigOptions(final ObjectNode customForkConfigRoot) { * @return the ibft forks */ public List getIbftForks() { - return getBftForks("ibft2", BftFork::new); + return getForks("ibft2", BftFork::new); } /** @@ -60,30 +60,39 @@ public List getIbftForks() { * @return the qbft forks */ public List getQbftForks() { - return getBftForks("qbft", QbftFork::new); + return getForks("qbft", QbftFork::new); } - private List getBftForks( + /** + * Gets clique forks. + * + * @return the clique forks + */ + public List getCliqueForks() { + return getForks("clique", CliqueFork::new); + } + + private List getForks( final String fieldKey, final Function forkConstructor) { - final Optional bftForksNode = JsonUtil.getArrayNode(customForkConfigRoot, fieldKey); + final Optional forksNode = JsonUtil.getArrayNode(customForkConfigRoot, fieldKey); - if (bftForksNode.isEmpty()) { + if (forksNode.isEmpty()) { return emptyList(); } - final List bftForks = Lists.newArrayList(); + final List forks = Lists.newArrayList(); - bftForksNode + forksNode .get() .elements() .forEachRemaining( node -> { if (!node.isObject()) { - throw new IllegalArgumentException("Bft fork is illegally formatted."); + throw new IllegalArgumentException("Transition is illegally formatted."); } - bftForks.add(forkConstructor.apply((ObjectNode) node)); + forks.add(forkConstructor.apply((ObjectNode) node)); }); - return Collections.unmodifiableList(bftForks); + return Collections.unmodifiableList(forks); } } diff --git a/config/src/test/java/org/hyperledger/besu/config/CliqueConfigOptionsTest.java b/config/src/test/java/org/hyperledger/besu/config/CliqueConfigOptionsTest.java index 355dc8f8aa0..f445730ac18 100644 --- a/config/src/test/java/org/hyperledger/besu/config/CliqueConfigOptionsTest.java +++ b/config/src/test/java/org/hyperledger/besu/config/CliqueConfigOptionsTest.java @@ -43,7 +43,7 @@ public void shouldFallbackToDefaultEpochLength() { @Test public void shouldGetDefaultEpochLengthFromDefaultConfig() { - assertThat(CliqueConfigOptions.DEFAULT.getEpochLength()) + assertThat(JsonCliqueConfigOptions.DEFAULT.getEpochLength()) .isEqualTo(EXPECTED_DEFAULT_EPOCH_LENGTH); } @@ -61,7 +61,7 @@ public void shouldFallbackToDefaultBlockPeriod() { @Test public void shouldGetDefaultBlockPeriodFromDefaultConfig() { - assertThat(CliqueConfigOptions.DEFAULT.getBlockPeriodSeconds()) + assertThat(JsonCliqueConfigOptions.DEFAULT.getBlockPeriodSeconds()) .isEqualTo(EXPECTED_DEFAULT_BLOCK_PERIOD); } diff --git a/config/src/test/java/org/hyperledger/besu/config/GenesisConfigOptionsTest.java b/config/src/test/java/org/hyperledger/besu/config/GenesisConfigOptionsTest.java index f7e89eef9e1..03007233592 100644 --- a/config/src/test/java/org/hyperledger/besu/config/GenesisConfigOptionsTest.java +++ b/config/src/test/java/org/hyperledger/besu/config/GenesisConfigOptionsTest.java @@ -52,6 +52,7 @@ void shouldUseIbft2WhenIbft2InConfig() { assertThat(config.getConsensusEngine()).isEqualTo("ibft2"); } + @Test void shouldUseQbftWhenQbftInConfig() { final GenesisConfigOptions config = fromConfigOptions(singletonMap("qbft", emptyMap())); assertThat(config.isQbft()).isTrue(); @@ -64,7 +65,7 @@ void shouldUseCliqueWhenCliqueInConfig() { final GenesisConfigOptions config = fromConfigOptions(singletonMap("clique", emptyMap())); assertThat(config.isClique()).isTrue(); assertThat(config.isPoa()).isTrue(); - assertThat(config.getCliqueConfigOptions()).isNotSameAs(CliqueConfigOptions.DEFAULT); + assertThat(config.getCliqueConfigOptions()).isNotSameAs(JsonCliqueConfigOptions.DEFAULT); assertThat(config.getConsensusEngine()).isEqualTo("clique"); } @@ -73,7 +74,7 @@ void shouldNotUseCliqueIfCliqueNotPresent() { final GenesisConfigOptions config = fromConfigOptions(emptyMap()); assertThat(config.isClique()).isFalse(); assertThat(config.isPoa()).isFalse(); - assertThat(config.getCliqueConfigOptions()).isSameAs(CliqueConfigOptions.DEFAULT); + assertThat(config.getCliqueConfigOptions()).isSameAs(JsonCliqueConfigOptions.DEFAULT); } @Test diff --git a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueForksSchedulesFactory.java b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueForksSchedulesFactory.java new file mode 100644 index 00000000000..357cc7ee2b4 --- /dev/null +++ b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueForksSchedulesFactory.java @@ -0,0 +1,49 @@ +/* + * 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.consensus.clique; + +import org.hyperledger.besu.config.CliqueConfigOptions; +import org.hyperledger.besu.config.CliqueFork; +import org.hyperledger.besu.config.GenesisConfigOptions; +import org.hyperledger.besu.config.ImmutableCliqueConfigOptions; +import org.hyperledger.besu.consensus.common.ForkSpec; +import org.hyperledger.besu.consensus.common.ForksSchedule; +import org.hyperledger.besu.consensus.common.ForksScheduleFactory; + +/** The Clique forks schedules factory. */ +public class CliqueForksSchedulesFactory { + + /** + * Create forks schedule. + * + * @param genesisConfig the genesis config + * @return the forks schedule + */ + public static ForksSchedule create( + final GenesisConfigOptions genesisConfig) { + return ForksScheduleFactory.create( + genesisConfig.getCliqueConfigOptions(), + genesisConfig.getTransitions().getCliqueForks(), + CliqueForksSchedulesFactory::createCliqueConfigOptions); + } + + private static CliqueConfigOptions createCliqueConfigOptions( + final ForkSpec lastSpec, final CliqueFork fork) { + + var options = ImmutableCliqueConfigOptions.builder().from(lastSpec.getValue()); + fork.getBlockPeriodSeconds().ifPresent(options::blockPeriodSeconds); + return options.build(); + } +} diff --git a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java index c0aaf61765b..b21a7d68edd 100644 --- a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java +++ b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java @@ -17,6 +17,7 @@ import org.hyperledger.besu.config.CliqueConfigOptions; import org.hyperledger.besu.config.GenesisConfigOptions; import org.hyperledger.besu.consensus.common.EpochManager; +import org.hyperledger.besu.consensus.common.ForksSchedule; import org.hyperledger.besu.cryptoservices.NodeKey; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; @@ -36,7 +37,10 @@ import org.hyperledger.besu.evm.internal.EvmConfiguration; import java.math.BigInteger; +import java.util.HashMap; +import java.util.Map; import java.util.Optional; +import java.util.function.Function; /** Defines the protocol behaviours for a blockchain using Clique. */ public class CliqueProtocolSchedule { @@ -47,6 +51,7 @@ public class CliqueProtocolSchedule { * Create protocol schedule. * * @param config the config + * @param forksSchedule the transitions * @param nodeKey the node key * @param privacyParameters the privacy parameters * @param isRevertReasonEnabled the is revert reason enabled @@ -56,6 +61,7 @@ public class CliqueProtocolSchedule { */ public static ProtocolSchedule create( final GenesisConfigOptions config, + final ForksSchedule forksSchedule, final NodeKey nodeKey, final PrivacyParameters privacyParameters, final boolean isRevertReasonEnabled, @@ -72,18 +78,26 @@ public static ProtocolSchedule create( final EpochManager epochManager = new EpochManager(cliqueConfig.getEpochLength()); + final Map> specMap = new HashMap<>(); + forksSchedule + .getForks() + .forEach( + forkSpec -> + specMap.put( + forkSpec.getBlock(), + builder -> + applyCliqueSpecificModifications( + epochManager, + forkSpec.getValue().getBlockPeriodSeconds(), + cliqueConfig.getCreateEmptyBlocks(), + localNodeAddress, + builder))); + final ProtocolSpecAdapters specAdapters = new ProtocolSpecAdapters(specMap); + return new ProtocolScheduleBuilder( config, DEFAULT_CHAIN_ID, - ProtocolSpecAdapters.create( - 0, - builder -> - applyCliqueSpecificModifications( - epochManager, - cliqueConfig.getBlockPeriodSeconds(), - cliqueConfig.getCreateEmptyBlocks(), - localNodeAddress, - builder)), + specAdapters, privacyParameters, isRevertReasonEnabled, evmConfiguration, @@ -95,6 +109,7 @@ public static ProtocolSchedule create( * Create protocol schedule. * * @param config the config + * @param forksSchedule the transitions * @param nodeKey the node key * @param isRevertReasonEnabled the is revert reason enabled * @param evmConfiguration the evm configuration @@ -103,12 +118,14 @@ public static ProtocolSchedule create( */ public static ProtocolSchedule create( final GenesisConfigOptions config, + final ForksSchedule forksSchedule, final NodeKey nodeKey, final boolean isRevertReasonEnabled, final EvmConfiguration evmConfiguration, final BadBlockManager badBlockManager) { return create( config, + forksSchedule, nodeKey, PrivacyParameters.DEFAULT, isRevertReasonEnabled, diff --git a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockScheduler.java b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockScheduler.java index aa28226a515..994b8fed137 100644 --- a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockScheduler.java +++ b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockScheduler.java @@ -14,6 +14,8 @@ */ package org.hyperledger.besu.consensus.clique.blockcreation; +import org.hyperledger.besu.config.CliqueConfigOptions; +import org.hyperledger.besu.consensus.common.ForksSchedule; import org.hyperledger.besu.consensus.common.validator.ValidatorProvider; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.ethereum.blockcreation.DefaultBlockScheduler; @@ -40,14 +42,22 @@ public class CliqueBlockScheduler extends DefaultBlockScheduler { * @param clock the clock * @param validatorProvider the validator provider * @param localNodeAddress the local node address - * @param secondsBetweenBlocks the seconds between blocks + * @param forksSchedule the transitions */ public CliqueBlockScheduler( final Clock clock, final ValidatorProvider validatorProvider, final Address localNodeAddress, - final long secondsBetweenBlocks) { - super(secondsBetweenBlocks, 0L, clock); + final ForksSchedule forksSchedule) { + super( + parentHeader -> + (long) + forksSchedule + .getFork(parentHeader.getNumber() + 1) + .getValue() + .getBlockPeriodSeconds(), + 0L, + clock); this.validatorProvider = validatorProvider; this.localNodeAddress = localNodeAddress; } diff --git a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/CliqueProtocolScheduleTest.java b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/CliqueProtocolScheduleTest.java index e6c962c0cdd..a6eefb94115 100644 --- a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/CliqueProtocolScheduleTest.java +++ b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/CliqueProtocolScheduleTest.java @@ -22,6 +22,9 @@ import org.hyperledger.besu.config.CliqueConfigOptions; import org.hyperledger.besu.config.GenesisConfigFile; import org.hyperledger.besu.config.GenesisConfigOptions; +import org.hyperledger.besu.config.JsonCliqueConfigOptions; +import org.hyperledger.besu.consensus.common.ForkSpec; +import org.hyperledger.besu.consensus.common.ForksSchedule; import org.hyperledger.besu.cryptoservices.NodeKey; import org.hyperledger.besu.cryptoservices.NodeKeyUtils; import org.hyperledger.besu.datatypes.Hash; @@ -35,6 +38,7 @@ import org.hyperledger.besu.evm.internal.EvmConfiguration; import java.time.Instant; +import java.util.List; import org.junit.jupiter.api.Test; @@ -57,7 +61,12 @@ public void protocolSpecsAreCreatedAtBlockDefinedInJson() { final GenesisConfigOptions config = GenesisConfigFile.fromConfig(jsonInput).getConfigOptions(); final ProtocolSchedule protocolSchedule = CliqueProtocolSchedule.create( - config, NODE_KEY, false, EvmConfiguration.DEFAULT, new BadBlockManager()); + config, + new ForksSchedule<>(List.of()), + NODE_KEY, + false, + EvmConfiguration.DEFAULT, + new BadBlockManager()); final ProtocolSpec homesteadSpec = protocolSchedule.getByBlockHeader(blockHeader(1)); final ProtocolSpec tangerineWhistleSpec = protocolSchedule.getByBlockHeader(blockHeader(2)); @@ -71,9 +80,12 @@ public void protocolSpecsAreCreatedAtBlockDefinedInJson() { @Test public void parametersAlignWithMainnetWithAdjustments() { + final ForksSchedule forksSchedule = + new ForksSchedule<>(List.of(new ForkSpec<>(0, JsonCliqueConfigOptions.DEFAULT))); final ProtocolSpec homestead = CliqueProtocolSchedule.create( GenesisConfigFile.DEFAULT.getConfigOptions(), + forksSchedule, NODE_KEY, false, EvmConfiguration.DEFAULT, @@ -88,7 +100,7 @@ public void parametersAlignWithMainnetWithAdjustments() { @Test public void zeroEpochLengthThrowsException() { - final CliqueConfigOptions cliqueOptions = mock(CliqueConfigOptions.class); + final CliqueConfigOptions cliqueOptions = mock(JsonCliqueConfigOptions.class); when(cliqueOptions.getEpochLength()).thenReturn(0L); when(genesisConfig.getCliqueConfigOptions()).thenReturn(cliqueOptions); @@ -96,6 +108,7 @@ public void zeroEpochLengthThrowsException() { () -> CliqueProtocolSchedule.create( genesisConfig, + new ForksSchedule<>(List.of()), NODE_KEY, false, EvmConfiguration.DEFAULT, @@ -106,7 +119,7 @@ public void zeroEpochLengthThrowsException() { @Test public void negativeEpochLengthThrowsException() { - final CliqueConfigOptions cliqueOptions = mock(CliqueConfigOptions.class); + final CliqueConfigOptions cliqueOptions = mock(JsonCliqueConfigOptions.class); when(cliqueOptions.getEpochLength()).thenReturn(-3000L); when(genesisConfig.getCliqueConfigOptions()).thenReturn(cliqueOptions); @@ -114,6 +127,7 @@ public void negativeEpochLengthThrowsException() { () -> CliqueProtocolSchedule.create( genesisConfig, + new ForksSchedule<>(List.of()), NODE_KEY, false, EvmConfiguration.DEFAULT, @@ -131,9 +145,16 @@ public void shouldValidateBaseFeeMarketTransition() { "{\"config\": " + "\t{\"chainId\": 1337,\n" + "\t\"londonBlock\": 2}\n" + "}"; final GenesisConfigOptions config = GenesisConfigFile.fromConfig(jsonInput).getConfigOptions(); + final ForksSchedule forksSchedule = + new ForksSchedule<>(List.of(new ForkSpec<>(0, JsonCliqueConfigOptions.DEFAULT))); final ProtocolSchedule protocolSchedule = CliqueProtocolSchedule.create( - config, NODE_KEY, false, EvmConfiguration.DEFAULT, new BadBlockManager()); + config, + forksSchedule, + NODE_KEY, + false, + EvmConfiguration.DEFAULT, + new BadBlockManager()); BlockHeader emptyFrontierParent = headerBuilder diff --git a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreatorTest.java b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreatorTest.java index a98c9f7b95e..58f7c72fce3 100644 --- a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreatorTest.java +++ b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreatorTest.java @@ -30,6 +30,7 @@ import org.hyperledger.besu.consensus.clique.CliqueProtocolSchedule; import org.hyperledger.besu.consensus.clique.TestHelpers; import org.hyperledger.besu.consensus.common.EpochManager; +import org.hyperledger.besu.consensus.common.ForksSchedule; import org.hyperledger.besu.consensus.common.validator.ValidatorProvider; import org.hyperledger.besu.consensus.common.validator.ValidatorVote; import org.hyperledger.besu.consensus.common.validator.VoteProvider; @@ -101,6 +102,7 @@ public void setup() { protocolSchedule = CliqueProtocolSchedule.create( GenesisConfigFile.DEFAULT.getConfigOptions(), + new ForksSchedule<>(List.of()), proposerNodeKey, false, EvmConfiguration.DEFAULT, diff --git a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockSchedulerTest.java b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockSchedulerTest.java index 87aeb9d6694..8421ac3f195 100644 --- a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockSchedulerTest.java +++ b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockSchedulerTest.java @@ -19,6 +19,11 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import org.hyperledger.besu.config.CliqueConfigOptions; +import org.hyperledger.besu.config.ImmutableCliqueConfigOptions; +import org.hyperledger.besu.config.JsonCliqueConfigOptions; +import org.hyperledger.besu.consensus.common.ForkSpec; +import org.hyperledger.besu.consensus.common.ForksSchedule; import org.hyperledger.besu.consensus.common.validator.ValidatorProvider; import org.hyperledger.besu.crypto.KeyPair; import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; @@ -44,6 +49,7 @@ public class CliqueBlockSchedulerTest { private final List
validatorList = Lists.newArrayList(); private ValidatorProvider validatorProvider; private BlockHeaderTestFixture blockHeaderBuilder; + private ForksSchedule forksSchedule; @BeforeEach public void setup() { @@ -56,16 +62,21 @@ public void setup() { when(validatorProvider.getValidatorsAfterBlock(any())).thenReturn(validatorList); blockHeaderBuilder = new BlockHeaderTestFixture(); + + var initialTransition = + ImmutableCliqueConfigOptions.builder().from(JsonCliqueConfigOptions.DEFAULT); + initialTransition.blockPeriodSeconds(5); + forksSchedule = new ForksSchedule<>(List.of(new ForkSpec<>(0, initialTransition.build()))); } @Test public void inturnValidatorWaitsExactlyBlockInterval() { final Clock clock = mock(Clock.class); final long currentSecondsSinceEpoch = 10L; - final long secondsBetweenBlocks = 5L; + final int secondsBetweenBlocks = 5; when(clock.millis()).thenReturn(currentSecondsSinceEpoch * 1000); final CliqueBlockScheduler scheduler = - new CliqueBlockScheduler(clock, validatorProvider, localAddr, secondsBetweenBlocks); + new CliqueBlockScheduler(clock, validatorProvider, localAddr, forksSchedule); // There are 2 validators, therefore block 2 will put localAddr as the in-turn voter, therefore // parent block should be number 1. @@ -80,13 +91,61 @@ public void inturnValidatorWaitsExactlyBlockInterval() { } @Test - public void outOfturnValidatorWaitsLongerThanBlockInterval() { + public void validatorWithTransitionForBlockTimeWaitsBlockInterval() { + final Clock clock = mock(Clock.class); + final long currentSecondsSinceEpoch = 10L; + when(clock.millis()).thenReturn(currentSecondsSinceEpoch * 1000); + + final var initialTransition = + ImmutableCliqueConfigOptions.builder().from(JsonCliqueConfigOptions.DEFAULT); + initialTransition.blockPeriodSeconds(5); + final var decreaseBlockTimeTransition = + ImmutableCliqueConfigOptions.builder().from(JsonCliqueConfigOptions.DEFAULT); + decreaseBlockTimeTransition.blockPeriodSeconds(1); + forksSchedule = + new ForksSchedule<>( + List.of( + new ForkSpec<>(0, initialTransition.build()), + new ForkSpec<>(4, decreaseBlockTimeTransition.build()))); + + final CliqueBlockScheduler scheduler = + new CliqueBlockScheduler(clock, validatorProvider, localAddr, forksSchedule); + + // getNextTimestamp for last block before transition + // There are 2 validators, therefore block 3 will put localAddr as the out-of-turn voter, + // therefore + // parent block should be number 2. + BlockHeader parentHeader = + blockHeaderBuilder.number(2).timestamp(currentSecondsSinceEpoch).buildHeader(); + BlockCreationTimeResult result = scheduler.getNextTimestamp(parentHeader); + assertThat(result.getTimestampForHeader()).isEqualTo(currentSecondsSinceEpoch + 5); + assertThat(result.getMillisecondsUntilValid()).isGreaterThan(5 * 1000); + + // getNextTimestamp for transition block + // There are 2 validators, therefore block 4 will put localAddr as the in-turn voter, therefore + // parent block should be number 3. + parentHeader = blockHeaderBuilder.number(3).timestamp(currentSecondsSinceEpoch).buildHeader(); + result = scheduler.getNextTimestamp(parentHeader); + assertThat(result.getTimestampForHeader()).isEqualTo(currentSecondsSinceEpoch + 1); + assertThat(result.getMillisecondsUntilValid()).isEqualTo(1000); + + // getNextTimestamp for block after transition + // There are 2 validators, therefore block 5 will put localAddr as the out-of-turn voter, + // therefore + // parent block should be number 4. + parentHeader = blockHeaderBuilder.number(4).timestamp(currentSecondsSinceEpoch).buildHeader(); + result = scheduler.getNextTimestamp(parentHeader); + assertThat(result.getTimestampForHeader()).isEqualTo(currentSecondsSinceEpoch + 1); + assertThat(result.getMillisecondsUntilValid()).isGreaterThan(1000); + } + + @Test + public void outOfTurnValidatorWaitsLongerThanBlockInterval() { final Clock clock = mock(Clock.class); final long currentSecondsSinceEpoch = 10L; - final long secondsBetweenBlocks = 5L; when(clock.millis()).thenReturn(currentSecondsSinceEpoch * 1000); final CliqueBlockScheduler scheduler = - new CliqueBlockScheduler(clock, validatorProvider, localAddr, secondsBetweenBlocks); + new CliqueBlockScheduler(clock, validatorProvider, localAddr, forksSchedule); // There are 2 validators, therefore block 3 will put localAddr as the out-turn voter, therefore // parent block should be number 2. @@ -95,6 +154,7 @@ public void outOfturnValidatorWaitsLongerThanBlockInterval() { final BlockCreationTimeResult result = scheduler.getNextTimestamp(parentHeader); + long secondsBetweenBlocks = 5L; assertThat(result.getTimestampForHeader()) .isEqualTo(currentSecondsSinceEpoch + secondsBetweenBlocks); assertThat(result.getMillisecondsUntilValid()).isGreaterThan(secondsBetweenBlocks * 1000); @@ -107,7 +167,7 @@ public void inTurnValidatorCreatesBlockNowIFParentTimestampSufficientlyBehindNow final long secondsBetweenBlocks = 5L; when(clock.millis()).thenReturn(currentSecondsSinceEpoch * 1000); final CliqueBlockScheduler scheduler = - new CliqueBlockScheduler(clock, validatorProvider, localAddr, secondsBetweenBlocks); + new CliqueBlockScheduler(clock, validatorProvider, localAddr, forksSchedule); // There are 2 validators, therefore block 2 will put localAddr as the in-turn voter, therefore // parent block should be number 1. diff --git a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java index a919360f5d5..8513b78a56d 100644 --- a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java +++ b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java @@ -28,6 +28,7 @@ import org.hyperledger.besu.consensus.clique.CliqueExtraData; import org.hyperledger.besu.consensus.clique.CliqueProtocolSchedule; import org.hyperledger.besu.consensus.common.EpochManager; +import org.hyperledger.besu.consensus.common.ForksSchedule; import org.hyperledger.besu.consensus.common.validator.ValidatorProvider; import org.hyperledger.besu.cryptoservices.NodeKey; import org.hyperledger.besu.cryptoservices.NodeKeyUtils; @@ -99,6 +100,7 @@ public void setup() { cliqueProtocolSchedule = CliqueProtocolSchedule.create( GENESIS_CONFIG_OPTIONS, + new ForksSchedule<>(List.of()), proposerNodeKey, false, EvmConfiguration.DEFAULT, diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftForksScheduleFactory.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/ForksScheduleFactory.java similarity index 64% rename from consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftForksScheduleFactory.java rename to consensus/common/src/main/java/org/hyperledger/besu/consensus/common/ForksScheduleFactory.java index ca14c94ac34..a0abb3effad 100644 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftForksScheduleFactory.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/ForksScheduleFactory.java @@ -1,5 +1,5 @@ /* - * Copyright Hyperledger Besu contributors. + * 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 @@ -12,36 +12,33 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.consensus.common.bft; +package org.hyperledger.besu.consensus.common; import static com.google.common.base.Preconditions.checkArgument; -import org.hyperledger.besu.config.BftConfigOptions; -import org.hyperledger.besu.config.BftFork; -import org.hyperledger.besu.consensus.common.ForkSpec; -import org.hyperledger.besu.consensus.common.ForksSchedule; +import org.hyperledger.besu.config.Fork; import java.util.Comparator; import java.util.List; import java.util.NavigableSet; import java.util.TreeSet; -/** The Bft forks schedule factory. */ -public class BftForksScheduleFactory { +/** The forks schedule factory. */ +public class ForksScheduleFactory { /** - * The interface Bft spec creator. + * The interface spec creator. * * @param the type parameter * @param the type parameter */ - public interface BftSpecCreator { + public interface SpecCreator { /** - * Create type of BftConfigOptions. + * Create type of ConfigOptions. * * @param lastSpec the last spec * @param fork the fork - * @return the type of BftConfigOptions + * @return the type of ConfigOptions */ T create(ForkSpec lastSpec, U fork); } @@ -49,20 +46,20 @@ public interface BftSpecCreator { /** * Create forks schedule. * - * @param the type parameter BftConfigOptions - * @param the type parameter BftFork + * @param the type parameter ConfigOptions + * @param the type parameter Fork * @param initial the initial * @param forks the forks * @param specCreator the spec creator * @return the forks schedule */ - public static ForksSchedule create( - final T initial, final List forks, final BftSpecCreator specCreator) { + public static ForksSchedule create( + final T initial, final List forks, final SpecCreator specCreator) { checkArgument( forks.stream().allMatch(f -> f.getForkBlock() > 0), "Transition cannot be created for genesis block"); checkArgument( - forks.stream().map(BftFork::getForkBlock).distinct().count() == forks.size(), + forks.stream().map(Fork::getForkBlock).distinct().count() == forks.size(), "Duplicate transitions cannot be created for the same block"); final NavigableSet> specs = new TreeSet<>(Comparator.comparing(ForkSpec::getBlock)); @@ -70,7 +67,7 @@ public static ForksSchedule c specs.add(initialForkSpec); forks.stream() - .sorted(Comparator.comparing(BftFork::getForkBlock)) + .sorted(Comparator.comparing(Fork::getForkBlock)) .forEachOrdered( f -> { final T spec = specCreator.create(specs.last(), f); diff --git a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/BftForksScheduleFactoryTest.java b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/ForksScheduleFactoryTest.java similarity index 80% rename from consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/BftForksScheduleFactoryTest.java rename to consensus/common/src/test/java/org/hyperledger/besu/consensus/common/ForksScheduleFactoryTest.java index 72b15b58bcf..0d10c67f21b 100644 --- a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/BftForksScheduleFactoryTest.java +++ b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/ForksScheduleFactoryTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.consensus.common.bft; +package org.hyperledger.besu.consensus.common; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -22,9 +22,8 @@ import org.hyperledger.besu.config.BftFork; import org.hyperledger.besu.config.JsonBftConfigOptions; import org.hyperledger.besu.config.JsonUtil; -import org.hyperledger.besu.consensus.common.ForkSpec; -import org.hyperledger.besu.consensus.common.ForksSchedule; -import org.hyperledger.besu.consensus.common.bft.BftForksScheduleFactory.BftSpecCreator; +import org.hyperledger.besu.consensus.common.ForksScheduleFactory.SpecCreator; +import org.hyperledger.besu.consensus.common.bft.MutableBftConfigOptions; import java.util.List; import java.util.Map; @@ -32,18 +31,17 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; -public class BftForksScheduleFactoryTest { +public class ForksScheduleFactoryTest { @Test @SuppressWarnings("unchecked") public void throwsErrorIfHasForkForGenesisBlock() { final BftConfigOptions genesisConfigOptions = JsonBftConfigOptions.DEFAULT; final BftFork fork = createFork(0, 10); - final BftSpecCreator specCreator = - Mockito.mock(BftSpecCreator.class); + final SpecCreator specCreator = Mockito.mock(SpecCreator.class); assertThatThrownBy( - () -> BftForksScheduleFactory.create(genesisConfigOptions, List.of(fork), specCreator)) + () -> ForksScheduleFactory.create(genesisConfigOptions, List.of(fork), specCreator)) .hasMessage("Transition cannot be created for genesis block"); } @@ -54,12 +52,11 @@ public void throwsErrorIfHasForksWithDuplicateBlock() { final BftFork fork1 = createFork(1, 10); final BftFork fork2 = createFork(1, 20); final BftFork fork3 = createFork(2, 30); - final BftSpecCreator specCreator = - Mockito.mock(BftSpecCreator.class); + final SpecCreator specCreator = Mockito.mock(SpecCreator.class); assertThatThrownBy( () -> - BftForksScheduleFactory.create( + ForksScheduleFactory.create( genesisConfigOptions, List.of(fork1, fork2, fork3), specCreator)) .hasMessage("Duplicate transitions cannot be created for the same block"); } @@ -71,8 +68,7 @@ public void createsScheduleUsingSpecCreator() { final ForkSpec genesisForkSpec = new ForkSpec<>(0, genesisConfigOptions); final BftFork fork1 = createFork(1, 10); final BftFork fork2 = createFork(2, 20); - final BftSpecCreator specCreator = - Mockito.mock(BftSpecCreator.class); + final SpecCreator specCreator = Mockito.mock(SpecCreator.class); final BftConfigOptions configOptions1 = createBftConfigOptions(10); final BftConfigOptions configOptions2 = createBftConfigOptions(20); @@ -80,7 +76,7 @@ public void createsScheduleUsingSpecCreator() { when(specCreator.create(new ForkSpec<>(1, configOptions1), fork2)).thenReturn(configOptions2); final ForksSchedule schedule = - BftForksScheduleFactory.create(genesisConfigOptions, List.of(fork1, fork2), specCreator); + ForksScheduleFactory.create(genesisConfigOptions, List.of(fork1, fork2), specCreator); assertThat(schedule.getFork(0)).isEqualTo(genesisForkSpec); assertThat(schedule.getFork(1)).isEqualTo(new ForkSpec<>(1, configOptions1)); assertThat(schedule.getFork(2)).isEqualTo(new ForkSpec<>(2, configOptions2)); diff --git a/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/IbftForksSchedulesFactory.java b/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/IbftForksSchedulesFactory.java index f41adff34a2..1f5b7fb5f06 100644 --- a/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/IbftForksSchedulesFactory.java +++ b/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/IbftForksSchedulesFactory.java @@ -19,7 +19,7 @@ import org.hyperledger.besu.config.GenesisConfigOptions; import org.hyperledger.besu.consensus.common.ForkSpec; import org.hyperledger.besu.consensus.common.ForksSchedule; -import org.hyperledger.besu.consensus.common.bft.BftForksScheduleFactory; +import org.hyperledger.besu.consensus.common.ForksScheduleFactory; import org.hyperledger.besu.consensus.common.bft.MutableBftConfigOptions; /** The Ibft forks schedules factory. */ @@ -32,7 +32,7 @@ public class IbftForksSchedulesFactory { * @return the forks schedule */ public static ForksSchedule create(final GenesisConfigOptions genesisConfig) { - return BftForksScheduleFactory.create( + return ForksScheduleFactory.create( genesisConfig.getBftConfigOptions(), genesisConfig.getTransitions().getIbftForks(), IbftForksSchedulesFactory::createBftConfigOptions); diff --git a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftForksSchedulesFactory.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftForksSchedulesFactory.java index cf87938cb4a..d66897e405d 100644 --- a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftForksSchedulesFactory.java +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftForksSchedulesFactory.java @@ -20,7 +20,7 @@ import org.hyperledger.besu.config.QbftFork.VALIDATOR_SELECTION_MODE; import org.hyperledger.besu.consensus.common.ForkSpec; import org.hyperledger.besu.consensus.common.ForksSchedule; -import org.hyperledger.besu.consensus.common.bft.BftForksScheduleFactory; +import org.hyperledger.besu.consensus.common.ForksScheduleFactory; import java.util.List; import java.util.Optional; @@ -35,7 +35,7 @@ public class QbftForksSchedulesFactory { * @return the forks schedule */ public static ForksSchedule create(final GenesisConfigOptions genesisConfig) { - return BftForksScheduleFactory.create( + return ForksScheduleFactory.create( genesisConfig.getQbftConfigOptions(), genesisConfig.getTransitions().getQbftForks(), QbftForksSchedulesFactory::createQbftConfigOptions); diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/DefaultBlockScheduler.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/DefaultBlockScheduler.java index becde4a9811..1e0830d3e4e 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/DefaultBlockScheduler.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/DefaultBlockScheduler.java @@ -18,21 +18,29 @@ import java.time.Clock; import java.util.concurrent.TimeUnit; +import java.util.function.ToLongFunction; import com.google.common.annotations.VisibleForTesting; public class DefaultBlockScheduler extends AbstractBlockScheduler { private final long acceptableClockDriftSeconds; - private final long minimumSecondsSinceParent; + private final ToLongFunction calcMinimumSecondsSinceParent; public DefaultBlockScheduler( - final long minimumSecondsSinceParent, + final long calcMinimumSecondsSinceParent, + final long acceptableClockDriftSeconds, + final Clock clock) { + this((bh) -> calcMinimumSecondsSinceParent, acceptableClockDriftSeconds, clock); + } + + protected DefaultBlockScheduler( + final ToLongFunction calcMinimumSecondsSinceParent, final long acceptableClockDriftSeconds, final Clock clock) { super(clock); this.acceptableClockDriftSeconds = acceptableClockDriftSeconds; - this.minimumSecondsSinceParent = minimumSecondsSinceParent; + this.calcMinimumSecondsSinceParent = calcMinimumSecondsSinceParent; } @Override @@ -42,7 +50,8 @@ public BlockCreationTimeResult getNextTimestamp(final BlockHeader parentHeader) final long now = TimeUnit.SECONDS.convert(msSinceEpoch, TimeUnit.MILLISECONDS); final long parentTimestamp = parentHeader.getTimestamp(); - final long nextHeaderTimestamp = Long.max(parentTimestamp + minimumSecondsSinceParent, now); + final long minSecondsSinceParent = calcMinimumSecondsSinceParent.applyAsLong(parentHeader); + final long nextHeaderTimestamp = Long.max(parentTimestamp + minSecondsSinceParent, now); final long earliestBlockTransmissionTime = nextHeaderTimestamp - acceptableClockDriftSeconds; final long msUntilBlocKTransmission = (earliestBlockTransmissionTime * 1000) - msSinceEpoch; diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWMinerExecutorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWMinerExecutorTest.java index 84c46ea6afb..905a6c979de 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWMinerExecutorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWMinerExecutorTest.java @@ -60,7 +60,7 @@ public void startingMiningWithoutCoinbaseThrowsException() { null, transactionPool, miningParameters, - new DefaultBlockScheduler(1, 10, TestClock.fixed()), + new DefaultBlockScheduler(1L, 10, TestClock.fixed()), new EpochCalculator.DefaultEpochCalculator(), ethScheduler); From 828af890218809fc86936bd2d898bc7ae782c620 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Thu, 29 Feb 2024 14:45:41 +1000 Subject: [PATCH 3/4] revert 6499 flat-db-healing-enabled false by default (#6642) Signed-off-by: Sally MacFarlane --- CHANGELOG.md | 1 - .../org/hyperledger/besu/cli/BesuCommand.java | 8 ++++++ .../options/unstable/SynchronizerOptions.java | 26 ++++++++++++++----- .../hyperledger/besu/cli/BesuCommandTest.java | 23 +++++++++++++--- .../sync/snapsync/SnapSyncConfiguration.java | 2 +- 5 files changed, 48 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eccec60f258..b448188004e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,6 @@ ### Deprecations - X_SNAP and X_CHECKPOINT are marked for deprecation and will be removed in 24.4.0 in favor of SNAP and CHECKPOINT [#6405](https://github.com/hyperledger/besu/pull/6405) -- `--Xsnapsync-synchronizer-flat-db-healing-enabled` is deprecated (always enabled). [#6499](https://github.com/hyperledger/besu/pull/6499) - `--Xp2p-peer-lower-bound` is deprecated. [#6501](https://github.com/hyperledger/besu/pull/6501) ### Upcoming Breaking Changes 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 da9f086eeb0..0cbc84645b6 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -1639,6 +1639,14 @@ && isOptionSet(commandLine, "--sync-min-peers")) { SyncMode.isCheckpointSync(getDefaultSyncModeIfNotSet()), singletonList("--Xcheckpoint-post-merge-enabled")); + CommandLineUtils.failIfOptionDoesntMeetRequirement( + commandLine, + "--Xsnapsync-synchronizer-flat option can only be used when -Xsnapsync-synchronizer-flat-db-healing-enabled is true", + unstableSynchronizerOptions.isSnapsyncFlatDbHealingEnabled(), + asList( + "--Xsnapsync-synchronizer-flat-account-healed-count-per-request", + "--Xsnapsync-synchronizer-flat-slot-healed-count-per-request")); + if (!securityModuleName.equals(DEFAULT_SECURITY_MODULE) && nodePrivateKeyFileOption.getNodePrivateKeyFile() != null) { logger.warn( diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/SynchronizerOptions.java b/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/SynchronizerOptions.java index 2825811e525..7c9de815797 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/SynchronizerOptions.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/SynchronizerOptions.java @@ -292,8 +292,7 @@ public void parseBlockPropagationRange(final String arg) { names = SNAP_FLAT_DB_HEALING_ENABLED_FLAG, hidden = true, paramLabel = "", - description = - "(Deprecated) Always enabled: Snap sync flat db healing enabled (default: ${DEFAULT-VALUE})") + description = "Snap sync flat db healing enabled (default: ${DEFAULT-VALUE})") private Boolean snapsyncFlatDbHealingEnabled = SnapSyncConfiguration.DEFAULT_IS_FLAT_DB_HEALING_ENABLED; @@ -306,6 +305,15 @@ public void parseBlockPropagationRange(final String arg) { private SynchronizerOptions() {} + /** + * Flag to know whether the flat db healing feature is enabled or disabled. + * + * @return true is the flat db healing is enabled + */ + public boolean isSnapsyncFlatDbHealingEnabled() { + return snapsyncFlatDbHealingEnabled; + } + /** * Create synchronizer options. * @@ -441,11 +449,15 @@ public List getCLIOptions() { SNAP_BYTECODE_COUNT_PER_REQUEST_FLAG, OptionParser.format(snapsyncBytecodeCountPerRequest), SNAP_TRIENODE_COUNT_PER_REQUEST_FLAG, - OptionParser.format(snapsyncTrieNodeCountPerRequest), - SNAP_FLAT_ACCOUNT_HEALED_COUNT_PER_REQUEST_FLAG, - OptionParser.format(snapsyncFlatAccountHealedCountPerRequest), - SNAP_FLAT_STORAGE_HEALED_COUNT_PER_REQUEST_FLAG, - OptionParser.format(snapsyncFlatStorageHealedCountPerRequest)); + OptionParser.format(snapsyncTrieNodeCountPerRequest)); + if (isSnapsyncFlatDbHealingEnabled()) { + value.addAll( + Arrays.asList( + SNAP_FLAT_ACCOUNT_HEALED_COUNT_PER_REQUEST_FLAG, + OptionParser.format(snapsyncFlatAccountHealedCountPerRequest), + SNAP_FLAT_STORAGE_HEALED_COUNT_PER_REQUEST_FLAG, + OptionParser.format(snapsyncFlatStorageHealedCountPerRequest))); + } return value; } } 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 252e30003d8..854512abfe3 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java @@ -3498,12 +3498,29 @@ public void txpoolTxPoolMinGasPriceMustNotBeGreaterThanMinGasPriceZero() { } @Test - public void snapsyncForHealingFeaturesShouldFailWhenHealingIsNotSet_EnabledByDefault() { + public void snapsyncHealingOptionShouldBeDisabledByDefault() { + final TestBesuCommand besuCommand = parseCommand(); + assertThat(besuCommand.unstableSynchronizerOptions.isSnapsyncFlatDbHealingEnabled()).isFalse(); + } + + @Test + public void snapsyncHealingOptionShouldWork() { + final TestBesuCommand besuCommand = + parseCommand("--Xsnapsync-synchronizer-flat-db-healing-enabled", "true"); + assertThat(besuCommand.unstableSynchronizerOptions.isSnapsyncFlatDbHealingEnabled()).isTrue(); + } + + @Test + public void snapsyncForHealingFeaturesShouldFailWhenHealingIsNotEnabled() { parseCommand("--Xsnapsync-synchronizer-flat-account-healed-count-per-request", "100"); - assertThat(commandOutput.toString(UTF_8)).isEmpty(); + assertThat(commandErrorOutput.toString(UTF_8)) + .contains( + "--Xsnapsync-synchronizer-flat option can only be used when -Xsnapsync-synchronizer-flat-db-healing-enabled is true"); parseCommand("--Xsnapsync-synchronizer-flat-slot-healed-count-per-request", "100"); - assertThat(commandOutput.toString(UTF_8)).isEmpty(); + assertThat(commandErrorOutput.toString(UTF_8)) + .contains( + "--Xsnapsync-synchronizer-flat option can only be used when -Xsnapsync-synchronizer-flat-db-healing-enabled is true"); } @Test diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapSyncConfiguration.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapSyncConfiguration.java index d43fc9d5bd9..6358590395b 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapSyncConfiguration.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapSyncConfiguration.java @@ -36,7 +36,7 @@ public class SnapSyncConfiguration { public static final int DEFAULT_LOCAL_FLAT_STORAGE_COUNT_TO_HEAL_PER_REQUEST = 1024; // The default number of flat slots entries to verify and heal per request. - public static final Boolean DEFAULT_IS_FLAT_DB_HEALING_ENABLED = Boolean.TRUE; + public static final Boolean DEFAULT_IS_FLAT_DB_HEALING_ENABLED = Boolean.FALSE; public static SnapSyncConfiguration getDefault() { return ImmutableSnapSyncConfiguration.builder().build(); From e3e512df90ab34a995ea5bb4e9dbd5f94b22b292 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Thu, 29 Feb 2024 15:19:47 +1000 Subject: [PATCH 4/4] prevent startup if PoA and snap/checkpoint (#6625) * don't start if PoA and snap/checkpoint Signed-off-by: Sally MacFarlane --------- Signed-off-by: Sally MacFarlane --- CHANGELOG.md | 1 + .../java/org/hyperledger/besu/cli/BesuCommand.java | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b448188004e..c5d7949f19c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Breaking Changes - RocksDB database metadata format has changed to be more expressive, the migration of an existing metadata file to the new format is automatic at startup. Before performing a downgrade to a previous version it is mandatory to revert to the original format using the subcommand `besu --data-path=/path/to/besu/datadir storage revert-metadata v2-to-v1`. +- PoA networks won't start with SNAP or CHECKPOINT sync (previously Besu would start with this config but quietly fail to sync, so it's now more obvious that it won't work) [#6625](https://github.com/hyperledger/besu/pull/6625) ### Upcoming Breaking Changes 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 0cbc84645b6..0d7bf07da39 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -1606,6 +1606,16 @@ private GenesisConfigOptions readGenesisConfigOptions() { throw new ParameterException( this.commandLine, "Unable to load genesis file. " + e.getCause()); } + if (genesisConfigOptions.isPoa()) { + final String errorSuffix = "can't be used with PoA networks"; + if (SyncMode.CHECKPOINT.equals(syncMode) || SyncMode.X_CHECKPOINT.equals(syncMode)) { + throw new ParameterException( + commandLine, String.format("%s %s", "Checkpoint sync", errorSuffix)); + } + if (syncMode == SyncMode.SNAP || syncMode == SyncMode.X_SNAP) { + throw new ParameterException(commandLine, String.format("%s %s", "Snap sync", errorSuffix)); + } + } return genesisConfigOptions; } @@ -1950,10 +1960,10 @@ private PrivacyParameters privacyParameters() { if (syncMode == SyncMode.FAST) { throw new ParameterException(commandLine, String.format("%s %s", "Fast sync", errorSuffix)); } - if (syncMode == SyncMode.SNAP) { + if (syncMode == SyncMode.SNAP || syncMode == SyncMode.X_SNAP) { throw new ParameterException(commandLine, String.format("%s %s", "Snap sync", errorSuffix)); } - if (syncMode == SyncMode.CHECKPOINT) { + if (syncMode == SyncMode.CHECKPOINT || syncMode == SyncMode.X_CHECKPOINT) { throw new ParameterException( commandLine, String.format("%s %s", "Checkpoint sync", errorSuffix)); }