Add fee cap for transactions submitted via RPC. (#1137)

- Added `--rpc-tx-feecap` command line flag.
    - Maximum transaction fees (in Wei) accepted for transaction submitted through RPC.
    - Defaulted to 1 ether.
- Updated `TransactionPool.addLocalTransaction` method: performs an additional check to verify if transaction fees don't exceed user defined fee cap (if cap is set to 0 then it is ignored and means no cap).

Signed-off-by: Abdelhamid Bakhta <abdelhamid.bakhta@consensys.net>
pull/1142/head
Abdelhamid Bakhta 4 years ago committed by GitHub
parent 9e224ed7f3
commit f5dd1db9b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
  2. 3
      besu/src/main/java/org/hyperledger/besu/cli/DefaultCommandValues.java
  3. 18
      besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java
  4. 1
      besu/src/test/resources/everything_config.toml
  5. 3
      ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthGetFilterChangesIntegrationTest.java
  6. 3
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLError.java
  7. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverter.java
  8. 1
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java
  9. 6
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthSendRawTransactionTest.java
  10. 1
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidator.java
  11. 2
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ValidationResult.java
  12. 11
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java
  13. 31
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolConfiguration.java
  14. 3
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactory.java
  15. 14
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactoryTest.java
  16. 81
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolTest.java

@ -769,6 +769,13 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
arity = "1") arity = "1")
private final Wei minTransactionGasPrice = DEFAULT_MIN_TRANSACTION_GAS_PRICE; 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( @Option(
names = {"--min-block-occupancy-ratio"}, names = {"--min-block-occupancy-ratio"},
description = "Minimum occupancy ratio for a mined block (default: ${DEFAULT-VALUE})", description = "Minimum occupancy ratio for a mined block (default: ${DEFAULT-VALUE})",
@ -1938,6 +1945,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.pooledTransactionHashesSize(pooledTransactionHashesSize) .pooledTransactionHashesSize(pooledTransactionHashesSize)
.pendingTxRetentionPeriod(pendingTxRetentionPeriod) .pendingTxRetentionPeriod(pendingTxRetentionPeriod)
.priceBump(priceBump) .priceBump(priceBump)
.txFeeCap(txFeeCap)
.build(); .build();
} }

@ -15,6 +15,7 @@
package org.hyperledger.besu.cli; package org.hyperledger.besu.cli;
import org.hyperledger.besu.ethereum.core.Wei; 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.ethereum.p2p.config.RlpxConfiguration;
import org.hyperledger.besu.nat.NatMethod; import org.hyperledger.besu.nat.NatMethod;
@ -44,6 +45,8 @@ public interface DefaultCommandValues {
String MANDATORY_NETWORK_FORMAT_HELP = "<NETWORK>"; String MANDATORY_NETWORK_FORMAT_HELP = "<NETWORK>";
String MANDATORY_NODE_ID_FORMAT_HELP = "<NODEID>"; String MANDATORY_NODE_ID_FORMAT_HELP = "<NODEID>";
Wei DEFAULT_MIN_TRANSACTION_GAS_PRICE = Wei.of(1000); 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; Double DEFAULT_MIN_BLOCK_OCCUPANCY_RATIO = 0.8;
Bytes DEFAULT_EXTRA_DATA = Bytes.EMPTY; Bytes DEFAULT_EXTRA_DATA = Bytes.EMPTY;
long DEFAULT_MAX_REFRESH_DELAY = 3600000; long DEFAULT_MAX_REFRESH_DELAY = 3600000;

@ -3170,6 +3170,24 @@ public class BesuCommandTest extends CommandTestAbstract {
"should be a number between 0 and 100 inclusive"); "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 @Test
public void txMessageKeepAliveSecondsWithInvalidInputShouldFail() { public void txMessageKeepAliveSecondsWithInvalidInputShouldFail() {
parseCommand("--Xincoming-tx-messages-keep-alive-seconds", "acbd"); parseCommand("--Xincoming-tx-messages-keep-alive-seconds", "acbd");

@ -138,6 +138,7 @@ tx-pool-price-bump=13
tx-pool-max-size=1234 tx-pool-max-size=1234
tx-pool-hashes-max-size=10000 tx-pool-hashes-max-size=10000
Xincoming-tx-messages-keep-alive-seconds=60 Xincoming-tx-messages-keep-alive-seconds=60
rpc-tx-feecap=2000000000000000000
# Revert Reason # Revert Reason
revert-reason-enabled=false revert-reason-enabled=false

@ -126,7 +126,8 @@ public class EthGetFilterChangesIntegrationTest {
Optional.of(peerPendingTransactionTracker), Optional.of(peerPendingTransactionTracker),
Wei.ZERO, Wei.ZERO,
metricsSystem, metricsSystem,
Optional.empty()); Optional.empty(),
TransactionPoolConfiguration.DEFAULT);
final BlockchainQueries blockchainQueries = final BlockchainQueries blockchainQueries =
new BlockchainQueries(blockchain, protocolContext.getWorldStateArchive()); new BlockchainQueries(blockchain, protocolContext.getWorldStateArchive());
filterManager = filterManager =

@ -38,6 +38,7 @@ public enum GraphQLError {
WRONG_CHAIN_ID(-32000, "Wrong Chain ID in transaction signature"), WRONG_CHAIN_ID(-32000, "Wrong Chain ID in transaction signature"),
REPLAY_PROTECTED_SIGNATURES_NOT_SUPPORTED( REPLAY_PROTECTED_SIGNATURES_NOT_SUPPORTED(
-32000, "Signatures with replay protection are not supported"), -32000, "Signatures with replay protection are not supported"),
TX_FEECAP_EXCEEDED(-32000, "Transaction fee cap exceeded"),
// Private Transaction Errors // Private Transaction Errors
PRIVATE_TRANSACTION_FAILED(-32000, "Private transaction failed"), PRIVATE_TRANSACTION_FAILED(-32000, "Private transaction failed"),
@ -91,6 +92,8 @@ public enum GraphQLError {
return PRIVATE_TRANSACTION_FAILED; return PRIVATE_TRANSACTION_FAILED;
case GAS_PRICE_TOO_LOW: case GAS_PRICE_TOO_LOW:
return GAS_PRICE_TOO_LOW; return GAS_PRICE_TOO_LOW;
case TX_FEECAP_EXCEEDED:
return TX_FEECAP_EXCEEDED;
default: default:
return INTERNAL_ERROR; return INTERNAL_ERROR;
} }

@ -47,6 +47,8 @@ public class JsonRpcErrorConverter {
return JsonRpcError.CHAIN_HEAD_WORLD_STATE_NOT_AVAILABLE; return JsonRpcError.CHAIN_HEAD_WORLD_STATE_NOT_AVAILABLE;
case GAS_PRICE_TOO_LOW: case GAS_PRICE_TOO_LOW:
return JsonRpcError.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: case OFFCHAIN_PRIVACY_GROUP_DOES_NOT_EXIST:
return JsonRpcError.OFFCHAIN_PRIVACY_GROUP_DOES_NOT_EXIST; return JsonRpcError.OFFCHAIN_PRIVACY_GROUP_DOES_NOT_EXIST;
case TRANSACTION_ALREADY_KNOWN: case TRANSACTION_ALREADY_KNOWN:

@ -59,6 +59,7 @@ public enum JsonRpcError {
GAS_PRICE_TOO_LOW(-32009, "Gas price below configured minimum gas price"), GAS_PRICE_TOO_LOW(-32009, "Gas price below configured minimum gas price"),
WRONG_CHAIN_ID(-32000, "Wrong chainId"), WRONG_CHAIN_ID(-32000, "Wrong chainId"),
REPLAY_PROTECTED_SIGNATURES_NOT_SUPPORTED(-32000, "ChainId not supported"), REPLAY_PROTECTED_SIGNATURES_NOT_SUPPORTED(-32000, "ChainId not supported"),
TX_FEECAP_EXCEEDED(-32000, "Transaction fee cap exceeded"),
// Miner failures // Miner failures
COINBASE_NOT_SET(-32010, "Coinbase not set. Unable to start mining without a coinbase"), COINBASE_NOT_SET(-32010, "Coinbase not set. Unable to start mining without a coinbase"),

@ -170,6 +170,12 @@ public class EthSendRawTransactionTest {
TransactionInvalidReason.TX_SENDER_NOT_AUTHORIZED, JsonRpcError.TX_SENDER_NOT_AUTHORIZED); 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( private void verifyErrorForInvalidTransaction(
final TransactionInvalidReason transactionInvalidReason, final JsonRpcError expectedError) { final TransactionInvalidReason transactionInvalidReason, final JsonRpcError expectedError) {
when(transactionPool.addLocalTransaction(any(Transaction.class))) when(transactionPool.addLocalTransaction(any(Transaction.class)))

@ -81,6 +81,7 @@ public interface TransactionValidator {
OFFCHAIN_PRIVACY_GROUP_DOES_NOT_EXIST, OFFCHAIN_PRIVACY_GROUP_DOES_NOT_EXIST,
INCORRECT_PRIVATE_NONCE, INCORRECT_PRIVATE_NONCE,
GAS_PRICE_TOO_LOW, GAS_PRICE_TOO_LOW,
TX_FEECAP_EXCEEDED,
PRIVATE_VALUE_NOT_ZERO, PRIVATE_VALUE_NOT_ZERO,
PRIVATE_UNIMPLEMENTED_TRANSACTION_TYPE; PRIVATE_UNIMPLEMENTED_TRANSACTION_TYPE;
} }

@ -33,7 +33,7 @@ public final class ValidationResult<T> {
} }
public boolean isValid() { public boolean isValid() {
return !invalidReason.isPresent(); return invalidReason.isEmpty();
} }
public T getInvalidReason() throws NoSuchElementException { public T getInvalidReason() throws NoSuchElementException {

@ -83,6 +83,7 @@ public class TransactionPool implements BlockAddedObserver {
TransactionPriceCalculator.frontier(); TransactionPriceCalculator.frontier();
private final TransactionPriceCalculator eip1559PriceCalculator = private final TransactionPriceCalculator eip1559PriceCalculator =
TransactionPriceCalculator.eip1559(); TransactionPriceCalculator.eip1559();
private final TransactionPoolConfiguration configuration;
public TransactionPool( public TransactionPool(
final PendingTransactions pendingTransactions, final PendingTransactions pendingTransactions,
@ -96,7 +97,8 @@ public class TransactionPool implements BlockAddedObserver {
final Optional<PeerPendingTransactionTracker> peerPendingTransactionTracker, final Optional<PeerPendingTransactionTracker> peerPendingTransactionTracker,
final Wei minTransactionGasPrice, final Wei minTransactionGasPrice,
final MetricsSystem metricsSystem, final MetricsSystem metricsSystem,
final Optional<EIP1559> eip1559) { final Optional<EIP1559> eip1559,
final TransactionPoolConfiguration configuration) {
this.pendingTransactions = pendingTransactions; this.pendingTransactions = pendingTransactions;
this.protocolSchedule = protocolSchedule; this.protocolSchedule = protocolSchedule;
this.protocolContext = protocolContext; this.protocolContext = protocolContext;
@ -107,6 +109,7 @@ public class TransactionPool implements BlockAddedObserver {
this.peerPendingTransactionTracker = peerPendingTransactionTracker; this.peerPendingTransactionTracker = peerPendingTransactionTracker;
this.minTransactionGasPrice = minTransactionGasPrice; this.minTransactionGasPrice = minTransactionGasPrice;
this.eip1559 = eip1559; this.eip1559 = eip1559;
this.configuration = configuration;
duplicateTransactionCounter = duplicateTransactionCounter =
metricsSystem.createLabelledCounter( metricsSystem.createLabelledCounter(
@ -152,6 +155,12 @@ public class TransactionPool implements BlockAddedObserver {
if (transactionGasPrice.compareTo(minTransactionGasPrice) < 0) { if (transactionGasPrice.compareTo(minTransactionGasPrice) < 0) {
return ValidationResult.invalid(TransactionInvalidReason.GAS_PRICE_TOO_LOW); 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<TransactionInvalidReason> validationResult = final ValidationResult<TransactionInvalidReason> validationResult =
validateTransaction(transaction); validateTransaction(transaction);
if (validationResult.isValid()) { if (validationResult.isValid()) {

@ -14,6 +14,7 @@
*/ */
package org.hyperledger.besu.ethereum.eth.transactions; package org.hyperledger.besu.ethereum.eth.transactions;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.util.number.Percentage; import org.hyperledger.besu.util.number.Percentage;
import java.util.Objects; import java.util.Objects;
@ -24,25 +25,31 @@ public class TransactionPoolConfiguration {
public static final int MAX_PENDING_TRANSACTIONS_HASHES = 4096; public static final int MAX_PENDING_TRANSACTIONS_HASHES = 4096;
public static final int DEFAULT_TX_RETENTION_HOURS = 13; public static final int DEFAULT_TX_RETENTION_HOURS = 13;
public static final Percentage DEFAULT_PRICE_BUMP = Percentage.fromInt(10); 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 txPoolMaxSize;
private final int pooledTransactionHashesSize; private final int pooledTransactionHashesSize;
private final int pendingTxRetentionPeriod; private final int pendingTxRetentionPeriod;
private final int txMessageKeepAliveSeconds; private final int txMessageKeepAliveSeconds;
private final Percentage priceBump; private final Percentage priceBump;
private final Wei txFeeCap;
public TransactionPoolConfiguration( public TransactionPoolConfiguration(
final int txPoolMaxSize, final int txPoolMaxSize,
final int pooledTransactionHashesSize, final int pooledTransactionHashesSize,
final int pendingTxRetentionPeriod, final int pendingTxRetentionPeriod,
final int txMessageKeepAliveSeconds, final int txMessageKeepAliveSeconds,
final Percentage priceBump) { final Percentage priceBump,
final Wei txFeeCap) {
this.txPoolMaxSize = txPoolMaxSize; this.txPoolMaxSize = txPoolMaxSize;
this.pooledTransactionHashesSize = pooledTransactionHashesSize; this.pooledTransactionHashesSize = pooledTransactionHashesSize;
this.pendingTxRetentionPeriod = pendingTxRetentionPeriod; this.pendingTxRetentionPeriod = pendingTxRetentionPeriod;
this.txMessageKeepAliveSeconds = txMessageKeepAliveSeconds; this.txMessageKeepAliveSeconds = txMessageKeepAliveSeconds;
this.priceBump = priceBump; this.priceBump = priceBump;
this.txFeeCap = txFeeCap;
} }
public int getTxPoolMaxSize() { public int getTxPoolMaxSize() {
@ -65,6 +72,10 @@ public class TransactionPoolConfiguration {
return priceBump; return priceBump;
} }
public Wei getTxFeeCap() {
return txFeeCap;
}
@Override @Override
public boolean equals(final Object o) { public boolean equals(final Object o) {
if (this == o) { if (this == o) {
@ -77,13 +88,14 @@ public class TransactionPoolConfiguration {
return txPoolMaxSize == that.txPoolMaxSize return txPoolMaxSize == that.txPoolMaxSize
&& Objects.equals(pendingTxRetentionPeriod, that.pendingTxRetentionPeriod) && Objects.equals(pendingTxRetentionPeriod, that.pendingTxRetentionPeriod)
&& Objects.equals(txMessageKeepAliveSeconds, that.txMessageKeepAliveSeconds) && Objects.equals(txMessageKeepAliveSeconds, that.txMessageKeepAliveSeconds)
&& Objects.equals(priceBump, that.priceBump); && Objects.equals(priceBump, that.priceBump)
&& Objects.equals(txFeeCap, that.txFeeCap);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash( return Objects.hash(
txPoolMaxSize, pendingTxRetentionPeriod, txMessageKeepAliveSeconds, priceBump); txPoolMaxSize, pendingTxRetentionPeriod, txMessageKeepAliveSeconds, priceBump, txFeeCap);
} }
@Override @Override
@ -97,6 +109,8 @@ public class TransactionPoolConfiguration {
+ txMessageKeepAliveSeconds + txMessageKeepAliveSeconds
+ ", priceBump=" + ", priceBump="
+ priceBump + priceBump
+ ", txFeeCap="
+ txFeeCap
+ '}'; + '}';
} }
@ -110,6 +124,7 @@ public class TransactionPoolConfiguration {
private Integer txMessageKeepAliveSeconds = DEFAULT_TX_MSG_KEEP_ALIVE; private Integer txMessageKeepAliveSeconds = DEFAULT_TX_MSG_KEEP_ALIVE;
private int pooledTransactionHashesSize = MAX_PENDING_TRANSACTIONS_HASHES; private int pooledTransactionHashesSize = MAX_PENDING_TRANSACTIONS_HASHES;
private Percentage priceBump = DEFAULT_PRICE_BUMP; private Percentage priceBump = DEFAULT_PRICE_BUMP;
private Wei txFeeCap = DEFAULT_RPC_TX_FEE_CAP;
public Builder txPoolMaxSize(final int txPoolMaxSize) { public Builder txPoolMaxSize(final int txPoolMaxSize) {
this.txPoolMaxSize = txPoolMaxSize; this.txPoolMaxSize = txPoolMaxSize;
@ -140,13 +155,19 @@ public class TransactionPoolConfiguration {
return priceBump(Percentage.fromInt(priceBump)); return priceBump(Percentage.fromInt(priceBump));
} }
public Builder txFeeCap(final Wei txFeeCap) {
this.txFeeCap = txFeeCap;
return this;
}
public TransactionPoolConfiguration build() { public TransactionPoolConfiguration build() {
return new TransactionPoolConfiguration( return new TransactionPoolConfiguration(
txPoolMaxSize, txPoolMaxSize,
pooledTransactionHashesSize, pooledTransactionHashesSize,
pendingTxRetentionPeriod, pendingTxRetentionPeriod,
txMessageKeepAliveSeconds, txMessageKeepAliveSeconds,
priceBump); priceBump,
txFeeCap);
} }
} }
} }

@ -125,7 +125,8 @@ public class TransactionPoolFactory {
pendingTransactionTracker, pendingTransactionTracker,
minTransactionGasPrice, minTransactionGasPrice,
metricsSystem, metricsSystem,
eip1559); eip1559,
transactionPoolConfiguration);
final TransactionsMessageHandler transactionsMessageHandler = final TransactionsMessageHandler transactionsMessageHandler =
new TransactionsMessageHandler( new TransactionsMessageHandler(
ethContext.getScheduler(), ethContext.getScheduler(),

@ -90,7 +90,12 @@ public class TransactionPoolFactoryTest {
state, state,
Wei.of(1), Wei.of(1),
new TransactionPoolConfiguration( 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, pendingTransactions,
peerTransactionTracker, peerTransactionTracker,
transactionsMessageSender, transactionsMessageSender,
@ -168,7 +173,12 @@ public class TransactionPoolFactoryTest {
state, state,
Wei.of(1), Wei.of(1),
new TransactionPoolConfiguration( 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, pendingTransactions,
peerTransactionTracker, peerTransactionTracker,
transactionsMessageSender, transactionsMessageSender,

@ -148,7 +148,8 @@ public class TransactionPoolTest {
Optional.of(peerPendingTransactionTracker), Optional.of(peerPendingTransactionTracker),
Wei.of(2), Wei.of(2),
metricsSystem, metricsSystem,
Optional.empty()); Optional.empty(),
TransactionPoolConfiguration.DEFAULT);
blockchain.observeBlockAdded(transactionPool); blockchain.observeBlockAdded(transactionPool);
} }
@ -404,7 +405,8 @@ public class TransactionPoolTest {
Optional.of(peerPendingTransactionTracker), Optional.of(peerPendingTransactionTracker),
Wei.ZERO, Wei.ZERO,
metricsSystem, metricsSystem,
Optional.empty()); Optional.empty(),
TransactionPoolConfiguration.DEFAULT);
when(pendingTransactions.containsTransaction(transaction1.getHash())).thenReturn(true); when(pendingTransactions.containsTransaction(transaction1.getHash())).thenReturn(true);
@ -541,7 +543,8 @@ public class TransactionPoolTest {
Optional.of(peerPendingTransactionTracker), Optional.of(peerPendingTransactionTracker),
Wei.ZERO, Wei.ZERO,
metricsSystem, metricsSystem,
Optional.empty()); Optional.empty(),
TransactionPoolConfiguration.DEFAULT);
final TransactionTestFixture builder = new TransactionTestFixture(); final TransactionTestFixture builder = new TransactionTestFixture();
final Transaction transaction1 = builder.nonce(1).createTransaction(KEY_PAIR1); final Transaction transaction1 = builder.nonce(1).createTransaction(KEY_PAIR1);
@ -610,7 +613,8 @@ public class TransactionPoolTest {
Optional.of(peerPendingTransactionTracker), Optional.of(peerPendingTransactionTracker),
Wei.ZERO, Wei.ZERO,
metricsSystem, metricsSystem,
Optional.empty()); Optional.empty(),
TransactionPoolConfiguration.DEFAULT);
final TransactionTestFixture builder = new TransactionTestFixture(); final TransactionTestFixture builder = new TransactionTestFixture();
final Transaction transactionLocal = builder.nonce(1).createTransaction(KEY_PAIR1); final Transaction transactionLocal = builder.nonce(1).createTransaction(KEY_PAIR1);
@ -650,6 +654,75 @@ public class TransactionPoolTest {
.isEqualToComparingFieldByField(expectedValidationParams); .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<TransactionInvalidReason> result =
transactionPool.addLocalTransaction(transactionLocal);
assertThat(result.getInvalidReason()).isEqualTo(TransactionInvalidReason.TX_FEECAP_EXCEEDED);
}
private void assertTransactionPending(final Transaction t) { private void assertTransactionPending(final Transaction t) {
assertThat(transactions.getTransactionByHash(t.getHash())).contains(t); assertThat(transactions.getTransactionByHash(t.getHash())).contains(t);
} }

Loading…
Cancel
Save