Reject locally-sourced transactions below the minimum gas price when not mining (#3397)

* Refactor TransactionPool to accept MiningParameters
* Check for zero GasPrice Frontier Transactions
* if you are not mining your node could fill up with pending transactions.
* make low-or-no-gas transactions viable for local transactions

Signed-off-by: Antony Denyer <git@antonydenyer.co.uk>
Co-authored-by: garyschulte <garyschulte@gmail.com>
pull/3495/head
Antony Denyer 3 years ago committed by GitHub
parent 6cff00234a
commit 889fda9207
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 7
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java
  3. 2
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java
  4. 2
      besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
  5. 2
      besu/src/main/java/org/hyperledger/besu/cli/subcommands/blocks/BlocksSubCommand.java
  6. 2
      besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java
  7. 2
      besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java
  8. 2
      besu/src/main/java/org/hyperledger/besu/controller/TransitionBesuControllerBuilder.java
  9. 2
      besu/src/test/java/org/hyperledger/besu/PrivacyReorgTest.java
  10. 2
      besu/src/test/java/org/hyperledger/besu/PrivacyTest.java
  11. 6
      besu/src/test/java/org/hyperledger/besu/RunnerTest.java
  12. 2
      besu/src/test/java/org/hyperledger/besu/chainexport/RlpBlockExporterTest.java
  13. 5
      besu/src/test/java/org/hyperledger/besu/chainimport/JsonBlockImporterTest.java
  14. 8
      besu/src/test/java/org/hyperledger/besu/chainimport/RlpBlockImporterTest.java
  15. 4
      besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java
  16. 3
      besu/src/test/java/org/hyperledger/besu/services/BesuEventsImplTest.java
  17. 6
      consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java
  18. 2
      consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/support/TestContextBuilder.java
  19. 2
      consensus/qbft/src/integration-test/java/org/hyperledger/besu/consensus/qbft/support/TestContextBuilder.java
  20. 3
      ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/frontier/EthGetFilterChangesIntegrationTest.java
  21. 3
      ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/london/EthGetFilterChangesIntegrationTest.java
  22. 26
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/MiningParameters.java
  23. 8
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java
  24. 2
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/feemarket/FeeMarket.java
  25. 5
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/feemarket/LegacyFeeMarket.java
  26. 11
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/feemarket/LondonFeeMarket.java
  27. 2
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TransactionTestFixture.java
  28. 38
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java
  29. 40
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java
  30. 10
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactory.java
  31. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTest.java
  32. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/ethtaskutils/AbstractMessageTaskTest.java
  33. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TestNode.java
  34. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactoryTest.java
  35. 78
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolTest.java
  36. 3
      ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java
  37. 2
      ethereum/stratum/src/test/java/org/hyperledger/besu/ethereum/stratum/GetWorkProtocolTest.java

@ -17,6 +17,7 @@
- QBFT consensus algorithm is production ready
### Bug Fixes
- Reject locally-sourced transactions below the minimum gas price when not mining. [#3397](https://github.com/hyperledger/besu/pull/3397)
## 22.1.0

@ -52,7 +52,7 @@ public class BesuNodeConfigurationBuilder {
private Optional<Path> dataPath = Optional.empty();
private MiningParameters miningParameters =
new MiningParameters.Builder()
.enabled(false)
.miningEnabled(false)
.coinbase(AddressHelpers.ofValue(1))
.minTransactionGasPrice(Wei.of(1000))
.build();
@ -108,7 +108,10 @@ public class BesuNodeConfigurationBuilder {
public BesuNodeConfigurationBuilder miningEnabled(final boolean enabled) {
this.miningParameters =
new MiningParameters.Builder().enabled(enabled).coinbase(AddressHelpers.ofValue(1)).build();
new MiningParameters.Builder()
.miningEnabled(enabled)
.coinbase(AddressHelpers.ofValue(1))
.build();
this.jsonRpcConfiguration.addRpcApi(RpcApis.MINER.name());
return this;
}

@ -279,7 +279,7 @@ public class BesuNodeFactory {
new MiningParameters.Builder()
.minTransactionGasPrice(Wei.ZERO)
.coinbase(AddressHelpers.ofValue(1))
.enabled(true)
.miningEnabled(true)
.build();
return create(

@ -1908,7 +1908,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.targetGasLimit(targetGasLimit)
.minTransactionGasPrice(minTransactionGasPrice)
.extraData(extraData)
.enabled(isMiningEnabled)
.miningEnabled(isMiningEnabled)
.stratumMiningEnabled(iStratumMiningEnabled)
.stratumNetworkInterface(stratumNetworkInterface)
.stratumPort(stratumPort)

@ -263,7 +263,7 @@ public class BlocksSubCommand implements Runnable {
.coinbase(coinbase)
.minTransactionGasPrice(minTransactionGasPrice)
.extraData(extraData)
.enabled(false)
.miningEnabled(false)
.stratumMiningEnabled(false)
.stratumNetworkInterface("0.0.0.0")
.stratumPort(8008)

@ -339,7 +339,7 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
clock,
metricsSystem,
syncState,
miningParameters.getMinTransactionGasPrice(),
miningParameters,
transactionPoolConfiguration);
final List<PeerValidator> peerValidators = createPeerValidators(protocolSchedule);

@ -148,6 +148,6 @@ public class CliqueBesuControllerBuilder extends BesuControllerBuilder {
@Override
public MiningParameters getMiningParameterOverrides(final MiningParameters fromCli) {
// Clique mines by default, reflect that with in the mining parameters:
return new MiningParameters.Builder(fromCli).enabled(true).build();
return new MiningParameters.Builder(fromCli).miningEnabled(true).build();
}
}

@ -92,7 +92,7 @@ public class TransitionBesuControllerBuilder extends BesuControllerBuilder {
tps.getPreMergeSchedule(),
protocolContext,
transactionPool,
transitionMiningParameters,
new MiningParameters.Builder(miningParameters).miningEnabled(false).build(),
syncState,
ethProtocolManager),
mergeBesuControllerBuilder.createMiningCoordinator(

@ -196,7 +196,7 @@ public class PrivacyReorgTest {
.miningParameters(
new MiningParameters.Builder()
.minTransactionGasPrice(Wei.of(1000))
.enabled(false)
.miningEnabled(false)
.build())
.nodeKey(NodeKeyUtils.generate())
.metricsSystem(new NoOpMetricsSystem())

@ -111,7 +111,7 @@ public class PrivacyTest {
.ethProtocolConfiguration(EthProtocolConfiguration.defaultConfig())
.storageProvider(new InMemoryKeyValueStorageProvider())
.networkId(BigInteger.ONE)
.miningParameters(new MiningParameters.Builder().enabled(false).build())
.miningParameters(new MiningParameters.Builder().miningEnabled(false).build())
.nodeKey(NodeKeyUtils.generate())
.metricsSystem(new NoOpMetricsSystem())
.dataDirectory(dataDir)

@ -166,7 +166,7 @@ public final class RunnerTest {
.ethProtocolConfiguration(EthProtocolConfiguration.defaultConfig())
.dataDirectory(dataDirAhead)
.networkId(networkId)
.miningParameters(new MiningParameters.Builder().enabled(false).build())
.miningParameters(new MiningParameters.Builder().miningEnabled(false).build())
.nodeKey(aheadDbNodeKey)
.metricsSystem(noOpMetricsSystem)
.privacyParameters(PrivacyParameters.DEFAULT)
@ -187,7 +187,7 @@ public final class RunnerTest {
.ethProtocolConfiguration(EthProtocolConfiguration.defaultConfig())
.dataDirectory(dataDirAhead)
.networkId(networkId)
.miningParameters(new MiningParameters.Builder().enabled(false).build())
.miningParameters(new MiningParameters.Builder().miningEnabled(false).build())
.nodeKey(aheadDbNodeKey)
.metricsSystem(noOpMetricsSystem)
.privacyParameters(PrivacyParameters.DEFAULT)
@ -257,7 +257,7 @@ public final class RunnerTest {
.ethProtocolConfiguration(EthProtocolConfiguration.defaultConfig())
.dataDirectory(dataDirBehind)
.networkId(networkId)
.miningParameters(new MiningParameters.Builder().enabled(false).build())
.miningParameters(new MiningParameters.Builder().miningEnabled(false).build())
.nodeKey(NodeKeyUtils.generate())
.storageProvider(new InMemoryKeyValueStorageProvider())
.metricsSystem(noOpMetricsSystem)

@ -88,7 +88,7 @@ public final class RlpBlockExporterTest {
.ethProtocolConfiguration(EthProtocolConfiguration.defaultConfig())
.storageProvider(new InMemoryKeyValueStorageProvider())
.networkId(BigInteger.ONE)
.miningParameters(new MiningParameters.Builder().enabled(false).build())
.miningParameters(new MiningParameters.Builder().miningEnabled(false).build())
.nodeKey(NodeKeyUtils.generate())
.metricsSystem(new NoOpMetricsSystem())
.privacyParameters(PrivacyParameters.DEFAULT)

@ -419,7 +419,10 @@ public abstract class JsonBlockImporterTest {
.storageProvider(new InMemoryKeyValueStorageProvider())
.networkId(BigInteger.valueOf(10))
.miningParameters(
new MiningParameters.Builder().minTransactionGasPrice(Wei.ZERO).enabled(true).build())
new MiningParameters.Builder()
.minTransactionGasPrice(Wei.ZERO)
.miningEnabled(true)
.build())
.nodeKey(NodeKeyUtils.generate())
.metricsSystem(new NoOpMetricsSystem())
.privacyParameters(PrivacyParameters.DEFAULT)

@ -68,7 +68,7 @@ public final class RlpBlockImporterTest {
.ethProtocolConfiguration(EthProtocolConfiguration.defaultConfig())
.storageProvider(new InMemoryKeyValueStorageProvider())
.networkId(BigInteger.ONE)
.miningParameters(new MiningParameters.Builder().enabled(false).build())
.miningParameters(new MiningParameters.Builder().miningEnabled(false).build())
.nodeKey(NodeKeyUtils.generate())
.metricsSystem(new NoOpMetricsSystem())
.privacyParameters(PrivacyParameters.DEFAULT)
@ -97,7 +97,7 @@ public final class RlpBlockImporterTest {
.ethProtocolConfiguration(EthProtocolConfiguration.defaultConfig())
.storageProvider(new InMemoryKeyValueStorageProvider())
.networkId(BigInteger.ONE)
.miningParameters(new MiningParameters.Builder().enabled(false).build())
.miningParameters(new MiningParameters.Builder().miningEnabled(false).build())
.nodeKey(NodeKeyUtils.generate())
.metricsSystem(new NoOpMetricsSystem())
.privacyParameters(PrivacyParameters.DEFAULT)
@ -126,7 +126,7 @@ public final class RlpBlockImporterTest {
.ethProtocolConfiguration(EthProtocolConfiguration.defaultConfig())
.storageProvider(new InMemoryKeyValueStorageProvider())
.networkId(BigInteger.ONE)
.miningParameters(new MiningParameters.Builder().enabled(false).build())
.miningParameters(new MiningParameters.Builder().miningEnabled(false).build())
.nodeKey(NodeKeyUtils.generate())
.metricsSystem(new NoOpMetricsSystem())
.privacyParameters(PrivacyParameters.DEFAULT)
@ -167,7 +167,7 @@ public final class RlpBlockImporterTest {
.ethProtocolConfiguration(EthProtocolConfiguration.defaultConfig())
.storageProvider(new InMemoryKeyValueStorageProvider())
.networkId(BigInteger.valueOf(10))
.miningParameters(new MiningParameters.Builder().enabled(false).build())
.miningParameters(new MiningParameters.Builder().miningEnabled(false).build())
.nodeKey(NodeKeyUtils.generate())
.metricsSystem(new NoOpMetricsSystem())
.privacyParameters(PrivacyParameters.DEFAULT)

@ -878,7 +878,7 @@ public class BesuCommandTest extends CommandTestAbstract {
.coinbase(Address.fromHexString(expectedCoinbase))
.minTransactionGasPrice(DefaultCommandValues.DEFAULT_MIN_TRANSACTION_GAS_PRICE)
.extraData(DefaultCommandValues.DEFAULT_EXTRA_DATA)
.enabled(false)
.miningEnabled(false)
.build());
}
@ -895,7 +895,7 @@ public class BesuCommandTest extends CommandTestAbstract {
.coinbase(Address.fromHexString(expectedCoinbase))
.minTransactionGasPrice(DefaultCommandValues.DEFAULT_MIN_TRANSACTION_GAS_PRICE)
.extraData(DefaultCommandValues.DEFAULT_EXTRA_DATA)
.enabled(false)
.miningEnabled(false)
.build());
}

@ -33,6 +33,7 @@ import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
@ -146,7 +147,7 @@ public class BesuEventsImplTest {
TestClock.fixed(),
new NoOpMetricsSystem(),
syncState,
Wei.ZERO,
new MiningParameters.Builder().minTransactionGasPrice(Wei.ZERO).build(),
txPoolConfig);
serviceImpl = new BesuEventsImpl(blockchain, blockBroadcaster, transactionPool, syncState);

@ -106,7 +106,7 @@ public class CliqueMinerExecutorTest {
.coinbase(AddressHelpers.ofValue(1))
.minTransactionGasPrice(Wei.ZERO)
.extraData(vanityData)
.enabled(false)
.miningEnabled(false)
.build(),
mock(CliqueBlockScheduler.class),
new EpochManager(EPOCH_LENGTH));
@ -151,7 +151,7 @@ public class CliqueMinerExecutorTest {
.coinbase(AddressHelpers.ofValue(1))
.minTransactionGasPrice(Wei.ZERO)
.extraData(vanityData)
.enabled(false)
.miningEnabled(false)
.build(),
mock(CliqueBlockScheduler.class),
new EpochManager(EPOCH_LENGTH));
@ -196,7 +196,7 @@ public class CliqueMinerExecutorTest {
.coinbase(AddressHelpers.ofValue(1))
.minTransactionGasPrice(Wei.ZERO)
.extraData(initialVanityData)
.enabled(false)
.miningEnabled(false)
.build(),
mock(CliqueBlockScheduler.class),
new EpochManager(EPOCH_LENGTH));

@ -300,7 +300,7 @@ public class TestContextBuilder {
.coinbase(AddressHelpers.ofValue(1))
.minTransactionGasPrice(Wei.ZERO)
.extraData(Bytes.wrap("Ibft Int tests".getBytes(UTF_8)))
.enabled(true)
.miningEnabled(true)
.build();
final StubGenesisConfigOptions genesisConfigOptions = new StubGenesisConfigOptions();

@ -386,7 +386,7 @@ public class TestContextBuilder {
.coinbase(AddressHelpers.ofValue(1))
.minTransactionGasPrice(Wei.ZERO)
.extraData(Bytes.wrap("Qbft Int tests".getBytes(UTF_8)))
.enabled(true)
.miningEnabled(true)
.build();
final StubGenesisConfigOptions genesisConfigOptions = new StubGenesisConfigOptions();

@ -43,6 +43,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.ExecutionContextTestFixture;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
@ -125,7 +126,7 @@ public class EthGetFilterChangesIntegrationTest {
ethContext,
peerTransactionTracker,
peerPendingTransactionTracker,
Wei.ZERO,
new MiningParameters.Builder().minTransactionGasPrice(Wei.ZERO).build(),
metricsSystem,
TransactionPoolConfiguration.DEFAULT);
final BlockchainQueries blockchainQueries =

@ -43,6 +43,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.ExecutionContextTestFixture;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
@ -125,7 +126,7 @@ public class EthGetFilterChangesIntegrationTest {
ethContext,
peerTransactionTracker,
peerPendingTransactionTracker,
Wei.ZERO,
new MiningParameters.Builder().minTransactionGasPrice(Wei.ZERO).build(),
metricsSystem,
TransactionPoolConfiguration.DEFAULT);
final BlockchainQueries blockchainQueries =

@ -38,7 +38,7 @@ public class MiningParameters {
private final Optional<AtomicLong> targetGasLimit;
private final Wei minTransactionGasPrice;
private final Bytes extraData;
private final boolean enabled;
private final boolean miningEnabled;
private final boolean stratumMiningEnabled;
private final String stratumNetworkInterface;
private final int stratumPort;
@ -55,7 +55,7 @@ public class MiningParameters {
final Long targetGasLimit,
final Wei minTransactionGasPrice,
final Bytes extraData,
final boolean enabled,
final boolean miningEnabled,
final boolean stratumMiningEnabled,
final String stratumNetworkInterface,
final int stratumPort,
@ -70,7 +70,7 @@ public class MiningParameters {
this.targetGasLimit = Optional.ofNullable(targetGasLimit).map(AtomicLong::new);
this.minTransactionGasPrice = minTransactionGasPrice;
this.extraData = extraData;
this.enabled = enabled;
this.miningEnabled = miningEnabled;
this.stratumMiningEnabled = stratumMiningEnabled;
this.stratumNetworkInterface = stratumNetworkInterface;
this.stratumPort = stratumPort;
@ -100,7 +100,7 @@ public class MiningParameters {
}
public boolean isMiningEnabled() {
return enabled;
return miningEnabled;
}
public boolean isStratumMiningEnabled() {
@ -153,7 +153,7 @@ public class MiningParameters {
&& Objects.equals(targetGasLimit, that.targetGasLimit)
&& Objects.equals(minTransactionGasPrice, that.minTransactionGasPrice)
&& Objects.equals(extraData, that.extraData)
&& enabled == that.enabled
&& miningEnabled == that.miningEnabled
&& stratumMiningEnabled == that.stratumMiningEnabled
&& Objects.equals(stratumNetworkInterface, that.stratumNetworkInterface)
&& Objects.equals(stratumExtranonce, that.stratumExtranonce)
@ -170,7 +170,7 @@ public class MiningParameters {
targetGasLimit,
minTransactionGasPrice,
extraData,
enabled,
miningEnabled,
stratumMiningEnabled,
stratumNetworkInterface,
stratumPort,
@ -192,8 +192,8 @@ public class MiningParameters {
+ minTransactionGasPrice
+ ", extraData="
+ extraData
+ ", enabled="
+ enabled
+ ", miningEnabled="
+ miningEnabled
+ ", stratumMiningEnabled="
+ stratumMiningEnabled
+ ", stratumNetworkInterface='"
@ -223,7 +223,7 @@ public class MiningParameters {
private Long targetGasLimit = null;
private Wei minTransactionGasPrice = Wei.ZERO;
private Bytes extraData = Bytes.EMPTY;
private boolean enabled = false;
private boolean miningEnabled = false;
private boolean stratumMiningEnabled = false;
private String stratumNetworkInterface = "0.0.0.0";
private int stratumPort = 8008;
@ -247,7 +247,7 @@ public class MiningParameters {
.ifPresent(gasLimit -> this.targetGasLimit = gasLimit);
this.minTransactionGasPrice = existing.getMinTransactionGasPrice();
this.extraData = existing.getExtraData();
this.enabled = existing.isMiningEnabled();
this.miningEnabled = existing.isMiningEnabled();
this.stratumMiningEnabled = existing.isStratumMiningEnabled();
this.stratumNetworkInterface = existing.getStratumNetworkInterface();
this.stratumPort = existing.getStratumPort();
@ -280,8 +280,8 @@ public class MiningParameters {
return this;
}
public Builder enabled(final boolean enabled) {
this.enabled = enabled;
public Builder miningEnabled(final boolean miningEnabled) {
this.miningEnabled = miningEnabled;
return this;
}
@ -341,7 +341,7 @@ public class MiningParameters {
targetGasLimit,
minTransactionGasPrice,
extraData,
enabled,
miningEnabled,
stratumMiningEnabled,
stratumNetworkInterface,
stratumPort,

@ -151,6 +151,14 @@ public class MainnetTransactionValidator {
}
}
// transactionValidationParams.isAllowExceedingBalance() is used on eth_call
if (!feeMarket.satisfiesFloorTxCost(transaction)
&& !transactionValidationParams.isAllowExceedingBalance()) {
return ValidationResult.invalid(
TransactionInvalidReason.INVALID_TRANSACTION_FORMAT,
"effective gas price is too low to execute");
}
final Gas intrinsicGasCost =
gasCalculator
.transactionIntrinsicGasCost(transaction.getPayload(), transaction.isContractCreation())

@ -32,6 +32,8 @@ public interface FeeMarket {
Wei minTransactionPriceInNextBlock(
Transaction transaction, Supplier<Optional<Wei>> baseFeeSupplier);
boolean satisfiesFloorTxCost(Transaction txn);
static BaseFeeMarket london(final long londonForkBlockNumber) {
return london(londonForkBlockNumber, Optional.empty());
}

@ -39,4 +39,9 @@ public class LegacyFeeMarket implements FeeMarket {
final Transaction transaction, final Supplier<Optional<Wei>> baseFeeSupplier) {
return txPriceCalculator.price(transaction, Optional.empty());
}
@Override
public boolean satisfiesFloorTxCost(final Transaction txn) {
return true;
}
}

@ -79,6 +79,17 @@ public class LondonFeeMarket implements BaseFeeMarket {
return this.getTransactionPriceCalculator().price(transaction, minBaseFeeInNextBlock);
}
@Override
public boolean satisfiesFloorTxCost(final Transaction txn) {
// London fee market arithmetic never allows for a base fee below 7 wei
// ensure effective baseFee is at least 7 wei
return txn.getGasPrice()
.map(Optional::of)
.orElse(txn.getMaxFeePerGas())
.filter(fee -> fee.greaterOrEqualThan(Wei.of(7L)))
.isPresent();
}
@Override
public Wei computeBaseFee(
final long blockNumber,

@ -30,7 +30,7 @@ public class TransactionTestFixture {
private long nonce = 0;
private Wei gasPrice = Wei.of(5);
private Wei gasPrice = Wei.of(5000);
private long gasLimit = 5000;

@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.mainnet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.GAS_PRICE_MUST_BE_ZERO;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.INVALID_TRANSACTION_FORMAT;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.MAX_PRIORITY_FEE_PER_GAS_EXCEEDS_MAX_FEE_PER_GAS;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE;
import static org.mockito.ArgumentMatchers.any;
@ -282,6 +283,39 @@ public class MainnetTransactionValidatorTest {
.isEqualTo("max priority fee per gas cannot be greater than max fee per gas");
}
@Test
public void shouldRejectTransactionWhenEffectiveGasPriceIsTooLow() {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator,
FeeMarket.london(0L),
false,
Optional.of(BigInteger.ONE),
Set.of(TransactionType.values()),
defaultGoQuorumCompatibilityMode);
validator.setTransactionFilter(transactionFilter(true));
final Transaction transaction =
Transaction.builder()
.type(TransactionType.EIP1559)
.nonce(0)
.maxPriorityFeePerGas(Wei.ZERO)
.maxFeePerGas(Wei.of(4))
.gasLimit(15)
.to(Address.ZERO)
.value(Wei.of(0))
.payload(Bytes.EMPTY)
.chainId(BigInteger.ONE)
.signAndBuild(new SECP256K1().generateKeyPair());
final ValidationResult<TransactionInvalidReason> validationResult =
validator.validate(transaction, Optional.of(Wei.ONE), transactionValidationParams);
assertThat(validationResult).isEqualTo(ValidationResult.invalid(INVALID_TRANSACTION_FORMAT));
assertThat(validationResult.getErrorMessage())
.isEqualTo("effective gas price is too low to execute");
}
@Test
public void shouldPropagateCorrectStateChangeParamToTransactionFilter() {
final ArgumentCaptor<Boolean> stateChangeLocalParamCaptor =
@ -363,7 +397,7 @@ public class MainnetTransactionValidatorTest {
assertThat(
frontierValidator.validate(transaction, Optional.empty(), transactionValidationParams))
.isEqualTo(ValidationResult.invalid(TransactionInvalidReason.INVALID_TRANSACTION_FORMAT));
.isEqualTo(ValidationResult.invalid(INVALID_TRANSACTION_FORMAT));
when(gasCalculator.transactionIntrinsicGasCost(any(), anyBoolean())).thenReturn(Gas.of(0));
@ -392,7 +426,7 @@ public class MainnetTransactionValidatorTest {
.createTransaction(senderKeys);
final Optional<Wei> basefee = Optional.of(Wei.of(150000L));
assertThat(validator.validate(transaction, basefee, transactionValidationParams))
.isEqualTo(ValidationResult.invalid(TransactionInvalidReason.INVALID_TRANSACTION_FORMAT));
.isEqualTo(ValidationResult.invalid(INVALID_TRANSACTION_FORMAT));
}
@Test

@ -26,6 +26,7 @@ import org.hyperledger.besu.ethereum.chain.BlockAddedEvent;
import org.hyperledger.besu.ethereum.chain.BlockAddedObserver;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.EthProtocol;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
@ -37,6 +38,7 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionValidator;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.metrics.BesuMetricCategory;
@ -73,7 +75,7 @@ public class TransactionPool implements BlockAddedObserver {
private final TransactionBatchAddedListener transactionBatchAddedListener;
private final TransactionBatchAddedListener pendingTransactionBatchAddedListener;
private final SyncState syncState;
private final Wei minTransactionGasPrice;
private final MiningParameters miningParameters;
private final LabelledMetric<Counter> duplicateTransactionCounter;
private final PeerTransactionTracker peerTransactionTracker;
private final PeerPendingTransactionTracker peerPendingTransactionTracker;
@ -89,7 +91,7 @@ public class TransactionPool implements BlockAddedObserver {
final EthContext ethContext,
final PeerTransactionTracker peerTransactionTracker,
final PeerPendingTransactionTracker peerPendingTransactionTracker,
final Wei minTransactionGasPrice,
final MiningParameters miningParameters,
final MetricsSystem metricsSystem,
final TransactionPoolConfiguration configuration) {
this.pendingTransactions = pendingTransactions;
@ -100,7 +102,7 @@ public class TransactionPool implements BlockAddedObserver {
this.syncState = syncState;
this.peerTransactionTracker = peerTransactionTracker;
this.peerPendingTransactionTracker = peerPendingTransactionTracker;
this.minTransactionGasPrice = minTransactionGasPrice;
this.miningParameters = miningParameters;
this.configuration = configuration;
duplicateTransactionCounter =
@ -152,6 +154,15 @@ public class TransactionPool implements BlockAddedObserver {
return validationResult;
}
private boolean effectiveGasPriceIsAboveConfiguredMinGasPrice(final Transaction transaction) {
return transaction
.getGasPrice()
.map(Optional::of)
.orElse(transaction.getMaxFeePerGas())
.map(g -> g.greaterOrEqualThan(miningParameters.getMinTransactionGasPrice()))
.orElse(false);
}
public void addRemoteTransactions(final Collection<Transaction> transactions) {
if (!syncState.isInSync(SYNC_TOLERANCE)) {
return;
@ -165,7 +176,7 @@ public class TransactionPool implements BlockAddedObserver {
continue;
}
final Wei transactionGasPrice = minTransactionGasPrice(transaction);
if (transactionGasPrice.compareTo(minTransactionGasPrice) < 0) {
if (transactionGasPrice.compareTo(miningParameters.getMinTransactionGasPrice()) < 0) {
continue;
}
final ValidationResult<TransactionInvalidReason> validationResult =
@ -235,6 +246,8 @@ public class TransactionPool implements BlockAddedObserver {
private ValidationResult<TransactionInvalidReason> validateTransaction(
final Transaction transaction, final boolean isLocal) {
final BlockHeader chainHeadBlockHeader = getChainHeadBlockHeader();
final FeeMarket feeMarket =
protocolSchedule.getByBlockNumber(chainHeadBlockHeader.getNumber()).getFeeMarket();
// Check whether it's a GoQuorum transaction
boolean goQuorumCompatibilityMode = getTransactionValidator().getGoQuorumCompatibilityMode();
@ -245,6 +258,14 @@ public class TransactionPool implements BlockAddedObserver {
}
}
// allow local transactions to be below minGas as long as we are mining and the transaction is
// executable:
if ((!effectiveGasPriceIsAboveConfiguredMinGasPrice(transaction)
&& !miningParameters.isMiningEnabled())
|| (!feeMarket.satisfiesFloorTxCost(transaction))) {
return ValidationResult.invalid(TransactionInvalidReason.GAS_PRICE_TOO_LOW);
}
final ValidationResult<TransactionInvalidReason> basicValidationResult =
getTransactionValidator()
.validate(
@ -268,13 +289,10 @@ public class TransactionPool implements BlockAddedObserver {
"Transaction gas limit of %s exceeds block gas limit of %s",
transaction.getGasLimit(), chainHeadBlockHeader.getGasLimit()));
}
if (transaction.getType().equals(TransactionType.EIP1559)) {
final long blknum = chainHeadBlockHeader.getNumber();
if (!protocolSchedule.getByBlockNumber(blknum).getFeeMarket().implementsBaseFee()) {
return ValidationResult.invalid(
TransactionInvalidReason.INVALID_TRANSACTION_FORMAT,
"EIP-1559 transaction are not allowed yet");
}
if (transaction.getType().equals(TransactionType.EIP1559) && !feeMarket.implementsBaseFee()) {
return ValidationResult.invalid(
TransactionInvalidReason.INVALID_TRANSACTION_FORMAT,
"EIP-1559 transaction are not allowed yet");
}
return protocolContext

@ -14,8 +14,8 @@
*/
package org.hyperledger.besu.ethereum.eth.transactions;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.messages.EthPV62;
import org.hyperledger.besu.ethereum.eth.messages.EthPV65;
@ -40,7 +40,7 @@ public class TransactionPoolFactory {
final Clock clock,
final MetricsSystem metricsSystem,
final SyncState syncState,
final Wei minTransactionGasPrice,
final MiningParameters miningParameters,
final TransactionPoolConfiguration transactionPoolConfiguration) {
final AbstractPendingTransactionsSorter pendingTransactions =
@ -62,7 +62,7 @@ public class TransactionPoolFactory {
ethContext,
metricsSystem,
syncState,
minTransactionGasPrice,
miningParameters,
transactionPoolConfiguration,
pendingTransactions,
transactionTracker,
@ -77,7 +77,7 @@ public class TransactionPoolFactory {
final EthContext ethContext,
final MetricsSystem metricsSystem,
final SyncState syncState,
final Wei minTransactionGasPrice,
final MiningParameters miningParameters,
final TransactionPoolConfiguration transactionPoolConfiguration,
final AbstractPendingTransactionsSorter pendingTransactions,
final PeerTransactionTracker transactionTracker,
@ -96,7 +96,7 @@ public class TransactionPoolFactory {
ethContext,
transactionTracker,
pendingTransactionTracker,
minTransactionGasPrice,
miningParameters,
metricsSystem,
transactionPoolConfiguration);
final TransactionsMessageHandler transactionsMessageHandler =

@ -35,6 +35,7 @@ import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.ProtocolScheduleFixture;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
@ -1025,7 +1026,7 @@ public final class EthProtocolManagerTest {
TestClock.fixed(),
metricsSystem,
mock(SyncState.class),
Wei.ZERO,
new MiningParameters.Builder().minTransactionGasPrice(Wei.ZERO).build(),
TransactionPoolConfiguration.DEFAULT);
// Send just a transaction message.

@ -21,6 +21,7 @@ import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.eth.EthProtocol;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.DeterministicEthScheduler;
@ -99,7 +100,7 @@ public abstract class AbstractMessageTaskTest<T, R> {
TestClock.fixed(),
metricsSystem,
syncState,
Wei.of(1),
new MiningParameters.Builder().minTransactionGasPrice(Wei.ONE).build(),
TransactionPoolConfiguration.DEFAULT);
ethProtocolManager =
EthProtocolManagerTestUtil.create(

@ -32,6 +32,7 @@ import org.hyperledger.besu.ethereum.chain.GenesisState;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions;
import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.difficulty.fixed.FixedDifficultyProtocolSchedule;
import org.hyperledger.besu.ethereum.eth.EthProtocol;
@ -136,7 +137,7 @@ public class TestNode implements Closeable {
TestClock.fixed(),
metricsSystem,
syncState,
Wei.ZERO,
new MiningParameters.Builder().minTransactionGasPrice(Wei.ZERO).build(),
TransactionPoolConfiguration.DEFAULT);
final EthProtocolManager ethProtocolManager =

@ -26,6 +26,7 @@ import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
@ -81,7 +82,7 @@ public class TransactionPoolFactoryTest {
ethContext,
new NoOpMetricsSystem(),
state,
Wei.of(1),
new MiningParameters.Builder().minTransactionGasPrice(Wei.ONE).build(),
ImmutableTransactionPoolConfiguration.of(
1,
1,

@ -21,7 +21,6 @@ import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.mainnet.ValidationResult.valid;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.CHAIN_HEAD_WORLD_STATE_NOT_AVAILABLE;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.EXCEEDS_BLOCK_GAS_LIMIT;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.NONCE_TOO_LOW;
import static org.mockito.ArgumentMatchers.any;
@ -48,6 +47,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.ExecutionContextTestFixture;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
@ -100,6 +100,7 @@ public class TransactionPoolTest {
@Mock private PendingTransactionListener listener;
@Mock private TransactionPool.TransactionBatchAddedListener batchAddedListener;
@Mock private TransactionPool.TransactionBatchAddedListener pendingBatchAddedListener;
@Mock private MiningParameters miningParameters;
@SuppressWarnings("unchecked")
@Mock
@ -150,6 +151,7 @@ public class TransactionPoolTest {
peerPendingTransactionTracker = mock(PeerPendingTransactionTracker.class);
transactionPool = createTransactionPool();
blockchain.observeBlockAdded(transactionPool);
when(miningParameters.getMinTransactionGasPrice()).thenReturn(Wei.of(2));
}
private TransactionPool createTransactionPool() {
@ -173,7 +175,7 @@ public class TransactionPoolTest {
ethContext,
peerTransactionTracker,
peerPendingTransactionTracker,
Wei.of(2),
miningParameters,
metricsSystem,
config);
}
@ -428,24 +430,6 @@ public class TransactionPoolTest {
verifyNoInteractions(transactionValidator); // Reject before validation
}
@Test
public void shouldNotRejectLocalTransactionsWhenGasPriceBelowMinimum() {
final Transaction transaction =
new TransactionTestFixture()
.nonce(1)
.gasLimit(0)
.gasPrice(Wei.of(1))
.createTransaction(KEY_PAIR1);
when(transactionValidator.validate(eq(transaction), any(Optional.class), any()))
.thenReturn(ValidationResult.valid());
final ValidationResult<TransactionInvalidReason> result =
transactionPool.addLocalTransaction(transaction);
assertThat(result).isEqualTo(ValidationResult.invalid(CHAIN_HEAD_WORLD_STATE_NOT_AVAILABLE));
}
@Test
public void shouldNotAddRemoteTransactionsThatAreInvalidAccordingToInvariantChecks() {
givenTransactionIsValid(transaction2);
@ -551,7 +535,7 @@ public class TransactionPoolTest {
ethContext,
peerTransactionTracker,
peerPendingTransactionTracker,
Wei.ZERO,
new MiningParameters.Builder().minTransactionGasPrice(Wei.ZERO).build(),
metricsSystem,
TransactionPoolConfiguration.DEFAULT);
@ -690,7 +674,7 @@ public class TransactionPoolTest {
ethContext,
peerTransactionTracker,
peerPendingTransactionTracker,
Wei.ZERO,
new MiningParameters.Builder().minTransactionGasPrice(Wei.ZERO).build(),
metricsSystem,
TransactionPoolConfiguration.DEFAULT);
@ -749,7 +733,7 @@ public class TransactionPoolTest {
ethContext,
peerTransactionTracker,
peerPendingTransactionTracker,
Wei.ZERO,
new MiningParameters.Builder().minTransactionGasPrice(Wei.ZERO).build(),
metricsSystem,
TransactionPoolConfiguration.DEFAULT);
@ -811,7 +795,7 @@ public class TransactionPoolTest {
ethContext,
peerTransactionTracker,
peerPendingTransactionTracker,
Wei.ZERO,
new MiningParameters.Builder().minTransactionGasPrice(Wei.ZERO).build(),
metricsSystem,
ImmutableTransactionPoolConfiguration.builder().txFeeCap(Wei.ZERO).build());
when(transactionValidator.validate(any(Transaction.class), any(Optional.class), any()))
@ -848,7 +832,7 @@ public class TransactionPoolTest {
ethContext,
peerTransactionTracker,
peerPendingTransactionTracker,
Wei.ZERO,
new MiningParameters.Builder().minTransactionGasPrice(Wei.ZERO).build(),
metricsSystem,
ImmutableTransactionPoolConfiguration.builder().txFeeCap(Wei.ONE).build());
when(transactionValidator.validate(any(Transaction.class), any(Optional.class), any()))
@ -885,7 +869,7 @@ public class TransactionPoolTest {
ethContext,
peerTransactionTracker,
peerPendingTransactionTracker,
Wei.ZERO,
new MiningParameters.Builder().minTransactionGasPrice(Wei.ZERO).build(),
metricsSystem,
ImmutableTransactionPoolConfiguration.builder().txFeeCap(Wei.ONE).build());
// pre-London feemarket
@ -925,7 +909,7 @@ public class TransactionPoolTest {
ethContext,
peerTransactionTracker,
peerPendingTransactionTracker,
Wei.ZERO,
new MiningParameters.Builder().minTransactionGasPrice(Wei.ZERO).build(),
metricsSystem,
ImmutableTransactionPoolConfiguration.builder().txFeeCap(twoEthers).build());
@ -966,7 +950,7 @@ public class TransactionPoolTest {
ethContext,
peerTransactionTracker,
peerPendingTransactionTracker,
Wei.ZERO,
new MiningParameters.Builder().minTransactionGasPrice(Wei.ZERO).build(),
metricsSystem,
ImmutableTransactionPoolConfiguration.builder().txFeeCap(twoEthers).build());
@ -986,6 +970,44 @@ public class TransactionPoolTest {
.isEqualTo(TransactionInvalidReason.ETHER_VALUE_NOT_SUPPORTED);
}
@Test
public void shouldAcceptZeroGasPriceFrontierTransactionsWhenMining() {
when(miningParameters.isMiningEnabled()).thenReturn(true);
final Transaction transaction =
new TransactionTestFixture()
.type(TransactionType.FRONTIER)
.gasPrice(Wei.ZERO)
.value(Wei.ONE)
.chainId(Optional.of(BigInteger.ONE))
.createTransaction(KEY_PAIR1);
givenTransactionIsValid(transaction);
final ValidationResult<TransactionInvalidReason> result =
transactionPool.addLocalTransaction(transaction);
assertThat(result).isEqualTo(ValidationResult.valid());
}
@Test
public void shouldRejectZeroGasPriceFrontierTransactionsWhenNotMining() {
when(miningParameters.isMiningEnabled()).thenReturn(false);
final Transaction transaction =
new TransactionTestFixture()
.type(TransactionType.FRONTIER)
.gasPrice(Wei.ZERO)
.createTransaction(KEY_PAIR1);
givenTransactionIsValid(transaction);
final ValidationResult<TransactionInvalidReason> result =
transactionPool.addLocalTransaction(transaction);
assertThat(result.getInvalidReason()).isEqualTo(TransactionInvalidReason.GAS_PRICE_TOO_LOW);
}
private void assertTransactionPending(final Transaction t) {
assertThat(transactions.getTransactionByHash(t.getHash())).contains(t);
}

@ -31,6 +31,7 @@ import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
@ -206,7 +207,7 @@ public class RetestethContext {
retestethClock,
metricsSystem,
syncState,
Wei.ZERO,
new MiningParameters.Builder().minTransactionGasPrice(Wei.ZERO).build(),
transactionPoolConfiguration);
if (LOG.isTraceEnabled()) {

@ -100,7 +100,7 @@ public class GetWorkProtocolTest {
.coinbase(Address.wrap(Bytes.random(20)))
.minTransactionGasPrice(Wei.of(1))
.extraData(Bytes.fromHexString("0xc0ffee"))
.enabled(true)
.miningEnabled(true)
.build());
AtomicReference<String> messageRef = new AtomicReference<>();
StratumConnection connection =

Loading…
Cancel
Save