Layered txpool by default and txpool options hoverhaul (#5772)

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
pull/5871/head
Fabio Di Fabio 1 year ago committed by GitHub
parent 3597ccbf01
commit 25c2065434
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      CHANGELOG.md
  2. 127
      besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
  3. 15
      besu/src/main/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilder.java
  4. 4
      besu/src/main/java/org/hyperledger/besu/cli/DefaultCommandValues.java
  5. 44
      besu/src/main/java/org/hyperledger/besu/cli/converter/DurationMillisConverter.java
  6. 6
      besu/src/main/java/org/hyperledger/besu/cli/converter/FractionConverter.java
  7. 6
      besu/src/main/java/org/hyperledger/besu/cli/converter/PercentageConverter.java
  8. 31
      besu/src/main/java/org/hyperledger/besu/cli/converter/TypeFormatter.java
  9. 39
      besu/src/main/java/org/hyperledger/besu/cli/converter/exception/DurationConversionException.java
  10. 45
      besu/src/main/java/org/hyperledger/besu/cli/options/OptionParser.java
  11. 263
      besu/src/main/java/org/hyperledger/besu/cli/options/stable/TransactionPoolOptions.java
  12. 122
      besu/src/main/java/org/hyperledger/besu/cli/options/unstable/TransactionPoolOptions.java
  13. 104
      besu/src/main/java/org/hyperledger/besu/cli/util/CommandLineUtils.java
  14. 3
      besu/src/main/java/org/hyperledger/besu/cli/util/TomlConfigFileDefaultProvider.java
  15. 187
      besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java
  16. 7
      besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java
  17. 22
      besu/src/test/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilderTest.java
  18. 5
      besu/src/test/java/org/hyperledger/besu/cli/converter/FractionConverterTest.java
  19. 5
      besu/src/test/java/org/hyperledger/besu/cli/converter/PercentageConverterTest.java
  20. 27
      besu/src/test/java/org/hyperledger/besu/cli/options/AbstractCLIOptionsTest.java
  21. 9
      besu/src/test/java/org/hyperledger/besu/cli/options/EthProtocolOptionsTest.java
  22. 9
      besu/src/test/java/org/hyperledger/besu/cli/options/MetricsCLIOptionsTest.java
  23. 8
      besu/src/test/java/org/hyperledger/besu/cli/options/NetworkingOptionsTest.java
  24. 21
      besu/src/test/java/org/hyperledger/besu/cli/options/OptionParserTest.java
  25. 8
      besu/src/test/java/org/hyperledger/besu/cli/options/SynchronizerOptionsTest.java
  26. 155
      besu/src/test/java/org/hyperledger/besu/cli/options/TransactionPoolOptionsTest.java
  27. 243
      besu/src/test/java/org/hyperledger/besu/cli/options/stable/TransactionPoolOptionsTest.java
  28. 105
      besu/src/test/java/org/hyperledger/besu/cli/options/unstable/TransactionPoolOptionsTest.java
  29. 11
      besu/src/test/java/org/hyperledger/besu/controller/BesuControllerBuilderTest.java
  30. 11
      besu/src/test/java/org/hyperledger/besu/controller/MergeBesuControllerBuilderTest.java
  31. 11
      besu/src/test/java/org/hyperledger/besu/controller/QbftBesuControllerBuilderTest.java
  32. 13
      besu/src/test/resources/everything_config.toml
  33. 3
      consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeCoordinatorTest.java
  34. 3
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LegacyFeeMarketBlockTransactionSelectorTest.java
  35. 3
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LondonFeeMarketBlockTransactionSelectorTest.java
  36. 8
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/NewPooledTransactionHashesMessageProcessor.java
  37. 57
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolConfiguration.java
  38. 22
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactory.java
  39. 1
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/AbstractTransactionsLayer.java
  40. 24
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/BaseFeePrioritizedTransactions.java
  41. 8
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/NewPooledTransactionHashesMessageProcessorTest.java
  42. 53
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactoryTest.java
  43. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolLegacyTest.java
  44. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolLondonTest.java
  45. 12
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/AbstractPendingTransactionsTestBase.java
  46. 3
      ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java

@ -5,11 +5,15 @@
### Breaking Changes
- Removed support for Kotti network (ETC) [#5816](https://github.com/hyperledger/besu/pull/5816)
- Layered transaction pool implementation is now stable and enabled by default, so the following changes to experimental options have been done [#5772](https://github.com/hyperledger/besu):
- `--Xlayered-tx-pool` is gone, to select the implementation use the new `--tx-pool` option with values `layered` (default) or `legacy`
- `--Xlayered-tx-pool-layer-max-capacity`, `--Xlayered-tx-pool-max-prioritized` and `--Xlayered-tx-pool-max-future-by-sender` just drop the `X` and keep the same behavior
### Additions and Improvements
- Layered transaction pool implementation is now stable and enabled by default. If you want still to use the legacy implementation, use `--tx-pool=legacy` [#5772](https://github.com/hyperledger/besu)
### Bug Fixes
- do not create ignorable storage on revert storage-variables subcommand [#5830](https://github.com/hyperledger/besu/pull/5830)
- do not create ignorable storage on revert storage-variables subcommand [#5830](https://github.com/hyperledger/besu/pull/5830)
### Download Links

@ -46,7 +46,6 @@ import org.hyperledger.besu.chainimport.JsonBlockImporter;
import org.hyperledger.besu.chainimport.RlpBlockImporter;
import org.hyperledger.besu.cli.config.EthNetworkConfig;
import org.hyperledger.besu.cli.config.NetworkName;
import org.hyperledger.besu.cli.converter.FractionConverter;
import org.hyperledger.besu.cli.converter.MetricCategoryConverter;
import org.hyperledger.besu.cli.converter.PercentageConverter;
import org.hyperledger.besu.cli.custom.CorsAllowedOriginsProperty;
@ -59,6 +58,7 @@ import org.hyperledger.besu.cli.options.stable.EthstatsOptions;
import org.hyperledger.besu.cli.options.stable.LoggingLevelOption;
import org.hyperledger.besu.cli.options.stable.NodePrivateKeyFileOption;
import org.hyperledger.besu.cli.options.stable.P2PTLSConfigOptions;
import org.hyperledger.besu.cli.options.stable.TransactionPoolOptions;
import org.hyperledger.besu.cli.options.unstable.ChainPruningOptions;
import org.hyperledger.besu.cli.options.unstable.DnsOptions;
import org.hyperledger.besu.cli.options.unstable.EthProtocolOptions;
@ -73,7 +73,6 @@ import org.hyperledger.besu.cli.options.unstable.PkiBlockCreationOptions;
import org.hyperledger.besu.cli.options.unstable.PrivacyPluginOptions;
import org.hyperledger.besu.cli.options.unstable.RPCOptions;
import org.hyperledger.besu.cli.options.unstable.SynchronizerOptions;
import org.hyperledger.besu.cli.options.unstable.TransactionPoolOptions;
import org.hyperledger.besu.cli.presynctasks.PreSynchronizationTaskRunner;
import org.hyperledger.besu.cli.presynctasks.PrivateDatabaseMigrationPreSyncTask;
import org.hyperledger.besu.cli.subcommands.PasswordSubCommand;
@ -127,6 +126,7 @@ import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.eth.sync.SyncMode;
import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.mainnet.FrontierTargetingGasLimitCalculator;
import org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration;
@ -280,7 +280,9 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
final SynchronizerOptions unstableSynchronizerOptions = SynchronizerOptions.create();
final EthProtocolOptions unstableEthProtocolOptions = EthProtocolOptions.create();
final MetricsCLIOptions unstableMetricsCLIOptions = MetricsCLIOptions.create();
final TransactionPoolOptions unstableTransactionPoolOptions = TransactionPoolOptions.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 MiningOptions unstableMiningOptions = MiningOptions.create();
private final NatOptions unstableNatOptions = NatOptions.create();
@ -298,6 +300,10 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
NodePrivateKeyFileOption.create();
private final LoggingLevelOption loggingLevelOption = LoggingLevelOption.create();
@CommandLine.ArgGroup(validate = false, heading = "@|bold Tx Pool Common Options|@%n")
final org.hyperledger.besu.cli.options.stable.TransactionPoolOptions
stableTransactionPoolOptions = TransactionPoolOptions.create();
private final RunnerBuilder runnerBuilder;
private final BesuController.Builder controllerBuilderFactory;
private final BesuPluginContextImpl besuPluginContext;
@ -454,10 +460,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
"The maximum percentage of P2P connections that can be initiated remotely. Must be between 0 and 100 inclusive. (default: ${DEFAULT-VALUE})",
arity = "1",
converter = PercentageConverter.class)
private final Integer maxRemoteConnectionsPercentage =
Fraction.fromFloat(DEFAULT_FRACTION_REMOTE_WIRE_CONNECTIONS_ALLOWED)
.toPercentage()
.getValue();
private final Percentage maxRemoteConnectionsPercentage =
Fraction.fromFloat(DEFAULT_FRACTION_REMOTE_WIRE_CONNECTIONS_ALLOWED).toPercentage();
@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings.
@CommandLine.Option(
@ -1111,13 +1115,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
arity = "1")
private final Wei minTransactionGasPrice = DEFAULT_MIN_TRANSACTION_GAS_PRICE;
@Option(
names = {"--rpc-tx-feecap"},
description =
"Maximum transaction fees (in Wei) accepted for transaction submitted through RPC (default: ${DEFAULT-VALUE})",
arity = "1")
private final Wei txFeeCap = DEFAULT_RPC_TX_FEE_CAP;
@Option(
names = {"--min-block-occupancy-ratio"},
description = "Minimum occupancy ratio for a mined block (default: ${DEFAULT-VALUE})",
@ -1209,75 +1206,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
"Sets target gas limit per block. If set, each block's gas limit will approach this setting over time if the current gas limit is different.")
private final Long targetGasLimit = null;
// Tx Pool Option Group
@CommandLine.ArgGroup(validate = false, heading = "@|bold Tx Pool Options|@%n")
TxPoolOptionGroup txPoolOptionGroup = new TxPoolOptionGroup();
static class TxPoolOptionGroup {
@CommandLine.Option(
names = {"--tx-pool-disable-locals"},
paramLabel = "<Boolean>",
description =
"Set to true if transactions sent via RPC should have the same checks and not be prioritized over remote ones (default: ${DEFAULT-VALUE})",
fallbackValue = "true",
arity = "0..1")
private Boolean disableLocalTxs = TransactionPoolConfiguration.DEFAULT_DISABLE_LOCAL_TXS;
@CommandLine.Option(
names = {"--tx-pool-enable-save-restore"},
paramLabel = "<Boolean>",
description =
"Set to true to enable saving the txpool content to file on shutdown and reloading it on startup (default: ${DEFAULT-VALUE})",
fallbackValue = "true",
arity = "0..1")
private Boolean saveRestoreEnabled = TransactionPoolConfiguration.DEFAULT_ENABLE_SAVE_RESTORE;
@CommandLine.Option(
names = {"--tx-pool-limit-by-account-percentage"},
paramLabel = "<DOUBLE>",
converter = FractionConverter.class,
description =
"Maximum portion of the transaction pool which a single account may occupy with future transactions (default: ${DEFAULT-VALUE})",
arity = "1")
private Float txPoolLimitByAccountPercentage =
TransactionPoolConfiguration.DEFAULT_LIMIT_TX_POOL_BY_ACCOUNT_PERCENTAGE;
@CommandLine.Option(
names = {"--tx-pool-save-file"},
paramLabel = "<STRING>",
description =
"If saving the txpool content is enabled, define a custom path for the save file (default: ${DEFAULT-VALUE} in the data-dir)",
arity = "1")
private File saveFile = TransactionPoolConfiguration.DEFAULT_SAVE_FILE;
@Option(
names = {"--tx-pool-max-size"},
paramLabel = MANDATORY_INTEGER_FORMAT_HELP,
description =
"Maximum number of pending transactions that will be kept in the transaction pool (default: ${DEFAULT-VALUE})",
arity = "1")
private final Integer txPoolMaxSize =
TransactionPoolConfiguration.DEFAULT_MAX_PENDING_TRANSACTIONS;
@Option(
names = {"--tx-pool-retention-hours"},
paramLabel = MANDATORY_INTEGER_FORMAT_HELP,
description =
"Maximum retention period of pending transactions in hours (default: ${DEFAULT-VALUE})",
arity = "1")
private final Integer pendingTxRetentionPeriod =
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS;
@Option(
names = {"--tx-pool-price-bump"},
paramLabel = MANDATORY_INTEGER_FORMAT_HELP,
converter = PercentageConverter.class,
description =
"Price bump percentage to replace an already existing transaction (default: ${DEFAULT-VALUE})",
arity = "1")
private final Integer priceBump = TransactionPoolConfiguration.DEFAULT_PRICE_BUMP.getValue();
}
@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings.
@Option(
names = {"--key-value-storage"},
@ -1906,10 +1834,15 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
validateRpcOptionsParams();
validateChainDataPruningParams();
validatePostMergeCheckpointBlockRequirements();
validateTransactionPoolOptions();
p2pTLSConfigOptions.checkP2PTLSOptionsDependencies(logger, commandLine);
pkiBlockCreationOptions.checkPkiBlockCreationOptionsDependencies(logger, commandLine);
}
private void validateTransactionPoolOptions() {
stableTransactionPoolOptions.validate(commandLine);
}
private void validateRequiredOptions() {
commandLine
.getCommandSpec()
@ -2987,28 +2920,14 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
}
private TransactionPoolConfiguration buildTransactionPoolConfiguration() {
return unstableTransactionPoolOptions
.toDomainObject()
.enableSaveRestore(txPoolOptionGroup.saveRestoreEnabled)
.disableLocalTransactions(txPoolOptionGroup.disableLocalTxs)
.txPoolLimitByAccountPercentage(txPoolOptionGroup.txPoolLimitByAccountPercentage)
.txPoolMaxSize(txPoolOptionGroup.txPoolMaxSize)
.pendingTxRetentionPeriod(txPoolOptionGroup.pendingTxRetentionPeriod)
.priceBump(Percentage.fromInt(txPoolOptionGroup.priceBump))
.txFeeCap(txFeeCap)
.saveFile(dataPath.resolve(txPoolOptionGroup.saveFile.getPath()).toFile())
final var stableTxPoolOption = stableTransactionPoolOptions.toDomainObject();
return ImmutableTransactionPoolConfiguration.builder()
.from(stableTxPoolOption)
.unstable(unstableTransactionPoolOptions.toDomainObject())
.saveFile((dataPath.resolve(stableTxPoolOption.getSaveFile().getPath()).toFile()))
.build();
}
/**
* Return the file where to save txpool content if the relative option is enabled.
*
* @return the save file
*/
public File getSaveFile() {
return txPoolOptionGroup.saveFile;
}
private boolean isPruningEnabled() {
return pruningEnabled;
}
@ -3613,9 +3532,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
builder.setHighSpecEnabled();
}
if (buildTransactionPoolConfiguration().getLayeredTxPoolEnabled()) {
builder.setLayeredTxPoolEnabled();
}
builder.setTxPoolImplementation(buildTransactionPoolConfiguration().getTxPoolImplementation());
return builder.build();
}

@ -15,6 +15,7 @@
package org.hyperledger.besu.cli;
import org.hyperledger.besu.BesuInfo;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.util.log.FramedLogMessage;
import org.hyperledger.besu.util.platform.PlatformDetector;
@ -47,7 +48,7 @@ public class ConfigurationOverviewBuilder {
private Collection<String> engineApis;
private String engineJwtFilePath;
private boolean isHighSpec = false;
private boolean isLayeredTxPool = false;
private TransactionPoolConfiguration.Implementation txPoolImplementation;
private Map<String, String> environment;
/**
@ -167,12 +168,14 @@ public class ConfigurationOverviewBuilder {
}
/**
* Sets experimental layered txpool enabled.
* Sets the txpool implementation in use.
*
* @param implementation the txpool implementation
* @return the builder
*/
public ConfigurationOverviewBuilder setLayeredTxPoolEnabled() {
isLayeredTxPool = true;
public ConfigurationOverviewBuilder setTxPoolImplementation(
final TransactionPoolConfiguration.Implementation implementation) {
txPoolImplementation = implementation;
return this;
}
@ -251,9 +254,7 @@ public class ConfigurationOverviewBuilder {
lines.add("Experimental high spec configuration enabled");
}
if (isLayeredTxPool) {
lines.add("Experimental layered transaction pool configuration enabled");
}
lines.add("Using " + txPoolImplementation + " transaction pool implementation");
lines.add("");
lines.add("Host:");

@ -16,7 +16,6 @@ package org.hyperledger.besu.cli;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.api.jsonrpc.authentication.JwtAlgorithm;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.p2p.config.RlpxConfiguration;
import org.hyperledger.besu.nat.NatMethod;
@ -61,9 +60,6 @@ public interface DefaultCommandValues {
String MANDATORY_NODE_ID_FORMAT_HELP = "<NODEID>";
/** The constant DEFAULT_MIN_TRANSACTION_GAS_PRICE. */
Wei DEFAULT_MIN_TRANSACTION_GAS_PRICE = Wei.of(1000);
/** The constant DEFAULT_RPC_TX_FEE_CAP. */
Wei DEFAULT_RPC_TX_FEE_CAP = TransactionPoolConfiguration.DEFAULT_RPC_TX_FEE_CAP;
/** The constant DEFAULT_MIN_BLOCK_OCCUPANCY_RATIO. */
Double DEFAULT_MIN_BLOCK_OCCUPANCY_RATIO = 0.8;
/** The constant DEFAULT_EXTRA_DATA. */

@ -0,0 +1,44 @@
/*
* 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.converter;
import org.hyperledger.besu.cli.converter.exception.DurationConversionException;
import java.time.Duration;
import picocli.CommandLine;
/** The Duration (milliseconds) Cli type converter. */
public class DurationMillisConverter
implements CommandLine.ITypeConverter<Duration>, TypeFormatter<Duration> {
@Override
public Duration convert(final String value) throws DurationConversionException {
try {
final long millis = Long.parseLong(value);
if (millis < 0) {
throw new DurationConversionException(millis);
}
return Duration.ofMillis(Long.parseLong(value));
} catch (NullPointerException | IllegalArgumentException e) {
throw new DurationConversionException(value);
}
}
@Override
public String format(final Duration value) {
return Long.toString(value.toMillis());
}
}

@ -20,12 +20,12 @@ import org.hyperledger.besu.util.number.Fraction;
import picocli.CommandLine;
/** The Fraction converter to convert floats in CLI. */
public class FractionConverter implements CommandLine.ITypeConverter<Float> {
public class FractionConverter implements CommandLine.ITypeConverter<Fraction> {
@Override
public Float convert(final String value) throws FractionConversionException {
public Fraction convert(final String value) throws FractionConversionException {
try {
return Fraction.fromString(value).getValue();
return Fraction.fromString(value);
} catch (final NullPointerException | IllegalArgumentException e) {
throw new FractionConversionException(value);
}

@ -20,12 +20,12 @@ import org.hyperledger.besu.util.number.Percentage;
import picocli.CommandLine;
/** The Percentage Cli type converter. */
public class PercentageConverter implements CommandLine.ITypeConverter<Integer> {
public class PercentageConverter implements CommandLine.ITypeConverter<Percentage> {
@Override
public Integer convert(final String value) throws PercentageConversionException {
public Percentage convert(final String value) throws PercentageConversionException {
try {
return Percentage.fromString(value).getValue();
return Percentage.fromString(value);
} catch (NullPointerException | IllegalArgumentException e) {
throw new PercentageConversionException(value);
}

@ -0,0 +1,31 @@
/*
* 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.converter;
/**
* This interface can be used to give a converter the capability to format the converted value back
* to its CLI form
*
* @param <V> the type of the CLI converted runtime value
*/
public interface TypeFormatter<V> {
/**
* Format a converted value back to its CLI form
*
* @param value the converted value
* @return the textual CLI form of the value
*/
String format(V value);
}

@ -0,0 +1,39 @@
/*
* 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.converter.exception;
import static java.lang.String.format;
/** The custom Duration conversion exception. */
public final class DurationConversionException extends Exception {
/**
* Instantiates a new Duration conversion exception for malformed value.
*
* @param value the value
*/
public DurationConversionException(final String value) {
super(format("'%s' is not a long", value));
}
/**
* Instantiates a new Duration conversion exception for negative value.
*
* @param value the millis
*/
public DurationConversionException(final long value) {
super(format("negative value '%d' is not allowed", value));
}
}

@ -16,6 +16,11 @@ package org.hyperledger.besu.cli.options;
import static com.google.common.base.Preconditions.checkArgument;
import org.hyperledger.besu.datatypes.Wei;
import java.lang.invoke.MethodType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Iterator;
import com.google.common.base.Splitter;
@ -96,4 +101,44 @@ public class OptionParser {
public static String format(final UInt256 value) {
return value.toBigInteger().toString(10);
}
/**
* Format Wei to string.
*
* @param value the value
* @return the string
*/
public static String format(final Wei value) {
return format(value.toUInt256());
}
/**
* Format any object to string. This implementation tries to find an existing format method, in
* this class, that matches the type of the passed object, and if not found just invoke, to string
* on the passed object
*
* @param value the object
* @return the string
*/
public static String format(final Object value) {
Method formatMethod;
try {
formatMethod = OptionParser.class.getMethod("format", value.getClass());
} catch (NoSuchMethodException e) {
try {
// maybe a primitive version of the method exists
formatMethod =
OptionParser.class.getMethod(
"format", MethodType.methodType(value.getClass()).unwrap().returnType());
} catch (NoSuchMethodException ex) {
return value.toString();
}
}
try {
return (String) formatMethod.invoke(null, value);
} catch (InvocationTargetException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,263 @@
/*
* 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.stable;
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_LONG_FORMAT_HELP;
import static org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration.Implementation.LAYERED;
import static org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration.Implementation.LEGACY;
import org.hyperledger.besu.cli.converter.FractionConverter;
import org.hyperledger.besu.cli.converter.PercentageConverter;
import org.hyperledger.besu.cli.options.CLIOptions;
import org.hyperledger.besu.cli.util.CommandLineUtils;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.util.number.Fraction;
import org.hyperledger.besu.util.number.Percentage;
import java.io.File;
import java.util.List;
import picocli.CommandLine;
/** The Transaction pool Cli stable options. */
public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfiguration> {
private static final String TX_POOL_IMPLEMENTATION = "--tx-pool";
private static final String TX_POOL_DISABLE_LOCALS = "--tx-pool-disable-locals";
private static final String TX_POOL_ENABLE_SAVE_RESTORE = "--tx-pool-enable-save-restore";
private static final String TX_POOL_SAVE_FILE = "--tx-pool-save-file";
private static final String TX_POOL_PRICE_BUMP = "--tx-pool-price-bump";
private static final String RPC_TX_FEECAP = "--rpc-tx-feecap";
private static final String STRICT_TX_REPLAY_PROTECTION_ENABLED_FLAG =
"--strict-tx-replay-protection-enabled";
@CommandLine.Option(
names = {TX_POOL_IMPLEMENTATION},
paramLabel = "<Enum>",
description = "The Transaction Pool implementation to use(default: ${DEFAULT-VALUE})",
arity = "0..1")
private TransactionPoolConfiguration.Implementation txPoolImplementation = LAYERED;
@CommandLine.Option(
names = {TX_POOL_DISABLE_LOCALS},
paramLabel = "<Boolean>",
description =
"Set to true if transactions sent via RPC should have the same checks and not be prioritized over remote ones (default: ${DEFAULT-VALUE})",
fallbackValue = "true",
arity = "0..1")
private Boolean disableLocalTxs = TransactionPoolConfiguration.DEFAULT_DISABLE_LOCAL_TXS;
@CommandLine.Option(
names = {TX_POOL_ENABLE_SAVE_RESTORE},
paramLabel = "<Boolean>",
description =
"Set to true to enable saving the txpool content to file on shutdown and reloading it on startup (default: ${DEFAULT-VALUE})",
fallbackValue = "true",
arity = "0..1")
private Boolean saveRestoreEnabled = TransactionPoolConfiguration.DEFAULT_ENABLE_SAVE_RESTORE;
@CommandLine.Option(
names = {TX_POOL_SAVE_FILE},
paramLabel = "<STRING>",
description =
"If saving the txpool content is enabled, define a custom path for the save file (default: ${DEFAULT-VALUE} in the data-dir)",
arity = "1")
private File saveFile = TransactionPoolConfiguration.DEFAULT_SAVE_FILE;
@CommandLine.Option(
names = {TX_POOL_PRICE_BUMP},
paramLabel = "<Percentage>",
converter = PercentageConverter.class,
description =
"Price bump percentage to replace an already existing transaction (default: ${DEFAULT-VALUE})",
arity = "1")
private Percentage priceBump = TransactionPoolConfiguration.DEFAULT_PRICE_BUMP;
@CommandLine.Option(
names = {RPC_TX_FEECAP},
description =
"Maximum transaction fees (in Wei) accepted for transaction submitted through RPC (default: ${DEFAULT-VALUE})",
arity = "1")
private Wei txFeeCap = TransactionPoolConfiguration.DEFAULT_RPC_TX_FEE_CAP;
@CommandLine.Option(
names = {STRICT_TX_REPLAY_PROTECTION_ENABLED_FLAG},
paramLabel = "<Boolean>",
description =
"Require transactions submitted via JSON-RPC to use replay protection in accordance with EIP-155 (default: ${DEFAULT-VALUE})",
fallbackValue = "true",
arity = "0..1")
private Boolean strictTxReplayProtectionEnabled = false;
@CommandLine.ArgGroup(
validate = false,
heading = "@|bold Tx Pool Layered Implementation Options|@%n")
private final Layered layeredOptions = new Layered();
static class Layered {
private static final String TX_POOL_LAYER_MAX_CAPACITY = "--tx-pool-layer-max-capacity";
private static final String TX_POOL_MAX_PRIORITIZED = "--tx-pool-max-prioritized";
private static final String TX_POOL_MAX_FUTURE_BY_SENDER = "--tx-pool-max-future-by-sender";
@CommandLine.Option(
names = {TX_POOL_LAYER_MAX_CAPACITY},
paramLabel = MANDATORY_LONG_FORMAT_HELP,
description =
"Max amount of memory space, in bytes, that any layer within the transaction pool could occupy (default: ${DEFAULT-VALUE})",
arity = "1")
Long txPoolLayerMaxCapacity =
TransactionPoolConfiguration.DEFAULT_PENDING_TRANSACTIONS_LAYER_MAX_CAPACITY_BYTES;
@CommandLine.Option(
names = {TX_POOL_MAX_PRIORITIZED},
paramLabel = MANDATORY_INTEGER_FORMAT_HELP,
description =
"Max number of pending transactions that are prioritized and thus kept sorted (default: ${DEFAULT-VALUE})",
arity = "1")
Integer txPoolMaxPrioritized =
TransactionPoolConfiguration.DEFAULT_MAX_PRIORITIZED_TRANSACTIONS;
@CommandLine.Option(
names = {TX_POOL_MAX_FUTURE_BY_SENDER},
paramLabel = MANDATORY_INTEGER_FORMAT_HELP,
description =
"Max number of future pending transactions allowed for a single sender (default: ${DEFAULT-VALUE})",
arity = "1")
Integer txPoolMaxFutureBySender = TransactionPoolConfiguration.DEFAULT_MAX_FUTURE_BY_SENDER;
}
@CommandLine.ArgGroup(
validate = false,
heading = "@|bold Tx Pool Legacy Implementation Options|@%n")
private final Legacy legacyOptions = new Legacy();
static class Legacy {
private static final String TX_POOL_RETENTION_HOURS = "--tx-pool-retention-hours";
private static final String TX_POOL_LIMIT_BY_ACCOUNT_PERCENTAGE =
"--tx-pool-limit-by-account-percentage";
private static final String TX_POOL_MAX_SIZE = "--tx-pool-max-size";
@CommandLine.Option(
names = {TX_POOL_RETENTION_HOURS},
paramLabel = MANDATORY_INTEGER_FORMAT_HELP,
description =
"Maximum retention period of pending transactions in hours (default: ${DEFAULT-VALUE})",
arity = "1")
Integer pendingTxRetentionPeriod = TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS;
@CommandLine.Option(
names = {TX_POOL_LIMIT_BY_ACCOUNT_PERCENTAGE},
paramLabel = MANDATORY_DOUBLE_FORMAT_HELP,
converter = FractionConverter.class,
description =
"Maximum portion of the transaction pool which a single account may occupy with future transactions (default: ${DEFAULT-VALUE})",
arity = "1")
Fraction txPoolLimitByAccountPercentage =
TransactionPoolConfiguration.DEFAULT_LIMIT_TX_POOL_BY_ACCOUNT_PERCENTAGE;
@CommandLine.Option(
names = {TX_POOL_MAX_SIZE},
paramLabel = MANDATORY_INTEGER_FORMAT_HELP,
description =
"Maximum number of pending transactions that will be kept in the transaction pool (default: ${DEFAULT-VALUE})",
arity = "1")
Integer txPoolMaxSize = TransactionPoolConfiguration.DEFAULT_MAX_PENDING_TRANSACTIONS;
}
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 config) {
final TransactionPoolOptions options = TransactionPoolOptions.create();
options.txPoolImplementation = config.getTxPoolImplementation();
options.saveRestoreEnabled = config.getEnableSaveRestore();
options.disableLocalTxs = config.getDisableLocalTransactions();
options.priceBump = config.getPriceBump();
options.txFeeCap = config.getTxFeeCap();
options.saveFile = config.getSaveFile();
options.strictTxReplayProtectionEnabled = config.getStrictTransactionReplayProtectionEnabled();
options.layeredOptions.txPoolLayerMaxCapacity =
config.getPendingTransactionsLayerMaxCapacityBytes();
options.layeredOptions.txPoolMaxPrioritized = config.getMaxPrioritizedTransactions();
options.layeredOptions.txPoolMaxFutureBySender = config.getMaxFutureBySender();
options.legacyOptions.txPoolLimitByAccountPercentage =
config.getTxPoolLimitByAccountPercentage();
options.legacyOptions.txPoolMaxSize = config.getTxPoolMaxSize();
options.legacyOptions.pendingTxRetentionPeriod = config.getPendingTxRetentionPeriod();
return options;
}
/**
* Validate that there are no inconsistencies in the specified options. For example that the
* options are valid for the selected implementation.
*
* @param commandLine the full commandLine to check all the options specified by the user
*/
public void validate(final CommandLine commandLine) {
CommandLineUtils.failIfOptionDoesntMeetRequirement(
commandLine,
"Could not use legacy transaction pool options with layered implementation",
!txPoolImplementation.equals(LAYERED),
CommandLineUtils.getCLIOptionNames(Legacy.class));
CommandLineUtils.failIfOptionDoesntMeetRequirement(
commandLine,
"Could not use layered transaction pool options with legacy implementation",
!txPoolImplementation.equals(LEGACY),
CommandLineUtils.getCLIOptionNames(Layered.class));
}
@Override
public TransactionPoolConfiguration toDomainObject() {
return ImmutableTransactionPoolConfiguration.builder()
.txPoolImplementation(txPoolImplementation)
.enableSaveRestore(saveRestoreEnabled)
.disableLocalTransactions(disableLocalTxs)
.priceBump(priceBump)
.txFeeCap(txFeeCap)
.saveFile(saveFile)
.strictTransactionReplayProtectionEnabled(strictTxReplayProtectionEnabled)
.pendingTransactionsLayerMaxCapacityBytes(layeredOptions.txPoolLayerMaxCapacity)
.maxPrioritizedTransactions(layeredOptions.txPoolMaxPrioritized)
.maxFutureBySender(layeredOptions.txPoolMaxFutureBySender)
.txPoolLimitByAccountPercentage(legacyOptions.txPoolLimitByAccountPercentage)
.txPoolMaxSize(legacyOptions.txPoolMaxSize)
.pendingTxRetentionPeriod(legacyOptions.pendingTxRetentionPeriod)
.build();
}
@Override
public List<String> getCLIOptions() {
return CommandLineUtils.getCLIOptions(this, new TransactionPoolOptions());
}
}

@ -14,50 +14,25 @@
*/
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.options.OptionParser;
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.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import picocli.CommandLine;
/** The Transaction pool Cli options. */
public class TransactionPoolOptions
implements CLIOptions<ImmutableTransactionPoolConfiguration.Builder> {
private static final Logger LOG = LoggerFactory.getLogger(TransactionPoolOptions.class);
/** 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";
private static final String STRICT_TX_REPLAY_PROTECTION_ENABLED_FLAG =
"--strict-tx-replay-protection-enabled";
private static final String LAYERED_TX_POOL_ENABLED_FLAG = "--Xlayered-tx-pool";
private static final String LAYERED_TX_POOL_LAYER_MAX_CAPACITY =
"--Xlayered-tx-pool-layer-max-capacity";
private static final String LAYERED_TX_POOL_MAX_PRIORITIZED =
"--Xlayered-tx-pool-max-prioritized";
private static final String LAYERED_TX_POOL_MAX_FUTURE_BY_SENDER =
"--Xlayered-tx-pool-max-future-by-sender";
@CommandLine.Option(
names = {STRICT_TX_REPLAY_PROTECTION_ENABLED_FLAG},
paramLabel = "<Boolean>",
description =
"Require transactions submitted via JSON-RPC to use replay protection in accordance with EIP-155 (default: ${DEFAULT-VALUE})",
fallbackValue = "true",
arity = "0..1")
private Boolean strictTxReplayProtectionEnabled = false;
@CommandLine.Option(
names = {TX_MESSAGE_KEEP_ALIVE_SEC_FLAG},
paramLabel = "<INTEGER>",
@ -66,56 +41,18 @@ public class TransactionPoolOptions
"Keep alive of incoming transaction messages in seconds (default: ${DEFAULT-VALUE})",
arity = "1")
private Integer txMessageKeepAliveSeconds =
TransactionPoolConfiguration.DEFAULT_TX_MSG_KEEP_ALIVE;
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 long eth65TrxAnnouncedBufferingPeriod =
TransactionPoolConfiguration.ETH65_TRX_ANNOUNCED_BUFFERING_PERIOD.toMillis();
@CommandLine.Option(
names = {LAYERED_TX_POOL_ENABLED_FLAG},
paramLabel = "<Boolean>",
hidden = true,
description = "Enable the Layered Transaction Pool (default: ${DEFAULT-VALUE})",
arity = "0..1")
private Boolean layeredTxPoolEnabled =
TransactionPoolConfiguration.DEFAULT_LAYERED_TX_POOL_ENABLED;
@CommandLine.Option(
names = {LAYERED_TX_POOL_LAYER_MAX_CAPACITY},
paramLabel = "<Long>",
hidden = true,
description =
"Max amount of memory space, in bytes, that any layer within the transaction pool could occupy (default: ${DEFAULT-VALUE})",
arity = "1")
private long layeredTxPoolLayerMaxCapacity =
TransactionPoolConfiguration.DEFAULT_PENDING_TRANSACTIONS_LAYER_MAX_CAPACITY_BYTES;
@CommandLine.Option(
names = {LAYERED_TX_POOL_MAX_PRIORITIZED},
paramLabel = "<Int>",
hidden = true,
description =
"Max number of pending transactions that are prioritized and thus kept sorted (default: ${DEFAULT-VALUE})",
arity = "1")
private int layeredTxPoolMaxPrioritized =
TransactionPoolConfiguration.DEFAULT_MAX_PRIORITIZED_TRANSACTIONS;
@CommandLine.Option(
names = {LAYERED_TX_POOL_MAX_FUTURE_BY_SENDER},
paramLabel = "<Int>",
hidden = true,
description =
"Max number of future pending transactions allowed for a single sender (default: ${DEFAULT-VALUE})",
arity = "1")
private int layeredTxPoolMaxFutureBySender =
TransactionPoolConfiguration.DEFAULT_MAX_FUTURE_BY_SENDER;
private Duration eth65TrxAnnouncedBufferingPeriod =
TransactionPoolConfiguration.Unstable.ETH65_TRX_ANNOUNCED_BUFFERING_PERIOD;
private TransactionPoolOptions() {}
@ -134,51 +71,24 @@ public class TransactionPoolOptions
* @param config the Transaction Pool Configuration
* @return the transaction pool options
*/
public static TransactionPoolOptions fromConfig(final TransactionPoolConfiguration config) {
public static TransactionPoolOptions fromConfig(
final TransactionPoolConfiguration.Unstable config) {
final TransactionPoolOptions options = TransactionPoolOptions.create();
options.txMessageKeepAliveSeconds = config.getTxMessageKeepAliveSeconds();
options.eth65TrxAnnouncedBufferingPeriod =
config.getEth65TrxAnnouncedBufferingPeriod().toMillis();
options.strictTxReplayProtectionEnabled = config.getStrictTransactionReplayProtectionEnabled();
options.layeredTxPoolEnabled = config.getLayeredTxPoolEnabled();
options.layeredTxPoolLayerMaxCapacity = config.getPendingTransactionsLayerMaxCapacityBytes();
options.layeredTxPoolMaxPrioritized = config.getMaxPrioritizedTransactions();
options.layeredTxPoolMaxFutureBySender = config.getMaxFutureBySender();
options.eth65TrxAnnouncedBufferingPeriod = config.getEth65TrxAnnouncedBufferingPeriod();
return options;
}
@Override
public ImmutableTransactionPoolConfiguration.Builder toDomainObject() {
if (layeredTxPoolEnabled) {
LOG.warn(
"Layered transaction pool enabled, ignoring settings for "
+ "--tx-pool-max-size and --tx-pool-limit-by-account-percentage");
}
return ImmutableTransactionPoolConfiguration.builder()
.strictTransactionReplayProtectionEnabled(strictTxReplayProtectionEnabled)
public TransactionPoolConfiguration.Unstable toDomainObject() {
return ImmutableTransactionPoolConfiguration.Unstable.builder()
.txMessageKeepAliveSeconds(txMessageKeepAliveSeconds)
.eth65TrxAnnouncedBufferingPeriod(Duration.ofMillis(eth65TrxAnnouncedBufferingPeriod))
.layeredTxPoolEnabled(layeredTxPoolEnabled)
.pendingTransactionsLayerMaxCapacityBytes(layeredTxPoolLayerMaxCapacity)
.maxPrioritizedTransactions(layeredTxPoolMaxPrioritized)
.maxFutureBySender(layeredTxPoolMaxFutureBySender);
.eth65TrxAnnouncedBufferingPeriod(eth65TrxAnnouncedBufferingPeriod)
.build();
}
@Override
public List<String> getCLIOptions() {
return Arrays.asList(
STRICT_TX_REPLAY_PROTECTION_ENABLED_FLAG + "=" + strictTxReplayProtectionEnabled,
TX_MESSAGE_KEEP_ALIVE_SEC_FLAG,
OptionParser.format(txMessageKeepAliveSeconds),
ETH65_TX_ANNOUNCED_BUFFERING_PERIOD_FLAG,
OptionParser.format(eth65TrxAnnouncedBufferingPeriod),
LAYERED_TX_POOL_ENABLED_FLAG + "=" + layeredTxPoolEnabled,
LAYERED_TX_POOL_LAYER_MAX_CAPACITY,
OptionParser.format(layeredTxPoolLayerMaxCapacity),
LAYERED_TX_POOL_MAX_PRIORITIZED,
OptionParser.format(layeredTxPoolMaxPrioritized),
LAYERED_TX_POOL_MAX_FUTURE_BY_SENDER,
OptionParser.format(layeredTxPoolMaxFutureBySender));
return CommandLineUtils.getCLIOptions(this, new TransactionPoolOptions());
}
}

@ -14,10 +14,17 @@
*/
package org.hyperledger.besu.cli.util;
import org.hyperledger.besu.cli.converter.TypeFormatter;
import org.hyperledger.besu.cli.options.OptionParser;
import org.hyperledger.besu.util.StringUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import com.google.common.base.Strings;
@ -124,6 +131,103 @@ public class CommandLineUtils {
}
}
/**
* Return all the option names declared in a class. Note this will recursively check in any inner
* option class if present.
*
* @param optClass the class to look for options
* @return a list of option names found in the class
*/
public static List<String> getCLIOptionNames(final Class<?> optClass) {
final List<String> cliOpts = new ArrayList<>();
final Field[] fields = optClass.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
Annotation ann = field.getAnnotation(CommandLine.Option.class);
if (ann != null) {
final var optAnn = CommandLine.Option.class.cast(ann);
cliOpts.add(optAnn.names()[0]);
} else {
ann = field.getAnnotation(CommandLine.ArgGroup.class);
if (ann != null) {
cliOpts.addAll(getCLIOptionNames(field.getType()));
}
}
}
return cliOpts;
}
/**
* Converts the runtime options into their CLI representation. Options with a value equals to its
* default are not included in the result since redundant. Note this will recursively check in any
* inner option class if present.
*
* @param currOptions the actual runtime options
* @param defaults the default option values
* @return a list of CLI arguments
*/
public static List<String> getCLIOptions(final Object currOptions, final Object defaults) {
final List<String> cliOpts = new ArrayList<>();
final Field[] fields = currOptions.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
Annotation ann = field.getAnnotation(CommandLine.Option.class);
if (ann != null) {
try {
var optVal = field.get(currOptions);
if (!Objects.equals(optVal, field.get(defaults))) {
var optAnn = CommandLine.Option.class.cast(ann);
cliOpts.add(optAnn.names()[0]);
final var optConverter = optAnn.converter();
cliOpts.add(formatValue(optConverter, optVal));
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
} else {
ann = field.getAnnotation(CommandLine.ArgGroup.class);
if (ann != null) {
try {
cliOpts.addAll(getCLIOptions(field.get(currOptions), field.get(defaults)));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
return cliOpts;
}
/**
* There are different ways to format an option value back to its CLI form, the first is to use a
* {@link TypeFormatter} if present, otherwise the formatting it is delegated to {@link
* OptionParser#format(Object)}
*
* @param optConverter the list of converter types for the option
* @param optVal the value of the options
* @return a string with the CLI form of the value
*/
@SuppressWarnings("unchecked")
private static String formatValue(
final Class<? extends CommandLine.ITypeConverter<?>>[] optConverter, final Object optVal) {
return Arrays.stream(optConverter)
.filter(c -> Arrays.stream(c.getInterfaces()).anyMatch(i -> i.equals(TypeFormatter.class)))
.findFirst()
.map(
ctf -> {
try {
return (TypeFormatter) ctf.getDeclaredConstructor().newInstance();
} catch (InstantiationException
| IllegalAccessException
| InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
})
.map(tf -> tf.format(optVal))
.orElseGet(() -> OptionParser.format(optVal));
}
private static String getAffectedOptions(
final CommandLine commandLine, final List<String> dependentOptionsNames) {
return commandLine.getCommandSpec().options().stream()

@ -15,6 +15,7 @@
package org.hyperledger.besu.cli.util;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.util.number.Percentage;
import java.io.File;
import java.io.IOException;
@ -89,6 +90,8 @@ public class TomlConfigFileDefaultProvider implements IDefaultValueProvider {
defaultValue = getNumericEntryAsString(optionSpec);
} else if (optionSpec.type().equals(Float.class) || optionSpec.type().equals(float.class)) {
defaultValue = getNumericEntryAsString(optionSpec);
} else if (optionSpec.type().equals(Percentage.class)) {
defaultValue = getNumericEntryAsString(optionSpec);
} else { // else will be treated as String
defaultValue = getEntryAsString(optionSpec);
}

@ -58,7 +58,6 @@ import static org.mockito.Mockito.when;
import org.hyperledger.besu.BesuInfo;
import org.hyperledger.besu.cli.config.EthNetworkConfig;
import org.hyperledger.besu.cli.options.unstable.TransactionPoolOptions;
import org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.config.MergeConfigOptions;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
@ -4597,192 +4596,6 @@ public class BesuCommandTest extends CommandTestAbstract {
.contains(staticNodeURI.toString(), "not in nodes-allowlist");
}
@Test
public void disableLocalsDefault() {
parseCommand();
verify(mockControllerBuilder)
.transactionPoolConfiguration(transactionPoolConfigCaptor.capture());
assertThat(transactionPoolConfigCaptor.getValue().getDisableLocalTransactions()).isFalse();
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void disableLocalsOn() {
parseCommand("--tx-pool-disable-locals=true");
verify(mockControllerBuilder)
.transactionPoolConfiguration(transactionPoolConfigCaptor.capture());
assertThat(transactionPoolConfigCaptor.getValue().getDisableLocalTransactions()).isTrue();
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void disableLocalsOff() {
parseCommand("--tx-pool-disable-locals=false");
verify(mockControllerBuilder)
.transactionPoolConfiguration(transactionPoolConfigCaptor.capture());
assertThat(transactionPoolConfigCaptor.getValue().getDisableLocalTransactions()).isFalse();
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void saveToFileDisabledByDefault() {
parseCommand();
verify(mockControllerBuilder)
.transactionPoolConfiguration(transactionPoolConfigCaptor.capture());
assertThat(transactionPoolConfigCaptor.getValue().getEnableSaveRestore()).isFalse();
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void saveToFileEnabledDefaultPath() {
parseCommand("--tx-pool-enable-save-restore=true");
verify(mockControllerBuilder)
.transactionPoolConfiguration(transactionPoolConfigCaptor.capture());
assertThat(transactionPoolConfigCaptor.getValue().getEnableSaveRestore()).isTrue();
assertThat(transactionPoolConfigCaptor.getValue().getSaveFile())
.hasName(TransactionPoolConfiguration.DEFAULT_SAVE_FILE_NAME);
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void saveToFileEnabledCustomPath() {
parseCommand("--tx-pool-enable-save-restore=true", "--tx-pool-save-file=my.save.file");
verify(mockControllerBuilder)
.transactionPoolConfiguration(transactionPoolConfigCaptor.capture());
assertThat(transactionPoolConfigCaptor.getValue().getEnableSaveRestore()).isTrue();
assertThat(transactionPoolConfigCaptor.getValue().getSaveFile()).hasName("my.save.file");
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void senderLimitedTxPool_derived() {
parseCommand("--tx-pool-limit-by-account-percentage=0.002");
verify(mockControllerBuilder)
.transactionPoolConfiguration(transactionPoolConfigCaptor.capture());
assertThat(transactionPoolConfigCaptor.getValue().getTxPoolMaxFutureTransactionByAccount())
.isEqualTo(9);
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void senderLimitedTxPoolFloor_derived() {
parseCommand("--tx-pool-limit-by-account-percentage=0.0001");
verify(mockControllerBuilder)
.transactionPoolConfiguration(transactionPoolConfigCaptor.capture());
assertThat(transactionPoolConfigCaptor.getValue().getTxPoolMaxFutureTransactionByAccount())
.isEqualTo(1);
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void senderLimitedTxPoolCeiling_violated() {
TestBesuCommand commandTest = parseCommand("--tx-pool-limit-by-account-percentage=1.00002341");
TransactionPoolOptions txPoolOption = commandTest.getTransactionPoolOptions();
final TransactionPoolConfiguration config = txPoolOption.toDomainObject().build();
assertThat(config.getTxPoolLimitByAccountPercentage())
.isEqualTo(TransactionPoolConfiguration.DEFAULT_LIMIT_TX_POOL_BY_ACCOUNT_PERCENTAGE);
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8))
.contains("Invalid value for option '--tx-pool-limit-by-account-percentage'");
}
@Test
public void pendingTransactionRetentionPeriod() {
final int pendingTxRetentionHours = 999;
parseCommand("--tx-pool-retention-hours", String.valueOf(pendingTxRetentionHours));
verify(mockControllerBuilder)
.transactionPoolConfiguration(transactionPoolConfigCaptor.capture());
assertThat(transactionPoolConfigCaptor.getValue().getPendingTxRetentionPeriod())
.isEqualTo(pendingTxRetentionHours);
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void transactionPoolPriceBump() {
final Percentage priceBump = Percentage.fromInt(13);
parseCommand("--tx-pool-price-bump", priceBump.toString());
verify(mockControllerBuilder)
.transactionPoolConfiguration(transactionPoolConfigCaptor.capture());
assertThat(transactionPoolConfigCaptor.getValue().getPriceBump()).isEqualTo(priceBump);
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void invalidTansactionPoolPriceBumpShouldFail() {
parseCommand("--tx-pool-price-bump", "101");
assertThat(commandErrorOutput.toString(UTF_8))
.contains(
"Invalid value for option '--tx-pool-price-bump'",
"should be a number between 0 and 100 inclusive");
}
@Test
public void transactionPoolTxFeeCap() {
final Wei txFeeCap = Wei.fromEth(2);
parseCommand("--rpc-tx-feecap", txFeeCap.toDecimalString());
verify(mockControllerBuilder)
.transactionPoolConfiguration(transactionPoolConfigCaptor.capture());
assertThat(transactionPoolConfigCaptor.getValue().getTxFeeCap()).isEqualTo(txFeeCap);
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void invalidTansactionPoolTxFeeCapShouldFail() {
parseCommand("--rpc-tx-feecap", "abcd");
assertThat(commandErrorOutput.toString(UTF_8))
.contains("Invalid value for option '--rpc-tx-feecap'", "cannot convert 'abcd' to Wei");
}
@Test
public void txMessageKeepAliveSecondsWithInvalidInputShouldFail() {
parseCommand("--Xincoming-tx-messages-keep-alive-seconds", "acbd");
Mockito.verifyNoInteractions(mockRunnerBuilder);
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8))
.contains(
"Invalid value for option '--Xincoming-tx-messages-keep-alive-seconds': 'acbd' is not an int");
}
@Test
public void eth65TrxAnnouncedBufferingPeriodWithInvalidInputShouldFail() {
parseCommand("--Xeth65-tx-announced-buffering-period-milliseconds", "acbd");
Mockito.verifyNoInteractions(mockRunnerBuilder);
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8))
.contains(
"Invalid value for option '--Xeth65-tx-announced-buffering-period-milliseconds': 'acbd' is not a long");
}
@Test
public void tomlThatHasInvalidOptions() throws IOException {
final URL configFile = this.getClass().getResource("/complete_config.toml");

@ -519,7 +519,12 @@ public abstract class CommandTestAbstract {
return unstableEthProtocolOptions;
}
public TransactionPoolOptions getTransactionPoolOptions() {
public org.hyperledger.besu.cli.options.stable.TransactionPoolOptions
getStableTransactionPoolOptions() {
return stableTransactionPoolOptions;
}
public TransactionPoolOptions getUnstableTransactionPoolOptions() {
return unstableTransactionPoolOptions;
}

@ -15,6 +15,8 @@
package org.hyperledger.besu.cli;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration.Implementation.LAYERED;
import static org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration.Implementation.LEGACY;
import static org.mockito.Mockito.mock;
import java.math.BigInteger;
@ -145,14 +147,16 @@ class ConfigurationOverviewBuilderTest {
}
@Test
void setLayeredTxPoolEnabled() {
final String layeredTxPoolDisabled = builder.build();
assertThat(layeredTxPoolDisabled)
.doesNotContain("Experimental layered transaction pool configuration enabled");
builder.setLayeredTxPoolEnabled();
final String layeredTxPoolEnabled = builder.build();
assertThat(layeredTxPoolEnabled)
.contains("Experimental layered transaction pool configuration enabled");
void setTxPoolImplementationLayered() {
builder.setTxPoolImplementation(LAYERED);
final String layeredTxPoolSelected = builder.build();
assertThat(layeredTxPoolSelected).contains("Using LAYERED transaction pool implementation");
}
@Test
void setTxPoolImplementationLegacy() {
builder.setTxPoolImplementation(LEGACY);
final String legacyTxPoolSelected = builder.build();
assertThat(legacyTxPoolSelected).contains("Using LEGACY transaction pool implementation");
}
}

@ -18,6 +18,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable;
import org.hyperledger.besu.cli.converter.exception.FractionConversionException;
import org.hyperledger.besu.util.number.Fraction;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -30,9 +31,9 @@ public class FractionConverterTest {
@Test
public void assertThatConvertHandlesProperlyAValidString() throws FractionConversionException {
final float fraction = fractionConverter.convert("0.58");
final Fraction fraction = fractionConverter.convert("0.58");
assertThat(fraction).isNotNull();
assertThat(fraction).isEqualTo(0.58f);
assertThat(fraction.getValue()).isEqualTo(0.58f);
}
@Test

@ -18,6 +18,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable;
import org.hyperledger.besu.cli.converter.exception.PercentageConversionException;
import org.hyperledger.besu.util.number.Percentage;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -30,9 +31,9 @@ public class PercentageConverterTest {
@Test
public void assertThatConvertHandlesProperlyAValidString() throws PercentageConversionException {
final int percentage = percentageConverter.convert("58");
final Percentage percentage = percentageConverter.convert("58");
assertThat(percentage).isNotNull();
assertThat(percentage).isEqualTo(58);
assertThat(percentage.getValue()).isEqualTo(58);
}
@Test

@ -21,6 +21,7 @@ import org.hyperledger.besu.cli.CommandTestAbstract;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import org.junit.Test;
@ -87,9 +88,9 @@ public abstract class AbstractCLIOptionsTest<D, T extends CLIOptions<D>>
.isEqualTo(defaultOptions);
}
abstract D createDefaultDomainObject();
protected abstract D createDefaultDomainObject();
abstract D createCustomizedDomainObject();
protected abstract D createCustomizedDomainObject();
protected List<String> getFieldsWithComputedDefaults() {
return Collections.emptyList();
@ -99,7 +100,25 @@ public abstract class AbstractCLIOptionsTest<D, T extends CLIOptions<D>>
return Collections.emptyList();
}
abstract T optionsFromDomainObject(D domainObject);
protected abstract T optionsFromDomainObject(D domainObject);
abstract T getOptionsFromBesuCommand(final TestBesuCommand besuCommand);
protected abstract T getOptionsFromBesuCommand(final TestBesuCommand besuCommand);
protected void internalTestSuccess(final Consumer<D> assertion, final String... args) {
final TestBesuCommand cmd = parseCommand(args);
final T options = getOptionsFromBesuCommand(cmd);
final D config = options.toDomainObject();
assertion.accept(config);
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
protected void internalTestFailure(final String errorMsg, final String... args) {
parseCommand(args);
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).contains(errorMsg);
}
}

@ -165,12 +165,12 @@ public class EthProtocolOptionsTest
}
@Override
EthProtocolConfiguration createDefaultDomainObject() {
protected EthProtocolConfiguration createDefaultDomainObject() {
return EthProtocolConfiguration.builder().build();
}
@Override
EthProtocolConfiguration createCustomizedDomainObject() {
protected EthProtocolConfiguration createCustomizedDomainObject() {
return EthProtocolConfiguration.builder()
.maxMessageSize(EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE * 2)
.maxGetBlockHeaders(EthProtocolConfiguration.DEFAULT_MAX_GET_BLOCK_HEADERS + 2)
@ -184,12 +184,13 @@ public class EthProtocolOptionsTest
}
@Override
EthProtocolOptions optionsFromDomainObject(final EthProtocolConfiguration domainObject) {
protected EthProtocolOptions optionsFromDomainObject(
final EthProtocolConfiguration domainObject) {
return EthProtocolOptions.fromConfig(domainObject);
}
@Override
EthProtocolOptions getOptionsFromBesuCommand(final TestBesuCommand besuCommand) {
protected EthProtocolOptions getOptionsFromBesuCommand(final TestBesuCommand besuCommand) {
return besuCommand.getEthProtocolOptions();
}
}

@ -25,24 +25,25 @@ public class MetricsCLIOptionsTest
extends AbstractCLIOptionsTest<MetricsConfiguration.Builder, MetricsCLIOptions> {
@Override
MetricsConfiguration.Builder createDefaultDomainObject() {
protected MetricsConfiguration.Builder createDefaultDomainObject() {
return MetricsConfiguration.builder();
}
@Override
MetricsConfiguration.Builder createCustomizedDomainObject() {
protected MetricsConfiguration.Builder createCustomizedDomainObject() {
return MetricsConfiguration.builder()
.timersEnabled(!MetricsConfiguration.DEFAULT_METRICS_TIMERS_ENABLED)
.idleTimeout(MetricsConfiguration.DEFAULT_METRICS_IDLE_TIMEOUT_SECONDS);
}
@Override
MetricsCLIOptions optionsFromDomainObject(final MetricsConfiguration.Builder domainObject) {
protected MetricsCLIOptions optionsFromDomainObject(
final MetricsConfiguration.Builder domainObject) {
return MetricsCLIOptions.fromConfiguration(domainObject.build());
}
@Override
MetricsCLIOptions getOptionsFromBesuCommand(final TestBesuCommand besuCommand) {
protected MetricsCLIOptions getOptionsFromBesuCommand(final TestBesuCommand besuCommand) {
return besuCommand.getMetricsCLIOptions();
}
}

@ -165,12 +165,12 @@ public class NetworkingOptionsTest
}
@Override
NetworkingConfiguration createDefaultDomainObject() {
protected NetworkingConfiguration createDefaultDomainObject() {
return NetworkingConfiguration.create();
}
@Override
NetworkingConfiguration createCustomizedDomainObject() {
protected NetworkingConfiguration createCustomizedDomainObject() {
final NetworkingConfiguration config = NetworkingConfiguration.create();
config.setInitiateConnectionsFrequency(
NetworkingConfiguration.DEFAULT_INITIATE_CONNECTIONS_FREQUENCY_SEC + 10);
@ -181,12 +181,12 @@ public class NetworkingOptionsTest
}
@Override
NetworkingOptions optionsFromDomainObject(final NetworkingConfiguration domainObject) {
protected NetworkingOptions optionsFromDomainObject(final NetworkingConfiguration domainObject) {
return NetworkingOptions.fromConfig(domainObject);
}
@Override
NetworkingOptions getOptionsFromBesuCommand(final TestBesuCommand besuCommand) {
protected NetworkingOptions getOptionsFromBesuCommand(final TestBesuCommand besuCommand) {
return besuCommand.getNetworkingOptions();
}

@ -104,4 +104,25 @@ public class OptionParserTest {
final String expected = "-1233";
assertThat(OptionParser.format(input)).isEqualTo(expected);
}
@Test
public void format_object_int() {
final Object input = 1233;
final String expected = "1233";
assertThat(OptionParser.format(input)).isEqualTo(expected);
}
@Test
public void format_object_Integer() {
final Object input = Integer.valueOf(1233);
final String expected = "1233";
assertThat(OptionParser.format(input)).isEqualTo(expected);
}
@Test
public void format_object_uint256() {
final Object input = UInt256.valueOf(new BigInteger("123456789", 10));
final String expected = "123456789";
assertThat(OptionParser.format(input)).isEqualTo(expected);
}
}

@ -31,12 +31,12 @@ public class SynchronizerOptionsTest
extends AbstractCLIOptionsTest<SynchronizerConfiguration.Builder, SynchronizerOptions> {
@Override
SynchronizerConfiguration.Builder createDefaultDomainObject() {
protected SynchronizerConfiguration.Builder createDefaultDomainObject() {
return SynchronizerConfiguration.builder();
}
@Override
SynchronizerConfiguration.Builder createCustomizedDomainObject() {
protected SynchronizerConfiguration.Builder createCustomizedDomainObject() {
return SynchronizerConfiguration.builder()
.fastSyncPivotDistance(SynchronizerConfiguration.DEFAULT_PIVOT_DISTANCE_FROM_HEAD + 10)
.fastSyncFullValidationRate(SynchronizerConfiguration.DEFAULT_FULL_VALIDATION_RATE / 2)
@ -87,7 +87,7 @@ public class SynchronizerOptionsTest
}
@Override
SynchronizerOptions getOptionsFromBesuCommand(final TestBesuCommand besuCommand) {
protected SynchronizerOptions getOptionsFromBesuCommand(final TestBesuCommand besuCommand) {
return besuCommand.getSynchronizerOptions();
}
@ -97,7 +97,7 @@ public class SynchronizerOptionsTest
}
@Override
SynchronizerOptions optionsFromDomainObject(
protected SynchronizerOptions optionsFromDomainObject(
final SynchronizerConfiguration.Builder domainObject) {
return SynchronizerOptions.fromConfig(domainObject.build());
}

@ -1,155 +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;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.cli.options.unstable.TransactionPoolOptions;
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<
ImmutableTransactionPoolConfiguration.Builder, TransactionPoolOptions> {
@Test
public void strictTxReplayProtection_enabled() {
final TestBesuCommand cmd = parseCommand("--strict-tx-replay-protection-enabled");
final TransactionPoolOptions options = getOptionsFromBesuCommand(cmd);
final TransactionPoolConfiguration config = options.toDomainObject().build();
assertThat(config.getStrictTransactionReplayProtectionEnabled()).isTrue();
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void strictTxReplayProtection_enabledWithBooleanArg() {
final TestBesuCommand cmd = parseCommand("--strict-tx-replay-protection-enabled=true");
final TransactionPoolOptions options = getOptionsFromBesuCommand(cmd);
final TransactionPoolConfiguration config = options.toDomainObject().build();
assertThat(config.getStrictTransactionReplayProtectionEnabled()).isTrue();
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void strictTxReplayProtection_disabled() {
final TestBesuCommand cmd = parseCommand("--strict-tx-replay-protection-enabled=false");
final TransactionPoolOptions options = getOptionsFromBesuCommand(cmd);
final TransactionPoolConfiguration config = options.toDomainObject().build();
assertThat(config.getStrictTransactionReplayProtectionEnabled()).isFalse();
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void strictTxReplayProtection_default() {
final TestBesuCommand cmd = parseCommand();
final TransactionPoolOptions options = getOptionsFromBesuCommand(cmd);
final TransactionPoolConfiguration config = options.toDomainObject().build();
assertThat(config.getStrictTransactionReplayProtectionEnabled()).isFalse();
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void txMessageKeepAliveSeconds() {
final int txMessageKeepAliveSeconds = 999;
final TestBesuCommand cmd =
parseCommand(
"--Xincoming-tx-messages-keep-alive-seconds",
String.valueOf(txMessageKeepAliveSeconds));
final TransactionPoolOptions options = getOptionsFromBesuCommand(cmd);
final TransactionPoolConfiguration config = options.toDomainObject().build();
assertThat(config.getTxMessageKeepAliveSeconds()).isEqualTo(txMessageKeepAliveSeconds);
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void eth65TrxAnnouncedBufferingPeriod() {
final long eth65TrxAnnouncedBufferingPeriod = 999;
final TestBesuCommand cmd =
parseCommand(
"--Xeth65-tx-announced-buffering-period-milliseconds",
String.valueOf(eth65TrxAnnouncedBufferingPeriod));
final TransactionPoolOptions options = getOptionsFromBesuCommand(cmd);
final TransactionPoolConfiguration config = options.toDomainObject().build();
assertThat(config.getEth65TrxAnnouncedBufferingPeriod())
.hasMillis(eth65TrxAnnouncedBufferingPeriod);
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Override
ImmutableTransactionPoolConfiguration.Builder createDefaultDomainObject() {
final ImmutableTransactionPoolConfiguration defaultValue =
ImmutableTransactionPoolConfiguration.builder().build();
return ImmutableTransactionPoolConfiguration.builder()
.strictTransactionReplayProtectionEnabled(false)
.txMessageKeepAliveSeconds(defaultValue.getTxMessageKeepAliveSeconds())
.eth65TrxAnnouncedBufferingPeriod(defaultValue.getEth65TrxAnnouncedBufferingPeriod())
.layeredTxPoolEnabled(defaultValue.getLayeredTxPoolEnabled())
.pendingTransactionsLayerMaxCapacityBytes(
defaultValue.getPendingTransactionsLayerMaxCapacityBytes())
.maxPrioritizedTransactions(defaultValue.getMaxPrioritizedTransactions())
.maxFutureBySender(defaultValue.getMaxFutureBySender());
}
@Override
ImmutableTransactionPoolConfiguration.Builder createCustomizedDomainObject() {
return ImmutableTransactionPoolConfiguration.builder()
.strictTransactionReplayProtectionEnabled(true)
.txMessageKeepAliveSeconds(TransactionPoolConfiguration.DEFAULT_TX_MSG_KEEP_ALIVE + 1)
.eth65TrxAnnouncedBufferingPeriod(
TransactionPoolConfiguration.ETH65_TRX_ANNOUNCED_BUFFERING_PERIOD.plus(
Duration.ofMillis(100)))
.layeredTxPoolEnabled(true)
.pendingTransactionsLayerMaxCapacityBytes(1_000_000L)
.maxPrioritizedTransactions(1000)
.maxFutureBySender(10);
}
@Override
TransactionPoolOptions optionsFromDomainObject(
final ImmutableTransactionPoolConfiguration.Builder domainObject) {
return TransactionPoolOptions.fromConfig(domainObject.build());
}
@Override
TransactionPoolOptions getOptionsFromBesuCommand(final TestBesuCommand besuCommand) {
return besuCommand.getTransactionPoolOptions();
}
}

@ -0,0 +1,243 @@
/*
* 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.stable;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration.Implementation.LAYERED;
import static org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration.Implementation.LEGACY;
import org.hyperledger.besu.cli.options.AbstractCLIOptionsTest;
import org.hyperledger.besu.cli.options.OptionParser;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.util.number.Percentage;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class TransactionPoolOptionsTest
extends AbstractCLIOptionsTest<TransactionPoolConfiguration, TransactionPoolOptions> {
@Test
public void strictTxReplayProtection_enabled() {
internalTestSuccess(
config -> assertThat(config.getStrictTransactionReplayProtectionEnabled()).isTrue(),
"--strict-tx-replay-protection-enabled");
}
@Test
public void strictTxReplayProtection_enabledWithBooleanArg() {
internalTestSuccess(
config -> assertThat(config.getStrictTransactionReplayProtectionEnabled()).isTrue(),
"--strict-tx-replay-protection-enabled=true");
}
@Test
public void strictTxReplayProtection_disabled() {
internalTestSuccess(
config -> assertThat(config.getStrictTransactionReplayProtectionEnabled()).isFalse(),
"--strict-tx-replay-protection-enabled=false");
}
@Test
public void strictTxReplayProtection_default() {
internalTestSuccess(
config -> assertThat(config.getStrictTransactionReplayProtectionEnabled()).isFalse());
}
@Test
public void pendingTransactionRetentionPeriod() {
final int pendingTxRetentionHours = 999;
internalTestSuccess(
config ->
assertThat(config.getPendingTxRetentionPeriod()).isEqualTo(pendingTxRetentionHours),
"--tx-pool-retention-hours",
String.valueOf(pendingTxRetentionHours),
"--tx-pool=legacy");
}
@Test
public void disableLocalsDefault() {
internalTestSuccess(config -> assertThat(config.getDisableLocalTransactions()).isFalse());
}
@Test
public void disableLocalsOn() {
internalTestSuccess(
config -> assertThat(config.getDisableLocalTransactions()).isTrue(),
"--tx-pool-disable-locals=true");
}
@Test
public void disableLocalsOff() {
internalTestSuccess(
config -> assertThat(config.getDisableLocalTransactions()).isFalse(),
"--tx-pool-disable-locals=false");
}
@Test
public void saveToFileDisabledByDefault() {
internalTestSuccess(config -> assertThat(config.getEnableSaveRestore()).isFalse());
}
@Test
public void saveToFileEnabledDefaultPath() {
internalTestSuccess(
config -> assertThat(config.getEnableSaveRestore()).isTrue(),
"--tx-pool-enable-save-restore=true");
}
@Test
public void saveToFileEnabledCustomPath() {
internalTestSuccess(
config -> {
assertThat(config.getEnableSaveRestore()).isTrue();
assertThat(config.getSaveFile()).hasName("my.save.file");
},
"--tx-pool-enable-save-restore=true",
"--tx-pool-save-file=my.save.file");
}
@Test
public void senderLimited_derived() {
internalTestSuccess(
config -> assertThat(config.getTxPoolMaxFutureTransactionByAccount()).isEqualTo(9),
"--tx-pool-limit-by-account-percentage=0.002",
"--tx-pool=legacy");
}
@Test
public void senderLimitedFloor_derived() {
internalTestSuccess(
config -> assertThat(config.getTxPoolMaxFutureTransactionByAccount()).isEqualTo(1),
"--tx-pool-limit-by-account-percentage=0.0001",
"--tx-pool=legacy");
}
@Test
public void senderLimitedCeiling_violated() {
internalTestFailure(
"Invalid value for option '--tx-pool-limit-by-account-percentage'",
"--tx-pool-limit-by-account-percentage=1.00002341",
"--tx-pool=legacy");
}
@Test
public void priceBump() {
final Percentage priceBump = Percentage.fromInt(13);
internalTestSuccess(
config -> assertThat(config.getPriceBump()).isEqualTo(priceBump),
"--tx-pool-price-bump",
priceBump.toString());
}
@Test
public void invalidPriceBumpShouldFail() {
internalTestFailure(
"Invalid value: 101, should be a number between 0 and 100 inclusive",
"--tx-pool-price-bump",
"101");
}
@Test
public void txFeeCap() {
final Wei txFeeCap = Wei.fromEth(2);
internalTestSuccess(
config -> assertThat(config.getTxFeeCap()).isEqualTo(txFeeCap),
"--rpc-tx-feecap",
OptionParser.format(txFeeCap));
}
@Test
public void invalidTxFeeCapShouldFail() {
internalTestFailure(
"Invalid value for option '--rpc-tx-feecap'",
"cannot convert 'abcd' to Wei",
"--rpc-tx-feecap",
"abcd");
}
@Test
public void selectLayeredImplementationByDefault() {
internalTestSuccess(config -> assertThat(config.getTxPoolImplementation()).isEqualTo(LAYERED));
}
@Test
public void selectLayeredImplementationByArg() {
internalTestSuccess(
config -> assertThat(config.getTxPoolImplementation()).isEqualTo(LAYERED),
"--tx-pool=layered");
}
@Test
public void selectLegacyImplementationByArg() {
internalTestSuccess(
config -> assertThat(config.getTxPoolImplementation()).isEqualTo(LEGACY),
"--tx-pool=legacy");
}
@Test
public void failIfLegacyOptionsWhenLayeredSelectedByDefault() {
internalTestFailure(
"Could not use legacy transaction pool options with layered implementation",
"--tx-pool-max-size=1000");
}
@Test
public void failIfLegacyOptionsWhenLayeredSelectedByArg() {
internalTestFailure(
"Could not use legacy transaction pool options with layered implementation",
"--tx-pool=layered",
"--tx-pool-max-size=1000");
}
@Test
public void failIfLayeredOptionsWhenLegacySelectedByArg() {
internalTestFailure(
"Could not use layered transaction pool options with legacy implementation",
"--tx-pool=legacy",
"--tx-pool-max-prioritized=1000");
}
@Override
protected TransactionPoolConfiguration createDefaultDomainObject() {
return TransactionPoolConfiguration.DEFAULT;
}
@Override
protected TransactionPoolConfiguration createCustomizedDomainObject() {
return ImmutableTransactionPoolConfiguration.builder()
.strictTransactionReplayProtectionEnabled(true)
.txPoolImplementation(LAYERED)
.pendingTransactionsLayerMaxCapacityBytes(1_000_000L)
.maxPrioritizedTransactions(1000)
.maxFutureBySender(10)
.build();
}
@Override
protected TransactionPoolOptions optionsFromDomainObject(
final TransactionPoolConfiguration domainObject) {
return TransactionPoolOptions.fromConfig(domainObject);
}
@Override
protected TransactionPoolOptions getOptionsFromBesuCommand(final TestBesuCommand besuCommand) {
return besuCommand.getStableTransactionPoolOptions();
}
}

@ -0,0 +1,105 @@
/*
* 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();
}
}

@ -15,7 +15,6 @@
package org.hyperledger.besu.controller;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@ -55,6 +54,7 @@ import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage;
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.ObservableMetricsSystem;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
import java.math.BigInteger;
@ -85,16 +85,17 @@ public class BesuControllerBuilderTest {
@Mock SynchronizerConfiguration synchronizerConfiguration;
@Mock EthProtocolConfiguration ethProtocolConfiguration;
@Mock MiningParameters miningParameters;
@Mock ObservableMetricsSystem observableMetricsSystem;
@Mock PrivacyParameters privacyParameters;
@Mock Clock clock;
@Mock TransactionPoolConfiguration poolConfiguration;
@Mock StorageProvider storageProvider;
@Mock GasLimitCalculator gasLimitCalculator;
@Mock WorldStateStorage worldStateStorage;
@Mock WorldStateArchive worldStateArchive;
@Mock BonsaiWorldStateKeyValueStorage bonsaiWorldStateStorage;
@Mock WorldStatePreimageStorage worldStatePreimageStorage;
private final TransactionPoolConfiguration poolConfiguration =
TransactionPoolConfiguration.DEFAULT;
private final ObservableMetricsSystem observableMetricsSystem = new NoOpMetricsSystem();
BigInteger networkId = BigInteger.ONE;
@ -127,10 +128,6 @@ public class BesuControllerBuilderTest {
when(synchronizerConfiguration.getBlockPropagationRange()).thenReturn(Range.closed(1L, 2L));
when(observableMetricsSystem.createLabelledCounter(
any(), anyString(), anyString(), anyString()))
.thenReturn(labels -> null);
when(storageProvider.createWorldStateStorage(DataStorageFormat.FOREST))
.thenReturn(worldStateStorage);
when(storageProvider.createWorldStatePreimageStorage()).thenReturn(worldStatePreimageStorage);

@ -18,7 +18,6 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider.createInMemoryBlockchain;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@ -58,6 +57,7 @@ import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage;
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.ObservableMetricsSystem;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
import java.math.BigInteger;
@ -90,10 +90,8 @@ public class MergeBesuControllerBuilderTest {
@Mock EthProtocolConfiguration ethProtocolConfiguration;
@Mock CheckpointConfigOptions checkpointConfigOptions;
@Mock MiningParameters miningParameters;
@Mock ObservableMetricsSystem observableMetricsSystem;
@Mock PrivacyParameters privacyParameters;
@Mock Clock clock;
@Mock TransactionPoolConfiguration poolConfiguration;
@Mock StorageProvider storageProvider;
@Mock GasLimitCalculator gasLimitCalculator;
@Mock WorldStateStorage worldStateStorage;
@ -102,6 +100,9 @@ public class MergeBesuControllerBuilderTest {
BigInteger networkId = BigInteger.ONE;
private final BlockHeaderTestFixture headerGenerator = new BlockHeaderTestFixture();
private final BaseFeeMarket feeMarket = new LondonFeeMarket(0, Optional.of(Wei.of(42)));
private final TransactionPoolConfiguration poolConfiguration =
TransactionPoolConfiguration.DEFAULT;
private final ObservableMetricsSystem observableMetricsSystem = new NoOpMetricsSystem();
@Rule public final TemporaryFolder tempDirRule = new TemporaryFolder();
@ -134,10 +135,6 @@ public class MergeBesuControllerBuilderTest {
when(synchronizerConfiguration.getBlockPropagationRange()).thenReturn(Range.closed(1L, 2L));
when(observableMetricsSystem.createLabelledCounter(
any(), anyString(), anyString(), anyString()))
.thenReturn(labels -> null);
when(storageProvider.createWorldStateStorage(DataStorageFormat.FOREST))
.thenReturn(worldStateStorage);
when(storageProvider.createWorldStatePreimageStorage()).thenReturn(worldStatePreimageStorage);

@ -17,7 +17,6 @@ package org.hyperledger.besu.controller;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -53,6 +52,7 @@ import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage;
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.ObservableMetricsSystem;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
import java.math.BigInteger;
@ -80,16 +80,17 @@ public class QbftBesuControllerBuilderTest {
@Mock private EthProtocolConfiguration ethProtocolConfiguration;
@Mock CheckpointConfigOptions checkpointConfigOptions;
@Mock private MiningParameters miningParameters;
@Mock private ObservableMetricsSystem observableMetricsSystem;
@Mock private PrivacyParameters privacyParameters;
@Mock private Clock clock;
@Mock private TransactionPoolConfiguration poolConfiguration;
@Mock private StorageProvider storageProvider;
@Mock private GasLimitCalculator gasLimitCalculator;
@Mock private WorldStateStorage worldStateStorage;
@Mock private WorldStatePreimageStorage worldStatePreimageStorage;
private static final BigInteger networkId = BigInteger.ONE;
private static final NodeKey nodeKey = NodeKeyUtils.generate();
private final TransactionPoolConfiguration poolConfiguration =
TransactionPoolConfiguration.DEFAULT;
private final ObservableMetricsSystem observableMetricsSystem = new NoOpMetricsSystem();
@Rule public final TemporaryFolder tempDirRule = new TemporaryFolder();
@ -120,9 +121,7 @@ public class QbftBesuControllerBuilderTest {
when(synchronizerConfiguration.getDownloaderParallelism()).thenReturn(1);
when(synchronizerConfiguration.getTransactionsParallelism()).thenReturn(1);
when(synchronizerConfiguration.getComputationParallelism()).thenReturn(1);
when(observableMetricsSystem.createLabelledCounter(
any(), anyString(), anyString(), anyString()))
.thenReturn(labels -> null);
when(synchronizerConfiguration.getBlockPropagationRange()).thenReturn(Range.closed(1L, 2L));
// qbft prepForBuild setup

@ -170,16 +170,21 @@ privacy-onchain-groups-enabled=false
privacy-flexible-groups-enabled=false
# Transaction Pool
tx-pool-retention-hours=999
tx-pool="layered"
tx-pool-price-bump=13
tx-pool-max-size=1234
tx-pool-limit-by-account-percentage=0.017
Xincoming-tx-messages-keep-alive-seconds=60
rpc-tx-feecap=2000000000000000000
strict-tx-replay-protection-enabled=true
tx-pool-disable-locals=false
tx-pool-enable-save-restore=true
tx-pool-save-file="txpool.dump"
## Layered
tx-pool-layer-max-capacity=12345678
tx-pool-max-prioritized=9876
tx-pool-max-future-by-sender=321
## Legacy
tx-pool-retention-hours=999
tx-pool-max-size=1234
tx-pool-limit-by-account-percentage=0.017
# Revert Reason
revert-reason-enabled=false

@ -76,6 +76,7 @@ import org.hyperledger.besu.ethereum.trie.MerkleTrieException;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.metrics.StubMetricsSystem;
import org.hyperledger.besu.testutil.TestClock;
import org.hyperledger.besu.util.number.Fraction;
import java.time.ZoneId;
import java.util.ArrayList;
@ -161,7 +162,7 @@ public class MergeCoordinatorTest implements MergeGenesisConfigHelper {
private final TransactionPoolConfiguration poolConf =
ImmutableTransactionPoolConfiguration.builder()
.txPoolMaxSize(10)
.txPoolLimitByAccountPercentage(100.0f)
.txPoolLimitByAccountPercentage(Fraction.fromPercentage(100))
.build();
private final BaseFeePendingTransactionsSorter transactions =
new BaseFeePendingTransactionsSorter(

@ -34,6 +34,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.testutil.TestClock;
import org.hyperledger.besu.util.number.Fraction;
import java.time.ZoneId;
import java.util.function.Function;
@ -64,7 +65,7 @@ public class LegacyFeeMarketBlockTransactionSelectorTest
final TransactionPoolConfiguration poolConf =
ImmutableTransactionPoolConfiguration.builder()
.txPoolMaxSize(5)
.txPoolLimitByAccountPercentage(1)
.txPoolLimitByAccountPercentage(Fraction.fromFloat(1f))
.build();
final PendingTransactions pendingTransactions =

@ -39,6 +39,7 @@ import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
import org.hyperledger.besu.testutil.TestClock;
import org.hyperledger.besu.util.number.Fraction;
import java.time.ZoneId;
import java.util.List;
@ -72,7 +73,7 @@ public class LondonFeeMarketBlockTransactionSelectorTest
final TransactionPoolConfiguration poolConf =
ImmutableTransactionPoolConfiguration.builder()
.txPoolMaxSize(5)
.txPoolLimitByAccountPercentage(1)
.txPoolLimitByAccountPercentage(Fraction.fromFloat(1f))
.build();
final PendingTransactions pendingTransactions =
new BaseFeePendingTransactionsSorter(

@ -101,8 +101,12 @@ public class NewPooledTransactionHashesMessageProcessor {
.getScheduler()
.scheduleFutureTaskWithFixedDelay(
new FetcherCreatorTask(peer),
transactionPoolConfiguration.getEth65TrxAnnouncedBufferingPeriod(),
transactionPoolConfiguration.getEth65TrxAnnouncedBufferingPeriod());
transactionPoolConfiguration
.getUnstable()
.getEth65TrxAnnouncedBufferingPeriod(),
transactionPoolConfiguration
.getUnstable()
.getEth65TrxAnnouncedBufferingPeriod());
return new BufferedGetPooledTransactionsFromPeerFetcher(
ethContext,

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.eth.transactions;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.util.number.Fraction;
import org.hyperledger.besu.util.number.Percentage;
import java.io.File;
@ -24,16 +25,41 @@ import org.immutables.value.Value;
@Value.Immutable
@Value.Style(allParameters = true)
@Value.Enclosing
public interface TransactionPoolConfiguration {
@Value.Immutable
interface Unstable {
Duration ETH65_TRX_ANNOUNCED_BUFFERING_PERIOD = Duration.ofMillis(500);
int DEFAULT_TX_MSG_KEEP_ALIVE = 60;
TransactionPoolConfiguration.Unstable DEFAULT =
ImmutableTransactionPoolConfiguration.Unstable.builder().build();
@Value.Default
default Duration getEth65TrxAnnouncedBufferingPeriod() {
return ETH65_TRX_ANNOUNCED_BUFFERING_PERIOD;
}
@Value.Default
default int getTxMessageKeepAliveSeconds() {
return DEFAULT_TX_MSG_KEEP_ALIVE;
}
}
enum Implementation {
LEGACY,
LAYERED;
}
String DEFAULT_SAVE_FILE_NAME = "txpool.dump";
int DEFAULT_TX_MSG_KEEP_ALIVE = 60;
int DEFAULT_MAX_PENDING_TRANSACTIONS = 4096;
float DEFAULT_LIMIT_TX_POOL_BY_ACCOUNT_PERCENTAGE = 0.001f; // 0.1%
Fraction DEFAULT_LIMIT_TX_POOL_BY_ACCOUNT_PERCENTAGE = Fraction.fromFloat(0.001f); // 0.1%
int DEFAULT_TX_RETENTION_HOURS = 13;
boolean DEFAULT_STRICT_TX_REPLAY_PROTECTION_ENABLED = false;
Percentage DEFAULT_PRICE_BUMP = Percentage.fromInt(10);
Wei DEFAULT_RPC_TX_FEE_CAP = Wei.fromEth(1);
Duration ETH65_TRX_ANNOUNCED_BUFFERING_PERIOD = Duration.ofMillis(500);
boolean DEFAULT_DISABLE_LOCAL_TXS = false;
boolean DEFAULT_ENABLE_SAVE_RESTORE = false;
@ -41,7 +67,7 @@ public interface TransactionPoolConfiguration {
long DEFAULT_PENDING_TRANSACTIONS_LAYER_MAX_CAPACITY_BYTES = 50_000_000L;
int DEFAULT_MAX_PRIORITIZED_TRANSACTIONS = 2000;
int DEFAULT_MAX_FUTURE_BY_SENDER = 200;
boolean DEFAULT_LAYERED_TX_POOL_ENABLED = false;
Implementation DEFAULT_TX_POOL_IMPLEMENTATION = Implementation.LAYERED;
TransactionPoolConfiguration DEFAULT = ImmutableTransactionPoolConfiguration.builder().build();
@ -51,13 +77,13 @@ public interface TransactionPoolConfiguration {
}
@Value.Default
default float getTxPoolLimitByAccountPercentage() {
default Fraction getTxPoolLimitByAccountPercentage() {
return DEFAULT_LIMIT_TX_POOL_BY_ACCOUNT_PERCENTAGE;
}
@Value.Derived
default int getTxPoolMaxFutureTransactionByAccount() {
return (int) Math.ceil(getTxPoolLimitByAccountPercentage() * getTxPoolMaxSize());
return (int) Math.ceil(getTxPoolLimitByAccountPercentage().getValue() * getTxPoolMaxSize());
}
@Value.Default
@ -65,21 +91,11 @@ public interface TransactionPoolConfiguration {
return DEFAULT_TX_RETENTION_HOURS;
}
@Value.Default
default int getTxMessageKeepAliveSeconds() {
return DEFAULT_TX_MSG_KEEP_ALIVE;
}
@Value.Default
default Percentage getPriceBump() {
return DEFAULT_PRICE_BUMP;
}
@Value.Default
default Duration getEth65TrxAnnouncedBufferingPeriod() {
return ETH65_TRX_ANNOUNCED_BUFFERING_PERIOD;
}
@Value.Default
default Wei getTxFeeCap() {
return DEFAULT_RPC_TX_FEE_CAP;
@ -106,8 +122,8 @@ public interface TransactionPoolConfiguration {
}
@Value.Default
default Boolean getLayeredTxPoolEnabled() {
return DEFAULT_LAYERED_TX_POOL_ENABLED;
default Implementation getTxPoolImplementation() {
return DEFAULT_TX_POOL_IMPLEMENTATION;
}
@Value.Default
@ -124,4 +140,9 @@ public interface TransactionPoolConfiguration {
default int getMaxFutureBySender() {
return DEFAULT_MAX_FUTURE_BY_SENDER;
}
@Value.Default
default Unstable getUnstable() {
return Unstable.DEFAULT;
}
}

@ -14,6 +14,8 @@
*/
package org.hyperledger.besu.ethereum.eth.transactions;
import static org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration.Implementation.LAYERED;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
@ -31,7 +33,7 @@ import org.hyperledger.besu.ethereum.eth.transactions.sorter.AbstractPendingTran
import org.hyperledger.besu.ethereum.eth.transactions.sorter.BaseFeePendingTransactionsSorter;
import org.hyperledger.besu.ethereum.eth.transactions.sorter.GasPricePendingTransactionsSorter;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.plugin.services.BesuEvents;
import org.hyperledger.besu.plugin.services.MetricsSystem;
@ -115,7 +117,7 @@ public class TransactionPoolFactory {
new TransactionsMessageHandler(
ethContext.getScheduler(),
new TransactionsMessageProcessor(transactionTracker, transactionPool, metrics),
transactionPoolConfiguration.getTxMessageKeepAliveSeconds());
transactionPoolConfiguration.getUnstable().getTxMessageKeepAliveSeconds());
final NewPooledTransactionHashesMessageHandler pooledTransactionsMessageHandler =
new NewPooledTransactionHashesMessageHandler(
@ -126,7 +128,7 @@ public class TransactionPoolFactory {
transactionPoolConfiguration,
ethContext,
metrics),
transactionPoolConfiguration.getTxMessageKeepAliveSeconds());
transactionPoolConfiguration.getUnstable().getTxMessageKeepAliveSeconds());
subscribeTransactionHandlers(
protocolContext,
@ -194,8 +196,7 @@ public class TransactionPoolFactory {
protocolSchedule.anyMatch(
scheduledSpec -> scheduledSpec.spec().getFeeMarket().implementsBaseFee());
if (transactionPoolConfiguration.getLayeredTxPoolEnabled()) {
LOG.info("Using layered transaction pool");
if (transactionPoolConfiguration.getTxPoolImplementation().equals(LAYERED)) {
return createLayeredPendingTransactions(
protocolSchedule,
protocolContext,
@ -263,11 +264,10 @@ public class TransactionPoolFactory {
final AbstractPrioritizedTransactions pendingTransactionsSorter;
if (isFeeMarketImplementBaseFee) {
final BaseFeeMarket baseFeeMarket =
(BaseFeeMarket)
protocolSchedule
.getByBlockHeader(protocolContext.getBlockchain().getChainHeadHeader())
.getFeeMarket();
final FeeMarket feeMarket =
protocolSchedule
.getByBlockHeader(protocolContext.getBlockchain().getChainHeadHeader())
.getFeeMarket();
pendingTransactionsSorter =
new BaseFeePrioritizedTransactions(
@ -276,7 +276,7 @@ public class TransactionPoolFactory {
readyTransactions,
metrics,
transactionReplacementTester,
baseFeeMarket);
feeMarket);
} else {
pendingTransactionsSorter =
new GasPricePrioritizedTransactions(

@ -330,6 +330,7 @@ public abstract class AbstractTransactionsLayer implements TransactionsLayer {
decreaseSpaceUsed(replacedTx);
metrics.incrementRemoved(replacedTx.isReceivedFromLocalSource(), REPLACED.label(), name());
internalReplaced(replacedTx);
notifyTransactionDropped(replacedTx);
}
protected abstract void internalReplaced(final PendingTransaction replacedTx);

@ -50,10 +50,10 @@ public class BaseFeePrioritizedTransactions extends AbstractPrioritizedTransacti
final TransactionPoolMetrics metrics,
final BiFunction<PendingTransaction, PendingTransaction, Boolean>
transactionReplacementTester,
final BaseFeeMarket baseFeeMarket) {
final FeeMarket feeMarket) {
super(poolConfig, nextLayer, metrics, transactionReplacementTester);
this.nextBlockBaseFee =
Optional.of(calculateNextBlockBaseFee(baseFeeMarket, chainHeadHeaderSupplier.get()));
Optional.of(calculateNextBlockBaseFee(feeMarket, chainHeadHeaderSupplier.get()));
}
@Override
@ -71,8 +71,7 @@ public class BaseFeePrioritizedTransactions extends AbstractPrioritizedTransacti
@Override
protected void internalBlockAdded(final BlockHeader blockHeader, final FeeMarket feeMarket) {
final BaseFeeMarket baseFeeMarket = (BaseFeeMarket) feeMarket;
final Wei newNextBlockBaseFee = calculateNextBlockBaseFee(baseFeeMarket, blockHeader);
final Wei newNextBlockBaseFee = calculateNextBlockBaseFee(feeMarket, blockHeader);
LOG.atTrace()
.setMessage("Updating base fee from {} to {}")
@ -85,13 +84,16 @@ public class BaseFeePrioritizedTransactions extends AbstractPrioritizedTransacti
orderByFee.addAll(pendingTransactions.values());
}
private Wei calculateNextBlockBaseFee(
final BaseFeeMarket baseFeeMarket, final BlockHeader blockHeader) {
return baseFeeMarket.computeBaseFee(
blockHeader.getNumber() + 1,
blockHeader.getBaseFee().orElse(Wei.ZERO),
blockHeader.getGasUsed(),
baseFeeMarket.targetGasUsed(blockHeader));
private Wei calculateNextBlockBaseFee(final FeeMarket feeMarket, final BlockHeader blockHeader) {
if (feeMarket.implementsBaseFee()) {
final var baseFeeMarket = (BaseFeeMarket) feeMarket;
return baseFeeMarket.computeBaseFee(
blockHeader.getNumber() + 1,
blockHeader.getBaseFee().orElse(Wei.ZERO),
blockHeader.getGasUsed(),
baseFeeMarket.targetGasUsed(blockHeader));
}
return Wei.ZERO;
}
@Override

@ -21,6 +21,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.eth.encoding.TransactionAnnouncementDecoder.getDecoder;
import static org.hyperledger.besu.ethereum.eth.encoding.TransactionAnnouncementEncoder.getEncoder;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Answers.RETURNS_DEEP_STUBS;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@ -66,7 +67,10 @@ import org.mockito.quality.Strictness;
public class NewPooledTransactionHashesMessageProcessorTest {
@Mock private TransactionPool transactionPool;
@Mock private TransactionPoolConfiguration transactionPoolConfiguration;
@Mock(answer = RETURNS_DEEP_STUBS)
private TransactionPoolConfiguration transactionPoolConfiguration;
@Mock private PeerTransactionTracker transactionTracker;
@Mock private EthPeer peer1;
@Mock private EthContext ethContext;
@ -88,7 +92,7 @@ public class NewPooledTransactionHashesMessageProcessorTest {
@BeforeEach
public void setup() {
metricsSystem = new StubMetricsSystem();
when(transactionPoolConfiguration.getEth65TrxAnnouncedBufferingPeriod())
when(transactionPoolConfiguration.getUnstable().getEth65TrxAnnouncedBufferingPeriod())
.thenReturn(Duration.ofMillis(500));
messageHandler =
new NewPooledTransactionHashesMessageProcessor(

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.eth.transactions;
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.mainnet.MainnetProtocolSchedule.DEFAULT_CHAIN_ID;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
@ -41,6 +42,7 @@ import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
import org.hyperledger.besu.ethereum.eth.transactions.layered.LayeredPendingTransactions;
import org.hyperledger.besu.ethereum.eth.transactions.sorter.BaseFeePendingTransactionsSorter;
import org.hyperledger.besu.ethereum.eth.transactions.sorter.GasPricePendingTransactionsSorter;
import org.hyperledger.besu.ethereum.forkid.ForkIdManager;
@ -242,8 +244,11 @@ public class TransactionPoolFactoryTest {
new MiningParameters.Builder().minTransactionGasPrice(Wei.ONE).build(),
ImmutableTransactionPoolConfiguration.builder()
.txPoolMaxSize(1)
.txMessageKeepAliveSeconds(1)
.pendingTxRetentionPeriod(1)
.unstable(
ImmutableTransactionPoolConfiguration.Unstable.builder()
.txMessageKeepAliveSeconds(1)
.build())
.build(),
peerTransactionTracker,
transactionsMessageSender,
@ -267,10 +272,12 @@ public class TransactionPoolFactoryTest {
}
@Test
public void createTransactionPool_shouldUseBaseFeePendingTransactionsSorter_whenLondonEnabled() {
public void
createLegacyTransactionPool_shouldUseBaseFeePendingTransactionsSorter_whenLondonEnabled() {
setupScheduleWith(new StubGenesisConfigOptions().londonBlock(0));
final TransactionPool pool = createTransactionPool();
final TransactionPool pool =
createTransactionPool(TransactionPoolConfiguration.Implementation.LEGACY);
assertThat(pool.pendingTransactionsImplementation())
.isEqualTo(BaseFeePendingTransactionsSorter.class);
@ -278,15 +285,42 @@ public class TransactionPoolFactoryTest {
@Test
public void
createTransactionPool_shouldUseGasPricePendingTransactionsSorter_whenLondonNotEnabled() {
createLegacyTransactionPool_shouldUseGasPricePendingTransactionsSorter_whenLondonNotEnabled() {
setupScheduleWith(new StubGenesisConfigOptions().berlinBlock(0));
final TransactionPool pool = createTransactionPool();
final TransactionPool pool =
createTransactionPool(TransactionPoolConfiguration.Implementation.LEGACY);
assertThat(pool.pendingTransactionsImplementation())
.isEqualTo(GasPricePendingTransactionsSorter.class);
}
@Test
public void
createLayeredTransactionPool_shouldUseBaseFeePendingTransactionsSorter_whenLondonEnabled() {
setupScheduleWith(new StubGenesisConfigOptions().londonBlock(0));
final TransactionPool pool = createTransactionPool(LAYERED);
assertThat(pool.pendingTransactionsImplementation())
.isEqualTo(LayeredPendingTransactions.class);
assertThat(pool.logStats()).startsWith("Basefee Prioritized");
}
@Test
public void
createLayeredTransactionPool_shouldUseGasPricePendingTransactionsSorter_whenLondonNotEnabled() {
setupScheduleWith(new StubGenesisConfigOptions().berlinBlock(0));
final TransactionPool pool = createTransactionPool(LAYERED);
assertThat(pool.pendingTransactionsImplementation())
.isEqualTo(LayeredPendingTransactions.class);
assertThat(pool.logStats()).startsWith("GasPrice Prioritized");
}
private void setupScheduleWith(final StubGenesisConfigOptions config) {
schedule =
new ProtocolScheduleBuilder(
@ -305,7 +339,8 @@ public class TransactionPoolFactoryTest {
syncState = new SyncState(blockchain, ethPeers, true, Optional.empty());
}
private TransactionPool createTransactionPool() {
private TransactionPool createTransactionPool(
final TransactionPoolConfiguration.Implementation implementation) {
final TransactionPool txPool =
TransactionPoolFactory.createTransactionPool(
schedule,
@ -316,9 +351,13 @@ public class TransactionPoolFactoryTest {
syncState,
new MiningParameters.Builder().minTransactionGasPrice(Wei.ONE).build(),
ImmutableTransactionPoolConfiguration.builder()
.txPoolImplementation(implementation)
.txPoolMaxSize(1)
.txMessageKeepAliveSeconds(1)
.pendingTxRetentionPeriod(1)
.unstable(
ImmutableTransactionPoolConfiguration.Unstable.builder()
.txMessageKeepAliveSeconds(1)
.build())
.build());
txPool.setEnabled();

@ -35,6 +35,7 @@ import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.eth.transactions.sorter.GasPricePendingTransactionsSorter;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.testutil.TestClock;
import org.hyperledger.besu.util.number.Fraction;
import java.math.BigInteger;
import java.time.ZoneId;
@ -57,7 +58,7 @@ public class TransactionPoolLegacyTest extends AbstractTransactionPoolTest {
return new GasPricePendingTransactionsSorter(
ImmutableTransactionPoolConfiguration.builder()
.txPoolMaxSize(MAX_TRANSACTIONS)
.txPoolLimitByAccountPercentage(1)
.txPoolLimitByAccountPercentage(Fraction.fromFloat(1.0f))
.build(),
TestClock.system(ZoneId.systemDefault()),
metricsSystem,

@ -42,6 +42,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.testutil.TestClock;
import org.hyperledger.besu.util.number.Fraction;
import java.math.BigInteger;
import java.time.ZoneId;
@ -63,7 +64,7 @@ public class TransactionPoolLondonTest extends AbstractTransactionPoolTest {
return new BaseFeePendingTransactionsSorter(
ImmutableTransactionPoolConfiguration.builder()
.txPoolMaxSize(MAX_TRANSACTIONS)
.txPoolLimitByAccountPercentage(1)
.txPoolLimitByAccountPercentage(Fraction.fromFloat(1.0f))
.build(),
TestClock.system(ZoneId.systemDefault()),
metricsSystem,

@ -45,6 +45,7 @@ import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.metrics.StubMetricsSystem;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
import org.hyperledger.besu.testutil.TestClock;
import org.hyperledger.besu.util.number.Fraction;
import java.time.Clock;
import java.time.temporal.ChronoUnit;
@ -80,13 +81,14 @@ public abstract class AbstractPendingTransactionsTestBase {
getPendingTransactions(
ImmutableTransactionPoolConfiguration.builder()
.txPoolMaxSize(MAX_TRANSACTIONS)
.txPoolLimitByAccountPercentage(1.0f)
.txPoolLimitByAccountPercentage(Fraction.fromFloat(1.0f))
.build(),
Optional.empty());
private final TransactionPoolConfiguration senderLimitedConfig =
ImmutableTransactionPoolConfiguration.builder()
.txPoolMaxSize(MAX_TRANSACTIONS)
.txPoolLimitByAccountPercentage(LIMITED_TRANSACTIONS_BY_SENDER_PERCENTAGE)
.txPoolLimitByAccountPercentage(
Fraction.fromFloat(LIMITED_TRANSACTIONS_BY_SENDER_PERCENTAGE))
.build();
protected PendingTransactions senderLimitedTransactions =
getPendingTransactions(senderLimitedConfig, Optional.empty());
@ -622,7 +624,7 @@ public abstract class AbstractPendingTransactionsTestBase {
ImmutableTransactionPoolConfiguration.builder()
.pendingTxRetentionPeriod(maxTransactionRetentionHours)
.txPoolMaxSize(MAX_TRANSACTIONS)
.txPoolLimitByAccountPercentage(1)
.txPoolLimitByAccountPercentage(Fraction.fromFloat(1.0f))
.build(),
Optional.of(clock));
@ -644,7 +646,7 @@ public abstract class AbstractPendingTransactionsTestBase {
ImmutableTransactionPoolConfiguration.builder()
.pendingTxRetentionPeriod(1)
.txPoolMaxSize(MAX_TRANSACTIONS)
.txPoolLimitByAccountPercentage(1.0f)
.txPoolLimitByAccountPercentage(Fraction.fromFloat(1.0f))
.build(),
Optional.of(clock));
evictSingleTransactions.addRemoteTransaction(transaction1, Optional.empty());
@ -662,7 +664,7 @@ public abstract class AbstractPendingTransactionsTestBase {
ImmutableTransactionPoolConfiguration.builder()
.pendingTxRetentionPeriod(2)
.txPoolMaxSize(MAX_TRANSACTIONS)
.txPoolLimitByAccountPercentage(1.0f)
.txPoolLimitByAccountPercentage(Fraction.fromFloat(1.0f))
.build(),
Optional.of(clock));

@ -65,6 +65,7 @@ import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
import org.hyperledger.besu.util.Subscribers;
import org.hyperledger.besu.util.number.Fraction;
import java.util.Collections;
import java.util.Optional;
@ -227,7 +228,7 @@ public class RetestethContext {
final TransactionPoolConfiguration transactionPoolConfiguration =
ImmutableTransactionPoolConfiguration.builder()
.txPoolLimitByAccountPercentage(0.004f)
.txPoolLimitByAccountPercentage(Fraction.fromFloat(0.004f))
.build();
transactionPool =

Loading…
Cancel
Save