Transaction pool flag to disable specific behaviors for locally submitted transactions (#5418)

* Transaction pool flag to disable special handling of locally submitted transactions

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>

* Update CHANGELOG

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>

* Fix options and more tests

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>

* Update besu/src/main/java/org/hyperledger/besu/cli/options/unstable/TransactionPoolOptions.java

Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>
Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>

---------

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
Co-authored-by: Stefan Pingel <16143240+pinges@users.noreply.github.com>
Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>
pull/5439/head
Fabio Di Fabio 2 years ago committed by GitHub
parent 8b4819ee6f
commit 64c422b490
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 16
      besu/src/main/java/org/hyperledger/besu/cli/options/unstable/TransactionPoolOptions.java
  3. 42
      besu/src/test/java/org/hyperledger/besu/cli/options/TransactionPoolOptionsTest.java
  4. 16
      besu/src/test/java/org/hyperledger/besu/services/BesuEventsImplTest.java
  5. 1
      besu/src/test/resources/everything_config.toml
  6. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetchers.java
  7. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugBatchSendRawTransaction.java
  8. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthSendRawTransaction.java
  9. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/AbstractEeaSendRawTransaction.java
  10. 4
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/graphql/AbstractEthGraphQLHttpServiceTest.java
  11. 4
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/AbstractJsonRpcHttpServiceTest.java
  12. 4
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugBatchSendRawTransactionTest.java
  13. 8
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthSendRawTransactionTest.java
  14. 2
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/EeaSendRawTransactionTest.java
  15. 4
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/PluginEeaSendRawTransactionTest.java
  16. 4
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/RestrictedFlexibleEeaSendRawTransactionTest.java
  17. 6
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/RestrictedOffchainEeaSendRawTransactionTest.java
  18. 115
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java
  19. 6
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolConfiguration.java
  20. 6
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/AbstractPendingTransactionsSorter.java
  21. 126
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionPoolTest.java
  22. 2
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TestNode.java
  23. 71
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolLegacyTest.java
  24. 27
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolLondonTest.java

@ -7,6 +7,7 @@
### Additions and Improvements
- "Big-EOF" (the EOF version initially slotted for Shanghai) has been moved from Cancun to FutureEIPs [#5429](https://github.com/hyperledger/besu/pull/5429)
- EIP-4844: Zero blob transactions are invalid [#5425](https://github.com/hyperledger/besu/pull/5425)
- Transaction pool flag to disable specific behaviors for locally submitted transactions [#5418](https://github.com/hyperledger/besu/pull/5418)
### Bug Fixes
- Fix eth_feeHistory response for the case in which blockCount is higher than highestBlock requested. [#5397](https://github.com/hyperledger/besu/pull/5397)

@ -41,6 +41,8 @@ public class TransactionPoolOptions
private static final String TX_POOL_LIMIT_BY_ACCOUNT_PERCENTAGE =
"--tx-pool-limit-by-account-percentage";
private static final String DISABLE_LOCAL_TXS_FLAG = "--tx-pool-disable-locals";
@CommandLine.Option(
names = {STRICT_TX_REPLAY_PROTECTION_ENABLED_FLAG},
paramLabel = "<Boolean>",
@ -80,6 +82,15 @@ public class TransactionPoolOptions
private Float txPoolLimitByAccountPercentage =
TransactionPoolConfiguration.LIMIT_TXPOOL_BY_ACCOUNT_PERCENTAGE;
@CommandLine.Option(
names = {DISABLE_LOCAL_TXS_FLAG},
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 = "false",
arity = "0..1")
private Boolean disableLocalTxs = TransactionPoolConfiguration.DEFAULT_DISABLE_LOCAL_TXS;
private TransactionPoolOptions() {}
/**
@ -104,6 +115,7 @@ public class TransactionPoolOptions
config.getEth65TrxAnnouncedBufferingPeriod().toMillis();
options.strictTxReplayProtectionEnabled = config.getStrictTransactionReplayProtectionEnabled();
options.txPoolLimitByAccountPercentage = config.getTxPoolLimitByAccountPercentage();
options.disableLocalTxs = config.getDisableLocalTransactions();
return options;
}
@ -113,13 +125,15 @@ public class TransactionPoolOptions
.strictTransactionReplayProtectionEnabled(strictTxReplayProtectionEnabled)
.txMessageKeepAliveSeconds(txMessageKeepAliveSeconds)
.eth65TrxAnnouncedBufferingPeriod(Duration.ofMillis(eth65TrxAnnouncedBufferingPeriod))
.txPoolLimitByAccountPercentage(txPoolLimitByAccountPercentage);
.txPoolLimitByAccountPercentage(txPoolLimitByAccountPercentage)
.disableLocalTransactions(disableLocalTxs);
}
@Override
public List<String> getCLIOptions() {
return Arrays.asList(
STRICT_TX_REPLAY_PROTECTION_ENABLED_FLAG + "=" + strictTxReplayProtectionEnabled,
DISABLE_LOCAL_TXS_FLAG + "=" + disableLocalTxs,
TX_POOL_LIMIT_BY_ACCOUNT_PERCENTAGE,
OptionParser.format(txPoolLimitByAccountPercentage),
TX_MESSAGE_KEEP_ALIVE_SEC_FLAG,

@ -152,6 +152,42 @@ public class TransactionPoolOptionsTest
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void disableLocalsDefault() {
final TestBesuCommand cmd = parseCommand();
final TransactionPoolOptions options = getOptionsFromBesuCommand(cmd);
final TransactionPoolConfiguration config = options.toDomainObject().build();
assertThat(config.getDisableLocalTransactions()).isFalse();
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void disableLocalsOn() {
final TestBesuCommand cmd = parseCommand("--tx-pool-disable-locals=true");
final TransactionPoolOptions options = getOptionsFromBesuCommand(cmd);
final TransactionPoolConfiguration config = options.toDomainObject().build();
assertThat(config.getDisableLocalTransactions()).isTrue();
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void disableLocalsOff() {
final TestBesuCommand cmd = parseCommand("--tx-pool-disable-locals=false");
final TransactionPoolOptions options = getOptionsFromBesuCommand(cmd);
final TransactionPoolConfiguration config = options.toDomainObject().build();
assertThat(config.getDisableLocalTransactions()).isFalse();
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Override
ImmutableTransactionPoolConfiguration.Builder createDefaultDomainObject() {
final ImmutableTransactionPoolConfiguration defaultValue =
@ -160,7 +196,8 @@ public class TransactionPoolOptionsTest
.strictTransactionReplayProtectionEnabled(false)
.txMessageKeepAliveSeconds(defaultValue.getTxMessageKeepAliveSeconds())
.eth65TrxAnnouncedBufferingPeriod(defaultValue.getEth65TrxAnnouncedBufferingPeriod())
.txPoolLimitByAccountPercentage(defaultValue.getTxPoolLimitByAccountPercentage());
.txPoolLimitByAccountPercentage(defaultValue.getTxPoolLimitByAccountPercentage())
.disableLocalTransactions(defaultValue.getDisableLocalTransactions());
}
@Override
@ -171,7 +208,8 @@ public class TransactionPoolOptionsTest
.eth65TrxAnnouncedBufferingPeriod(
TransactionPoolConfiguration.ETH65_TRX_ANNOUNCED_BUFFERING_PERIOD.plus(
Duration.ofMillis(100)))
.txPoolLimitByAccountPercentage(0.5f);
.txPoolLimitByAccountPercentage(0.5f)
.disableLocalTransactions(true);
}
@Override

@ -384,7 +384,7 @@ public class BesuEventsImplTest {
serviceImpl.addTransactionAddedListener(result::set);
assertThat(result.get()).isNull();
transactionPool.addLocalTransaction(TX1);
transactionPool.addTransactionViaApi(TX1);
assertThat(result.get()).isNotNull();
}
@ -395,13 +395,13 @@ public class BesuEventsImplTest {
final long id = serviceImpl.addTransactionAddedListener(result::set);
assertThat(result.get()).isNull();
transactionPool.addLocalTransaction(TX1);
transactionPool.addTransactionViaApi(TX1);
assertThat(result.get()).isNotNull();
serviceImpl.removeTransactionAddedListener(id);
result.set(null);
transactionPool.addLocalTransaction(TX2);
transactionPool.addTransactionViaApi(TX2);
assertThat(result.get()).isNull();
}
@ -418,8 +418,8 @@ public class BesuEventsImplTest {
assertThat(result.get()).isNull();
// sending a replacement with higher gas should drop the previous one
transactionPool.addLocalTransaction(TX1);
transactionPool.addLocalTransaction(
transactionPool.addTransactionViaApi(TX1);
transactionPool.addTransactionViaApi(
bumpTransactionGasPrice(TX1, TX1.getGasPrice().get().multiply(2)));
assertThat(result.get()).isNotNull();
@ -431,9 +431,9 @@ public class BesuEventsImplTest {
final long id = serviceImpl.addTransactionDroppedListener(result::set);
assertThat(result.get()).isNull();
transactionPool.addLocalTransaction(TX1);
transactionPool.addTransactionViaApi(TX1);
// first replacement with higher gas should drop the previous one
transactionPool.addLocalTransaction(
transactionPool.addTransactionViaApi(
bumpTransactionGasPrice(TX1, TX1.getGasPrice().get().multiply(2)));
assertThat(result.get()).isNotNull();
@ -441,7 +441,7 @@ public class BesuEventsImplTest {
result.set(null);
// second replacement with higher gas should drop the previous one
transactionPool.addLocalTransaction(
transactionPool.addTransactionViaApi(
bumpTransactionGasPrice(TX1, TX1.getGasPrice().get().multiply(4)));
assertThat(result.get()).isNull();
}

@ -175,6 +175,7 @@ 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
# Revert Reason
revert-reason-enabled=false

@ -88,7 +88,7 @@ public class GraphQLDataFetchers {
final Transaction transaction = Transaction.readFrom(RLP.input(rawTran));
final ValidationResult<TransactionInvalidReason> validationResult =
transactionPool.addLocalTransaction(transaction);
transactionPool.addTransactionViaApi(transaction);
if (validationResult.isValid()) {
return Optional.of(transaction.getHash());
} else {

@ -68,7 +68,7 @@ public class DebugBatchSendRawTransaction implements JsonRpcMethod {
final ValidationResult<TransactionInvalidReason> validationResult =
transactionPool
.get()
.addLocalTransaction(DomainObjectDecodeUtils.decodeRawTransaction(rawTransaction));
.addTransactionViaApi(DomainObjectDecodeUtils.decodeRawTransaction(rawTransaction));
return validationResult.either(
() -> new ExecutionStatus(index),
errorReason -> new ExecutionStatus(index, false, errorReason.name()));

@ -85,7 +85,7 @@ public class EthSendRawTransaction implements JsonRpcMethod {
}
final ValidationResult<TransactionInvalidReason> validationResult =
transactionPool.get().addLocalTransaction(transaction);
transactionPool.get().addTransactionViaApi(transaction);
return validationResult.either(
() ->
new JsonRpcSuccessResponse(

@ -95,7 +95,7 @@ public abstract class AbstractEeaSendRawTransaction implements JsonRpcMethod {
createPrivateMarkerTransaction(sender, privateTransaction, user);
return transactionPool
.addLocalTransaction(privateMarkerTransaction)
.addTransactionViaApi(privateMarkerTransaction)
.either(
() -> new JsonRpcSuccessResponse(id, privateMarkerTransaction.getHash().toString()),
errorReason -> getJsonRpcErrorResponse(id, errorReason));

@ -133,11 +133,11 @@ public abstract class AbstractEthGraphQLHttpServiceTest {
final TransactionPool transactionPoolMock = Mockito.mock(TransactionPool.class);
Mockito.when(transactionPoolMock.addLocalTransaction(ArgumentMatchers.any(Transaction.class)))
Mockito.when(transactionPoolMock.addTransactionViaApi(ArgumentMatchers.any(Transaction.class)))
.thenReturn(ValidationResult.valid());
// nonce too low tests uses a tx with nonce=16
Mockito.when(
transactionPoolMock.addLocalTransaction(
transactionPoolMock.addTransactionViaApi(
ArgumentMatchers.argThat(tx -> tx.getNonce() == 16)))
.thenReturn(ValidationResult.invalid(TransactionInvalidReason.NONCE_TOO_LOW));
final GasPricePendingTransactionsSorter pendingTransactionsMock =

@ -135,10 +135,10 @@ public abstract class AbstractJsonRpcHttpServiceTest {
final P2PNetwork peerDiscoveryMock = mock(P2PNetwork.class);
final TransactionPool transactionPoolMock = mock(TransactionPool.class);
final PoWMiningCoordinator miningCoordinatorMock = mock(PoWMiningCoordinator.class);
when(transactionPoolMock.addLocalTransaction(any(Transaction.class)))
when(transactionPoolMock.addTransactionViaApi(any(Transaction.class)))
.thenReturn(ValidationResult.valid());
// nonce too low tests uses a tx with nonce=16
when(transactionPoolMock.addLocalTransaction(argThat(tx -> tx.getNonce() == 16)))
when(transactionPoolMock.addTransactionViaApi(argThat(tx -> tx.getNonce() == 16)))
.thenReturn(ValidationResult.invalid(TransactionInvalidReason.NONCE_TOO_LOW));
final GasPricePendingTransactionsSorter pendingTransactionsMock =
mock(GasPricePendingTransactionsSorter.class);

@ -59,7 +59,7 @@ public class DebugBatchSendRawTransactionTest {
new Object[] {
"0xf868808203e882520894627306090abab3a6e1400e9345bc60c78a8bef57872386f26fc10000801ba0ac74ecfa0e9b85785f042c143ead4780931234cc9a032fce99fab1f45e0d90faa02fd17e8eb433d4ca47727653232045d4f81322619c0852d3fe8ddcfcedb66a43"
}));
when(transactionPool.addLocalTransaction(any(Transaction.class)))
when(transactionPool.addTransactionViaApi(any(Transaction.class)))
.thenReturn(ValidationResult.valid());
final JsonRpcSuccessResponse response = (JsonRpcSuccessResponse) method.response(request);
assertThat(response).isNotNull();
@ -78,7 +78,7 @@ public class DebugBatchSendRawTransactionTest {
"0xf868808203e882520894627306090abab3a6e1400e9345bc60c78a8bef57872386f26fc10000801ba0ac74ecfa0e9b85785f042c143ead4780931234cc9a032fce99fab1f45e0d90faa02fd17e8eb433d4ca47727653232045d4f81322619c0852d3fe8ddcfcedb66a43",
"0xf868018203e882520894627306090abab3a6e1400e9345bc60c78a8bef57876a94d74f430000801ba092faeec7bcb7418a79cd9f74c739237d72d52b5ab25aa08e332053304456e129a0386e3e9205a3553ecc5e85fc753c93196484c0fdeaaacdd61425caeb11bc6e5a"
}));
when(transactionPool.addLocalTransaction(any(Transaction.class)))
when(transactionPool.addTransactionViaApi(any(Transaction.class)))
.thenReturn(ValidationResult.valid());
final JsonRpcSuccessResponse response = (JsonRpcSuccessResponse) method.response(request);
assertThat(response).isNotNull();

@ -108,7 +108,7 @@ public class EthSendRawTransactionTest {
@Test
public void validTransactionIsSentToTransactionPool() {
when(transactionPool.addLocalTransaction(any(Transaction.class)))
when(transactionPool.addTransactionViaApi(any(Transaction.class)))
.thenReturn(ValidationResult.valid());
final JsonRpcRequestContext request =
@ -123,7 +123,7 @@ public class EthSendRawTransactionTest {
final JsonRpcResponse actualResponse = method.response(request);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
verify(transactionPool).addLocalTransaction(any(Transaction.class));
verify(transactionPool).addTransactionViaApi(any(Transaction.class));
}
@Test
@ -178,7 +178,7 @@ public class EthSendRawTransactionTest {
private void verifyErrorForInvalidTransaction(
final TransactionInvalidReason transactionInvalidReason, final JsonRpcError expectedError) {
when(transactionPool.addLocalTransaction(any(Transaction.class)))
when(transactionPool.addTransactionViaApi(any(Transaction.class)))
.thenReturn(ValidationResult.invalid(transactionInvalidReason));
final JsonRpcRequestContext request =
@ -191,7 +191,7 @@ public class EthSendRawTransactionTest {
final JsonRpcResponse actualResponse = method.response(request);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
verify(transactionPool).addLocalTransaction(any(Transaction.class));
verify(transactionPool).addTransactionViaApi(any(Transaction.class));
}
@Test

@ -221,7 +221,7 @@ public class EeaSendRawTransactionTest extends BaseEeaSendRawTransaction {
.thenReturn(MOCK_ORION_KEY);
when(privacyController.validatePrivateTransaction(any(), anyString()))
.thenReturn(ValidationResult.valid());
when(transactionPool.addLocalTransaction(any()))
when(transactionPool.addTransactionViaApi(any()))
.thenReturn(ValidationResult.invalid(transactionInvalidReason));
final JsonRpcResponse expectedResponse =

@ -53,7 +53,7 @@ public class PluginEeaSendRawTransactionTest extends BaseEeaSendRawTransaction {
when(privacyController.validatePrivateTransaction(any(), any()))
.thenReturn(ValidationResult.valid());
when(transactionPool.addLocalTransaction(any())).thenReturn(ValidationResult.valid());
when(transactionPool.addTransactionViaApi(any())).thenReturn(ValidationResult.valid());
final JsonRpcResponse expectedResponse =
new JsonRpcSuccessResponse(
@ -64,6 +64,6 @@ public class PluginEeaSendRawTransactionTest extends BaseEeaSendRawTransaction {
method.response(validUnrestrictedPrivacyGroupTransactionRequest);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
verify(transactionPool).addLocalTransaction(PUBLIC_PLUGIN_TRANSACTION);
verify(transactionPool).addTransactionViaApi(PUBLIC_PLUGIN_TRANSACTION);
}
}

@ -59,7 +59,7 @@ public class RestrictedFlexibleEeaSendRawTransactionTest extends BaseEeaSendRawT
public void validFlexibleTransactionPrivacyGroupIsSentToTransactionPool() {
when(privacyController.validatePrivateTransaction(any(), any()))
.thenReturn(ValidationResult.valid());
when(transactionPool.addLocalTransaction(any(Transaction.class)))
when(transactionPool.addTransactionViaApi(any(Transaction.class)))
.thenReturn(ValidationResult.valid());
when(privacyController.createPrivateMarkerTransactionPayload(any(), any(), any()))
@ -81,7 +81,7 @@ public class RestrictedFlexibleEeaSendRawTransactionTest extends BaseEeaSendRawT
final JsonRpcResponse actualResponse = method.response(validPrivacyGroupTransactionRequest);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
verify(transactionPool).addLocalTransaction(PUBLIC_FLEXIBLE_TRANSACTION);
verify(transactionPool).addTransactionViaApi(PUBLIC_FLEXIBLE_TRANSACTION);
}
@Test

@ -60,7 +60,7 @@ public class RestrictedOffchainEeaSendRawTransactionTest extends BaseEeaSendRawT
.thenReturn(MOCK_ORION_KEY);
when(privacyController.validatePrivateTransaction(any(), any()))
.thenReturn(ValidationResult.valid());
when(transactionPool.addLocalTransaction(any())).thenReturn(ValidationResult.valid());
when(transactionPool.addTransactionViaApi(any())).thenReturn(ValidationResult.valid());
final JsonRpcResponse expectedResponse =
new JsonRpcSuccessResponse(
@ -70,7 +70,7 @@ public class RestrictedOffchainEeaSendRawTransactionTest extends BaseEeaSendRawT
final JsonRpcResponse actualResponse = method.response(validPrivateForTransactionRequest);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
verify(transactionPool).addLocalTransaction(PUBLIC_OFF_CHAIN_TRANSACTION);
verify(transactionPool).addTransactionViaApi(PUBLIC_OFF_CHAIN_TRANSACTION);
}
@Test
@ -87,7 +87,7 @@ public class RestrictedOffchainEeaSendRawTransactionTest extends BaseEeaSendRawT
when(privacyController.findPrivacyGroupByGroupId(any(), any()))
.thenReturn(pantheonPrivacyGroup);
when(transactionPool.addLocalTransaction(any())).thenReturn(ValidationResult.valid());
when(transactionPool.addTransactionViaApi(any())).thenReturn(ValidationResult.valid());
final JsonRpcResponse expectedResponse =
new JsonRpcSuccessResponse(

@ -20,6 +20,7 @@ import static org.hyperledger.besu.ethereum.eth.transactions.TransactionAddedSta
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.CHAIN_HEAD_NOT_AVAILABLE;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.CHAIN_HEAD_WORLD_STATE_NOT_AVAILABLE;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.INTERNAL_ERROR;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.TRANSACTION_ALREADY_KNOWN;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
@ -113,8 +114,20 @@ public class TransactionPool implements BlockAddedObserver {
pendingTransactions.reset();
}
public ValidationResult<TransactionInvalidReason> addLocalTransaction(
public ValidationResult<TransactionInvalidReason> addTransactionViaApi(
final Transaction transaction) {
if (configuration.getDisableLocalTransactions()) {
final var result = addRemoteTransaction(transaction);
if (result.isValid()) {
transactionBroadcaster.onTransactionsAdded(List.of(transaction));
}
return result;
}
return addLocalTransaction(transaction);
}
ValidationResult<TransactionInvalidReason> addLocalTransaction(final Transaction transaction) {
final ValidationResultAndAccount validationResult = validateLocalTransaction(transaction);
if (validationResult.result.isValid()) {
@ -159,48 +172,9 @@ public class TransactionPool implements BlockAddedObserver {
for (final Transaction transaction : transactions) {
if (pendingTransactions.containsTransaction(transaction.getHash())) {
LOG.atTrace()
.setMessage("Discard already present transaction {}")
.addArgument(transaction::toTraceLog)
.log();
// We already have this transaction, don't even validate it.
duplicateTransactionCounter.labels(REMOTE).inc();
continue;
}
final ValidationResultAndAccount validationResult = validateRemoteTransaction(transaction);
if (validationResult.result.isValid()) {
final TransactionAddedStatus status =
pendingTransactions.addRemoteTransaction(transaction, validationResult.maybeAccount);
switch (status) {
case ADDED:
LOG.atTrace()
.setMessage("Added remote transaction {}")
.addArgument(transaction::toTraceLog)
.log();
addedTransactions.add(transaction);
break;
case ALREADY_KNOWN:
LOG.atTrace()
.setMessage("Duplicate remote transaction {}")
.addArgument(transaction::toTraceLog)
.log();
duplicateTransactionCounter.labels(REMOTE).inc();
break;
default:
LOG.atTrace().setMessage("Transaction added status {}").addArgument(status::name).log();
}
} else {
LOG.atTrace()
.setMessage("Discard invalid transaction {}, reason {}")
.addArgument(transaction::toTraceLog)
.addArgument(validationResult.result::getInvalidReason)
.log();
pendingTransactions
.signalInvalidAndGetDependentTransactions(transaction)
.forEach(pendingTransactions::removeTransaction);
final var result = addRemoteTransaction(transaction);
if (result.isValid()) {
addedTransactions.add(transaction);
}
}
@ -215,6 +189,56 @@ public class TransactionPool implements BlockAddedObserver {
}
}
private ValidationResult<TransactionInvalidReason> addRemoteTransaction(
final Transaction transaction) {
if (pendingTransactions.containsTransaction(transaction.getHash())) {
LOG.atTrace()
.setMessage("Discard already present transaction {}")
.addArgument(transaction::toTraceLog)
.log();
// We already have this transaction, don't even validate it.
duplicateTransactionCounter.labels(REMOTE).inc();
return ValidationResult.invalid(TRANSACTION_ALREADY_KNOWN);
}
final ValidationResultAndAccount validationResult = validateRemoteTransaction(transaction);
if (validationResult.result.isValid()) {
final var status =
pendingTransactions.addRemoteTransaction(transaction, validationResult.maybeAccount);
switch (status) {
case ADDED:
LOG.atTrace()
.setMessage("Added remote transaction {}")
.addArgument(transaction::toTraceLog)
.log();
break;
case ALREADY_KNOWN:
LOG.atTrace()
.setMessage("Duplicate remote transaction {}")
.addArgument(transaction::toTraceLog)
.log();
duplicateTransactionCounter.labels(REMOTE).inc();
return ValidationResult.invalid(TRANSACTION_ALREADY_KNOWN);
default:
LOG.atTrace().setMessage("Transaction added status {}").addArgument(status::name).log();
return ValidationResult.invalid(status.getInvalidReason().get());
}
} else {
LOG.atTrace()
.setMessage("Discard invalid transaction {}, reason {}")
.addArgument(transaction::toTraceLog)
.addArgument(validationResult.result::getInvalidReason)
.log();
pendingTransactions
.signalInvalidAndGetDependentTransactions(transaction)
.forEach(pendingTransactions::removeTransaction);
}
return validationResult.result;
}
public long subscribePendingTransactions(final PendingTransactionListener listener) {
return pendingTransactions.subscribePendingTransactions(listener);
}
@ -247,7 +271,10 @@ public class TransactionPool implements BlockAddedObserver {
reAddTransactions.stream()
.collect(
Collectors.partitioningBy(
tx -> pendingTransactions.isLocalSender(tx.getSender())));
tx ->
configuration.getDisableLocalTransactions()
? false
: pendingTransactions.isLocalSender(tx.getSender())));
var reAddLocalTxs = txsByOrigin.get(true);
var reAddRemoteTxs = txsByOrigin.get(false);
if (!reAddLocalTxs.isEmpty()) {

@ -32,6 +32,7 @@ public interface TransactionPoolConfiguration {
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;
TransactionPoolConfiguration DEFAULT = ImmutableTransactionPoolConfiguration.builder().build();
@ -79,4 +80,9 @@ public interface TransactionPoolConfiguration {
default Boolean getStrictTransactionReplayProtectionEnabled() {
return DEFAULT_STRICT_TX_REPLAY_PROTECTION_ENABLED;
}
@Value.Default
default Boolean getDisableLocalTransactions() {
return DEFAULT_DISABLE_LOCAL_TXS;
}
}

@ -96,7 +96,7 @@ public abstract class AbstractPendingTransactionsSorter implements PendingTransa
protected final TransactionPoolReplacementHandler transactionReplacementHandler;
protected final Supplier<BlockHeader> chainHeadHeaderSupplier;
private final Set<Address> localSenders = ConcurrentHashMap.newKeySet();
private final Set<Address> localSenders;
public AbstractPendingTransactionsSorter(
final TransactionPoolConfiguration poolConfig,
@ -105,6 +105,8 @@ public abstract class AbstractPendingTransactionsSorter implements PendingTransa
final Supplier<BlockHeader> chainHeadHeaderSupplier) {
this.poolConfig = poolConfig;
this.pendingTransactions = new ConcurrentHashMap<>(poolConfig.getTxPoolMaxSize());
this.localSenders =
poolConfig.getDisableLocalTransactions() ? Set.of() : ConcurrentHashMap.newKeySet();
this.clock = clock;
this.chainHeadHeaderSupplier = chainHeadHeaderSupplier;
this.transactionReplacementHandler =
@ -551,6 +553,6 @@ public abstract class AbstractPendingTransactionsSorter implements PendingTransa
@Override
public boolean isLocalSender(final Address sender) {
return localSenders.contains(sender);
return poolConfig.getDisableLocalTransactions() ? false : localSenders.contains(sender);
}
}

@ -37,6 +37,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.quality.Strictness.LENIENT;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
@ -75,15 +76,19 @@ import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
@SuppressWarnings("unchecked")
@RunWith(MockitoJUnitRunner.class)
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = LENIENT)
public abstract class AbstractTransactionPoolTest {
protected static final int MAX_TRANSACTIONS = 5;
@ -125,7 +130,7 @@ public abstract class AbstractTransactionPoolTest {
protected abstract FeeMarket getFeeMarket();
@Before
@BeforeEach
public void setUp() {
executionContext = createExecutionContextTestFixture();
protocolContext = executionContext.getProtocolContext();
@ -181,30 +186,35 @@ public abstract class AbstractTransactionPoolTest {
config);
}
@Test
public void localTransactionHappyPath() {
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void localTransactionHappyPath(final boolean disableLocalTxs) {
this.transactionPool = createTransactionPool(b -> b.disableLocalTransactions(disableLocalTxs));
final Transaction transaction = createTransaction(0);
givenTransactionIsValid(transaction);
assertLocalTransactionValid(transaction);
assertTransactionViaApiValid(transaction, disableLocalTxs);
}
@Test
public void shouldReturnExclusivelyLocalTransactionsWhenAppropriate() {
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void shouldReturnExclusivelyLocalTransactionsWhenAppropriate(
final boolean disableLocalTxs) {
this.transactionPool = createTransactionPool(b -> b.disableLocalTransactions(disableLocalTxs));
final Transaction localTransaction0 = createTransaction(0);
givenTransactionIsValid(localTransaction0);
givenTransactionIsValid(transaction1);
givenTransactionIsValid(transaction2);
assertLocalTransactionValid(localTransaction0);
assertTransactionViaApiValid(localTransaction0, disableLocalTxs);
assertRemoteTransactionValid(transaction1);
assertRemoteTransactionValid(transaction2);
assertThat(transactions.size()).isEqualTo(3);
List<Transaction> localTransactions = transactions.getLocalTransactions();
assertThat(localTransactions.size()).isEqualTo(1);
assertThat(localTransactions.size()).isEqualTo(disableLocalTxs ? 0 : 1);
}
@Test
@ -319,14 +329,20 @@ public abstract class AbstractTransactionPoolTest {
assertTransactionPending(transaction2);
}
@Test
public void addLocalTransaction_strictReplayProtectionOn_txWithChainId_chainIdIsConfigured() {
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void addLocalTransaction_strictReplayProtectionOn_txWithChainId_chainIdIsConfigured(
final boolean disableLocalTxs) {
protocolSupportsTxReplayProtection(1337, true);
transactionPool = createTransactionPool(b -> b.strictTransactionReplayProtectionEnabled(true));
transactionPool =
createTransactionPool(
b ->
b.strictTransactionReplayProtectionEnabled(true)
.disableLocalTransactions(disableLocalTxs));
final Transaction tx = createTransaction(1);
givenTransactionIsValid(tx);
assertLocalTransactionValid(tx);
assertTransactionViaApiValid(tx, disableLocalTxs);
}
@Test
@ -381,8 +397,11 @@ public abstract class AbstractTransactionPoolTest {
verifyNoMoreInteractions(transactionValidator);
}
@Test
public void shouldAllowSequenceOfTransactionsWithIncreasingNonceFromSameSender() {
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void shouldAllowSequenceOfTransactionsWithIncreasingNonceFromSameSender(
final boolean disableLocalTxs) {
transactionPool = createTransactionPool(b -> b.disableLocalTransactions(disableLocalTxs));
final Transaction transaction1 = createTransaction(1);
final Transaction transaction2 = createTransaction(2);
final Transaction transaction3 = createTransaction(3);
@ -391,9 +410,9 @@ public abstract class AbstractTransactionPoolTest {
givenTransactionIsValid(transaction2);
givenTransactionIsValid(transaction3);
assertLocalTransactionValid(transaction1);
assertLocalTransactionValid(transaction2);
assertLocalTransactionValid(transaction3);
assertTransactionViaApiValid(transaction1, disableLocalTxs);
assertTransactionViaApiValid(transaction2, disableLocalTxs);
assertTransactionViaApiValid(transaction3, disableLocalTxs);
}
@Test
@ -434,16 +453,19 @@ public abstract class AbstractTransactionPoolTest {
assertRemoteTransactionInvalid(transaction2);
}
@Test
public void shouldNotNotifyBatchListenerWhenLocalTransactionDoesNotReplaceExisting() {
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void shouldNotNotifyBatchListenerWhenLocalTransactionDoesNotReplaceExisting(
final boolean disableLocalTxs) {
transactionPool = createTransactionPool(b -> b.disableLocalTransactions(disableLocalTxs));
final Transaction transaction1 = createTransaction(1, Wei.of(10));
final Transaction transaction2 = createTransaction(1, Wei.of(9));
givenTransactionIsValid(transaction1);
givenTransactionIsValid(transaction2);
assertLocalTransactionValid(transaction1);
assertLocalTransactionInvalid(transaction2, TRANSACTION_REPLACEMENT_UNDERPRICED);
assertTransactionViaApiValid(transaction1, disableLocalTxs);
assertTransactionViaApiInvalid(transaction2, TRANSACTION_REPLACEMENT_UNDERPRICED);
}
@Test
@ -453,7 +475,7 @@ public abstract class AbstractTransactionPoolTest {
givenTransactionIsValid(transaction1);
assertLocalTransactionInvalid(transaction1, EXCEEDS_BLOCK_GAS_LIMIT);
assertTransactionViaApiInvalid(transaction1, EXCEEDS_BLOCK_GAS_LIMIT);
}
@Test
@ -482,6 +504,7 @@ public abstract class AbstractTransactionPoolTest {
@Test
public void shouldAcceptLocalTransactionsEvenIfAnInvalidTransactionWithLowerNonceExists() {
transactionPool = createTransactionPool(b -> b.disableLocalTransactions(false));
final Transaction invalidTx =
createBaseTransaction(0).gasLimit(blockGasLimit + 1).createTransaction(KEY_PAIR1);
@ -490,17 +513,19 @@ public abstract class AbstractTransactionPoolTest {
givenTransactionIsValid(invalidTx);
givenTransactionIsValid(nextTx);
assertLocalTransactionInvalid(invalidTx, EXCEEDS_BLOCK_GAS_LIMIT);
assertLocalTransactionValid(nextTx);
assertTransactionViaApiInvalid(invalidTx, EXCEEDS_BLOCK_GAS_LIMIT);
assertTransactionViaApiValid(nextTx, false);
}
@Test
public void shouldRejectLocalTransactionsWhenNonceTooFarInFuture() {
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void shouldRejectLocalTransactionsWhenNonceTooFarInFuture(final boolean disableLocalTxs) {
transactionPool = createTransactionPool(b -> b.disableLocalTransactions(disableLocalTxs));
final Transaction transaction1 = createTransaction(Integer.MAX_VALUE);
givenTransactionIsValid(transaction1);
assertLocalTransactionInvalid(transaction1, NONCE_TOO_FAR_IN_FUTURE_FOR_SENDER);
assertTransactionViaApiInvalid(transaction1, NONCE_TOO_FAR_IN_FUTURE_FOR_SENDER);
}
@Test
@ -572,38 +597,43 @@ public abstract class AbstractTransactionPoolTest {
.isEqualTo(expectedValidationParams);
}
@Test
public void shouldIgnoreFeeCapIfSetZero() {
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void shouldIgnoreFeeCapIfSetZero(final boolean disableLocalTxs) {
final Wei twoEthers = Wei.fromEth(2);
transactionPool = createTransactionPool(b -> b.txFeeCap(Wei.ZERO));
transactionPool =
createTransactionPool(b -> b.txFeeCap(Wei.ZERO).disableLocalTransactions(disableLocalTxs));
final Transaction transaction = createTransaction(1, twoEthers.add(Wei.of(1)));
givenTransactionIsValid(transaction);
assertLocalTransactionValid(transaction);
assertTransactionViaApiValid(transaction, disableLocalTxs);
}
@Test
public void shouldRejectLocalTransactionIfFeeCapExceeded() {
final Wei twoEthers = Wei.fromEth(2);
transactionPool = createTransactionPool(b -> b.txFeeCap(twoEthers));
transactionPool =
createTransactionPool(b -> b.txFeeCap(twoEthers).disableLocalTransactions(false));
final Transaction transactionLocal = createTransaction(1, twoEthers.add(1));
givenTransactionIsValid(transactionLocal);
assertLocalTransactionInvalid(transactionLocal, TX_FEECAP_EXCEEDED);
assertTransactionViaApiInvalid(transactionLocal, TX_FEECAP_EXCEEDED);
}
@Test
public void shouldRejectZeroGasPriceTransactionWhenNotMining() {
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void shouldRejectZeroGasPriceLocalTransactionWhenNotMining(final boolean disableLocalTxs) {
transactionPool = createTransactionPool(b -> b.disableLocalTransactions(disableLocalTxs));
when(miningParameters.isMiningEnabled()).thenReturn(false);
final Transaction transaction = createTransaction(0, Wei.ZERO);
givenTransactionIsValid(transaction);
assertLocalTransactionInvalid(transaction, GAS_PRICE_TOO_LOW);
assertTransactionViaApiInvalid(transaction, GAS_PRICE_TOO_LOW);
}
private void assertTransactionPending(final Transaction t) {
@ -660,10 +690,10 @@ public abstract class AbstractTransactionPoolTest {
.thenReturn(valid());
}
protected void assertLocalTransactionInvalid(
protected void assertTransactionViaApiInvalid(
final Transaction tx, final TransactionInvalidReason invalidReason) {
final ValidationResult<TransactionInvalidReason> result =
transactionPool.addLocalTransaction(tx);
transactionPool.addTransactionViaApi(tx);
assertThat(result.isValid()).isFalse();
assertThat(result.getInvalidReason()).isEqualTo(invalidReason);
@ -671,14 +701,18 @@ public abstract class AbstractTransactionPoolTest {
verify(transactionBroadcaster, never()).onTransactionsAdded(singletonList(tx));
}
protected void assertLocalTransactionValid(final Transaction tx) {
protected void assertTransactionViaApiValid(final Transaction tx, final boolean disableLocals) {
final ValidationResult<TransactionInvalidReason> result =
transactionPool.addLocalTransaction(tx);
transactionPool.addTransactionViaApi(tx);
assertThat(result.isValid()).isTrue();
assertTransactionPending(tx);
verify(transactionBroadcaster).onTransactionsAdded(singletonList(tx));
assertThat(transactions.getLocalTransactions()).contains(tx);
if (disableLocals) {
assertThat(transactions.getLocalTransactions()).doesNotContain(tx);
} else {
assertThat(transactions.getLocalTransactions()).contains(tx);
}
}
protected void assertRemoteTransactionValid(final Transaction tx) {

@ -265,7 +265,7 @@ public class TestNode implements Closeable {
}
public void receiveLocalTransaction(final Transaction transaction) {
transactionPool.addLocalTransaction(transaction);
transactionPool.addTransactionViaApi(transaction);
}
public int getPendingTransactionCount() {

@ -41,12 +41,14 @@ import java.time.ZoneId;
import java.util.List;
import java.util.Optional;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.junit.jupiter.MockitoExtension;
@SuppressWarnings("unchecked")
@RunWith(MockitoJUnitRunner.class)
@ExtendWith(MockitoExtension.class)
public class TransactionPoolLegacyTest extends AbstractTransactionPoolTest {
@Override
@ -116,15 +118,21 @@ public class TransactionPoolLegacyTest extends AbstractTransactionPoolTest {
return block;
}
@Test
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void
addLocalTransaction_strictReplayProtectionOn_txWithoutChainId_chainIdIsConfigured_protectionNotSupportedAtCurrentBlock() {
addLocalTransaction_strictReplayProtectionOn_txWithoutChainId_chainIdIsConfigured_protectionNotSupportedAtCurrentBlock(
final boolean disableLocalTxs) {
protocolSupportsTxReplayProtection(1337, false);
transactionPool = createTransactionPool(b -> b.strictTransactionReplayProtectionEnabled(true));
transactionPool =
createTransactionPool(
b ->
b.strictTransactionReplayProtectionEnabled(true)
.disableLocalTransactions(disableLocalTxs));
final Transaction tx = createTransactionWithoutChainId(1);
givenTransactionIsValid(tx);
assertLocalTransactionValid(tx);
assertTransactionViaApiValid(tx, disableLocalTxs);
}
@Test
@ -138,14 +146,20 @@ public class TransactionPoolLegacyTest extends AbstractTransactionPoolTest {
assertRemoteTransactionValid(tx);
}
@Test
public void addLocalTransaction_strictReplayProtectionOff_txWithoutChainId_chainIdIsConfigured() {
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void addLocalTransaction_strictReplayProtectionOff_txWithoutChainId_chainIdIsConfigured(
final boolean disableLocalTxs) {
protocolSupportsTxReplayProtection(1337, true);
transactionPool = createTransactionPool(b -> b.strictTransactionReplayProtectionEnabled(false));
transactionPool =
createTransactionPool(
b ->
b.strictTransactionReplayProtectionEnabled(false)
.disableLocalTransactions(disableLocalTxs));
final Transaction tx = createTransactionWithoutChainId(1);
givenTransactionIsValid(tx);
assertLocalTransactionValid(tx);
assertTransactionViaApiValid(tx, disableLocalTxs);
}
@Test
@ -155,7 +169,7 @@ public class TransactionPoolLegacyTest extends AbstractTransactionPoolTest {
final Transaction tx = createTransactionWithoutChainId(1);
givenTransactionIsValid(tx);
assertLocalTransactionInvalid(tx, REPLAY_PROTECTED_SIGNATURE_REQUIRED);
assertTransactionViaApiInvalid(tx, REPLAY_PROTECTED_SIGNATURE_REQUIRED);
}
@Test
@ -169,15 +183,20 @@ public class TransactionPoolLegacyTest extends AbstractTransactionPoolTest {
assertRemoteTransactionValid(tx);
}
@Test
public void
addLocalTransaction_strictReplayProtectionOn_txWithoutChainId_chainIdIsNotConfigured() {
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void addLocalTransaction_strictReplayProtectionOn_txWithoutChainId_chainIdIsNotConfigured(
final boolean disableLocalTxs) {
protocolDoesNotSupportTxReplayProtection();
transactionPool = createTransactionPool(b -> b.strictTransactionReplayProtectionEnabled(true));
transactionPool =
createTransactionPool(
b ->
b.strictTransactionReplayProtectionEnabled(true)
.disableLocalTransactions(disableLocalTxs));
final Transaction tx = createTransactionWithoutChainId(1);
givenTransactionIsValid(tx);
assertLocalTransactionValid(tx);
assertTransactionViaApiValid(tx, disableLocalTxs);
}
@Test
@ -204,29 +223,33 @@ public class TransactionPoolLegacyTest extends AbstractTransactionPoolTest {
givenTransactionIsValid(transaction);
assertLocalTransactionInvalid(transaction, INVALID_TRANSACTION_FORMAT);
assertTransactionViaApiInvalid(transaction, INVALID_TRANSACTION_FORMAT);
}
@Test
public void shouldAcceptZeroGasPriceFrontierTransactionsWhenMining() {
public void shouldAcceptZeroGasPriceFrontierLocalTransactionsWhenMining() {
transactionPool = createTransactionPool(b -> b.disableLocalTransactions(false));
when(miningParameters.isMiningEnabled()).thenReturn(true);
final Transaction transaction = createTransaction(0, Wei.ZERO);
givenTransactionIsValid(transaction);
assertLocalTransactionValid(transaction);
assertTransactionViaApiValid(transaction, false);
}
@Test
public void shouldAcceptZeroGasPriceTransactionWhenMinGasPriceIsZero() {
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void shouldAcceptZeroGasPriceTransactionWhenMinGasPriceIsZero(
final boolean disableLocalTxs) {
transactionPool = createTransactionPool(b -> b.disableLocalTransactions(disableLocalTxs));
when(miningParameters.getMinTransactionGasPrice()).thenReturn(Wei.ZERO);
final Transaction transaction = createTransaction(0, Wei.ZERO);
givenTransactionIsValid(transaction);
assertLocalTransactionValid(transaction);
assertTransactionViaApiValid(transaction, disableLocalTxs);
}
private Transaction createTransactionWithoutChainId(final int transactionNumber) {

@ -49,7 +49,9 @@ import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
public class TransactionPoolLondonTest extends AbstractTransactionPoolTest {
@ -158,8 +160,11 @@ public class TransactionPoolLondonTest extends AbstractTransactionPoolTest {
return block;
}
@Test
public void shouldAcceptZeroGasPriceFrontierTxsWhenMinGasPriceIsZeroAndLondonWithZeroBaseFee() {
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void shouldAcceptZeroGasPriceFrontierTxsWhenMinGasPriceIsZeroAndLondonWithZeroBaseFee(
final boolean disableLocalTxs) {
transactionPool = createTransactionPool(b -> b.disableLocalTransactions(disableLocalTxs));
when(miningParameters.getMinTransactionGasPrice()).thenReturn(Wei.ZERO);
when(protocolSpec.getFeeMarket()).thenReturn(FeeMarket.london(0, Optional.of(Wei.ZERO)));
whenBlockBaseFeeIs(Wei.ZERO);
@ -167,11 +172,14 @@ public class TransactionPoolLondonTest extends AbstractTransactionPoolTest {
final Transaction frontierTransaction = createFrontierTransaction(0, Wei.ZERO);
givenTransactionIsValid(frontierTransaction);
assertLocalTransactionValid(frontierTransaction);
assertTransactionViaApiValid(frontierTransaction, disableLocalTxs);
}
@Test
public void shouldAcceptZeroGasPrice1559TxsWhenMinGasPriceIsZeroAndLondonWithZeroBaseFee() {
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void shouldAcceptZeroGasPrice1559TxsWhenMinGasPriceIsZeroAndLondonWithZeroBaseFee(
final boolean disableLocalTxs) {
transactionPool = createTransactionPool(b -> b.disableLocalTransactions(disableLocalTxs));
when(miningParameters.getMinTransactionGasPrice()).thenReturn(Wei.ZERO);
when(protocolSpec.getFeeMarket()).thenReturn(FeeMarket.london(0, Optional.of(Wei.ZERO)));
whenBlockBaseFeeIs(Wei.ZERO);
@ -179,16 +187,17 @@ public class TransactionPoolLondonTest extends AbstractTransactionPoolTest {
final Transaction transaction = createTransaction(0, Wei.ZERO);
givenTransactionIsValid(transaction);
assertLocalTransactionValid(transaction);
assertTransactionViaApiValid(transaction, disableLocalTxs);
}
@Test
public void shouldAcceptBaseFeeFloorGasPriceFrontierTransactionsWhenMining() {
public void shouldAcceptBaseFeeFloorGasPriceFrontierLocalTransactionsWhenMining() {
transactionPool = createTransactionPool(b -> b.disableLocalTransactions(false));
final Transaction frontierTransaction = createFrontierTransaction(0, BASE_FEE_FLOOR);
givenTransactionIsValid(frontierTransaction);
assertLocalTransactionValid(frontierTransaction);
assertTransactionViaApiValid(frontierTransaction, false);
}
@Test

Loading…
Cancel
Save