mirror of https://github.com/hyperledger/besu
Layered txpool by default and txpool options hoverhaul (#5772)
Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>pull/5871/head
parent
3597ccbf01
commit
25c2065434
@ -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()); |
||||
} |
||||
} |
@ -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)); |
||||
} |
||||
} |
@ -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()); |
||||
} |
||||
} |
@ -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(); |
||||
} |
||||
} |
Loading…
Reference in new issue