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. 4
      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) - 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) - Upgrade EVM Reference Tests to v13 (Cancun) [#6114](https://github.com/hyperledger/besu/pull/6114)
- Add `yParity` to GraphQL and JSON-RPC for relevant querise. [6119](https://github.com/hyperledger/besu/pull/6119) - Add `yParity` to GraphQL and JSON-RPC for relevant querise. [6119](https://github.com/hyperledger/besu/pull/6119)
- Force tx replacement price bump to zero when zero base fee market is configured or `--min-gas-price` is set to 0. This allows for easier tx replacement in networks where there is not gas price. [#6079](https://github.com/hyperledger/besu/pull/6079)
- Introduce the possibility to limit the time spent selecting pending transactions during block creation, using the new experimental option `Xblock-txs-selection-max-time` on PoS and PoW networks (by default set to 5000ms) or `Xpoa-block-txs-selection-max-time` on PoA networks (by default 75% of the min block time) [#6044](https://github.com/hyperledger/besu/pull/6044)
### Bug fixes ### Bug fixes
- Upgrade netty to address CVE-2023-44487, CVE-2023-34462 [#6100](https://github.com/hyperledger/besu/pull/6100) - Upgrade netty to address CVE-2023-44487, CVE-2023-34462 [#6100](https://github.com/hyperledger/besu/pull/6100)
- Upgrade grpc to address CVE-2023-32731, CVE-2023-33953, CVE-2023-44487, CVE-2023-4785 [#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) - 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)
--- ---

@ -54,12 +54,12 @@ import org.hyperledger.besu.cli.custom.RpcAuthFileValidator;
import org.hyperledger.besu.cli.error.BesuExecutionExceptionHandler; import org.hyperledger.besu.cli.error.BesuExecutionExceptionHandler;
import org.hyperledger.besu.cli.error.BesuParameterExceptionHandler; import org.hyperledger.besu.cli.error.BesuParameterExceptionHandler;
import org.hyperledger.besu.cli.options.MiningOptions; 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.DataStorageOptions;
import org.hyperledger.besu.cli.options.stable.EthstatsOptions; import org.hyperledger.besu.cli.options.stable.EthstatsOptions;
import org.hyperledger.besu.cli.options.stable.LoggingLevelOption; import org.hyperledger.besu.cli.options.stable.LoggingLevelOption;
import org.hyperledger.besu.cli.options.stable.NodePrivateKeyFileOption; import org.hyperledger.besu.cli.options.stable.NodePrivateKeyFileOption;
import org.hyperledger.besu.cli.options.stable.P2PTLSConfigOptions; 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.ChainPruningOptions;
import org.hyperledger.besu.cli.options.unstable.DnsOptions; import org.hyperledger.besu.cli.options.unstable.DnsOptions;
import org.hyperledger.besu.cli.options.unstable.EthProtocolOptions; 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.TlsClientAuthConfiguration;
import org.hyperledger.besu.ethereum.api.tls.TlsConfiguration; import org.hyperledger.besu.ethereum.api.tls.TlsConfiguration;
import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters;
import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.eth.sync.SyncMode; import org.hyperledger.besu.ethereum.eth.sync.SyncMode;
@ -283,9 +284,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
final SynchronizerOptions unstableSynchronizerOptions = SynchronizerOptions.create(); final SynchronizerOptions unstableSynchronizerOptions = SynchronizerOptions.create();
final EthProtocolOptions unstableEthProtocolOptions = EthProtocolOptions.create(); final EthProtocolOptions unstableEthProtocolOptions = EthProtocolOptions.create();
final MetricsCLIOptions unstableMetricsCLIOptions = MetricsCLIOptions.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 DnsOptions unstableDnsOptions = DnsOptions.create();
private final NatOptions unstableNatOptions = NatOptions.create(); private final NatOptions unstableNatOptions = NatOptions.create();
private final NativeLibraryOptions unstableNativeLibraryOptions = NativeLibraryOptions.create(); private final NativeLibraryOptions unstableNativeLibraryOptions = NativeLibraryOptions.create();
@ -303,8 +301,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
private final LoggingLevelOption loggingLevelOption = LoggingLevelOption.create(); private final LoggingLevelOption loggingLevelOption = LoggingLevelOption.create();
@CommandLine.ArgGroup(validate = false, heading = "@|bold Tx Pool Common Options|@%n") @CommandLine.ArgGroup(validate = false, heading = "@|bold Tx Pool Common Options|@%n")
final org.hyperledger.besu.cli.options.stable.TransactionPoolOptions final TransactionPoolOptions transactionPoolOptions = TransactionPoolOptions.create();
stableTransactionPoolOptions = TransactionPoolOptions.create();
@CommandLine.ArgGroup(validate = false, heading = "@|bold Block Builder Options|@%n") @CommandLine.ArgGroup(validate = false, heading = "@|bold Block Builder Options|@%n")
final MiningOptions miningOptions = MiningOptions.create(); final MiningOptions miningOptions = MiningOptions.create();
@ -1525,7 +1522,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.put("NAT Configuration", unstableNatOptions) .put("NAT Configuration", unstableNatOptions)
.put("Privacy Plugin Configuration", unstablePrivacyPluginOptions) .put("Privacy Plugin Configuration", unstablePrivacyPluginOptions)
.put("Synchronizer", unstableSynchronizerOptions) .put("Synchronizer", unstableSynchronizerOptions)
.put("TransactionPool", unstableTransactionPoolOptions)
.put("Native Library", unstableNativeLibraryOptions) .put("Native Library", unstableNativeLibraryOptions)
.put("EVM Options", unstableEvmOptions) .put("EVM Options", unstableEvmOptions)
.put("IPC Options", unstableIpcOptions) .put("IPC Options", unstableIpcOptions)
@ -1794,7 +1790,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
} }
private void validateTransactionPoolOptions() { private void validateTransactionPoolOptions() {
stableTransactionPoolOptions.validate(commandLine); transactionPoolOptions.validate(commandLine, getActualGenesisConfigOptions());
} }
private void validateRequiredOptions() { private void validateRequiredOptions() {
@ -1811,8 +1807,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
} }
private void validateMiningParams() { private void validateMiningParams() {
miningOptions.validate( miningOptions.validate(commandLine, getActualGenesisConfigOptions(), isMergeEnabled(), logger);
commandLine, logger, isMergeEnabled(), getActualGenesisConfigOptions().isEthHash());
} }
/** /**
@ -2811,21 +2806,60 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
} }
private TransactionPoolConfiguration buildTransactionPoolConfiguration() { private TransactionPoolConfiguration buildTransactionPoolConfiguration() {
final var stableTxPoolOption = stableTransactionPoolOptions.toDomainObject(); final var txPoolConf = transactionPoolOptions.toDomainObject();
return ImmutableTransactionPoolConfiguration.builder() final var txPoolConfBuilder =
.from(stableTxPoolOption) ImmutableTransactionPoolConfiguration.builder()
.unstable(unstableTransactionPoolOptions.toDomainObject()) .from(txPoolConf)
.saveFile((dataPath.resolve(stableTxPoolOption.getSaveFile().getPath()).toFile())) .saveFile((dataPath.resolve(txPoolConf.getSaveFile().getPath()).toFile()));
.build();
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() { private MiningParameters getMiningParameters() {
if (miningParameters == null) { if (miningParameters == null) {
miningParameters = miningOptions.toDomainObject(); final var miningParametersBuilder =
ImmutableMiningParameters.builder().from(miningOptions.toDomainObject());
final var actualGenesisOptions = getActualGenesisConfigOptions();
if (actualGenesisOptions.isPoa()) {
miningParametersBuilder.unstable(
ImmutableMiningParameters.Unstable.builder()
.minBlockTime(getMinBlockTime(actualGenesisOptions))
.build());
}
miningParameters = miningParametersBuilder.build();
} }
return miningParameters; return miningParameters;
} }
private int getMinBlockTime(final GenesisConfigOptions genesisConfigOptions) {
if (genesisConfigOptions.isClique()) {
return genesisConfigOptions.getCliqueConfigOptions().getBlockPeriodSeconds();
}
if (genesisConfigOptions.isIbft2()) {
return genesisConfigOptions.getBftConfigOptions().getBlockPeriodSeconds();
}
if (genesisConfigOptions.isQbft()) {
return genesisConfigOptions.getQbftConfigOptions().getBlockPeriodSeconds();
}
throw new IllegalArgumentException("Should only be called for a PoA network");
}
private boolean isPruningEnabled() { private boolean isPruningEnabled() {
return pruningEnabled; return pruningEnabled;
} }

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

@ -12,7 +12,7 @@
* *
* SPDX-License-Identifier: Apache-2.0 * 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_DOUBLE_FORMAT_HELP;
import static org.hyperledger.besu.cli.DefaultCommandValues.MANDATORY_INTEGER_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.LAYERED;
import static org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration.Implementation.LEGACY; 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.FractionConverter;
import org.hyperledger.besu.cli.converter.PercentageConverter; 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.cli.util.CommandLineUtils;
import org.hyperledger.besu.config.GenesisConfigOptions;
import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; 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 org.hyperledger.besu.util.number.Percentage;
import java.io.File; import java.io.File;
import java.time.Duration;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -195,6 +197,39 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
Integer txPoolMaxSize = TransactionPoolConfiguration.DEFAULT_MAX_PENDING_TRANSACTIONS; 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() {} private TransactionPoolOptions() {}
/** /**
@ -230,6 +265,10 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
config.getTxPoolLimitByAccountPercentage(); config.getTxPoolLimitByAccountPercentage();
options.legacyOptions.txPoolMaxSize = config.getTxPoolMaxSize(); options.legacyOptions.txPoolMaxSize = config.getTxPoolMaxSize();
options.legacyOptions.pendingTxRetentionPeriod = config.getPendingTxRetentionPeriod(); options.legacyOptions.pendingTxRetentionPeriod = config.getPendingTxRetentionPeriod();
options.unstableOptions.txMessageKeepAliveSeconds =
config.getUnstable().getTxMessageKeepAliveSeconds();
options.unstableOptions.eth65TrxAnnouncedBufferingPeriod =
config.getUnstable().getEth65TrxAnnouncedBufferingPeriod();
return options; return options;
} }
@ -239,8 +278,10 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
* options are valid for the selected implementation. * options are valid for the selected implementation.
* *
* @param commandLine the full commandLine to check all the options specified by the user * @param commandLine the full commandLine to check all the options specified by the user
* @param genesisConfigOptions the genesis config options
*/ */
public void validate(final CommandLine commandLine) { public void validate(
final CommandLine commandLine, final GenesisConfigOptions genesisConfigOptions) {
CommandLineUtils.failIfOptionDoesntMeetRequirement( CommandLineUtils.failIfOptionDoesntMeetRequirement(
commandLine, commandLine,
"Could not use legacy transaction pool options with layered implementation", "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", "Could not use layered transaction pool options with legacy implementation",
!txPoolImplementation.equals(LEGACY), !txPoolImplementation.equals(LEGACY),
CommandLineUtils.getCLIOptionNames(Layered.class)); 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 @Override
@ -271,6 +318,11 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
.txPoolLimitByAccountPercentage(legacyOptions.txPoolLimitByAccountPercentage) .txPoolLimitByAccountPercentage(legacyOptions.txPoolLimitByAccountPercentage)
.txPoolMaxSize(legacyOptions.txPoolMaxSize) .txPoolMaxSize(legacyOptions.txPoolMaxSize)
.pendingTxRetentionPeriod(legacyOptions.pendingTxRetentionPeriod) .pendingTxRetentionPeriod(legacyOptions.pendingTxRetentionPeriod)
.unstable(
ImmutableTransactionPoolConfiguration.Unstable.builder()
.txMessageKeepAliveSeconds(unstableOptions.txMessageKeepAliveSeconds)
.eth65TrxAnnouncedBufferingPeriod(unstableOptions.eth65TrxAnnouncedBufferingPeriod)
.build())
.build(); .build();
} }
@ -278,4 +330,14 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
public List<String> getCLIOptions() { public List<String> getCLIOptions() {
return CommandLineUtils.getCLIOptions(this, new TransactionPoolOptions()); 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; 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, localAddress,
secondsBetweenBlocks), secondsBetweenBlocks),
epochManager, epochManager,
createEmptyBlocks); createEmptyBlocks,
ethProtocolManager.ethContext().getScheduler());
final CliqueMiningCoordinator miningCoordinator = final CliqueMiningCoordinator miningCoordinator =
new CliqueMiningCoordinator( new CliqueMiningCoordinator(
protocolContext.getBlockchain(), protocolContext.getBlockchain(),

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

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

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

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

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

@ -183,6 +183,9 @@ public class BesuCommandTest extends CommandTestAbstract {
private static final JsonObject GENESIS_WITH_DATA_BLOBS_ENABLED = private static final JsonObject GENESIS_WITH_DATA_BLOBS_ENABLED =
new JsonObject().put("config", new JsonObject().put("cancunTime", 1L)); 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 { static {
DEFAULT_JSON_RPC_CONFIGURATION = JsonRpcConfiguration.createDefault(); DEFAULT_JSON_RPC_CONFIGURATION = JsonRpcConfiguration.createDefault();
DEFAULT_GRAPH_QL_CONFIGURATION = GraphQLConfiguration.createDefault(); DEFAULT_GRAPH_QL_CONFIGURATION = GraphQLConfiguration.createDefault();
@ -5195,6 +5198,56 @@ public class BesuCommandTest extends CommandTestAbstract {
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); 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 @Test
public void snapsyncHealingOptionShouldBeDisabledByDefault() { public void snapsyncHealingOptionShouldBeDisabledByDefault() {
final TestBesuCommand besuCommand = parseCommand(); final TestBesuCommand besuCommand = parseCommand();

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

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

@ -12,20 +12,21 @@
* *
* SPDX-License-Identifier: Apache-2.0 * 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.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.LAYERED;
import static org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration.Implementation.LEGACY; import static org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration.Implementation.LEGACY;
import org.hyperledger.besu.cli.options.AbstractCLIOptionsTest; import org.hyperledger.besu.cli.converter.DurationMillisConverter;
import org.hyperledger.besu.cli.options.OptionParser;
import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.util.number.Percentage; import org.hyperledger.besu.util.number.Percentage;
import java.time.Duration;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
@ -270,6 +271,52 @@ public class TransactionPoolOptionsTest
+ prioritySender3.toHexString()); + 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 @Override
protected TransactionPoolConfiguration createDefaultDomainObject() { protected TransactionPoolConfiguration createDefaultDomainObject() {
return TransactionPoolConfiguration.DEFAULT; return TransactionPoolConfiguration.DEFAULT;
@ -294,6 +341,6 @@ public class TransactionPoolOptionsTest
@Override @Override
protected TransactionPoolOptions getOptionsFromBesuCommand(final TestBesuCommand besuCommand) { 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.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.storage.StorageProvider; import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import java.util.Optional; import java.util.Optional;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
@ -71,7 +73,10 @@ public class TransitionControllerBuilderTest {
@Mock MutableBlockchain mockBlockchain; @Mock MutableBlockchain mockBlockchain;
@Mock TransactionPool transactionPool; @Mock TransactionPool transactionPool;
@Mock SyncState syncState; @Mock SyncState syncState;
@Mock EthProtocolManager ethProtocolManager;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
EthProtocolManager ethProtocolManager;
@Mock PostMergeContext mergeContext; @Mock PostMergeContext mergeContext;
StorageProvider storageProvider = new InMemoryKeyValueStorageProvider(); StorageProvider storageProvider = new InMemoryKeyValueStorageProvider();
@ -101,6 +106,8 @@ public class TransitionControllerBuilderTest {
.thenReturn(mock(CliqueContext.class)); .thenReturn(mock(CliqueContext.class));
when(protocolContext.getConsensusContext(PostMergeContext.class)).thenReturn(mergeContext); when(protocolContext.getConsensusContext(PostMergeContext.class)).thenReturn(mergeContext);
when(protocolContext.getConsensusContext(MergeContext.class)).thenReturn(mergeContext); when(protocolContext.getConsensusContext(MergeContext.class)).thenReturn(mergeContext);
when(ethProtocolManager.ethContext().getScheduler())
.thenReturn(new DeterministicEthScheduler());
miningParameters = MiningParameters.newDefault(); miningParameters = MiningParameters.newDefault();
} }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -49,11 +49,6 @@ public class TransactionReplacementByFeeMarketRule implements TransactionPoolRep
Wei newEffPriority = Wei newEffPriority =
newPendingTransaction.getTransaction().getEffectivePriorityFeePerGas(maybeBaseFee); 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 curEffPrice = priceOf(existingPendingTransaction.getTransaction(), maybeBaseFee);
Wei curEffPriority = Wei curEffPriority =
existingPendingTransaction.getTransaction().getEffectivePriorityFeePerGas(maybeBaseFee); 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) { 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 = final Wei replacementThreshold =
existingPendingTransaction.getGasPrice().multiply(100 + priceBump.getValue()).divide(100); 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 org.hyperledger.besu.plugin.services.MetricsSystem;
import java.time.Clock; import java.time.Clock;
import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
import java.util.NavigableSet; import java.util.NavigableSet;
import java.util.TreeSet; import java.util.TreeSet;
@ -39,8 +40,7 @@ public class GasPricePendingTransactionsSorter extends AbstractPendingTransactio
new TreeSet<>( new TreeSet<>(
comparing(PendingTransaction::hasPriority) comparing(PendingTransaction::hasPriority)
.thenComparing(PendingTransaction::getGasPrice) .thenComparing(PendingTransaction::getGasPrice)
.thenComparing(PendingTransaction::getAddedAt) .thenComparing(PendingTransaction::getSequence, Comparator.reverseOrder())
.thenComparing(PendingTransaction::getSequence)
.reversed()); .reversed());
public GasPricePendingTransactionsSorter( public GasPricePendingTransactionsSorter(

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

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

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

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

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

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

@ -17,10 +17,11 @@
package org.hyperledger.besu.ethereum.eth.sync.backwardsync; package org.hyperledger.besu.ethereum.eth.sync.backwardsync;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider.createInMemoryBlockchain; import static org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider.createInMemoryBlockchain;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; 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.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Answers; import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.Spy; import org.mockito.Spy;
@ -75,11 +77,12 @@ import org.mockito.quality.Strictness;
@MockitoSettings(strictness = Strictness.LENIENT) @MockitoSettings(strictness = Strictness.LENIENT)
public class BackwardSyncContextTest { public class BackwardSyncContextTest {
public static final int REMOTE_HEIGHT = 50;
public static final int LOCAL_HEIGHT = 25; 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 UNCLE_HEIGHT = 25 - 3;
public static final int NUM_OF_RETRIES = 100; public static final int NUM_OF_RETRIES = 100;
public static final int TEST_MAX_BAD_CHAIN_EVENT_ENTRIES = 25;
private BackwardSyncContext context; private BackwardSyncContext context;
@ -173,7 +176,8 @@ public class BackwardSyncContextTest {
ethContext, ethContext,
syncState, syncState,
backwardChain, backwardChain,
NUM_OF_RETRIES)); NUM_OF_RETRIES,
TEST_MAX_BAD_CHAIN_EVENT_ENTRIES));
doReturn(true).when(context).isReady(); doReturn(true).when(context).isReady();
doReturn(2).when(context).getBatchSize(); doReturn(2).when(context).getBatchSize();
} }
@ -347,6 +351,72 @@ public class BackwardSyncContextTest {
block, Collections.emptyList(), List.of(childBlockHeader, grandChildBlockHeader)); 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 @Test
public void shouldFailAfterMaxNumberOfRetries() { public void shouldFailAfterMaxNumberOfRetries() {
doReturn(CompletableFuture.failedFuture(new Exception())) 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.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.TransactionReceipt; import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.eth.manager.DeterministicEthScheduler;
import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
@ -39,6 +38,7 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;

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

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

@ -19,7 +19,6 @@ import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toList;
import static org.assertj.core.api.Assertions.assertThat; 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.mainnet.ValidationResult.valid;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.EXCEEDS_BLOCK_GAS_LIMIT; import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.EXCEEDS_BLOCK_GAS_LIMIT;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.GAS_PRICE_TOO_LOW; 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.MetricsSystem;
import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionValidator; import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionValidator;
import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionValidatorFactory; import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionValidatorFactory;
import org.hyperledger.besu.util.number.Percentage;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.Arrays; import java.util.Arrays;
@ -106,6 +106,7 @@ import java.util.function.Supplier;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIf; 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.ExtendWith;
import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
@ -1033,6 +1034,157 @@ public abstract class AbstractTransactionPoolTest {
addAndAssertTransactionViaApiValid(transaction, noLocalPriority); 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 @Test
public void shouldAcceptBaseFeeFloorGasPriceFrontierLocalPriorityTransactionsWhenMining() { public void shouldAcceptBaseFeeFloorGasPriceFrontierLocalPriorityTransactionsWhenMining() {
transactionPool = createTransactionPool(b -> b.noLocalPriority(false)); transactionPool = createTransactionPool(b -> b.noLocalPriority(false));
@ -1112,20 +1264,21 @@ public abstract class AbstractTransactionPoolTest {
@Test @Test
public void addRemoteTransactionsShouldAllowDuplicates() { public void addRemoteTransactionsShouldAllowDuplicates() {
final Transaction transaction1 = createTransaction(1, Wei.of(7L)); final Transaction transaction1 = createTransaction(1, Wei.of(7L));
final Transaction transaction2 = createTransaction(2, Wei.of(7L)); final Transaction transaction2a = createTransaction(2, Wei.of(7L));
final Transaction transaction3 = createTransaction(2, Wei.of(7L)); final Transaction transaction2b = createTransaction(2, Wei.of(7L));
final Transaction transaction4 = createTransaction(3, Wei.of(7L)); final Transaction transaction3 = createTransaction(3, Wei.of(7L));
givenTransactionIsValid(transaction1); givenTransactionIsValid(transaction1);
givenTransactionIsValid(transaction2); givenTransactionIsValid(transaction2a);
givenTransactionIsValid(transaction2b);
givenTransactionIsValid(transaction3); givenTransactionIsValid(transaction3);
givenTransactionIsValid(transaction4);
assertThatCode( transactionPool.addRemoteTransactions(
() -> List.of(transaction1, transaction2a, transaction2b, transaction3));
transactionPool.addRemoteTransactions(
List.of(transaction1, transaction2, transaction3, transaction4))) assertThat(transactionPool.getPendingTransactions())
.doesNotThrowAnyException(); .map(PendingTransaction::getTransaction)
.containsExactlyInAnyOrder(transaction1, transaction2a, transaction3);
} }
private static PluginTransactionValidatorFactory getPluginTransactionValidatorFactoryReturning( 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.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.util.number.Percentage; import org.hyperledger.besu.util.number.Percentage;
import java.math.BigInteger;
import java.util.Collection; import java.util.Collection;
import java.util.Optional; import java.util.Optional;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.MethodSource;
public class TransactionReplacementRulesTest { public class TransactionReplacementRulesTest extends AbstractTransactionReplacementTest {
public static Collection<Object[]> data() { public static Collection<Object[]> data() {
return asList( return asList(
@ -41,39 +38,45 @@ public class TransactionReplacementRulesTest {
// TransactionReplacementByGasPriceRule // TransactionReplacementByGasPriceRule
// basefee absent // basefee absent
{frontierTx(5L), frontierTx(6L), empty(), 0, true}, {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(5L), frontierTx(4L), empty(), 0, false},
{frontierTx(100L), frontierTx(105L), empty(), 10, 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}, {frontierTx(100L), frontierTx(111L), empty(), 10, true},
// basefee present // basefee present
{frontierTx(5L), frontierTx(6L), Optional.of(Wei.of(3L)), 0, true}, {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(5L), frontierTx(4L), Optional.of(Wei.of(3L)), 0, false},
{frontierTx(100L), frontierTx(105L), Optional.of(Wei.of(3L)), 10, 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}, {frontierTx(100L), frontierTx(111L), Optional.of(Wei.of(3L)), 10, true},
// TransactionReplacementByFeeMarketRule // TransactionReplacementByFeeMarketRule
// eip1559 replacing frontier // eip1559 replacing frontier
{frontierTx(5L), eip1559Tx(3L, 6L), Optional.of(Wei.of(1L)), 0, false}, {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}, {frontierTx(5L), eip1559Tx(3L, 6L), Optional.of(Wei.of(3L)), 0, true},
// frontier replacing 1559 // 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}, {eip1559Tx(3L, 8L), frontierTx(8L), Optional.of(Wei.of(4L)), 0, true},
// eip1559 replacing eip1559 // eip1559 replacing eip1559
{eip1559Tx(3L, 6L), eip1559Tx(3L, 6L), 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, false}, {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(4L)), 0, true},
{eip1559Tx(10L, 200L), eip1559Tx(10L, 200L), Optional.of(Wei.of(90L)), 10, 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(15L, 200L), Optional.of(Wei.of(90L)), 10, false},
{eip1559Tx(10L, 200L), eip1559Tx(21L, 200L), Optional.of(Wei.of(90L)), 10, true}, {eip1559Tx(10L, 200L), eip1559Tx(21L, 200L), Optional.of(Wei.of(90L)), 10, true},
// pathological, priority fee > max fee // 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}, {eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.of(Wei.of(4L)), 0, true},
// pathological, eip1559 without basefee // pathological, eip1559 without basefee
{eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.empty(), 0, false}, {eip1559Tx(8L, 6L), eip1559Tx(3L, 7L), Optional.empty(), 0, false},
{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)) .shouldReplace(oldTx, newTx, mockHeader))
.isEqualTo(expected); .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.ExecutionContextTestFixture;
import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture; import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction; import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions; import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.testutil.TestClock; import org.hyperledger.besu.testutil.TestClock;
import org.hyperledger.besu.util.number.Fraction;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.Optional; import java.util.Optional;
@ -42,10 +40,7 @@ public class LegacyTransactionPoolBaseFeeTest extends AbstractLegacyTransactionP
transactionReplacementTester) { transactionReplacementTester) {
return new BaseFeePendingTransactionsSorter( return new BaseFeePendingTransactionsSorter(
ImmutableTransactionPoolConfiguration.builder() poolConfig,
.txPoolMaxSize(MAX_TRANSACTIONS)
.txPoolLimitByAccountPercentage(Fraction.fromFloat(1.0f))
.build(),
TestClock.system(ZoneId.systemDefault()), TestClock.system(ZoneId.systemDefault()),
metricsSystem, metricsSystem,
protocolContext.getBlockchain()::getChainHeadHeader); 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.ExecutionContextTestFixture;
import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture; import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction; import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions; import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.testutil.TestClock; import org.hyperledger.besu.testutil.TestClock;
import org.hyperledger.besu.util.number.Fraction;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@ -41,10 +39,7 @@ public class LegacyTransactionPoolGasPriceTest extends AbstractLegacyTransaction
transactionReplacementTester) { transactionReplacementTester) {
return new GasPricePendingTransactionsSorter( return new GasPricePendingTransactionsSorter(
ImmutableTransactionPoolConfiguration.builder() poolConfig,
.txPoolMaxSize(MAX_TRANSACTIONS)
.txPoolLimitByAccountPercentage(Fraction.fromFloat(1.0f))
.build(),
TestClock.system(ZoneId.systemDefault()), TestClock.system(ZoneId.systemDefault()),
metricsSystem, metricsSystem,
protocolContext.getBlockchain()::getChainHeadHeader); protocolContext.getBlockchain()::getChainHeadHeader);

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

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

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

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

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

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

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

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

Loading…
Cancel
Save