diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index d84a420dc2..8dcc7d623a 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -769,6 +769,13 @@ 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})", @@ -1938,6 +1945,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { .pooledTransactionHashesSize(pooledTransactionHashesSize) .pendingTxRetentionPeriod(pendingTxRetentionPeriod) .priceBump(priceBump) + .txFeeCap(txFeeCap) .build(); } diff --git a/besu/src/main/java/org/hyperledger/besu/cli/DefaultCommandValues.java b/besu/src/main/java/org/hyperledger/besu/cli/DefaultCommandValues.java index cbf2fc0740..1e34c76414 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/DefaultCommandValues.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/DefaultCommandValues.java @@ -15,6 +15,7 @@ package org.hyperledger.besu.cli; import org.hyperledger.besu.ethereum.core.Wei; +import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; import org.hyperledger.besu.ethereum.p2p.config.RlpxConfiguration; import org.hyperledger.besu.nat.NatMethod; @@ -44,6 +45,8 @@ public interface DefaultCommandValues { String MANDATORY_NETWORK_FORMAT_HELP = ""; String MANDATORY_NODE_ID_FORMAT_HELP = ""; Wei DEFAULT_MIN_TRANSACTION_GAS_PRICE = Wei.of(1000); + Wei DEFAULT_RPC_TX_FEE_CAP = TransactionPoolConfiguration.DEFAULT_RPC_TX_FEE_CAP; + Double DEFAULT_MIN_BLOCK_OCCUPANCY_RATIO = 0.8; Bytes DEFAULT_EXTRA_DATA = Bytes.EMPTY; long DEFAULT_MAX_REFRESH_DELAY = 3600000; diff --git a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java index 7b6703440d..6c5a68e637 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java @@ -3170,6 +3170,24 @@ public class BesuCommandTest extends CommandTestAbstract { "should be a number between 0 and 100 inclusive"); } + @Test + public void transactionPoolTxFeeCap() { + final Wei txFeeCap = Wei.fromEth(2); + parseCommand("--rpc-tx-feecap", txFeeCap.toString()); + verify(mockControllerBuilder) + .transactionPoolConfiguration(transactionPoolConfigCaptor.capture()); + assertThat(transactionPoolConfigCaptor.getValue().getTxFeeCap()).isEqualTo(txFeeCap); + assertThat(commandOutput.toString()).isEmpty(); + assertThat(commandErrorOutput.toString()).isEmpty(); + } + + @Test + public void invalidTansactionPoolTxFeeCapShouldFail() { + parseCommand("--rpc-tx-feecap", "abcd"); + assertThat(commandErrorOutput.toString()) + .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"); diff --git a/besu/src/test/resources/everything_config.toml b/besu/src/test/resources/everything_config.toml index 5132078fed..24bf79cd8a 100644 --- a/besu/src/test/resources/everything_config.toml +++ b/besu/src/test/resources/everything_config.toml @@ -138,6 +138,7 @@ tx-pool-price-bump=13 tx-pool-max-size=1234 tx-pool-hashes-max-size=10000 Xincoming-tx-messages-keep-alive-seconds=60 +rpc-tx-feecap=2000000000000000000 # Revert Reason revert-reason-enabled=false diff --git a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthGetFilterChangesIntegrationTest.java b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthGetFilterChangesIntegrationTest.java index f1bb3e3b1b..d1ac6e4f31 100644 --- a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthGetFilterChangesIntegrationTest.java +++ b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthGetFilterChangesIntegrationTest.java @@ -126,7 +126,8 @@ public class EthGetFilterChangesIntegrationTest { Optional.of(peerPendingTransactionTracker), Wei.ZERO, metricsSystem, - Optional.empty()); + Optional.empty(), + TransactionPoolConfiguration.DEFAULT); final BlockchainQueries blockchainQueries = new BlockchainQueries(blockchain, protocolContext.getWorldStateArchive()); filterManager = diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLError.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLError.java index 6bd5a70839..b8fef3ffe1 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLError.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLError.java @@ -38,6 +38,7 @@ public enum GraphQLError { WRONG_CHAIN_ID(-32000, "Wrong Chain ID in transaction signature"), REPLAY_PROTECTED_SIGNATURES_NOT_SUPPORTED( -32000, "Signatures with replay protection are not supported"), + TX_FEECAP_EXCEEDED(-32000, "Transaction fee cap exceeded"), // Private Transaction Errors PRIVATE_TRANSACTION_FAILED(-32000, "Private transaction failed"), @@ -91,6 +92,8 @@ public enum GraphQLError { return PRIVATE_TRANSACTION_FAILED; case GAS_PRICE_TOO_LOW: return GAS_PRICE_TOO_LOW; + case TX_FEECAP_EXCEEDED: + return TX_FEECAP_EXCEEDED; default: return INTERNAL_ERROR; } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverter.java index 4fb03df66e..9a75b40c85 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverter.java @@ -47,6 +47,8 @@ public class JsonRpcErrorConverter { return JsonRpcError.CHAIN_HEAD_WORLD_STATE_NOT_AVAILABLE; case GAS_PRICE_TOO_LOW: return JsonRpcError.GAS_PRICE_TOO_LOW; + case TX_FEECAP_EXCEEDED: + return JsonRpcError.TX_FEECAP_EXCEEDED; case OFFCHAIN_PRIVACY_GROUP_DOES_NOT_EXIST: return JsonRpcError.OFFCHAIN_PRIVACY_GROUP_DOES_NOT_EXIST; case TRANSACTION_ALREADY_KNOWN: diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java index 989e07faf3..61ff0ac312 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java @@ -59,6 +59,7 @@ public enum JsonRpcError { GAS_PRICE_TOO_LOW(-32009, "Gas price below configured minimum gas price"), WRONG_CHAIN_ID(-32000, "Wrong chainId"), REPLAY_PROTECTED_SIGNATURES_NOT_SUPPORTED(-32000, "ChainId not supported"), + TX_FEECAP_EXCEEDED(-32000, "Transaction fee cap exceeded"), // Miner failures COINBASE_NOT_SET(-32010, "Coinbase not set. Unable to start mining without a coinbase"), diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthSendRawTransactionTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthSendRawTransactionTest.java index 06231814dc..aa7ab69f31 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthSendRawTransactionTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthSendRawTransactionTest.java @@ -170,6 +170,12 @@ public class EthSendRawTransactionTest { TransactionInvalidReason.TX_SENDER_NOT_AUTHORIZED, JsonRpcError.TX_SENDER_NOT_AUTHORIZED); } + @Test + public void transactionWithFeeCapExceededIsRejected() { + verifyErrorForInvalidTransaction( + TransactionInvalidReason.TX_FEECAP_EXCEEDED, JsonRpcError.TX_FEECAP_EXCEEDED); + } + private void verifyErrorForInvalidTransaction( final TransactionInvalidReason transactionInvalidReason, final JsonRpcError expectedError) { when(transactionPool.addLocalTransaction(any(Transaction.class))) diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidator.java index 99059f3fb6..44bdbf6776 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidator.java @@ -81,6 +81,7 @@ public interface TransactionValidator { OFFCHAIN_PRIVACY_GROUP_DOES_NOT_EXIST, INCORRECT_PRIVATE_NONCE, GAS_PRICE_TOO_LOW, + TX_FEECAP_EXCEEDED, PRIVATE_VALUE_NOT_ZERO, PRIVATE_UNIMPLEMENTED_TRANSACTION_TYPE; } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ValidationResult.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ValidationResult.java index b5b327b5a2..7ec09eedfb 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ValidationResult.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ValidationResult.java @@ -33,7 +33,7 @@ public final class ValidationResult { } public boolean isValid() { - return !invalidReason.isPresent(); + return invalidReason.isEmpty(); } public T getInvalidReason() throws NoSuchElementException { diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java index 1e4f3977e9..e090b1fd14 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java @@ -83,6 +83,7 @@ public class TransactionPool implements BlockAddedObserver { TransactionPriceCalculator.frontier(); private final TransactionPriceCalculator eip1559PriceCalculator = TransactionPriceCalculator.eip1559(); + private final TransactionPoolConfiguration configuration; public TransactionPool( final PendingTransactions pendingTransactions, @@ -96,7 +97,8 @@ public class TransactionPool implements BlockAddedObserver { final Optional peerPendingTransactionTracker, final Wei minTransactionGasPrice, final MetricsSystem metricsSystem, - final Optional eip1559) { + final Optional eip1559, + final TransactionPoolConfiguration configuration) { this.pendingTransactions = pendingTransactions; this.protocolSchedule = protocolSchedule; this.protocolContext = protocolContext; @@ -107,6 +109,7 @@ public class TransactionPool implements BlockAddedObserver { this.peerPendingTransactionTracker = peerPendingTransactionTracker; this.minTransactionGasPrice = minTransactionGasPrice; this.eip1559 = eip1559; + this.configuration = configuration; duplicateTransactionCounter = metricsSystem.createLabelledCounter( @@ -152,6 +155,12 @@ public class TransactionPool implements BlockAddedObserver { if (transactionGasPrice.compareTo(minTransactionGasPrice) < 0) { return ValidationResult.invalid(TransactionInvalidReason.GAS_PRICE_TOO_LOW); } + + if (!configuration.getTxFeeCap().isZero() + && transactionGasPrice.compareTo(configuration.getTxFeeCap()) > 0) { + return ValidationResult.invalid(TransactionInvalidReason.TX_FEECAP_EXCEEDED); + } + final ValidationResult validationResult = validateTransaction(transaction); if (validationResult.isValid()) { diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolConfiguration.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolConfiguration.java index 5bb4c2bcd3..74268c510c 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolConfiguration.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolConfiguration.java @@ -14,6 +14,7 @@ */ package org.hyperledger.besu.ethereum.eth.transactions; +import org.hyperledger.besu.ethereum.core.Wei; import org.hyperledger.besu.util.number.Percentage; import java.util.Objects; @@ -24,25 +25,31 @@ public class TransactionPoolConfiguration { public static final int MAX_PENDING_TRANSACTIONS_HASHES = 4096; public static final int DEFAULT_TX_RETENTION_HOURS = 13; public static final Percentage DEFAULT_PRICE_BUMP = Percentage.fromInt(10); + public static final Wei DEFAULT_RPC_TX_FEE_CAP = Wei.fromEth(1); + public static final TransactionPoolConfiguration DEFAULT = + TransactionPoolConfiguration.builder().build(); private final int txPoolMaxSize; private final int pooledTransactionHashesSize; private final int pendingTxRetentionPeriod; private final int txMessageKeepAliveSeconds; - private final Percentage priceBump; + private final Wei txFeeCap; + public TransactionPoolConfiguration( final int txPoolMaxSize, final int pooledTransactionHashesSize, final int pendingTxRetentionPeriod, final int txMessageKeepAliveSeconds, - final Percentage priceBump) { + final Percentage priceBump, + final Wei txFeeCap) { this.txPoolMaxSize = txPoolMaxSize; this.pooledTransactionHashesSize = pooledTransactionHashesSize; this.pendingTxRetentionPeriod = pendingTxRetentionPeriod; this.txMessageKeepAliveSeconds = txMessageKeepAliveSeconds; this.priceBump = priceBump; + this.txFeeCap = txFeeCap; } public int getTxPoolMaxSize() { @@ -65,6 +72,10 @@ public class TransactionPoolConfiguration { return priceBump; } + public Wei getTxFeeCap() { + return txFeeCap; + } + @Override public boolean equals(final Object o) { if (this == o) { @@ -77,13 +88,14 @@ public class TransactionPoolConfiguration { return txPoolMaxSize == that.txPoolMaxSize && Objects.equals(pendingTxRetentionPeriod, that.pendingTxRetentionPeriod) && Objects.equals(txMessageKeepAliveSeconds, that.txMessageKeepAliveSeconds) - && Objects.equals(priceBump, that.priceBump); + && Objects.equals(priceBump, that.priceBump) + && Objects.equals(txFeeCap, that.txFeeCap); } @Override public int hashCode() { return Objects.hash( - txPoolMaxSize, pendingTxRetentionPeriod, txMessageKeepAliveSeconds, priceBump); + txPoolMaxSize, pendingTxRetentionPeriod, txMessageKeepAliveSeconds, priceBump, txFeeCap); } @Override @@ -97,6 +109,8 @@ public class TransactionPoolConfiguration { + txMessageKeepAliveSeconds + ", priceBump=" + priceBump + + ", txFeeCap=" + + txFeeCap + '}'; } @@ -110,6 +124,7 @@ public class TransactionPoolConfiguration { private Integer txMessageKeepAliveSeconds = DEFAULT_TX_MSG_KEEP_ALIVE; private int pooledTransactionHashesSize = MAX_PENDING_TRANSACTIONS_HASHES; private Percentage priceBump = DEFAULT_PRICE_BUMP; + private Wei txFeeCap = DEFAULT_RPC_TX_FEE_CAP; public Builder txPoolMaxSize(final int txPoolMaxSize) { this.txPoolMaxSize = txPoolMaxSize; @@ -140,13 +155,19 @@ public class TransactionPoolConfiguration { return priceBump(Percentage.fromInt(priceBump)); } + public Builder txFeeCap(final Wei txFeeCap) { + this.txFeeCap = txFeeCap; + return this; + } + public TransactionPoolConfiguration build() { return new TransactionPoolConfiguration( txPoolMaxSize, pooledTransactionHashesSize, pendingTxRetentionPeriod, txMessageKeepAliveSeconds, - priceBump); + priceBump, + txFeeCap); } } } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactory.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactory.java index 3424c58cdb..a4b5008137 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactory.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactory.java @@ -125,7 +125,8 @@ public class TransactionPoolFactory { pendingTransactionTracker, minTransactionGasPrice, metricsSystem, - eip1559); + eip1559, + transactionPoolConfiguration); final TransactionsMessageHandler transactionsMessageHandler = new TransactionsMessageHandler( ethContext.getScheduler(), diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactoryTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactoryTest.java index 93c43d7d3a..66212a7984 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactoryTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactoryTest.java @@ -90,7 +90,12 @@ public class TransactionPoolFactoryTest { state, Wei.of(1), new TransactionPoolConfiguration( - 1, 1, 1, 1, TransactionPoolConfiguration.DEFAULT_PRICE_BUMP), + 1, + 1, + 1, + 1, + TransactionPoolConfiguration.DEFAULT_PRICE_BUMP, + TransactionPoolConfiguration.DEFAULT_RPC_TX_FEE_CAP), pendingTransactions, peerTransactionTracker, transactionsMessageSender, @@ -168,7 +173,12 @@ public class TransactionPoolFactoryTest { state, Wei.of(1), new TransactionPoolConfiguration( - 1, 1, 1, 1, TransactionPoolConfiguration.DEFAULT_PRICE_BUMP), + 1, + 1, + 1, + 1, + TransactionPoolConfiguration.DEFAULT_PRICE_BUMP, + TransactionPoolConfiguration.DEFAULT_RPC_TX_FEE_CAP), pendingTransactions, peerTransactionTracker, transactionsMessageSender, diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolTest.java index e1ecc6dc39..b515009a4f 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolTest.java @@ -148,7 +148,8 @@ public class TransactionPoolTest { Optional.of(peerPendingTransactionTracker), Wei.of(2), metricsSystem, - Optional.empty()); + Optional.empty(), + TransactionPoolConfiguration.DEFAULT); blockchain.observeBlockAdded(transactionPool); } @@ -404,7 +405,8 @@ public class TransactionPoolTest { Optional.of(peerPendingTransactionTracker), Wei.ZERO, metricsSystem, - Optional.empty()); + Optional.empty(), + TransactionPoolConfiguration.DEFAULT); when(pendingTransactions.containsTransaction(transaction1.getHash())).thenReturn(true); @@ -541,7 +543,8 @@ public class TransactionPoolTest { Optional.of(peerPendingTransactionTracker), Wei.ZERO, metricsSystem, - Optional.empty()); + Optional.empty(), + TransactionPoolConfiguration.DEFAULT); final TransactionTestFixture builder = new TransactionTestFixture(); final Transaction transaction1 = builder.nonce(1).createTransaction(KEY_PAIR1); @@ -610,7 +613,8 @@ public class TransactionPoolTest { Optional.of(peerPendingTransactionTracker), Wei.ZERO, metricsSystem, - Optional.empty()); + Optional.empty(), + TransactionPoolConfiguration.DEFAULT); final TransactionTestFixture builder = new TransactionTestFixture(); final Transaction transactionLocal = builder.nonce(1).createTransaction(KEY_PAIR1); @@ -650,6 +654,75 @@ public class TransactionPoolTest { .isEqualToComparingFieldByField(expectedValidationParams); } + @Test + public void shouldIgnoreFeeCapIfSetZero() { + final EthProtocolManager ethProtocolManager = EthProtocolManagerTestUtil.create(); + final EthContext ethContext = ethProtocolManager.ethContext(); + final PeerTransactionTracker peerTransactionTracker = new PeerTransactionTracker(); + final Wei twoEthers = Wei.fromEth(2); + final TransactionPool transactionPool = + new TransactionPool( + transactions, + protocolSchedule, + protocolContext, + batchAddedListener, + Optional.of(pendingBatchAddedListener), + syncState, + ethContext, + peerTransactionTracker, + Optional.of(peerPendingTransactionTracker), + Wei.ZERO, + metricsSystem, + Optional.empty(), + TransactionPoolConfiguration.builder().txFeeCap(Wei.ZERO).build()); + when(transactionValidator.validate(any(Transaction.class))).thenReturn(valid()); + when(transactionValidator.validateForSender( + any(Transaction.class), + nullable(Account.class), + any(TransactionValidationParams.class))) + .thenReturn(valid()); + assertThat( + transactionPool + .addLocalTransaction( + new TransactionTestFixture() + .nonce(1) + .gasPrice(twoEthers.add(Wei.of(1))) + .createTransaction(KEY_PAIR1)) + .isValid()) + .isTrue(); + } + + @Test + public void shouldRejectLocalTransactionIfFeeCapExceeded() { + final EthProtocolManager ethProtocolManager = EthProtocolManagerTestUtil.create(); + final EthContext ethContext = ethProtocolManager.ethContext(); + final PeerTransactionTracker peerTransactionTracker = new PeerTransactionTracker(); + final Wei twoEthers = Wei.fromEth(2); + TransactionPool transactionPool = + new TransactionPool( + transactions, + protocolSchedule, + protocolContext, + batchAddedListener, + Optional.of(pendingBatchAddedListener), + syncState, + ethContext, + peerTransactionTracker, + Optional.of(peerPendingTransactionTracker), + Wei.ZERO, + metricsSystem, + Optional.empty(), + TransactionPoolConfiguration.builder().txFeeCap(twoEthers).build()); + + final TransactionTestFixture builder = new TransactionTestFixture(); + final Transaction transactionLocal = + builder.nonce(1).gasPrice(twoEthers.add(Wei.of(1))).createTransaction(KEY_PAIR1); + + final ValidationResult result = + transactionPool.addLocalTransaction(transactionLocal); + assertThat(result.getInvalidReason()).isEqualTo(TransactionInvalidReason.TX_FEECAP_EXCEEDED); + } + private void assertTransactionPending(final Transaction t) { assertThat(transactions.getTransactionByHash(t.getHash())).contains(t); }