mirror of
https://github.com/vacp2p/status-linea-besu.git
synced 2026-01-08 23:08:15 -05:00
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>
This commit is contained in:
@@ -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;
|
||||
|
||||
// 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;
|
||||
// Experimental option for test scenarios only. Not for production use.
|
||||
final long blockPeriodMilliseconds =
|
||||
forksSchedule.getFork(round.getSequenceNumber()).getValue().getBlockPeriodMilliseconds();
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user