From 1758102a30fff242f3555901dd1493f5a160ecfb Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Wed, 22 May 2024 08:35:35 +0200 Subject: [PATCH] Make `eth_gasPrice` aware of the base fee market (#7102) Signed-off-by: Fabio Di Fabio Signed-off-by: Justin Florentine --- CHANGELOG.md | 1 + .../org/hyperledger/besu/RunnerBuilder.java | 19 +- .../org/hyperledger/besu/cli/BesuCommand.java | 44 ++- .../stable/ApiConfigurationOptions.java | 9 +- .../controller/BesuControllerBuilder.java | 8 +- .../CliqueBesuControllerBuilder.java | 6 +- ...onsensusScheduleBesuControllerBuilder.java | 6 +- .../controller/IbftBesuControllerBuilder.java | 6 +- .../controller/QbftBesuControllerBuilder.java | 9 +- .../QbftBesuControllerBuilderTest.java | 7 +- .../besu/services/TraceServiceImplTest.java | 8 +- .../clique/jsonrpc/CliqueJsonRpcMethods.java | 17 +- .../ibft/jsonrpc/IbftJsonRpcMethods.java | 18 +- .../qbft/jsonrpc/QbftJsonRpcMethods.java | 21 +- .../jsonrpc/JsonRpcTestMethodsFactory.java | 18 +- .../EthGetFilterChangesIntegrationTest.java | 7 +- .../EthGetFilterChangesIntegrationTest.java | 7 +- .../besu/ethereum/api/ApiConfiguration.java | 12 +- .../api/graphql/GraphQLDataFetchers.java | 10 +- .../jsonrpc/internal/methods/EthGasPrice.java | 43 +-- .../jsonrpc/methods/EthJsonRpcMethods.java | 2 +- .../ethereum/api/query/BlockchainQueries.java | 132 +++++-- .../AbstractEthGraphQLHttpServiceTest.java | 15 +- .../api/graphql/GraphQLHttpServiceTest.java | 2 +- .../AbstractJsonRpcHttpServiceTest.java | 5 +- .../internal/methods/EthGasPriceTest.java | 354 ++++++++++++------ .../methods/EthGetBlockByNumberTest.java | 8 +- .../methods/EthGetBlockReceiptsTest.java | 6 +- .../internal/methods/EthGetProofTest.java | 8 +- .../methods/EthGetTransactionReceiptTest.java | 8 +- ...ewBlockHeadersSubscriptionServiceTest.java | 12 +- .../query/BlockchainQueriesLogCacheTest.java | 10 +- .../api/query/BlockchainQueriesTest.java | 24 +- .../ethereum/api/graphql/eth_gasPrice.json | 2 +- .../ethereum/retesteth/RetestethContext.java | 4 +- 35 files changed, 594 insertions(+), 274 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 303ec891e1e..5fe0cbbc3c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ ### Bug fixes - Fix parsing `gasLimit` parameter when its value is > `Long.MAX_VALUE` [#7116](https://github.com/hyperledger/besu/pull/7116) +- Make `eth_gasPrice` aware of the base fee market [#7102](https://github.com/hyperledger/besu/pull/7102) - Skip validation of withdrawals when importing BFT blocks since withdrawals don't apply to BFT chains [#7115](https://github.com/hyperledger/besu/pull/7115) ## 24.5.1 diff --git a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java index 90e0b6368a6..eb734ba606a 100644 --- a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java @@ -106,7 +106,6 @@ import org.hyperledger.besu.ethereum.storage.StorageProvider; import org.hyperledger.besu.ethereum.stratum.StratumServer; import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; -import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.ethstats.EthStatsService; import org.hyperledger.besu.ethstats.util.EthStatsConnectOptions; import org.hyperledger.besu.metrics.MetricsService; @@ -725,14 +724,17 @@ public Runner build() { final TransactionPool transactionPool = besuController.getTransactionPool(); final MiningCoordinator miningCoordinator = besuController.getMiningCoordinator(); + final MiningParameters miningParameters = besuController.getMiningParameters(); final BlockchainQueries blockchainQueries = new BlockchainQueries( + protocolSchedule, context.getBlockchain(), context.getWorldStateArchive(), Optional.of(dataDir.resolve(CACHE_PATH)), Optional.of(besuController.getProtocolManager().ethContext().getScheduler()), - apiConfiguration); + apiConfiguration, + miningParameters); final PrivacyParameters privacyParameters = besuController.getPrivacyParameters(); @@ -749,7 +751,6 @@ public Runner build() { final P2PNetwork peerNetwork = networkRunner.getNetwork(); - final MiningParameters miningParameters = besuController.getMiningParameters(); Optional stratumServer = Optional.empty(); if (miningParameters.isStratumMiningEnabled()) { @@ -957,10 +958,7 @@ public Runner build() { rpcEndpointServiceImpl); createLogsSubscriptionService( - context.getBlockchain(), - context.getWorldStateArchive(), - subscriptionManager, - privacyParameters); + context.getBlockchain(), subscriptionManager, privacyParameters, blockchainQueries); createNewBlockHeadersSubscriptionService( context.getBlockchain(), blockchainQueries, subscriptionManager); @@ -1273,15 +1271,12 @@ private SubscriptionManager createSubscriptionManager( private void createLogsSubscriptionService( final Blockchain blockchain, - final WorldStateArchive worldStateArchive, final SubscriptionManager subscriptionManager, - final PrivacyParameters privacyParameters) { + final PrivacyParameters privacyParameters, + final BlockchainQueries blockchainQueries) { Optional privacyQueries = Optional.empty(); if (privacyParameters.isEnabled()) { - final BlockchainQueries blockchainQueries = - new BlockchainQueries( - blockchain, worldStateArchive, Optional.empty(), Optional.empty(), apiConfiguration); privacyQueries = Optional.of( new PrivacyQueries( 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 07058883bb0..ccbc619b5ac 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -327,6 +327,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable { Suppliers.memoize(this::readGenesisConfigFile); private final Supplier genesisConfigOptionsSupplier = Suppliers.memoize(this::readGenesisConfigOptions); + private final Supplier miningParametersSupplier = + Suppliers.memoize(this::getMiningParameters); private RocksDBPlugin rocksDBPlugin; @@ -898,7 +900,6 @@ static class MetricsOptionGroup { private Collection staticNodes; private BesuController besuController; private BesuConfigurationImpl pluginCommonConfiguration; - private MiningParameters miningParameters; private BesuComponent besuComponent; private final Supplier metricsSystem = @@ -1351,8 +1352,10 @@ private void startPlugins() { TraceService.class, new TraceServiceImpl( new BlockchainQueries( + besuController.getProtocolSchedule(), besuController.getProtocolContext().getBlockchain(), - besuController.getProtocolContext().getWorldStateArchive()), + besuController.getProtocolContext().getWorldStateArchive(), + miningParametersSupplier.get()), besuController.getProtocolSchedule())); besuController.getAdditionalPluginServices().appendPluginServices(besuPluginContext); @@ -1374,7 +1377,7 @@ private void validatePluginOptions() { "--privacy-marker-transaction-signing-key-file can not be used in conjunction with a plugin that specifies a PrivateMarkerTransactionFactory"); } - if (Wei.ZERO.compareTo(getMiningParameters().getMinTransactionGasPrice()) < 0 + if (Wei.ZERO.compareTo(miningParametersSupplier.get().getMinTransactionGasPrice()) < 0 && (privacyOptionGroup.privateMarkerTransactionSigningKeyPath == null && (privacyPluginService == null || privacyPluginService.getPrivateMarkerTransactionFactory() == null))) { @@ -1755,7 +1758,7 @@ private void configure() throws Exception { unstableIpcOptions.isEnabled(), unstableIpcOptions.getIpcPath(), unstableIpcOptions.getRpcIpcApis()); - apiConfiguration = apiConfigurationOptions.apiConfiguration(getMiningParameters()); + apiConfiguration = apiConfigurationOptions.apiConfiguration(); dataStorageConfiguration = getDataStorageConfiguration(); // hostsWhitelist is a hidden option. If it is specified, add the list to hostAllowlist if (!hostsWhitelist.isEmpty()) { @@ -1844,7 +1847,7 @@ public BesuControllerBuilder getControllerBuilder() { dataDir(), dataDir().resolve(DATABASE_PATH), getDataStorageConfiguration(), - getMiningParameters()); + miningParametersSupplier.get()); final KeyValueStorageProvider storageProvider = keyValueStorageProvider(keyValueStorageName); return controllerBuilderFactory .fromEthNetworkConfig(updateNetworkConfig(network), getDefaultSyncModeIfNotSet()) @@ -1853,7 +1856,7 @@ public BesuControllerBuilder getControllerBuilder() { .networkConfiguration(unstableNetworkingOptions.toDomainObject()) .dataDirectory(dataDir()) .dataStorageConfiguration(getDataStorageConfiguration()) - .miningParameters(getMiningParameters()) + .miningParameters(miningParametersSupplier.get()) .transactionPoolConfiguration(buildTransactionPoolConfiguration()) .nodeKey(new NodeKey(securityModule())) .metricsSystem(metricsSystem.get()) @@ -1864,7 +1867,7 @@ public BesuControllerBuilder getControllerBuilder() { .isRevertReasonEnabled(isRevertReasonEnabled) .storageProvider(storageProvider) .gasLimitCalculator( - getMiningParameters().getTargetGasLimit().isPresent() + miningParametersSupplier.get().getTargetGasLimit().isPresent() ? new FrontierTargetingGasLimitCalculator() : GasLimitCalculator.constant()) .requiredBlocks(requiredBlocks) @@ -2157,14 +2160,17 @@ private TransactionPoolConfiguration buildTransactionPoolConfiguration() { txPoolConfBuilder.priceBump(Percentage.ZERO); } - if (getMiningParameters().getMinTransactionGasPrice().equals(Wei.ZERO) + if (miningParametersSupplier.get().getMinTransactionGasPrice().equals(Wei.ZERO) && !transactionPoolOptions.isPriceBumpSet(commandLine)) { logger.info( "Forcing price bump for transaction replacement to 0, since min-gas-price is set to 0"); txPoolConfBuilder.priceBump(Percentage.ZERO); } - if (getMiningParameters().getMinTransactionGasPrice().lessThan(txPoolConf.getMinGasPrice())) { + if (miningParametersSupplier + .get() + .getMinTransactionGasPrice() + .lessThan(txPoolConf.getMinGasPrice())) { if (transactionPoolOptions.isMinGasPriceSet(commandLine)) { throw new ParameterException( commandLine, "tx-pool-min-gas-price cannot be greater than the value of min-gas-price"); @@ -2175,9 +2181,9 @@ private TransactionPoolConfiguration buildTransactionPoolConfiguration() { // the user of the change logger.warn( "Forcing tx-pool-min-gas-price=" - + getMiningParameters().getMinTransactionGasPrice().toDecimalString() + + miningParametersSupplier.get().getMinTransactionGasPrice().toDecimalString() + ", since it cannot be greater than the value of min-gas-price"); - txPoolConfBuilder.minGasPrice(getMiningParameters().getMinTransactionGasPrice()); + txPoolConfBuilder.minGasPrice(miningParametersSupplier.get().getMinTransactionGasPrice()); } } @@ -2185,13 +2191,11 @@ private TransactionPoolConfiguration buildTransactionPoolConfiguration() { } private MiningParameters getMiningParameters() { - if (miningParameters == null) { - miningOptions.setTransactionSelectionService(transactionSelectionServiceImpl); - miningParameters = miningOptions.toDomainObject(); - getGenesisBlockPeriodSeconds(genesisConfigOptionsSupplier.get()) - .ifPresent(miningParameters::setBlockPeriodSeconds); - initMiningParametersMetrics(miningParameters); - } + miningOptions.setTransactionSelectionService(transactionSelectionServiceImpl); + final var miningParameters = miningOptions.toDomainObject(); + getGenesisBlockPeriodSeconds(genesisConfigOptionsSupplier.get()) + .ifPresent(miningParameters::setBlockPeriodSeconds); + initMiningParametersMetrics(miningParameters); return miningParameters; } @@ -2567,8 +2571,8 @@ private List getEffectivePorts() { effectivePorts, metricsOptionGroup.metricsPort, metricsOptionGroup.isMetricsEnabled); addPortIfEnabled( effectivePorts, - getMiningParameters().getStratumPort(), - getMiningParameters().isStratumMiningEnabled()); + miningParametersSupplier.get().getStratumPort(), + miningParametersSupplier.get().isStratumMiningEnabled()); return effectivePorts; } diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/ApiConfigurationOptions.java b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/ApiConfigurationOptions.java index 5fa2756c853..d9882382769 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/ApiConfigurationOptions.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/ApiConfigurationOptions.java @@ -17,9 +17,9 @@ import static java.util.Arrays.asList; import org.hyperledger.besu.cli.util.CommandLineUtils; +import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.api.ApiConfiguration; import org.hyperledger.besu.ethereum.api.ImmutableApiConfiguration; -import org.hyperledger.besu.ethereum.core.MiningParameters; import org.slf4j.Logger; import picocli.CommandLine; @@ -119,17 +119,14 @@ private void checkApiOptionsDependencies(final CommandLine commandLine, final Lo /** * Creates an ApiConfiguration based on the provided options. * - * @param miningParameters The mining parameters * @return An ApiConfiguration instance */ - public ApiConfiguration apiConfiguration(final MiningParameters miningParameters) { + public ApiConfiguration apiConfiguration() { var builder = ImmutableApiConfiguration.builder() .gasPriceBlocks(apiGasPriceBlocks) .gasPricePercentile(apiGasPricePercentile) - .gasPriceMinSupplier( - miningParameters.getMinTransactionGasPrice().getAsBigInteger()::longValueExact) - .gasPriceMax(apiGasPriceMax) + .gasPriceMax(Wei.of(apiGasPriceMax)) .maxLogsRange(rpcMaxLogsRange) .gasCap(rpcGasCap) .isGasAndPriorityFeeLimitingEnabled(apiGasAndPriorityFeeLimitingEnabled) diff --git a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java index 86a3db25c3a..d801528cf55 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java @@ -731,7 +731,7 @@ public BesuController build() { createSubProtocolConfiguration(ethProtocolManager, maybeSnapProtocolManager); final JsonRpcMethods additionalJsonRpcMethodFactory = - createAdditionalJsonRpcMethodFactory(protocolContext); + createAdditionalJsonRpcMethodFactory(protocolContext, protocolSchedule, miningParameters); if (dataStorageConfiguration.getUnstable().getBonsaiLimitTrieLogsEnabled() && DataStorageFormat.BONSAI.equals(dataStorageConfiguration.getDataStorageFormat())) { @@ -884,10 +884,14 @@ protected void prepForBuild() {} * Create additional json rpc method factory json rpc methods. * * @param protocolContext the protocol context + * @param protocolSchedule the protocol schedule + * @param miningParameters the mining parameters * @return the json rpc methods */ protected JsonRpcMethods createAdditionalJsonRpcMethodFactory( - final ProtocolContext protocolContext) { + final ProtocolContext protocolContext, + final ProtocolSchedule protocolSchedule, + final MiningParameters miningParameters) { return apis -> Collections.emptyMap(); } diff --git a/besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java index 6b180af2442..c1274240694 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java @@ -72,8 +72,10 @@ protected void prepForBuild() { @Override protected JsonRpcMethods createAdditionalJsonRpcMethodFactory( - final ProtocolContext protocolContext) { - return new CliqueJsonRpcMethods(protocolContext); + final ProtocolContext protocolContext, + final ProtocolSchedule protocolSchedule, + final MiningParameters miningParameters) { + return new CliqueJsonRpcMethods(protocolContext, protocolSchedule, miningParameters); } @Override diff --git a/besu/src/main/java/org/hyperledger/besu/controller/ConsensusScheduleBesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/ConsensusScheduleBesuControllerBuilder.java index e172115f8dc..d138c96a24e 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/ConsensusScheduleBesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/ConsensusScheduleBesuControllerBuilder.java @@ -204,10 +204,12 @@ protected PluginServiceFactory createAdditionalPluginServices( @Override protected JsonRpcMethods createAdditionalJsonRpcMethodFactory( - final ProtocolContext protocolContext) { + final ProtocolContext protocolContext, + final ProtocolSchedule protocolSchedule, + final MiningParameters miningParameters) { return besuControllerBuilderSchedule .get(0L) - .createAdditionalJsonRpcMethodFactory(protocolContext); + .createAdditionalJsonRpcMethodFactory(protocolContext, protocolSchedule, miningParameters); } @Override diff --git a/besu/src/main/java/org/hyperledger/besu/controller/IbftBesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/IbftBesuControllerBuilder.java index e9e69611f11..473549e0bd7 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/IbftBesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/IbftBesuControllerBuilder.java @@ -111,8 +111,10 @@ protected void prepForBuild() { @Override protected JsonRpcMethods createAdditionalJsonRpcMethodFactory( - final ProtocolContext protocolContext) { - return new IbftJsonRpcMethods(protocolContext); + final ProtocolContext protocolContext, + final ProtocolSchedule protocolSchedule, + final MiningParameters miningParameters) { + return new IbftJsonRpcMethods(protocolContext, protocolSchedule, miningParameters); } @Override diff --git a/besu/src/main/java/org/hyperledger/besu/controller/QbftBesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/QbftBesuControllerBuilder.java index 8b0f5900ac3..030173acb0b 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/QbftBesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/QbftBesuControllerBuilder.java @@ -130,10 +130,15 @@ protected void prepForBuild() { @Override protected JsonRpcMethods createAdditionalJsonRpcMethodFactory( - final ProtocolContext protocolContext) { + final ProtocolContext protocolContext, + final ProtocolSchedule protocolSchedule, + final MiningParameters miningParameters) { return new QbftJsonRpcMethods( - protocolContext, createReadOnlyValidatorProvider(protocolContext.getBlockchain())); + protocolContext, + protocolSchedule, + miningParameters, + createReadOnlyValidatorProvider(protocolContext.getBlockchain())); } private ValidatorProvider createReadOnlyValidatorProvider(final Blockchain blockchain) { diff --git a/besu/src/test/java/org/hyperledger/besu/controller/QbftBesuControllerBuilderTest.java b/besu/src/test/java/org/hyperledger/besu/controller/QbftBesuControllerBuilderTest.java index 1d3c6ad0113..3b88ae258b7 100644 --- a/besu/src/test/java/org/hyperledger/besu/controller/QbftBesuControllerBuilderTest.java +++ b/besu/src/test/java/org/hyperledger/besu/controller/QbftBesuControllerBuilderTest.java @@ -32,6 +32,8 @@ import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.chain.MutableBlockchain; import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions; +import org.hyperledger.besu.ethereum.core.MiningParameters; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import java.util.List; @@ -99,10 +101,13 @@ public void forkingValidatorProviderIsAvailableOnBftContext() { @Test public void missingTransactionValidatorProviderThrowsError() { final ProtocolContext protocolContext = mock(ProtocolContext.class); + final ProtocolSchedule protocolSchedule = mock(ProtocolSchedule.class); when(protocolContext.getBlockchain()).thenReturn(mock(MutableBlockchain.class)); assertThatThrownBy( - () -> bftBesuControllerBuilder.createAdditionalJsonRpcMethodFactory(protocolContext)) + () -> + bftBesuControllerBuilder.createAdditionalJsonRpcMethodFactory( + protocolContext, protocolSchedule, MiningParameters.newDefault())) .isInstanceOf(NullPointerException.class) .hasMessage("transactionValidatorProvider should have been initialised"); } diff --git a/besu/src/test/java/org/hyperledger/besu/services/TraceServiceImplTest.java b/besu/src/test/java/org/hyperledger/besu/services/TraceServiceImplTest.java index 0e339f2f4f3..f7db3639ae1 100644 --- a/besu/src/test/java/org/hyperledger/besu/services/TraceServiceImplTest.java +++ b/besu/src/test/java/org/hyperledger/besu/services/TraceServiceImplTest.java @@ -31,6 +31,7 @@ import org.hyperledger.besu.ethereum.chain.MutableBlockchain; import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil; +import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.evm.log.Log; import org.hyperledger.besu.evm.worldstate.WorldView; @@ -74,7 +75,12 @@ public void setup() { blockchainSetupUtil.importAllBlocks(); blockchain = blockchainSetupUtil.getBlockchain(); worldStateArchive = blockchainSetupUtil.getWorldArchive(); - blockchainQueries = new BlockchainQueries(blockchain, worldStateArchive); + blockchainQueries = + new BlockchainQueries( + blockchainSetupUtil.getProtocolSchedule(), + blockchain, + worldStateArchive, + MiningParameters.newDefault()); traceService = new TraceServiceImpl(blockchainQueries, blockchainSetupUtil.getProtocolSchedule()); } diff --git a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/jsonrpc/CliqueJsonRpcMethods.java b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/jsonrpc/CliqueJsonRpcMethods.java index 1f929e77e7c..762cce1c569 100644 --- a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/jsonrpc/CliqueJsonRpcMethods.java +++ b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/jsonrpc/CliqueJsonRpcMethods.java @@ -31,6 +31,8 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.methods.ApiGroupJsonRpcMethods; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.hyperledger.besu.ethereum.chain.MutableBlockchain; +import org.hyperledger.besu.ethereum.core.MiningParameters; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import java.util.Map; @@ -38,14 +40,23 @@ /** The Clique json rpc methods. */ public class CliqueJsonRpcMethods extends ApiGroupJsonRpcMethods { private final ProtocolContext context; + private final ProtocolSchedule protocolSchedule; + private final MiningParameters miningParameters; /** * Instantiates a new Clique json rpc methods. * - * @param context the context + * @param context the protocol context + * @param protocolSchedule the protocol schedule + * @param miningParameters the mining parameters */ - public CliqueJsonRpcMethods(final ProtocolContext context) { + public CliqueJsonRpcMethods( + final ProtocolContext context, + final ProtocolSchedule protocolSchedule, + final MiningParameters miningParameters) { this.context = context; + this.protocolSchedule = protocolSchedule; + this.miningParameters = miningParameters; } @Override @@ -58,7 +69,7 @@ protected Map create() { final MutableBlockchain blockchain = context.getBlockchain(); final WorldStateArchive worldStateArchive = context.getWorldStateArchive(); final BlockchainQueries blockchainQueries = - new BlockchainQueries(blockchain, worldStateArchive); + new BlockchainQueries(protocolSchedule, blockchain, worldStateArchive, miningParameters); final ValidatorProvider validatorProvider = context.getConsensusContext(CliqueContext.class).getValidatorProvider(); diff --git a/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/jsonrpc/IbftJsonRpcMethods.java b/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/jsonrpc/IbftJsonRpcMethods.java index 13ccf4214a4..1afe067867d 100644 --- a/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/jsonrpc/IbftJsonRpcMethods.java +++ b/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/jsonrpc/IbftJsonRpcMethods.java @@ -32,6 +32,8 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.methods.ApiGroupJsonRpcMethods; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.hyperledger.besu.ethereum.chain.MutableBlockchain; +import org.hyperledger.besu.ethereum.core.MiningParameters; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import java.util.Map; @@ -39,14 +41,23 @@ public class IbftJsonRpcMethods extends ApiGroupJsonRpcMethods { private final ProtocolContext context; + private final ProtocolSchedule protocolSchedule; + private final MiningParameters miningParameters; /** * Instantiates a new Ibft json rpc methods. * - * @param context the context + * @param context the protocol context + * @param protocolSchedule the protocol schedule + * @param miningParameters the mining parameters */ - public IbftJsonRpcMethods(final ProtocolContext context) { + public IbftJsonRpcMethods( + final ProtocolContext context, + final ProtocolSchedule protocolSchedule, + final MiningParameters miningParameters) { this.context = context; + this.protocolSchedule = protocolSchedule; + this.miningParameters = miningParameters; } @Override @@ -58,7 +69,8 @@ protected String getApiGroup() { protected Map create() { final MutableBlockchain blockchain = context.getBlockchain(); final BlockchainQueries blockchainQueries = - new BlockchainQueries(blockchain, context.getWorldStateArchive()); + new BlockchainQueries( + protocolSchedule, blockchain, context.getWorldStateArchive(), miningParameters); final BftContext bftContext = context.getConsensusContext(BftContext.class); final BlockInterface blockInterface = bftContext.getBlockInterface(); final ValidatorProvider validatorProvider = diff --git a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/jsonrpc/QbftJsonRpcMethods.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/jsonrpc/QbftJsonRpcMethods.java index 47b10bc3ed4..366d1c560da 100644 --- a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/jsonrpc/QbftJsonRpcMethods.java +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/jsonrpc/QbftJsonRpcMethods.java @@ -28,6 +28,8 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.methods.ApiGroupJsonRpcMethods; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; +import org.hyperledger.besu.ethereum.core.MiningParameters; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import java.util.Map; @@ -36,17 +38,26 @@ public class QbftJsonRpcMethods extends ApiGroupJsonRpcMethods { private final ProtocolContext context; private final ValidatorProvider readOnlyValidatorProvider; + private final ProtocolSchedule protocolSchedule; + private final MiningParameters miningParameters; /** * Instantiates a new Qbft json rpc methods. * - * @param context the context + * @param context the protocol context + * @param protocolSchedule the protocol schedule + * @param miningParameters the mining parameters * @param readOnlyValidatorProvider the read only validator provider */ public QbftJsonRpcMethods( - final ProtocolContext context, final ValidatorProvider readOnlyValidatorProvider) { + final ProtocolContext context, + final ProtocolSchedule protocolSchedule, + final MiningParameters miningParameters, + final ValidatorProvider readOnlyValidatorProvider) { this.context = context; this.readOnlyValidatorProvider = readOnlyValidatorProvider; + this.protocolSchedule = protocolSchedule; + this.miningParameters = miningParameters; } @Override @@ -57,7 +68,11 @@ protected String getApiGroup() { @Override protected Map create() { final BlockchainQueries blockchainQueries = - new BlockchainQueries(context.getBlockchain(), context.getWorldStateArchive()); + new BlockchainQueries( + protocolSchedule, + context.getBlockchain(), + context.getWorldStateArchive(), + miningParameters); final BftContext bftContext = context.getConsensusContext(BftContext.class); final BlockInterface blockInterface = bftContext.getBlockInterface(); final ValidatorProvider validatorProvider = bftContext.getValidatorProvider(); diff --git a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcTestMethodsFactory.java b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcTestMethodsFactory.java index d31a12e0f68..a8226a6a697 100644 --- a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcTestMethodsFactory.java +++ b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcTestMethodsFactory.java @@ -89,7 +89,9 @@ public JsonRpcTestMethodsFactory(final BlockchainImporter importer) { final BlockImporter blockImporter = protocolSpec.getBlockImporter(); blockImporter.importBlock(context, block, HeaderValidationMode.FULL); } - this.blockchainQueries = new BlockchainQueries(blockchain, stateArchive); + this.blockchainQueries = + new BlockchainQueries( + protocolSchedule, blockchain, stateArchive, MiningParameters.newDefault()); } public JsonRpcTestMethodsFactory( @@ -101,7 +103,12 @@ public JsonRpcTestMethodsFactory( this.blockchain = blockchain; this.stateArchive = stateArchive; this.context = context; - this.blockchainQueries = new BlockchainQueries(blockchain, stateArchive); + this.blockchainQueries = + new BlockchainQueries( + importer.getProtocolSchedule(), + blockchain, + stateArchive, + MiningParameters.newDefault()); this.synchronizer = mock(Synchronizer.class); } @@ -116,7 +123,12 @@ public JsonRpcTestMethodsFactory( this.stateArchive = stateArchive; this.context = context; this.synchronizer = synchronizer; - this.blockchainQueries = new BlockchainQueries(blockchain, stateArchive); + this.blockchainQueries = + new BlockchainQueries( + importer.getProtocolSchedule(), + blockchain, + stateArchive, + MiningParameters.newDefault()); } public BlockchainQueries getBlockchainQueries() { diff --git a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/frontier/EthGetFilterChangesIntegrationTest.java b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/frontier/EthGetFilterChangesIntegrationTest.java index 64e0155287c..16a25d3500a 100644 --- a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/frontier/EthGetFilterChangesIntegrationTest.java +++ b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/frontier/EthGetFilterChangesIntegrationTest.java @@ -45,6 +45,7 @@ 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; @@ -123,7 +124,11 @@ public void setUp() { TransactionPoolConfiguration.DEFAULT); transactionPool.setEnabled(); final BlockchainQueries blockchainQueries = - new BlockchainQueries(blockchain, protocolContext.getWorldStateArchive()); + new BlockchainQueries( + executionContext.getProtocolSchedule(), + blockchain, + protocolContext.getWorldStateArchive(), + MiningParameters.newDefault()); filterManager = new FilterManagerBuilder() .blockchainQueries(blockchainQueries) diff --git a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/london/EthGetFilterChangesIntegrationTest.java b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/london/EthGetFilterChangesIntegrationTest.java index 57612545aa7..e88d377f7f0 100644 --- a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/london/EthGetFilterChangesIntegrationTest.java +++ b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/london/EthGetFilterChangesIntegrationTest.java @@ -45,6 +45,7 @@ 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; @@ -123,7 +124,11 @@ public void setUp() { TransactionPoolConfiguration.DEFAULT); transactionPool.setEnabled(); final BlockchainQueries blockchainQueries = - new BlockchainQueries(blockchain, protocolContext.getWorldStateArchive()); + new BlockchainQueries( + executionContext.getProtocolSchedule(), + blockchain, + protocolContext.getWorldStateArchive(), + MiningParameters.newDefault()); filterManager = new FilterManagerBuilder() .blockchainQueries(blockchainQueries) diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/ApiConfiguration.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/ApiConfiguration.java index 66663ce4fd8..334b716900e 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/ApiConfiguration.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/ApiConfiguration.java @@ -14,7 +14,7 @@ */ package org.hyperledger.besu.ethereum.api; -import java.util.function.LongSupplier; +import org.hyperledger.besu.datatypes.Wei; import org.immutables.value.Value; @@ -36,14 +36,8 @@ public double getGasPricePercentile() { } @Value.Default - @Value.Auxiliary - public LongSupplier getGasPriceMinSupplier() { - return () -> 1_000_000_000L; // 1 GWei - } - - @Value.Default - public long getGasPriceMax() { - return 500_000_000_000L; // 500 GWei + public Wei getGasPriceMax() { + return Wei.of(500_000_000_000L); // 500 GWei } @Value.Derived diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetchers.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetchers.java index fccb0cb0110..312491f5c36 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetchers.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetchers.java @@ -31,7 +31,6 @@ import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.hyperledger.besu.ethereum.api.query.LogsQuery; import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata; -import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator; import org.hyperledger.besu.ethereum.core.LogWithMetadata; import org.hyperledger.besu.ethereum.core.Synchronizer; import org.hyperledger.besu.ethereum.core.Transaction; @@ -117,17 +116,12 @@ DataFetcher> getPendingStateDataFetcher() { }; } - DataFetcher> getGasPriceDataFetcher() { + DataFetcher getGasPriceDataFetcher() { return dataFetchingEnvironment -> { final GraphQLContext graphQLContext = dataFetchingEnvironment.getGraphQlContext(); final BlockchainQueries blockchainQueries = graphQLContext.get(GraphQLContextType.BLOCKCHAIN_QUERIES); - final MiningCoordinator miningCoordinator = - graphQLContext.get(GraphQLContextType.MINING_COORDINATOR); - return blockchainQueries - .gasPrice() - .map(Wei::of) - .or(() -> Optional.of(miningCoordinator.getMinTransactionGasPrice())); + return blockchainQueries.gasPrice(); }; } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGasPrice.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGasPrice.java index 4913f70ede1..1b65de17f1b 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGasPrice.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGasPrice.java @@ -22,30 +22,15 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; -import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator; - -import java.util.Optional; -import java.util.function.Supplier; public class EthGasPrice implements JsonRpcMethod { - private final Supplier blockchain; - private final MiningCoordinator miningCoordinator; + private final BlockchainQueries blockchainQueries; private final ApiConfiguration apiConfiguration; public EthGasPrice( - final BlockchainQueries blockchain, - final MiningCoordinator miningCoordinator, - final ApiConfiguration apiConfiguration) { - this(() -> blockchain, miningCoordinator, apiConfiguration); - } - - public EthGasPrice( - final Supplier blockchain, - final MiningCoordinator miningCoordinator, - final ApiConfiguration apiConfiguration) { - this.blockchain = blockchain; - this.miningCoordinator = miningCoordinator; + final BlockchainQueries blockchainQueries, final ApiConfiguration apiConfiguration) { + this.blockchainQueries = blockchainQueries; this.apiConfiguration = apiConfiguration; } @@ -61,30 +46,26 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { } private Wei calculateGasPrice() { - Wei gasPrice = getGasPrice().orElseGet(miningCoordinator::getMinTransactionGasPrice); + final Wei gasPrice = blockchainQueries.gasPrice(); return isGasPriceLimitingEnabled() ? limitGasPrice(gasPrice) : gasPrice; } - private Optional getGasPrice() { - return blockchain.get().gasPrice().map(Wei::of); - } - private boolean isGasPriceLimitingEnabled() { return apiConfiguration.isGasAndPriorityFeeLimitingEnabled(); } private Wei limitGasPrice(final Wei gasPrice) { - Wei minTransactionGasPrice = miningCoordinator.getMinTransactionGasPrice(); - Wei lowerBound = + final Wei lowerBoundGasPrice = blockchainQueries.gasPriceLowerBound(); + final Wei forcedLowerBound = calculateBound( - minTransactionGasPrice, apiConfiguration.getLowerBoundGasAndPriorityFeeCoefficient()); - Wei upperBound = + lowerBoundGasPrice, apiConfiguration.getLowerBoundGasAndPriorityFeeCoefficient()); + final Wei forcedUpperBound = calculateBound( - minTransactionGasPrice, apiConfiguration.getUpperBoundGasAndPriorityFeeCoefficient()); + lowerBoundGasPrice, apiConfiguration.getUpperBoundGasAndPriorityFeeCoefficient()); - return gasPrice.compareTo(lowerBound) <= 0 - ? lowerBound - : gasPrice.compareTo(upperBound) >= 0 ? upperBound : gasPrice; + return gasPrice.compareTo(forcedLowerBound) <= 0 + ? forcedLowerBound + : gasPrice.compareTo(forcedUpperBound) >= 0 ? forcedUpperBound : gasPrice; } private Wei calculateBound(final Wei price, final long coefficient) { diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthJsonRpcMethods.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthJsonRpcMethods.java index c84b6897174..678df31824e 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthJsonRpcMethods.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthJsonRpcMethods.java @@ -175,7 +175,7 @@ protected Map create() { new EthMining(miningCoordinator), new EthCoinbase(miningCoordinator), new EthProtocolVersion(supportedCapabilities), - new EthGasPrice(blockchainQueries, miningCoordinator, apiConfiguration), + new EthGasPrice(blockchainQueries, apiConfiguration), new EthGetWork(miningCoordinator), new EthSubmitWork(miningCoordinator), new EthHashrate(miningCoordinator), diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java index d5228de45e0..16b683ed712 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java @@ -30,12 +30,15 @@ import org.hyperledger.besu.ethereum.core.BlockBody; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.LogWithMetadata; +import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.TransactionReceipt; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; +import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket; +import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.log.LogsBloomFilter; @@ -60,49 +63,77 @@ import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt256; +import org.apache.tuweni.units.bigints.UInt256s; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class BlockchainQueries { private static final Logger LOG = LoggerFactory.getLogger(BlockchainQueries.class); + private final ProtocolSchedule protocolSchedule; private final WorldStateArchive worldStateArchive; private final Blockchain blockchain; private final Optional cachePath; private final Optional transactionLogBloomCacher; private final Optional ethScheduler; private final ApiConfiguration apiConfig; + private final MiningParameters miningParameters; - public BlockchainQueries(final Blockchain blockchain, final WorldStateArchive worldStateArchive) { - this(blockchain, worldStateArchive, Optional.empty(), Optional.empty()); + public BlockchainQueries( + final ProtocolSchedule protocolSchedule, + final Blockchain blockchain, + final WorldStateArchive worldStateArchive, + final MiningParameters miningParameters) { + this( + protocolSchedule, + blockchain, + worldStateArchive, + Optional.empty(), + Optional.empty(), + miningParameters); } public BlockchainQueries( + final ProtocolSchedule protocolSchedule, final Blockchain blockchain, final WorldStateArchive worldStateArchive, - final EthScheduler scheduler) { - this(blockchain, worldStateArchive, Optional.empty(), Optional.ofNullable(scheduler)); + final EthScheduler scheduler, + final MiningParameters miningParameters) { + this( + protocolSchedule, + blockchain, + worldStateArchive, + Optional.empty(), + Optional.ofNullable(scheduler), + miningParameters); } public BlockchainQueries( + final ProtocolSchedule protocolSchedule, final Blockchain blockchain, final WorldStateArchive worldStateArchive, final Optional cachePath, - final Optional scheduler) { + final Optional scheduler, + final MiningParameters miningParameters) { this( + protocolSchedule, blockchain, worldStateArchive, cachePath, scheduler, - ImmutableApiConfiguration.builder().build()); + ImmutableApiConfiguration.builder().build(), + miningParameters); } public BlockchainQueries( + final ProtocolSchedule protocolSchedule, final Blockchain blockchain, final WorldStateArchive worldStateArchive, final Optional cachePath, final Optional scheduler, - final ApiConfiguration apiConfig) { + final ApiConfiguration apiConfig, + final MiningParameters miningParameters) { + this.protocolSchedule = protocolSchedule; this.blockchain = blockchain; this.worldStateArchive = worldStateArchive; this.cachePath = cachePath; @@ -113,6 +144,7 @@ public BlockchainQueries( new TransactionLogBloomCacher(blockchain, cachePath.get(), scheduler.get())) : Optional.empty(); this.apiConfig = apiConfig; + this.miningParameters = miningParameters; } public Blockchain getBlockchain() { @@ -943,10 +975,15 @@ public Optional getAndMapWorldState( return getAndMapWorldState(blockHash, mapper); } - public Optional gasPrice() { + public Wei gasPrice() { final long blockHeight = headBlockNumber(); - final long[] gasCollection = - LongStream.range(Math.max(0, blockHeight - apiConfig.getGasPriceBlocks()), blockHeight) + final var chainHeadHeader = blockchain.getChainHeadHeader(); + final var nextBlockProtocolSpec = + protocolSchedule.getForNextBlockHeader(chainHeadHeader, System.currentTimeMillis()); + final var nextBlockFeeMarket = nextBlockProtocolSpec.getFeeMarket(); + final Wei[] gasCollection = + LongStream.rangeClosed( + Math.max(0, blockHeight - apiConfig.getGasPriceBlocks() + 1), blockHeight) .mapToObj( l -> blockchain @@ -957,20 +994,46 @@ public Optional gasPrice() { () -> new IllegalStateException("Could not retrieve block #" + l))) .flatMap(Collection::stream) .filter(t -> t.getGasPrice().isPresent()) - .mapToLong(t -> t.getGasPrice().get().toLong()) + .map(t -> t.getGasPrice().get()) .sorted() - .toArray(); + .toArray(Wei[]::new); return (gasCollection == null || gasCollection.length == 0) - ? Optional.empty() - : Optional.of( - Math.max( - apiConfig.getGasPriceMinSupplier().getAsLong(), - Math.min( - apiConfig.getGasPriceMax(), - gasCollection[ - Math.min( - gasCollection.length - 1, - (int) ((gasCollection.length) * apiConfig.getGasPriceFraction()))]))); + ? gasPriceLowerBound(chainHeadHeader, nextBlockFeeMarket) + : UInt256s.max( + gasPriceLowerBound(chainHeadHeader, nextBlockFeeMarket), + UInt256s.min( + apiConfig.getGasPriceMax(), + gasCollection[ + Math.min( + gasCollection.length - 1, + (int) ((gasCollection.length) * apiConfig.getGasPriceFraction()))])); + } + + /** + * Return the min gas required for a tx to be mineable. On networks with gas price fee market it + * is just the minGasPrice, while on networks with base fee market it is the max between the + * minGasPrice and the baseFee for the next block. + * + * @return the min gas required for a tx to be mineable. + */ + public Wei gasPriceLowerBound() { + final var chainHeadHeader = blockchain.getChainHeadHeader(); + final var nextBlockProtocolSpec = + protocolSchedule.getForNextBlockHeader(chainHeadHeader, System.currentTimeMillis()); + final var nextBlockFeeMarket = nextBlockProtocolSpec.getFeeMarket(); + return gasPriceLowerBound(chainHeadHeader, nextBlockFeeMarket); + } + + private Wei gasPriceLowerBound( + final BlockHeader chainHeadHeader, final FeeMarket nextBlockFeeMarket) { + final var minGasPrice = miningParameters.getMinTransactionGasPrice(); + + if (nextBlockFeeMarket.implementsBaseFee()) { + return UInt256s.max( + getNextBlockBaseFee(chainHeadHeader, (BaseFeeMarket) nextBlockFeeMarket), minGasPrice); + } + + return minGasPrice; } public Optional gasPriorityFee() { @@ -1000,6 +1063,31 @@ public Optional gasPriorityFee() { (int) ((gasCollection.length) * apiConfig.getGasPriceFraction()))])); } + /** + * Calculate and return the value of the base fee for the next block, if the network has a base + * fee market, otherwise return empty. + * + * @return the optional base fee + */ + public Optional getNextBlockBaseFee() { + final var chainHeadHeader = blockchain.getChainHeadHeader(); + final var nextBlockProtocolSpec = + protocolSchedule.getForNextBlockHeader(chainHeadHeader, System.currentTimeMillis()); + final var nextBlockFeeMarket = nextBlockProtocolSpec.getFeeMarket(); + return nextBlockFeeMarket.implementsBaseFee() + ? Optional.of(getNextBlockBaseFee(chainHeadHeader, (BaseFeeMarket) nextBlockFeeMarket)) + : Optional.empty(); + } + + private Wei getNextBlockBaseFee( + final BlockHeader chainHeadHeader, final BaseFeeMarket nextBlockFeeMarket) { + return nextBlockFeeMarket.computeBaseFee( + chainHeadHeader.getNumber() + 1, + chainHeadHeader.getBaseFee().orElse(Wei.ZERO), + chainHeadHeader.getGasUsed(), + nextBlockFeeMarket.targetGasUsed(chainHeadHeader)); + } + private Optional fromAccount( final Address address, final Hash blockHash, diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/graphql/AbstractEthGraphQLHttpServiceTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/graphql/AbstractEthGraphQLHttpServiceTest.java index 5565c9e4855..5a1c23d40ee 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/graphql/AbstractEthGraphQLHttpServiceTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/graphql/AbstractEthGraphQLHttpServiceTest.java @@ -14,6 +14,7 @@ */ package org.hyperledger.besu.ethereum.api.graphql; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import org.hyperledger.besu.datatypes.TransactionType; @@ -26,6 +27,7 @@ import org.hyperledger.besu.ethereum.chain.MutableBlockchain; import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil; import org.hyperledger.besu.ethereum.core.DefaultSyncStatus; +import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.Synchronizer; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.eth.EthProtocol; @@ -80,14 +82,13 @@ public static void setupConstants() { @BeforeEach public void setupTest() throws Exception { - final Synchronizer synchronizerMock = Mockito.mock(Synchronizer.class); + final Synchronizer synchronizerMock = mock(Synchronizer.class); final SyncStatus status = new DefaultSyncStatus(1, 2, 3, Optional.of(4L), Optional.of(5L)); when(synchronizerMock.getSyncStatus()).thenReturn(Optional.of(status)); - final PoWMiningCoordinator miningCoordinatorMock = Mockito.mock(PoWMiningCoordinator.class); - when(miningCoordinatorMock.getMinTransactionGasPrice()).thenReturn(Wei.of(16)); + final PoWMiningCoordinator miningCoordinatorMock = mock(PoWMiningCoordinator.class); - final TransactionPool transactionPoolMock = Mockito.mock(TransactionPool.class); + final TransactionPool transactionPoolMock = mock(TransactionPool.class); when(transactionPoolMock.addTransactionViaApi(ArgumentMatchers.any(Transaction.class))) .thenReturn(ValidationResult.valid()); @@ -112,11 +113,13 @@ public void setupTest() throws Exception { blockchain, blockchainSetupUtil.getWorldArchive(), null, new BadBlockManager()); final BlockchainQueries blockchainQueries = new BlockchainQueries( + blockchainSetupUtil.getProtocolSchedule(), context.getBlockchain(), context.getWorldStateArchive(), Optional.empty(), Optional.empty(), - ImmutableApiConfiguration.builder().gasPriceMinSupplier(() -> 0).build()); + ImmutableApiConfiguration.builder().build(), + MiningParameters.newDefault().setMinTransactionGasPrice(Wei.ZERO)); final Set supportedCapabilities = new HashSet<>(); supportedCapabilities.add(EthProtocol.ETH62); @@ -147,7 +150,7 @@ public void setupTest() throws Exception { synchronizerMock, GraphQLContextType.GAS_CAP, 0L), - Mockito.mock(EthScheduler.class)); + mock(EthScheduler.class)); service.start().join(); client = new OkHttpClient(); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLHttpServiceTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLHttpServiceTest.java index 71ff8565157..433472af569 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLHttpServiceTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLHttpServiceTest.java @@ -216,7 +216,7 @@ public void handleInvalidQuerySchema() throws Exception { @Test public void query_get() throws Exception { final Wei price = Wei.of(16); - Mockito.when(blockchainQueries.gasPrice()).thenReturn(Optional.of(price.toLong())); + Mockito.when(blockchainQueries.gasPrice()).thenReturn(price); Mockito.when(miningCoordinatorMock.getMinTransactionGasPrice()).thenReturn(price); try (final Response resp = client.newCall(buildGetRequest("?query={gasPrice}")).execute()) { diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/AbstractJsonRpcHttpServiceTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/AbstractJsonRpcHttpServiceTest.java index 9b1163d1e0f..9193574c45b 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/AbstractJsonRpcHttpServiceTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/AbstractJsonRpcHttpServiceTest.java @@ -145,7 +145,10 @@ protected Map getRpcMethods( final BlockchainQueries blockchainQueries = new BlockchainQueries( - blockchainSetupUtil.getBlockchain(), blockchainSetupUtil.getWorldArchive()); + blockchainSetupUtil.getProtocolSchedule(), + blockchainSetupUtil.getBlockchain(), + blockchainSetupUtil.getWorldArchive(), + miningParameters); final FilterIdGenerator filterIdGenerator = mock(FilterIdGenerator.class); final FilterRepository filterRepository = new FilterRepository(); when(filterIdGenerator.nextId()).thenReturn("0x1"); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGasPriceTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGasPriceTest.java index cd1e890fd41..54e17cb21cb 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGasPriceTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGasPriceTest.java @@ -15,7 +15,9 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -30,38 +32,57 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; -import org.hyperledger.besu.ethereum.blockcreation.PoWMiningCoordinator; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.BlockBody; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.Difficulty; +import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; +import org.hyperledger.besu.ethereum.mainnet.feemarket.CancunFeeMarket; +import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; +import org.hyperledger.besu.ethereum.mainnet.feemarket.LegacyFeeMarket; +import org.hyperledger.besu.ethereum.mainnet.feemarket.LondonFeeMarket; import org.hyperledger.besu.evm.log.LogsBloomFilter; +import java.util.HashMap; import java.util.List; import java.util.Optional; +import java.util.stream.IntStream; +import java.util.stream.Stream; import org.apache.tuweni.bytes.Bytes; 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.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; import org.mockito.internal.verification.VerificationModeFactory; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) public class EthGasPriceTest { - - @Mock private PoWMiningCoordinator miningCoordinator; + private static final String JSON_RPC_VERSION = "2.0"; + private static final String ETH_METHOD = "eth_gasPrice"; + private static final long DEFAULT_BLOCK_GAS_LIMIT = 100_000; + private static final long DEFAULT_BLOCK_GAS_USED = 21_000; + private static final Wei DEFAULT_MIN_GAS_PRICE = Wei.of(1_000); + private static final Wei DEFAULT_BASE_FEE = Wei.of(100_000); + + @Mock private ProtocolSchedule protocolSchedule; @Mock private Blockchain blockchain; private EthGasPrice method; - private final String JSON_RPC_VERSION = "2.0"; - private final String ETH_METHOD = "eth_gasPrice"; + private MiningParameters miningParameters; @BeforeEach public void setUp() { ApiConfiguration apiConfig = createDefaultApiConfiguration(); + miningParameters = + MiningParameters.newDefault().setMinTransactionGasPrice(DEFAULT_MIN_GAS_PRICE); method = createEthGasPriceMethod(apiConfig); } @@ -73,41 +94,57 @@ public void returnsCorrectMethodName() { @Test public void shouldReturnMinValueWhenNoTransactionsExist() { final JsonRpcRequestContext request = requestWithParams(); - final String expectedWei = "0x4d2"; + final String expectedWei = "0x4d2"; // minGasPrice > nextBlockBaseFee + miningParameters.setMinTransactionGasPrice(Wei.fromHexString(expectedWei)); final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(request.getRequest().getId(), expectedWei); - when(miningCoordinator.getMinTransactionGasPrice()).thenReturn(Wei.of(1234)); - when(blockchain.getChainHeadBlockNumber()).thenReturn(1000L); - when(blockchain.getBlockByNumber(anyLong())) - .thenAnswer(invocation -> createEmptyBlock(invocation.getArgument(0, Long.class))); + mockBaseFeeMarket(); + + mockBlockchain(1000, 0); final JsonRpcResponse actualResponse = method.response(request); assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); - verify(miningCoordinator).getMinTransactionGasPrice(); - verifyNoMoreInteractions(miningCoordinator); - verify(blockchain).getChainHeadBlockNumber(); verify(blockchain, VerificationModeFactory.times(100)).getBlockByNumber(anyLong()); verifyNoMoreInteractions(blockchain); } @Test - public void shouldReturnMedianWhenTransactionsExist() { + public void shouldReturnBaseFeeAsMinValueOnGenesis() { final JsonRpcRequestContext request = requestWithParams(); - final String expectedWei = "0x389fd980"; // 950Wei, gas prices are 900-999 wei. + final String expectedWei = + DEFAULT_BASE_FEE.toShortHexString(); // nextBlockBaseFee > minGasPrice + miningParameters.setMinTransactionGasPrice(Wei.fromHexString(expectedWei)); final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(request.getRequest().getId(), expectedWei); - when(blockchain.getChainHeadBlockNumber()).thenReturn(1000L); - when(blockchain.getBlockByNumber(anyLong())) - .thenAnswer(invocation -> createFakeBlock(invocation.getArgument(0, Long.class))); + mockBaseFeeMarket(); + + mockBlockchain(0, 0); final JsonRpcResponse actualResponse = method.response(request); assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); - verifyNoMoreInteractions(miningCoordinator); + verify(blockchain).getChainHeadBlockNumber(); + verify(blockchain, VerificationModeFactory.times(1)).getBlockByNumber(anyLong()); + verifyNoMoreInteractions(blockchain); + } + + @Test + public void shouldReturnMedianWhenTransactionsExist() { + final JsonRpcRequestContext request = requestWithParams(); + final String expectedWei = "0x911c70"; // 9.51 mwei, gas prices are 9.01-10 mwei. + final JsonRpcResponse expectedResponse = + new JsonRpcSuccessResponse(request.getRequest().getId(), expectedWei); + + mockBaseFeeMarket(); + + mockBlockchain(1000L, 1); + + final JsonRpcResponse actualResponse = method.response(request); + assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); verify(blockchain).getChainHeadBlockNumber(); verify(blockchain, VerificationModeFactory.times(100)).getBlockByNumber(anyLong()); @@ -117,21 +154,19 @@ public void shouldReturnMedianWhenTransactionsExist() { @Test public void shortChainQueriesAllBlocks() { final JsonRpcRequestContext request = requestWithParams(); - final String expectedWei = "0x2625a00"; + final String expectedWei = "0x64190"; // 410 kwei final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(request.getRequest().getId(), expectedWei); - when(blockchain.getChainHeadBlockNumber()).thenReturn(80L); - when(blockchain.getBlockByNumber(anyLong())) - .thenAnswer(invocation -> createFakeBlock(invocation.getArgument(0, Long.class))); + mockBaseFeeMarket(); + + mockBlockchain(80L, 1); final JsonRpcResponse actualResponse = method.response(request); assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); - verifyNoMoreInteractions(miningCoordinator); - verify(blockchain).getChainHeadBlockNumber(); - verify(blockchain, VerificationModeFactory.times(80)).getBlockByNumber(anyLong()); + verify(blockchain, VerificationModeFactory.times(81)).getBlockByNumber(anyLong()); verifyNoMoreInteractions(blockchain); } @@ -141,9 +176,9 @@ public void shortChainQueriesAllBlocks() { */ @Test public void shouldReturnLimitedPriceWhenLowerBoundIsPresent() { - long gasPrice = 5000000; - long lowerBoundGasPrice = gasPrice + 1; - verifyGasPriceLimit(lowerBoundGasPrice, null, lowerBoundGasPrice); + long expectedGasPrice = 31_841 * 2; + long lowerBoundCoefficient = 200; + verifyGasPriceLimit(lowerBoundCoefficient, null, expectedGasPrice); } /** @@ -152,9 +187,9 @@ public void shouldReturnLimitedPriceWhenLowerBoundIsPresent() { */ @Test public void shouldReturnLimitedPriceWhenUpperBoundIsPresent() { - long gasPrice = 5000000; - long upperBoundGasPrice = gasPrice - 1; - verifyGasPriceLimit(null, upperBoundGasPrice, upperBoundGasPrice); + long expectedGasPrice = (long) (31_841 * 1.5); + long upperBoundCoefficient = 150; + verifyGasPriceLimit(null, upperBoundCoefficient, expectedGasPrice); } /** @@ -163,25 +198,83 @@ public void shouldReturnLimitedPriceWhenUpperBoundIsPresent() { */ @Test public void shouldReturnActualGasPriceWhenWithinBoundRange() { - long gasPrice = 5000000; - long lowerBoundGasPrice = gasPrice - 1; - long upperBoundGasPrice = gasPrice + 1; - verifyGasPriceLimit(lowerBoundGasPrice, upperBoundGasPrice, gasPrice); + long gasPrice = 60_000; + long lowerBoundCoefficient = 120; + long upperBoundCoefficient = 200; + verifyGasPriceLimit(lowerBoundCoefficient, upperBoundCoefficient, gasPrice); + } + + private static Stream ethGasPriceAtGenesis() { + return Stream.of( + // base fee > min gas price + Arguments.of( + DEFAULT_MIN_GAS_PRICE.divide(2), + Optional.of(DEFAULT_MIN_GAS_PRICE), + DEFAULT_MIN_GAS_PRICE.subtract( + DEFAULT_MIN_GAS_PRICE + .multiply(125) + .divide(1000)) // expect base fee for the 1st block + ), + // base fee < min gas price + Arguments.of( + DEFAULT_BASE_FEE.multiply(2), + Optional.of(DEFAULT_BASE_FEE), + DEFAULT_BASE_FEE.multiply(2)) // expect min gas price value + , + + // no base fee market + Arguments.of( + DEFAULT_MIN_GAS_PRICE, + Optional.empty(), + DEFAULT_MIN_GAS_PRICE // expect min gas price value + )); + } + + @ParameterizedTest + @MethodSource("ethGasPriceAtGenesis") + public void ethGasPriceAtGenesis( + final Wei minGasPrice, final Optional maybeGenesisBaseFee, final Wei expectedGasPrice) { + miningParameters.setMinTransactionGasPrice(minGasPrice); + + if (maybeGenesisBaseFee.isPresent()) { + mockBaseFeeMarket(); + mockBlockchain(maybeGenesisBaseFee.get(), 0, 0); + } else { + mockGasPriceMarket(); + mockBlockchain(null, 0, 0); + } + + final JsonRpcRequestContext request = requestWithParams(); + final JsonRpcResponse expectedResponse = + new JsonRpcSuccessResponse( + request.getRequest().getId(), expectedGasPrice.toShortHexString()); + + final JsonRpcResponse actualResponse = method.response(request); + assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); + + verify(blockchain).getChainHeadBlockNumber(); + verify(blockchain, VerificationModeFactory.times(1)).getBlockByNumber(anyLong()); + verifyNoMoreInteractions(blockchain); } /** * Helper method to verify the gas price limit. * - * @param lowerBound The lower bound of the gas price. - * @param upperBound The upper bound of the gas price. + * @param lowerBoundCoefficient The lower bound of the gas price. + * @param upperBoundCoefficient The upper bound of the gas price. * @param expectedGasPrice The expected gas price. */ private void verifyGasPriceLimit( - final Long lowerBound, final Long upperBound, final long expectedGasPrice) { - when(miningCoordinator.getMinTransactionGasPrice()).thenReturn(Wei.of(100)); + final Long lowerBoundCoefficient, + final Long upperBoundCoefficient, + final long expectedGasPrice) { + miningParameters.setMinTransactionGasPrice(Wei.of(100)); + + mockBaseFeeMarket(); var apiConfig = - createApiConfiguration(Optional.ofNullable(lowerBound), Optional.ofNullable(upperBound)); + createApiConfiguration( + Optional.ofNullable(lowerBoundCoefficient), Optional.ofNullable(upperBoundCoefficient)); method = createEthGasPriceMethod(apiConfig); final JsonRpcRequestContext request = requestWithParams(); @@ -189,78 +282,108 @@ private void verifyGasPriceLimit( new JsonRpcSuccessResponse( request.getRequest().getId(), Wei.of(expectedGasPrice).toShortHexString()); - when(blockchain.getChainHeadBlockNumber()).thenReturn(10L); - when(blockchain.getBlockByNumber(anyLong())) - .thenAnswer(invocation -> createFakeBlock(invocation.getArgument(0, Long.class))); + final var chainHeadBlockNumber = 10L; + mockBlockchain(chainHeadBlockNumber, 1); final JsonRpcResponse actualResponse = method.response(request); assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); } - private Object createFakeBlock(final Long height) { - return Optional.of( - new Block( - new BlockHeader( - Hash.EMPTY, - Hash.EMPTY_TRIE_HASH, - Address.ZERO, - Hash.EMPTY_TRIE_HASH, - Hash.EMPTY_TRIE_HASH, - Hash.EMPTY_TRIE_HASH, - LogsBloomFilter.builder().build(), - Difficulty.ONE, - height, - 0, - 0, - 0, - Bytes.EMPTY, - Wei.ZERO, - Hash.EMPTY, - 0, - null, - null, - null, - null, - null, - null), - new BlockBody( - List.of( - new Transaction.Builder() - .nonce(0) - .gasPrice(Wei.of(height * 1000000L)) - .gasLimit(0) - .value(Wei.ZERO) - .build()), - List.of()))); + private void mockBaseFeeMarket() { + mockFeeMarket(new LondonFeeMarket(0)); + } + + private void mockGasPriceMarket() { + mockFeeMarket(new LegacyFeeMarket()); + } + + private void mockFeeMarket(final FeeMarket feeMarket) { + final var protocolSpec = mock(ProtocolSpec.class); + when(protocolSpec.getFeeMarket()).thenReturn(feeMarket); + when(protocolSchedule.getForNextBlockHeader(any(), anyLong())).thenReturn(protocolSpec); + } + + private void mockBlockchain(final long chainHeadBlockNumber, final int txsNum) { + mockBlockchain(DEFAULT_BASE_FEE, chainHeadBlockNumber, txsNum); + } + + private void mockBlockchain( + final Wei genesisBaseFee, final long chainHeadBlockNumber, final int txsNum) { + final var blocksByNumber = new HashMap(); + + final var genesisBlock = createFakeBlock(0, 0, genesisBaseFee); + blocksByNumber.put(0L, genesisBlock); + + final var baseFeeMarket = new CancunFeeMarket(0, Optional.empty()); + + var baseFee = genesisBaseFee; + for (long i = 1; i <= chainHeadBlockNumber; i++) { + final var parentHeader = blocksByNumber.get(i - 1).getHeader(); + baseFee = + baseFeeMarket.computeBaseFee( + i, + parentHeader.getBaseFee().get(), + parentHeader.getGasUsed(), + parentHeader.getGasLimit()); + blocksByNumber.put(i, createFakeBlock(i, txsNum, baseFee)); + } + + when(blockchain.getChainHeadBlockNumber()).thenReturn(chainHeadBlockNumber); + when(blockchain.getBlockByNumber(anyLong())) + .thenAnswer( + invocation -> Optional.of(blocksByNumber.get(invocation.getArgument(0, Long.class)))); + + when(blockchain.getChainHeadHeader()) + .thenReturn(blocksByNumber.get(chainHeadBlockNumber).getHeader()); + } + + private Block createFakeBlock(final long height, final int txsNum, final Wei baseFee) { + return createFakeBlock( + height, txsNum, baseFee, DEFAULT_BLOCK_GAS_LIMIT, DEFAULT_BLOCK_GAS_USED * txsNum); } - private Object createEmptyBlock(final Long height) { - return Optional.of( - new Block( - new BlockHeader( - Hash.EMPTY, - Hash.EMPTY_TRIE_HASH, - Address.ZERO, - Hash.EMPTY_TRIE_HASH, - Hash.EMPTY_TRIE_HASH, - Hash.EMPTY_TRIE_HASH, - LogsBloomFilter.builder().build(), - Difficulty.ONE, - height, - 0, - 0, - 0, - Bytes.EMPTY, - Wei.ZERO, - Hash.EMPTY, - 0, - null, - null, - null, - null, - null, - null), - new BlockBody(List.of(), List.of()))); + private Block createFakeBlock( + final long height, + final int txsNum, + final Wei baseFee, + final long gasLimit, + final long gasUsed) { + return new Block( + new BlockHeader( + Hash.EMPTY, + Hash.EMPTY_TRIE_HASH, + Address.ZERO, + Hash.EMPTY_TRIE_HASH, + Hash.EMPTY_TRIE_HASH, + Hash.EMPTY_TRIE_HASH, + LogsBloomFilter.builder().build(), + Difficulty.ONE, + height, + gasLimit, + gasUsed, + 0, + Bytes.EMPTY, + baseFee, + Hash.EMPTY, + 0, + null, + null, + null, + null, + null, + null), + new BlockBody( + IntStream.range(0, txsNum) + .mapToObj( + i -> + new Transaction.Builder() + .nonce(i) + .gasPrice(Wei.of(height * 10_000L)) + .gasLimit(gasUsed) + .value(Wei.ZERO) + .build()) + .toList(), + List.of())); } private JsonRpcRequestContext requestWithParams(final Object... params) { @@ -272,16 +395,15 @@ private ApiConfiguration createDefaultApiConfiguration() { } private ApiConfiguration createApiConfiguration( - final Optional lowerBound, final Optional upperBound) { - ImmutableApiConfiguration.Builder builder = - ImmutableApiConfiguration.builder().gasPriceMinSupplier(() -> 100); + final Optional lowerBoundCoefficient, final Optional upperBoundCoefficient) { + ImmutableApiConfiguration.Builder builder = ImmutableApiConfiguration.builder(); - lowerBound.ifPresent( + lowerBoundCoefficient.ifPresent( value -> builder .isGasAndPriorityFeeLimitingEnabled(true) .lowerBoundGasAndPriorityFeeCoefficient(value)); - upperBound.ifPresent( + upperBoundCoefficient.ifPresent( value -> builder .isGasAndPriorityFeeLimitingEnabled(true) @@ -292,8 +414,14 @@ private ApiConfiguration createApiConfiguration( private EthGasPrice createEthGasPriceMethod(final ApiConfiguration apiConfig) { return new EthGasPrice( - new BlockchainQueries(blockchain, null, Optional.empty(), Optional.empty(), apiConfig), - miningCoordinator, + new BlockchainQueries( + protocolSchedule, + blockchain, + null, + Optional.empty(), + Optional.empty(), + apiConfig, + miningParameters), apiConfig); } } diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetBlockByNumberTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetBlockByNumberTest.java index 166d238caa0..1c1cbaf6f78 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetBlockByNumberTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetBlockByNumberTest.java @@ -36,8 +36,10 @@ import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.BlockDataGenerator; import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.Synchronizer; import org.hyperledger.besu.ethereum.core.TransactionReceipt; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import java.util.List; @@ -66,6 +68,7 @@ public class EthGetBlockByNumberTest { private EthGetBlockByNumber method; @Mock private Synchronizer synchronizer; @Mock private WorldStateArchive worldStateArchive; + @Mock private ProtocolSchedule protocolSchedule; @BeforeEach public void setUp() { @@ -87,7 +90,10 @@ public void setUp() { latestHeader.getStateRoot(), latestHeader.getHash())) .thenReturn(Boolean.TRUE); - blockchainQueries = spy(new BlockchainQueries(blockchain, worldStateArchive)); + blockchainQueries = + spy( + new BlockchainQueries( + protocolSchedule, blockchain, worldStateArchive, MiningParameters.newDefault())); method = new EthGetBlockByNumber(blockchainQueries, blockResult, synchronizer); } diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetBlockReceiptsTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetBlockReceiptsTest.java index a36af3755ed..5e8ad1455d8 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetBlockReceiptsTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetBlockReceiptsTest.java @@ -35,6 +35,7 @@ import org.hyperledger.besu.ethereum.chain.MutableBlockchain; import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.BlockDataGenerator; +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.mainnet.ProtocolSchedule; @@ -82,7 +83,10 @@ public void setUp() { blockchain.appendBlock(block, receipts); } - blockchainQueries = spy(new BlockchainQueries(blockchain, worldStateArchive)); + blockchainQueries = + spy( + new BlockchainQueries( + protocolSchedule, blockchain, worldStateArchive, MiningParameters.newDefault())); protocolSchedule = mock(ProtocolSchedule.class); method = new EthGetBlockReceipts(blockchainQueries, protocolSchedule); } diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetProofTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetProofTest.java index 2f7d82d1bf5..2106b86744d 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetProofTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetProofTest.java @@ -39,6 +39,8 @@ import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.chain.ChainHead; import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.MiningParameters; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.proof.WorldStateProof; import org.hyperledger.besu.ethereum.worldstate.StateTrieAccountValue; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; @@ -63,6 +65,7 @@ @MockitoSettings(strictness = Strictness.LENIENT) class EthGetProofTest { @Mock private Blockchain blockchain; + @Mock private ProtocolSchedule protocolSchedule; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private WorldStateArchive archive; @@ -83,7 +86,10 @@ class EthGetProofTest { @BeforeEach public void setUp() { - blockchainQueries = spy(new BlockchainQueries(blockchain, archive)); + blockchainQueries = + spy( + new BlockchainQueries( + protocolSchedule, blockchain, archive, MiningParameters.newDefault())); when(blockchainQueries.getBlockchain()).thenReturn(blockchain); when(blockchainQueries.headBlockNumber()).thenReturn(14L); when(blockchain.getChainHead()).thenReturn(chainHead); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetTransactionReceiptTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetTransactionReceiptTest.java index 476705f6775..884cc504d8e 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetTransactionReceiptTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetTransactionReceiptTest.java @@ -42,6 +42,7 @@ import org.hyperledger.besu.ethereum.core.BlockDataGenerator; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; +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.mainnet.PoWHasher; @@ -189,7 +190,12 @@ public class EthGetTransactionReceiptTest { private final Blockchain blockchain = mock(Blockchain.class); private final BlockchainQueries blockchainQueries = - spy(new BlockchainQueries(blockchain, mock(WorldStateArchive.class))); + spy( + new BlockchainQueries( + protocolSchedule, + blockchain, + mock(WorldStateArchive.class), + MiningParameters.newDefault())); private final EthGetTransactionReceipt ethGetTransactionReceipt = new EthGetTransactionReceipt(blockchainQueries, protocolSchedule); private final String receiptString = diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/blockheaders/NewBlockHeadersSubscriptionServiceTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/blockheaders/NewBlockHeadersSubscriptionServiceTest.java index ecf3e5cc691..8cb33fa714e 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/blockheaders/NewBlockHeadersSubscriptionServiceTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/blockheaders/NewBlockHeadersSubscriptionServiceTest.java @@ -32,8 +32,10 @@ import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.BlockDataGenerator; import org.hyperledger.besu.ethereum.core.BlockDataGenerator.BlockOptions; +import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.TransactionReceipt; import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage; import org.hyperledger.besu.ethereum.storage.keyvalue.VariablesKeyValueStorage; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; @@ -47,6 +49,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; +import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; @@ -73,9 +76,16 @@ public class NewBlockHeadersSubscriptionServiceTest { private final SubscriptionManager subscriptionManagerSpy = new SubscriptionManager(new NoOpMetricsSystem()); + @Mock ProtocolSchedule protocolSchedule; + @Spy private final BlockchainQueries blockchainQueriesSpy = - Mockito.spy(new BlockchainQueries(blockchain, createInMemoryWorldStateArchive())); + Mockito.spy( + new BlockchainQueries( + protocolSchedule, + blockchain, + createInMemoryWorldStateArchive(), + MiningParameters.newDefault())); @BeforeEach public void before() { diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueriesLogCacheTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueriesLogCacheTest.java index 0d77d2ceb1a..c0420163de8 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueriesLogCacheTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueriesLogCacheTest.java @@ -28,8 +28,10 @@ import org.hyperledger.besu.ethereum.core.BlockBody; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.Difficulty; +import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.evm.log.Log; import org.hyperledger.besu.evm.log.LogsBloomFilter; @@ -63,6 +65,7 @@ public class BlockchainQueriesLogCacheTest { private Hash testHash; private static LogsBloomFilter testLogsBloomFilter; + @Mock ProtocolSchedule protocolSchedule; @Mock MutableBlockchain blockchain; @Mock WorldStateArchive worldStateArchive; @Mock EthScheduler scheduler; @@ -127,7 +130,12 @@ public void setup() { when(blockchain.getBlockBody(any())).thenReturn(Optional.of(fakeBody)); blockchainQueries = new BlockchainQueries( - blockchain, worldStateArchive, Optional.of(cacheDir), Optional.of(scheduler)); + protocolSchedule, + blockchain, + worldStateArchive, + Optional.of(cacheDir), + Optional.of(scheduler), + MiningParameters.newDefault()); } /** diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueriesTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueriesTest.java index 11fc9f9bef8..a49b290e4db 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueriesTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueriesTest.java @@ -28,9 +28,11 @@ import org.hyperledger.besu.ethereum.core.BlockDataGenerator.BlockOptions; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.LogWithMetadata; +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.EthScheduler; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.worldstate.WorldState; @@ -46,6 +48,7 @@ import org.apache.tuweni.units.bigints.UInt256; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; public class BlockchainQueriesTest { private BlockDataGenerator gen; @@ -504,6 +507,9 @@ public void getLatestBlockOmmerByIndexShouldReturnExpectedOmmerHeader() { assertThat(retrievedOmmerBlockHeader).isEqualTo(ommerBlockHeader); } + @Test + public void getGasPriceLowerBound() {} + private void assertBlockMatchesResult( final Block targetBlock, final BlockWithMetadata result) { assertThat(result.getHeader()).isEqualTo(targetBlock.getHeader()); @@ -587,17 +593,15 @@ private BlockchainWithData( this.blockchain = blockchain; this.blockData = blockData; this.worldStateArchive = worldStateArchive; - this.blockchainQueries = new BlockchainQueries(blockchain, worldStateArchive, scheduler); + this.blockchainQueries = + new BlockchainQueries( + Mockito.mock(ProtocolSchedule.class), + blockchain, + worldStateArchive, + scheduler, + MiningParameters.newDefault()); } } - private static class BlockData { - final Block block; - final List receipts; - - private BlockData(final Block block, final List receipts) { - this.block = block; - this.receipts = receipts; - } - } + private record BlockData(Block block, List receipts) {} } diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/graphql/eth_gasPrice.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/graphql/eth_gasPrice.json index 40b8bf0fa0d..1f5ad97ff89 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/graphql/eth_gasPrice.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/graphql/eth_gasPrice.json @@ -2,7 +2,7 @@ "request": "{ gasPrice maxPriorityFeePerGas }", "response": { "data": { - "gasPrice": "0x1", + "gasPrice": "0x2dbc88c1", "maxPriorityFeePerGas": "0x3b9aca00" } }, diff --git a/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java b/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java index cad559ae15b..eb43f96dfb9 100644 --- a/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java +++ b/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java @@ -181,7 +181,9 @@ private boolean buildContext( blockchain = createInMemoryBlockchain(genesisState.getBlock()); protocolContext = new ProtocolContext(blockchain, worldStateArchive, null, badBlockManager); - blockchainQueries = new BlockchainQueries(blockchain, worldStateArchive, ethScheduler); + blockchainQueries = + new BlockchainQueries( + protocolSchedule, blockchain, worldStateArchive, ethScheduler, miningParameters); final String sealengine = JsonUtil.getString(genesisConfig, "sealengine", ""); headerValidationMode =