Clique block period transition (#6596)

Add BFT-style transitions to Clique, modelled with ForksSchedule<CliqueConfigOptions>
Add ability to transition the blockperiodseconds config.

---------

Signed-off-by: Jason Frame <jason.frame@consensys.net>
Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
Co-authored-by: Jason Frame <jason.frame@consensys.net>
pull/6642/head
Simon Dudley 9 months ago committed by GitHub
parent 647750c201
commit 0e3d2f47d7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 122
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/clique/CliqueMiningAcceptanceTest.java
  3. 9
      besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java
  4. 2
      config/build.gradle
  5. 3
      config/src/main/java/org/hyperledger/besu/config/BftFork.java
  6. 59
      config/src/main/java/org/hyperledger/besu/config/CliqueConfigOptions.java
  7. 66
      config/src/main/java/org/hyperledger/besu/config/CliqueFork.java
  8. 27
      config/src/main/java/org/hyperledger/besu/config/Fork.java
  9. 90
      config/src/main/java/org/hyperledger/besu/config/JsonCliqueConfigOptions.java
  10. 6
      config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java
  11. 4
      config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java
  12. 29
      config/src/main/java/org/hyperledger/besu/config/TransitionsConfigOptions.java
  13. 4
      config/src/test/java/org/hyperledger/besu/config/CliqueConfigOptionsTest.java
  14. 5
      config/src/test/java/org/hyperledger/besu/config/GenesisConfigOptionsTest.java
  15. 49
      consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueForksSchedulesFactory.java
  16. 35
      consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java
  17. 16
      consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockScheduler.java
  18. 29
      consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/CliqueProtocolScheduleTest.java
  19. 2
      consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreatorTest.java
  20. 72
      consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockSchedulerTest.java
  21. 2
      consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java
  22. 33
      consensus/common/src/main/java/org/hyperledger/besu/consensus/common/ForksScheduleFactory.java
  23. 24
      consensus/common/src/test/java/org/hyperledger/besu/consensus/common/ForksScheduleFactoryTest.java
  24. 4
      consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/IbftForksSchedulesFactory.java
  25. 4
      consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftForksSchedulesFactory.java
  26. 17
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/DefaultBlockScheduler.java
  27. 2
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWMinerExecutorTest.java

@ -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)

@ -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 class CliqueMiningAcceptanceTest extends AcceptanceTestBaseJunit5 {
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<String, Object> decreasePeriodTo2_Transition =
Map.of("block", 3, "blockperiodseconds", 2);
final Map<String, Object> decreasePeriodTo1_Transition =
Map.of("block", 4, "blockperiodseconds", 1);
final Map<String, Object> increasePeriodTo2_Transition =
Map.of("block", 6, "blockperiodseconds", 2);
final Optional<String> 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<Map<String, Object>> 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<Map<String, Object>> 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<String, Object> 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();
}
}

@ -19,6 +19,7 @@ import static org.hyperledger.besu.consensus.clique.CliqueHelpers.installCliqueB
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.blockcreation.CliqueMiningCoordinat
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<CliqueConfigOptions> 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 @@ public class CliqueBesuControllerBuilder extends BesuControllerBuilder {
clock,
protocolContext.getConsensusContext(CliqueContext.class).getValidatorProvider(),
localAddress,
secondsBetweenBlocks),
forksSchedule),
epochManager,
createEmptyBlocks,
ethProtocolManager.ethContext().getScheduler());
@ -113,6 +115,7 @@ public class CliqueBesuControllerBuilder extends BesuControllerBuilder {
protected ProtocolSchedule createProtocolSchedule() {
return CliqueProtocolSchedule.create(
configOptionsSupplier.get(),
forksSchedule,
nodeKey,
privacyParameters,
isRevertReasonEnabled,

@ -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')

@ -28,7 +28,7 @@ import com.google.common.collect.Lists;
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 class BftFork {
*
* @return the fork block
*/
@Override
public long getForkBlock() {
return JsonUtil.getLong(forkConfigRoot, FORK_BLOCK_KEY)
.orElseThrow(

@ -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<String, Object> asMap() {
return ImmutableMap.of(
"epochLength",
getEpochLength(),
"blockPeriodSeconds",
getBlockPeriodSeconds(),
"createemptyblocks",
getCreateEmptyBlocks());
}
Map<String, Object> asMap();
}

@ -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);
}
}

@ -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();
}

@ -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<String, Object> asMap() {
return ImmutableMap.of(
"epochLength",
getEpochLength(),
"blockPeriodSeconds",
getBlockPeriodSeconds(),
"createemptyblocks",
getCreateEmptyBlocks());
}
}

@ -180,10 +180,10 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions {
}
@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

@ -129,8 +129,8 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions, Cloneable
}
@Override
public CliqueConfigOptions getCliqueConfigOptions() {
return CliqueConfigOptions.DEFAULT;
public JsonCliqueConfigOptions getCliqueConfigOptions() {
return JsonCliqueConfigOptions.DEFAULT;
}
@Override

@ -51,7 +51,7 @@ public class TransitionsConfigOptions {
* @return the ibft forks
*/
public List<BftFork> getIbftForks() {
return getBftForks("ibft2", BftFork::new);
return getForks("ibft2", BftFork::new);
}
/**
@ -60,30 +60,39 @@ public class TransitionsConfigOptions {
* @return the qbft forks
*/
public List<QbftFork> getQbftForks() {
return getBftForks("qbft", QbftFork::new);
return getForks("qbft", QbftFork::new);
}
private <T> List<T> getBftForks(
/**
* Gets clique forks.
*
* @return the clique forks
*/
public List<CliqueFork> getCliqueForks() {
return getForks("clique", CliqueFork::new);
}
private <T> List<T> getForks(
final String fieldKey, final Function<ObjectNode, T> forkConstructor) {
final Optional<ArrayNode> bftForksNode = JsonUtil.getArrayNode(customForkConfigRoot, fieldKey);
final Optional<ArrayNode> forksNode = JsonUtil.getArrayNode(customForkConfigRoot, fieldKey);
if (bftForksNode.isEmpty()) {
if (forksNode.isEmpty()) {
return emptyList();
}
final List<T> bftForks = Lists.newArrayList();
final List<T> 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);
}
}

@ -43,7 +43,7 @@ public class CliqueConfigOptionsTest {
@Test
public void shouldGetDefaultEpochLengthFromDefaultConfig() {
assertThat(CliqueConfigOptions.DEFAULT.getEpochLength())
assertThat(JsonCliqueConfigOptions.DEFAULT.getEpochLength())
.isEqualTo(EXPECTED_DEFAULT_EPOCH_LENGTH);
}
@ -61,7 +61,7 @@ public class CliqueConfigOptionsTest {
@Test
public void shouldGetDefaultBlockPeriodFromDefaultConfig() {
assertThat(CliqueConfigOptions.DEFAULT.getBlockPeriodSeconds())
assertThat(JsonCliqueConfigOptions.DEFAULT.getBlockPeriodSeconds())
.isEqualTo(EXPECTED_DEFAULT_BLOCK_PERIOD);
}

@ -52,6 +52,7 @@ class GenesisConfigOptionsTest {
assertThat(config.getConsensusEngine()).isEqualTo("ibft2");
}
@Test
void shouldUseQbftWhenQbftInConfig() {
final GenesisConfigOptions config = fromConfigOptions(singletonMap("qbft", emptyMap()));
assertThat(config.isQbft()).isTrue();
@ -64,7 +65,7 @@ class GenesisConfigOptionsTest {
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 @@ class GenesisConfigOptionsTest {
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

@ -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<CliqueConfigOptions> create(
final GenesisConfigOptions genesisConfig) {
return ForksScheduleFactory.create(
genesisConfig.getCliqueConfigOptions(),
genesisConfig.getTransitions().getCliqueForks(),
CliqueForksSchedulesFactory::createCliqueConfigOptions);
}
private static CliqueConfigOptions createCliqueConfigOptions(
final ForkSpec<CliqueConfigOptions> lastSpec, final CliqueFork fork) {
var options = ImmutableCliqueConfigOptions.builder().from(lastSpec.getValue());
fork.getBlockPeriodSeconds().ifPresent(options::blockPeriodSeconds);
return options.build();
}
}

@ -17,6 +17,7 @@ package org.hyperledger.besu.consensus.clique;
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.ethereum.mainnet.feemarket.FeeMarket;
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<CliqueConfigOptions> forksSchedule,
final NodeKey nodeKey,
final PrivacyParameters privacyParameters,
final boolean isRevertReasonEnabled,
@ -72,18 +78,26 @@ public class CliqueProtocolSchedule {
final EpochManager epochManager = new EpochManager(cliqueConfig.getEpochLength());
final Map<Long, Function<ProtocolSpecBuilder, ProtocolSpecBuilder>> 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 class CliqueProtocolSchedule {
* 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 class CliqueProtocolSchedule {
*/
public static ProtocolSchedule create(
final GenesisConfigOptions config,
final ForksSchedule<CliqueConfigOptions> forksSchedule,
final NodeKey nodeKey,
final boolean isRevertReasonEnabled,
final EvmConfiguration evmConfiguration,
final BadBlockManager badBlockManager) {
return create(
config,
forksSchedule,
nodeKey,
PrivacyParameters.DEFAULT,
isRevertReasonEnabled,

@ -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<CliqueConfigOptions> forksSchedule) {
super(
parentHeader ->
(long)
forksSchedule
.getFork(parentHeader.getNumber() + 1)
.getValue()
.getBlockPeriodSeconds(),
0L,
clock);
this.validatorProvider = validatorProvider;
this.localNodeAddress = localNodeAddress;
}

@ -22,6 +22,9 @@ import static org.mockito.Mockito.when;
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.ethereum.mainnet.ProtocolSpec;
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 class CliqueProtocolScheduleTest {
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 class CliqueProtocolScheduleTest {
@Test
public void parametersAlignWithMainnetWithAdjustments() {
final ForksSchedule<CliqueConfigOptions> 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 class CliqueProtocolScheduleTest {
@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 class CliqueProtocolScheduleTest {
() ->
CliqueProtocolSchedule.create(
genesisConfig,
new ForksSchedule<>(List.of()),
NODE_KEY,
false,
EvmConfiguration.DEFAULT,
@ -106,7 +119,7 @@ public class CliqueProtocolScheduleTest {
@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 class CliqueProtocolScheduleTest {
() ->
CliqueProtocolSchedule.create(
genesisConfig,
new ForksSchedule<>(List.of()),
NODE_KEY,
false,
EvmConfiguration.DEFAULT,
@ -131,9 +145,16 @@ public class CliqueProtocolScheduleTest {
"{\"config\": " + "\t{\"chainId\": 1337,\n" + "\t\"londonBlock\": 2}\n" + "}";
final GenesisConfigOptions config = GenesisConfigFile.fromConfig(jsonInput).getConfigOptions();
final ForksSchedule<CliqueConfigOptions> 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

@ -30,6 +30,7 @@ import org.hyperledger.besu.consensus.clique.CliqueHelpers;
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 class CliqueBlockCreatorTest {
protocolSchedule =
CliqueProtocolSchedule.create(
GenesisConfigFile.DEFAULT.getConfigOptions(),
new ForksSchedule<>(List.of()),
proposerNodeKey,
false,
EvmConfiguration.DEFAULT,

@ -19,6 +19,11 @@ import static org.mockito.ArgumentMatchers.any;
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<Address> validatorList = Lists.newArrayList();
private ValidatorProvider validatorProvider;
private BlockHeaderTestFixture blockHeaderBuilder;
private ForksSchedule<CliqueConfigOptions> forksSchedule;
@BeforeEach
public void setup() {
@ -56,16 +62,21 @@ public class CliqueBlockSchedulerTest {
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 class CliqueBlockSchedulerTest {
}
@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 class CliqueBlockSchedulerTest {
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 class CliqueBlockSchedulerTest {
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.

@ -28,6 +28,7 @@ import org.hyperledger.besu.consensus.clique.CliqueContext;
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 class CliqueMinerExecutorTest {
cliqueProtocolSchedule =
CliqueProtocolSchedule.create(
GENESIS_CONFIG_OPTIONS,
new ForksSchedule<>(List.of()),
proposerNodeKey,
false,
EvmConfiguration.DEFAULT,

@ -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 <T> the type parameter
* @param <U> the type parameter
*/
public interface BftSpecCreator<T extends BftConfigOptions, U extends BftFork> {
public interface SpecCreator<T, U> {
/**
* 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<T> lastSpec, U fork);
}
@ -49,20 +46,20 @@ public class BftForksScheduleFactory {
/**
* Create forks schedule.
*
* @param <T> the type parameter BftConfigOptions
* @param <U> the type parameter BftFork
* @param <T> the type parameter ConfigOptions
* @param <U> the type parameter Fork
* @param initial the initial
* @param forks the forks
* @param specCreator the spec creator
* @return the forks schedule
*/
public static <T extends BftConfigOptions, U extends BftFork> ForksSchedule<T> create(
final T initial, final List<U> forks, final BftSpecCreator<T, U> specCreator) {
public static <T, U extends Fork> ForksSchedule<T> create(
final T initial, final List<U> forks, final SpecCreator<T, U> 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<ForkSpec<T>> specs = new TreeSet<>(Comparator.comparing(ForkSpec::getBlock));
@ -70,7 +67,7 @@ public class BftForksScheduleFactory {
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);

@ -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.BftConfigOptions;
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 java.util.Map;
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<BftConfigOptions, BftFork> specCreator =
Mockito.mock(BftSpecCreator.class);
final SpecCreator<BftConfigOptions, BftFork> 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 class BftForksScheduleFactoryTest {
final BftFork fork1 = createFork(1, 10);
final BftFork fork2 = createFork(1, 20);
final BftFork fork3 = createFork(2, 30);
final BftSpecCreator<BftConfigOptions, BftFork> specCreator =
Mockito.mock(BftSpecCreator.class);
final SpecCreator<BftConfigOptions, BftFork> 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 class BftForksScheduleFactoryTest {
final ForkSpec<BftConfigOptions> genesisForkSpec = new ForkSpec<>(0, genesisConfigOptions);
final BftFork fork1 = createFork(1, 10);
final BftFork fork2 = createFork(2, 20);
final BftSpecCreator<BftConfigOptions, BftFork> specCreator =
Mockito.mock(BftSpecCreator.class);
final SpecCreator<BftConfigOptions, BftFork> specCreator = Mockito.mock(SpecCreator.class);
final BftConfigOptions configOptions1 = createBftConfigOptions(10);
final BftConfigOptions configOptions2 = createBftConfigOptions(20);
@ -80,7 +76,7 @@ public class BftForksScheduleFactoryTest {
when(specCreator.create(new ForkSpec<>(1, configOptions1), fork2)).thenReturn(configOptions2);
final ForksSchedule<BftConfigOptions> 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));

@ -19,7 +19,7 @@ import org.hyperledger.besu.config.BftFork;
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<BftConfigOptions> create(final GenesisConfigOptions genesisConfig) {
return BftForksScheduleFactory.create(
return ForksScheduleFactory.create(
genesisConfig.getBftConfigOptions(),
genesisConfig.getTransitions().getIbftForks(),
IbftForksSchedulesFactory::createBftConfigOptions);

@ -20,7 +20,7 @@ import org.hyperledger.besu.config.QbftFork;
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<QbftConfigOptions> create(final GenesisConfigOptions genesisConfig) {
return BftForksScheduleFactory.create(
return ForksScheduleFactory.create(
genesisConfig.getQbftConfigOptions(),
genesisConfig.getTransitions().getQbftForks(),
QbftForksSchedulesFactory::createQbftConfigOptions);

@ -18,21 +18,29 @@ import org.hyperledger.besu.ethereum.core.BlockHeader;
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<BlockHeader> calcMinimumSecondsSinceParent;
public DefaultBlockScheduler(
final long minimumSecondsSinceParent,
final long calcMinimumSecondsSinceParent,
final long acceptableClockDriftSeconds,
final Clock clock) {
this((bh) -> calcMinimumSecondsSinceParent, acceptableClockDriftSeconds, clock);
}
protected DefaultBlockScheduler(
final ToLongFunction<BlockHeader> calcMinimumSecondsSinceParent,
final long acceptableClockDriftSeconds,
final Clock clock) {
super(clock);
this.acceptableClockDriftSeconds = acceptableClockDriftSeconds;
this.minimumSecondsSinceParent = minimumSecondsSinceParent;
this.calcMinimumSecondsSinceParent = calcMinimumSecondsSinceParent;
}
@Override
@ -42,7 +50,8 @@ public class DefaultBlockScheduler extends AbstractBlockScheduler {
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;

@ -60,7 +60,7 @@ public class PoWMinerExecutorTest {
null,
transactionPool,
miningParameters,
new DefaultBlockScheduler(1, 10, TestClock.fixed()),
new DefaultBlockScheduler(1L, 10, TestClock.fixed()),
new EpochCalculator.DefaultEpochCalculator(),
ethScheduler);

Loading…
Cancel
Save