From ce148f66405a3657735fe970b3ebb50b5cf6d56b Mon Sep 17 00:00:00 2001 From: Gabriel-Trintinalia Date: Fri, 8 Dec 2023 10:43:10 +1100 Subject: [PATCH] [RPC] Use apiConfiguration to limit gasPrice in eth_getGasPrice (#6243) Signed-off-by: Gabriel-Trintinalia Signed-off-by: jflo --- .../org/hyperledger/besu/cli/BesuCommand.java | 42 +++---- .../hyperledger/besu/cli/BesuCommandTest.java | 29 ++--- .../besu/ethereum/api/ApiConfiguration.java | 14 +-- .../internal/methods/EthFeeHistory.java | 26 ++--- .../jsonrpc/internal/methods/EthGasPrice.java | 53 +++++++-- .../jsonrpc/methods/EthJsonRpcMethods.java | 2 +- .../internal/methods/EthFeeHistoryTest.java | 6 +- .../internal/methods/EthGasPriceTest.java | 104 ++++++++++++++++-- 8 files changed, 196 insertions(+), 80 deletions(-) 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 a4045cee1c5..b2cd625e9ec 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -1218,27 +1218,27 @@ static class PermissionsOptionGroup { private final Long apiGasPriceMax = 500_000_000_000L; @CommandLine.Option( - names = {"--api-priority-fee-limiting-enabled"}, + names = {"--api-gas-and-priority-fee-limiting-enabled"}, hidden = true, description = - "Set to enable priority fee limit in eth_feeHistory (default: ${DEFAULT-VALUE})") - private final Boolean apiPriorityFeeLimitingEnabled = false; + "Set to enable gas price and minimum priority fee limit in eth_getGasPrice and eth_feeHistory (default: ${DEFAULT-VALUE})") + private final Boolean apiGasAndPriorityFeeLimitingEnabled = false; @CommandLine.Option( - names = {"--api-priority-fee-lower-bound-coefficient"}, + names = {"--api-gas-and-priority-fee-lower-bound-coefficient"}, hidden = true, description = - "Coefficient for setting the lower limit of minimum priority fee in eth_feeHistory (default: ${DEFAULT-VALUE})") - private final Long apiPriorityFeeLowerBoundCoefficient = - ApiConfiguration.DEFAULT_LOWER_BOUND_PRIORITY_FEE_COEFFICIENT; + "Coefficient for setting the lower limit of gas price and minimum priority fee in eth_getGasPrice and eth_feeHistory (default: ${DEFAULT-VALUE})") + private final Long apiGasAndPriorityFeeLowerBoundCoefficient = + ApiConfiguration.DEFAULT_LOWER_BOUND_GAS_AND_PRIORITY_FEE_COEFFICIENT; @CommandLine.Option( - names = {"--api-priority-fee-upper-bound-coefficient"}, + names = {"--api-gas-and-priority-fee-upper-bound-coefficient"}, hidden = true, description = - "Coefficient for setting the upper limit of minimum priority fee in eth_feeHistory (default: ${DEFAULT-VALUE})") - private final Long apiPriorityFeeUpperBoundCoefficient = - ApiConfiguration.DEFAULT_UPPER_BOUND_PRIORITY_FEE_COEFFICIENT; + "Coefficient for setting the upper limit of gas price and minimum priority fee in eth_getGasPrice and eth_feeHistory (default: ${DEFAULT-VALUE})") + private final Long apiGasAndPriorityFeeUpperBoundCoefficient = + ApiConfiguration.DEFAULT_UPPER_BOUND_GAS_AND_PRIORITY_FEE_COEFFICIENT; @CommandLine.Option( names = {"--static-nodes-file"}, @@ -1902,11 +1902,11 @@ private void checkApiOptionsDependencies() { CommandLineUtils.checkOptionDependencies( logger, commandLine, - "--api-priority-fee-limiting-enabled", - !apiPriorityFeeLimitingEnabled, + "--api-gas-and-priority-fee-limiting-enabled", + !apiGasAndPriorityFeeLimitingEnabled, asList( - "--api-priority-fee-upper-bound-coefficient", - "--api-priority-fee-lower-bound-coefficient")); + "--api-gas-and-priority-fee-upper-bound-coefficient", + "--api-gas-and-priority-fee-lower-bound-coefficient")); } private void ensureValidPeerBoundParams() { @@ -2534,16 +2534,16 @@ private ApiConfiguration apiConfiguration() { .gasPriceMax(apiGasPriceMax) .maxLogsRange(rpcMaxLogsRange) .gasCap(rpcGasCap) - .isPriorityFeeLimitingEnabled(apiPriorityFeeLimitingEnabled); - if (apiPriorityFeeLimitingEnabled) { - if (apiPriorityFeeLowerBoundCoefficient > apiPriorityFeeUpperBoundCoefficient) { + .isGasAndPriorityFeeLimitingEnabled(apiGasAndPriorityFeeLimitingEnabled); + if (apiGasAndPriorityFeeLimitingEnabled) { + if (apiGasAndPriorityFeeLowerBoundCoefficient > apiGasAndPriorityFeeUpperBoundCoefficient) { throw new ParameterException( this.commandLine, - "--api-priority-fee-lower-bound-coefficient cannot be greater than the value of --api-priority-fee-upper-bound-coefficient"); + "--api-gas-and-priority-fee-lower-bound-coefficient cannot be greater than the value of --api-gas-and-priority-fee-upper-bound-coefficient"); } builder - .lowerBoundPriorityFeeCoefficient(apiPriorityFeeLowerBoundCoefficient) - .upperBoundPriorityFeeCoefficient(apiPriorityFeeUpperBoundCoefficient); + .lowerBoundGasAndPriorityFeeCoefficient(apiGasAndPriorityFeeLowerBoundCoefficient) + .upperBoundGasAndPriorityFeeCoefficient(apiGasAndPriorityFeeUpperBoundCoefficient); } return builder.build(); } diff --git a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java index 0df397af71f..955c9df7aaf 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java @@ -1611,11 +1611,12 @@ public void rpcGasCapOptionMustBeUsed() { @Test public void apiPriorityFeeLimitingEnabledOptionMustBeUsed() { - parseCommand("--api-priority-fee-limiting-enabled"); + parseCommand("--api-gas-and-priority-fee-limiting-enabled"); verify(mockRunnerBuilder).apiConfiguration(apiConfigurationCaptor.capture()); verify(mockRunnerBuilder).build(); assertThat(apiConfigurationCaptor.getValue()) - .isEqualTo(ImmutableApiConfiguration.builder().isPriorityFeeLimitingEnabled(true).build()); + .isEqualTo( + ImmutableApiConfiguration.builder().isGasAndPriorityFeeLimitingEnabled(true).build()); assertThat(commandOutput.toString(UTF_8)).isEmpty(); assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); @@ -1625,16 +1626,16 @@ public void apiPriorityFeeLimitingEnabledOptionMustBeUsed() { public void apiPriorityFeeLowerBoundCoefficientOptionMustBeUsed() { final long lowerBound = 150L; parseCommand( - "--api-priority-fee-lower-bound-coefficient", + "--api-gas-and-priority-fee-lower-bound-coefficient", Long.toString(lowerBound), - "--api-priority-fee-limiting-enabled"); + "--api-gas-and-priority-fee-limiting-enabled"); verify(mockRunnerBuilder).apiConfiguration(apiConfigurationCaptor.capture()); verify(mockRunnerBuilder).build(); assertThat(apiConfigurationCaptor.getValue()) .isEqualTo( ImmutableApiConfiguration.builder() - .lowerBoundPriorityFeeCoefficient(lowerBound) - .isPriorityFeeLimitingEnabled(true) + .lowerBoundGasAndPriorityFeeCoefficient(lowerBound) + .isGasAndPriorityFeeLimitingEnabled(true) .build()); assertThat(commandOutput.toString(UTF_8)).isEmpty(); @@ -1648,32 +1649,32 @@ public void apiPriorityFeeLowerBoundCoefficientOptionMustBeUsed() { final long upperBound = 100L; parseCommand( - "--api-priority-fee-limiting-enabled", - "--api-priority-fee-lower-bound-coefficient", + "--api-gas-and-priority-fee-limiting-enabled", + "--api-gas-and-priority-fee-lower-bound-coefficient", Long.toString(lowerBound), - "--api-priority-fee-upper-bound-coefficient", + "--api-gas-and-priority-fee-upper-bound-coefficient", Long.toString(upperBound)); Mockito.verifyNoInteractions(mockRunnerBuilder); assertThat(commandOutput.toString(UTF_8)).isEmpty(); assertThat(commandErrorOutput.toString(UTF_8)) .contains( - "--api-priority-fee-lower-bound-coefficient cannot be greater than the value of --api-priority-fee-upper-bound-coefficient"); + "--api-gas-and-priority-fee-lower-bound-coefficient cannot be greater than the value of --api-gas-and-priority-fee-upper-bound-coefficient"); } @Test public void apiPriorityFeeUpperBoundCoefficientsOptionMustBeUsed() { final long upperBound = 200L; parseCommand( - "--api-priority-fee-upper-bound-coefficient", + "--api-gas-and-priority-fee-upper-bound-coefficient", Long.toString(upperBound), - "--api-priority-fee-limiting-enabled"); + "--api-gas-and-priority-fee-limiting-enabled"); verify(mockRunnerBuilder).apiConfiguration(apiConfigurationCaptor.capture()); verify(mockRunnerBuilder).build(); assertThat(apiConfigurationCaptor.getValue()) .isEqualTo( ImmutableApiConfiguration.builder() - .upperBoundPriorityFeeCoefficient(upperBound) - .isPriorityFeeLimitingEnabled(true) + .upperBoundGasAndPriorityFeeCoefficient(upperBound) + .isGasAndPriorityFeeLimitingEnabled(true) .build()); assertThat(commandOutput.toString(UTF_8)).isEmpty(); 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 e277c2ba2ff..e9f30fca336 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 @@ -24,8 +24,8 @@ @Value.Style(allParameters = true) public abstract class ApiConfiguration { - public static final long DEFAULT_LOWER_BOUND_PRIORITY_FEE_COEFFICIENT = 0L; - public static final long DEFAULT_UPPER_BOUND_PRIORITY_FEE_COEFFICIENT = Long.MAX_VALUE; + public static final long DEFAULT_LOWER_BOUND_GAS_AND_PRIORITY_FEE_COEFFICIENT = 0L; + public static final long DEFAULT_UPPER_BOUND_GAS_AND_PRIORITY_FEE_COEFFICIENT = Long.MAX_VALUE; @Value.Default public long getGasPriceBlocks() { @@ -64,17 +64,17 @@ public Long getGasCap() { } @Value.Default - public boolean isPriorityFeeLimitingEnabled() { + public boolean isGasAndPriorityFeeLimitingEnabled() { return false; } @Value.Default - public Long getLowerBoundPriorityFeeCoefficient() { - return DEFAULT_LOWER_BOUND_PRIORITY_FEE_COEFFICIENT; + public Long getLowerBoundGasAndPriorityFeeCoefficient() { + return DEFAULT_LOWER_BOUND_GAS_AND_PRIORITY_FEE_COEFFICIENT; } @Value.Default - public Long getUpperBoundPriorityFeeCoefficient() { - return DEFAULT_UPPER_BOUND_PRIORITY_FEE_COEFFICIENT; + public Long getUpperBoundGasAndPriorityFeeCoefficient() { + return DEFAULT_UPPER_BOUND_GAS_AND_PRIORITY_FEE_COEFFICIENT; } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthFeeHistory.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthFeeHistory.java index e4d71ae8e79..4455bd30b55 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthFeeHistory.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthFeeHistory.java @@ -218,7 +218,7 @@ public List computeRewards(final List rewardPercentiles, final Bloc // If the priority fee boundary is set, return the bounded rewards. Otherwise, return the real // rewards. - if (apiConfiguration.isPriorityFeeLimitingEnabled()) { + if (apiConfiguration.isGasAndPriorityFeeLimitingEnabled()) { return boundRewards(realRewards); } else { return realRewards; @@ -263,9 +263,13 @@ private List calculateRewards( private List boundRewards(final List rewards) { Wei minPriorityFee = miningCoordinator.getMinPriorityFeePerGas(); Wei lowerBound = - minPriorityFee.multiply(apiConfiguration.getLowerBoundPriorityFeeCoefficient()).divide(100); + minPriorityFee + .multiply(apiConfiguration.getLowerBoundGasAndPriorityFeeCoefficient()) + .divide(100); Wei upperBound = - minPriorityFee.multiply(apiConfiguration.getUpperBoundPriorityFeeCoefficient()).divide(100); + minPriorityFee + .multiply(apiConfiguration.getUpperBoundGasAndPriorityFeeCoefficient()) + .divide(100); return rewards.stream().map(reward -> boundReward(reward, lowerBound, upperBound)).toList(); } @@ -279,19 +283,9 @@ private List boundRewards(final List rewards) { * @return The bounded reward. */ private Wei boundReward(final Wei reward, final Wei lowerBound, final Wei upperBound) { - - // If the reward is less than the lower bound, return the lower bound. - if (reward.compareTo(lowerBound) <= 0) { - return lowerBound; - } - - // If the reward is greater than the upper bound, return the upper bound. - if (reward.compareTo(upperBound) > 0) { - return upperBound; - } - - // If the reward is within the bounds, return the reward as is. - return reward; + return reward.compareTo(lowerBound) <= 0 + ? lowerBound + : reward.compareTo(upperBound) >= 0 ? upperBound : reward; } private List calculateTransactionsGasUsed(final Block block) { 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 117fdbdcbb1..4913f70ede1 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 @@ -14,6 +14,8 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.api.ApiConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; @@ -22,22 +24,29 @@ 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 ApiConfiguration apiConfiguration; public EthGasPrice( - final BlockchainQueries blockchain, final MiningCoordinator miningCoordinator) { - this(() -> blockchain, miningCoordinator); + final BlockchainQueries blockchain, + final MiningCoordinator miningCoordinator, + final ApiConfiguration apiConfiguration) { + this(() -> blockchain, miningCoordinator, apiConfiguration); } public EthGasPrice( - final Supplier blockchain, final MiningCoordinator miningCoordinator) { + final Supplier blockchain, + final MiningCoordinator miningCoordinator, + final ApiConfiguration apiConfiguration) { this.blockchain = blockchain; this.miningCoordinator = miningCoordinator; + this.apiConfiguration = apiConfiguration; } @Override @@ -48,11 +57,37 @@ public String getName() { @Override public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { return new JsonRpcSuccessResponse( - requestContext.getRequest().getId(), - blockchain - .get() - .gasPrice() - .map(Quantity::create) - .orElseGet(() -> Quantity.create(miningCoordinator.getMinTransactionGasPrice()))); + requestContext.getRequest().getId(), Quantity.create(calculateGasPrice())); + } + + private Wei calculateGasPrice() { + Wei gasPrice = getGasPrice().orElseGet(miningCoordinator::getMinTransactionGasPrice); + 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 = + calculateBound( + minTransactionGasPrice, apiConfiguration.getLowerBoundGasAndPriorityFeeCoefficient()); + Wei upperBound = + calculateBound( + minTransactionGasPrice, apiConfiguration.getUpperBoundGasAndPriorityFeeCoefficient()); + + return gasPrice.compareTo(lowerBound) <= 0 + ? lowerBound + : gasPrice.compareTo(upperBound) >= 0 ? upperBound : gasPrice; + } + + private Wei calculateBound(final Wei price, final long coefficient) { + return price.multiply(coefficient).divide(100); } } 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 9d70e681fdf..88b9348edf0 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 @@ -174,7 +174,7 @@ protected Map create() { new EthMining(miningCoordinator), new EthCoinbase(miningCoordinator), new EthProtocolVersion(supportedCapabilities), - new EthGasPrice(blockchainQueries, miningCoordinator), + new EthGasPrice(blockchainQueries, miningCoordinator, apiConfiguration), new EthGetWork(miningCoordinator), new EthSubmitWork(miningCoordinator), new EthHashrate(miningCoordinator), diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthFeeHistoryTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthFeeHistoryTest.java index bffd9278e37..7d58848a132 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthFeeHistoryTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthFeeHistoryTest.java @@ -174,9 +174,9 @@ public void shouldBoundRewardsCorrectly() { ApiConfiguration apiConfiguration = ImmutableApiConfiguration.builder() - .isPriorityFeeLimitingEnabled(true) - .lowerBoundPriorityFeeCoefficient(200L) // Min reward = Wei.One * 200L / 100 = 2.0 - .upperBoundPriorityFeeCoefficient(500L) + .isGasAndPriorityFeeLimitingEnabled(true) + .lowerBoundGasAndPriorityFeeCoefficient(200L) // Min reward = Wei.One * 200L / 100 = 2.0 + .upperBoundGasAndPriorityFeeCoefficient(500L) .build(); // Max reward = Wei.One * 500L / 100 = 5.0 EthFeeHistory ethFeeHistory = 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 27462501010..cd1e890fd41 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 @@ -23,6 +23,7 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; 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.api.jsonrpc.internal.JsonRpcRequest; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; @@ -60,15 +61,8 @@ public class EthGasPriceTest { @BeforeEach public void setUp() { - method = - new EthGasPrice( - new BlockchainQueries( - blockchain, - null, - Optional.empty(), - Optional.empty(), - ImmutableApiConfiguration.builder().gasPriceMinSupplier(() -> 100).build()), - miningCoordinator); + ApiConfiguration apiConfig = createDefaultApiConfiguration(); + method = createEthGasPriceMethod(apiConfig); } @Test @@ -141,6 +135,68 @@ public void shortChainQueriesAllBlocks() { verifyNoMoreInteractions(blockchain); } + /** + * Test to verify that the method returns the lower bound gas price when the lower bound parameter + * is present. + */ + @Test + public void shouldReturnLimitedPriceWhenLowerBoundIsPresent() { + long gasPrice = 5000000; + long lowerBoundGasPrice = gasPrice + 1; + verifyGasPriceLimit(lowerBoundGasPrice, null, lowerBoundGasPrice); + } + + /** + * Test to verify that the method returns the upper bound gas price when the upper bound parameter + * is present. + */ + @Test + public void shouldReturnLimitedPriceWhenUpperBoundIsPresent() { + long gasPrice = 5000000; + long upperBoundGasPrice = gasPrice - 1; + verifyGasPriceLimit(null, upperBoundGasPrice, upperBoundGasPrice); + } + + /** + * Test to verify that the method returns the actual gas price when the gas price is within the + * bound range. + */ + @Test + public void shouldReturnActualGasPriceWhenWithinBoundRange() { + long gasPrice = 5000000; + long lowerBoundGasPrice = gasPrice - 1; + long upperBoundGasPrice = gasPrice + 1; + verifyGasPriceLimit(lowerBoundGasPrice, upperBoundGasPrice, gasPrice); + } + + /** + * 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 expectedGasPrice The expected gas price. + */ + private void verifyGasPriceLimit( + final Long lowerBound, final Long upperBound, final long expectedGasPrice) { + when(miningCoordinator.getMinTransactionGasPrice()).thenReturn(Wei.of(100)); + + var apiConfig = + createApiConfiguration(Optional.ofNullable(lowerBound), Optional.ofNullable(upperBound)); + method = createEthGasPriceMethod(apiConfig); + + final JsonRpcRequestContext request = requestWithParams(); + final JsonRpcResponse expectedResponse = + 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 JsonRpcResponse actualResponse = method.response(request); + assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); + } + private Object createFakeBlock(final Long height) { return Optional.of( new Block( @@ -210,4 +266,34 @@ private Object createEmptyBlock(final Long height) { private JsonRpcRequestContext requestWithParams(final Object... params) { return new JsonRpcRequestContext(new JsonRpcRequest(JSON_RPC_VERSION, ETH_METHOD, params)); } + + private ApiConfiguration createDefaultApiConfiguration() { + return createApiConfiguration(Optional.empty(), Optional.empty()); + } + + private ApiConfiguration createApiConfiguration( + final Optional lowerBound, final Optional upperBound) { + ImmutableApiConfiguration.Builder builder = + ImmutableApiConfiguration.builder().gasPriceMinSupplier(() -> 100); + + lowerBound.ifPresent( + value -> + builder + .isGasAndPriorityFeeLimitingEnabled(true) + .lowerBoundGasAndPriorityFeeCoefficient(value)); + upperBound.ifPresent( + value -> + builder + .isGasAndPriorityFeeLimitingEnabled(true) + .upperBoundGasAndPriorityFeeCoefficient(value)); + + return builder.build(); + } + + private EthGasPrice createEthGasPriceMethod(final ApiConfiguration apiConfig) { + return new EthGasPrice( + new BlockchainQueries(blockchain, null, Optional.empty(), Optional.empty(), apiConfig), + miningCoordinator, + apiConfig); + } }