Merge branch 'main' into 5561migrate-tests-to-Junit5

5561migrate-tests-to-Junit5
Sally MacFarlane 1 year ago committed by GitHub
commit 9f3fe28391
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      CHANGELOG.md
  2. 68
      besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
  3. 70
      besu/src/main/java/org/hyperledger/besu/cli/options/MiningOptions.java
  4. 68
      besu/src/main/java/org/hyperledger/besu/cli/options/TransactionPoolOptions.java
  5. 94
      besu/src/main/java/org/hyperledger/besu/cli/options/unstable/TransactionPoolOptions.java
  6. 13
      besu/src/main/java/org/hyperledger/besu/cli/util/CommandLineUtils.java
  7. 3
      besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java
  8. 3
      besu/src/main/java/org/hyperledger/besu/controller/IbftBesuControllerBuilder.java
  9. 3
      besu/src/main/java/org/hyperledger/besu/controller/MainnetBesuControllerBuilder.java
  10. 18
      besu/src/main/java/org/hyperledger/besu/controller/MergeBesuControllerBuilder.java
  11. 3
      besu/src/main/java/org/hyperledger/besu/controller/QbftBesuControllerBuilder.java
  12. 2
      besu/src/main/java/org/hyperledger/besu/controller/TransitionBesuControllerBuilder.java
  13. 53
      besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java
  14. 11
      besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java
  15. 76
      besu/src/test/java/org/hyperledger/besu/cli/options/MiningOptionsTest.java
  16. 55
      besu/src/test/java/org/hyperledger/besu/cli/options/TransactionPoolOptionsTest.java
  17. 105
      besu/src/test/java/org/hyperledger/besu/cli/options/unstable/TransactionPoolOptionsTest.java
  18. 9
      besu/src/test/java/org/hyperledger/besu/controller/TransitionControllerBuilderTest.java
  19. 8
      consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreator.java
  20. 16
      consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutor.java
  21. 13
      consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreatorTest.java
  22. 12
      consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java
  23. 8
      consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/blockcreation/BftBlockCreator.java
  24. 11
      consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/blockcreation/BftBlockCreatorFactory.java
  25. 7
      consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/support/TestContextBuilder.java
  26. 4
      consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/BftBlockCreatorTest.java
  27. 7
      consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeBlockCreator.java
  28. 34
      consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeCoordinator.java
  29. 23
      consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeCoordinatorTest.java
  30. 7
      consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeReorgTest.java
  31. 7
      consensus/qbft/src/integration-test/java/org/hyperledger/besu/consensus/qbft/support/TestContextBuilder.java
  32. 8
      consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/blockcreation/QbftBlockCreatorFactory.java
  33. 4
      consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/blockcreation/QbftBlockCreatorFactoryTest.java
  34. 10
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java
  35. 9
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractMinerExecutor.java
  36. 7
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/PoWBlockCreator.java
  37. 15
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/PoWMinerExecutor.java
  38. 134
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java
  39. 16
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/TransactionSelectionResults.java
  40. 12
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreatorTest.java
  41. 426
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java
  42. 3
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LegacyFeeMarketBlockTransactionSelectorTest.java
  43. 54
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LondonFeeMarketBlockTransactionSelectorTest.java
  44. 12
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWBlockCreatorTest.java
  45. 9
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWMinerExecutorTest.java
  46. 7
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/BadBlockManager.java
  47. 29
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/MiningParameters.java
  48. 18
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/AbstractIsolationTests.java
  49. 13
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthScheduler.java
  50. 14
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/BackwardSyncContext.java
  51. 7
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionReplacementByFeeMarketRule.java
  52. 2
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionReplacementByGasPriceRule.java
  53. 4
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/GasPricePendingTransactionsSorter.java
  54. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTest.java
  55. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTestUtil.java
  56. 5
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthSchedulerShutdownTest.java
  57. 2
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthSchedulerTest.java
  58. 2
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/ethtaskutils/AbstractMessageTaskTest.java
  59. 2
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/peervalidation/AbstractPeerBlockValidatorTest.java
  60. 78
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/BackwardSyncContextTest.java
  61. 2
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/BackwardSyncStepTest.java
  62. 2
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/worldstate/FastWorldStateDownloaderTest.java
  63. 2
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/DynamicPivotBlockManagerTest.java
  64. 175
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionPoolTest.java
  65. 53
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionReplacementTest.java
  66. 100
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionReplacementByFeeMarketRuleTest.java
  67. 87
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionReplacementByGasPriceRuleTest.java
  68. 56
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionReplacementRulesTest.java
  69. 7
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/LegacyTransactionPoolBaseFeeTest.java
  70. 7
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/LegacyTransactionPoolGasPriceTest.java
  71. 8
      ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java
  72. 3
      ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/methods/TestMineBlocks.java
  73. 2
      plugin-api/build.gradle
  74. 6
      plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionSelectionResult.java
  75. 1
      testutil/build.gradle
  76. 65
      testutil/src/main/java/org/hyperledger/besu/testutil/DeterministicEthScheduler.java
  77. 5
      testutil/src/main/java/org/hyperledger/besu/testutil/MockScheduledExecutor.java
  78. 2
      util/src/main/java/org/hyperledger/besu/util/number/Percentage.java

@ -17,12 +17,14 @@
- Clique config option `createemptyblocks` to not create empty blocks [#6082](https://github.com/hyperledger/besu/pull/6082)
- 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)
- 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
- Upgrade netty to address CVE-2023-44487, CVE-2023-34462 [#6100](https://github.com/hyperledger/besu/pull/6100)
- Upgrade grpc to address CVE-2023-32731, CVE-2023-33953, CVE-2023-44487, CVE-2023-4785 [#6100](https://github.com/hyperledger/besu/pull/6100)
- Fix blob gas calculation in reference tests [#6107](https://github.com/hyperledger/besu/pull/6107)
- Limit memory used in handling invalid blocks [#6138](https://github.com/hyperledger/besu/pull/6138)
---
@ -33,8 +35,8 @@
### 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)
- 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)
- 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)
- 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)
- 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)
- Add GraphQL support for new fields in Cancun [#5923](https://github.com/hyperledger/besu/pull/5923) [#5975](https://github.com/hyperledger/besu/pull/5975)

@ -54,12 +54,12 @@ import org.hyperledger.besu.cli.custom.RpcAuthFileValidator;
import org.hyperledger.besu.cli.error.BesuExecutionExceptionHandler;
import org.hyperledger.besu.cli.error.BesuParameterExceptionHandler;
import org.hyperledger.besu.cli.options.MiningOptions;
import org.hyperledger.besu.cli.options.TransactionPoolOptions;
import org.hyperledger.besu.cli.options.stable.DataStorageOptions;
import org.hyperledger.besu.cli.options.stable.EthstatsOptions;
import org.hyperledger.besu.cli.options.stable.LoggingLevelOption;
import org.hyperledger.besu.cli.options.stable.NodePrivateKeyFileOption;
import org.hyperledger.besu.cli.options.stable.P2PTLSConfigOptions;
import org.hyperledger.besu.cli.options.stable.TransactionPoolOptions;
import org.hyperledger.besu.cli.options.unstable.ChainPruningOptions;
import org.hyperledger.besu.cli.options.unstable.DnsOptions;
import org.hyperledger.besu.cli.options.unstable.EthProtocolOptions;
@ -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.TlsConfiguration;
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.PrivacyParameters;
import org.hyperledger.besu.ethereum.eth.sync.SyncMode;
@ -283,9 +284,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
final SynchronizerOptions unstableSynchronizerOptions = SynchronizerOptions.create();
final EthProtocolOptions unstableEthProtocolOptions = EthProtocolOptions.create();
final MetricsCLIOptions unstableMetricsCLIOptions = MetricsCLIOptions.create();
final org.hyperledger.besu.cli.options.unstable.TransactionPoolOptions
unstableTransactionPoolOptions =
org.hyperledger.besu.cli.options.unstable.TransactionPoolOptions.create();
private final DnsOptions unstableDnsOptions = DnsOptions.create();
private final NatOptions unstableNatOptions = NatOptions.create();
private final NativeLibraryOptions unstableNativeLibraryOptions = NativeLibraryOptions.create();
@ -303,8 +301,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
private final LoggingLevelOption loggingLevelOption = LoggingLevelOption.create();
@CommandLine.ArgGroup(validate = false, heading = "@|bold Tx Pool Common Options|@%n")
final org.hyperledger.besu.cli.options.stable.TransactionPoolOptions
stableTransactionPoolOptions = TransactionPoolOptions.create();
final TransactionPoolOptions transactionPoolOptions = TransactionPoolOptions.create();
@CommandLine.ArgGroup(validate = false, heading = "@|bold Block Builder Options|@%n")
final MiningOptions miningOptions = MiningOptions.create();
@ -1525,7 +1522,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.put("NAT Configuration", unstableNatOptions)
.put("Privacy Plugin Configuration", unstablePrivacyPluginOptions)
.put("Synchronizer", unstableSynchronizerOptions)
.put("TransactionPool", unstableTransactionPoolOptions)
.put("Native Library", unstableNativeLibraryOptions)
.put("EVM Options", unstableEvmOptions)
.put("IPC Options", unstableIpcOptions)
@ -1794,7 +1790,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
}
private void validateTransactionPoolOptions() {
stableTransactionPoolOptions.validate(commandLine);
transactionPoolOptions.validate(commandLine, getActualGenesisConfigOptions());
}
private void validateRequiredOptions() {
@ -1811,8 +1807,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
}
private void validateMiningParams() {
miningOptions.validate(
commandLine, logger, isMergeEnabled(), getActualGenesisConfigOptions().isEthHash());
miningOptions.validate(commandLine, getActualGenesisConfigOptions(), isMergeEnabled(), logger);
}
/**
@ -2811,21 +2806,60 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
}
private TransactionPoolConfiguration buildTransactionPoolConfiguration() {
final var stableTxPoolOption = stableTransactionPoolOptions.toDomainObject();
return ImmutableTransactionPoolConfiguration.builder()
.from(stableTxPoolOption)
.unstable(unstableTransactionPoolOptions.toDomainObject())
.saveFile((dataPath.resolve(stableTxPoolOption.getSaveFile().getPath()).toFile()))
.build();
final var txPoolConf = transactionPoolOptions.toDomainObject();
final var txPoolConfBuilder =
ImmutableTransactionPoolConfiguration.builder()
.from(txPoolConf)
.saveFile((dataPath.resolve(txPoolConf.getSaveFile().getPath()).toFile()));
if (getActualGenesisConfigOptions().isZeroBaseFee()) {
logger.info(
"Forcing price bump for transaction replacement to 0, since we are on a zero basefee network");
txPoolConfBuilder.priceBump(Percentage.ZERO);
}
if (getMiningParameters().getMinTransactionGasPrice().equals(Wei.ZERO)
&& !transactionPoolOptions.isPriceBumpSet(commandLine)) {
logger.info(
"Forcing price bump for transaction replacement to 0, since min-gas-price is set to 0");
txPoolConfBuilder.priceBump(Percentage.ZERO);
}
return txPoolConfBuilder.build();
}
private MiningParameters getMiningParameters() {
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;
}
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() {
return pruningEnabled;
}

@ -15,23 +15,29 @@
package org.hyperledger.besu.cli.options;
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_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_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_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_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_REMOTE_SEALERS_LIMIT;
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.config.GenesisConfigOptions;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters.MutableInitValues;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.util.number.Percentage;
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)")
private Long posBlockCreationRepetitionMinDuration =
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() {}
@ -180,15 +205,15 @@ public class MiningOptions implements CLIOptions<MiningParameters> {
* options are valid for the selected implementation.
*
* @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 isEthHash is EthHash?
* @param logger the logger
*/
public void validate(
final CommandLine commandLine,
final Logger logger,
final GenesisConfigOptions genesisConfigOptions,
final boolean isMergeEnabled,
final boolean isEthHash) {
final Logger logger) {
if (Boolean.TRUE.equals(isMiningEnabled) && coinbase == null) {
throw new ParameterException(
commandLine,
@ -203,7 +228,7 @@ public class MiningOptions implements CLIOptions<MiningParameters> {
}
// Check that block producer options work
if (!isMergeEnabled && isEthHash) {
if (!isMergeEnabled && genesisConfigOptions.isEthHash()) {
CommandLineUtils.checkOptionDependencies(
logger,
commandLine,
@ -231,7 +256,9 @@ public class MiningOptions implements CLIOptions<MiningParameters> {
if (unstableOptions.posBlockCreationMaxTime <= 0
|| unstableOptions.posBlockCreationMaxTime > DEFAULT_POS_BLOCK_CREATION_MAX_TIME) {
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
@ -239,6 +266,31 @@ public class MiningOptions implements CLIOptions<MiningParameters> {
throw new ParameterException(
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) {
@ -265,6 +317,10 @@ public class MiningOptions implements CLIOptions<MiningParameters> {
miningParameters.getUnstable().getPosBlockCreationMaxTime();
miningOptions.unstableOptions.posBlockCreationRepetitionMinDuration =
miningParameters.getUnstable().getPosBlockCreationRepetitionMinDuration();
miningOptions.unstableOptions.nonPoaBlockTxsSelectionMaxTime =
miningParameters.getUnstable().getBlockTxsSelectionMaxTime();
miningOptions.unstableOptions.poaBlockTxsSelectionMaxTime =
miningParameters.getUnstable().getPoaBlockTxsSelectionMaxTime();
miningParameters.getCoinbase().ifPresent(coinbase -> miningOptions.coinbase = coinbase);
miningParameters.getTargetGasLimit().ifPresent(tgl -> miningOptions.targetGasLimit = tgl);
@ -304,6 +360,8 @@ public class MiningOptions implements CLIOptions<MiningParameters> {
.posBlockCreationMaxTime(unstableOptions.posBlockCreationMaxTime)
.posBlockCreationRepetitionMinDuration(
unstableOptions.posBlockCreationRepetitionMinDuration)
.nonPoaBlockTxsSelectionMaxTime(unstableOptions.nonPoaBlockTxsSelectionMaxTime)
.poaBlockTxsSelectionMaxTime(unstableOptions.poaBlockTxsSelectionMaxTime)
.build());
return miningParametersBuilder.build();

@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.cli.options.stable;
package org.hyperledger.besu.cli.options;
import static org.hyperledger.besu.cli.DefaultCommandValues.MANDATORY_DOUBLE_FORMAT_HELP;
import static org.hyperledger.besu.cli.DefaultCommandValues.MANDATORY_INTEGER_FORMAT_HELP;
@ -20,10 +20,11 @@ import static org.hyperledger.besu.cli.DefaultCommandValues.MANDATORY_LONG_FORMA
import static org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration.Implementation.LAYERED;
import static org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration.Implementation.LEGACY;
import org.hyperledger.besu.cli.converter.DurationMillisConverter;
import org.hyperledger.besu.cli.converter.FractionConverter;
import org.hyperledger.besu.cli.converter.PercentageConverter;
import org.hyperledger.besu.cli.options.CLIOptions;
import org.hyperledger.besu.cli.util.CommandLineUtils;
import org.hyperledger.besu.config.GenesisConfigOptions;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
@ -32,6 +33,7 @@ import org.hyperledger.besu.util.number.Fraction;
import org.hyperledger.besu.util.number.Percentage;
import java.io.File;
import java.time.Duration;
import java.util.List;
import java.util.Set;
@ -195,6 +197,39 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
Integer txPoolMaxSize = TransactionPoolConfiguration.DEFAULT_MAX_PENDING_TRANSACTIONS;
}
@CommandLine.ArgGroup(validate = false)
private final TransactionPoolOptions.Unstable unstableOptions =
new TransactionPoolOptions.Unstable();
static class Unstable {
private static final String TX_MESSAGE_KEEP_ALIVE_SEC_FLAG =
"--Xincoming-tx-messages-keep-alive-seconds";
private static final String ETH65_TX_ANNOUNCED_BUFFERING_PERIOD_FLAG =
"--Xeth65-tx-announced-buffering-period-milliseconds";
@CommandLine.Option(
names = {TX_MESSAGE_KEEP_ALIVE_SEC_FLAG},
paramLabel = "<INTEGER>",
hidden = true,
description =
"Keep alive of incoming transaction messages in seconds (default: ${DEFAULT-VALUE})",
arity = "1")
private Integer txMessageKeepAliveSeconds =
TransactionPoolConfiguration.Unstable.DEFAULT_TX_MSG_KEEP_ALIVE;
@CommandLine.Option(
names = {ETH65_TX_ANNOUNCED_BUFFERING_PERIOD_FLAG},
paramLabel = "<LONG>",
converter = DurationMillisConverter.class,
hidden = true,
description =
"The period for which the announced transactions remain in the buffer before being requested from the peers in milliseconds (default: ${DEFAULT-VALUE})",
arity = "1")
private Duration eth65TrxAnnouncedBufferingPeriod =
TransactionPoolConfiguration.Unstable.ETH65_TRX_ANNOUNCED_BUFFERING_PERIOD;
}
private TransactionPoolOptions() {}
/**
@ -230,6 +265,10 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
config.getTxPoolLimitByAccountPercentage();
options.legacyOptions.txPoolMaxSize = config.getTxPoolMaxSize();
options.legacyOptions.pendingTxRetentionPeriod = config.getPendingTxRetentionPeriod();
options.unstableOptions.txMessageKeepAliveSeconds =
config.getUnstable().getTxMessageKeepAliveSeconds();
options.unstableOptions.eth65TrxAnnouncedBufferingPeriod =
config.getUnstable().getEth65TrxAnnouncedBufferingPeriod();
return options;
}
@ -239,8 +278,10 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
* options are valid for the selected implementation.
*
* @param commandLine the full commandLine to check all the options specified by the user
* @param genesisConfigOptions the genesis config options
*/
public void validate(final CommandLine commandLine) {
public void validate(
final CommandLine commandLine, final GenesisConfigOptions genesisConfigOptions) {
CommandLineUtils.failIfOptionDoesntMeetRequirement(
commandLine,
"Could not use legacy transaction pool options with layered implementation",
@ -252,6 +293,12 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
"Could not use layered transaction pool options with legacy implementation",
!txPoolImplementation.equals(LEGACY),
CommandLineUtils.getCLIOptionNames(Layered.class));
CommandLineUtils.failIfOptionDoesntMeetRequirement(
commandLine,
"Price bump option is not compatible with zero base fee market",
!genesisConfigOptions.isZeroBaseFee(),
List.of(TX_POOL_PRICE_BUMP));
}
@Override
@ -271,6 +318,11 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
.txPoolLimitByAccountPercentage(legacyOptions.txPoolLimitByAccountPercentage)
.txPoolMaxSize(legacyOptions.txPoolMaxSize)
.pendingTxRetentionPeriod(legacyOptions.pendingTxRetentionPeriod)
.unstable(
ImmutableTransactionPoolConfiguration.Unstable.builder()
.txMessageKeepAliveSeconds(unstableOptions.txMessageKeepAliveSeconds)
.eth65TrxAnnouncedBufferingPeriod(unstableOptions.eth65TrxAnnouncedBufferingPeriod)
.build())
.build();
}
@ -278,4 +330,14 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
public List<String> getCLIOptions() {
return CommandLineUtils.getCLIOptions(this, new TransactionPoolOptions());
}
/**
* Is price bump option set?
*
* @param commandLine the command line
* @return true is tx-pool-price-bump is set
*/
public boolean isPriceBumpSet(final CommandLine commandLine) {
return CommandLineUtils.isOptionSet(commandLine, TransactionPoolOptions.TX_POOL_PRICE_BUMP);
}
}

@ -1,94 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.cli.options.unstable;
import org.hyperledger.besu.cli.converter.DurationMillisConverter;
import org.hyperledger.besu.cli.options.CLIOptions;
import org.hyperledger.besu.cli.util.CommandLineUtils;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import java.time.Duration;
import java.util.List;
import picocli.CommandLine;
/** The Transaction pool Cli unstable options. */
public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfiguration.Unstable> {
private static final String TX_MESSAGE_KEEP_ALIVE_SEC_FLAG =
"--Xincoming-tx-messages-keep-alive-seconds";
private static final String ETH65_TX_ANNOUNCED_BUFFERING_PERIOD_FLAG =
"--Xeth65-tx-announced-buffering-period-milliseconds";
@CommandLine.Option(
names = {TX_MESSAGE_KEEP_ALIVE_SEC_FLAG},
paramLabel = "<INTEGER>",
hidden = true,
description =
"Keep alive of incoming transaction messages in seconds (default: ${DEFAULT-VALUE})",
arity = "1")
private Integer txMessageKeepAliveSeconds =
TransactionPoolConfiguration.Unstable.DEFAULT_TX_MSG_KEEP_ALIVE;
@CommandLine.Option(
names = {ETH65_TX_ANNOUNCED_BUFFERING_PERIOD_FLAG},
paramLabel = "<LONG>",
converter = DurationMillisConverter.class,
hidden = true,
description =
"The period for which the announced transactions remain in the buffer before being requested from the peers in milliseconds (default: ${DEFAULT-VALUE})",
arity = "1")
private Duration eth65TrxAnnouncedBufferingPeriod =
TransactionPoolConfiguration.Unstable.ETH65_TRX_ANNOUNCED_BUFFERING_PERIOD;
private TransactionPoolOptions() {}
/**
* Create transaction pool options.
*
* @return the transaction pool options
*/
public static TransactionPoolOptions create() {
return new TransactionPoolOptions();
}
/**
* Create Transaction Pool Options from Transaction Pool Configuration.
*
* @param config the Transaction Pool Configuration
* @return the transaction pool options
*/
public static TransactionPoolOptions fromConfig(
final TransactionPoolConfiguration.Unstable config) {
final TransactionPoolOptions options = TransactionPoolOptions.create();
options.txMessageKeepAliveSeconds = config.getTxMessageKeepAliveSeconds();
options.eth65TrxAnnouncedBufferingPeriod = config.getEth65TrxAnnouncedBufferingPeriod();
return options;
}
@Override
public TransactionPoolConfiguration.Unstable toDomainObject() {
return ImmutableTransactionPoolConfiguration.Unstable.builder()
.txMessageKeepAliveSeconds(txMessageKeepAliveSeconds)
.eth65TrxAnnouncedBufferingPeriod(eth65TrxAnnouncedBufferingPeriod)
.build();
}
@Override
public List<String> getCLIOptions() {
return CommandLineUtils.getCLIOptions(this, new TransactionPoolOptions());
}
}

@ -248,4 +248,17 @@ public class CommandLineUtils {
return false;
}
}
/**
* Is the option with that name set on the command line?
*
* @param commandLine the command line
* @param optionName the option name to check
* @return true if set
*/
public static boolean isOptionSet(final CommandLine commandLine, final String optionName) {
return commandLine.getCommandSpec().options().stream()
.filter(optionSpec -> Arrays.stream(optionSpec.names()).anyMatch(optionName::equals))
.anyMatch(CommandLineUtils::isOptionSet);
}
}

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

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

@ -55,7 +55,8 @@ public class MainnetBesuControllerBuilder extends BesuControllerBuilder {
MainnetBlockHeaderValidator.MINIMUM_SECONDS_SINCE_PARENT,
MainnetBlockHeaderValidator.TIMESTAMP_TOLERANCE_S,
clock),
epochCalculator);
epochCalculator,
ethProtocolManager.ethContext().getScheduler());
final PoWMiningCoordinator miningCoordinator =
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.EthScheduler;
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.RequiredBlocksPeerValidator;
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.ScheduleBasedBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.util.List;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicReference;
import org.slf4j.Logger;
@ -85,7 +81,7 @@ public class MergeBesuControllerBuilder extends BesuControllerBuilder {
syncState,
BackwardChain.from(
storageProvider, ScheduleBasedBlockHeaderFunctions.create(protocolSchedule))),
metricsSystem);
ethProtocolManager.ethContext().getScheduler());
}
@Override
@ -146,7 +142,7 @@ public class MergeBesuControllerBuilder extends BesuControllerBuilder {
* @param miningParameters the mining parameters
* @param syncState the sync state
* @param backwardSyncContext the backward sync context
* @param metricsSystem the metrics system
* @param ethScheduler the scheduler
* @return the mining coordinator
*/
protected MiningCoordinator createTransitionMiningCoordinator(
@ -156,13 +152,10 @@ public class MergeBesuControllerBuilder extends BesuControllerBuilder {
final MiningParameters miningParameters,
final SyncState syncState,
final BackwardSyncContext backwardSyncContext,
final MetricsSystem metricsSystem) {
final EthScheduler ethScheduler) {
this.syncState.set(syncState);
final ExecutorService blockBuilderExecutor =
MonitoredExecutors.newSingleThreadExecutor("PoS-Block-Builder", metricsSystem);
final GenesisConfigOptions genesisConfigOptions = configOptionsSupplier.get();
final Optional<Address> depositContractAddress =
genesisConfigOptions.getDepositContractAddress();
@ -170,10 +163,7 @@ public class MergeBesuControllerBuilder extends BesuControllerBuilder {
return new MergeCoordinator(
protocolContext,
protocolSchedule,
task -> {
LOG.debug("Block builder executor status {}", blockBuilderExecutor);
return CompletableFuture.runAsync(task, blockBuilderExecutor);
},
ethScheduler,
transactionPool,
miningParameters,
backwardSyncContext,

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

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

@ -183,6 +183,9 @@ public class BesuCommandTest extends CommandTestAbstract {
private static final JsonObject GENESIS_WITH_DATA_BLOBS_ENABLED =
new JsonObject().put("config", new JsonObject().put("cancunTime", 1L));
private static final JsonObject GENESIS_WITH_ZERO_BASE_FEE_MARKET =
new JsonObject().put("config", new JsonObject().put("zeroBaseFee", true));
static {
DEFAULT_JSON_RPC_CONFIGURATION = JsonRpcConfiguration.createDefault();
DEFAULT_GRAPH_QL_CONFIGURATION = GraphQLConfiguration.createDefault();
@ -5195,6 +5198,56 @@ public class BesuCommandTest extends CommandTestAbstract {
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void txpoolForcePriceBumpToZeroWhenZeroBaseFeeMarket() throws IOException {
final Path genesisFile = createFakeGenesisFile(GENESIS_WITH_ZERO_BASE_FEE_MARKET);
parseCommand("--genesis-file", genesisFile.toString());
verify(mockControllerBuilder)
.transactionPoolConfiguration(transactionPoolConfigCaptor.capture());
final Percentage priceBump = transactionPoolConfigCaptor.getValue().getPriceBump();
assertThat(priceBump).isEqualTo(Percentage.ZERO);
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void txpoolPriceBumpOptionIncompatibleWithZeroWhenZeroBaseFeeMarket() throws IOException {
final Path genesisFile = createFakeGenesisFile(GENESIS_WITH_ZERO_BASE_FEE_MARKET);
parseCommand("--genesis-file", genesisFile.toString(), "--tx-pool-price-bump", "5");
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8))
.contains("Price bump option is not compatible with zero base fee market");
}
@Test
public void txpoolForcePriceBumpToZeroWhenMinGasPriceZero() {
parseCommand("--min-gas-price", "0");
verify(mockControllerBuilder)
.transactionPoolConfiguration(transactionPoolConfigCaptor.capture());
final Percentage priceBump = transactionPoolConfigCaptor.getValue().getPriceBump();
assertThat(priceBump).isEqualTo(Percentage.ZERO);
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void txpoolPriceBumpKeepItsValueIfSetEvenWhenMinGasPriceZero() {
parseCommand("--min-gas-price", "0", "--tx-pool-price-bump", "1");
verify(mockControllerBuilder)
.transactionPoolConfiguration(transactionPoolConfigCaptor.capture());
final Percentage priceBump = transactionPoolConfigCaptor.getValue().getPriceBump();
assertThat(priceBump).isEqualTo(Percentage.fromInt(1));
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void snapsyncHealingOptionShouldBeDisabledByDefault() {
final TestBesuCommand besuCommand = parseCommand();

@ -32,12 +32,12 @@ import org.hyperledger.besu.chainimport.JsonBlockImporter;
import org.hyperledger.besu.chainimport.RlpBlockImporter;
import org.hyperledger.besu.cli.config.EthNetworkConfig;
import org.hyperledger.besu.cli.options.MiningOptions;
import org.hyperledger.besu.cli.options.TransactionPoolOptions;
import org.hyperledger.besu.cli.options.stable.EthstatsOptions;
import org.hyperledger.besu.cli.options.unstable.EthProtocolOptions;
import org.hyperledger.besu.cli.options.unstable.MetricsCLIOptions;
import org.hyperledger.besu.cli.options.unstable.NetworkingOptions;
import org.hyperledger.besu.cli.options.unstable.SynchronizerOptions;
import org.hyperledger.besu.cli.options.unstable.TransactionPoolOptions;
import org.hyperledger.besu.components.BesuComponent;
import org.hyperledger.besu.consensus.qbft.pki.PkiBlockCreationConfiguration;
import org.hyperledger.besu.consensus.qbft.pki.PkiBlockCreationConfigurationProvider;
@ -560,17 +560,12 @@ public abstract class CommandTestAbstract {
return unstableEthProtocolOptions;
}
public org.hyperledger.besu.cli.options.stable.TransactionPoolOptions
getStableTransactionPoolOptions() {
return stableTransactionPoolOptions;
}
public MiningOptions getMiningOptions() {
return miningOptions;
}
public TransactionPoolOptions getUnstableTransactionPoolOptions() {
return unstableTransactionPoolOptions;
public TransactionPoolOptions getTransactionPoolOptions() {
return transactionPoolOptions;
}
public MetricsCLIOptions getMetricsCLIOptions() {

@ -15,6 +15,8 @@
package org.hyperledger.besu.cli.options;
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.mockito.Mockito.atMost;
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.Unstable;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.util.number.Percentage;
import java.io.IOException;
import java.nio.file.Path;
@ -308,6 +311,79 @@ public class MiningOptionsTest extends AbstractCLIOptionsTest<MiningParameters,
"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
protected MiningParameters createDefaultDomainObject() {
return MiningParameters.newDefault();

@ -12,20 +12,21 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.cli.options.stable;
package org.hyperledger.besu.cli.options;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration.Implementation.LAYERED;
import static org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration.Implementation.LEGACY;
import org.hyperledger.besu.cli.options.AbstractCLIOptionsTest;
import org.hyperledger.besu.cli.options.OptionParser;
import org.hyperledger.besu.cli.converter.DurationMillisConverter;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.util.number.Percentage;
import java.time.Duration;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
@ -270,6 +271,52 @@ public class TransactionPoolOptionsTest
+ prioritySender3.toHexString());
}
@Test
public void txMessageKeepAliveSeconds() {
final int txMessageKeepAliveSeconds = 999;
internalTestSuccess(
config ->
assertThat(config.getUnstable().getTxMessageKeepAliveSeconds())
.isEqualTo(txMessageKeepAliveSeconds),
"--Xincoming-tx-messages-keep-alive-seconds",
String.valueOf(txMessageKeepAliveSeconds));
}
@Test
public void txMessageKeepAliveSecondsWithInvalidInputShouldFail() {
internalTestFailure(
"Invalid value for option '--Xincoming-tx-messages-keep-alive-seconds': 'acbd' is not an int",
"--Xincoming-tx-messages-keep-alive-seconds",
"acbd");
}
@Test
public void eth65TrxAnnouncedBufferingPeriod() {
final Duration eth65TrxAnnouncedBufferingPeriod = Duration.ofMillis(999);
internalTestSuccess(
config ->
assertThat(config.getUnstable().getEth65TrxAnnouncedBufferingPeriod())
.isEqualTo(eth65TrxAnnouncedBufferingPeriod),
"--Xeth65-tx-announced-buffering-period-milliseconds",
new DurationMillisConverter().format(eth65TrxAnnouncedBufferingPeriod));
}
@Test
public void eth65TrxAnnouncedBufferingPeriodWithInvalidInputShouldFail() {
internalTestFailure(
"Invalid value for option '--Xeth65-tx-announced-buffering-period-milliseconds': cannot convert 'acbd' to Duration (org.hyperledger.besu.cli.converter.exception.DurationConversionException: 'acbd' is not a long)",
"--Xeth65-tx-announced-buffering-period-milliseconds",
"acbd");
}
@Test
public void eth65TrxAnnouncedBufferingPeriodWithInvalidInputShouldFail2() {
internalTestFailure(
"Invalid value for option '--Xeth65-tx-announced-buffering-period-milliseconds': cannot convert '-1' to Duration (org.hyperledger.besu.cli.converter.exception.DurationConversionException: negative value '-1' is not allowed)",
"--Xeth65-tx-announced-buffering-period-milliseconds",
"-1");
}
@Override
protected TransactionPoolConfiguration createDefaultDomainObject() {
return TransactionPoolConfiguration.DEFAULT;
@ -294,6 +341,6 @@ public class TransactionPoolOptionsTest
@Override
protected TransactionPoolOptions getOptionsFromBesuCommand(final TestBesuCommand besuCommand) {
return besuCommand.getStableTransactionPoolOptions();
return besuCommand.getTransactionPoolOptions();
}
}

@ -1,105 +0,0 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.cli.options.unstable;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.cli.converter.DurationMillisConverter;
import org.hyperledger.besu.cli.options.AbstractCLIOptionsTest;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import java.time.Duration;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class TransactionPoolOptionsTest
extends AbstractCLIOptionsTest<TransactionPoolConfiguration.Unstable, TransactionPoolOptions> {
@Test
public void txMessageKeepAliveSeconds() {
final int txMessageKeepAliveSeconds = 999;
internalTestSuccess(
config ->
assertThat(config.getTxMessageKeepAliveSeconds()).isEqualTo(txMessageKeepAliveSeconds),
"--Xincoming-tx-messages-keep-alive-seconds",
String.valueOf(txMessageKeepAliveSeconds));
}
@Test
public void txMessageKeepAliveSecondsWithInvalidInputShouldFail() {
internalTestFailure(
"Invalid value for option '--Xincoming-tx-messages-keep-alive-seconds': 'acbd' is not an int",
"--Xincoming-tx-messages-keep-alive-seconds",
"acbd");
}
@Test
public void eth65TrxAnnouncedBufferingPeriod() {
final Duration eth65TrxAnnouncedBufferingPeriod = Duration.ofMillis(999);
internalTestSuccess(
config ->
assertThat(config.getEth65TrxAnnouncedBufferingPeriod())
.isEqualTo(eth65TrxAnnouncedBufferingPeriod),
"--Xeth65-tx-announced-buffering-period-milliseconds",
new DurationMillisConverter().format(eth65TrxAnnouncedBufferingPeriod));
}
@Test
public void eth65TrxAnnouncedBufferingPeriodWithInvalidInputShouldFail() {
internalTestFailure(
"Invalid value for option '--Xeth65-tx-announced-buffering-period-milliseconds': cannot convert 'acbd' to Duration (org.hyperledger.besu.cli.converter.exception.DurationConversionException: 'acbd' is not a long)",
"--Xeth65-tx-announced-buffering-period-milliseconds",
"acbd");
}
@Test
public void eth65TrxAnnouncedBufferingPeriodWithInvalidInputShouldFail2() {
internalTestFailure(
"Invalid value for option '--Xeth65-tx-announced-buffering-period-milliseconds': cannot convert '-1' to Duration (org.hyperledger.besu.cli.converter.exception.DurationConversionException: negative value '-1' is not allowed)",
"--Xeth65-tx-announced-buffering-period-milliseconds",
"-1");
}
@Override
protected TransactionPoolConfiguration.Unstable createDefaultDomainObject() {
return TransactionPoolConfiguration.Unstable.DEFAULT;
}
@Override
protected TransactionPoolConfiguration.Unstable createCustomizedDomainObject() {
return ImmutableTransactionPoolConfiguration.Unstable.builder()
.txMessageKeepAliveSeconds(
TransactionPoolConfiguration.Unstable.DEFAULT_TX_MSG_KEEP_ALIVE + 1)
.eth65TrxAnnouncedBufferingPeriod(
TransactionPoolConfiguration.Unstable.ETH65_TRX_ANNOUNCED_BUFFERING_PERIOD.plus(
Duration.ofMillis(100)))
.build();
}
@Override
protected TransactionPoolOptions optionsFromDomainObject(
final TransactionPoolConfiguration.Unstable domainObject) {
return TransactionPoolOptions.fromConfig(domainObject);
}
@Override
protected TransactionPoolOptions getOptionsFromBesuCommand(final TestBesuCommand besuCommand) {
return besuCommand.getUnstableTransactionPoolOptions();
}
}

@ -49,12 +49,14 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@ -71,7 +73,10 @@ public class TransitionControllerBuilderTest {
@Mock MutableBlockchain mockBlockchain;
@Mock TransactionPool transactionPool;
@Mock SyncState syncState;
@Mock EthProtocolManager ethProtocolManager;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
EthProtocolManager ethProtocolManager;
@Mock PostMergeContext mergeContext;
StorageProvider storageProvider = new InMemoryKeyValueStorageProvider();
@ -101,6 +106,8 @@ public class TransitionControllerBuilderTest {
.thenReturn(mock(CliqueContext.class));
when(protocolContext.getConsensusContext(PostMergeContext.class)).thenReturn(mergeContext);
when(protocolContext.getConsensusContext(MergeContext.class)).thenReturn(mergeContext);
when(ethProtocolManager.ethContext().getScheduler())
.thenReturn(new DeterministicEthScheduler());
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.SealableBlockHeader;
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.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions;
@ -55,6 +56,7 @@ public class CliqueBlockCreator extends AbstractBlockCreator {
* @param nodeKey the node key
* @param parentHeader the parent header
* @param epochManager the epoch manager
* @param ethScheduler the scheduler for asynchronous block creation tasks
*/
public CliqueBlockCreator(
final MiningParameters miningParameters,
@ -64,7 +66,8 @@ public class CliqueBlockCreator extends AbstractBlockCreator {
final ProtocolSchedule protocolSchedule,
final NodeKey nodeKey,
final BlockHeader parentHeader,
final EpochManager epochManager) {
final EpochManager epochManager,
final EthScheduler ethScheduler) {
super(
miningParameters,
__ -> Util.publicKeyToAddress(nodeKey.getPublicKey()),
@ -73,7 +76,8 @@ public class CliqueBlockCreator extends AbstractBlockCreator {
protocolContext,
protocolSchedule,
parentHeader,
Optional.empty());
Optional.empty(),
ethScheduler);
this.nodeKey = nodeKey;
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.MiningParameters;
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.mainnet.ProtocolSchedule;
import org.hyperledger.besu.util.Subscribers;
@ -60,6 +61,7 @@ public class CliqueMinerExecutor extends AbstractMinerExecutor<CliqueBlockMiner>
* @param blockScheduler the block scheduler
* @param epochManager the epoch manager
* @param createEmptyBlocks whether clique should allow the creation of empty blocks.
* @param ethScheduler the scheduler for asynchronous block creation tasks
*/
public CliqueMinerExecutor(
final ProtocolContext protocolContext,
@ -69,8 +71,15 @@ public class CliqueMinerExecutor extends AbstractMinerExecutor<CliqueBlockMiner>
final MiningParameters miningParams,
final AbstractBlockScheduler blockScheduler,
final EpochManager epochManager,
final boolean createEmptyBlocks) {
super(protocolContext, protocolSchedule, transactionPool, miningParams, blockScheduler);
final boolean createEmptyBlocks,
final EthScheduler ethScheduler) {
super(
protocolContext,
protocolSchedule,
transactionPool,
miningParams,
blockScheduler,
ethScheduler);
this.nodeKey = nodeKey;
this.localAddress = Util.publicKeyToAddress(nodeKey.getPublicKey());
this.epochManager = epochManager;
@ -93,7 +102,8 @@ public class CliqueMinerExecutor extends AbstractMinerExecutor<CliqueBlockMiner>
protocolSchedule,
nodeKey,
header,
epochManager);
epochManager,
ethScheduler);
return new CliqueBlockMiner(
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.Util;
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.TransactionBroadcaster;
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.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.testutil.TestClock;
import java.time.ZoneId;
@ -83,7 +85,7 @@ public class CliqueBlockCreatorTest {
private final List<Address> validatorList = Lists.newArrayList();
private final MetricsSystem metricsSystem = new NoOpMetricsSystem();
private final CliqueBlockInterface blockInterface = new CliqueBlockInterface();
private final EthScheduler ethScheduler = new DeterministicEthScheduler();
private ProtocolSchedule protocolSchedule;
private final WorldStateArchive stateArchive = createInMemoryWorldStateArchive();
@ -148,7 +150,8 @@ public class CliqueBlockCreatorTest {
protocolSchedule,
proposerNodeKey,
blockchain.getChainHeadHeader(),
epochManager);
epochManager,
ethScheduler);
final Block createdBlock = blockCreator.createBlock(5L).getBlock();
@ -176,7 +179,8 @@ public class CliqueBlockCreatorTest {
protocolSchedule,
proposerNodeKey,
blockchain.getChainHeadHeader(),
epochManager);
epochManager,
ethScheduler);
final Block createdBlock = blockCreator.createBlock(0L).getBlock();
assertThat(createdBlock.getHeader().getNonce()).isEqualTo(CliqueBlockInterface.ADD_NONCE);
@ -209,7 +213,8 @@ public class CliqueBlockCreatorTest {
protocolSchedule,
proposerNodeKey,
blockchain.getChainHeadHeader(),
epochManager);
epochManager,
ethScheduler);
final Block createdBlock = blockCreator.createBlock(0L).getBlock();
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.Util;
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.TransactionBroadcaster;
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.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.testutil.TestClock;
import java.time.ZoneId;
@ -78,6 +80,7 @@ public class CliqueMinerExecutorTest {
private BlockHeaderTestFixture blockHeaderBuilder;
private final MetricsSystem metricsSystem = new NoOpMetricsSystem();
private final CliqueBlockInterface blockInterface = new CliqueBlockInterface();
private final EthScheduler ethScheduler = new DeterministicEthScheduler();
@BeforeEach
public void setup() {
@ -114,7 +117,8 @@ public class CliqueMinerExecutorTest {
miningParameters,
mock(CliqueBlockScheduler.class),
new EpochManager(EPOCH_LENGTH),
true);
true,
ethScheduler);
// NOTE: Passing in the *parent* block, so must be 1 less than EPOCH
final BlockHeader header = blockHeaderBuilder.number(EPOCH_LENGTH - 1).buildHeader();
@ -149,7 +153,8 @@ public class CliqueMinerExecutorTest {
miningParameters,
mock(CliqueBlockScheduler.class),
new EpochManager(EPOCH_LENGTH),
true);
true,
ethScheduler);
// Parent block was epoch, so the next block should contain no validators.
final BlockHeader header = blockHeaderBuilder.number(EPOCH_LENGTH).buildHeader();
@ -184,7 +189,8 @@ public class CliqueMinerExecutorTest {
miningParameters,
mock(CliqueBlockScheduler.class),
new EpochManager(EPOCH_LENGTH),
true);
true,
ethScheduler);
executor.setExtraData(modifiedVanityData);
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.MiningParameters;
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.mainnet.ProtocolSchedule;
@ -50,6 +51,7 @@ public class BftBlockCreator extends AbstractBlockCreator {
* @param protocolSchedule the protocol schedule
* @param parentHeader the parent header
* @param bftExtraDataCodec the bft extra data codec
* @param ethScheduler the scheduler for asynchronous block creation tasks
*/
public BftBlockCreator(
final MiningParameters miningParameters,
@ -60,7 +62,8 @@ public class BftBlockCreator extends AbstractBlockCreator {
final ProtocolContext protocolContext,
final ProtocolSchedule protocolSchedule,
final BlockHeader parentHeader,
final BftExtraDataCodec bftExtraDataCodec) {
final BftExtraDataCodec bftExtraDataCodec,
final EthScheduler ethScheduler) {
super(
miningParameters.setCoinbase(localAddress),
miningBeneficiaryCalculator(localAddress, forksSchedule),
@ -69,7 +72,8 @@ public class BftBlockCreator extends AbstractBlockCreator {
protocolContext,
protocolSchedule,
parentHeader,
Optional.empty());
Optional.empty(),
ethScheduler);
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.core.BlockHeader;
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.mainnet.AbstractGasLimitSpecification;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
@ -61,6 +62,8 @@ public class BftBlockCreatorFactory<T extends BftConfigOptions> {
protected final ProtocolSchedule protocolSchedule;
/** The Bft extra data codec. */
protected final BftExtraDataCodec bftExtraDataCodec;
/** The scheduler for asynchronous block creation tasks */
protected final EthScheduler ethScheduler;
private final Address localAddress;
@ -74,6 +77,7 @@ public class BftBlockCreatorFactory<T extends BftConfigOptions> {
* @param miningParams the mining params
* @param localAddress the local address
* @param bftExtraDataCodec the bft extra data codec
* @param ethScheduler the scheduler for asynchronous block creation tasks
*/
public BftBlockCreatorFactory(
final TransactionPool transactionPool,
@ -82,7 +86,8 @@ public class BftBlockCreatorFactory<T extends BftConfigOptions> {
final ForksSchedule<T> forksSchedule,
final MiningParameters miningParams,
final Address localAddress,
final BftExtraDataCodec bftExtraDataCodec) {
final BftExtraDataCodec bftExtraDataCodec,
final EthScheduler ethScheduler) {
this.transactionPool = transactionPool;
this.protocolContext = protocolContext;
this.protocolSchedule = protocolSchedule;
@ -90,6 +95,7 @@ public class BftBlockCreatorFactory<T extends BftConfigOptions> {
this.localAddress = localAddress;
this.miningParameters = miningParams;
this.bftExtraDataCodec = bftExtraDataCodec;
this.ethScheduler = ethScheduler;
}
/**
@ -109,7 +115,8 @@ public class BftBlockCreatorFactory<T extends BftConfigOptions> {
protocolContext,
protocolSchedule,
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.Util;
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.TransactionBroadcaster;
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.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.testutil.TestClock;
import org.hyperledger.besu.util.Subscribers;
@ -367,6 +369,8 @@ public class TestContextBuilder {
transactionPool.setEnabled();
final EthScheduler ethScheduler = new DeterministicEthScheduler();
final Address localAddress = Util.publicKeyToAddress(nodeKey.getPublicKey());
final BftBlockCreatorFactory<?> blockCreatorFactory =
new BftBlockCreatorFactory<>(
@ -376,7 +380,8 @@ public class TestContextBuilder {
forksSchedule,
miningParams,
localAddress,
IBFT_EXTRA_DATA_ENCODER);
IBFT_EXTRA_DATA_ENCODER,
ethScheduler);
final ProposerSelector proposerSelector =
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.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.testutil.TestClock;
import java.time.ZoneId;
@ -186,7 +187,8 @@ public class BftBlockCreatorTest {
protContext,
protocolSchedule,
parentHeader,
bftExtraDataEncoder);
bftExtraDataEncoder,
new DeterministicEthScheduler());
final int secondsBetweenBlocks = 1;
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.Transaction;
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.mainnet.ProtocolSchedule;
@ -53,7 +54,8 @@ class MergeBlockCreator extends AbstractBlockCreator {
final ProtocolContext protocolContext,
final ProtocolSchedule protocolSchedule,
final BlockHeader parentHeader,
final Optional<Address> depositContractAddress) {
final Optional<Address> depositContractAddress,
final EthScheduler ethScheduler) {
super(
miningParameters,
__ -> miningParameters.getCoinbase().orElseThrow(),
@ -62,7 +64,8 @@ class MergeBlockCreator extends AbstractBlockCreator {
protocolContext,
protocolSchedule,
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.Transaction;
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.BadChainListener;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
@ -86,7 +87,7 @@ public class MergeCoordinator implements MergeMiningCoordinator, BadChainListene
/** The Protocol context. */
protected final ProtocolContext protocolContext;
/** The Block builder executor. */
protected final ProposalBuilderExecutor blockBuilderExecutor;
protected final EthScheduler ethScheduler;
/** The Backward sync context. */
protected final BackwardSyncContext backwardSyncContext;
/** The Protocol schedule. */
@ -100,7 +101,7 @@ public class MergeCoordinator implements MergeMiningCoordinator, BadChainListene
*
* @param protocolContext the protocol context
* @param protocolSchedule the protocol schedule
* @param blockBuilderExecutor the block builder executor
* @param ethScheduler the block builder executor
* @param transactionPool the pending transactions
* @param miningParams the mining params
* @param backwardSyncContext the backward sync context
@ -109,14 +110,14 @@ public class MergeCoordinator implements MergeMiningCoordinator, BadChainListene
public MergeCoordinator(
final ProtocolContext protocolContext,
final ProtocolSchedule protocolSchedule,
final ProposalBuilderExecutor blockBuilderExecutor,
final EthScheduler ethScheduler,
final TransactionPool transactionPool,
final MiningParameters miningParams,
final BackwardSyncContext backwardSyncContext,
final Optional<Address> depositContractAddress) {
this.protocolContext = protocolContext;
this.protocolSchedule = protocolSchedule;
this.blockBuilderExecutor = blockBuilderExecutor;
this.ethScheduler = ethScheduler;
this.mergeContext = protocolContext.getConsensusContext(MergeContext.class);
this.backwardSyncContext = backwardSyncContext;
@ -140,7 +141,8 @@ public class MergeCoordinator implements MergeMiningCoordinator, BadChainListene
protocolContext,
protocolSchedule,
parentHeader,
depositContractAddress);
depositContractAddress,
ethScheduler);
};
this.backwardSyncContext.subscribeBadChainListener(this);
@ -151,7 +153,7 @@ public class MergeCoordinator implements MergeMiningCoordinator, BadChainListene
*
* @param protocolContext the protocol context
* @param protocolSchedule the protocol schedule
* @param blockBuilderExecutor the block builder executor
* @param ethScheduler the block builder executor
* @param miningParams the mining params
* @param backwardSyncContext the backward sync context
* @param mergeBlockCreatorFactory the merge block creator factory
@ -159,14 +161,14 @@ public class MergeCoordinator implements MergeMiningCoordinator, BadChainListene
public MergeCoordinator(
final ProtocolContext protocolContext,
final ProtocolSchedule protocolSchedule,
final ProposalBuilderExecutor blockBuilderExecutor,
final EthScheduler ethScheduler,
final MiningParameters miningParams,
final BackwardSyncContext backwardSyncContext,
final MergeBlockCreatorFactory mergeBlockCreatorFactory) {
this.protocolContext = protocolContext;
this.protocolSchedule = protocolSchedule;
this.blockBuilderExecutor = blockBuilderExecutor;
this.ethScheduler = ethScheduler;
this.mergeContext = protocolContext.getConsensusContext(MergeContext.class);
this.backwardSyncContext = backwardSyncContext;
if (miningParams.getTargetGasLimit().isEmpty()) {
@ -366,8 +368,9 @@ public class MergeCoordinator implements MergeMiningCoordinator, BadChainListene
payloadIdentifier,
miningParameters.getUnstable().getPosBlockCreationMaxTime());
blockBuilderExecutor
.buildProposal(() -> retryBlockCreationUntilUseful(payloadIdentifier, blockCreator))
ethScheduler
.scheduleBlockCreationTask(
() -> retryBlockCreationUntilUseful(payloadIdentifier, blockCreator))
.orTimeout(
miningParameters.getUnstable().getPosBlockCreationMaxTime(), TimeUnit.MILLISECONDS)
.whenComplete(
@ -895,15 +898,4 @@ public class MergeCoordinator implements MergeMiningCoordinator, BadChainListene
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.consensus.merge.MergeContext;
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.crypto.KeyPair;
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.TransactionTestFixture;
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.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
@ -130,10 +130,11 @@ public class MergeCoordinatorTest implements MergeGenesisConfigHelper {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
EthContext ethContext;
@Mock ProposalBuilderExecutor proposalBuilderExecutor;
@Mock EthScheduler ethScheduler;
private final Address coinbase = genesisAllocations(getPosGenesisConfigFile()).findFirst().get();
MiningParameters miningParameters =
private MiningParameters miningParameters =
ImmutableMiningParameters.builder()
.mutableInitValues(MutableInitValues.builder().coinbase(coinbase).build())
.unstable(
@ -205,10 +206,13 @@ public class MergeCoordinatorTest implements MergeGenesisConfigHelper {
genesisState.writeStateTo(mutable);
mutable.persist(null);
when(proposalBuilderExecutor.buildProposal(any()))
when(ethScheduler.scheduleBlockCreationTask(any()))
.thenAnswer(
invocation -> {
final Runnable runnable = invocation.getArgument(0);
if (!invocation.toString().contains("MergeCoordinator")) {
return CompletableFuture.runAsync(runnable);
}
blockCreationTask = CompletableFuture.runAsync(runnable);
return blockCreationTask;
});
@ -234,7 +238,7 @@ public class MergeCoordinatorTest implements MergeGenesisConfigHelper {
new MergeCoordinator(
protocolContext,
protocolSchedule,
proposalBuilderExecutor,
ethScheduler,
transactionPool,
miningParameters,
backwardSyncContext,
@ -287,7 +291,8 @@ public class MergeCoordinatorTest implements MergeGenesisConfigHelper {
protocolContext,
protocolSchedule,
parentHeader,
Optional.empty()));
Optional.empty(),
ethScheduler));
doCallRealMethod()
.doCallRealMethod()
@ -304,7 +309,7 @@ public class MergeCoordinatorTest implements MergeGenesisConfigHelper {
new MergeCoordinator(
protocolContext,
protocolSchedule,
proposalBuilderExecutor,
ethScheduler,
miningParameters,
backwardSyncContext,
mergeBlockCreatorFactory));
@ -646,7 +651,7 @@ public class MergeCoordinatorTest implements MergeGenesisConfigHelper {
doAnswer(
invocation -> {
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(
createLocalTransaction(retries.get() - 1), Optional.empty());
} else {
@ -751,7 +756,7 @@ public class MergeCoordinatorTest implements MergeGenesisConfigHelper {
new MergeCoordinator(
protocolContext,
protocolSchedule,
proposalBuilderExecutor,
ethScheduler,
transactionPool,
miningParameters,
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.ImmutableMiningParameters;
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.transactions.TransactionPool;
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.LondonFeeMarket;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.util.LogConfigurator;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -72,7 +73,7 @@ public class MergeReorgTest implements MergeGenesisConfigHelper {
private final WorldStateArchive worldStateArchive = createInMemoryWorldStateArchive();
private final MutableBlockchain blockchain = createInMemoryBlockchain(genesisState.getBlock());
private final EthScheduler ethScheduler = new DeterministicEthScheduler();
private final ProtocolContext protocolContext =
new ProtocolContext(blockchain, worldStateArchive, mergeContext, Optional.empty());
@ -92,7 +93,7 @@ public class MergeReorgTest implements MergeGenesisConfigHelper {
new MergeCoordinator(
protocolContext,
mockProtocolSchedule,
CompletableFuture::runAsync,
ethScheduler,
mockTransactionPool,
ImmutableMiningParameters.builder()
.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.Util;
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.TransactionBroadcaster;
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.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.testutil.TestClock;
import org.hyperledger.besu.util.Subscribers;
@ -455,6 +457,8 @@ public class TestContextBuilder {
transactionPool.setEnabled();
final EthScheduler ethScheduler = new DeterministicEthScheduler();
final Address localAddress = Util.publicKeyToAddress(nodeKey.getPublicKey());
final BftBlockCreatorFactory<?> blockCreatorFactory =
new QbftBlockCreatorFactory(
@ -464,7 +468,8 @@ public class TestContextBuilder {
forksSchedule,
miningParams,
localAddress,
BFT_EXTRA_DATA_ENCODER);
BFT_EXTRA_DATA_ENCODER,
ethScheduler);
final ProposerSelector proposerSelector =
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.core.BlockHeader;
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.mainnet.ProtocolSchedule;
@ -46,6 +47,7 @@ public class QbftBlockCreatorFactory extends BftBlockCreatorFactory<QbftConfigOp
* @param miningParams the mining params
* @param localAddress the local address
* @param bftExtraDataCodec the bft extra data codec
* @param ethScheduler the scheduler for asynchronous block creation tasks
*/
public QbftBlockCreatorFactory(
final TransactionPool transactionPool,
@ -54,7 +56,8 @@ public class QbftBlockCreatorFactory extends BftBlockCreatorFactory<QbftConfigOp
final ForksSchedule<QbftConfigOptions> forksSchedule,
final MiningParameters miningParams,
final Address localAddress,
final BftExtraDataCodec bftExtraDataCodec) {
final BftExtraDataCodec bftExtraDataCodec,
final EthScheduler ethScheduler) {
super(
transactionPool,
protocolContext,
@ -62,7 +65,8 @@ public class QbftBlockCreatorFactory extends BftBlockCreatorFactory<QbftConfigOp
forksSchedule,
miningParams,
localAddress,
bftExtraDataCodec);
bftExtraDataCodec,
ethScheduler);
}
@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.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import java.util.Optional;
@ -72,7 +73,8 @@ public class QbftBlockCreatorFactoryTest {
forksSchedule,
miningParams,
mock(Address.class),
extraDataCodec);
extraDataCodec,
new DeterministicEthScheduler());
}
@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.Withdrawal;
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.mainnet.AbstractBlockProcessor;
import org.hyperledger.besu.ethereum.mainnet.BodyValidation;
@ -92,7 +93,7 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
protected final BlockHeaderFunctions blockHeaderFunctions;
protected final BlockHeader parentHeader;
private final Optional<Address> depositContractAddress;
private final EthScheduler ethScheduler;
private final AtomicBoolean isCancelled = new AtomicBoolean(false);
protected AbstractBlockCreator(
@ -103,7 +104,8 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
final ProtocolContext protocolContext,
final ProtocolSchedule protocolSchedule,
final BlockHeader parentHeader,
final Optional<Address> depositContractAddress) {
final Optional<Address> depositContractAddress,
final EthScheduler ethScheduler) {
this.miningParameters = miningParameters;
this.miningBeneficiaryCalculator = miningBeneficiaryCalculator;
this.extraDataCalculator = extraDataCalculator;
@ -112,6 +114,7 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
this.protocolSchedule = protocolSchedule;
this.parentHeader = parentHeader;
this.depositContractAddress = depositContractAddress;
this.ethScheduler = ethScheduler;
blockHeaderFunctions = ScheduleBasedBlockHeaderFunctions.create(protocolSchedule);
}
@ -360,7 +363,8 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
protocolSpec.getFeeMarket(),
protocolSpec.getGasCalculator(),
protocolSpec.getGasLimitCalculator(),
pluginTransactionSelector);
pluginTransactionSelector,
ethScheduler);
if (transactions.isPresent()) {
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.core.BlockHeader;
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.mainnet.AbstractGasLimitSpecification;
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 final ExecutorService executorService = Executors.newCachedThreadPool();
private final ExecutorService executorService =
Executors.newCachedThreadPool(r -> new Thread(r, "MinerExecutor"));
protected final ProtocolContext protocolContext;
protected final ProtocolSchedule protocolSchedule;
protected final TransactionPool transactionPool;
protected final AbstractBlockScheduler blockScheduler;
protected final MiningParameters miningParameters;
protected final EthScheduler ethScheduler;
private final AtomicBoolean stopped = new AtomicBoolean(false);
protected AbstractMinerExecutor(
@ -54,12 +57,14 @@ public abstract class AbstractMinerExecutor<M extends BlockMiner<? extends Abstr
final ProtocolSchedule protocolSchedule,
final TransactionPool transactionPool,
final MiningParameters miningParams,
final AbstractBlockScheduler blockScheduler) {
final AbstractBlockScheduler blockScheduler,
final EthScheduler ethScheduler) {
this.protocolContext = protocolContext;
this.protocolSchedule = protocolSchedule;
this.transactionPool = transactionPool;
this.blockScheduler = blockScheduler;
this.miningParameters = miningParams;
this.ethScheduler = ethScheduler;
}
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.MiningParameters;
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.mainnet.EthHash;
import org.hyperledger.besu.ethereum.mainnet.PoWSolution;
@ -44,7 +45,8 @@ public class PoWBlockCreator extends AbstractBlockCreator {
final ProtocolContext protocolContext,
final ProtocolSchedule protocolSchedule,
final PoWSolver nonceSolver,
final BlockHeader parentHeader) {
final BlockHeader parentHeader,
final EthScheduler ethScheduler) {
super(
miningParameters,
__ -> miningParameters.getCoinbase().orElseThrow(),
@ -53,7 +55,8 @@ public class PoWBlockCreator extends AbstractBlockCreator {
protocolContext,
protocolSchedule,
parentHeader,
Optional.empty());
Optional.empty(),
ethScheduler);
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.core.BlockHeader;
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.mainnet.EpochCalculator;
import org.hyperledger.besu.ethereum.mainnet.PoWSolver;
@ -41,8 +42,15 @@ public class PoWMinerExecutor extends AbstractMinerExecutor<PoWBlockMiner> {
final TransactionPool transactionPool,
final MiningParameters miningParams,
final AbstractBlockScheduler blockScheduler,
final EpochCalculator epochCalculator) {
super(protocolContext, protocolSchedule, transactionPool, miningParams, blockScheduler);
final EpochCalculator epochCalculator,
final EthScheduler ethScheduler) {
super(
protocolContext,
protocolSchedule,
transactionPool,
miningParams,
blockScheduler,
ethScheduler);
if (miningParams.getNonceGenerator().isEmpty()) {
miningParams.setNonceGenerator(new RandomNonceGenerator());
}
@ -85,7 +93,8 @@ public class PoWMinerExecutor extends AbstractMinerExecutor<PoWBlockMiner> {
protocolContext,
protocolSchedule,
solver,
parentHeader);
parentHeader,
ethScheduler);
return new PoWBlockMiner(
blockCreator, protocolSchedule, protocolContext, observers, blockScheduler, parentHeader);

@ -14,6 +14,9 @@
*/
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.Wei;
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.Transaction;
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.TransactionPool;
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.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 org.slf4j.Logger;
@ -85,6 +93,9 @@ public class BlockTransactionSelector {
private final List<AbstractTransactionSelector> transactionSelectors;
private final PluginTransactionSelector pluginTransactionSelector;
private final BlockAwareOperationTracer pluginOperationTracer;
private final EthScheduler ethScheduler;
private final AtomicBoolean isTimeout = new AtomicBoolean(false);
private WorldUpdater blockWorldStateUpdater;
public BlockTransactionSelector(
final MiningParameters miningParameters,
@ -100,12 +111,14 @@ public class BlockTransactionSelector {
final FeeMarket feeMarket,
final GasCalculator gasCalculator,
final GasLimitCalculator gasLimitCalculator,
final PluginTransactionSelector pluginTransactionSelector) {
final PluginTransactionSelector pluginTransactionSelector,
final EthScheduler ethScheduler) {
this.transactionProcessor = transactionProcessor;
this.blockchain = blockchain;
this.worldState = worldState;
this.transactionReceiptFactory = transactionReceiptFactory;
this.isCancelled = isCancelled;
this.ethScheduler = ethScheduler;
this.blockSelectionContext =
new BlockSelectionContext(
miningParameters,
@ -119,6 +132,7 @@ public class BlockTransactionSelector {
transactionSelectors = createTransactionSelectors(blockSelectionContext);
this.pluginTransactionSelector = pluginTransactionSelector;
this.pluginOperationTracer = pluginTransactionSelector.getOperationTracer();
blockWorldStateUpdater = worldState.updater();
}
private List<AbstractTransactionSelector> createTransactionSelectors(
@ -145,9 +159,7 @@ public class BlockTransactionSelector {
.setMessage("Transaction pool stats {}")
.addArgument(blockSelectionContext.transactionPool().logStats())
.log();
blockSelectionContext.transactionPool().selectTransactions(this::evaluateTransaction);
timeLimitedSelection();
LOG.atTrace()
.setMessage("Transaction selection result {}")
.addArgument(transactionSelectionResults::toTraceLog)
@ -155,6 +167,36 @@ public class BlockTransactionSelector {
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
* 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);
}
final WorldUpdater worldStateUpdater = worldState.updater();
final WorldUpdater txWorldStateUpdater = blockWorldStateUpdater.updater();
final TransactionProcessingResult processingResult =
processTransaction(pendingTransaction, worldStateUpdater);
processTransaction(pendingTransaction, txWorldStateUpdater);
var postProcessingSelectionResult =
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 =
selector.evaluateTransactionPreProcessing(
pendingTransaction, transactionSelectionResults);
if (!result.equals(TransactionSelectionResult.SELECTED)) {
if (!result.equals(SELECTED)) {
return result;
}
}
@ -243,7 +286,7 @@ public class BlockTransactionSelector {
TransactionSelectionResult result =
selector.evaluateTransactionPostProcessing(
pendingTransaction, transactionSelectionResults, processingResult);
if (!result.equals(TransactionSelectionResult.SELECTED)) {
if (!result.equals(SELECTED)) {
return result;
}
}
@ -282,14 +325,13 @@ public class BlockTransactionSelector {
*
* @param pendingTransaction The pending transaction.
* @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.
*/
private TransactionSelectionResult handleTransactionSelected(
final PendingTransaction pendingTransaction,
final TransactionProcessingResult processingResult,
final WorldUpdater worldStateUpdater) {
worldStateUpdater.commit();
final WorldUpdater txWorldStateUpdater) {
final Transaction transaction = pendingTransaction.getTransaction();
final long gasUsedByTransaction =
@ -299,17 +341,47 @@ public class BlockTransactionSelector {
final long blobGasUsed =
blockSelectionContext.gasCalculator().blobGasCost(transaction.getBlobCount());
final TransactionReceipt receipt =
transactionReceiptFactory.create(
transaction.getType(), processingResult, worldState, cumulativeGasUsed);
final boolean tooLate;
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(
pendingTransaction.getTransaction(), receipt, gasUsedByTransaction, blobGasUsed);
pluginTransactionSelector.onTransactionSelected(pendingTransaction, processingResult);
transactionSelectionResults.updateSelected(
pendingTransaction.getTransaction(), receipt, gasUsedByTransaction, blobGasUsed);
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(
final PendingTransaction pendingTransaction,
final TransactionSelectionResult selectionResult) {
transactionSelectionResults.updateNotSelected(
pendingTransaction.getTransaction(), selectionResult);
pluginTransactionSelector.onTransactionNotSelected(pendingTransaction, selectionResult);
return selectionResult;
}
private TransactionSelectionResult handleTransactionNotSelected(
final PendingTransaction pendingTransaction,
final TransactionSelectionResult selectionResult,
final WorldUpdater txWorldStateUpdater) {
txWorldStateUpdater.revert();
return handleTransactionNotSelected(pendingTransaction, selectionResult);
}
private void checkCancellation() {
if (isCancelled.get()) {
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.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -39,8 +39,13 @@ public class TransactionSelectionResults {
private final Map<TransactionType, List<Transaction>> transactionsByType =
new EnumMap<>(TransactionType.class);
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 =
new HashMap<>();
new ConcurrentHashMap<>();
private long cumulativeGasUsed = 0;
private long cumulativeBlobGasUsed = 0;
@ -92,18 +97,19 @@ public class TransactionSelectionResults {
}
public Map<Transaction, TransactionSelectionResult> getNotSelectedTransactions() {
return notSelectedTransactions;
return Map.copyOf(notSelectedTransactions);
}
public void logSelectionStats() {
if (LOG.isDebugEnabled()) {
final var notSelectedTxs = getNotSelectedTransactions();
final Map<TransactionSelectionResult, Long> notSelectedStats =
notSelectedTransactions.values().stream()
notSelectedTxs.values().stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
LOG.debug(
"Selection stats: Totals[Evaluated={}, Selected={}, NotSelected={}, Discarded={}]; Detailed[{}]",
selectedTransactions.size() + notSelectedTransactions.size(),
selectedTransactions.size() + notSelectedTxs.size(),
selectedTransactions.size(),
notSelectedStats.size(),
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.Withdrawal;
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.TransactionBroadcaster;
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.LogTopic;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import java.math.BigInteger;
import java.time.Clock;
@ -97,6 +99,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
abstract class AbstractBlockCreatorTest {
private static final Optional<Address> EMPTY_DEPOSIT_CONTRACT_ADDRESS = Optional.empty();
@Mock private WithdrawalsProcessor withdrawalsProcessor;
protected EthScheduler ethScheduler = new DeterministicEthScheduler();
@Test
void findDepositsFromReceipts() {
@ -405,7 +408,8 @@ abstract class AbstractBlockCreatorTest {
executionContextTestFixture.getProtocolContext(),
executionContextTestFixture.getProtocolSchedule(),
blockchain.getChainHeadHeader(),
depositContractAddress);
depositContractAddress,
ethScheduler);
}
static class TestBlockCreator extends AbstractBlockCreator {
@ -418,7 +422,8 @@ abstract class AbstractBlockCreatorTest {
final ProtocolContext protocolContext,
final ProtocolSchedule protocolSchedule,
final BlockHeader parentHeader,
final Optional<Address> depositContractAddress) {
final Optional<Address> depositContractAddress,
final EthScheduler ethScheduler) {
super(
miningParameters,
miningBeneficiaryCalculator,
@ -427,7 +432,8 @@ abstract class AbstractBlockCreatorTest {
protocolContext,
protocolSchedule,
parentHeader,
depositContractAddress);
depositContractAddress,
ethScheduler);
}
@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.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.anyBoolean;
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.ImmutableMiningParameters;
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.MiningParameters;
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.difficulty.fixed.FixedDifficultyProtocolSchedule;
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.mainnet.MainnetBlockHeaderFunctions;
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.PluginTransactionSelectorFactory;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
import org.hyperledger.besu.util.number.Percentage;
import java.math.BigInteger;
import java.time.Instant;
@ -83,24 +88,34 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
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 org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
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.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
public abstract class AbstractBlockTransactionSelectorTest {
protected static final double MIN_OCCUPANCY_80_PERCENT = 0.8;
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 KeyPair keyPair =
SignatureAlgorithmFactory.getInstance().generateKeyPair();
@ -113,7 +128,11 @@ public abstract class AbstractBlockTransactionSelectorTest {
protected TransactionPool transactionPool;
protected MutableWorldState worldState;
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)
protected ProtocolContext protocolContext;
@ -150,19 +169,15 @@ public abstract class AbstractBlockTransactionSelectorTest {
when(protocolContext.getWorldStateArchive().getMutable(any(), anyBoolean()))
.thenReturn(Optional.of(worldState));
when(ethContext.getEthPeers().subscribeConnect(any())).thenReturn(1L);
miningParameters =
ImmutableMiningParameters.builder()
.mutableInitValues(MutableInitValues.builder().minTransactionGasPrice(Wei.ONE).build())
.build();
transactionPool = createTransactionPool();
when(ethScheduler.scheduleBlockCreationTask(any(Runnable.class)))
.thenAnswer(invocation -> CompletableFuture.runAsync(invocation.getArgument(0)));
}
protected abstract GenesisConfigFile getGenesisConfigFile();
protected abstract ProtocolSchedule createProtocolSchedule();
protected abstract TransactionPool createTransactionPool();
protected abstract TransactionPool createTransactionPool(final MiningParameters miningParameters);
private Boolean isCancelled() {
return false;
@ -198,13 +213,13 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelector(
createBlockSelectorAndSetupTxPool(
defaultTestMiningParameters,
mainnetTransactionProcessor,
blockHeader,
Wei.ZERO,
miningBeneficiary,
Wei.ZERO,
MIN_OCCUPANCY_80_PERCENT);
NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
final TransactionSelectionResults results = selector.buildTransactionListForBlock();
@ -216,23 +231,21 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test
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 Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelector(
createBlockSelectorAndSetupTxPool(
defaultTestMiningParameters,
transactionProcessor,
blockHeader,
Wei.ZERO,
miningBeneficiary,
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();
@ -244,6 +257,18 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test
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();
for (int i = 0; i < 5; i++) {
final Transaction tx = createTransaction(i, Wei.of(7), 100_000);
@ -256,20 +281,6 @@ public abstract class AbstractBlockTransactionSelectorTest {
}
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 Transaction invalidTx = transactionsToInject.get(1);
@ -288,26 +299,24 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test
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 Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelector(
createBlockSelectorAndSetupTxPool(
defaultTestMiningParameters,
transactionProcessor,
blockHeader,
Wei.ZERO,
miningBeneficiary,
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();
@ -332,16 +341,15 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test
public void transactionTooLargeForBlockDoesNotPreventMoreBeingAddedIfBlockOccupancyNotReached() {
final ProcessableBlockHeader blockHeader = createBlock(300_000);
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelector(
createBlockSelectorAndSetupTxPool(
defaultTestMiningParameters,
transactionProcessor,
blockHeader,
Wei.ZERO,
miningBeneficiary,
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
// should end up selecting the first and third only.
@ -368,16 +376,15 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test
public void transactionSelectionStopsWhenSufficientBlockOccupancyIsReached() {
final ProcessableBlockHeader blockHeader = createBlock(300_000);
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelector(
createBlockSelectorAndSetupTxPool(
defaultTestMiningParameters,
transactionProcessor,
blockHeader,
Wei.ZERO,
miningBeneficiary,
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%
// (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 BlockTransactionSelector selector =
createBlockSelector(
createBlockSelectorAndSetupTxPool(
createMiningParameters(
Wei.ZERO, MIN_OCCUPANCY_100_PERCENT, DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME),
transactionProcessor,
blockHeader,
Wei.ZERO,
miningBeneficiary,
Wei.ZERO,
MIN_OCCUPANCY_100_PERCENT);
NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
final long minTxGasCost = getGasCalculator().getMinimumTransactionCost();
@ -467,13 +475,14 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelector(
createBlockSelectorAndSetupTxPool(
createMiningParameters(
Wei.ZERO, MIN_OCCUPANCY_100_PERCENT, DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME),
transactionProcessor,
blockHeader,
Wei.ZERO,
miningBeneficiary,
Wei.ZERO,
MIN_OCCUPANCY_100_PERCENT);
NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
final long minTxGasCost = getGasCalculator().getMinimumTransactionCost();
@ -519,13 +528,13 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelector(
createBlockSelectorAndSetupTxPool(
defaultTestMiningParameters,
transactionProcessor,
blockHeader,
Wei.ZERO,
miningBeneficiary,
Wei.ZERO,
MIN_OCCUPANCY_80_PERCENT);
NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
final Transaction validTransaction = createTransaction(0, Wei.of(10), 21_000);
@ -552,6 +561,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test
public void transactionSelectionPluginShouldWork_PreProcessing() {
final ProcessableBlockHeader blockHeader = createBlock(300_000);
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final Transaction selected = createTransaction(0, Wei.of(10), 21_000);
ensureTransactionIsValid(selected, 21_000, 0);
@ -584,9 +594,9 @@ public abstract class AbstractBlockTransactionSelectorTest {
}
};
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelectorWithTxSelPlugin(
createBlockSelectorAndSetupTxPool(
defaultTestMiningParameters,
transactionProcessor,
blockHeader,
miningBeneficiary,
@ -648,7 +658,9 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelectorWithTxSelPlugin(
createBlockSelectorAndSetupTxPool(
createMiningParameters(
Wei.ZERO, MIN_OCCUPANCY_80_PERCENT, DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME),
transactionProcessor,
blockHeader,
miningBeneficiary,
@ -678,15 +690,19 @@ public abstract class AbstractBlockTransactionSelectorTest {
final TransactionInvalidReason invalidReason = TransactionInvalidReason.PLUGIN_TX_VALIDATOR;
final Transaction invalidTransaction = createTransaction(1, Wei.of(10), 21_000);
ensureTransactionIsInvalid(invalidTransaction, TransactionInvalidReason.PLUGIN_TX_VALIDATOR);
transactionPool.addRemoteTransactions(List.of(transaction, invalidTransaction));
createBlockSelectorWithTxSelPlugin(
final BlockTransactionSelector selector =
createBlockSelectorAndSetupTxPool(
defaultTestMiningParameters,
transactionProcessor,
createBlock(300_000),
AddressHelpers.ofValue(1),
Wei.ZERO,
transactionSelectorFactory)
.buildTransactionListForBlock();
transactionSelectorFactory);
transactionPool.addRemoteTransactions(List.of(transaction, invalidTransaction));
selector.buildTransactionListForBlock();
ArgumentCaptor<PendingTransaction> argumentCaptor =
ArgumentCaptor.forClass(PendingTransaction.class);
@ -709,21 +725,20 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test
public void transactionWithIncorrectNonceRemainsInPoolAndNotSelected() {
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 BlockTransactionSelector selector =
createBlockSelector(
createBlockSelectorAndSetupTxPool(
defaultTestMiningParameters,
transactionProcessor,
blockHeader,
Wei.ZERO,
miningBeneficiary,
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();
@ -740,22 +755,25 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test
public void increaseOfMinGasPriceAtRuntimeExcludeTxFromBeingSelected() {
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 Address miningBeneficiary = AddressHelpers.ofValue(1);
final MiningParameters miningParameters =
ImmutableMiningParameters.builder().from(defaultTestMiningParameters).build();
final BlockTransactionSelector selector =
createBlockSelector(
createBlockSelectorAndSetupTxPool(
miningParameters,
transactionProcessor,
blockHeader,
Wei.ZERO,
miningBeneficiary,
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
miningParameters.setMinTransactionGasPrice(Wei.of(10));
@ -774,22 +792,23 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test
public void decreaseOfMinGasPriceAtRuntimeIncludeTxThatWasPreviouslyNotSelected() {
final Transaction transaction = createTransaction(0, Wei.of(7L), 100_000);
transactionPool.addRemoteTransactions(List.of(transaction));
ensureTransactionIsValid(transaction, 0, 5);
final MiningParameters miningParameters =
ImmutableMiningParameters.builder().from(defaultTestMiningParameters).build();
final ProcessableBlockHeader blockHeader = createBlock(500_000);
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector1 =
createBlockSelector(
createBlockSelectorAndSetupTxPool(
miningParameters,
transactionProcessor,
blockHeader,
Wei.ZERO,
miningBeneficiary,
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
miningParameters.setMinTransactionGasPrice(Wei.of(10));
@ -809,12 +828,12 @@ public abstract class AbstractBlockTransactionSelectorTest {
final BlockTransactionSelector selector2 =
createBlockSelector(
miningParameters,
transactionProcessor,
blockHeader,
Wei.ZERO,
miningBeneficiary,
Wei.ZERO,
MIN_OCCUPANCY_80_PERCENT);
NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
final TransactionSelectionResults results2 = selector2.buildTransactionListForBlock();
@ -826,22 +845,25 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test
public void shouldNotSelectTransactionsWithPriorityFeeLessThanConfig() {
ProcessableBlockHeader blockHeader = createBlock(5_000_000, Wei.ONE);
final MiningParameters miningParameters =
ImmutableMiningParameters.builder().from(defaultTestMiningParameters).build();
miningParameters.setMinPriorityFeePerGas(Wei.of(7));
final Transaction txSelected = createTransaction(1, Wei.of(8), 100_000);
ensureTransactionIsValid(txSelected);
// transaction txNotSelected should not be selected
final Transaction txNotSelected = createTransaction(2, Wei.of(7), 100_000);
ensureTransactionIsValid(txNotSelected);
transactionPool.addRemoteTransactions(List.of(txSelected, txNotSelected));
final BlockTransactionSelector selector =
createBlockSelector(
createBlockSelectorAndSetupTxPool(
miningParameters,
transactionProcessor,
blockHeader,
Wei.ZERO,
AddressHelpers.ofValue(1),
Wei.ZERO,
MIN_OCCUPANCY_100_PERCENT);
NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
transactionPool.addRemoteTransactions(List.of(txSelected, txNotSelected));
final TransactionSelectionResults results = selector.buildTransactionListForBlock();
@ -852,41 +874,136 @@ public abstract class AbstractBlockTransactionSelectorTest {
txNotSelected, TransactionSelectionResult.PRIORITY_FEE_PER_GAS_BELOW_CURRENT_MIN));
}
protected BlockTransactionSelector createBlockSelector(
final MainnetTransactionProcessor transactionProcessor,
final ProcessableBlockHeader blockHeader,
final Wei minGasPrice,
final Address miningBeneficiary,
final Wei blobGasPrice,
final double minBlockOccupancyRatio) {
@ParameterizedTest
@MethodSource("subsetOfPendingTransactionsIncludedWhenTxSelectionMaxTimeIsOver")
public void subsetOfPendingTransactionsIncludedWhenTxSelectionMaxTimeIsOver(
final boolean isPoa,
final boolean preProcessingTooLate,
final boolean processingTooLate,
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 =
new BlockTransactionSelector(
miningParameters
.setMinTransactionGasPrice(minGasPrice)
.setMinBlockOccupancyRatio(minBlockOccupancyRatio),
createBlockSelectorAndSetupTxPool(
isPoa
? createMiningParameters(
Wei.ZERO, MIN_OCCUPANCY_100_PERCENT, poaMinBlockTime, Percentage.fromInt(75))
: createMiningParameters(
Wei.ZERO, MIN_OCCUPANCY_100_PERCENT, blockTxsSelectionMaxTime),
transactionProcessor,
blockchain,
worldState,
transactionPool,
blockHeader,
this::createReceipt,
this::isCancelled,
miningBeneficiary,
blobGasPrice,
getFeeMarket(),
new LondonGasCalculator(),
GasLimitCalculator.constant(),
AllAcceptingTransactionSelector.INSTANCE);
Wei.ZERO,
transactionSelectorFactory);
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 ProcessableBlockHeader blockHeader,
final Address miningBeneficiary,
final Wei blobGasPrice,
final PluginTransactionSelectorFactory transactionSelectorFactory) {
final BlockTransactionSelector selector =
new BlockTransactionSelector(
miningParameters,
@ -902,7 +1019,8 @@ public abstract class AbstractBlockTransactionSelectorTest {
getFeeMarket(),
new LondonGasCalculator(),
GasLimitCalculator.constant(),
transactionSelectorFactory.create());
transactionSelectorFactory.create(),
ethScheduler);
return selector;
}
@ -965,15 +1083,28 @@ public abstract class AbstractBlockTransactionSelectorTest {
protected void ensureTransactionIsValid(
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(
any(), any(), any(), eq(tx), any(), any(), any(), anyBoolean(), any(), any()))
.thenReturn(
TransactionProcessingResult.successful(
new ArrayList<>(),
gasUsedByTransaction,
gasRemaining,
Bytes.EMPTY,
ValidationResult.valid()));
.thenAnswer(
invocation -> {
if (processingTime > 0) {
Thread.sleep(processingTime);
}
return TransactionProcessingResult.successful(
new ArrayList<>(),
gasUsedByTransaction,
gasRemaining,
Bytes.EMPTY,
ValidationResult.valid());
});
}
protected void ensureTransactionIsInvalid(
@ -986,4 +1117,35 @@ public abstract class AbstractBlockTransactionSelectorTest {
private BlockHeader blockHeader(final long number) {
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 org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
@ -61,7 +62,7 @@ public class LegacyFeeMarketBlockTransactionSelectorTest
}
@Override
protected TransactionPool createTransactionPool() {
protected TransactionPool createTransactionPool(final MiningParameters miningParameters) {
final TransactionPoolConfiguration poolConf =
ImmutableTransactionPoolConfiguration.builder()
.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.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 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.TransactionSelectionResults;
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.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
@ -71,7 +74,7 @@ public class LondonFeeMarketBlockTransactionSelectorTest
}
@Override
protected TransactionPool createTransactionPool() {
protected TransactionPool createTransactionPool(final MiningParameters miningParameters) {
final TransactionPoolConfiguration poolConf =
ImmutableTransactionPoolConfiguration.builder()
.txPoolMaxSize(5)
@ -105,13 +108,14 @@ public class LondonFeeMarketBlockTransactionSelectorTest
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelector(
createBlockSelectorAndSetupTxPool(
createMiningParameters(
Wei.of(6), MIN_OCCUPANCY_80_PERCENT, DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME),
transactionProcessor,
blockHeader,
Wei.of(6),
miningBeneficiary,
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)
// 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 BlockTransactionSelector selector =
createBlockSelector(
createBlockSelectorAndSetupTxPool(
createMiningParameters(
Wei.of(6), MIN_OCCUPANCY_80_PERCENT, DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME),
transactionProcessor,
blockHeader,
Wei.of(6),
miningBeneficiary,
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)
// 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 BlockTransactionSelector selector =
createBlockSelector(
createBlockSelectorAndSetupTxPool(
createMiningParameters(
Wei.of(6), MIN_OCCUPANCY_80_PERCENT, DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME),
transactionProcessor,
blockHeader,
Wei.of(6),
miningBeneficiary,
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)
// 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 txLondon2 = createEIP1559Transaction(3, Wei.ONE, Wei.ONE, 100_000);
transactionPool.addRemoteTransactions(List.of(txFrontier1, txLondon1, txFrontier2, txLondon2));
ensureTransactionIsValid(txFrontier1);
ensureTransactionIsValid(txLondon1);
ensureTransactionIsValid(txFrontier2);
@ -201,13 +205,15 @@ public class LondonFeeMarketBlockTransactionSelectorTest
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelector(
createBlockSelectorAndSetupTxPool(
defaultTestMiningParameters,
transactionProcessor,
blockHeader,
Wei.ZERO,
miningBeneficiary,
Wei.ZERO,
MIN_OCCUPANCY_80_PERCENT);
NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY);
transactionPool.addRemoteTransactions(List.of(txFrontier1, txLondon1, txFrontier2, txLondon2));
final TransactionSelectionResults results = selector.buildTransactionListForBlock();
@ -220,6 +226,8 @@ public class LondonFeeMarketBlockTransactionSelectorTest
@Override
public void shouldNotSelectTransactionsWithPriorityFeeLessThanConfig() {
ProcessableBlockHeader blockHeader = createBlock(5_000_000, Wei.ONE);
final MiningParameters miningParameters =
ImmutableMiningParameters.builder().from(defaultTestMiningParameters).build();
miningParameters.setMinPriorityFeePerGas(Wei.of(7));
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);
ensureTransactionIsValid(txNotSelected2);
transactionPool.addRemoteTransactions(
List.of(txSelected1, txNotSelected1, txSelected2, txNotSelected2));
assertThat(transactionPool.getPendingTransactions().size()).isEqualTo(4);
final BlockTransactionSelector selector =
createBlockSelector(
createBlockSelectorAndSetupTxPool(
miningParameters,
transactionProcessor,
blockHeader,
Wei.ZERO,
AddressHelpers.ofValue(1),
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();

@ -115,7 +115,8 @@ class PoWBlockCreatorTest extends AbstractBlockCreatorTest {
executionContextTestFixture.getProtocolContext(),
executionContextTestFixture.getProtocolSchedule(),
solver,
executionContextTestFixture.getBlockchain().getChainHeadHeader());
executionContextTestFixture.getBlockchain().getChainHeadHeader(),
ethScheduler);
// A Hashrate should not exist in the block creator prior to creating a block
assertThat(blockCreator.getHashesPerSecond()).isNotPresent();
@ -168,7 +169,8 @@ class PoWBlockCreatorTest extends AbstractBlockCreatorTest {
executionContextTestFixture.getProtocolContext(),
executionContextTestFixture.getProtocolSchedule(),
solver,
executionContextTestFixture.getBlockchain().getChainHeadHeader());
executionContextTestFixture.getBlockchain().getChainHeadHeader(),
ethScheduler);
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
@ -212,7 +214,8 @@ class PoWBlockCreatorTest extends AbstractBlockCreatorTest {
executionContextTestFixture.getProtocolContext(),
executionContextTestFixture.getProtocolSchedule(),
solver,
executionContextTestFixture.getBlockchain().getChainHeadHeader());
executionContextTestFixture.getBlockchain().getChainHeadHeader(),
ethScheduler);
final MutableWorldState mutableWorldState =
executionContextTestFixture.getStateArchive().getMutable();
@ -278,7 +281,8 @@ class PoWBlockCreatorTest extends AbstractBlockCreatorTest {
executionContextTestFixture.getProtocolContext(),
executionContextTestFixture.getProtocolSchedule(),
solver,
executionContextTestFixture.getBlockchain().getChainHeadHeader());
executionContextTestFixture.getBlockchain().getChainHeadHeader(),
ethScheduler);
final MutableWorldState mutableWorldState =
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.MiningParameters;
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.TransactionBroadcaster;
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.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.testutil.TestClock;
import org.hyperledger.besu.util.Subscribers;
@ -44,6 +46,7 @@ import org.junit.jupiter.api.Test;
public class PoWMinerExecutorTest {
private final MetricsSystem metricsSystem = new NoOpMetricsSystem();
private final EthScheduler ethScheduler = new DeterministicEthScheduler();
@Test
public void startingMiningWithoutCoinbaseThrowsException() {
@ -58,7 +61,8 @@ public class PoWMinerExecutorTest {
transactionPool,
miningParameters,
new DefaultBlockScheduler(1, 10, TestClock.fixed()),
new EpochCalculator.DefaultEpochCalculator());
new EpochCalculator.DefaultEpochCalculator(),
ethScheduler);
assertThatExceptionOfType(CoinbaseNotSetException.class)
.isThrownBy(() -> executor.startAsyncMining(Subscribers.create(), Subscribers.none(), null))
@ -78,7 +82,8 @@ public class PoWMinerExecutorTest {
transactionPool,
miningParameters,
new DefaultBlockScheduler(1, 10, TestClock.fixed()),
new EpochCalculator.DefaultEpochCalculator());
new EpochCalculator.DefaultEpochCalculator(),
ethScheduler);
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> executor.setCoinbase(null))

@ -28,12 +28,13 @@ import com.google.common.cache.CacheBuilder;
public class BadBlockManager {
public static final int MAX_BAD_BLOCKS_SIZE = 100;
private final Cache<Hash, Block> badBlocks =
CacheBuilder.newBuilder().maximumSize(100).concurrencyLevel(1).build();
CacheBuilder.newBuilder().maximumSize(MAX_BAD_BLOCKS_SIZE).concurrencyLevel(1).build();
private final Cache<Hash, BlockHeader> badHeaders =
CacheBuilder.newBuilder().maximumSize(100).concurrencyLevel(1).build();
CacheBuilder.newBuilder().maximumSize(MAX_BAD_BLOCKS_SIZE).concurrencyLevel(1).build();
private final Cache<Hash, Hash> latestValidHashes =
CacheBuilder.newBuilder().maximumSize(100).concurrencyLevel(1).build();
CacheBuilder.newBuilder().maximumSize(MAX_BAD_BLOCKS_SIZE).concurrencyLevel(1).build();
/**
* Add a new invalid block.

@ -16,12 +16,16 @@ package org.hyperledger.besu.ethereum.core;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.util.number.Percentage;
import java.time.Duration;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.concurrent.TimeUnit;
import com.google.common.annotations.VisibleForTesting;
import org.apache.tuweni.bytes.Bytes;
import org.immutables.value.Value;
@ -34,6 +38,7 @@ public abstract class MiningParameters {
ImmutableMiningParameters.MutableInitValues.builder().isMiningEnabled(false).build())
.build();
@VisibleForTesting
public static final MiningParameters newDefault() {
return ImmutableMiningParameters.builder().build();
}
@ -261,6 +266,8 @@ public abstract class MiningParameters {
int DEFAULT_MAX_OMMERS_DEPTH = 8;
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_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();
@ -298,5 +305,27 @@ public abstract class MiningParameters {
default String getStratumExtranonce() {
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.TransactionTestFixture;
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.PendingTransaction;
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.RocksDBMetricsFactory;
import org.hyperledger.besu.plugin.services.storage.rocksdb.configuration.RocksDBFactoryConfiguration;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import java.nio.file.Path;
import java.util.Arrays;
@ -92,6 +94,7 @@ public abstract class AbstractIsolationTests {
protected BonsaiWorldStateKeyValueStorage bonsaiWorldStateStorage;
protected ProtocolContext protocolContext;
protected EthContext ethContext;
protected EthScheduler ethScheduler = new DeterministicEthScheduler();
final Function<String, KeyPair> asKeyPair =
key ->
SignatureAlgorithmFactory.getInstance()
@ -214,7 +217,8 @@ public abstract class AbstractIsolationTests {
final TransactionPool transactionPool,
final ProtocolContext protocolContext,
final ProtocolSchedule protocolSchedule,
final BlockHeader parentHeader) {
final BlockHeader parentHeader,
final EthScheduler ethScheduler) {
super(
miningParameters,
miningBeneficiaryCalculator,
@ -223,14 +227,16 @@ public abstract class AbstractIsolationTests {
protocolContext,
protocolSchedule,
parentHeader,
Optional.empty());
Optional.empty(),
ethScheduler);
}
static TestBlockCreator forHeader(
final BlockHeader parentHeader,
final ProtocolContext protocolContext,
final ProtocolSchedule protocolSchedule,
final TransactionPool transactionPool) {
final TransactionPool transactionPool,
final EthScheduler ethScheduler) {
final MiningParameters miningParameters =
ImmutableMiningParameters.builder()
@ -251,7 +257,8 @@ public abstract class AbstractIsolationTests {
transactionPool,
protocolContext,
protocolSchedule,
parentHeader);
parentHeader,
ethScheduler);
}
@Override
@ -282,7 +289,8 @@ public abstract class AbstractIsolationTests {
protected Block forTransactions(
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())
.getBlock();
}

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

@ -19,6 +19,7 @@ import static org.hyperledger.besu.util.FutureUtils.exceptionallyCompose;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.BlockValidator;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.BadBlockManager;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
@ -46,6 +47,7 @@ public class BackwardSyncContext {
private static final int DEFAULT_MAX_RETRIES = 20;
private static final long MILLIS_DELAY_BETWEEN_PROGRESS_LOG = 10_000L;
private static final long DEFAULT_MILLIS_BETWEEN_RETRIES = 5000;
private static final int DEFAULT_MAX_CHAIN_EVENT_ENTRIES = BadBlockManager.MAX_BAD_BLOCKS_SIZE;
protected final ProtocolContext protocolContext;
private final ProtocolSchedule protocolSchedule;
@ -56,6 +58,7 @@ public class BackwardSyncContext {
private final BackwardChain backwardChain;
private int batchSize = BATCH_SIZE;
private final int maxRetries;
private final int maxBadChainEventEntries;
private final long millisBetweenRetries = DEFAULT_MILLIS_BETWEEN_RETRIES;
private final Subscribers<BadChainListener> badChainListeners = Subscribers.create();
@ -73,7 +76,8 @@ public class BackwardSyncContext {
ethContext,
syncState,
backwardChain,
DEFAULT_MAX_RETRIES);
DEFAULT_MAX_RETRIES,
DEFAULT_MAX_CHAIN_EVENT_ENTRIES);
}
public BackwardSyncContext(
@ -83,7 +87,8 @@ public class BackwardSyncContext {
final EthContext ethContext,
final SyncState syncState,
final BackwardChain backwardChain,
final int maxRetries) {
final int maxRetries,
final int maxBadChainEventEntries) {
this.protocolContext = protocolContext;
this.protocolSchedule = protocolSchedule;
@ -92,6 +97,7 @@ public class BackwardSyncContext {
this.syncState = syncState;
this.backwardChain = backwardChain;
this.maxRetries = maxRetries;
this.maxBadChainEventEntries = maxBadChainEventEntries;
}
public synchronized boolean isSyncing() {
@ -368,7 +374,9 @@ public class BackwardSyncContext {
Optional<Hash> descendant = backwardChain.getDescendant(badBlock.getHash());
while (descendant.isPresent()) {
while (descendant.isPresent()
&& badBlockDescendants.size() < maxBadChainEventEntries
&& badBlockHeaderDescendants.size() < maxBadChainEventEntries) {
final Optional<Block> block = backwardChain.getBlock(descendant.get());
if (block.isPresent()) {
badBlockDescendants.add(block.get());

@ -49,11 +49,6 @@ public class TransactionReplacementByFeeMarketRule implements TransactionPoolRep
Wei newEffPriority =
newPendingTransaction.getTransaction().getEffectivePriorityFeePerGas(maybeBaseFee);
// bail early if price is not strictly positive
if (newEffPrice.equals(Wei.ZERO)) {
return false;
}
Wei curEffPrice = priceOf(existingPendingTransaction.getTransaction(), maybeBaseFee);
Wei curEffPriority =
existingPendingTransaction.getTransaction().getEffectivePriorityFeePerGas(maybeBaseFee);
@ -77,6 +72,6 @@ public class TransactionReplacementByFeeMarketRule implements TransactionPoolRep
}
private boolean isBumpedBy(final Wei val, final Wei bumpVal, final Percentage percent) {
return val.multiply(percent.getValue() + 100L).compareTo(bumpVal.multiply(100L)) < 0;
return val.multiply(percent.getValue() + 100L).compareTo(bumpVal.multiply(100L)) <= 0;
}
}

@ -41,6 +41,6 @@ public class TransactionReplacementByGasPriceRule implements TransactionPoolRepl
final Wei replacementThreshold =
existingPendingTransaction.getGasPrice().multiply(100 + priceBump.getValue()).divide(100);
return newPendingTransaction.getGasPrice().compareTo(replacementThreshold) > 0;
return newPendingTransaction.getGasPrice().compareTo(replacementThreshold) >= 0;
}
}

@ -22,6 +22,7 @@ import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfigurati
import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.time.Clock;
import java.util.Comparator;
import java.util.Iterator;
import java.util.NavigableSet;
import java.util.TreeSet;
@ -39,8 +40,7 @@ public class GasPricePendingTransactionsSorter extends AbstractPendingTransactio
new TreeSet<>(
comparing(PendingTransaction::hasPriority)
.thenComparing(PendingTransaction::getGasPrice)
.thenComparing(PendingTransaction::getAddedAt)
.thenComparing(PendingTransaction::getSequence)
.thenComparing(PendingTransaction::getSequence, Comparator.reverseOrder())
.reversed());
public GasPricePendingTransactionsSorter(

@ -1088,8 +1088,9 @@ public final class EthProtocolManagerTest {
final ExecutorService transactions = mock(ExecutorService.class);
final ExecutorService services = mock(ExecutorService.class);
final ExecutorService computations = mock(ExecutorService.class);
final ExecutorService blockCreation = mock(ExecutorService.class);
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.
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.eth.EthProtocol;
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.peervalidation.PeerValidator;
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.WorldStateArchive;
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 java.math.BigInteger;

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

@ -21,7 +21,9 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.testutil.MockExecutorService;
import org.hyperledger.besu.testutil.MockScheduledExecutor;
import java.time.Duration;
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.eth.EthProtocol;
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.EthMessages;
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.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.testutil.TestClock;
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.BlockDataGenerator;
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.EthProtocolManager;
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.EthPV62;
import org.hyperledger.besu.ethereum.eth.messages.GetBlockHeadersMessage;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import java.util.List;
import java.util.concurrent.CompletableFuture;

@ -17,10 +17,11 @@
package org.hyperledger.besu.ethereum.eth.sync.backwardsync;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider.createInMemoryBlockchain;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@ -64,6 +65,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Spy;
@ -75,11 +77,12 @@ import org.mockito.quality.Strictness;
@MockitoSettings(strictness = Strictness.LENIENT)
public class BackwardSyncContextTest {
public static final int REMOTE_HEIGHT = 50;
public static final int LOCAL_HEIGHT = 25;
public static final int REMOTE_HEIGHT = 50;
public static final int UNCLE_HEIGHT = 25 - 3;
public static final int NUM_OF_RETRIES = 100;
public static final int TEST_MAX_BAD_CHAIN_EVENT_ENTRIES = 25;
private BackwardSyncContext context;
@ -173,7 +176,8 @@ public class BackwardSyncContextTest {
ethContext,
syncState,
backwardChain,
NUM_OF_RETRIES));
NUM_OF_RETRIES,
TEST_MAX_BAD_CHAIN_EVENT_ENTRIES));
doReturn(true).when(context).isReady();
doReturn(2).when(context).getBatchSize();
}
@ -347,6 +351,72 @@ public class BackwardSyncContextTest {
block, Collections.emptyList(), List.of(childBlockHeader, grandChildBlockHeader));
}
@Test
@SuppressWarnings("unchecked")
public void shouldEmitBadChainEventWithIncludedBlockHeadersLimitedToMaxBadChainEventsSize() {
Block block = Mockito.mock(Block.class);
BlockHeader blockHeader = Mockito.mock(BlockHeader.class);
when(block.getHash()).thenReturn(Hash.fromHexStringLenient("0x42"));
when(blockHeader.getHash()).thenReturn(Hash.fromHexStringLenient("0x42"));
BadChainListener badChainListener = Mockito.mock(BadChainListener.class);
context.subscribeBadChainListener(badChainListener);
backwardChain.clear();
for (int i = REMOTE_HEIGHT; i >= 0; i--) {
backwardChain.prependAncestorsHeader(remoteBlockchain.getBlockByNumber(i).get().getHeader());
}
backwardChain.prependAncestorsHeader(blockHeader);
doReturn(blockValidator).when(context).getBlockValidatorForBlock(any());
BlockProcessingResult result = new BlockProcessingResult("custom error");
doReturn(result).when(blockValidator).validateAndProcessBlock(any(), any(), any(), any());
assertThatThrownBy(() -> context.saveBlock(block))
.isInstanceOf(BackwardSyncException.class)
.hasMessageContaining("custom error");
final ArgumentCaptor<List<BlockHeader>> badBlockHeaderDescendants =
ArgumentCaptor.forClass(List.class);
verify(badChainListener)
.onBadChain(eq(block), eq(Collections.emptyList()), badBlockHeaderDescendants.capture());
assertThat(badBlockHeaderDescendants.getValue()).hasSize(TEST_MAX_BAD_CHAIN_EVENT_ENTRIES);
}
@SuppressWarnings("unchecked")
@Test
public void shouldEmitBadChainEventWithIncludedBlocksLimitedToMaxBadChainEventsSize() {
Block block = Mockito.mock(Block.class);
BlockHeader blockHeader = Mockito.mock(BlockHeader.class);
when(block.getHash()).thenReturn(Hash.fromHexStringLenient("0x42"));
when(blockHeader.getHash()).thenReturn(Hash.fromHexStringLenient("0x42"));
BadChainListener badChainListener = Mockito.mock(BadChainListener.class);
context.subscribeBadChainListener(badChainListener);
backwardChain.clear();
for (int i = REMOTE_HEIGHT; i >= 0; i--) {
backwardChain.prependAncestorsHeader(remoteBlockchain.getBlockByNumber(i).get().getHeader());
}
backwardChain.prependAncestorsHeader(blockHeader);
for (int i = REMOTE_HEIGHT; i >= 0; i--) {
backwardChain.appendTrustedBlock(remoteBlockchain.getBlockByNumber(i).get());
}
doReturn(blockValidator).when(context).getBlockValidatorForBlock(any());
BlockProcessingResult result = new BlockProcessingResult("custom error");
doReturn(result).when(blockValidator).validateAndProcessBlock(any(), any(), any(), any());
assertThatThrownBy(() -> context.saveBlock(block))
.isInstanceOf(BackwardSyncException.class)
.hasMessageContaining("custom error");
final ArgumentCaptor<List<Block>> badBlockDescendants = ArgumentCaptor.forClass(List.class);
verify(badChainListener)
.onBadChain(eq(block), badBlockDescendants.capture(), eq(Collections.emptyList()));
assertThat(badBlockDescendants.getValue()).hasSize(TEST_MAX_BAD_CHAIN_EVENT_ENTRIES);
}
@Test
public void shouldFailAfterMaxNumberOfRetries() {
doReturn(CompletableFuture.failedFuture(new Exception()))

@ -29,7 +29,6 @@ import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.BlockHeader;
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.EthProtocolManager;
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.ProtocolSchedule;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import java.nio.charset.StandardCharsets;
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.MutableWorldState;
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.EthProtocolManager;
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.services.kvstore.InMemoryKeyValueStorage;
import org.hyperledger.besu.services.tasks.InMemoryTasksPriorityQueues;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.testutil.MockExecutorService;
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.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.eth.manager.DeterministicEthScheduler;
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.FastSyncState;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import java.util.OptionalLong;

@ -19,7 +19,6 @@ import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.hyperledger.besu.ethereum.mainnet.ValidationResult.valid;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.EXCEEDS_BLOCK_GAS_LIMIT;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.GAS_PRICE_TOO_LOW;
@ -91,6 +90,7 @@ import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionValidator;
import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionValidatorFactory;
import org.hyperledger.besu.util.number.Percentage;
import java.math.BigInteger;
import java.util.Arrays;
@ -106,6 +106,7 @@ import java.util.function.Supplier;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIf;
import org.junit.jupiter.api.condition.EnabledIf;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
@ -1033,6 +1034,157 @@ public abstract class AbstractTransactionPoolTest {
addAndAssertTransactionViaApiValid(transaction, noLocalPriority);
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void samePriceTxReplacementWhenPriceBumpIsZeroFrontier(final boolean noLocalPriority) {
transactionPool =
createTransactionPool(b -> b.priceBump(Percentage.ZERO).noLocalPriority(noLocalPriority));
when(miningParameters.getMinTransactionGasPrice()).thenReturn(Wei.ZERO);
final Transaction transaction1a =
createBaseTransactionGasPriceMarket(0)
.gasPrice(Wei.ZERO)
.to(Optional.of(Address.ALTBN128_ADD))
.createTransaction(KEY_PAIR1);
givenTransactionIsValid(transaction1a);
transactionPool.addRemoteTransactions(List.of(transaction1a));
assertThat(transactionPool.getPendingTransactions())
.map(PendingTransaction::getTransaction)
.containsOnly(transaction1a);
final Transaction transaction1b =
createBaseTransactionGasPriceMarket(0)
.gasPrice(Wei.ZERO)
.to(Optional.of(Address.KZG_POINT_EVAL))
.createTransaction(KEY_PAIR1);
givenTransactionIsValid(transaction1b);
transactionPool.addRemoteTransactions(List.of(transaction1b));
assertThat(transactionPool.getPendingTransactions())
.map(PendingTransaction::getTransaction)
.containsOnly(transaction1b);
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
@EnabledIf("isBaseFeeMarket")
public void replaceSamePriceTxWhenPriceBumpIsZeroLondon(final boolean noLocalPriority) {
transactionPool =
createTransactionPool(b -> b.priceBump(Percentage.ZERO).noLocalPriority(noLocalPriority));
when(miningParameters.getMinTransactionGasPrice()).thenReturn(Wei.ZERO);
final Transaction transaction1a =
createBaseTransactionBaseFeeMarket(0)
.maxFeePerGas(Optional.of(Wei.ZERO))
.maxPriorityFeePerGas(Optional.of(Wei.ZERO))
.to(Optional.of(Address.ALTBN128_ADD))
.createTransaction(KEY_PAIR1);
givenTransactionIsValid(transaction1a);
transactionPool.addRemoteTransactions(List.of(transaction1a));
assertThat(transactionPool.getPendingTransactions())
.map(PendingTransaction::getTransaction)
.containsOnly(transaction1a);
final Transaction transaction1b =
createBaseTransactionBaseFeeMarket(0)
.maxFeePerGas(Optional.of(Wei.ZERO))
.maxPriorityFeePerGas(Optional.of(Wei.ZERO))
.to(Optional.of(Address.KZG_POINT_EVAL))
.createTransaction(KEY_PAIR1);
givenTransactionIsValid(transaction1b);
transactionPool.addRemoteTransactions(List.of(transaction1b));
assertThat(transactionPool.getPendingTransactions())
.map(PendingTransaction::getTransaction)
.containsOnly(transaction1b);
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
@EnabledIf("isBaseFeeMarket")
public void replaceSamePriceTxWhenPriceBumpIsZeroLondonToFrontier(final boolean noLocalPriority) {
transactionPool =
createTransactionPool(b -> b.priceBump(Percentage.ZERO).noLocalPriority(noLocalPriority));
when(miningParameters.getMinTransactionGasPrice()).thenReturn(Wei.ZERO);
final Transaction transaction1a =
createBaseTransactionBaseFeeMarket(0)
.maxFeePerGas(Optional.of(Wei.ZERO))
.maxPriorityFeePerGas(Optional.of(Wei.ZERO))
.to(Optional.of(Address.ALTBN128_ADD))
.createTransaction(KEY_PAIR1);
givenTransactionIsValid(transaction1a);
transactionPool.addRemoteTransactions(List.of(transaction1a));
assertThat(transactionPool.getPendingTransactions())
.map(PendingTransaction::getTransaction)
.containsOnly(transaction1a);
final Transaction transaction1b =
createBaseTransactionGasPriceMarket(0)
.gasPrice(Wei.ZERO)
.to(Optional.of(Address.KZG_POINT_EVAL))
.createTransaction(KEY_PAIR1);
givenTransactionIsValid(transaction1b);
transactionPool.addRemoteTransactions(List.of(transaction1b));
assertThat(transactionPool.getPendingTransactions())
.map(PendingTransaction::getTransaction)
.containsOnly(transaction1b);
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
@EnabledIf("isBaseFeeMarket")
public void replaceSamePriceTxWhenPriceBumpIsZeroFrontierToLondon(final boolean noLocalPriority) {
transactionPool =
createTransactionPool(b -> b.priceBump(Percentage.ZERO).noLocalPriority(noLocalPriority));
when(miningParameters.getMinTransactionGasPrice()).thenReturn(Wei.ZERO);
final Transaction transaction1a =
createBaseTransactionGasPriceMarket(0)
.gasPrice(Wei.ZERO)
.to(Optional.of(Address.KZG_POINT_EVAL))
.createTransaction(KEY_PAIR1);
givenTransactionIsValid(transaction1a);
transactionPool.addRemoteTransactions(List.of(transaction1a));
assertThat(transactionPool.getPendingTransactions())
.map(PendingTransaction::getTransaction)
.containsOnly(transaction1a);
final Transaction transaction1b =
createBaseTransactionBaseFeeMarket(0)
.maxFeePerGas(Optional.of(Wei.ZERO))
.maxPriorityFeePerGas(Optional.of(Wei.ZERO))
.to(Optional.of(Address.ALTBN128_ADD))
.createTransaction(KEY_PAIR1);
givenTransactionIsValid(transaction1b);
transactionPool.addRemoteTransactions(List.of(transaction1b));
assertThat(transactionPool.getPendingTransactions())
.map(PendingTransaction::getTransaction)
.containsOnly(transaction1b);
}
@Test
public void shouldAcceptBaseFeeFloorGasPriceFrontierLocalPriorityTransactionsWhenMining() {
transactionPool = createTransactionPool(b -> b.noLocalPriority(false));
@ -1112,20 +1264,21 @@ public abstract class AbstractTransactionPoolTest {
@Test
public void addRemoteTransactionsShouldAllowDuplicates() {
final Transaction transaction1 = createTransaction(1, Wei.of(7L));
final Transaction transaction2 = createTransaction(2, Wei.of(7L));
final Transaction transaction3 = createTransaction(2, Wei.of(7L));
final Transaction transaction4 = createTransaction(3, Wei.of(7L));
final Transaction transaction2a = createTransaction(2, Wei.of(7L));
final Transaction transaction2b = createTransaction(2, Wei.of(7L));
final Transaction transaction3 = createTransaction(3, Wei.of(7L));
givenTransactionIsValid(transaction1);
givenTransactionIsValid(transaction2);
givenTransactionIsValid(transaction2a);
givenTransactionIsValid(transaction2b);
givenTransactionIsValid(transaction3);
givenTransactionIsValid(transaction4);
assertThatCode(
() ->
transactionPool.addRemoteTransactions(
List.of(transaction1, transaction2, transaction3, transaction4)))
.doesNotThrowAnyException();
transactionPool.addRemoteTransactions(
List.of(transaction1, transaction2a, transaction2b, transaction3));
assertThat(transactionPool.getPendingTransactions())
.map(PendingTransaction::getTransaction)
.containsExactlyInAnyOrder(transaction1, transaction2a, transaction3);
}
private static PluginTransactionValidatorFactory getPluginTransactionValidatorFactoryReturning(

@ -0,0 +1,53 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.transactions;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.Transaction;
import java.math.BigInteger;
public class AbstractTransactionReplacementTest {
protected static PendingTransaction frontierTx(final long price) {
final PendingTransaction pendingTransaction = mock(PendingTransaction.class);
final Transaction transaction =
Transaction.builder()
.chainId(BigInteger.ZERO)
.type(TransactionType.FRONTIER)
.gasPrice(Wei.of(price))
.build();
when(pendingTransaction.getTransaction()).thenReturn(transaction);
when(pendingTransaction.getGasPrice()).thenReturn(Wei.of(price));
return pendingTransaction;
}
protected static PendingTransaction eip1559Tx(
final long maxPriorityFeePerGas, final long maxFeePerGas) {
final PendingTransaction pendingTransaction = mock(PendingTransaction.class);
final Transaction transaction =
Transaction.builder()
.chainId(BigInteger.ZERO)
.type(TransactionType.EIP1559)
.maxPriorityFeePerGas(Wei.of(maxPriorityFeePerGas))
.maxFeePerGas(Wei.of(maxFeePerGas))
.build();
when(pendingTransaction.getTransaction()).thenReturn(transaction);
return pendingTransaction;
}
}

@ -0,0 +1,100 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.transactions;
import static java.util.Arrays.asList;
import static java.util.Optional.empty;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.util.number.Percentage;
import java.util.Collection;
import java.util.Optional;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class TransactionReplacementByFeeMarketRuleTest extends AbstractTransactionReplacementTest {
public static Collection<Object[]> data() {
return asList(
new Object[][] {
// basefee absent
{frontierTx(5L), frontierTx(6L), empty(), 0, false},
{frontierTx(5L), frontierTx(5L), empty(), 0, false},
{frontierTx(5L), frontierTx(4L), empty(), 0, false},
{frontierTx(100L), frontierTx(105L), empty(), 10, false},
{frontierTx(100L), frontierTx(110L), empty(), 10, false},
{frontierTx(100L), frontierTx(111L), empty(), 10, false},
// basefee present
{frontierTx(5L), frontierTx(6L), Optional.of(Wei.of(3L)), 0, false},
{frontierTx(5L), frontierTx(5L), Optional.of(Wei.of(3L)), 0, false},
{frontierTx(5L), frontierTx(4L), Optional.of(Wei.of(3L)), 0, false},
{frontierTx(100L), frontierTx(105L), Optional.of(Wei.of(3L)), 10, false},
{frontierTx(100L), frontierTx(110L), Optional.of(Wei.of(3L)), 10, false},
{frontierTx(100L), frontierTx(111L), Optional.of(Wei.of(3L)), 10, false},
// eip1559 replacing frontier
{frontierTx(5L), eip1559Tx(3L, 6L), Optional.of(Wei.of(1L)), 0, false},
{frontierTx(5L), eip1559Tx(3L, 5L), Optional.of(Wei.of(3L)), 0, true},
{frontierTx(5L), eip1559Tx(3L, 6L), Optional.of(Wei.of(3L)), 0, true},
// frontier replacing 1559
{eip1559Tx(3L, 8L), frontierTx(7L), Optional.of(Wei.of(4L)), 0, true},
{eip1559Tx(3L, 8L), frontierTx(7L), Optional.of(Wei.of(5L)), 0, false},
{eip1559Tx(3L, 8L), frontierTx(8L), Optional.of(Wei.of(4L)), 0, true},
// eip1559 replacing eip1559
{eip1559Tx(3L, 6L), eip1559Tx(3L, 6L), Optional.of(Wei.of(3L)), 0, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(3L)), 0, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(4L)), 0, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(5L)), 0, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(6L)), 0, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(7L)), 0, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(8L)), 0, true},
{eip1559Tx(10L, 200L), eip1559Tx(10L, 200L), Optional.of(Wei.of(90L)), 10, false},
{eip1559Tx(10L, 200L), eip1559Tx(15L, 200L), Optional.of(Wei.of(90L)), 10, false},
{eip1559Tx(10L, 200L), eip1559Tx(20L, 200L), Optional.of(Wei.of(90L)), 10, true},
{eip1559Tx(10L, 200L), eip1559Tx(20L, 200L), Optional.of(Wei.of(200L)), 10, true},
{eip1559Tx(10L, 200L), eip1559Tx(10L, 220L), Optional.of(Wei.of(220L)), 10, true},
// pathological, priority fee > max fee
{eip1559Tx(8L, 6L), eip1559Tx(3L, 6L), Optional.of(Wei.of(2L)), 0, false},
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(3L)), 0, true},
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(4L)), 0, true},
// pathological, eip1559 without basefee
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.empty(), 0, false},
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.empty(), 0, false},
// zero base fee market
{frontierTx(0L), frontierTx(0L), Optional.of(Wei.ZERO), 0, false},
{eip1559Tx(0L, 0L), frontierTx(0L), Optional.of(Wei.ZERO), 0, true},
{frontierTx(0L), eip1559Tx(0L, 0L), Optional.of(Wei.ZERO), 0, true},
{eip1559Tx(0L, 0L), eip1559Tx(0L, 0L), Optional.of(Wei.ZERO), 0, true},
});
}
@ParameterizedTest
@MethodSource("data")
public void shouldReplace(
final PendingTransaction oldTx,
final PendingTransaction newTx,
final Optional<Wei> baseFee,
final int priceBump,
final boolean expected) {
assertThat(
new TransactionReplacementByFeeMarketRule(Percentage.fromInt(priceBump))
.shouldReplace(oldTx, newTx, baseFee))
.isEqualTo(expected);
}
}

@ -0,0 +1,87 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.transactions;
import static java.util.Arrays.asList;
import static java.util.Optional.empty;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.util.number.Percentage;
import java.util.Collection;
import java.util.Optional;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class TransactionReplacementByGasPriceRuleTest extends AbstractTransactionReplacementTest {
public static Collection<Object[]> data() {
return asList(
new Object[][] {
// basefee absent
{frontierTx(5L), frontierTx(6L), empty(), 0, true},
{frontierTx(5L), frontierTx(5L), empty(), 0, true},
{frontierTx(5L), frontierTx(4L), empty(), 0, false},
{frontierTx(100L), frontierTx(105L), empty(), 10, false},
{frontierTx(100L), frontierTx(110L), empty(), 10, true},
{frontierTx(100L), frontierTx(111L), empty(), 10, true},
// basefee present
{frontierTx(5L), frontierTx(6L), Optional.of(Wei.of(3L)), 0, true},
{frontierTx(5L), frontierTx(5L), Optional.of(Wei.of(3L)), 0, true},
{frontierTx(5L), frontierTx(4L), Optional.of(Wei.of(3L)), 0, false},
{frontierTx(100L), frontierTx(105L), Optional.of(Wei.of(3L)), 10, false},
{frontierTx(100L), frontierTx(110L), Optional.of(Wei.of(3L)), 10, true},
{frontierTx(100L), frontierTx(111L), Optional.of(Wei.of(3L)), 10, true},
// eip1559 replacing frontier
{frontierTx(5L), eip1559Tx(3L, 6L), Optional.of(Wei.of(1L)), 0, false},
{frontierTx(5L), eip1559Tx(3L, 5L), Optional.of(Wei.of(3L)), 0, false},
{frontierTx(5L), eip1559Tx(3L, 6L), Optional.of(Wei.of(3L)), 0, false},
// frontier replacing 1559
{eip1559Tx(3L, 8L), frontierTx(7L), Optional.of(Wei.of(4L)), 0, false},
{eip1559Tx(3L, 8L), frontierTx(8L), Optional.of(Wei.of(4L)), 0, false},
// eip1559 replacing eip1559
{eip1559Tx(3L, 6L), eip1559Tx(3L, 6L), Optional.of(Wei.of(3L)), 0, false},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(3L)), 0, false},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(4L)), 0, false},
{eip1559Tx(10L, 200L), eip1559Tx(10L, 200L), Optional.of(Wei.of(90L)), 10, false},
{eip1559Tx(10L, 200L), eip1559Tx(15L, 200L), Optional.of(Wei.of(90L)), 10, false},
{eip1559Tx(10L, 200L), eip1559Tx(21L, 200L), Optional.of(Wei.of(90L)), 10, false},
// pathological, priority fee > max fee
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(3L)), 0, false},
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(4L)), 0, false},
// pathological, eip1559 without basefee
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.empty(), 0, false},
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.empty(), 0, false},
});
}
@ParameterizedTest
@MethodSource("data")
public void shouldReplace(
final PendingTransaction oldTx,
final PendingTransaction newTx,
final Optional<Wei> baseFee,
final int priceBump,
final boolean expected) {
assertThat(
new TransactionReplacementByGasPriceRule(Percentage.fromInt(priceBump))
.shouldReplace(oldTx, newTx, baseFee))
.isEqualTo(expected);
}
}

@ -20,20 +20,17 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.util.number.Percentage;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Optional;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class TransactionReplacementRulesTest {
public class TransactionReplacementRulesTest extends AbstractTransactionReplacementTest {
public static Collection<Object[]> data() {
return asList(
@ -41,39 +38,45 @@ public class TransactionReplacementRulesTest {
// TransactionReplacementByGasPriceRule
// basefee absent
{frontierTx(5L), frontierTx(6L), empty(), 0, true},
{frontierTx(5L), frontierTx(5L), empty(), 0, false},
{frontierTx(5L), frontierTx(5L), empty(), 0, true},
{frontierTx(5L), frontierTx(4L), empty(), 0, false},
{frontierTx(100L), frontierTx(105L), empty(), 10, false},
{frontierTx(100L), frontierTx(110L), empty(), 10, false},
{frontierTx(100L), frontierTx(110L), empty(), 10, true},
{frontierTx(100L), frontierTx(111L), empty(), 10, true},
// basefee present
{frontierTx(5L), frontierTx(6L), Optional.of(Wei.of(3L)), 0, true},
{frontierTx(5L), frontierTx(5L), Optional.of(Wei.of(3L)), 0, false},
{frontierTx(5L), frontierTx(5L), Optional.of(Wei.of(3L)), 0, true},
{frontierTx(5L), frontierTx(4L), Optional.of(Wei.of(3L)), 0, false},
{frontierTx(100L), frontierTx(105L), Optional.of(Wei.of(3L)), 10, false},
{frontierTx(100L), frontierTx(110L), Optional.of(Wei.of(3L)), 10, false},
{frontierTx(100L), frontierTx(110L), Optional.of(Wei.of(3L)), 10, true},
{frontierTx(100L), frontierTx(111L), Optional.of(Wei.of(3L)), 10, true},
// TransactionReplacementByFeeMarketRule
// eip1559 replacing frontier
{frontierTx(5L), eip1559Tx(3L, 6L), Optional.of(Wei.of(1L)), 0, false},
{frontierTx(5L), eip1559Tx(3L, 5L), Optional.of(Wei.of(3L)), 0, false},
{frontierTx(5L), eip1559Tx(3L, 5L), Optional.of(Wei.of(3L)), 0, true},
{frontierTx(5L), eip1559Tx(3L, 6L), Optional.of(Wei.of(3L)), 0, true},
// frontier replacing 1559
{eip1559Tx(3L, 8L), frontierTx(7L), Optional.of(Wei.of(4L)), 0, false},
{eip1559Tx(3L, 8L), frontierTx(6L), Optional.of(Wei.of(4L)), 0, false},
{eip1559Tx(3L, 8L), frontierTx(7L), Optional.of(Wei.of(4L)), 0, true},
{eip1559Tx(3L, 8L), frontierTx(8L), Optional.of(Wei.of(4L)), 0, true},
// eip1559 replacing eip1559
{eip1559Tx(3L, 6L), eip1559Tx(3L, 6L), Optional.of(Wei.of(3L)), 0, false},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(3L)), 0, false},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 6L), Optional.of(Wei.of(3L)), 0, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(3L)), 0, true},
{eip1559Tx(3L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(4L)), 0, true},
{eip1559Tx(10L, 200L), eip1559Tx(10L, 200L), Optional.of(Wei.of(90L)), 10, false},
{eip1559Tx(10L, 200L), eip1559Tx(15L, 200L), Optional.of(Wei.of(90L)), 10, false},
{eip1559Tx(10L, 200L), eip1559Tx(21L, 200L), Optional.of(Wei.of(90L)), 10, true},
// pathological, priority fee > max fee
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(3L)), 0, false},
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(3L)), 0, true},
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(4L)), 0, true},
// pathological, eip1559 without basefee
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.empty(), 0, false},
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.empty(), 0, false},
// zero base fee market
{frontierTx(0L), frontierTx(0L), Optional.of(Wei.ZERO), 0, true},
{eip1559Tx(0L, 0L), frontierTx(0L), Optional.of(Wei.ZERO), 0, true},
{frontierTx(0L), eip1559Tx(0L, 0L), Optional.of(Wei.ZERO), 0, true},
{eip1559Tx(0L, 0L), eip1559Tx(0L, 0L), Optional.of(Wei.ZERO), 0, true},
});
}
@ -93,31 +96,4 @@ public class TransactionReplacementRulesTest {
.shouldReplace(oldTx, newTx, mockHeader))
.isEqualTo(expected);
}
private static PendingTransaction frontierTx(final long price) {
final PendingTransaction pendingTransaction = mock(PendingTransaction.class);
final Transaction transaction =
Transaction.builder()
.chainId(BigInteger.ZERO)
.type(TransactionType.FRONTIER)
.gasPrice(Wei.of(price))
.build();
when(pendingTransaction.getTransaction()).thenReturn(transaction);
when(pendingTransaction.getGasPrice()).thenReturn(Wei.of(price));
return pendingTransaction;
}
private static PendingTransaction eip1559Tx(
final long maxPriorityFeePerGas, final long maxFeePerGas) {
final PendingTransaction pendingTransaction = mock(PendingTransaction.class);
final Transaction transaction =
Transaction.builder()
.chainId(BigInteger.ZERO)
.type(TransactionType.EIP1559)
.maxPriorityFeePerGas(Wei.of(maxPriorityFeePerGas))
.maxFeePerGas(Wei.of(maxFeePerGas))
.build();
when(pendingTransaction.getTransaction()).thenReturn(transaction);
return pendingTransaction;
}
}

@ -21,13 +21,11 @@ import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.ExecutionContextTestFixture;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.testutil.TestClock;
import org.hyperledger.besu.util.number.Fraction;
import java.time.ZoneId;
import java.util.Optional;
@ -42,10 +40,7 @@ public class LegacyTransactionPoolBaseFeeTest extends AbstractLegacyTransactionP
transactionReplacementTester) {
return new BaseFeePendingTransactionsSorter(
ImmutableTransactionPoolConfiguration.builder()
.txPoolMaxSize(MAX_TRANSACTIONS)
.txPoolLimitByAccountPercentage(Fraction.fromFloat(1.0f))
.build(),
poolConfig,
TestClock.system(ZoneId.systemDefault()),
metricsSystem,
protocolContext.getBlockchain()::getChainHeadHeader);

@ -21,13 +21,11 @@ import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.ExecutionContextTestFixture;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.testutil.TestClock;
import org.hyperledger.besu.util.number.Fraction;
import java.time.ZoneId;
import java.util.function.BiFunction;
@ -41,10 +39,7 @@ public class LegacyTransactionPoolGasPriceTest extends AbstractLegacyTransaction
transactionReplacementTester) {
return new GasPricePendingTransactionsSorter(
ImmutableTransactionPoolConfiguration.builder()
.txPoolMaxSize(MAX_TRANSACTIONS)
.txPoolLimitByAccountPercentage(Fraction.fromFloat(1.0f))
.build(),
poolConfig,
TestClock.system(ZoneId.systemDefault()),
metricsSystem,
protocolContext.getBlockchain()::getChainHeadHeader);

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

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

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

@ -28,6 +28,7 @@ public class TransactionSelectionResult {
SELECTED,
BLOCK_FULL(true, false),
BLOCK_OCCUPANCY_ABOVE_THRESHOLD(true, false),
BLOCK_SELECTION_TIMEOUT(true, false),
INVALID_TRANSIENT(false, false),
INVALID(false, true);
@ -56,6 +57,11 @@ public class TransactionSelectionResult {
/** The transaction has not been selected since the block is full. */
public static final TransactionSelectionResult 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
* to stop the selection.

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

@ -12,9 +12,9 @@
*
* 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.util.ArrayList;
@ -32,16 +32,23 @@ public class DeterministicEthScheduler extends EthScheduler {
private final List<MockExecutorService> executors;
private final List<PendingTimeout<?>> pendingTimeouts = new ArrayList<>();
/** Create a new deterministic scheduler that never timeouts */
public DeterministicEthScheduler() {
this(TimeoutPolicy.NEVER_TIMEOUT);
}
/**
* Create a new deterministic scheduler with the provided timeout policy
*
* @param timeoutPolicy the timeout policy
*/
public DeterministicEthScheduler(final TimeoutPolicy timeoutPolicy) {
super(
new MockExecutorService(),
new MockScheduledExecutor(),
new MockExecutorService(),
new MockExecutorService(),
new MockExecutorService(),
new MockExecutorService());
this.timeoutPolicy = timeoutPolicy;
@ -51,40 +58,72 @@ public class DeterministicEthScheduler extends EthScheduler {
(MockExecutorService) this.scheduler,
(MockExecutorService) this.txWorkerExecutor,
(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() {
executors.forEach(MockExecutorService::runPendingFutures);
}
/**
* Get the count of pending tasks
*
* @return the count of pending tasks
*/
public long getPendingFuturesCount() {
return executors.stream().mapToLong(MockExecutorService::getPendingFuturesCount).sum();
}
/** Expire all pending timeouts */
public void expirePendingTimeouts() {
final List<PendingTimeout<?>> toExpire = new ArrayList<>(pendingTimeouts);
pendingTimeouts.clear();
toExpire.forEach(PendingTimeout::expire);
}
/** Do not automatically run submitted tasks. Tasks can be later run using runPendingFutures */
public void disableAutoRun() {
executors.forEach(e -> e.setAutoRun(false));
}
MockExecutorService mockSyncWorkerExecutor() {
/**
* Get the sync worker mock executor
*
* @return the mock executor
*/
public MockExecutorService mockSyncWorkerExecutor() {
return (MockExecutorService) syncWorkerExecutor;
}
MockScheduledExecutor mockScheduledExecutor() {
/**
* Get the scheduled mock executor
*
* @return the mock executor
*/
public MockScheduledExecutor mockScheduledExecutor() {
return (MockScheduledExecutor) scheduler;
}
/**
* Get the service mock executor
*
* @return the mock executor
*/
public MockExecutorService mockServiceExecutor() {
return (MockExecutorService) servicesExecutor;
}
/**
* Get the block creation mock executor
*
* @return the mock executor
*/
public MockExecutorService mockBlockCreationExecutor() {
return (MockExecutorService) blockCreationExecutor;
}
@Override
public <T> void failAfterTimeout(final CompletableFuture<T> promise, final Duration 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
public interface TimeoutPolicy {
/** A policy that never timeouts */
TimeoutPolicy NEVER_TIMEOUT = () -> false;
/** A policy that timeouts on every task */
TimeoutPolicy ALWAYS_TIMEOUT = () -> true;
/**
* If it should simulate a timeout when called
*
* @return true if the scheduler should timeouts
*/
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) {
final AtomicInteger timeouts = new AtomicInteger(times);
return () -> {

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

@ -21,6 +21,8 @@ import java.util.Objects;
/** The Percentage utility. */
public class Percentage {
/** Represent 0% */
public static final Percentage ZERO = new Percentage(0);
private final int value;

Loading…
Cancel
Save