Dev/test option for short BFT block periods (#7588)

* Dev mode for short BFT block periods

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Refactoring

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Fix comment

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Refactor to make BFT block milliseconds an experimental QBFT config option

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Update Json BFT config options

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

---------

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
pull/7655/head
Matt Whitehead 1 month ago committed by GitHub
parent e721237c26
commit 19d3ca84b2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 6
      besu/src/main/java/org/hyperledger/besu/controller/IbftBesuControllerBuilder.java
  2. 6
      besu/src/main/java/org/hyperledger/besu/controller/QbftBesuControllerBuilder.java
  3. 7
      config/src/main/java/org/hyperledger/besu/config/BftConfigOptions.java
  4. 13
      config/src/main/java/org/hyperledger/besu/config/BftFork.java
  5. 10
      config/src/main/java/org/hyperledger/besu/config/JsonBftConfigOptions.java
  6. 29
      consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BlockTimer.java
  7. 17
      consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/MutableBftConfigOptions.java
  8. 12
      consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/RoundTimer.java
  9. 3
      consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/RoundTimerTest.java
  10. 3
      consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/support/TestContextBuilder.java
  11. 58
      consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/IbftBlockHeaderValidationRulesetFactory.java
  12. 6
      consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/IbftProtocolScheduleBuilder.java
  13. 6
      consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/IbftBlockHeaderValidationRulesetFactoryTest.java
  14. 5
      consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/BftBlockCreatorTest.java
  15. 3
      consensus/qbft/src/integration-test/java/org/hyperledger/besu/consensus/qbft/support/TestContextBuilder.java
  16. 50
      consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftBlockHeaderValidationRulesetFactory.java
  17. 1
      consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftForksSchedulesFactory.java
  18. 5
      consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftProtocolScheduleBuilder.java
  19. 6
      consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/QbftBlockHeaderValidationRulesetFactoryTest.java

@ -74,6 +74,7 @@ import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.plugin.services.BesuEvents;
import org.hyperledger.besu.util.Subscribers;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -182,7 +183,10 @@ public class IbftBesuControllerBuilder extends BftBesuControllerBuilder {
Util.publicKeyToAddress(nodeKey.getPublicKey()),
proposerSelector,
uniqueMessageMulticaster,
new RoundTimer(bftEventQueue, bftConfig.getRequestTimeoutSeconds(), bftExecutors),
new RoundTimer(
bftEventQueue,
Duration.ofSeconds(bftConfig.getRequestTimeoutSeconds()),
bftExecutors),
new BlockTimer(bftEventQueue, forksSchedule, bftExecutors, clock),
blockCreatorFactory,
clock);

@ -84,6 +84,7 @@ import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.plugin.services.BesuEvents;
import org.hyperledger.besu.util.Subscribers;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -222,7 +223,10 @@ public class QbftBesuControllerBuilder extends BftBesuControllerBuilder {
Util.publicKeyToAddress(nodeKey.getPublicKey()),
proposerSelector,
uniqueMessageMulticaster,
new RoundTimer(bftEventQueue, qbftConfig.getRequestTimeoutSeconds(), bftExecutors),
new RoundTimer(
bftEventQueue,
Duration.ofSeconds(qbftConfig.getRequestTimeoutSeconds()),
bftExecutors),
new BlockTimer(bftEventQueue, qbftForksSchedule, bftExecutors, clock),
blockCreatorFactory,
clock);

@ -37,6 +37,13 @@ public interface BftConfigOptions {
*/
int getBlockPeriodSeconds();
/**
* Gets block period milliseconds. For TESTING only. If set then blockperiodseconds is ignored.
*
* @return the block period milliseconds
*/
long getBlockPeriodMilliseconds();
/**
* Gets request timeout seconds.
*

@ -21,6 +21,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.OptionalLong;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.databind.node.ArrayNode;
@ -40,6 +41,9 @@ public class BftFork implements Fork {
/** The constant BLOCK_PERIOD_SECONDS_KEY. */
public static final String BLOCK_PERIOD_SECONDS_KEY = "blockperiodseconds";
/** The constant BLOCK_PERIOD_MILLISECONDS_KEY. */
public static final String BLOCK_PERIOD_MILLISECONDS_KEY = "xblockperiodmilliseconds";
/** The constant BLOCK_REWARD_KEY. */
public static final String BLOCK_REWARD_KEY = "blockreward";
@ -82,6 +86,15 @@ public class BftFork implements Fork {
return JsonUtil.getPositiveInt(forkConfigRoot, BLOCK_PERIOD_SECONDS_KEY);
}
/**
* Gets block period milliseconds. Experimental for test scenarios only.
*
* @return the block period milliseconds
*/
public OptionalLong getBlockPeriodMilliseconds() {
return JsonUtil.getLong(forkConfigRoot, BLOCK_PERIOD_MILLISECONDS_KEY);
}
/**
* Gets block reward wei.
*

@ -34,6 +34,7 @@ public class JsonBftConfigOptions implements BftConfigOptions {
private static final long DEFAULT_EPOCH_LENGTH = 30_000;
private static final int DEFAULT_BLOCK_PERIOD_SECONDS = 1;
private static final int DEFAULT_BLOCK_PERIOD_MILLISECONDS = 0; // Experimental for test only
private static final int DEFAULT_ROUND_EXPIRY_SECONDS = 1;
// In a healthy network this can be very small. This default limit will allow for suitable
// protection for on a typical 20 node validator network with multiple rounds
@ -66,6 +67,12 @@ public class JsonBftConfigOptions implements BftConfigOptions {
bftConfigRoot, "blockperiodseconds", DEFAULT_BLOCK_PERIOD_SECONDS);
}
@Override
public long getBlockPeriodMilliseconds() {
return JsonUtil.getLong(
bftConfigRoot, "xblockperiodmilliseconds", DEFAULT_BLOCK_PERIOD_MILLISECONDS);
}
@Override
public int getRequestTimeoutSeconds() {
return JsonUtil.getInt(bftConfigRoot, "requesttimeoutseconds", DEFAULT_ROUND_EXPIRY_SECONDS);
@ -133,6 +140,9 @@ public class JsonBftConfigOptions implements BftConfigOptions {
if (bftConfigRoot.has("blockperiodseconds")) {
builder.put("blockPeriodSeconds", getBlockPeriodSeconds());
}
if (bftConfigRoot.has("xblockperiodmilliseconds")) {
builder.put("xBlockPeriodMilliSeconds", getBlockPeriodMilliseconds());
}
if (bftConfigRoot.has("requesttimeoutseconds")) {
builder.put("requestTimeoutSeconds", getRequestTimeoutSeconds());
}

@ -24,9 +24,14 @@ import java.util.Optional;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Class for starting and keeping organised block timers */
public class BlockTimer {
private static final Logger LOG = LoggerFactory.getLogger(BlockTimer.class);
private final ForksSchedule<? extends BftConfigOptions> forksSchedule;
private final BftExecutors bftExecutors;
private Optional<ScheduledFuture<?>> currentTimerTask;
@ -79,12 +84,26 @@ public class BlockTimer {
cancelTimer();
final long now = clock.millis();
final long expiryTime;
// Experimental option for test scenarios only. Not for production use.
final long blockPeriodMilliseconds =
forksSchedule.getFork(round.getSequenceNumber()).getValue().getBlockPeriodMilliseconds();
// absolute time when the timer is supposed to expire
final int blockPeriodSeconds =
forksSchedule.getFork(round.getSequenceNumber()).getValue().getBlockPeriodSeconds();
final long minimumTimeBetweenBlocksMillis = blockPeriodSeconds * 1000L;
final long expiryTime = chainHeadHeader.getTimestamp() * 1_000 + minimumTimeBetweenBlocksMillis;
if (blockPeriodMilliseconds > 0) {
// Experimental mode for setting < 1 second block periods e.g. for CI/CD pipelines
// running tests against Besu
expiryTime = clock.millis() + blockPeriodMilliseconds;
LOG.warn(
"Test-mode only xblockperiodmilliseconds has been set to {} millisecond blocks. Do not use in a production system.",
blockPeriodMilliseconds);
} else {
// absolute time when the timer is supposed to expire
final int blockPeriodSeconds =
forksSchedule.getFork(round.getSequenceNumber()).getValue().getBlockPeriodSeconds();
final long minimumTimeBetweenBlocksMillis = blockPeriodSeconds * 1000L;
expiryTime = chainHeadHeader.getTimestamp() * 1_000 + minimumTimeBetweenBlocksMillis;
}
if (expiryTime > now) {
final long delay = expiryTime - now;

@ -31,6 +31,7 @@ import java.util.Optional;
public class MutableBftConfigOptions implements BftConfigOptions {
private long epochLength;
private int blockPeriodSeconds;
private long blockPeriodMilliseconds;
private int requestTimeoutSeconds;
private int gossipedHistoryLimit;
private int messageQueueLimit;
@ -48,6 +49,7 @@ public class MutableBftConfigOptions implements BftConfigOptions {
public MutableBftConfigOptions(final BftConfigOptions bftConfigOptions) {
this.epochLength = bftConfigOptions.getEpochLength();
this.blockPeriodSeconds = bftConfigOptions.getBlockPeriodSeconds();
this.blockPeriodMilliseconds = bftConfigOptions.getBlockPeriodMilliseconds();
this.requestTimeoutSeconds = bftConfigOptions.getRequestTimeoutSeconds();
this.gossipedHistoryLimit = bftConfigOptions.getGossipedHistoryLimit();
this.messageQueueLimit = bftConfigOptions.getMessageQueueLimit();
@ -68,6 +70,11 @@ public class MutableBftConfigOptions implements BftConfigOptions {
return blockPeriodSeconds;
}
@Override
public long getBlockPeriodMilliseconds() {
return blockPeriodMilliseconds;
}
@Override
public int getRequestTimeoutSeconds() {
return requestTimeoutSeconds;
@ -131,6 +138,16 @@ public class MutableBftConfigOptions implements BftConfigOptions {
this.blockPeriodSeconds = blockPeriodSeconds;
}
/**
* Sets block period milliseconds. Experimental for test scenarios. Not for use on production
* systems.
*
* @param blockPeriodMilliseconds the block period milliseconds
*/
public void setBlockPeriodMilliseconds(final long blockPeriodMilliseconds) {
this.blockPeriodMilliseconds = blockPeriodMilliseconds;
}
/**
* Sets request timeout seconds.
*

@ -16,6 +16,7 @@ package org.hyperledger.besu.consensus.common.bft;
import org.hyperledger.besu.consensus.common.bft.events.RoundExpiry;
import java.time.Duration;
import java.util.Optional;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@ -31,21 +32,21 @@ public class RoundTimer {
private final BftExecutors bftExecutors;
private Optional<ScheduledFuture<?>> currentTimerTask;
private final BftEventQueue queue;
private final long baseExpiryMillis;
private final Duration baseExpiryPeriod;
/**
* Construct a RoundTimer with primed executor service ready to start timers
*
* @param queue The queue in which to put round expiry events
* @param baseExpirySeconds The initial round length for round 0
* @param baseExpiryPeriod The initial round length for round 0
* @param bftExecutors executor service that timers can be scheduled with
*/
public RoundTimer(
final BftEventQueue queue, final long baseExpirySeconds, final BftExecutors bftExecutors) {
final BftEventQueue queue, final Duration baseExpiryPeriod, final BftExecutors bftExecutors) {
this.queue = queue;
this.bftExecutors = bftExecutors;
this.currentTimerTask = Optional.empty();
this.baseExpiryMillis = baseExpirySeconds * 1000;
this.baseExpiryPeriod = baseExpiryPeriod;
}
/** Cancels the current running round timer if there is one */
@ -71,7 +72,8 @@ public class RoundTimer {
public synchronized void startTimer(final ConsensusRoundIdentifier round) {
cancelTimer();
final long expiryTime = baseExpiryMillis * (long) Math.pow(2, round.getRoundNumber());
final long expiryTime =
baseExpiryPeriod.toMillis() * (long) Math.pow(2, round.getRoundNumber());
final Runnable newTimerRunnable = () -> queue.add(new RoundExpiry(round));

@ -25,6 +25,7 @@ import static org.mockito.Mockito.when;
import org.hyperledger.besu.consensus.common.bft.events.RoundExpiry;
import java.time.Duration;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@ -46,7 +47,7 @@ public class RoundTimerTest {
bftExecutors = mock(BftExecutors.class);
queue = new BftEventQueue(1000);
queue.start();
timer = new RoundTimer(queue, 1, bftExecutors);
timer = new RoundTimer(queue, Duration.ofSeconds(1), bftExecutors);
}
@Test

@ -100,6 +100,7 @@ import org.hyperledger.besu.testutil.TestClock;
import org.hyperledger.besu.util.Subscribers;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.util.ArrayList;
@ -403,7 +404,7 @@ public class TestContextBuilder {
Util.publicKeyToAddress(nodeKey.getPublicKey()),
proposerSelector,
multicaster,
new RoundTimer(bftEventQueue, ROUND_TIMER_SEC, bftExecutors),
new RoundTimer(bftEventQueue, Duration.ofSeconds(ROUND_TIMER_SEC), bftExecutors),
new BlockTimer(bftEventQueue, forksSchedule, bftExecutors, TestClock.fixed()),
blockCreatorFactory,
clock);

@ -32,6 +32,7 @@ import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.GasUsageValid
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.TimestampBoundedByFutureParameter;
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.TimestampMoreRecentThanParent;
import java.time.Duration;
import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt256;
@ -45,32 +46,43 @@ public class IbftBlockHeaderValidationRulesetFactory {
* Produces a BlockHeaderValidator configured for assessing bft block headers which are to form
* part of the BlockChain (i.e. not proposed blocks, which do not contain commit seals)
*
* @param secondsBetweenBlocks the minimum number of seconds which must elapse between blocks.
* @param minimumTimeBetweenBlocks the minimum time which must elapse between blocks.
* @param baseFeeMarket an {@link Optional} wrapping {@link BaseFeeMarket} class if appropriate.
* @return BlockHeaderValidator configured for assessing bft block headers
*/
public static BlockHeaderValidator.Builder blockHeaderValidator(
final long secondsBetweenBlocks, final Optional<BaseFeeMarket> baseFeeMarket) {
return new BlockHeaderValidator.Builder()
.addRule(new AncestryValidationRule())
.addRule(new GasUsageValidationRule())
.addRule(
new GasLimitRangeAndDeltaValidationRule(
DEFAULT_MIN_GAS_LIMIT, DEFAULT_MAX_GAS_LIMIT, baseFeeMarket))
.addRule(new TimestampBoundedByFutureParameter(1))
.addRule(new TimestampMoreRecentThanParent(secondsBetweenBlocks))
.addRule(
new ConstantFieldValidationRule<>(
"MixHash", BlockHeader::getMixHash, BftHelpers.EXPECTED_MIX_HASH))
.addRule(
new ConstantFieldValidationRule<>(
"OmmersHash", BlockHeader::getOmmersHash, Hash.EMPTY_LIST_HASH))
.addRule(
new ConstantFieldValidationRule<>(
"Difficulty", BlockHeader::getDifficulty, UInt256.ONE))
.addRule(new ConstantFieldValidationRule<>("Nonce", BlockHeader::getNonce, 0L))
.addRule(new BftValidatorsValidationRule())
.addRule(new BftCoinbaseValidationRule())
.addRule(new BftCommitSealsValidationRule());
final Duration minimumTimeBetweenBlocks, final Optional<BaseFeeMarket> baseFeeMarket) {
final BlockHeaderValidator.Builder ruleBuilder =
new BlockHeaderValidator.Builder()
.addRule(new AncestryValidationRule())
.addRule(new GasUsageValidationRule())
.addRule(
new GasLimitRangeAndDeltaValidationRule(
DEFAULT_MIN_GAS_LIMIT, DEFAULT_MAX_GAS_LIMIT, baseFeeMarket))
.addRule(new TimestampBoundedByFutureParameter(1))
.addRule(
new ConstantFieldValidationRule<>(
"MixHash", BlockHeader::getMixHash, BftHelpers.EXPECTED_MIX_HASH))
.addRule(
new ConstantFieldValidationRule<>(
"OmmersHash", BlockHeader::getOmmersHash, Hash.EMPTY_LIST_HASH))
.addRule(
new ConstantFieldValidationRule<>(
"Difficulty", BlockHeader::getDifficulty, UInt256.ONE))
.addRule(new ConstantFieldValidationRule<>("Nonce", BlockHeader::getNonce, 0L))
.addRule(new BftValidatorsValidationRule())
.addRule(new BftCoinbaseValidationRule())
.addRule(new BftCommitSealsValidationRule());
// Currently the minimum acceptable time between blocks is 1 second. The timestamp of an
// Ethereum header is stored as seconds since Unix epoch so blocks being produced more
// frequently than once a second cannot pass this validator. For non-production scenarios
// (e.g. for testing block production much more frequently than once a second) Besu has
// an experimental 'xblockperiodmilliseconds' option for BFT chains. If this is enabled
// we cannot apply the TimestampMoreRecentThanParent validation rule so we do not add it
if (minimumTimeBetweenBlocks.compareTo(Duration.ofSeconds(1)) >= 0) {
ruleBuilder.addRule(new TimestampMoreRecentThanParent(minimumTimeBetweenBlocks.getSeconds()));
}
return ruleBuilder;
}
}

@ -29,6 +29,7 @@ import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.time.Duration;
import java.util.Optional;
/** Defines the protocol behaviours for a blockchain using a BFT consensus mechanism. */
@ -120,6 +121,9 @@ public class IbftProtocolScheduleBuilder extends BaseBftProtocolScheduleBuilder
Optional.of(feeMarket).filter(FeeMarket::implementsBaseFee).map(BaseFeeMarket.class::cast);
return IbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
config.getBlockPeriodSeconds(), baseFeeMarket);
config.getBlockPeriodMilliseconds() > 0
? Duration.ofMillis(config.getBlockPeriodMilliseconds())
: Duration.ofSeconds(config.getBlockPeriodSeconds()),
baseFeeMarket);
}
}

@ -38,6 +38,7 @@ import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
@ -93,7 +94,7 @@ public class IbftBlockHeaderValidationRulesetFactoryTest {
final BlockHeaderValidator validator =
IbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
5, Optional.of(FeeMarket.london(1)))
Duration.ofSeconds(5), Optional.of(FeeMarket.london(1)))
.build();
assertThat(
@ -372,7 +373,8 @@ public class IbftBlockHeaderValidationRulesetFactoryTest {
}
public BlockHeaderValidator getBlockHeaderValidator() {
return IbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(5, Optional.empty())
return IbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
Duration.ofSeconds(5), Optional.empty())
.build();
}
}

@ -64,6 +64,7 @@ import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.testutil.TestClock;
import java.time.Duration;
import java.time.ZoneId;
import java.util.Collections;
import java.util.List;
@ -105,7 +106,7 @@ public class BftBlockCreatorTest {
public BlockHeaderValidator.Builder createBlockHeaderRuleset(
final BftConfigOptions config, final FeeMarket feeMarket) {
return IbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
5, Optional.empty());
Duration.ofSeconds(5), Optional.empty());
}
};
final GenesisConfigOptions configOptions =
@ -200,7 +201,7 @@ public class BftBlockCreatorTest {
final BlockHeaderValidator rules =
IbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
secondsBetweenBlocks, Optional.empty())
Duration.ofSeconds(secondsBetweenBlocks), Optional.empty())
.build();
// NOTE: The header will not contain commit seals, so can only do light validation on header.

@ -118,6 +118,7 @@ import org.hyperledger.besu.util.Subscribers;
import java.io.IOException;
import java.nio.file.Path;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.util.ArrayList;
@ -512,7 +513,7 @@ public class TestContextBuilder {
Util.publicKeyToAddress(nodeKey.getPublicKey()),
proposerSelector,
multicaster,
new RoundTimer(bftEventQueue, ROUND_TIMER_SEC, bftExecutors),
new RoundTimer(bftEventQueue, Duration.ofSeconds(ROUND_TIMER_SEC), bftExecutors),
new BlockTimer(bftEventQueue, forksSchedule, bftExecutors, TestClock.fixed()),
blockCreatorFactory,
clock);

@ -31,6 +31,7 @@ import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.GasUsageValid
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.TimestampBoundedByFutureParameter;
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.TimestampMoreRecentThanParent;
import java.time.Duration;
import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt256;
@ -44,31 +45,42 @@ public class QbftBlockHeaderValidationRulesetFactory {
* Produces a BlockHeaderValidator configured for assessing bft block headers which are to form
* part of the BlockChain (i.e. not proposed blocks, which do not contain commit seals)
*
* @param secondsBetweenBlocks the minimum number of seconds which must elapse between blocks.
* @param minimumTimeBetweenBlocks the minimum amount of time that must elapse between blocks.
* @param useValidatorContract whether validator selection is using a validator contract
* @param baseFeeMarket an {@link Optional} wrapping {@link BaseFeeMarket} class if appropriate.
* @return BlockHeaderValidator configured for assessing bft block headers
*/
public static BlockHeaderValidator.Builder blockHeaderValidator(
final long secondsBetweenBlocks,
final Duration minimumTimeBetweenBlocks,
final boolean useValidatorContract,
final Optional<BaseFeeMarket> baseFeeMarket) {
return new BlockHeaderValidator.Builder()
.addRule(new AncestryValidationRule())
.addRule(new GasUsageValidationRule())
.addRule(
new GasLimitRangeAndDeltaValidationRule(
DEFAULT_MIN_GAS_LIMIT, DEFAULT_MAX_GAS_LIMIT, baseFeeMarket))
.addRule(new TimestampBoundedByFutureParameter(1))
.addRule(new TimestampMoreRecentThanParent(secondsBetweenBlocks))
.addRule(
new ConstantFieldValidationRule<>(
"MixHash", BlockHeader::getMixHash, BftHelpers.EXPECTED_MIX_HASH))
.addRule(
new ConstantFieldValidationRule<>(
"Difficulty", BlockHeader::getDifficulty, UInt256.ONE))
.addRule(new QbftValidatorsValidationRule(useValidatorContract))
.addRule(new BftCoinbaseValidationRule())
.addRule(new BftCommitSealsValidationRule());
BlockHeaderValidator.Builder ruleBuilder =
new BlockHeaderValidator.Builder()
.addRule(new AncestryValidationRule())
.addRule(new GasUsageValidationRule())
.addRule(
new GasLimitRangeAndDeltaValidationRule(
DEFAULT_MIN_GAS_LIMIT, DEFAULT_MAX_GAS_LIMIT, baseFeeMarket))
.addRule(new TimestampBoundedByFutureParameter(1))
.addRule(
new ConstantFieldValidationRule<>(
"MixHash", BlockHeader::getMixHash, BftHelpers.EXPECTED_MIX_HASH))
.addRule(
new ConstantFieldValidationRule<>(
"Difficulty", BlockHeader::getDifficulty, UInt256.ONE))
.addRule(new QbftValidatorsValidationRule(useValidatorContract))
.addRule(new BftCoinbaseValidationRule())
.addRule(new BftCommitSealsValidationRule());
// Currently the minimum acceptable time between blocks is 1 second. The timestamp of an
// Ethereum header is stored as seconds since Unix epoch so blocks being produced more
// frequently than once a second cannot pass this validator. For non-production scenarios
// (e.g. for testing block production much more frequently than once a second) Besu has
// an experimental 'xblockperiodmilliseconds' option for BFT chains. If this is enabled
// we cannot apply the TimestampMoreRecentThanParent validation rule so we do not add it
if (minimumTimeBetweenBlocks.compareTo(Duration.ofSeconds(1)) >= 0) {
ruleBuilder.addRule(new TimestampMoreRecentThanParent(minimumTimeBetweenBlocks.getSeconds()));
}
return ruleBuilder;
}
}

@ -49,6 +49,7 @@ public class QbftForksSchedulesFactory {
new MutableQbftConfigOptions(lastSpec.getValue());
fork.getBlockPeriodSeconds().ifPresent(bftConfigOptions::setBlockPeriodSeconds);
fork.getBlockPeriodMilliseconds().ifPresent(bftConfigOptions::setBlockPeriodMilliseconds);
fork.getBlockRewardWei().ifPresent(bftConfigOptions::setBlockRewardWei);
if (fork.isMiningBeneficiaryConfigured()) {

@ -33,6 +33,7 @@ import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.time.Duration;
import java.util.Optional;
/** Defines the protocol behaviours for a blockchain using a QBFT consensus mechanism. */
@ -164,7 +165,9 @@ public class QbftProtocolScheduleBuilder extends BaseBftProtocolScheduleBuilder
Optional.of(feeMarket).filter(FeeMarket::implementsBaseFee).map(BaseFeeMarket.class::cast);
return QbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
qbftConfigOptions.getBlockPeriodSeconds(),
qbftConfigOptions.getBlockPeriodMilliseconds() > 0
? Duration.ofMillis(qbftConfigOptions.getBlockPeriodMilliseconds())
: Duration.ofSeconds(qbftConfigOptions.getBlockPeriodSeconds()),
qbftConfigOptions.isValidatorContractMode(),
baseFeeMarket);
}

@ -34,6 +34,7 @@ import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
@ -92,7 +93,7 @@ public class QbftBlockHeaderValidationRulesetFactoryTest {
final BlockHeaderValidator validator =
QbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
5, false, Optional.of(FeeMarket.london(1)))
Duration.ofSeconds(5), false, Optional.of(FeeMarket.london(1)))
.build();
assertThat(
@ -366,7 +367,8 @@ public class QbftBlockHeaderValidationRulesetFactoryTest {
}
public BlockHeaderValidator getBlockHeaderValidator() {
return QbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(5, false, Optional.empty())
return QbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
Duration.ofSeconds(5), false, Optional.empty())
.build();
}
}

Loading…
Cancel
Save