Time limited block creation (#6044)

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>
pull/6148/head
Fabio Di Fabio 1 year ago committed by GitHub
parent 636ad8a65a
commit 8319fae725
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      CHANGELOG.md
  2. 31
      besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
  3. 70
      besu/src/main/java/org/hyperledger/besu/cli/options/MiningOptions.java
  4. 3
      besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java
  5. 3
      besu/src/main/java/org/hyperledger/besu/controller/IbftBesuControllerBuilder.java
  6. 3
      besu/src/main/java/org/hyperledger/besu/controller/MainnetBesuControllerBuilder.java
  7. 18
      besu/src/main/java/org/hyperledger/besu/controller/MergeBesuControllerBuilder.java
  8. 3
      besu/src/main/java/org/hyperledger/besu/controller/QbftBesuControllerBuilder.java
  9. 2
      besu/src/main/java/org/hyperledger/besu/controller/TransitionBesuControllerBuilder.java
  10. 76
      besu/src/test/java/org/hyperledger/besu/cli/options/MiningOptionsTest.java
  11. 9
      besu/src/test/java/org/hyperledger/besu/controller/TransitionControllerBuilderTest.java
  12. 8
      consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreator.java
  13. 16
      consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutor.java
  14. 13
      consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreatorTest.java
  15. 12
      consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java
  16. 8
      consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/blockcreation/BftBlockCreator.java
  17. 11
      consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/blockcreation/BftBlockCreatorFactory.java
  18. 7
      consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/support/TestContextBuilder.java
  19. 4
      consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/BftBlockCreatorTest.java
  20. 7
      consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeBlockCreator.java
  21. 34
      consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeCoordinator.java
  22. 23
      consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeCoordinatorTest.java
  23. 7
      consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeReorgTest.java
  24. 7
      consensus/qbft/src/integration-test/java/org/hyperledger/besu/consensus/qbft/support/TestContextBuilder.java
  25. 8
      consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/blockcreation/QbftBlockCreatorFactory.java
  26. 4
      consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/blockcreation/QbftBlockCreatorFactoryTest.java
  27. 10
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java
  28. 9
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractMinerExecutor.java
  29. 7
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/PoWBlockCreator.java
  30. 15
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/PoWMinerExecutor.java
  31. 134
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java
  32. 16
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/TransactionSelectionResults.java
  33. 12
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreatorTest.java
  34. 426
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java
  35. 3
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LegacyFeeMarketBlockTransactionSelectorTest.java
  36. 54
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LondonFeeMarketBlockTransactionSelectorTest.java
  37. 12
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWBlockCreatorTest.java
  38. 9
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWMinerExecutorTest.java
  39. 29
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/MiningParameters.java
  40. 18
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/AbstractIsolationTests.java
  41. 13
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthScheduler.java
  42. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTest.java
  43. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTestUtil.java
  44. 5
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthSchedulerShutdownTest.java
  45. 2
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthSchedulerTest.java
  46. 2
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/ethtaskutils/AbstractMessageTaskTest.java
  47. 2
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/peervalidation/AbstractPeerBlockValidatorTest.java
  48. 2
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/BackwardSyncStepTest.java
  49. 2
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/worldstate/FastWorldStateDownloaderTest.java
  50. 2
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/DynamicPivotBlockManagerTest.java
  51. 8
      ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java
  52. 3
      ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/methods/TestMineBlocks.java
  53. 2
      plugin-api/build.gradle
  54. 6
      plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionSelectionResult.java
  55. 1
      testutil/build.gradle
  56. 65
      testutil/src/main/java/org/hyperledger/besu/testutil/DeterministicEthScheduler.java
  57. 5
      testutil/src/main/java/org/hyperledger/besu/testutil/MockScheduledExecutor.java

@ -18,6 +18,7 @@
- Upgrade EVM Reference Tests to v13 (Cancun) [#6114](https://github.com/hyperledger/besu/pull/6114) - Upgrade EVM Reference Tests to v13 (Cancun) [#6114](https://github.com/hyperledger/besu/pull/6114)
- Add `yParity` to GraphQL and JSON-RPC for relevant querise. [6119](https://github.com/hyperledger/besu/pull/6119) - Add `yParity` to GraphQL and JSON-RPC for relevant querise. [6119](https://github.com/hyperledger/besu/pull/6119)
- Force tx replacement price bump to zero when zero base fee market is configured or `--min-gas-price` is set to 0. This allows for easier tx replacement in networks where there is not gas price. [#6079](https://github.com/hyperledger/besu/pull/6079) - Force tx replacement price bump to zero when zero base fee market is configured or `--min-gas-price` is set to 0. This allows for easier tx replacement in networks where there is not gas price. [#6079](https://github.com/hyperledger/besu/pull/6079)
- Introduce the possibility to limit the time spent selecting pending transactions during block creation, using the new experimental option `Xblock-txs-selection-max-time` on PoS and PoW networks (by default set to 5000ms) or `Xpoa-block-txs-selection-max-time` on PoA networks (by default 75% of the min block time) [#6044](https://github.com/hyperledger/besu/pull/6044)
### Bug fixes ### Bug fixes
- Upgrade netty to address CVE-2023-44487, CVE-2023-34462 [#6100](https://github.com/hyperledger/besu/pull/6100) - Upgrade netty to address CVE-2023-44487, CVE-2023-34462 [#6100](https://github.com/hyperledger/besu/pull/6100)
@ -33,8 +34,8 @@
### Additions and Improvements ### Additions and Improvements
- New option `--tx-pool-priority-senders` to specify a list of senders, that has the effect to prioritize any transactions sent by these senders from any source [#5959](https://github.com/hyperledger/besu/pull/5959) - New option `--tx-pool-priority-senders` to specify a list of senders, that has the effect to prioritize any transactions sent by these senders from any source [#5959](https://github.com/hyperledger/besu/pull/5959)
- Cache last n blocks by using a new Besu flag `--cache-last-blocks=n` [#6009](https://github.com/hyperledger/besu/pull/6009) - Cache last n blocks by using a new Besu flag `--cache-last-blocks=n` [#6009](https://github.com/hyperledger/besu/pull/6009)
- Optimize performances of RPC method `eth_feeHistory` [#6011](https://github.com/hyperledger/besu/pull/6011) [#6035](https://github.com/hyperledger/besu/pull/6035) - Optimize performances of RPC method `eth_feeHistory` [#6011](https://github.com/hyperledger/besu/pull/6011) [#6035](https://github.com/hyperledger/besu/pull/6035)
- Logging summary of plugins at Info as part of the config overview [#5964](https://github.com/hyperledger/besu/pull/5964) [#6049](https://github.com/hyperledger/besu/pull/6049) - Logging summary of plugins at Info as part of the config overview [#5964](https://github.com/hyperledger/besu/pull/5964) [#6049](https://github.com/hyperledger/besu/pull/6049)
- Layered tx pool memory improvements [#5985](https://github.com/hyperledger/besu/pull/5985) [#5974](https://github.com/hyperledger/besu/pull/5974) - Layered tx pool memory improvements [#5985](https://github.com/hyperledger/besu/pull/5985) [#5974](https://github.com/hyperledger/besu/pull/5974)
- Update Bouncy Castle to 1.76, and force the use of the `jdk18on` variant [#5748](https://github.com/hyperledger/besu/pull/5748) - Update Bouncy Castle to 1.76, and force the use of the `jdk18on` variant [#5748](https://github.com/hyperledger/besu/pull/5748)
- Add GraphQL support for new fields in Cancun [#5923](https://github.com/hyperledger/besu/pull/5923) [#5975](https://github.com/hyperledger/besu/pull/5975) - Add GraphQL support for new fields in Cancun [#5923](https://github.com/hyperledger/besu/pull/5923) [#5975](https://github.com/hyperledger/besu/pull/5975)

@ -122,6 +122,7 @@ import org.hyperledger.besu.ethereum.api.tls.FileBasedPasswordProvider;
import org.hyperledger.besu.ethereum.api.tls.TlsClientAuthConfiguration; import org.hyperledger.besu.ethereum.api.tls.TlsClientAuthConfiguration;
import org.hyperledger.besu.ethereum.api.tls.TlsConfiguration; import org.hyperledger.besu.ethereum.api.tls.TlsConfiguration;
import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters;
import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.eth.sync.SyncMode; import org.hyperledger.besu.ethereum.eth.sync.SyncMode;
@ -1806,8 +1807,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
} }
private void validateMiningParams() { private void validateMiningParams() {
miningOptions.validate( miningOptions.validate(commandLine, getActualGenesisConfigOptions(), isMergeEnabled(), logger);
commandLine, logger, isMergeEnabled(), getActualGenesisConfigOptions().isEthHash());
} }
/** /**
@ -2830,11 +2830,36 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
private MiningParameters getMiningParameters() { private MiningParameters getMiningParameters() {
if (miningParameters == null) { if (miningParameters == null) {
miningParameters = miningOptions.toDomainObject(); final var miningParametersBuilder =
ImmutableMiningParameters.builder().from(miningOptions.toDomainObject());
final var actualGenesisOptions = getActualGenesisConfigOptions();
if (actualGenesisOptions.isPoa()) {
miningParametersBuilder.unstable(
ImmutableMiningParameters.Unstable.builder()
.minBlockTime(getMinBlockTime(actualGenesisOptions))
.build());
}
miningParameters = miningParametersBuilder.build();
} }
return miningParameters; return miningParameters;
} }
private int getMinBlockTime(final GenesisConfigOptions genesisConfigOptions) {
if (genesisConfigOptions.isClique()) {
return genesisConfigOptions.getCliqueConfigOptions().getBlockPeriodSeconds();
}
if (genesisConfigOptions.isIbft2()) {
return genesisConfigOptions.getBftConfigOptions().getBlockPeriodSeconds();
}
if (genesisConfigOptions.isQbft()) {
return genesisConfigOptions.getQbftConfigOptions().getBlockPeriodSeconds();
}
throw new IllegalArgumentException("Should only be called for a PoA network");
}
private boolean isPruningEnabled() { private boolean isPruningEnabled() {
return pruningEnabled; return pruningEnabled;
} }

@ -15,23 +15,29 @@
package org.hyperledger.besu.cli.options; package org.hyperledger.besu.cli.options;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.hyperledger.besu.ethereum.core.MiningParameters.MutableInitValues.DEFAULT_EXTRA_DATA; import static org.hyperledger.besu.ethereum.core.MiningParameters.MutableInitValues.DEFAULT_EXTRA_DATA;
import static org.hyperledger.besu.ethereum.core.MiningParameters.MutableInitValues.DEFAULT_MIN_BLOCK_OCCUPANCY_RATIO; import static org.hyperledger.besu.ethereum.core.MiningParameters.MutableInitValues.DEFAULT_MIN_BLOCK_OCCUPANCY_RATIO;
import static org.hyperledger.besu.ethereum.core.MiningParameters.MutableInitValues.DEFAULT_MIN_PRIORITY_FEE_PER_GAS; import static org.hyperledger.besu.ethereum.core.MiningParameters.MutableInitValues.DEFAULT_MIN_PRIORITY_FEE_PER_GAS;
import static org.hyperledger.besu.ethereum.core.MiningParameters.MutableInitValues.DEFAULT_MIN_TRANSACTION_GAS_PRICE; import static org.hyperledger.besu.ethereum.core.MiningParameters.MutableInitValues.DEFAULT_MIN_TRANSACTION_GAS_PRICE;
import static org.hyperledger.besu.ethereum.core.MiningParameters.Unstable.DEFAULT_MAX_OMMERS_DEPTH; import static org.hyperledger.besu.ethereum.core.MiningParameters.Unstable.DEFAULT_MAX_OMMERS_DEPTH;
import static org.hyperledger.besu.ethereum.core.MiningParameters.Unstable.DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME;
import static org.hyperledger.besu.ethereum.core.MiningParameters.Unstable.DEFAULT_POA_BLOCK_TXS_SELECTION_MAX_TIME;
import static org.hyperledger.besu.ethereum.core.MiningParameters.Unstable.DEFAULT_POS_BLOCK_CREATION_MAX_TIME; import static org.hyperledger.besu.ethereum.core.MiningParameters.Unstable.DEFAULT_POS_BLOCK_CREATION_MAX_TIME;
import static org.hyperledger.besu.ethereum.core.MiningParameters.Unstable.DEFAULT_POS_BLOCK_CREATION_REPETITION_MIN_DURATION; import static org.hyperledger.besu.ethereum.core.MiningParameters.Unstable.DEFAULT_POS_BLOCK_CREATION_REPETITION_MIN_DURATION;
import static org.hyperledger.besu.ethereum.core.MiningParameters.Unstable.DEFAULT_POW_JOB_TTL; import static org.hyperledger.besu.ethereum.core.MiningParameters.Unstable.DEFAULT_POW_JOB_TTL;
import static org.hyperledger.besu.ethereum.core.MiningParameters.Unstable.DEFAULT_REMOTE_SEALERS_LIMIT; import static org.hyperledger.besu.ethereum.core.MiningParameters.Unstable.DEFAULT_REMOTE_SEALERS_LIMIT;
import static org.hyperledger.besu.ethereum.core.MiningParameters.Unstable.DEFAULT_REMOTE_SEALERS_TTL; import static org.hyperledger.besu.ethereum.core.MiningParameters.Unstable.DEFAULT_REMOTE_SEALERS_TTL;
import org.hyperledger.besu.cli.converter.PercentageConverter;
import org.hyperledger.besu.cli.util.CommandLineUtils; import org.hyperledger.besu.cli.util.CommandLineUtils;
import org.hyperledger.besu.config.GenesisConfigOptions;
import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters; import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters.MutableInitValues; import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters.MutableInitValues;
import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.util.number.Percentage;
import java.util.List; import java.util.List;
@ -162,6 +168,25 @@ public class MiningOptions implements CLIOptions<MiningParameters> {
+ " then it waits before next repetition. Must be positive and ≤ 2000 (default: ${DEFAULT-VALUE} milliseconds)") + " then it waits before next repetition. Must be positive and ≤ 2000 (default: ${DEFAULT-VALUE} milliseconds)")
private Long posBlockCreationRepetitionMinDuration = private Long posBlockCreationRepetitionMinDuration =
DEFAULT_POS_BLOCK_CREATION_REPETITION_MIN_DURATION; DEFAULT_POS_BLOCK_CREATION_REPETITION_MIN_DURATION;
@CommandLine.Option(
hidden = true,
names = {"--Xblock-txs-selection-max-time"},
description =
"Specifies the maximum time, in milliseconds, that could be spent selecting transactions to be included in the block."
+ " Not compatible with PoA networks, see Xpoa-block-txs-selection-max-time."
+ " Must be positive and ≤ (default: ${DEFAULT-VALUE})")
private Long nonPoaBlockTxsSelectionMaxTime = DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME;
@CommandLine.Option(
hidden = true,
names = {"--Xpoa-block-txs-selection-max-time"},
converter = PercentageConverter.class,
description =
"Specifies the maximum time that could be spent selecting transactions to be included in the block, as a percentage of the fixed block time of the PoA network."
+ " To be only used on PoA networks, for other networks see Xblock-txs-selection-max-time."
+ " (default: ${DEFAULT-VALUE})")
private Percentage poaBlockTxsSelectionMaxTime = DEFAULT_POA_BLOCK_TXS_SELECTION_MAX_TIME;
} }
private MiningOptions() {} private MiningOptions() {}
@ -180,15 +205,15 @@ public class MiningOptions implements CLIOptions<MiningParameters> {
* options are valid for the selected implementation. * options are valid for the selected implementation.
* *
* @param commandLine the full commandLine to check all the options specified by the user * @param commandLine the full commandLine to check all the options specified by the user
* @param logger the logger * @param genesisConfigOptions is EthHash?
* @param isMergeEnabled is the Merge enabled? * @param isMergeEnabled is the Merge enabled?
* @param isEthHash is EthHash? * @param logger the logger
*/ */
public void validate( public void validate(
final CommandLine commandLine, final CommandLine commandLine,
final Logger logger, final GenesisConfigOptions genesisConfigOptions,
final boolean isMergeEnabled, final boolean isMergeEnabled,
final boolean isEthHash) { final Logger logger) {
if (Boolean.TRUE.equals(isMiningEnabled) && coinbase == null) { if (Boolean.TRUE.equals(isMiningEnabled) && coinbase == null) {
throw new ParameterException( throw new ParameterException(
commandLine, commandLine,
@ -203,7 +228,7 @@ public class MiningOptions implements CLIOptions<MiningParameters> {
} }
// Check that block producer options work // Check that block producer options work
if (!isMergeEnabled && isEthHash) { if (!isMergeEnabled && genesisConfigOptions.isEthHash()) {
CommandLineUtils.checkOptionDependencies( CommandLineUtils.checkOptionDependencies(
logger, logger,
commandLine, commandLine,
@ -231,7 +256,9 @@ public class MiningOptions implements CLIOptions<MiningParameters> {
if (unstableOptions.posBlockCreationMaxTime <= 0 if (unstableOptions.posBlockCreationMaxTime <= 0
|| unstableOptions.posBlockCreationMaxTime > DEFAULT_POS_BLOCK_CREATION_MAX_TIME) { || unstableOptions.posBlockCreationMaxTime > DEFAULT_POS_BLOCK_CREATION_MAX_TIME) {
throw new ParameterException( throw new ParameterException(
commandLine, "--Xpos-block-creation-max-time must be positive and ≤ 12000"); commandLine,
"--Xpos-block-creation-max-time must be positive and ≤ "
+ DEFAULT_POS_BLOCK_CREATION_MAX_TIME);
} }
if (unstableOptions.posBlockCreationRepetitionMinDuration <= 0 if (unstableOptions.posBlockCreationRepetitionMinDuration <= 0
@ -239,6 +266,31 @@ public class MiningOptions implements CLIOptions<MiningParameters> {
throw new ParameterException( throw new ParameterException(
commandLine, "--Xpos-block-creation-repetition-min-duration must be positive and ≤ 2000"); commandLine, "--Xpos-block-creation-repetition-min-duration must be positive and ≤ 2000");
} }
if (genesisConfigOptions.isPoa()) {
CommandLineUtils.failIfOptionDoesntMeetRequirement(
commandLine,
"--Xblock-txs-selection-max-time can't be used with PoA networks,"
+ " see Xpoa-block-txs-selection-max-time instead",
false,
singletonList("--Xblock-txs-selection-max-time"));
} else {
CommandLineUtils.failIfOptionDoesntMeetRequirement(
commandLine,
"--Xpoa-block-txs-selection-max-time can be only used with PoA networks,"
+ " see --Xblock-txs-selection-max-time instead",
false,
singletonList("--Xpoa-block-txs-selection-max-time"));
if (unstableOptions.nonPoaBlockTxsSelectionMaxTime <= 0
|| unstableOptions.nonPoaBlockTxsSelectionMaxTime
> DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME) {
throw new ParameterException(
commandLine,
"--Xblock-txs-selection-max-time must be positive and ≤ "
+ DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME);
}
}
} }
static MiningOptions fromConfig(final MiningParameters miningParameters) { static MiningOptions fromConfig(final MiningParameters miningParameters) {
@ -265,6 +317,10 @@ public class MiningOptions implements CLIOptions<MiningParameters> {
miningParameters.getUnstable().getPosBlockCreationMaxTime(); miningParameters.getUnstable().getPosBlockCreationMaxTime();
miningOptions.unstableOptions.posBlockCreationRepetitionMinDuration = miningOptions.unstableOptions.posBlockCreationRepetitionMinDuration =
miningParameters.getUnstable().getPosBlockCreationRepetitionMinDuration(); miningParameters.getUnstable().getPosBlockCreationRepetitionMinDuration();
miningOptions.unstableOptions.nonPoaBlockTxsSelectionMaxTime =
miningParameters.getUnstable().getBlockTxsSelectionMaxTime();
miningOptions.unstableOptions.poaBlockTxsSelectionMaxTime =
miningParameters.getUnstable().getPoaBlockTxsSelectionMaxTime();
miningParameters.getCoinbase().ifPresent(coinbase -> miningOptions.coinbase = coinbase); miningParameters.getCoinbase().ifPresent(coinbase -> miningOptions.coinbase = coinbase);
miningParameters.getTargetGasLimit().ifPresent(tgl -> miningOptions.targetGasLimit = tgl); miningParameters.getTargetGasLimit().ifPresent(tgl -> miningOptions.targetGasLimit = tgl);
@ -304,6 +360,8 @@ public class MiningOptions implements CLIOptions<MiningParameters> {
.posBlockCreationMaxTime(unstableOptions.posBlockCreationMaxTime) .posBlockCreationMaxTime(unstableOptions.posBlockCreationMaxTime)
.posBlockCreationRepetitionMinDuration( .posBlockCreationRepetitionMinDuration(
unstableOptions.posBlockCreationRepetitionMinDuration) unstableOptions.posBlockCreationRepetitionMinDuration)
.nonPoaBlockTxsSelectionMaxTime(unstableOptions.nonPoaBlockTxsSelectionMaxTime)
.poaBlockTxsSelectionMaxTime(unstableOptions.poaBlockTxsSelectionMaxTime)
.build()); .build());
return miningParametersBuilder.build(); return miningParametersBuilder.build();

@ -94,7 +94,8 @@ public class CliqueBesuControllerBuilder extends BesuControllerBuilder {
localAddress, localAddress,
secondsBetweenBlocks), secondsBetweenBlocks),
epochManager, epochManager,
createEmptyBlocks); createEmptyBlocks,
ethProtocolManager.ethContext().getScheduler());
final CliqueMiningCoordinator miningCoordinator = final CliqueMiningCoordinator miningCoordinator =
new CliqueMiningCoordinator( new CliqueMiningCoordinator(
protocolContext.getBlockchain(), protocolContext.getBlockchain(),

@ -153,7 +153,8 @@ public class IbftBesuControllerBuilder extends BftBesuControllerBuilder {
forksSchedule, forksSchedule,
miningParameters, miningParameters,
localAddress, localAddress,
bftExtraDataCodec().get()); bftExtraDataCodec().get(),
ethProtocolManager.ethContext().getScheduler());
final ValidatorProvider validatorProvider = final ValidatorProvider validatorProvider =
protocolContext.getConsensusContext(BftContext.class).getValidatorProvider(); protocolContext.getConsensusContext(BftContext.class).getValidatorProvider();

@ -55,7 +55,8 @@ public class MainnetBesuControllerBuilder extends BesuControllerBuilder {
MainnetBlockHeaderValidator.MINIMUM_SECONDS_SINCE_PARENT, MainnetBlockHeaderValidator.MINIMUM_SECONDS_SINCE_PARENT,
MainnetBlockHeaderValidator.TIMESTAMP_TOLERANCE_S, MainnetBlockHeaderValidator.TIMESTAMP_TOLERANCE_S,
clock), clock),
epochCalculator); epochCalculator,
ethProtocolManager.ethContext().getScheduler());
final PoWMiningCoordinator miningCoordinator = final PoWMiningCoordinator miningCoordinator =
new PoWMiningCoordinator( new PoWMiningCoordinator(

@ -35,7 +35,6 @@ import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.manager.MergePeerFilter; import org.hyperledger.besu.ethereum.eth.manager.MergePeerFilter;
import org.hyperledger.besu.ethereum.eth.manager.MonitoredExecutors;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
import org.hyperledger.besu.ethereum.eth.peervalidation.RequiredBlocksPeerValidator; import org.hyperledger.besu.ethereum.eth.peervalidation.RequiredBlocksPeerValidator;
import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration;
@ -46,13 +45,10 @@ import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions; import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalLong; import java.util.OptionalLong;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -85,7 +81,7 @@ public class MergeBesuControllerBuilder extends BesuControllerBuilder {
syncState, syncState,
BackwardChain.from( BackwardChain.from(
storageProvider, ScheduleBasedBlockHeaderFunctions.create(protocolSchedule))), storageProvider, ScheduleBasedBlockHeaderFunctions.create(protocolSchedule))),
metricsSystem); ethProtocolManager.ethContext().getScheduler());
} }
@Override @Override
@ -146,7 +142,7 @@ public class MergeBesuControllerBuilder extends BesuControllerBuilder {
* @param miningParameters the mining parameters * @param miningParameters the mining parameters
* @param syncState the sync state * @param syncState the sync state
* @param backwardSyncContext the backward sync context * @param backwardSyncContext the backward sync context
* @param metricsSystem the metrics system * @param ethScheduler the scheduler
* @return the mining coordinator * @return the mining coordinator
*/ */
protected MiningCoordinator createTransitionMiningCoordinator( protected MiningCoordinator createTransitionMiningCoordinator(
@ -156,13 +152,10 @@ public class MergeBesuControllerBuilder extends BesuControllerBuilder {
final MiningParameters miningParameters, final MiningParameters miningParameters,
final SyncState syncState, final SyncState syncState,
final BackwardSyncContext backwardSyncContext, final BackwardSyncContext backwardSyncContext,
final MetricsSystem metricsSystem) { final EthScheduler ethScheduler) {
this.syncState.set(syncState); this.syncState.set(syncState);
final ExecutorService blockBuilderExecutor =
MonitoredExecutors.newSingleThreadExecutor("PoS-Block-Builder", metricsSystem);
final GenesisConfigOptions genesisConfigOptions = configOptionsSupplier.get(); final GenesisConfigOptions genesisConfigOptions = configOptionsSupplier.get();
final Optional<Address> depositContractAddress = final Optional<Address> depositContractAddress =
genesisConfigOptions.getDepositContractAddress(); genesisConfigOptions.getDepositContractAddress();
@ -170,10 +163,7 @@ public class MergeBesuControllerBuilder extends BesuControllerBuilder {
return new MergeCoordinator( return new MergeCoordinator(
protocolContext, protocolContext,
protocolSchedule, protocolSchedule,
task -> { ethScheduler,
LOG.debug("Block builder executor status {}", blockBuilderExecutor);
return CompletableFuture.runAsync(task, blockBuilderExecutor);
},
transactionPool, transactionPool,
miningParameters, miningParameters,
backwardSyncContext, backwardSyncContext,

@ -191,7 +191,8 @@ public class QbftBesuControllerBuilder extends BftBesuControllerBuilder {
qbftForksSchedule, qbftForksSchedule,
miningParameters, miningParameters,
localAddress, localAddress,
bftExtraDataCodec().get()); bftExtraDataCodec().get(),
ethProtocolManager.ethContext().getScheduler());
final ValidatorProvider validatorProvider = final ValidatorProvider validatorProvider =
protocolContext.getConsensusContext(BftContext.class).getValidatorProvider(); protocolContext.getConsensusContext(BftContext.class).getValidatorProvider();

@ -144,7 +144,7 @@ public class TransitionBesuControllerBuilder extends BesuControllerBuilder {
transitionMiningParameters, transitionMiningParameters,
syncState, syncState,
transitionBackwardsSyncContext, transitionBackwardsSyncContext,
metricsSystem)); ethProtocolManager.ethContext().getScheduler()));
initTransitionWatcher(protocolContext, composedCoordinator); initTransitionWatcher(protocolContext, composedCoordinator);
return composedCoordinator; return composedCoordinator;
} }

@ -15,6 +15,8 @@
package org.hyperledger.besu.cli.options; package org.hyperledger.besu.cli.options;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.core.MiningParameters.Unstable.DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME;
import static org.hyperledger.besu.ethereum.core.MiningParameters.Unstable.DEFAULT_POA_BLOCK_TXS_SELECTION_MAX_TIME;
import static org.hyperledger.besu.ethereum.core.MiningParameters.Unstable.DEFAULT_POS_BLOCK_CREATION_MAX_TIME; import static org.hyperledger.besu.ethereum.core.MiningParameters.Unstable.DEFAULT_POS_BLOCK_CREATION_MAX_TIME;
import static org.mockito.Mockito.atMost; import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@ -26,6 +28,7 @@ import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters.MutableInitValues; import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters.MutableInitValues;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters.Unstable; import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters.Unstable;
import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.util.number.Percentage;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
@ -308,6 +311,79 @@ public class MiningOptionsTest extends AbstractCLIOptionsTest<MiningParameters,
"17000"); "17000");
} }
@Test
public void blockTxsSelectionMaxTimeDefaultValue() {
internalTestSuccess(
miningParams ->
assertThat(miningParams.getUnstable().getBlockTxsSelectionMaxTime())
.isEqualTo(DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME));
}
@Test
public void blockTxsSelectionMaxTimeOption() {
internalTestSuccess(
miningParams ->
assertThat(miningParams.getUnstable().getBlockTxsSelectionMaxTime()).isEqualTo(1700L),
"--Xblock-txs-selection-max-time",
"1700");
}
@Test
public void blockTxsSelectionMaxTimeOutOfAllowedRange() {
internalTestFailure(
"--Xblock-txs-selection-max-time must be positive and ≤ 5000",
"--Xblock-txs-selection-max-time",
"6000");
}
@Test
public void blockTxsSelectionMaxTimeIncompatibleWithPoaNetworks() throws IOException {
final Path genesisFileIBFT2 = createFakeGenesisFile(VALID_GENESIS_IBFT2_POST_LONDON);
internalTestFailure(
"--Xblock-txs-selection-max-time can't be used with PoA networks, see Xpoa-block-txs-selection-max-time instead",
"--genesis-file",
genesisFileIBFT2.toString(),
"--Xblock-txs-selection-max-time",
"2");
}
@Test
public void poaBlockTxsSelectionMaxTimeDefaultValue() {
internalTestSuccess(
miningParams ->
assertThat(miningParams.getUnstable().getPoaBlockTxsSelectionMaxTime())
.isEqualTo(DEFAULT_POA_BLOCK_TXS_SELECTION_MAX_TIME));
}
@Test
public void poaBlockTxsSelectionMaxTimeOption() throws IOException {
final Path genesisFileIBFT2 = createFakeGenesisFile(VALID_GENESIS_IBFT2_POST_LONDON);
internalTestSuccess(
miningParams ->
assertThat(miningParams.getUnstable().getPoaBlockTxsSelectionMaxTime())
.isEqualTo(Percentage.fromInt(80)),
"--genesis-file",
genesisFileIBFT2.toString(),
"--Xpoa-block-txs-selection-max-time",
"80");
}
@Test
public void poaBlockTxsSelectionMaxTimeOutOfAllowedRange() {
internalTestFailure(
"Invalid value for option '--Xpoa-block-txs-selection-max-time': cannot convert '110' to Percentage",
"--Xpoa-block-txs-selection-max-time",
"110");
}
@Test
public void poaBlockTxsSelectionMaxTimeOnlyCompatibleWithPoaNetworks() {
internalTestFailure(
"--Xpoa-block-txs-selection-max-time can be only used with PoA networks, see --Xblock-txs-selection-max-time instead",
"--Xpoa-block-txs-selection-max-time",
"90");
}
@Override @Override
protected MiningParameters createDefaultDomainObject() { protected MiningParameters createDefaultDomainObject() {
return MiningParameters.newDefault(); return MiningParameters.newDefault();

@ -49,12 +49,14 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.storage.StorageProvider; import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import java.util.Optional; import java.util.Optional;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
@ -71,7 +73,10 @@ public class TransitionControllerBuilderTest {
@Mock MutableBlockchain mockBlockchain; @Mock MutableBlockchain mockBlockchain;
@Mock TransactionPool transactionPool; @Mock TransactionPool transactionPool;
@Mock SyncState syncState; @Mock SyncState syncState;
@Mock EthProtocolManager ethProtocolManager;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
EthProtocolManager ethProtocolManager;
@Mock PostMergeContext mergeContext; @Mock PostMergeContext mergeContext;
StorageProvider storageProvider = new InMemoryKeyValueStorageProvider(); StorageProvider storageProvider = new InMemoryKeyValueStorageProvider();
@ -101,6 +106,8 @@ public class TransitionControllerBuilderTest {
.thenReturn(mock(CliqueContext.class)); .thenReturn(mock(CliqueContext.class));
when(protocolContext.getConsensusContext(PostMergeContext.class)).thenReturn(mergeContext); when(protocolContext.getConsensusContext(PostMergeContext.class)).thenReturn(mergeContext);
when(protocolContext.getConsensusContext(MergeContext.class)).thenReturn(mergeContext); when(protocolContext.getConsensusContext(MergeContext.class)).thenReturn(mergeContext);
when(ethProtocolManager.ethContext().getScheduler())
.thenReturn(new DeterministicEthScheduler());
miningParameters = MiningParameters.newDefault(); miningParameters = MiningParameters.newDefault();
} }

@ -32,6 +32,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions;
import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.SealableBlockHeader; import org.hyperledger.besu.ethereum.core.SealableBlockHeader;
import org.hyperledger.besu.ethereum.core.Util; import org.hyperledger.besu.ethereum.core.Util;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions; import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions;
@ -55,6 +56,7 @@ public class CliqueBlockCreator extends AbstractBlockCreator {
* @param nodeKey the node key * @param nodeKey the node key
* @param parentHeader the parent header * @param parentHeader the parent header
* @param epochManager the epoch manager * @param epochManager the epoch manager
* @param ethScheduler the scheduler for asynchronous block creation tasks
*/ */
public CliqueBlockCreator( public CliqueBlockCreator(
final MiningParameters miningParameters, final MiningParameters miningParameters,
@ -64,7 +66,8 @@ public class CliqueBlockCreator extends AbstractBlockCreator {
final ProtocolSchedule protocolSchedule, final ProtocolSchedule protocolSchedule,
final NodeKey nodeKey, final NodeKey nodeKey,
final BlockHeader parentHeader, final BlockHeader parentHeader,
final EpochManager epochManager) { final EpochManager epochManager,
final EthScheduler ethScheduler) {
super( super(
miningParameters, miningParameters,
__ -> Util.publicKeyToAddress(nodeKey.getPublicKey()), __ -> Util.publicKeyToAddress(nodeKey.getPublicKey()),
@ -73,7 +76,8 @@ public class CliqueBlockCreator extends AbstractBlockCreator {
protocolContext, protocolContext,
protocolSchedule, protocolSchedule,
parentHeader, parentHeader,
Optional.empty()); Optional.empty(),
ethScheduler);
this.nodeKey = nodeKey; this.nodeKey = nodeKey;
this.epochManager = epochManager; this.epochManager = epochManager;
} }

@ -28,6 +28,7 @@ import org.hyperledger.besu.ethereum.chain.PoWObserver;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.Util; import org.hyperledger.besu.ethereum.core.Util;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.util.Subscribers; import org.hyperledger.besu.util.Subscribers;
@ -60,6 +61,7 @@ public class CliqueMinerExecutor extends AbstractMinerExecutor<CliqueBlockMiner>
* @param blockScheduler the block scheduler * @param blockScheduler the block scheduler
* @param epochManager the epoch manager * @param epochManager the epoch manager
* @param createEmptyBlocks whether clique should allow the creation of empty blocks. * @param createEmptyBlocks whether clique should allow the creation of empty blocks.
* @param ethScheduler the scheduler for asynchronous block creation tasks
*/ */
public CliqueMinerExecutor( public CliqueMinerExecutor(
final ProtocolContext protocolContext, final ProtocolContext protocolContext,
@ -69,8 +71,15 @@ public class CliqueMinerExecutor extends AbstractMinerExecutor<CliqueBlockMiner>
final MiningParameters miningParams, final MiningParameters miningParams,
final AbstractBlockScheduler blockScheduler, final AbstractBlockScheduler blockScheduler,
final EpochManager epochManager, final EpochManager epochManager,
final boolean createEmptyBlocks) { final boolean createEmptyBlocks,
super(protocolContext, protocolSchedule, transactionPool, miningParams, blockScheduler); final EthScheduler ethScheduler) {
super(
protocolContext,
protocolSchedule,
transactionPool,
miningParams,
blockScheduler,
ethScheduler);
this.nodeKey = nodeKey; this.nodeKey = nodeKey;
this.localAddress = Util.publicKeyToAddress(nodeKey.getPublicKey()); this.localAddress = Util.publicKeyToAddress(nodeKey.getPublicKey());
this.epochManager = epochManager; this.epochManager = epochManager;
@ -93,7 +102,8 @@ public class CliqueMinerExecutor extends AbstractMinerExecutor<CliqueBlockMiner>
protocolSchedule, protocolSchedule,
nodeKey, nodeKey,
header, header,
epochManager); epochManager,
ethScheduler);
return new CliqueBlockMiner( return new CliqueBlockMiner(
blockCreator, blockCreator,

@ -52,6 +52,7 @@ import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters.MutableInitV
import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.Util; import org.hyperledger.besu.ethereum.core.Util;
import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionBroadcaster; import org.hyperledger.besu.ethereum.eth.transactions.TransactionBroadcaster;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
@ -63,6 +64,7 @@ import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.testutil.TestClock; import org.hyperledger.besu.testutil.TestClock;
import java.time.ZoneId; import java.time.ZoneId;
@ -83,7 +85,7 @@ public class CliqueBlockCreatorTest {
private final List<Address> validatorList = Lists.newArrayList(); private final List<Address> validatorList = Lists.newArrayList();
private final MetricsSystem metricsSystem = new NoOpMetricsSystem(); private final MetricsSystem metricsSystem = new NoOpMetricsSystem();
private final CliqueBlockInterface blockInterface = new CliqueBlockInterface(); private final CliqueBlockInterface blockInterface = new CliqueBlockInterface();
private final EthScheduler ethScheduler = new DeterministicEthScheduler();
private ProtocolSchedule protocolSchedule; private ProtocolSchedule protocolSchedule;
private final WorldStateArchive stateArchive = createInMemoryWorldStateArchive(); private final WorldStateArchive stateArchive = createInMemoryWorldStateArchive();
@ -148,7 +150,8 @@ public class CliqueBlockCreatorTest {
protocolSchedule, protocolSchedule,
proposerNodeKey, proposerNodeKey,
blockchain.getChainHeadHeader(), blockchain.getChainHeadHeader(),
epochManager); epochManager,
ethScheduler);
final Block createdBlock = blockCreator.createBlock(5L).getBlock(); final Block createdBlock = blockCreator.createBlock(5L).getBlock();
@ -176,7 +179,8 @@ public class CliqueBlockCreatorTest {
protocolSchedule, protocolSchedule,
proposerNodeKey, proposerNodeKey,
blockchain.getChainHeadHeader(), blockchain.getChainHeadHeader(),
epochManager); epochManager,
ethScheduler);
final Block createdBlock = blockCreator.createBlock(0L).getBlock(); final Block createdBlock = blockCreator.createBlock(0L).getBlock();
assertThat(createdBlock.getHeader().getNonce()).isEqualTo(CliqueBlockInterface.ADD_NONCE); assertThat(createdBlock.getHeader().getNonce()).isEqualTo(CliqueBlockInterface.ADD_NONCE);
@ -209,7 +213,8 @@ public class CliqueBlockCreatorTest {
protocolSchedule, protocolSchedule,
proposerNodeKey, proposerNodeKey,
blockchain.getChainHeadHeader(), blockchain.getChainHeadHeader(),
epochManager); epochManager,
ethScheduler);
final Block createdBlock = blockCreator.createBlock(0L).getBlock(); final Block createdBlock = blockCreator.createBlock(0L).getBlock();
assertThat(createdBlock.getHeader().getNonce()).isEqualTo(CliqueBlockInterface.DROP_NONCE); assertThat(createdBlock.getHeader().getNonce()).isEqualTo(CliqueBlockInterface.DROP_NONCE);

@ -42,6 +42,7 @@ import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters.MutableInitV
import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.Util; import org.hyperledger.besu.ethereum.core.Util;
import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionBroadcaster; import org.hyperledger.besu.ethereum.eth.transactions.TransactionBroadcaster;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
@ -51,6 +52,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.testutil.TestClock; import org.hyperledger.besu.testutil.TestClock;
import java.time.ZoneId; import java.time.ZoneId;
@ -78,6 +80,7 @@ public class CliqueMinerExecutorTest {
private BlockHeaderTestFixture blockHeaderBuilder; private BlockHeaderTestFixture blockHeaderBuilder;
private final MetricsSystem metricsSystem = new NoOpMetricsSystem(); private final MetricsSystem metricsSystem = new NoOpMetricsSystem();
private final CliqueBlockInterface blockInterface = new CliqueBlockInterface(); private final CliqueBlockInterface blockInterface = new CliqueBlockInterface();
private final EthScheduler ethScheduler = new DeterministicEthScheduler();
@BeforeEach @BeforeEach
public void setup() { public void setup() {
@ -114,7 +117,8 @@ public class CliqueMinerExecutorTest {
miningParameters, miningParameters,
mock(CliqueBlockScheduler.class), mock(CliqueBlockScheduler.class),
new EpochManager(EPOCH_LENGTH), new EpochManager(EPOCH_LENGTH),
true); true,
ethScheduler);
// NOTE: Passing in the *parent* block, so must be 1 less than EPOCH // NOTE: Passing in the *parent* block, so must be 1 less than EPOCH
final BlockHeader header = blockHeaderBuilder.number(EPOCH_LENGTH - 1).buildHeader(); final BlockHeader header = blockHeaderBuilder.number(EPOCH_LENGTH - 1).buildHeader();
@ -149,7 +153,8 @@ public class CliqueMinerExecutorTest {
miningParameters, miningParameters,
mock(CliqueBlockScheduler.class), mock(CliqueBlockScheduler.class),
new EpochManager(EPOCH_LENGTH), new EpochManager(EPOCH_LENGTH),
true); true,
ethScheduler);
// Parent block was epoch, so the next block should contain no validators. // Parent block was epoch, so the next block should contain no validators.
final BlockHeader header = blockHeaderBuilder.number(EPOCH_LENGTH).buildHeader(); final BlockHeader header = blockHeaderBuilder.number(EPOCH_LENGTH).buildHeader();
@ -184,7 +189,8 @@ public class CliqueMinerExecutorTest {
miningParameters, miningParameters,
mock(CliqueBlockScheduler.class), mock(CliqueBlockScheduler.class),
new EpochManager(EPOCH_LENGTH), new EpochManager(EPOCH_LENGTH),
true); true,
ethScheduler);
executor.setExtraData(modifiedVanityData); executor.setExtraData(modifiedVanityData);
final Bytes extraDataBytes = executor.calculateExtraData(blockHeaderBuilder.buildHeader()); final Bytes extraDataBytes = executor.calculateExtraData(blockHeaderBuilder.buildHeader());

@ -26,6 +26,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder; import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder;
import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.SealableBlockHeader; import org.hyperledger.besu.ethereum.core.SealableBlockHeader;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
@ -50,6 +51,7 @@ public class BftBlockCreator extends AbstractBlockCreator {
* @param protocolSchedule the protocol schedule * @param protocolSchedule the protocol schedule
* @param parentHeader the parent header * @param parentHeader the parent header
* @param bftExtraDataCodec the bft extra data codec * @param bftExtraDataCodec the bft extra data codec
* @param ethScheduler the scheduler for asynchronous block creation tasks
*/ */
public BftBlockCreator( public BftBlockCreator(
final MiningParameters miningParameters, final MiningParameters miningParameters,
@ -60,7 +62,8 @@ public class BftBlockCreator extends AbstractBlockCreator {
final ProtocolContext protocolContext, final ProtocolContext protocolContext,
final ProtocolSchedule protocolSchedule, final ProtocolSchedule protocolSchedule,
final BlockHeader parentHeader, final BlockHeader parentHeader,
final BftExtraDataCodec bftExtraDataCodec) { final BftExtraDataCodec bftExtraDataCodec,
final EthScheduler ethScheduler) {
super( super(
miningParameters.setCoinbase(localAddress), miningParameters.setCoinbase(localAddress),
miningBeneficiaryCalculator(localAddress, forksSchedule), miningBeneficiaryCalculator(localAddress, forksSchedule),
@ -69,7 +72,8 @@ public class BftBlockCreator extends AbstractBlockCreator {
protocolContext, protocolContext,
protocolSchedule, protocolSchedule,
parentHeader, parentHeader,
Optional.empty()); Optional.empty(),
ethScheduler);
this.bftExtraDataCodec = bftExtraDataCodec; this.bftExtraDataCodec = bftExtraDataCodec;
} }

@ -32,6 +32,7 @@ import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.blockcreation.BlockCreator; import org.hyperledger.besu.ethereum.blockcreation.BlockCreator;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.AbstractGasLimitSpecification; import org.hyperledger.besu.ethereum.mainnet.AbstractGasLimitSpecification;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
@ -61,6 +62,8 @@ public class BftBlockCreatorFactory<T extends BftConfigOptions> {
protected final ProtocolSchedule protocolSchedule; protected final ProtocolSchedule protocolSchedule;
/** The Bft extra data codec. */ /** The Bft extra data codec. */
protected final BftExtraDataCodec bftExtraDataCodec; protected final BftExtraDataCodec bftExtraDataCodec;
/** The scheduler for asynchronous block creation tasks */
protected final EthScheduler ethScheduler;
private final Address localAddress; private final Address localAddress;
@ -74,6 +77,7 @@ public class BftBlockCreatorFactory<T extends BftConfigOptions> {
* @param miningParams the mining params * @param miningParams the mining params
* @param localAddress the local address * @param localAddress the local address
* @param bftExtraDataCodec the bft extra data codec * @param bftExtraDataCodec the bft extra data codec
* @param ethScheduler the scheduler for asynchronous block creation tasks
*/ */
public BftBlockCreatorFactory( public BftBlockCreatorFactory(
final TransactionPool transactionPool, final TransactionPool transactionPool,
@ -82,7 +86,8 @@ public class BftBlockCreatorFactory<T extends BftConfigOptions> {
final ForksSchedule<T> forksSchedule, final ForksSchedule<T> forksSchedule,
final MiningParameters miningParams, final MiningParameters miningParams,
final Address localAddress, final Address localAddress,
final BftExtraDataCodec bftExtraDataCodec) { final BftExtraDataCodec bftExtraDataCodec,
final EthScheduler ethScheduler) {
this.transactionPool = transactionPool; this.transactionPool = transactionPool;
this.protocolContext = protocolContext; this.protocolContext = protocolContext;
this.protocolSchedule = protocolSchedule; this.protocolSchedule = protocolSchedule;
@ -90,6 +95,7 @@ public class BftBlockCreatorFactory<T extends BftConfigOptions> {
this.localAddress = localAddress; this.localAddress = localAddress;
this.miningParameters = miningParams; this.miningParameters = miningParams;
this.bftExtraDataCodec = bftExtraDataCodec; this.bftExtraDataCodec = bftExtraDataCodec;
this.ethScheduler = ethScheduler;
} }
/** /**
@ -109,7 +115,8 @@ public class BftBlockCreatorFactory<T extends BftConfigOptions> {
protocolContext, protocolContext,
protocolSchedule, protocolSchedule,
parentHeader, parentHeader,
bftExtraDataCodec); bftExtraDataCodec,
ethScheduler);
} }
/** /**

@ -82,6 +82,7 @@ import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters.MutableInitV
import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.Util; import org.hyperledger.besu.ethereum.core.Util;
import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionBroadcaster; import org.hyperledger.besu.ethereum.eth.transactions.TransactionBroadcaster;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
@ -92,6 +93,7 @@ import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.testutil.TestClock; import org.hyperledger.besu.testutil.TestClock;
import org.hyperledger.besu.util.Subscribers; import org.hyperledger.besu.util.Subscribers;
@ -367,6 +369,8 @@ public class TestContextBuilder {
transactionPool.setEnabled(); transactionPool.setEnabled();
final EthScheduler ethScheduler = new DeterministicEthScheduler();
final Address localAddress = Util.publicKeyToAddress(nodeKey.getPublicKey()); final Address localAddress = Util.publicKeyToAddress(nodeKey.getPublicKey());
final BftBlockCreatorFactory<?> blockCreatorFactory = final BftBlockCreatorFactory<?> blockCreatorFactory =
new BftBlockCreatorFactory<>( new BftBlockCreatorFactory<>(
@ -376,7 +380,8 @@ public class TestContextBuilder {
forksSchedule, forksSchedule,
miningParams, miningParams,
localAddress, localAddress,
IBFT_EXTRA_DATA_ENCODER); IBFT_EXTRA_DATA_ENCODER,
ethScheduler);
final ProposerSelector proposerSelector = final ProposerSelector proposerSelector =
new ProposerSelector(blockChain, blockInterface, true, validatorProvider); new ProposerSelector(blockChain, blockInterface, true, validatorProvider);

@ -59,6 +59,7 @@ import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.testutil.TestClock; import org.hyperledger.besu.testutil.TestClock;
import java.time.ZoneId; import java.time.ZoneId;
@ -186,7 +187,8 @@ public class BftBlockCreatorTest {
protContext, protContext,
protocolSchedule, protocolSchedule,
parentHeader, parentHeader,
bftExtraDataEncoder); bftExtraDataEncoder,
new DeterministicEthScheduler());
final int secondsBetweenBlocks = 1; final int secondsBetweenBlocks = 1;
final Block block = blockCreator.createBlock(parentHeader.getTimestamp() + 1).getBlock(); final Block block = blockCreator.createBlock(parentHeader.getTimestamp() + 1).getBlock();

@ -24,6 +24,7 @@ import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.SealableBlockHeader; import org.hyperledger.besu.ethereum.core.SealableBlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.Withdrawal; import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
@ -53,7 +54,8 @@ class MergeBlockCreator extends AbstractBlockCreator {
final ProtocolContext protocolContext, final ProtocolContext protocolContext,
final ProtocolSchedule protocolSchedule, final ProtocolSchedule protocolSchedule,
final BlockHeader parentHeader, final BlockHeader parentHeader,
final Optional<Address> depositContractAddress) { final Optional<Address> depositContractAddress,
final EthScheduler ethScheduler) {
super( super(
miningParameters, miningParameters,
__ -> miningParameters.getCoinbase().orElseThrow(), __ -> miningParameters.getCoinbase().orElseThrow(),
@ -62,7 +64,8 @@ class MergeBlockCreator extends AbstractBlockCreator {
protocolContext, protocolContext,
protocolSchedule, protocolSchedule,
parentHeader, parentHeader,
depositContractAddress); depositContractAddress,
ethScheduler);
} }
/** /**

@ -36,6 +36,7 @@ import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.Withdrawal; import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.sync.backwardsync.BackwardSyncContext; import org.hyperledger.besu.ethereum.eth.sync.backwardsync.BackwardSyncContext;
import org.hyperledger.besu.ethereum.eth.sync.backwardsync.BadChainListener; import org.hyperledger.besu.ethereum.eth.sync.backwardsync.BadChainListener;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
@ -86,7 +87,7 @@ public class MergeCoordinator implements MergeMiningCoordinator, BadChainListene
/** The Protocol context. */ /** The Protocol context. */
protected final ProtocolContext protocolContext; protected final ProtocolContext protocolContext;
/** The Block builder executor. */ /** The Block builder executor. */
protected final ProposalBuilderExecutor blockBuilderExecutor; protected final EthScheduler ethScheduler;
/** The Backward sync context. */ /** The Backward sync context. */
protected final BackwardSyncContext backwardSyncContext; protected final BackwardSyncContext backwardSyncContext;
/** The Protocol schedule. */ /** The Protocol schedule. */
@ -100,7 +101,7 @@ public class MergeCoordinator implements MergeMiningCoordinator, BadChainListene
* *
* @param protocolContext the protocol context * @param protocolContext the protocol context
* @param protocolSchedule the protocol schedule * @param protocolSchedule the protocol schedule
* @param blockBuilderExecutor the block builder executor * @param ethScheduler the block builder executor
* @param transactionPool the pending transactions * @param transactionPool the pending transactions
* @param miningParams the mining params * @param miningParams the mining params
* @param backwardSyncContext the backward sync context * @param backwardSyncContext the backward sync context
@ -109,14 +110,14 @@ public class MergeCoordinator implements MergeMiningCoordinator, BadChainListene
public MergeCoordinator( public MergeCoordinator(
final ProtocolContext protocolContext, final ProtocolContext protocolContext,
final ProtocolSchedule protocolSchedule, final ProtocolSchedule protocolSchedule,
final ProposalBuilderExecutor blockBuilderExecutor, final EthScheduler ethScheduler,
final TransactionPool transactionPool, final TransactionPool transactionPool,
final MiningParameters miningParams, final MiningParameters miningParams,
final BackwardSyncContext backwardSyncContext, final BackwardSyncContext backwardSyncContext,
final Optional<Address> depositContractAddress) { final Optional<Address> depositContractAddress) {
this.protocolContext = protocolContext; this.protocolContext = protocolContext;
this.protocolSchedule = protocolSchedule; this.protocolSchedule = protocolSchedule;
this.blockBuilderExecutor = blockBuilderExecutor; this.ethScheduler = ethScheduler;
this.mergeContext = protocolContext.getConsensusContext(MergeContext.class); this.mergeContext = protocolContext.getConsensusContext(MergeContext.class);
this.backwardSyncContext = backwardSyncContext; this.backwardSyncContext = backwardSyncContext;
@ -140,7 +141,8 @@ public class MergeCoordinator implements MergeMiningCoordinator, BadChainListene
protocolContext, protocolContext,
protocolSchedule, protocolSchedule,
parentHeader, parentHeader,
depositContractAddress); depositContractAddress,
ethScheduler);
}; };
this.backwardSyncContext.subscribeBadChainListener(this); this.backwardSyncContext.subscribeBadChainListener(this);
@ -151,7 +153,7 @@ public class MergeCoordinator implements MergeMiningCoordinator, BadChainListene
* *
* @param protocolContext the protocol context * @param protocolContext the protocol context
* @param protocolSchedule the protocol schedule * @param protocolSchedule the protocol schedule
* @param blockBuilderExecutor the block builder executor * @param ethScheduler the block builder executor
* @param miningParams the mining params * @param miningParams the mining params
* @param backwardSyncContext the backward sync context * @param backwardSyncContext the backward sync context
* @param mergeBlockCreatorFactory the merge block creator factory * @param mergeBlockCreatorFactory the merge block creator factory
@ -159,14 +161,14 @@ public class MergeCoordinator implements MergeMiningCoordinator, BadChainListene
public MergeCoordinator( public MergeCoordinator(
final ProtocolContext protocolContext, final ProtocolContext protocolContext,
final ProtocolSchedule protocolSchedule, final ProtocolSchedule protocolSchedule,
final ProposalBuilderExecutor blockBuilderExecutor, final EthScheduler ethScheduler,
final MiningParameters miningParams, final MiningParameters miningParams,
final BackwardSyncContext backwardSyncContext, final BackwardSyncContext backwardSyncContext,
final MergeBlockCreatorFactory mergeBlockCreatorFactory) { final MergeBlockCreatorFactory mergeBlockCreatorFactory) {
this.protocolContext = protocolContext; this.protocolContext = protocolContext;
this.protocolSchedule = protocolSchedule; this.protocolSchedule = protocolSchedule;
this.blockBuilderExecutor = blockBuilderExecutor; this.ethScheduler = ethScheduler;
this.mergeContext = protocolContext.getConsensusContext(MergeContext.class); this.mergeContext = protocolContext.getConsensusContext(MergeContext.class);
this.backwardSyncContext = backwardSyncContext; this.backwardSyncContext = backwardSyncContext;
if (miningParams.getTargetGasLimit().isEmpty()) { if (miningParams.getTargetGasLimit().isEmpty()) {
@ -366,8 +368,9 @@ public class MergeCoordinator implements MergeMiningCoordinator, BadChainListene
payloadIdentifier, payloadIdentifier,
miningParameters.getUnstable().getPosBlockCreationMaxTime()); miningParameters.getUnstable().getPosBlockCreationMaxTime());
blockBuilderExecutor ethScheduler
.buildProposal(() -> retryBlockCreationUntilUseful(payloadIdentifier, blockCreator)) .scheduleBlockCreationTask(
() -> retryBlockCreationUntilUseful(payloadIdentifier, blockCreator))
.orTimeout( .orTimeout(
miningParameters.getUnstable().getPosBlockCreationMaxTime(), TimeUnit.MILLISECONDS) miningParameters.getUnstable().getPosBlockCreationMaxTime(), TimeUnit.MILLISECONDS)
.whenComplete( .whenComplete(
@ -895,15 +898,4 @@ public class MergeCoordinator implements MergeMiningCoordinator, BadChainListene
blockCreator.cancel(); blockCreator.cancel();
} }
} }
/** The interface Proposal builder executor. */
public interface ProposalBuilderExecutor {
/**
* Build proposal and return completable future.
*
* @param task the task
* @return the completable future
*/
CompletableFuture<Void> buildProposal(final Runnable task);
}
} }

@ -35,7 +35,6 @@ import static org.mockito.Mockito.when;
import org.hyperledger.besu.config.MergeConfigOptions; import org.hyperledger.besu.config.MergeConfigOptions;
import org.hyperledger.besu.consensus.merge.MergeContext; import org.hyperledger.besu.consensus.merge.MergeContext;
import org.hyperledger.besu.consensus.merge.PayloadWrapper; import org.hyperledger.besu.consensus.merge.PayloadWrapper;
import org.hyperledger.besu.consensus.merge.blockcreation.MergeCoordinator.ProposalBuilderExecutor;
import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator.ForkchoiceResult; import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator.ForkchoiceResult;
import org.hyperledger.besu.crypto.KeyPair; import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SECPPrivateKey; import org.hyperledger.besu.crypto.SECPPrivateKey;
@ -62,6 +61,7 @@ import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters.Unstable;
import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture; import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.sync.backwardsync.BackwardSyncContext; import org.hyperledger.besu.ethereum.eth.sync.backwardsync.BackwardSyncContext;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction; import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
@ -130,10 +130,11 @@ public class MergeCoordinatorTest implements MergeGenesisConfigHelper {
@Mock(answer = Answers.RETURNS_DEEP_STUBS) @Mock(answer = Answers.RETURNS_DEEP_STUBS)
EthContext ethContext; EthContext ethContext;
@Mock ProposalBuilderExecutor proposalBuilderExecutor; @Mock EthScheduler ethScheduler;
private final Address coinbase = genesisAllocations(getPosGenesisConfigFile()).findFirst().get(); private final Address coinbase = genesisAllocations(getPosGenesisConfigFile()).findFirst().get();
MiningParameters miningParameters = private MiningParameters miningParameters =
ImmutableMiningParameters.builder() ImmutableMiningParameters.builder()
.mutableInitValues(MutableInitValues.builder().coinbase(coinbase).build()) .mutableInitValues(MutableInitValues.builder().coinbase(coinbase).build())
.unstable( .unstable(
@ -205,10 +206,13 @@ public class MergeCoordinatorTest implements MergeGenesisConfigHelper {
genesisState.writeStateTo(mutable); genesisState.writeStateTo(mutable);
mutable.persist(null); mutable.persist(null);
when(proposalBuilderExecutor.buildProposal(any())) when(ethScheduler.scheduleBlockCreationTask(any()))
.thenAnswer( .thenAnswer(
invocation -> { invocation -> {
final Runnable runnable = invocation.getArgument(0); final Runnable runnable = invocation.getArgument(0);
if (!invocation.toString().contains("MergeCoordinator")) {
return CompletableFuture.runAsync(runnable);
}
blockCreationTask = CompletableFuture.runAsync(runnable); blockCreationTask = CompletableFuture.runAsync(runnable);
return blockCreationTask; return blockCreationTask;
}); });
@ -234,7 +238,7 @@ public class MergeCoordinatorTest implements MergeGenesisConfigHelper {
new MergeCoordinator( new MergeCoordinator(
protocolContext, protocolContext,
protocolSchedule, protocolSchedule,
proposalBuilderExecutor, ethScheduler,
transactionPool, transactionPool,
miningParameters, miningParameters,
backwardSyncContext, backwardSyncContext,
@ -287,7 +291,8 @@ public class MergeCoordinatorTest implements MergeGenesisConfigHelper {
protocolContext, protocolContext,
protocolSchedule, protocolSchedule,
parentHeader, parentHeader,
Optional.empty())); Optional.empty(),
ethScheduler));
doCallRealMethod() doCallRealMethod()
.doCallRealMethod() .doCallRealMethod()
@ -304,7 +309,7 @@ public class MergeCoordinatorTest implements MergeGenesisConfigHelper {
new MergeCoordinator( new MergeCoordinator(
protocolContext, protocolContext,
protocolSchedule, protocolSchedule,
proposalBuilderExecutor, ethScheduler,
miningParameters, miningParameters,
backwardSyncContext, backwardSyncContext,
mergeBlockCreatorFactory)); mergeBlockCreatorFactory));
@ -646,7 +651,7 @@ public class MergeCoordinatorTest implements MergeGenesisConfigHelper {
doAnswer( doAnswer(
invocation -> { invocation -> {
if (retries.getAndIncrement() < 5) { if (retries.getAndIncrement() < 5) {
// a new transaction every time a block is built // add a new transaction every time a block is built
transactions.addTransaction( transactions.addTransaction(
createLocalTransaction(retries.get() - 1), Optional.empty()); createLocalTransaction(retries.get() - 1), Optional.empty());
} else { } else {
@ -751,7 +756,7 @@ public class MergeCoordinatorTest implements MergeGenesisConfigHelper {
new MergeCoordinator( new MergeCoordinator(
protocolContext, protocolContext,
protocolSchedule, protocolSchedule,
proposalBuilderExecutor, ethScheduler,
transactionPool, transactionPool,
miningParameters, miningParameters,
backwardSyncContext, backwardSyncContext,

@ -35,6 +35,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.Difficulty; import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters; import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters.MutableInitValues; import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters.MutableInitValues;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.sync.backwardsync.BackwardSyncContext; import org.hyperledger.besu.ethereum.eth.sync.backwardsync.BackwardSyncContext;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator; import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator;
@ -42,12 +43,12 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket; import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket;
import org.hyperledger.besu.ethereum.mainnet.feemarket.LondonFeeMarket; import org.hyperledger.besu.ethereum.mainnet.feemarket.LondonFeeMarket;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.util.LogConfigurator; import org.hyperledger.besu.util.LogConfigurator;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -72,7 +73,7 @@ public class MergeReorgTest implements MergeGenesisConfigHelper {
private final WorldStateArchive worldStateArchive = createInMemoryWorldStateArchive(); private final WorldStateArchive worldStateArchive = createInMemoryWorldStateArchive();
private final MutableBlockchain blockchain = createInMemoryBlockchain(genesisState.getBlock()); private final MutableBlockchain blockchain = createInMemoryBlockchain(genesisState.getBlock());
private final EthScheduler ethScheduler = new DeterministicEthScheduler();
private final ProtocolContext protocolContext = private final ProtocolContext protocolContext =
new ProtocolContext(blockchain, worldStateArchive, mergeContext, Optional.empty()); new ProtocolContext(blockchain, worldStateArchive, mergeContext, Optional.empty());
@ -92,7 +93,7 @@ public class MergeReorgTest implements MergeGenesisConfigHelper {
new MergeCoordinator( new MergeCoordinator(
protocolContext, protocolContext,
mockProtocolSchedule, mockProtocolSchedule,
CompletableFuture::runAsync, ethScheduler,
mockTransactionPool, mockTransactionPool,
ImmutableMiningParameters.builder() ImmutableMiningParameters.builder()
.mutableInitValues(MutableInitValues.builder().coinbase(coinbase).build()) .mutableInitValues(MutableInitValues.builder().coinbase(coinbase).build())

@ -96,6 +96,7 @@ import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.ProtocolScheduleFixture; import org.hyperledger.besu.ethereum.core.ProtocolScheduleFixture;
import org.hyperledger.besu.ethereum.core.Util; import org.hyperledger.besu.ethereum.core.Util;
import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionBroadcaster; import org.hyperledger.besu.ethereum.eth.transactions.TransactionBroadcaster;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
@ -108,6 +109,7 @@ import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.testutil.TestClock; import org.hyperledger.besu.testutil.TestClock;
import org.hyperledger.besu.util.Subscribers; import org.hyperledger.besu.util.Subscribers;
@ -455,6 +457,8 @@ public class TestContextBuilder {
transactionPool.setEnabled(); transactionPool.setEnabled();
final EthScheduler ethScheduler = new DeterministicEthScheduler();
final Address localAddress = Util.publicKeyToAddress(nodeKey.getPublicKey()); final Address localAddress = Util.publicKeyToAddress(nodeKey.getPublicKey());
final BftBlockCreatorFactory<?> blockCreatorFactory = final BftBlockCreatorFactory<?> blockCreatorFactory =
new QbftBlockCreatorFactory( new QbftBlockCreatorFactory(
@ -464,7 +468,8 @@ public class TestContextBuilder {
forksSchedule, forksSchedule,
miningParams, miningParams,
localAddress, localAddress,
BFT_EXTRA_DATA_ENCODER); BFT_EXTRA_DATA_ENCODER,
ethScheduler);
final ProposerSelector proposerSelector = final ProposerSelector proposerSelector =
new ProposerSelector(blockChain, blockInterface, true, validatorProvider); new ProposerSelector(blockChain, blockInterface, true, validatorProvider);

@ -26,6 +26,7 @@ import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.blockcreation.BlockCreator; import org.hyperledger.besu.ethereum.blockcreation.BlockCreator;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
@ -46,6 +47,7 @@ public class QbftBlockCreatorFactory extends BftBlockCreatorFactory<QbftConfigOp
* @param miningParams the mining params * @param miningParams the mining params
* @param localAddress the local address * @param localAddress the local address
* @param bftExtraDataCodec the bft extra data codec * @param bftExtraDataCodec the bft extra data codec
* @param ethScheduler the scheduler for asynchronous block creation tasks
*/ */
public QbftBlockCreatorFactory( public QbftBlockCreatorFactory(
final TransactionPool transactionPool, final TransactionPool transactionPool,
@ -54,7 +56,8 @@ public class QbftBlockCreatorFactory extends BftBlockCreatorFactory<QbftConfigOp
final ForksSchedule<QbftConfigOptions> forksSchedule, final ForksSchedule<QbftConfigOptions> forksSchedule,
final MiningParameters miningParams, final MiningParameters miningParams,
final Address localAddress, final Address localAddress,
final BftExtraDataCodec bftExtraDataCodec) { final BftExtraDataCodec bftExtraDataCodec,
final EthScheduler ethScheduler) {
super( super(
transactionPool, transactionPool,
protocolContext, protocolContext,
@ -62,7 +65,8 @@ public class QbftBlockCreatorFactory extends BftBlockCreatorFactory<QbftConfigOp
forksSchedule, forksSchedule,
miningParams, miningParams,
localAddress, localAddress,
bftExtraDataCodec); bftExtraDataCodec,
ethScheduler);
} }
@Override @Override

@ -35,6 +35,7 @@ import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters.MutableInitV
import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import java.util.Optional; import java.util.Optional;
@ -72,7 +73,8 @@ public class QbftBlockCreatorFactoryTest {
forksSchedule, forksSchedule,
miningParams, miningParams,
mock(Address.class), mock(Address.class),
extraDataCodec); extraDataCodec,
new DeterministicEthScheduler());
} }
@Test @Test

@ -39,6 +39,7 @@ import org.hyperledger.besu.ethereum.core.SealableBlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.Withdrawal; import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.core.encoding.DepositDecoder; import org.hyperledger.besu.ethereum.core.encoding.DepositDecoder;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor; import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor;
import org.hyperledger.besu.ethereum.mainnet.BodyValidation; import org.hyperledger.besu.ethereum.mainnet.BodyValidation;
@ -92,7 +93,7 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
protected final BlockHeaderFunctions blockHeaderFunctions; protected final BlockHeaderFunctions blockHeaderFunctions;
protected final BlockHeader parentHeader; protected final BlockHeader parentHeader;
private final Optional<Address> depositContractAddress; private final Optional<Address> depositContractAddress;
private final EthScheduler ethScheduler;
private final AtomicBoolean isCancelled = new AtomicBoolean(false); private final AtomicBoolean isCancelled = new AtomicBoolean(false);
protected AbstractBlockCreator( protected AbstractBlockCreator(
@ -103,7 +104,8 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
final ProtocolContext protocolContext, final ProtocolContext protocolContext,
final ProtocolSchedule protocolSchedule, final ProtocolSchedule protocolSchedule,
final BlockHeader parentHeader, final BlockHeader parentHeader,
final Optional<Address> depositContractAddress) { final Optional<Address> depositContractAddress,
final EthScheduler ethScheduler) {
this.miningParameters = miningParameters; this.miningParameters = miningParameters;
this.miningBeneficiaryCalculator = miningBeneficiaryCalculator; this.miningBeneficiaryCalculator = miningBeneficiaryCalculator;
this.extraDataCalculator = extraDataCalculator; this.extraDataCalculator = extraDataCalculator;
@ -112,6 +114,7 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
this.protocolSchedule = protocolSchedule; this.protocolSchedule = protocolSchedule;
this.parentHeader = parentHeader; this.parentHeader = parentHeader;
this.depositContractAddress = depositContractAddress; this.depositContractAddress = depositContractAddress;
this.ethScheduler = ethScheduler;
blockHeaderFunctions = ScheduleBasedBlockHeaderFunctions.create(protocolSchedule); blockHeaderFunctions = ScheduleBasedBlockHeaderFunctions.create(protocolSchedule);
} }
@ -360,7 +363,8 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
protocolSpec.getFeeMarket(), protocolSpec.getFeeMarket(),
protocolSpec.getGasCalculator(), protocolSpec.getGasCalculator(),
protocolSpec.getGasLimitCalculator(), protocolSpec.getGasLimitCalculator(),
pluginTransactionSelector); pluginTransactionSelector,
ethScheduler);
if (transactions.isPresent()) { if (transactions.isPresent()) {
return selector.evaluateTransactions(transactions.get()); return selector.evaluateTransactions(transactions.get());

@ -21,6 +21,7 @@ import org.hyperledger.besu.ethereum.chain.MinedBlockObserver;
import org.hyperledger.besu.ethereum.chain.PoWObserver; import org.hyperledger.besu.ethereum.chain.PoWObserver;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.AbstractGasLimitSpecification; import org.hyperledger.besu.ethereum.mainnet.AbstractGasLimitSpecification;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
@ -41,12 +42,14 @@ public abstract class AbstractMinerExecutor<M extends BlockMiner<? extends Abstr
private static final Logger LOG = LoggerFactory.getLogger(AbstractMinerExecutor.class); private static final Logger LOG = LoggerFactory.getLogger(AbstractMinerExecutor.class);
private final ExecutorService executorService = Executors.newCachedThreadPool(); private final ExecutorService executorService =
Executors.newCachedThreadPool(r -> new Thread(r, "MinerExecutor"));
protected final ProtocolContext protocolContext; protected final ProtocolContext protocolContext;
protected final ProtocolSchedule protocolSchedule; protected final ProtocolSchedule protocolSchedule;
protected final TransactionPool transactionPool; protected final TransactionPool transactionPool;
protected final AbstractBlockScheduler blockScheduler; protected final AbstractBlockScheduler blockScheduler;
protected final MiningParameters miningParameters; protected final MiningParameters miningParameters;
protected final EthScheduler ethScheduler;
private final AtomicBoolean stopped = new AtomicBoolean(false); private final AtomicBoolean stopped = new AtomicBoolean(false);
protected AbstractMinerExecutor( protected AbstractMinerExecutor(
@ -54,12 +57,14 @@ public abstract class AbstractMinerExecutor<M extends BlockMiner<? extends Abstr
final ProtocolSchedule protocolSchedule, final ProtocolSchedule protocolSchedule,
final TransactionPool transactionPool, final TransactionPool transactionPool,
final MiningParameters miningParams, final MiningParameters miningParams,
final AbstractBlockScheduler blockScheduler) { final AbstractBlockScheduler blockScheduler,
final EthScheduler ethScheduler) {
this.protocolContext = protocolContext; this.protocolContext = protocolContext;
this.protocolSchedule = protocolSchedule; this.protocolSchedule = protocolSchedule;
this.transactionPool = transactionPool; this.transactionPool = transactionPool;
this.blockScheduler = blockScheduler; this.blockScheduler = blockScheduler;
this.miningParameters = miningParams; this.miningParameters = miningParams;
this.ethScheduler = ethScheduler;
} }
public Optional<M> startAsyncMining( public Optional<M> startAsyncMining(

@ -19,6 +19,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder; import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder;
import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.SealableBlockHeader; import org.hyperledger.besu.ethereum.core.SealableBlockHeader;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.EthHash; import org.hyperledger.besu.ethereum.mainnet.EthHash;
import org.hyperledger.besu.ethereum.mainnet.PoWSolution; import org.hyperledger.besu.ethereum.mainnet.PoWSolution;
@ -44,7 +45,8 @@ public class PoWBlockCreator extends AbstractBlockCreator {
final ProtocolContext protocolContext, final ProtocolContext protocolContext,
final ProtocolSchedule protocolSchedule, final ProtocolSchedule protocolSchedule,
final PoWSolver nonceSolver, final PoWSolver nonceSolver,
final BlockHeader parentHeader) { final BlockHeader parentHeader,
final EthScheduler ethScheduler) {
super( super(
miningParameters, miningParameters,
__ -> miningParameters.getCoinbase().orElseThrow(), __ -> miningParameters.getCoinbase().orElseThrow(),
@ -53,7 +55,8 @@ public class PoWBlockCreator extends AbstractBlockCreator {
protocolContext, protocolContext,
protocolSchedule, protocolSchedule,
parentHeader, parentHeader,
Optional.empty()); Optional.empty(),
ethScheduler);
this.nonceSolver = nonceSolver; this.nonceSolver = nonceSolver;
} }

@ -20,6 +20,7 @@ import org.hyperledger.besu.ethereum.chain.MinedBlockObserver;
import org.hyperledger.besu.ethereum.chain.PoWObserver; import org.hyperledger.besu.ethereum.chain.PoWObserver;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.EpochCalculator; import org.hyperledger.besu.ethereum.mainnet.EpochCalculator;
import org.hyperledger.besu.ethereum.mainnet.PoWSolver; import org.hyperledger.besu.ethereum.mainnet.PoWSolver;
@ -41,8 +42,15 @@ public class PoWMinerExecutor extends AbstractMinerExecutor<PoWBlockMiner> {
final TransactionPool transactionPool, final TransactionPool transactionPool,
final MiningParameters miningParams, final MiningParameters miningParams,
final AbstractBlockScheduler blockScheduler, final AbstractBlockScheduler blockScheduler,
final EpochCalculator epochCalculator) { final EpochCalculator epochCalculator,
super(protocolContext, protocolSchedule, transactionPool, miningParams, blockScheduler); final EthScheduler ethScheduler) {
super(
protocolContext,
protocolSchedule,
transactionPool,
miningParams,
blockScheduler,
ethScheduler);
if (miningParams.getNonceGenerator().isEmpty()) { if (miningParams.getNonceGenerator().isEmpty()) {
miningParams.setNonceGenerator(new RandomNonceGenerator()); miningParams.setNonceGenerator(new RandomNonceGenerator());
} }
@ -85,7 +93,8 @@ public class PoWMinerExecutor extends AbstractMinerExecutor<PoWBlockMiner> {
protocolContext, protocolContext,
protocolSchedule, protocolSchedule,
solver, solver,
parentHeader); parentHeader,
ethScheduler);
return new PoWBlockMiner( return new PoWBlockMiner(
blockCreator, protocolSchedule, protocolContext, observers, blockScheduler, parentHeader); blockCreator, protocolSchedule, protocolContext, observers, blockScheduler, parentHeader);

@ -14,6 +14,9 @@
*/ */
package org.hyperledger.besu.ethereum.blockcreation.txselection; package org.hyperledger.besu.ethereum.blockcreation.txselection;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.BLOCK_SELECTION_TIMEOUT;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.SELECTED;
import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.GasLimitCalculator; import org.hyperledger.besu.ethereum.GasLimitCalculator;
@ -29,6 +32,7 @@ import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt; import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction; import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor; import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor;
@ -46,6 +50,10 @@ import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelecto
import java.util.List; import java.util.List;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -85,6 +93,9 @@ public class BlockTransactionSelector {
private final List<AbstractTransactionSelector> transactionSelectors; private final List<AbstractTransactionSelector> transactionSelectors;
private final PluginTransactionSelector pluginTransactionSelector; private final PluginTransactionSelector pluginTransactionSelector;
private final BlockAwareOperationTracer pluginOperationTracer; private final BlockAwareOperationTracer pluginOperationTracer;
private final EthScheduler ethScheduler;
private final AtomicBoolean isTimeout = new AtomicBoolean(false);
private WorldUpdater blockWorldStateUpdater;
public BlockTransactionSelector( public BlockTransactionSelector(
final MiningParameters miningParameters, final MiningParameters miningParameters,
@ -100,12 +111,14 @@ public class BlockTransactionSelector {
final FeeMarket feeMarket, final FeeMarket feeMarket,
final GasCalculator gasCalculator, final GasCalculator gasCalculator,
final GasLimitCalculator gasLimitCalculator, final GasLimitCalculator gasLimitCalculator,
final PluginTransactionSelector pluginTransactionSelector) { final PluginTransactionSelector pluginTransactionSelector,
final EthScheduler ethScheduler) {
this.transactionProcessor = transactionProcessor; this.transactionProcessor = transactionProcessor;
this.blockchain = blockchain; this.blockchain = blockchain;
this.worldState = worldState; this.worldState = worldState;
this.transactionReceiptFactory = transactionReceiptFactory; this.transactionReceiptFactory = transactionReceiptFactory;
this.isCancelled = isCancelled; this.isCancelled = isCancelled;
this.ethScheduler = ethScheduler;
this.blockSelectionContext = this.blockSelectionContext =
new BlockSelectionContext( new BlockSelectionContext(
miningParameters, miningParameters,
@ -119,6 +132,7 @@ public class BlockTransactionSelector {
transactionSelectors = createTransactionSelectors(blockSelectionContext); transactionSelectors = createTransactionSelectors(blockSelectionContext);
this.pluginTransactionSelector = pluginTransactionSelector; this.pluginTransactionSelector = pluginTransactionSelector;
this.pluginOperationTracer = pluginTransactionSelector.getOperationTracer(); this.pluginOperationTracer = pluginTransactionSelector.getOperationTracer();
blockWorldStateUpdater = worldState.updater();
} }
private List<AbstractTransactionSelector> createTransactionSelectors( private List<AbstractTransactionSelector> createTransactionSelectors(
@ -145,9 +159,7 @@ public class BlockTransactionSelector {
.setMessage("Transaction pool stats {}") .setMessage("Transaction pool stats {}")
.addArgument(blockSelectionContext.transactionPool().logStats()) .addArgument(blockSelectionContext.transactionPool().logStats())
.log(); .log();
timeLimitedSelection();
blockSelectionContext.transactionPool().selectTransactions(this::evaluateTransaction);
LOG.atTrace() LOG.atTrace()
.setMessage("Transaction selection result {}") .setMessage("Transaction selection result {}")
.addArgument(transactionSelectionResults::toTraceLog) .addArgument(transactionSelectionResults::toTraceLog)
@ -155,6 +167,36 @@ public class BlockTransactionSelector {
return transactionSelectionResults; return transactionSelectionResults;
} }
private void timeLimitedSelection() {
final long blockTxsSelectionMaxTime =
blockSelectionContext.miningParameters().getUnstable().getBlockTxsSelectionMaxTime();
final var txSelection =
ethScheduler.scheduleBlockCreationTask(
() ->
blockSelectionContext
.transactionPool()
.selectTransactions(this::evaluateTransaction));
try {
txSelection.get(blockTxsSelectionMaxTime, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException e) {
if (isCancelled.get()) {
throw new CancellationException("Cancelled during transaction selection");
}
LOG.warn("Error during block transaction selection", e);
} catch (TimeoutException e) {
// synchronize since we want to be sure that there is no concurrent state update
synchronized (isTimeout) {
isTimeout.set(true);
}
LOG.warn(
"Interrupting transaction selection since it is taking more than the max configured time of "
+ blockTxsSelectionMaxTime
+ "ms",
e);
}
}
/** /**
* Evaluates a list of transactions and updates the selection results accordingly. If a * Evaluates a list of transactions and updates the selection results accordingly. If a
* transaction is not selected during the evaluation, it is updated as not selected in the * transaction is not selected during the evaluation, it is updated as not selected in the
@ -189,17 +231,18 @@ public class BlockTransactionSelector {
return handleTransactionNotSelected(pendingTransaction, selectionResult); return handleTransactionNotSelected(pendingTransaction, selectionResult);
} }
final WorldUpdater worldStateUpdater = worldState.updater(); final WorldUpdater txWorldStateUpdater = blockWorldStateUpdater.updater();
final TransactionProcessingResult processingResult = final TransactionProcessingResult processingResult =
processTransaction(pendingTransaction, worldStateUpdater); processTransaction(pendingTransaction, txWorldStateUpdater);
var postProcessingSelectionResult = var postProcessingSelectionResult =
evaluatePostProcessing(pendingTransaction, processingResult); evaluatePostProcessing(pendingTransaction, processingResult);
if (!postProcessingSelectionResult.selected()) {
return handleTransactionNotSelected(pendingTransaction, postProcessingSelectionResult);
}
return handleTransactionSelected(pendingTransaction, processingResult, worldStateUpdater); if (postProcessingSelectionResult.selected()) {
return handleTransactionSelected(pendingTransaction, processingResult, txWorldStateUpdater);
}
return handleTransactionNotSelected(
pendingTransaction, postProcessingSelectionResult, txWorldStateUpdater);
} }
/** /**
@ -218,7 +261,7 @@ public class BlockTransactionSelector {
TransactionSelectionResult result = TransactionSelectionResult result =
selector.evaluateTransactionPreProcessing( selector.evaluateTransactionPreProcessing(
pendingTransaction, transactionSelectionResults); pendingTransaction, transactionSelectionResults);
if (!result.equals(TransactionSelectionResult.SELECTED)) { if (!result.equals(SELECTED)) {
return result; return result;
} }
} }
@ -243,7 +286,7 @@ public class BlockTransactionSelector {
TransactionSelectionResult result = TransactionSelectionResult result =
selector.evaluateTransactionPostProcessing( selector.evaluateTransactionPostProcessing(
pendingTransaction, transactionSelectionResults, processingResult); pendingTransaction, transactionSelectionResults, processingResult);
if (!result.equals(TransactionSelectionResult.SELECTED)) { if (!result.equals(SELECTED)) {
return result; return result;
} }
} }
@ -282,14 +325,13 @@ public class BlockTransactionSelector {
* *
* @param pendingTransaction The pending transaction. * @param pendingTransaction The pending transaction.
* @param processingResult The result of the transaction processing. * @param processingResult The result of the transaction processing.
* @param worldStateUpdater The world state updater. * @param txWorldStateUpdater The world state updater.
* @return The result of the transaction selection process. * @return The result of the transaction selection process.
*/ */
private TransactionSelectionResult handleTransactionSelected( private TransactionSelectionResult handleTransactionSelected(
final PendingTransaction pendingTransaction, final PendingTransaction pendingTransaction,
final TransactionProcessingResult processingResult, final TransactionProcessingResult processingResult,
final WorldUpdater worldStateUpdater) { final WorldUpdater txWorldStateUpdater) {
worldStateUpdater.commit();
final Transaction transaction = pendingTransaction.getTransaction(); final Transaction transaction = pendingTransaction.getTransaction();
final long gasUsedByTransaction = final long gasUsedByTransaction =
@ -299,17 +341,47 @@ public class BlockTransactionSelector {
final long blobGasUsed = final long blobGasUsed =
blockSelectionContext.gasCalculator().blobGasCost(transaction.getBlobCount()); blockSelectionContext.gasCalculator().blobGasCost(transaction.getBlobCount());
final TransactionReceipt receipt = final boolean tooLate;
transactionReceiptFactory.create(
transaction.getType(), processingResult, worldState, cumulativeGasUsed);
logTransactionSelection(pendingTransaction.getTransaction()); // only add this tx to the selected set if it is not too late,
// this need to be done synchronously to avoid that a concurrent timeout
// could start packing a block while we are updating the state here
synchronized (isTimeout) {
if (!isTimeout.get()) {
txWorldStateUpdater.commit();
blockWorldStateUpdater.commit();
final TransactionReceipt receipt =
transactionReceiptFactory.create(
transaction.getType(), processingResult, worldState, cumulativeGasUsed);
transactionSelectionResults.updateSelected( transactionSelectionResults.updateSelected(
pendingTransaction.getTransaction(), receipt, gasUsedByTransaction, blobGasUsed); pendingTransaction.getTransaction(), receipt, gasUsedByTransaction, blobGasUsed);
pluginTransactionSelector.onTransactionSelected(pendingTransaction, processingResult); tooLate = false;
} else {
tooLate = true;
}
}
if (tooLate) {
// even if this tx passed all the checks, it is too late to include it in this block,
// so we need to treat it as not selected
LOG.atTrace()
.setMessage("{} processed too late for block creation")
.addArgument(transaction::toTraceLog)
.log();
// do not rely on the presence of this result, since by the time it is added, the code
// reading it could have been already executed by another thread
return handleTransactionNotSelected(
pendingTransaction, BLOCK_SELECTION_TIMEOUT, txWorldStateUpdater);
}
return TransactionSelectionResult.SELECTED; pluginTransactionSelector.onTransactionSelected(pendingTransaction, processingResult);
blockWorldStateUpdater = worldState.updater();
LOG.atTrace()
.setMessage("Selected {} for block creation")
.addArgument(transaction::toTraceLog)
.log();
return SELECTED;
} }
/** /**
@ -324,22 +396,24 @@ public class BlockTransactionSelector {
private TransactionSelectionResult handleTransactionNotSelected( private TransactionSelectionResult handleTransactionNotSelected(
final PendingTransaction pendingTransaction, final PendingTransaction pendingTransaction,
final TransactionSelectionResult selectionResult) { final TransactionSelectionResult selectionResult) {
transactionSelectionResults.updateNotSelected( transactionSelectionResults.updateNotSelected(
pendingTransaction.getTransaction(), selectionResult); pendingTransaction.getTransaction(), selectionResult);
pluginTransactionSelector.onTransactionNotSelected(pendingTransaction, selectionResult); pluginTransactionSelector.onTransactionNotSelected(pendingTransaction, selectionResult);
return selectionResult; return selectionResult;
} }
private TransactionSelectionResult handleTransactionNotSelected(
final PendingTransaction pendingTransaction,
final TransactionSelectionResult selectionResult,
final WorldUpdater txWorldStateUpdater) {
txWorldStateUpdater.revert();
return handleTransactionNotSelected(pendingTransaction, selectionResult);
}
private void checkCancellation() { private void checkCancellation() {
if (isCancelled.get()) { if (isCancelled.get()) {
throw new CancellationException("Cancelled during transaction selection."); throw new CancellationException("Cancelled during transaction selection.");
} }
} }
private void logTransactionSelection(final Transaction transaction) {
LOG.atTrace()
.setMessage("Selected {} for block creation")
.addArgument(transaction::toTraceLog)
.log();
}
} }

@ -21,10 +21,10 @@ import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -39,8 +39,13 @@ public class TransactionSelectionResults {
private final Map<TransactionType, List<Transaction>> transactionsByType = private final Map<TransactionType, List<Transaction>> transactionsByType =
new EnumMap<>(TransactionType.class); new EnumMap<>(TransactionType.class);
private final List<TransactionReceipt> receipts = Lists.newArrayList(); private final List<TransactionReceipt> receipts = Lists.newArrayList();
/**
* Access to this field needs to be guarded, since it is possible to read it while another
* processing thread is writing, when the selection time is over.
*/
private final Map<Transaction, TransactionSelectionResult> notSelectedTransactions = private final Map<Transaction, TransactionSelectionResult> notSelectedTransactions =
new HashMap<>(); new ConcurrentHashMap<>();
private long cumulativeGasUsed = 0; private long cumulativeGasUsed = 0;
private long cumulativeBlobGasUsed = 0; private long cumulativeBlobGasUsed = 0;
@ -92,18 +97,19 @@ public class TransactionSelectionResults {
} }
public Map<Transaction, TransactionSelectionResult> getNotSelectedTransactions() { public Map<Transaction, TransactionSelectionResult> getNotSelectedTransactions() {
return notSelectedTransactions; return Map.copyOf(notSelectedTransactions);
} }
public void logSelectionStats() { public void logSelectionStats() {
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
final var notSelectedTxs = getNotSelectedTransactions();
final Map<TransactionSelectionResult, Long> notSelectedStats = final Map<TransactionSelectionResult, Long> notSelectedStats =
notSelectedTransactions.values().stream() notSelectedTxs.values().stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
LOG.debug( LOG.debug(
"Selection stats: Totals[Evaluated={}, Selected={}, NotSelected={}, Discarded={}]; Detailed[{}]", "Selection stats: Totals[Evaluated={}, Selected={}, NotSelected={}, Discarded={}]; Detailed[{}]",
selectedTransactions.size() + notSelectedTransactions.size(), selectedTransactions.size() + notSelectedTxs.size(),
selectedTransactions.size(), selectedTransactions.size(),
notSelectedStats.size(), notSelectedStats.size(),
notSelectedStats.entrySet().stream() notSelectedStats.entrySet().stream()

@ -59,6 +59,7 @@ import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture; import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.core.Withdrawal; import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionBroadcaster; import org.hyperledger.besu.ethereum.eth.transactions.TransactionBroadcaster;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
@ -77,6 +78,7 @@ import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.log.Log; import org.hyperledger.besu.evm.log.Log;
import org.hyperledger.besu.evm.log.LogTopic; import org.hyperledger.besu.evm.log.LogTopic;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import java.math.BigInteger; import java.math.BigInteger;
import java.time.Clock; import java.time.Clock;
@ -97,6 +99,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
abstract class AbstractBlockCreatorTest { abstract class AbstractBlockCreatorTest {
private static final Optional<Address> EMPTY_DEPOSIT_CONTRACT_ADDRESS = Optional.empty(); private static final Optional<Address> EMPTY_DEPOSIT_CONTRACT_ADDRESS = Optional.empty();
@Mock private WithdrawalsProcessor withdrawalsProcessor; @Mock private WithdrawalsProcessor withdrawalsProcessor;
protected EthScheduler ethScheduler = new DeterministicEthScheduler();
@Test @Test
void findDepositsFromReceipts() { void findDepositsFromReceipts() {
@ -405,7 +408,8 @@ abstract class AbstractBlockCreatorTest {
executionContextTestFixture.getProtocolContext(), executionContextTestFixture.getProtocolContext(),
executionContextTestFixture.getProtocolSchedule(), executionContextTestFixture.getProtocolSchedule(),
blockchain.getChainHeadHeader(), blockchain.getChainHeadHeader(),
depositContractAddress); depositContractAddress,
ethScheduler);
} }
static class TestBlockCreator extends AbstractBlockCreator { static class TestBlockCreator extends AbstractBlockCreator {
@ -418,7 +422,8 @@ abstract class AbstractBlockCreatorTest {
final ProtocolContext protocolContext, final ProtocolContext protocolContext,
final ProtocolSchedule protocolSchedule, final ProtocolSchedule protocolSchedule,
final BlockHeader parentHeader, final BlockHeader parentHeader,
final Optional<Address> depositContractAddress) { final Optional<Address> depositContractAddress,
final EthScheduler ethScheduler) {
super( super(
miningParameters, miningParameters,
miningBeneficiaryCalculator, miningBeneficiaryCalculator,
@ -427,7 +432,8 @@ abstract class AbstractBlockCreatorTest {
protocolContext, protocolContext,
protocolSchedule, protocolSchedule,
parentHeader, parentHeader,
depositContractAddress); depositContractAddress,
ethScheduler);
} }
@Override @Override

@ -16,6 +16,8 @@ package org.hyperledger.besu.ethereum.blockcreation;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry; import static org.assertj.core.api.Assertions.entry;
import static org.awaitility.Awaitility.await;
import static org.hyperledger.besu.ethereum.core.MiningParameters.Unstable.DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
@ -48,6 +50,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.Difficulty; import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters; import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters.MutableInitValues; import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters.MutableInitValues;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters.Unstable;
import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider; import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider;
import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.MutableWorldState;
@ -56,6 +59,7 @@ import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt; import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.difficulty.fixed.FixedDifficultyProtocolSchedule; import org.hyperledger.besu.ethereum.difficulty.fixed.FixedDifficultyProtocolSchedule;
import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
@ -76,6 +80,7 @@ import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector; import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector;
import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelectorFactory; import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelectorFactory;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
import org.hyperledger.besu.util.number.Percentage;
import java.math.BigInteger; import java.math.BigInteger;
import java.time.Instant; import java.time.Instant;
@ -83,24 +88,34 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import java.util.stream.Stream;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Answers; import org.mockito.Answers;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness; import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT) @MockitoSettings(strictness = Strictness.LENIENT)
public abstract class AbstractBlockTransactionSelectorTest { public abstract class AbstractBlockTransactionSelectorTest {
protected static final double MIN_OCCUPANCY_80_PERCENT = 0.8; protected static final double MIN_OCCUPANCY_80_PERCENT = 0.8;
protected static final double MIN_OCCUPANCY_100_PERCENT = 1; protected static final double MIN_OCCUPANCY_100_PERCENT = 1;
protected static final PluginTransactionSelectorFactory NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY =
() -> AllAcceptingTransactionSelector.INSTANCE;
protected static final BigInteger CHAIN_ID = BigInteger.valueOf(42L); protected static final BigInteger CHAIN_ID = BigInteger.valueOf(42L);
protected static final KeyPair keyPair = protected static final KeyPair keyPair =
SignatureAlgorithmFactory.getInstance().generateKeyPair(); SignatureAlgorithmFactory.getInstance().generateKeyPair();
@ -113,7 +128,11 @@ public abstract class AbstractBlockTransactionSelectorTest {
protected TransactionPool transactionPool; protected TransactionPool transactionPool;
protected MutableWorldState worldState; protected MutableWorldState worldState;
protected ProtocolSchedule protocolSchedule; protected ProtocolSchedule protocolSchedule;
protected MiningParameters miningParameters; protected final MiningParameters defaultTestMiningParameters =
createMiningParameters(
Wei.ZERO, MIN_OCCUPANCY_80_PERCENT, DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME);
@Mock protected EthScheduler ethScheduler;
@Mock(answer = Answers.RETURNS_DEEP_STUBS) @Mock(answer = Answers.RETURNS_DEEP_STUBS)
protected ProtocolContext protocolContext; protected ProtocolContext protocolContext;
@ -150,19 +169,15 @@ public abstract class AbstractBlockTransactionSelectorTest {
when(protocolContext.getWorldStateArchive().getMutable(any(), anyBoolean())) when(protocolContext.getWorldStateArchive().getMutable(any(), anyBoolean()))
.thenReturn(Optional.of(worldState)); .thenReturn(Optional.of(worldState));
when(ethContext.getEthPeers().subscribeConnect(any())).thenReturn(1L); when(ethContext.getEthPeers().subscribeConnect(any())).thenReturn(1L);
miningParameters = when(ethScheduler.scheduleBlockCreationTask(any(Runnable.class)))
ImmutableMiningParameters.builder() .thenAnswer(invocation -> CompletableFuture.runAsync(invocation.getArgument(0)));
.mutableInitValues(MutableInitValues.builder().minTransactionGasPrice(Wei.ONE).build())
.build();
transactionPool = createTransactionPool();
} }
protected abstract GenesisConfigFile getGenesisConfigFile(); protected abstract GenesisConfigFile getGenesisConfigFile();
protected abstract ProtocolSchedule createProtocolSchedule(); protected abstract ProtocolSchedule createProtocolSchedule();
protected abstract TransactionPool createTransactionPool(); protected abstract TransactionPool createTransactionPool(final MiningParameters miningParameters);
private Boolean isCancelled() { private Boolean isCancelled() {
return false; return false;
@ -198,13 +213,13 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Address miningBeneficiary = AddressHelpers.ofValue(1); final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector = final BlockTransactionSelector selector =
createBlockSelector( createBlockSelectorAndSetupTxPool(
defaultTestMiningParameters,
mainnetTransactionProcessor, mainnetTransactionProcessor,
blockHeader, blockHeader,
Wei.ZERO,
miningBeneficiary, miningBeneficiary,
Wei.ZERO, Wei.ZERO,
MIN_OCCUPANCY_80_PERCENT); NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
final TransactionSelectionResults results = selector.buildTransactionListForBlock(); final TransactionSelectionResults results = selector.buildTransactionListForBlock();
@ -216,23 +231,21 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test @Test
public void validPendingTransactionIsIncludedInTheBlock() { public void validPendingTransactionIsIncludedInTheBlock() {
final Transaction transaction = createTransaction(1, Wei.of(7L), 100_000);
transactionPool.addRemoteTransactions(List.of(transaction));
ensureTransactionIsValid(transaction, 0, 5);
final ProcessableBlockHeader blockHeader = createBlock(500_000); final ProcessableBlockHeader blockHeader = createBlock(500_000);
final Address miningBeneficiary = AddressHelpers.ofValue(1); final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector = final BlockTransactionSelector selector =
createBlockSelector( createBlockSelectorAndSetupTxPool(
defaultTestMiningParameters,
transactionProcessor, transactionProcessor,
blockHeader, blockHeader,
Wei.ZERO,
miningBeneficiary, miningBeneficiary,
Wei.ZERO, Wei.ZERO,
MIN_OCCUPANCY_80_PERCENT); NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
final Transaction transaction = createTransaction(1, Wei.of(7L), 100_000);
transactionPool.addRemoteTransactions(List.of(transaction));
ensureTransactionIsValid(transaction, 0, 5);
final TransactionSelectionResults results = selector.buildTransactionListForBlock(); final TransactionSelectionResults results = selector.buildTransactionListForBlock();
@ -244,6 +257,18 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test @Test
public void invalidTransactionsAreSkippedButBlockStillFills() { public void invalidTransactionsAreSkippedButBlockStillFills() {
// The block should fit 4 transactions only
final ProcessableBlockHeader blockHeader = createBlock(400_000);
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelectorAndSetupTxPool(
defaultTestMiningParameters,
transactionProcessor,
blockHeader,
miningBeneficiary,
Wei.ZERO,
NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
final List<Transaction> transactionsToInject = Lists.newArrayList(); final List<Transaction> transactionsToInject = Lists.newArrayList();
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
final Transaction tx = createTransaction(i, Wei.of(7), 100_000); final Transaction tx = createTransaction(i, Wei.of(7), 100_000);
@ -256,20 +281,6 @@ public abstract class AbstractBlockTransactionSelectorTest {
} }
transactionPool.addRemoteTransactions(transactionsToInject); transactionPool.addRemoteTransactions(transactionsToInject);
// The block should fit 4 transactions only
final ProcessableBlockHeader blockHeader = createBlock(400_000);
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelector(
transactionProcessor,
blockHeader,
Wei.ZERO,
miningBeneficiary,
Wei.ZERO,
MIN_OCCUPANCY_80_PERCENT);
final TransactionSelectionResults results = selector.buildTransactionListForBlock(); final TransactionSelectionResults results = selector.buildTransactionListForBlock();
final Transaction invalidTx = transactionsToInject.get(1); final Transaction invalidTx = transactionsToInject.get(1);
@ -288,26 +299,24 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test @Test
public void subsetOfPendingTransactionsIncludedWhenBlockGasLimitHit() { public void subsetOfPendingTransactionsIncludedWhenBlockGasLimitHit() {
final List<Transaction> transactionsToInject = Lists.newArrayList();
for (int i = 0; i < 5; i++) {
final Transaction tx = createTransaction(i, Wei.of(7), 100_000);
transactionsToInject.add(tx);
ensureTransactionIsValid(tx);
}
transactionPool.addRemoteTransactions(transactionsToInject);
final ProcessableBlockHeader blockHeader = createBlock(301_000); final ProcessableBlockHeader blockHeader = createBlock(301_000);
final Address miningBeneficiary = AddressHelpers.ofValue(1); final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector = final BlockTransactionSelector selector =
createBlockSelector( createBlockSelectorAndSetupTxPool(
defaultTestMiningParameters,
transactionProcessor, transactionProcessor,
blockHeader, blockHeader,
Wei.ZERO,
miningBeneficiary, miningBeneficiary,
Wei.ZERO, Wei.ZERO,
MIN_OCCUPANCY_80_PERCENT); NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
final List<Transaction> transactionsToInject = Lists.newArrayList();
for (int i = 0; i < 5; i++) {
final Transaction tx = createTransaction(i, Wei.of(7), 100_000);
transactionsToInject.add(tx);
ensureTransactionIsValid(tx);
}
transactionPool.addRemoteTransactions(transactionsToInject);
final TransactionSelectionResults results = selector.buildTransactionListForBlock(); final TransactionSelectionResults results = selector.buildTransactionListForBlock();
@ -332,16 +341,15 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test @Test
public void transactionTooLargeForBlockDoesNotPreventMoreBeingAddedIfBlockOccupancyNotReached() { public void transactionTooLargeForBlockDoesNotPreventMoreBeingAddedIfBlockOccupancyNotReached() {
final ProcessableBlockHeader blockHeader = createBlock(300_000); final ProcessableBlockHeader blockHeader = createBlock(300_000);
final Address miningBeneficiary = AddressHelpers.ofValue(1); final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector = final BlockTransactionSelector selector =
createBlockSelector( createBlockSelectorAndSetupTxPool(
defaultTestMiningParameters,
transactionProcessor, transactionProcessor,
blockHeader, blockHeader,
Wei.ZERO,
miningBeneficiary, miningBeneficiary,
Wei.ZERO, Wei.ZERO,
MIN_OCCUPANCY_80_PERCENT); NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
// Add 3 transactions to the Pending Transactions, 79% of block, 100% of block and 10% of block // Add 3 transactions to the Pending Transactions, 79% of block, 100% of block and 10% of block
// should end up selecting the first and third only. // should end up selecting the first and third only.
@ -368,16 +376,15 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test @Test
public void transactionSelectionStopsWhenSufficientBlockOccupancyIsReached() { public void transactionSelectionStopsWhenSufficientBlockOccupancyIsReached() {
final ProcessableBlockHeader blockHeader = createBlock(300_000); final ProcessableBlockHeader blockHeader = createBlock(300_000);
final Address miningBeneficiary = AddressHelpers.ofValue(1); final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector = final BlockTransactionSelector selector =
createBlockSelector( createBlockSelectorAndSetupTxPool(
defaultTestMiningParameters,
transactionProcessor, transactionProcessor,
blockHeader, blockHeader,
Wei.ZERO,
miningBeneficiary, miningBeneficiary,
Wei.ZERO, Wei.ZERO,
MIN_OCCUPANCY_80_PERCENT); NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
// Add 4 transactions to the Pending Transactions 15% (ok), 79% (ok), 25% (too large), 10% // Add 4 transactions to the Pending Transactions 15% (ok), 79% (ok), 25% (too large), 10%
// (not included, it would fit, however previous transaction was too large and block was // (not included, it would fit, however previous transaction was too large and block was
@ -409,13 +416,14 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Address miningBeneficiary = AddressHelpers.ofValue(1); final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector = final BlockTransactionSelector selector =
createBlockSelector( createBlockSelectorAndSetupTxPool(
createMiningParameters(
Wei.ZERO, MIN_OCCUPANCY_100_PERCENT, DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME),
transactionProcessor, transactionProcessor,
blockHeader, blockHeader,
Wei.ZERO,
miningBeneficiary, miningBeneficiary,
Wei.ZERO, Wei.ZERO,
MIN_OCCUPANCY_100_PERCENT); NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
final long minTxGasCost = getGasCalculator().getMinimumTransactionCost(); final long minTxGasCost = getGasCalculator().getMinimumTransactionCost();
@ -467,13 +475,14 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Address miningBeneficiary = AddressHelpers.ofValue(1); final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector = final BlockTransactionSelector selector =
createBlockSelector( createBlockSelectorAndSetupTxPool(
createMiningParameters(
Wei.ZERO, MIN_OCCUPANCY_100_PERCENT, DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME),
transactionProcessor, transactionProcessor,
blockHeader, blockHeader,
Wei.ZERO,
miningBeneficiary, miningBeneficiary,
Wei.ZERO, Wei.ZERO,
MIN_OCCUPANCY_100_PERCENT); NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
final long minTxGasCost = getGasCalculator().getMinimumTransactionCost(); final long minTxGasCost = getGasCalculator().getMinimumTransactionCost();
@ -519,13 +528,13 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Address miningBeneficiary = AddressHelpers.ofValue(1); final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector = final BlockTransactionSelector selector =
createBlockSelector( createBlockSelectorAndSetupTxPool(
defaultTestMiningParameters,
transactionProcessor, transactionProcessor,
blockHeader, blockHeader,
Wei.ZERO,
miningBeneficiary, miningBeneficiary,
Wei.ZERO, Wei.ZERO,
MIN_OCCUPANCY_80_PERCENT); NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
final Transaction validTransaction = createTransaction(0, Wei.of(10), 21_000); final Transaction validTransaction = createTransaction(0, Wei.of(10), 21_000);
@ -552,6 +561,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test @Test
public void transactionSelectionPluginShouldWork_PreProcessing() { public void transactionSelectionPluginShouldWork_PreProcessing() {
final ProcessableBlockHeader blockHeader = createBlock(300_000); final ProcessableBlockHeader blockHeader = createBlock(300_000);
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final Transaction selected = createTransaction(0, Wei.of(10), 21_000); final Transaction selected = createTransaction(0, Wei.of(10), 21_000);
ensureTransactionIsValid(selected, 21_000, 0); ensureTransactionIsValid(selected, 21_000, 0);
@ -584,9 +594,9 @@ public abstract class AbstractBlockTransactionSelectorTest {
} }
}; };
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector = final BlockTransactionSelector selector =
createBlockSelectorWithTxSelPlugin( createBlockSelectorAndSetupTxPool(
defaultTestMiningParameters,
transactionProcessor, transactionProcessor,
blockHeader, blockHeader,
miningBeneficiary, miningBeneficiary,
@ -648,7 +658,9 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Address miningBeneficiary = AddressHelpers.ofValue(1); final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector = final BlockTransactionSelector selector =
createBlockSelectorWithTxSelPlugin( createBlockSelectorAndSetupTxPool(
createMiningParameters(
Wei.ZERO, MIN_OCCUPANCY_80_PERCENT, DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME),
transactionProcessor, transactionProcessor,
blockHeader, blockHeader,
miningBeneficiary, miningBeneficiary,
@ -678,15 +690,19 @@ public abstract class AbstractBlockTransactionSelectorTest {
final TransactionInvalidReason invalidReason = TransactionInvalidReason.PLUGIN_TX_VALIDATOR; final TransactionInvalidReason invalidReason = TransactionInvalidReason.PLUGIN_TX_VALIDATOR;
final Transaction invalidTransaction = createTransaction(1, Wei.of(10), 21_000); final Transaction invalidTransaction = createTransaction(1, Wei.of(10), 21_000);
ensureTransactionIsInvalid(invalidTransaction, TransactionInvalidReason.PLUGIN_TX_VALIDATOR); ensureTransactionIsInvalid(invalidTransaction, TransactionInvalidReason.PLUGIN_TX_VALIDATOR);
transactionPool.addRemoteTransactions(List.of(transaction, invalidTransaction));
createBlockSelectorWithTxSelPlugin( final BlockTransactionSelector selector =
createBlockSelectorAndSetupTxPool(
defaultTestMiningParameters,
transactionProcessor, transactionProcessor,
createBlock(300_000), createBlock(300_000),
AddressHelpers.ofValue(1), AddressHelpers.ofValue(1),
Wei.ZERO, Wei.ZERO,
transactionSelectorFactory) transactionSelectorFactory);
.buildTransactionListForBlock();
transactionPool.addRemoteTransactions(List.of(transaction, invalidTransaction));
selector.buildTransactionListForBlock();
ArgumentCaptor<PendingTransaction> argumentCaptor = ArgumentCaptor<PendingTransaction> argumentCaptor =
ArgumentCaptor.forClass(PendingTransaction.class); ArgumentCaptor.forClass(PendingTransaction.class);
@ -709,21 +725,20 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test @Test
public void transactionWithIncorrectNonceRemainsInPoolAndNotSelected() { public void transactionWithIncorrectNonceRemainsInPoolAndNotSelected() {
final ProcessableBlockHeader blockHeader = createBlock(5_000_000); final ProcessableBlockHeader blockHeader = createBlock(5_000_000);
final Transaction futureTransaction = createTransaction(4, Wei.of(10), 100_000);
transactionPool.addRemoteTransactions(List.of(futureTransaction));
ensureTransactionIsInvalid(futureTransaction, TransactionInvalidReason.NONCE_TOO_HIGH);
final Address miningBeneficiary = AddressHelpers.ofValue(1); final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector = final BlockTransactionSelector selector =
createBlockSelector( createBlockSelectorAndSetupTxPool(
defaultTestMiningParameters,
transactionProcessor, transactionProcessor,
blockHeader, blockHeader,
Wei.ZERO,
miningBeneficiary, miningBeneficiary,
Wei.ZERO, Wei.ZERO,
MIN_OCCUPANCY_80_PERCENT); NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
final Transaction futureTransaction = createTransaction(4, Wei.of(10), 100_000);
transactionPool.addRemoteTransactions(List.of(futureTransaction));
ensureTransactionIsInvalid(futureTransaction, TransactionInvalidReason.NONCE_TOO_HIGH);
final TransactionSelectionResults results = selector.buildTransactionListForBlock(); final TransactionSelectionResults results = selector.buildTransactionListForBlock();
@ -740,22 +755,25 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test @Test
public void increaseOfMinGasPriceAtRuntimeExcludeTxFromBeingSelected() { public void increaseOfMinGasPriceAtRuntimeExcludeTxFromBeingSelected() {
final Transaction transaction = createTransaction(0, Wei.of(7L), 100_000); final Transaction transaction = createTransaction(0, Wei.of(7L), 100_000);
transactionPool.addRemoteTransactions(List.of(transaction));
ensureTransactionIsValid(transaction, 0, 5);
final ProcessableBlockHeader blockHeader = createBlock(500_000); final ProcessableBlockHeader blockHeader = createBlock(500_000);
final Address miningBeneficiary = AddressHelpers.ofValue(1); final Address miningBeneficiary = AddressHelpers.ofValue(1);
final MiningParameters miningParameters =
ImmutableMiningParameters.builder().from(defaultTestMiningParameters).build();
final BlockTransactionSelector selector = final BlockTransactionSelector selector =
createBlockSelector( createBlockSelectorAndSetupTxPool(
miningParameters,
transactionProcessor, transactionProcessor,
blockHeader, blockHeader,
Wei.ZERO,
miningBeneficiary, miningBeneficiary,
Wei.ZERO, Wei.ZERO,
MIN_OCCUPANCY_80_PERCENT); NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
transactionPool.addRemoteTransactions(List.of(transaction));
ensureTransactionIsValid(transaction, 0, 5);
// raise the minGasPrice at runtime from 1 wei to 10 wei // raise the minGasPrice at runtime from 1 wei to 10 wei
miningParameters.setMinTransactionGasPrice(Wei.of(10)); miningParameters.setMinTransactionGasPrice(Wei.of(10));
@ -774,22 +792,23 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test @Test
public void decreaseOfMinGasPriceAtRuntimeIncludeTxThatWasPreviouslyNotSelected() { public void decreaseOfMinGasPriceAtRuntimeIncludeTxThatWasPreviouslyNotSelected() {
final Transaction transaction = createTransaction(0, Wei.of(7L), 100_000); final Transaction transaction = createTransaction(0, Wei.of(7L), 100_000);
transactionPool.addRemoteTransactions(List.of(transaction)); final MiningParameters miningParameters =
ImmutableMiningParameters.builder().from(defaultTestMiningParameters).build();
ensureTransactionIsValid(transaction, 0, 5);
final ProcessableBlockHeader blockHeader = createBlock(500_000); final ProcessableBlockHeader blockHeader = createBlock(500_000);
final Address miningBeneficiary = AddressHelpers.ofValue(1); final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector1 = final BlockTransactionSelector selector1 =
createBlockSelector( createBlockSelectorAndSetupTxPool(
miningParameters,
transactionProcessor, transactionProcessor,
blockHeader, blockHeader,
Wei.ZERO,
miningBeneficiary, miningBeneficiary,
Wei.ZERO, Wei.ZERO,
MIN_OCCUPANCY_80_PERCENT); NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
transactionPool.addRemoteTransactions(List.of(transaction));
ensureTransactionIsValid(transaction, 0, 5);
// raise the minGasPrice at runtime from 1 wei to 10 wei // raise the minGasPrice at runtime from 1 wei to 10 wei
miningParameters.setMinTransactionGasPrice(Wei.of(10)); miningParameters.setMinTransactionGasPrice(Wei.of(10));
@ -809,12 +828,12 @@ public abstract class AbstractBlockTransactionSelectorTest {
final BlockTransactionSelector selector2 = final BlockTransactionSelector selector2 =
createBlockSelector( createBlockSelector(
miningParameters,
transactionProcessor, transactionProcessor,
blockHeader, blockHeader,
Wei.ZERO,
miningBeneficiary, miningBeneficiary,
Wei.ZERO, Wei.ZERO,
MIN_OCCUPANCY_80_PERCENT); NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
final TransactionSelectionResults results2 = selector2.buildTransactionListForBlock(); final TransactionSelectionResults results2 = selector2.buildTransactionListForBlock();
@ -826,22 +845,25 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test @Test
public void shouldNotSelectTransactionsWithPriorityFeeLessThanConfig() { public void shouldNotSelectTransactionsWithPriorityFeeLessThanConfig() {
ProcessableBlockHeader blockHeader = createBlock(5_000_000, Wei.ONE); ProcessableBlockHeader blockHeader = createBlock(5_000_000, Wei.ONE);
final MiningParameters miningParameters =
ImmutableMiningParameters.builder().from(defaultTestMiningParameters).build();
miningParameters.setMinPriorityFeePerGas(Wei.of(7)); miningParameters.setMinPriorityFeePerGas(Wei.of(7));
final Transaction txSelected = createTransaction(1, Wei.of(8), 100_000); final Transaction txSelected = createTransaction(1, Wei.of(8), 100_000);
ensureTransactionIsValid(txSelected); ensureTransactionIsValid(txSelected);
// transaction txNotSelected should not be selected // transaction txNotSelected should not be selected
final Transaction txNotSelected = createTransaction(2, Wei.of(7), 100_000); final Transaction txNotSelected = createTransaction(2, Wei.of(7), 100_000);
ensureTransactionIsValid(txNotSelected); ensureTransactionIsValid(txNotSelected);
transactionPool.addRemoteTransactions(List.of(txSelected, txNotSelected));
final BlockTransactionSelector selector = final BlockTransactionSelector selector =
createBlockSelector( createBlockSelectorAndSetupTxPool(
miningParameters,
transactionProcessor, transactionProcessor,
blockHeader, blockHeader,
Wei.ZERO,
AddressHelpers.ofValue(1), AddressHelpers.ofValue(1),
Wei.ZERO, Wei.ZERO,
MIN_OCCUPANCY_100_PERCENT); NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
transactionPool.addRemoteTransactions(List.of(txSelected, txNotSelected));
final TransactionSelectionResults results = selector.buildTransactionListForBlock(); final TransactionSelectionResults results = selector.buildTransactionListForBlock();
@ -852,41 +874,136 @@ public abstract class AbstractBlockTransactionSelectorTest {
txNotSelected, TransactionSelectionResult.PRIORITY_FEE_PER_GAS_BELOW_CURRENT_MIN)); txNotSelected, TransactionSelectionResult.PRIORITY_FEE_PER_GAS_BELOW_CURRENT_MIN));
} }
protected BlockTransactionSelector createBlockSelector( @ParameterizedTest
final MainnetTransactionProcessor transactionProcessor, @MethodSource("subsetOfPendingTransactionsIncludedWhenTxSelectionMaxTimeIsOver")
final ProcessableBlockHeader blockHeader, public void subsetOfPendingTransactionsIncludedWhenTxSelectionMaxTimeIsOver(
final Wei minGasPrice, final boolean isPoa,
final Address miningBeneficiary, final boolean preProcessingTooLate,
final Wei blobGasPrice, final boolean processingTooLate,
final double minBlockOccupancyRatio) { final boolean postProcessingTooLate) {
final Supplier<Answer<TransactionSelectionResult>> inTime =
() -> invocation -> TransactionSelectionResult.SELECTED;
final BiFunction<Transaction, Long, Answer<TransactionSelectionResult>> tooLate =
(p, t) ->
invocation -> {
if (((PendingTransaction) invocation.getArgument(0)).getTransaction().equals(p)) {
Thread.sleep(t);
}
return TransactionSelectionResult.SELECTED;
};
final ProcessableBlockHeader blockHeader = createBlock(301_000);
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final int poaMinBlockTime = 1;
final long blockTxsSelectionMaxTime = 750;
final long longProcessingTxTime = 500;
final List<Transaction> transactionsToInject = new ArrayList<>(3);
for (int i = 0; i < 2; i++) {
final Transaction tx = createTransaction(i, Wei.of(7), 100_000);
transactionsToInject.add(tx);
ensureTransactionIsValid(tx);
}
final Transaction lateTx = createTransaction(2, Wei.of(7), 100_000);
transactionsToInject.add(lateTx);
ensureTransactionIsValid(
lateTx, 0, 0, processingTooLate ? blockTxsSelectionMaxTime + longProcessingTxTime : 0);
PluginTransactionSelector transactionSelector = mock(PluginTransactionSelector.class);
when(transactionSelector.evaluateTransactionPreProcessing(any()))
.thenAnswer(
preProcessingTooLate
? inTime.get()
: tooLate.apply(lateTx, blockTxsSelectionMaxTime + longProcessingTxTime));
when(transactionSelector.evaluateTransactionPostProcessing(any(), any()))
.thenAnswer(
postProcessingTooLate
? inTime.get()
: tooLate.apply(lateTx, blockTxsSelectionMaxTime + longProcessingTxTime));
final PluginTransactionSelectorFactory transactionSelectorFactory =
mock(PluginTransactionSelectorFactory.class);
when(transactionSelectorFactory.create()).thenReturn(transactionSelector);
final BlockTransactionSelector selector = final BlockTransactionSelector selector =
new BlockTransactionSelector( createBlockSelectorAndSetupTxPool(
miningParameters isPoa
.setMinTransactionGasPrice(minGasPrice) ? createMiningParameters(
.setMinBlockOccupancyRatio(minBlockOccupancyRatio), Wei.ZERO, MIN_OCCUPANCY_100_PERCENT, poaMinBlockTime, Percentage.fromInt(75))
: createMiningParameters(
Wei.ZERO, MIN_OCCUPANCY_100_PERCENT, blockTxsSelectionMaxTime),
transactionProcessor, transactionProcessor,
blockchain,
worldState,
transactionPool,
blockHeader, blockHeader,
this::createReceipt,
this::isCancelled,
miningBeneficiary, miningBeneficiary,
blobGasPrice, Wei.ZERO,
getFeeMarket(), transactionSelectorFactory);
new LondonGasCalculator(),
GasLimitCalculator.constant(),
AllAcceptingTransactionSelector.INSTANCE);
return selector; transactionPool.addRemoteTransactions(transactionsToInject);
final TransactionSelectionResults results = selector.buildTransactionListForBlock();
// third tx is not selected, even if it could fit in the block,
// since the selection time was over
assertThat(results.getSelectedTransactions().size()).isEqualTo(2);
assertThat(results.getSelectedTransactions().containsAll(transactionsToInject.subList(0, 2)))
.isTrue();
assertThat(results.getReceipts().size()).isEqualTo(2);
assertThat(results.getCumulativeGasUsed()).isEqualTo(200_000);
// Ensure receipts have the correct cumulative gas
assertThat(results.getReceipts().get(0).getCumulativeGasUsed()).isEqualTo(100_000);
assertThat(results.getReceipts().get(1).getCumulativeGasUsed()).isEqualTo(200_000);
// given enough time we can check the not selected tx
await().until(() -> !results.getNotSelectedTransactions().isEmpty());
assertThat(results.getNotSelectedTransactions())
.containsOnly(entry(lateTx, TransactionSelectionResult.BLOCK_SELECTION_TIMEOUT));
}
private static Stream<Arguments>
subsetOfPendingTransactionsIncludedWhenTxSelectionMaxTimeIsOver() {
return Stream.of(
Arguments.of(false, true, false, false),
Arguments.of(false, false, true, false),
Arguments.of(false, false, false, true),
Arguments.of(true, true, false, false),
Arguments.of(true, false, true, false),
Arguments.of(true, false, false, true));
}
protected BlockTransactionSelector createBlockSelectorAndSetupTxPool(
final MiningParameters miningParameters,
final MainnetTransactionProcessor transactionProcessor,
final ProcessableBlockHeader blockHeader,
final Address miningBeneficiary,
final Wei blobGasPrice,
final PluginTransactionSelectorFactory transactionSelectorFactory) {
transactionPool = createTransactionPool(miningParameters);
return createBlockSelector(
miningParameters,
transactionProcessor,
blockHeader,
miningBeneficiary,
blobGasPrice,
transactionSelectorFactory);
} }
protected BlockTransactionSelector createBlockSelectorWithTxSelPlugin( protected BlockTransactionSelector createBlockSelector(
final MiningParameters miningParameters,
final MainnetTransactionProcessor transactionProcessor, final MainnetTransactionProcessor transactionProcessor,
final ProcessableBlockHeader blockHeader, final ProcessableBlockHeader blockHeader,
final Address miningBeneficiary, final Address miningBeneficiary,
final Wei blobGasPrice, final Wei blobGasPrice,
final PluginTransactionSelectorFactory transactionSelectorFactory) { final PluginTransactionSelectorFactory transactionSelectorFactory) {
final BlockTransactionSelector selector = final BlockTransactionSelector selector =
new BlockTransactionSelector( new BlockTransactionSelector(
miningParameters, miningParameters,
@ -902,7 +1019,8 @@ public abstract class AbstractBlockTransactionSelectorTest {
getFeeMarket(), getFeeMarket(),
new LondonGasCalculator(), new LondonGasCalculator(),
GasLimitCalculator.constant(), GasLimitCalculator.constant(),
transactionSelectorFactory.create()); transactionSelectorFactory.create(),
ethScheduler);
return selector; return selector;
} }
@ -965,15 +1083,28 @@ public abstract class AbstractBlockTransactionSelectorTest {
protected void ensureTransactionIsValid( protected void ensureTransactionIsValid(
final Transaction tx, final long gasUsedByTransaction, final long gasRemaining) { final Transaction tx, final long gasUsedByTransaction, final long gasRemaining) {
ensureTransactionIsValid(tx, gasUsedByTransaction, gasRemaining, 0);
}
protected void ensureTransactionIsValid(
final Transaction tx,
final long gasUsedByTransaction,
final long gasRemaining,
final long processingTime) {
when(transactionProcessor.processTransaction( when(transactionProcessor.processTransaction(
any(), any(), any(), eq(tx), any(), any(), any(), anyBoolean(), any(), any())) any(), any(), any(), eq(tx), any(), any(), any(), anyBoolean(), any(), any()))
.thenReturn( .thenAnswer(
TransactionProcessingResult.successful( invocation -> {
new ArrayList<>(), if (processingTime > 0) {
gasUsedByTransaction, Thread.sleep(processingTime);
gasRemaining, }
Bytes.EMPTY, return TransactionProcessingResult.successful(
ValidationResult.valid())); new ArrayList<>(),
gasUsedByTransaction,
gasRemaining,
Bytes.EMPTY,
ValidationResult.valid());
});
} }
protected void ensureTransactionIsInvalid( protected void ensureTransactionIsInvalid(
@ -986,4 +1117,35 @@ public abstract class AbstractBlockTransactionSelectorTest {
private BlockHeader blockHeader(final long number) { private BlockHeader blockHeader(final long number) {
return new BlockHeaderTestFixture().number(number).buildHeader(); return new BlockHeaderTestFixture().number(number).buildHeader();
} }
protected MiningParameters createMiningParameters(
final Wei minGasPrice, final double minBlockOccupancyRatio, final long txsSelectionMaxTime) {
return ImmutableMiningParameters.builder()
.mutableInitValues(
MutableInitValues.builder()
.minTransactionGasPrice(minGasPrice)
.minBlockOccupancyRatio(minBlockOccupancyRatio)
.build())
.unstable(Unstable.builder().nonPoaBlockTxsSelectionMaxTime(txsSelectionMaxTime).build())
.build();
}
protected MiningParameters createMiningParameters(
final Wei minGasPrice,
final double minBlockOccupancyRatio,
final int minBlockTime,
final Percentage minBlockTimePercentage) {
return ImmutableMiningParameters.builder()
.mutableInitValues(
MutableInitValues.builder()
.minTransactionGasPrice(minGasPrice)
.minBlockOccupancyRatio(minBlockOccupancyRatio)
.build())
.unstable(
Unstable.builder()
.minBlockTime(minBlockTime)
.poaBlockTxsSelectionMaxTime(minBlockTimePercentage)
.build())
.build();
}
} }

@ -20,6 +20,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import org.hyperledger.besu.config.GenesisConfigFile; import org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
@ -61,7 +62,7 @@ public class LegacyFeeMarketBlockTransactionSelectorTest
} }
@Override @Override
protected TransactionPool createTransactionPool() { protected TransactionPool createTransactionPool(final MiningParameters miningParameters) {
final TransactionPoolConfiguration poolConf = final TransactionPoolConfiguration poolConf =
ImmutableTransactionPoolConfiguration.builder() ImmutableTransactionPoolConfiguration.builder()
.txPoolMaxSize(5) .txPoolMaxSize(5)

@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.blockcreation;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry; import static org.assertj.core.api.Assertions.entry;
import static org.hyperledger.besu.ethereum.core.MiningParameters.Unstable.DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import org.hyperledger.besu.config.GenesisConfigFile; import org.hyperledger.besu.config.GenesisConfigFile;
@ -24,6 +25,8 @@ import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockTransactionSelector; import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockTransactionSelector;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionSelectionResults; import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionSelectionResults;
import org.hyperledger.besu.ethereum.core.AddressHelpers; import org.hyperledger.besu.ethereum.core.AddressHelpers;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.Transaction;
@ -71,7 +74,7 @@ public class LondonFeeMarketBlockTransactionSelectorTest
} }
@Override @Override
protected TransactionPool createTransactionPool() { protected TransactionPool createTransactionPool(final MiningParameters miningParameters) {
final TransactionPoolConfiguration poolConf = final TransactionPoolConfiguration poolConf =
ImmutableTransactionPoolConfiguration.builder() ImmutableTransactionPoolConfiguration.builder()
.txPoolMaxSize(5) .txPoolMaxSize(5)
@ -105,13 +108,14 @@ public class LondonFeeMarketBlockTransactionSelectorTest
final Address miningBeneficiary = AddressHelpers.ofValue(1); final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector = final BlockTransactionSelector selector =
createBlockSelector( createBlockSelectorAndSetupTxPool(
createMiningParameters(
Wei.of(6), MIN_OCCUPANCY_80_PERCENT, DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME),
transactionProcessor, transactionProcessor,
blockHeader, blockHeader,
Wei.of(6),
miningBeneficiary, miningBeneficiary,
Wei.ZERO, Wei.ZERO,
MIN_OCCUPANCY_80_PERCENT); NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
// tx is willing to pay max 7 wei for gas, but current network condition (baseFee == 1) // tx is willing to pay max 7 wei for gas, but current network condition (baseFee == 1)
// result in it paying 2 wei, that is below the minimum accepted by the node, so it is skipped // result in it paying 2 wei, that is below the minimum accepted by the node, so it is skipped
@ -133,13 +137,14 @@ public class LondonFeeMarketBlockTransactionSelectorTest
final Address miningBeneficiary = AddressHelpers.ofValue(1); final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector = final BlockTransactionSelector selector =
createBlockSelector( createBlockSelectorAndSetupTxPool(
createMiningParameters(
Wei.of(6), MIN_OCCUPANCY_80_PERCENT, DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME),
transactionProcessor, transactionProcessor,
blockHeader, blockHeader,
Wei.of(6),
miningBeneficiary, miningBeneficiary,
Wei.ZERO, Wei.ZERO,
MIN_OCCUPANCY_80_PERCENT); NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
// tx is willing to pay max 7 wei for gas, and current network condition (baseFee == 5) // tx is willing to pay max 7 wei for gas, and current network condition (baseFee == 5)
// result in it paying the max, that is >= the minimum accepted by the node, so it is selected // result in it paying the max, that is >= the minimum accepted by the node, so it is selected
@ -160,13 +165,14 @@ public class LondonFeeMarketBlockTransactionSelectorTest
final Address miningBeneficiary = AddressHelpers.ofValue(1); final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector = final BlockTransactionSelector selector =
createBlockSelector( createBlockSelectorAndSetupTxPool(
createMiningParameters(
Wei.of(6), MIN_OCCUPANCY_80_PERCENT, DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME),
transactionProcessor, transactionProcessor,
blockHeader, blockHeader,
Wei.of(6),
miningBeneficiary, miningBeneficiary,
Wei.ZERO, Wei.ZERO,
MIN_OCCUPANCY_80_PERCENT); NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
// tx is willing to pay max 7 wei for gas, but current network condition (baseFee == 1) // tx is willing to pay max 7 wei for gas, but current network condition (baseFee == 1)
// result in it paying 2 wei, that is below the minimum accepted by the node, but since it is // result in it paying 2 wei, that is below the minimum accepted by the node, but since it is
@ -192,8 +198,6 @@ public class LondonFeeMarketBlockTransactionSelectorTest
final Transaction txFrontier2 = createTransaction(2, Wei.of(7L), 100_000); final Transaction txFrontier2 = createTransaction(2, Wei.of(7L), 100_000);
final Transaction txLondon2 = createEIP1559Transaction(3, Wei.ONE, Wei.ONE, 100_000); final Transaction txLondon2 = createEIP1559Transaction(3, Wei.ONE, Wei.ONE, 100_000);
transactionPool.addRemoteTransactions(List.of(txFrontier1, txLondon1, txFrontier2, txLondon2));
ensureTransactionIsValid(txFrontier1); ensureTransactionIsValid(txFrontier1);
ensureTransactionIsValid(txLondon1); ensureTransactionIsValid(txLondon1);
ensureTransactionIsValid(txFrontier2); ensureTransactionIsValid(txFrontier2);
@ -201,13 +205,15 @@ public class LondonFeeMarketBlockTransactionSelectorTest
final Address miningBeneficiary = AddressHelpers.ofValue(1); final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector = final BlockTransactionSelector selector =
createBlockSelector( createBlockSelectorAndSetupTxPool(
defaultTestMiningParameters,
transactionProcessor, transactionProcessor,
blockHeader, blockHeader,
Wei.ZERO,
miningBeneficiary, miningBeneficiary,
Wei.ZERO, Wei.ZERO,
MIN_OCCUPANCY_80_PERCENT); NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
transactionPool.addRemoteTransactions(List.of(txFrontier1, txLondon1, txFrontier2, txLondon2));
final TransactionSelectionResults results = selector.buildTransactionListForBlock(); final TransactionSelectionResults results = selector.buildTransactionListForBlock();
@ -220,6 +226,8 @@ public class LondonFeeMarketBlockTransactionSelectorTest
@Override @Override
public void shouldNotSelectTransactionsWithPriorityFeeLessThanConfig() { public void shouldNotSelectTransactionsWithPriorityFeeLessThanConfig() {
ProcessableBlockHeader blockHeader = createBlock(5_000_000, Wei.ONE); ProcessableBlockHeader blockHeader = createBlock(5_000_000, Wei.ONE);
final MiningParameters miningParameters =
ImmutableMiningParameters.builder().from(defaultTestMiningParameters).build();
miningParameters.setMinPriorityFeePerGas(Wei.of(7)); miningParameters.setMinPriorityFeePerGas(Wei.of(7));
final Transaction txSelected1 = createEIP1559Transaction(1, Wei.of(8), Wei.of(8), 100_000); final Transaction txSelected1 = createEIP1559Transaction(1, Wei.of(8), Wei.of(8), 100_000);
@ -237,19 +245,19 @@ public class LondonFeeMarketBlockTransactionSelectorTest
final Transaction txNotSelected2 = createEIP1559Transaction(4, Wei.of(8), Wei.of(6), 100_000); final Transaction txNotSelected2 = createEIP1559Transaction(4, Wei.of(8), Wei.of(6), 100_000);
ensureTransactionIsValid(txNotSelected2); ensureTransactionIsValid(txNotSelected2);
transactionPool.addRemoteTransactions(
List.of(txSelected1, txNotSelected1, txSelected2, txNotSelected2));
assertThat(transactionPool.getPendingTransactions().size()).isEqualTo(4);
final BlockTransactionSelector selector = final BlockTransactionSelector selector =
createBlockSelector( createBlockSelectorAndSetupTxPool(
miningParameters,
transactionProcessor, transactionProcessor,
blockHeader, blockHeader,
Wei.ZERO,
AddressHelpers.ofValue(1), AddressHelpers.ofValue(1),
Wei.ZERO, Wei.ZERO,
MIN_OCCUPANCY_100_PERCENT); NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
transactionPool.addRemoteTransactions(
List.of(txSelected1, txNotSelected1, txSelected2, txNotSelected2));
assertThat(transactionPool.getPendingTransactions().size()).isEqualTo(4);
final TransactionSelectionResults results = selector.buildTransactionListForBlock(); final TransactionSelectionResults results = selector.buildTransactionListForBlock();

@ -115,7 +115,8 @@ class PoWBlockCreatorTest extends AbstractBlockCreatorTest {
executionContextTestFixture.getProtocolContext(), executionContextTestFixture.getProtocolContext(),
executionContextTestFixture.getProtocolSchedule(), executionContextTestFixture.getProtocolSchedule(),
solver, solver,
executionContextTestFixture.getBlockchain().getChainHeadHeader()); executionContextTestFixture.getBlockchain().getChainHeadHeader(),
ethScheduler);
// A Hashrate should not exist in the block creator prior to creating a block // A Hashrate should not exist in the block creator prior to creating a block
assertThat(blockCreator.getHashesPerSecond()).isNotPresent(); assertThat(blockCreator.getHashesPerSecond()).isNotPresent();
@ -168,7 +169,8 @@ class PoWBlockCreatorTest extends AbstractBlockCreatorTest {
executionContextTestFixture.getProtocolContext(), executionContextTestFixture.getProtocolContext(),
executionContextTestFixture.getProtocolSchedule(), executionContextTestFixture.getProtocolSchedule(),
solver, solver,
executionContextTestFixture.getBlockchain().getChainHeadHeader()); executionContextTestFixture.getBlockchain().getChainHeadHeader(),
ethScheduler);
assertThat(blockCreator.createBlock(BLOCK_1_TIMESTAMP)).isNotNull(); assertThat(blockCreator.createBlock(BLOCK_1_TIMESTAMP)).isNotNull();
// If we weren't setting difficulty to 2^256-1 a difficulty of 1 would have caused a // If we weren't setting difficulty to 2^256-1 a difficulty of 1 would have caused a
@ -212,7 +214,8 @@ class PoWBlockCreatorTest extends AbstractBlockCreatorTest {
executionContextTestFixture.getProtocolContext(), executionContextTestFixture.getProtocolContext(),
executionContextTestFixture.getProtocolSchedule(), executionContextTestFixture.getProtocolSchedule(),
solver, solver,
executionContextTestFixture.getBlockchain().getChainHeadHeader()); executionContextTestFixture.getBlockchain().getChainHeadHeader(),
ethScheduler);
final MutableWorldState mutableWorldState = final MutableWorldState mutableWorldState =
executionContextTestFixture.getStateArchive().getMutable(); executionContextTestFixture.getStateArchive().getMutable();
@ -278,7 +281,8 @@ class PoWBlockCreatorTest extends AbstractBlockCreatorTest {
executionContextTestFixture.getProtocolContext(), executionContextTestFixture.getProtocolContext(),
executionContextTestFixture.getProtocolSchedule(), executionContextTestFixture.getProtocolSchedule(),
solver, solver,
executionContextTestFixture.getBlockchain().getChainHeadHeader()); executionContextTestFixture.getBlockchain().getChainHeadHeader(),
ethScheduler);
final MutableWorldState mutableWorldState = final MutableWorldState mutableWorldState =
executionContextTestFixture.getStateArchive().getMutable(); executionContextTestFixture.getStateArchive().getMutable();

@ -24,6 +24,7 @@ import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionBroadcaster; import org.hyperledger.besu.ethereum.eth.transactions.TransactionBroadcaster;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
@ -34,6 +35,7 @@ import org.hyperledger.besu.ethereum.mainnet.EpochCalculator;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.testutil.TestClock; import org.hyperledger.besu.testutil.TestClock;
import org.hyperledger.besu.util.Subscribers; import org.hyperledger.besu.util.Subscribers;
@ -44,6 +46,7 @@ import org.junit.jupiter.api.Test;
public class PoWMinerExecutorTest { public class PoWMinerExecutorTest {
private final MetricsSystem metricsSystem = new NoOpMetricsSystem(); private final MetricsSystem metricsSystem = new NoOpMetricsSystem();
private final EthScheduler ethScheduler = new DeterministicEthScheduler();
@Test @Test
public void startingMiningWithoutCoinbaseThrowsException() { public void startingMiningWithoutCoinbaseThrowsException() {
@ -58,7 +61,8 @@ public class PoWMinerExecutorTest {
transactionPool, transactionPool,
miningParameters, miningParameters,
new DefaultBlockScheduler(1, 10, TestClock.fixed()), new DefaultBlockScheduler(1, 10, TestClock.fixed()),
new EpochCalculator.DefaultEpochCalculator()); new EpochCalculator.DefaultEpochCalculator(),
ethScheduler);
assertThatExceptionOfType(CoinbaseNotSetException.class) assertThatExceptionOfType(CoinbaseNotSetException.class)
.isThrownBy(() -> executor.startAsyncMining(Subscribers.create(), Subscribers.none(), null)) .isThrownBy(() -> executor.startAsyncMining(Subscribers.create(), Subscribers.none(), null))
@ -78,7 +82,8 @@ public class PoWMinerExecutorTest {
transactionPool, transactionPool,
miningParameters, miningParameters,
new DefaultBlockScheduler(1, 10, TestClock.fixed()), new DefaultBlockScheduler(1, 10, TestClock.fixed()),
new EpochCalculator.DefaultEpochCalculator()); new EpochCalculator.DefaultEpochCalculator(),
ethScheduler);
assertThatExceptionOfType(IllegalArgumentException.class) assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> executor.setCoinbase(null)) .isThrownBy(() -> executor.setCoinbase(null))

@ -16,12 +16,16 @@ package org.hyperledger.besu.ethereum.core;
import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.util.number.Percentage;
import java.time.Duration; import java.time.Duration;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalInt;
import java.util.OptionalLong; import java.util.OptionalLong;
import java.util.concurrent.TimeUnit;
import com.google.common.annotations.VisibleForTesting;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
import org.immutables.value.Value; import org.immutables.value.Value;
@ -34,6 +38,7 @@ public abstract class MiningParameters {
ImmutableMiningParameters.MutableInitValues.builder().isMiningEnabled(false).build()) ImmutableMiningParameters.MutableInitValues.builder().isMiningEnabled(false).build())
.build(); .build();
@VisibleForTesting
public static final MiningParameters newDefault() { public static final MiningParameters newDefault() {
return ImmutableMiningParameters.builder().build(); return ImmutableMiningParameters.builder().build();
} }
@ -261,6 +266,8 @@ public abstract class MiningParameters {
int DEFAULT_MAX_OMMERS_DEPTH = 8; int DEFAULT_MAX_OMMERS_DEPTH = 8;
long DEFAULT_POS_BLOCK_CREATION_MAX_TIME = Duration.ofSeconds(12).toMillis(); long DEFAULT_POS_BLOCK_CREATION_MAX_TIME = Duration.ofSeconds(12).toMillis();
long DEFAULT_POS_BLOCK_CREATION_REPETITION_MIN_DURATION = Duration.ofMillis(500).toMillis(); long DEFAULT_POS_BLOCK_CREATION_REPETITION_MIN_DURATION = Duration.ofMillis(500).toMillis();
long DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME = Duration.ofSeconds(5).toMillis();
Percentage DEFAULT_POA_BLOCK_TXS_SELECTION_MAX_TIME = Percentage.fromInt(75);
MiningParameters.Unstable DEFAULT = ImmutableMiningParameters.Unstable.builder().build(); MiningParameters.Unstable DEFAULT = ImmutableMiningParameters.Unstable.builder().build();
@ -298,5 +305,27 @@ public abstract class MiningParameters {
default String getStratumExtranonce() { default String getStratumExtranonce() {
return "080c"; return "080c";
} }
@Value.Default
default long getNonPoaBlockTxsSelectionMaxTime() {
return DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME;
}
@Value.Default
default Percentage getPoaBlockTxsSelectionMaxTime() {
return DEFAULT_POA_BLOCK_TXS_SELECTION_MAX_TIME;
}
OptionalInt getMinBlockTime();
@Value.Derived
default long getBlockTxsSelectionMaxTime() {
if (getMinBlockTime().isPresent()) {
return (TimeUnit.SECONDS.toMillis(getMinBlockTime().getAsInt())
* getPoaBlockTxsSelectionMaxTime().getValue())
/ 100;
}
return getNonPoaBlockTxsSelectionMaxTime();
}
} }
} }

@ -49,6 +49,7 @@ import org.hyperledger.besu.ethereum.core.SealableBlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture; import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction; import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions; import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions;
@ -72,6 +73,7 @@ import org.hyperledger.besu.plugin.services.BesuConfiguration;
import org.hyperledger.besu.plugin.services.storage.rocksdb.RocksDBKeyValueStorageFactory; import org.hyperledger.besu.plugin.services.storage.rocksdb.RocksDBKeyValueStorageFactory;
import org.hyperledger.besu.plugin.services.storage.rocksdb.RocksDBMetricsFactory; import org.hyperledger.besu.plugin.services.storage.rocksdb.RocksDBMetricsFactory;
import org.hyperledger.besu.plugin.services.storage.rocksdb.configuration.RocksDBFactoryConfiguration; import org.hyperledger.besu.plugin.services.storage.rocksdb.configuration.RocksDBFactoryConfiguration;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Arrays; import java.util.Arrays;
@ -92,6 +94,7 @@ public abstract class AbstractIsolationTests {
protected BonsaiWorldStateKeyValueStorage bonsaiWorldStateStorage; protected BonsaiWorldStateKeyValueStorage bonsaiWorldStateStorage;
protected ProtocolContext protocolContext; protected ProtocolContext protocolContext;
protected EthContext ethContext; protected EthContext ethContext;
protected EthScheduler ethScheduler = new DeterministicEthScheduler();
final Function<String, KeyPair> asKeyPair = final Function<String, KeyPair> asKeyPair =
key -> key ->
SignatureAlgorithmFactory.getInstance() SignatureAlgorithmFactory.getInstance()
@ -214,7 +217,8 @@ public abstract class AbstractIsolationTests {
final TransactionPool transactionPool, final TransactionPool transactionPool,
final ProtocolContext protocolContext, final ProtocolContext protocolContext,
final ProtocolSchedule protocolSchedule, final ProtocolSchedule protocolSchedule,
final BlockHeader parentHeader) { final BlockHeader parentHeader,
final EthScheduler ethScheduler) {
super( super(
miningParameters, miningParameters,
miningBeneficiaryCalculator, miningBeneficiaryCalculator,
@ -223,14 +227,16 @@ public abstract class AbstractIsolationTests {
protocolContext, protocolContext,
protocolSchedule, protocolSchedule,
parentHeader, parentHeader,
Optional.empty()); Optional.empty(),
ethScheduler);
} }
static TestBlockCreator forHeader( static TestBlockCreator forHeader(
final BlockHeader parentHeader, final BlockHeader parentHeader,
final ProtocolContext protocolContext, final ProtocolContext protocolContext,
final ProtocolSchedule protocolSchedule, final ProtocolSchedule protocolSchedule,
final TransactionPool transactionPool) { final TransactionPool transactionPool,
final EthScheduler ethScheduler) {
final MiningParameters miningParameters = final MiningParameters miningParameters =
ImmutableMiningParameters.builder() ImmutableMiningParameters.builder()
@ -251,7 +257,8 @@ public abstract class AbstractIsolationTests {
transactionPool, transactionPool,
protocolContext, protocolContext,
protocolSchedule, protocolSchedule,
parentHeader); parentHeader,
ethScheduler);
} }
@Override @Override
@ -282,7 +289,8 @@ public abstract class AbstractIsolationTests {
protected Block forTransactions( protected Block forTransactions(
final List<Transaction> transactions, final BlockHeader forHeader) { final List<Transaction> transactions, final BlockHeader forHeader) {
return TestBlockCreator.forHeader(forHeader, protocolContext, protocolSchedule, transactionPool) return TestBlockCreator.forHeader(
forHeader, protocolContext, protocolSchedule, transactionPool, ethScheduler)
.createBlock(transactions, Collections.emptyList(), System.currentTimeMillis()) .createBlock(transactions, Collections.emptyList(), System.currentTimeMillis())
.getBlock(); .getBlock();
} }

@ -53,6 +53,7 @@ public class EthScheduler {
protected final ExecutorService txWorkerExecutor; protected final ExecutorService txWorkerExecutor;
protected final ExecutorService servicesExecutor; protected final ExecutorService servicesExecutor;
protected final ExecutorService computationExecutor; protected final ExecutorService computationExecutor;
protected final ExecutorService blockCreationExecutor;
private final Collection<CompletableFuture<?>> pendingFutures = new ConcurrentLinkedDeque<>(); private final Collection<CompletableFuture<?>> pendingFutures = new ConcurrentLinkedDeque<>();
@ -87,7 +88,9 @@ public class EthScheduler {
EthScheduler.class.getSimpleName() + "-Computation", EthScheduler.class.getSimpleName() + "-Computation",
1, 1,
computationWorkerCount, computationWorkerCount,
metricsSystem)); metricsSystem),
MonitoredExecutors.newCachedThreadPool(
EthScheduler.class.getSimpleName() + "-BlockCreation", metricsSystem));
} }
protected EthScheduler( protected EthScheduler(
@ -95,12 +98,14 @@ public class EthScheduler {
final ScheduledExecutorService scheduler, final ScheduledExecutorService scheduler,
final ExecutorService txWorkerExecutor, final ExecutorService txWorkerExecutor,
final ExecutorService servicesExecutor, final ExecutorService servicesExecutor,
final ExecutorService computationExecutor) { final ExecutorService computationExecutor,
final ExecutorService blockCreationExecutor) {
this.syncWorkerExecutor = syncWorkerExecutor; this.syncWorkerExecutor = syncWorkerExecutor;
this.scheduler = scheduler; this.scheduler = scheduler;
this.txWorkerExecutor = txWorkerExecutor; this.txWorkerExecutor = txWorkerExecutor;
this.servicesExecutor = servicesExecutor; this.servicesExecutor = servicesExecutor;
this.computationExecutor = computationExecutor; this.computationExecutor = computationExecutor;
this.blockCreationExecutor = blockCreationExecutor;
} }
public <T> CompletableFuture<T> scheduleSyncWorkerTask( public <T> CompletableFuture<T> scheduleSyncWorkerTask(
@ -202,6 +207,10 @@ public class EthScheduler {
return promise; return promise;
} }
public CompletableFuture<Void> scheduleBlockCreationTask(final Runnable task) {
return CompletableFuture.runAsync(task, blockCreationExecutor);
}
public <T> CompletableFuture<T> timeout(final EthTask<T> task) { public <T> CompletableFuture<T> timeout(final EthTask<T> task) {
return timeout(task, defaultTimeout); return timeout(task, defaultTimeout);
} }

@ -1088,8 +1088,9 @@ public final class EthProtocolManagerTest {
final ExecutorService transactions = mock(ExecutorService.class); final ExecutorService transactions = mock(ExecutorService.class);
final ExecutorService services = mock(ExecutorService.class); final ExecutorService services = mock(ExecutorService.class);
final ExecutorService computations = mock(ExecutorService.class); final ExecutorService computations = mock(ExecutorService.class);
final ExecutorService blockCreation = mock(ExecutorService.class);
final EthScheduler ethScheduler = final EthScheduler ethScheduler =
new EthScheduler(worker, scheduled, transactions, services, computations); new EthScheduler(worker, scheduled, transactions, services, computations, blockCreation);
// Create the fake TransactionMessage to feed to the EthManager. // Create the fake TransactionMessage to feed to the EthManager.
final BlockDataGenerator gen = new BlockDataGenerator(1); final BlockDataGenerator gen = new BlockDataGenerator(1);

@ -27,7 +27,6 @@ import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.ProtocolScheduleFixture; import org.hyperledger.besu.ethereum.core.ProtocolScheduleFixture;
import org.hyperledger.besu.ethereum.eth.EthProtocol; import org.hyperledger.besu.ethereum.eth.EthProtocol;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration; import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.DeterministicEthScheduler.TimeoutPolicy;
import org.hyperledger.besu.ethereum.eth.manager.snap.SnapProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.snap.SnapProtocolManager;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration;
@ -39,6 +38,8 @@ import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.ethereum.worldstate.DataStorageFormat; import org.hyperledger.besu.ethereum.worldstate.DataStorageFormat;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.testutil.DeterministicEthScheduler.TimeoutPolicy;
import org.hyperledger.besu.testutil.TestClock; import org.hyperledger.besu.testutil.TestClock;
import java.math.BigInteger; import java.math.BigInteger;

@ -32,6 +32,7 @@ public class EthSchedulerShutdownTest {
private ExecutorService txWorkerExecutor; private ExecutorService txWorkerExecutor;
private ExecutorService servicesExecutor; private ExecutorService servicesExecutor;
private ExecutorService computationExecutor; private ExecutorService computationExecutor;
private ExecutorService blockCreationExecutor;
@BeforeEach @BeforeEach
public void setup() { public void setup() {
@ -40,13 +41,15 @@ public class EthSchedulerShutdownTest {
txWorkerExecutor = Executors.newSingleThreadExecutor(); txWorkerExecutor = Executors.newSingleThreadExecutor();
servicesExecutor = Executors.newSingleThreadExecutor(); servicesExecutor = Executors.newSingleThreadExecutor();
computationExecutor = Executors.newSingleThreadExecutor(); computationExecutor = Executors.newSingleThreadExecutor();
blockCreationExecutor = Executors.newSingleThreadExecutor();
ethScheduler = ethScheduler =
new EthScheduler( new EthScheduler(
syncWorkerExecutor, syncWorkerExecutor,
scheduledExecutor, scheduledExecutor,
txWorkerExecutor, txWorkerExecutor,
servicesExecutor, servicesExecutor,
computationExecutor); computationExecutor,
blockCreationExecutor);
} }
@Test @Test

@ -21,7 +21,9 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.testutil.MockExecutorService; import org.hyperledger.besu.testutil.MockExecutorService;
import org.hyperledger.besu.testutil.MockScheduledExecutor;
import java.time.Duration; import java.time.Duration;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;

@ -28,7 +28,6 @@ import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.eth.EthProtocol; import org.hyperledger.besu.ethereum.eth.EthProtocol;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration; import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.DeterministicEthScheduler;
import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthMessages; import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer; import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
@ -46,6 +45,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.worldstate.DataStorageFormat; import org.hyperledger.besu.ethereum.worldstate.DataStorageFormat;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.testutil.TestClock; import org.hyperledger.besu.testutil.TestClock;
import java.time.ZoneId; import java.time.ZoneId;

@ -22,7 +22,6 @@ import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator; import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator.BlockOptions; import org.hyperledger.besu.ethereum.core.BlockDataGenerator.BlockOptions;
import org.hyperledger.besu.ethereum.eth.manager.DeterministicEthScheduler;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer; import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
@ -30,6 +29,7 @@ import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
import org.hyperledger.besu.ethereum.eth.messages.BlockHeadersMessage; import org.hyperledger.besu.ethereum.eth.messages.BlockHeadersMessage;
import org.hyperledger.besu.ethereum.eth.messages.EthPV62; import org.hyperledger.besu.ethereum.eth.messages.EthPV62;
import org.hyperledger.besu.ethereum.eth.messages.GetBlockHeadersMessage; import org.hyperledger.besu.ethereum.eth.messages.GetBlockHeadersMessage;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;

@ -29,7 +29,6 @@ import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator; import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.TransactionReceipt; import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.eth.manager.DeterministicEthScheduler;
import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
@ -39,6 +38,7 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;

@ -34,7 +34,6 @@ import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.ProtocolScheduleFixture; import org.hyperledger.besu.ethereum.core.ProtocolScheduleFixture;
import org.hyperledger.besu.ethereum.eth.manager.DeterministicEthScheduler;
import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
@ -68,6 +67,7 @@ import org.hyperledger.besu.evm.worldstate.WorldState;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
import org.hyperledger.besu.services.tasks.InMemoryTasksPriorityQueues; import org.hyperledger.besu.services.tasks.InMemoryTasksPriorityQueues;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.testutil.MockExecutorService; import org.hyperledger.besu.testutil.MockExecutorService;
import org.hyperledger.besu.testutil.TestClock; import org.hyperledger.besu.testutil.TestClock;

@ -22,11 +22,11 @@ import static org.mockito.Mockito.when;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.eth.manager.DeterministicEthScheduler;
import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.sync.fastsync.FastSyncActions; import org.hyperledger.besu.ethereum.eth.sync.fastsync.FastSyncActions;
import org.hyperledger.besu.ethereum.eth.sync.fastsync.FastSyncState; import org.hyperledger.besu.ethereum.eth.sync.fastsync.FastSyncState;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState; import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import java.util.OptionalLong; import java.util.OptionalLong;

@ -290,6 +290,14 @@ public class RetestethContext {
return protocolContext; return protocolContext;
} }
public EthScheduler getEthScheduler() {
return ethScheduler;
}
public void setEthScheduler(final EthScheduler ethScheduler) {
this.ethScheduler = ethScheduler;
}
public long getBlockHeight() { public long getBlockHeight() {
return blockchain.getChainHeadBlockNumber(); return blockchain.getChainHeadBlockNumber();
} }

@ -69,7 +69,8 @@ public class TestMineBlocks implements JsonRpcMethod {
protocolContext, protocolContext,
protocolSchedule, protocolSchedule,
context.getEthHashSolver(), context.getEthHashSolver(),
blockchain.getChainHeadHeader()); blockchain.getChainHeadHeader(),
context.getEthScheduler());
final Block block = final Block block =
blockCreator.createBlock(retesethClock.instant().getEpochSecond()).getBlock(); blockCreator.createBlock(retesethClock.instant().getEpochSecond()).getBlock();

@ -69,7 +69,7 @@ Calculated : ${currentHash}
tasks.register('checkAPIChanges', FileStateChecker) { tasks.register('checkAPIChanges', FileStateChecker) {
description = "Checks that the API for the Plugin-API project does not change without deliberate thought" description = "Checks that the API for the Plugin-API project does not change without deliberate thought"
files = sourceSets.main.allJava.files files = sourceSets.main.allJava.files
knownHash = 'n4WfeMvltN4XWGtztd8ABjSU2TLiI3tk5yABivkgsFA=' knownHash = '7Aj0APsKs1wBVqaWQFdEs85/MNKxTiVzyjIeZ+zCWlw='
} }
check.dependsOn('checkAPIChanges') check.dependsOn('checkAPIChanges')

@ -28,6 +28,7 @@ public class TransactionSelectionResult {
SELECTED, SELECTED,
BLOCK_FULL(true, false), BLOCK_FULL(true, false),
BLOCK_OCCUPANCY_ABOVE_THRESHOLD(true, false), BLOCK_OCCUPANCY_ABOVE_THRESHOLD(true, false),
BLOCK_SELECTION_TIMEOUT(true, false),
INVALID_TRANSIENT(false, false), INVALID_TRANSIENT(false, false),
INVALID(false, true); INVALID(false, true);
@ -56,6 +57,11 @@ public class TransactionSelectionResult {
/** The transaction has not been selected since the block is full. */ /** The transaction has not been selected since the block is full. */
public static final TransactionSelectionResult BLOCK_FULL = public static final TransactionSelectionResult BLOCK_FULL =
new TransactionSelectionResult(Status.BLOCK_FULL); new TransactionSelectionResult(Status.BLOCK_FULL);
/** There was no more time to add transaction to the block */
public static final TransactionSelectionResult BLOCK_SELECTION_TIMEOUT =
new TransactionSelectionResult(Status.BLOCK_SELECTION_TIMEOUT);
;
/** /**
* The transaction has not been selected since too large and the occupancy of the block is enough * The transaction has not been selected since too large and the occupancy of the block is enough
* to stop the selection. * to stop the selection.

@ -28,6 +28,7 @@ jar {
} }
dependencies { dependencies {
implementation project(':ethereum:eth')
implementation project(':plugin-api') implementation project(':plugin-api')
implementation project(':util') implementation project(':util')

@ -12,9 +12,9 @@
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
package org.hyperledger.besu.ethereum.eth.manager; package org.hyperledger.besu.testutil;
import org.hyperledger.besu.testutil.MockExecutorService; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import java.time.Duration; import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
@ -32,16 +32,23 @@ public class DeterministicEthScheduler extends EthScheduler {
private final List<MockExecutorService> executors; private final List<MockExecutorService> executors;
private final List<PendingTimeout<?>> pendingTimeouts = new ArrayList<>(); private final List<PendingTimeout<?>> pendingTimeouts = new ArrayList<>();
/** Create a new deterministic scheduler that never timeouts */
public DeterministicEthScheduler() { public DeterministicEthScheduler() {
this(TimeoutPolicy.NEVER_TIMEOUT); this(TimeoutPolicy.NEVER_TIMEOUT);
} }
/**
* Create a new deterministic scheduler with the provided timeout policy
*
* @param timeoutPolicy the timeout policy
*/
public DeterministicEthScheduler(final TimeoutPolicy timeoutPolicy) { public DeterministicEthScheduler(final TimeoutPolicy timeoutPolicy) {
super( super(
new MockExecutorService(), new MockExecutorService(),
new MockScheduledExecutor(), new MockScheduledExecutor(),
new MockExecutorService(), new MockExecutorService(),
new MockExecutorService(), new MockExecutorService(),
new MockExecutorService(),
new MockExecutorService()); new MockExecutorService());
this.timeoutPolicy = timeoutPolicy; this.timeoutPolicy = timeoutPolicy;
@ -51,40 +58,72 @@ public class DeterministicEthScheduler extends EthScheduler {
(MockExecutorService) this.scheduler, (MockExecutorService) this.scheduler,
(MockExecutorService) this.txWorkerExecutor, (MockExecutorService) this.txWorkerExecutor,
(MockExecutorService) this.servicesExecutor, (MockExecutorService) this.servicesExecutor,
(MockExecutorService) this.computationExecutor); (MockExecutorService) this.computationExecutor,
(MockExecutorService) this.blockCreationExecutor);
} }
// Test utility for running pending futures /** Test utility for manually running pending futures, when autorun is disabled */
public void runPendingFutures() { public void runPendingFutures() {
executors.forEach(MockExecutorService::runPendingFutures); executors.forEach(MockExecutorService::runPendingFutures);
} }
/**
* Get the count of pending tasks
*
* @return the count of pending tasks
*/
public long getPendingFuturesCount() { public long getPendingFuturesCount() {
return executors.stream().mapToLong(MockExecutorService::getPendingFuturesCount).sum(); return executors.stream().mapToLong(MockExecutorService::getPendingFuturesCount).sum();
} }
/** Expire all pending timeouts */
public void expirePendingTimeouts() { public void expirePendingTimeouts() {
final List<PendingTimeout<?>> toExpire = new ArrayList<>(pendingTimeouts); final List<PendingTimeout<?>> toExpire = new ArrayList<>(pendingTimeouts);
pendingTimeouts.clear(); pendingTimeouts.clear();
toExpire.forEach(PendingTimeout::expire); toExpire.forEach(PendingTimeout::expire);
} }
/** Do not automatically run submitted tasks. Tasks can be later run using runPendingFutures */
public void disableAutoRun() { public void disableAutoRun() {
executors.forEach(e -> e.setAutoRun(false)); executors.forEach(e -> e.setAutoRun(false));
} }
MockExecutorService mockSyncWorkerExecutor() { /**
* Get the sync worker mock executor
*
* @return the mock executor
*/
public MockExecutorService mockSyncWorkerExecutor() {
return (MockExecutorService) syncWorkerExecutor; return (MockExecutorService) syncWorkerExecutor;
} }
MockScheduledExecutor mockScheduledExecutor() { /**
* Get the scheduled mock executor
*
* @return the mock executor
*/
public MockScheduledExecutor mockScheduledExecutor() {
return (MockScheduledExecutor) scheduler; return (MockScheduledExecutor) scheduler;
} }
/**
* Get the service mock executor
*
* @return the mock executor
*/
public MockExecutorService mockServiceExecutor() { public MockExecutorService mockServiceExecutor() {
return (MockExecutorService) servicesExecutor; return (MockExecutorService) servicesExecutor;
} }
/**
* Get the block creation mock executor
*
* @return the mock executor
*/
public MockExecutorService mockBlockCreationExecutor() {
return (MockExecutorService) blockCreationExecutor;
}
@Override @Override
public <T> void failAfterTimeout(final CompletableFuture<T> promise, final Duration timeout) { public <T> void failAfterTimeout(final CompletableFuture<T> promise, final Duration timeout) {
final PendingTimeout<T> pendingTimeout = new PendingTimeout<>(promise, timeout); final PendingTimeout<T> pendingTimeout = new PendingTimeout<>(promise, timeout);
@ -95,13 +134,27 @@ public class DeterministicEthScheduler extends EthScheduler {
} }
} }
/** Used to define the timeout behavior of the scheduler */
@FunctionalInterface @FunctionalInterface
public interface TimeoutPolicy { public interface TimeoutPolicy {
/** A policy that never timeouts */
TimeoutPolicy NEVER_TIMEOUT = () -> false; TimeoutPolicy NEVER_TIMEOUT = () -> false;
/** A policy that timeouts on every task */
TimeoutPolicy ALWAYS_TIMEOUT = () -> true; TimeoutPolicy ALWAYS_TIMEOUT = () -> true;
/**
* If it should simulate a timeout when called
*
* @return true if the scheduler should timeouts
*/
boolean shouldTimeout(); boolean shouldTimeout();
/**
* Create a timeout policy that timeouts x times
*
* @param times the number of timeouts
* @return the timeout policy
*/
static TimeoutPolicy timeoutXTimes(final int times) { static TimeoutPolicy timeoutXTimes(final int times) {
final AtomicInteger timeouts = new AtomicInteger(times); final AtomicInteger timeouts = new AtomicInteger(times);
return () -> { return () -> {

@ -12,9 +12,7 @@
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
package org.hyperledger.besu.ethereum.eth.manager; package org.hyperledger.besu.testutil;
import org.hyperledger.besu.testutil.MockExecutorService;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.Delayed; import java.util.concurrent.Delayed;
@ -25,6 +23,7 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
/** The mock scheduled executor */
public class MockScheduledExecutor extends MockExecutorService implements ScheduledExecutorService { public class MockScheduledExecutor extends MockExecutorService implements ScheduledExecutorService {
@Override @Override
Loading…
Cancel
Save