Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New cli options to limit rewards return by eth_feeHistory #6202

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 56 additions & 9 deletions besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -1217,6 +1217,29 @@ static class PermissionsOptionGroup {
description = "Maximum gas price for eth_gasPrice (default: ${DEFAULT-VALUE})")
private final Long apiGasPriceMax = 500_000_000_000L;

@CommandLine.Option(
names = {"--api-priority-fee-limiting-enabled"},
hidden = true,
description =
"Set to enable priority fee limit in eth_feeHistory (default: ${DEFAULT-VALUE})")
private final Boolean apiPriorityFeeLimitingEnabled = false;

@CommandLine.Option(
names = {"--api-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;

@CommandLine.Option(
names = {"--api-priority-fee-upper-bound-coefficient"},
Gabriel-Trintinalia marked this conversation as resolved.
Show resolved Hide resolved
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;

@CommandLine.Option(
names = {"--static-nodes-file"},
paramLabel = MANDATORY_FILE_FORMAT_HELP,
Expand Down Expand Up @@ -1875,6 +1898,17 @@ private void validateDnsOptionsParams() {
}
}

private void checkApiOptionsDependencies() {
CommandLineUtils.checkOptionDependencies(
logger,
commandLine,
"--api-priority-fee-limiting-enabled",
!apiPriorityFeeLimitingEnabled,
asList(
"--api-priority-fee-upper-bound-coefficient",
"--api-priority-fee-lower-bound-coefficient"));
}

private void ensureValidPeerBoundParams() {
maxPeers = p2PDiscoveryOptionGroup.maxPeers;
peersLowerBound = unstableNetworkingOptions.toDomainObject().getPeerLowerBound();
Expand Down Expand Up @@ -2485,15 +2519,28 @@ && rpcWsAuthenticationCredentialsFile() == null
}

private ApiConfiguration apiConfiguration() {
return ImmutableApiConfiguration.builder()
.gasPriceBlocks(apiGasPriceBlocks)
.gasPricePercentile(apiGasPricePercentile)
.gasPriceMinSupplier(
getMiningParameters().getMinTransactionGasPrice().getAsBigInteger()::longValueExact)
.gasPriceMax(apiGasPriceMax)
.maxLogsRange(rpcMaxLogsRange)
.gasCap(rpcGasCap)
.build();
checkApiOptionsDependencies();
var builder =
ImmutableApiConfiguration.builder()
.gasPriceBlocks(apiGasPriceBlocks)
.gasPricePercentile(apiGasPricePercentile)
.gasPriceMinSupplier(
getMiningParameters().getMinTransactionGasPrice().getAsBigInteger()::longValueExact)
.gasPriceMax(apiGasPriceMax)
.maxLogsRange(rpcMaxLogsRange)
.gasCap(rpcGasCap)
.isPriorityFeeLimitingEnabled(apiPriorityFeeLimitingEnabled);
if (apiPriorityFeeLimitingEnabled) {
if (apiPriorityFeeLowerBoundCoefficient > apiPriorityFeeUpperBoundCoefficient) {
throw new ParameterException(
this.commandLine,
"--api-priority-fee-lower-bound-coefficient cannot be greater than the value of --api-priority-fee-upper-bound-coefficient");
}
builder
.lowerBoundPriorityFeeCoefficient(apiPriorityFeeLowerBoundCoefficient)
.upperBoundPriorityFeeCoefficient(apiPriorityFeeUpperBoundCoefficient);
}
return builder.build();
}

/**
Expand Down
71 changes: 71 additions & 0 deletions besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1609,6 +1609,77 @@ public void rpcGasCapOptionMustBeUsed() {
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}

@Test
public void apiPriorityFeeLimitingEnabledOptionMustBeUsed() {
parseCommand("--api-priority-fee-limiting-enabled");
verify(mockRunnerBuilder).apiConfiguration(apiConfigurationCaptor.capture());
verify(mockRunnerBuilder).build();
assertThat(apiConfigurationCaptor.getValue())
.isEqualTo(ImmutableApiConfiguration.builder().isPriorityFeeLimitingEnabled(true).build());

assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}

@Test
public void apiPriorityFeeLowerBoundCoefficientOptionMustBeUsed() {
final long lowerBound = 150L;
parseCommand(
"--api-priority-fee-lower-bound-coefficient",
Long.toString(lowerBound),
"--api-priority-fee-limiting-enabled");
verify(mockRunnerBuilder).apiConfiguration(apiConfigurationCaptor.capture());
verify(mockRunnerBuilder).build();
assertThat(apiConfigurationCaptor.getValue())
.isEqualTo(
ImmutableApiConfiguration.builder()
.lowerBoundPriorityFeeCoefficient(lowerBound)
.isPriorityFeeLimitingEnabled(true)
.build());

assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}

@Test
public void
apiPriorityFeeLowerBoundCoefficients_MustNotBeGreaterThan_apiPriorityFeeUpperBoundCoefficient() {
final long lowerBound = 200L;
final long upperBound = 100L;

parseCommand(
"--api-priority-fee-limiting-enabled",
"--api-priority-fee-lower-bound-coefficient",
Long.toString(lowerBound),
"--api-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");
}

@Test
public void apiPriorityFeeUpperBoundCoefficientsOptionMustBeUsed() {
final long upperBound = 200L;
parseCommand(
"--api-priority-fee-upper-bound-coefficient",
Long.toString(upperBound),
"--api-priority-fee-limiting-enabled");
verify(mockRunnerBuilder).apiConfiguration(apiConfigurationCaptor.capture());
verify(mockRunnerBuilder).build();
assertThat(apiConfigurationCaptor.getValue())
.isEqualTo(
ImmutableApiConfiguration.builder()
.upperBoundPriorityFeeCoefficient(upperBound)
.isPriorityFeeLimitingEnabled(true)
.build());

assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}

@Test
public void p2pPeerUpperBound_without_p2pPeerLowerBound_shouldSetLowerBoundEqualToUpperBound() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ public Wei getMinTransactionGasPrice() {
return activeMiningCoordinator.getMinTransactionGasPrice();
}

@Override
public Wei getMinPriorityFeePerGas() {
return activeMiningCoordinator.getMinPriorityFeePerGas();
}

@Override
public void setExtraData(final Bytes extraData) {
activeMiningCoordinator.setExtraData(extraData);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,15 @@ public Wei getMinTransactionGasPrice() {
return miningParameters.getMinTransactionGasPrice();
}

/**
* Gets min priority fee per gas
*
* @return min priority fee per gas
*/
public Wei getMinPriorityFeePerGas() {
return miningParameters.getMinPriorityFeePerGas();
}

/**
* Create extra data bytes.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@ public Wei getMinTransactionGasPrice() {
return blockCreatorFactory.getMinTransactionGasPrice();
}

@Override
public Wei getMinPriorityFeePerGas() {
return blockCreatorFactory.getMinPriorityFeePerGas();
}

@Override
public void setExtraData(final Bytes extraData) {
blockCreatorFactory.setExtraData(extraData);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,11 @@ public Wei getMinTransactionGasPrice() {
return miningParameters.getMinTransactionGasPrice();
}

@Override
public Wei getMinPriorityFeePerGas() {
return miningParameters.getMinPriorityFeePerGas();
}

@Override
public void setExtraData(final Bytes extraData) {
this.miningParameters.setExtraData(extraData);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ public Wei getMinTransactionGasPrice() {
return dispatchFunctionAccordingToMergeState(MiningCoordinator::getMinTransactionGasPrice);
}

@Override
public Wei getMinPriorityFeePerGas() {
return dispatchFunctionAccordingToMergeState(MiningCoordinator::getMinPriorityFeePerGas);
}

@Override
public void setExtraData(final Bytes extraData) {
miningCoordinator.setExtraData(extraData);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
@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;

@Value.Default
public long getGasPriceBlocks() {
return 100;
Expand Down Expand Up @@ -59,4 +62,19 @@ public Long getMaxLogsRange() {
public Long getGasCap() {
return 0L;
}

@Value.Default
public boolean isPriorityFeeLimitingEnabled() {
return false;
}

@Value.Default
public Long getLowerBoundPriorityFeeCoefficient() {
return DEFAULT_LOWER_BOUND_PRIORITY_FEE_COEFFICIENT;
}

@Value.Default
public Long getUpperBoundPriorityFeeCoefficient() {
return DEFAULT_UPPER_BOUND_PRIORITY_FEE_COEFFICIENT;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

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.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameter;
Expand All @@ -28,6 +29,7 @@
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.FeeHistory;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.ImmutableFeeHistory;
import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
Expand All @@ -53,14 +55,22 @@
public class EthFeeHistory implements JsonRpcMethod {
private final ProtocolSchedule protocolSchedule;
private final Blockchain blockchain;
private final MiningCoordinator miningCoordinator;
private final ApiConfiguration apiConfiguration;
private final Cache<RewardCacheKey, List<Wei>> cache;
private static final int MAXIMUM_CACHE_SIZE = 100_000;

record RewardCacheKey(Hash blockHash, List<Double> rewardPercentiles) {}

public EthFeeHistory(final ProtocolSchedule protocolSchedule, final Blockchain blockchain) {
public EthFeeHistory(
final ProtocolSchedule protocolSchedule,
final Blockchain blockchain,
final MiningCoordinator miningCoordinator,
final ApiConfiguration apiConfiguration) {
this.protocolSchedule = protocolSchedule;
this.blockchain = blockchain;
this.miningCoordinator = miningCoordinator;
this.apiConfiguration = apiConfiguration;
this.cache = Caffeine.newBuilder().maximumSize(MAXIMUM_CACHE_SIZE).build();
}

Expand Down Expand Up @@ -203,7 +213,16 @@ public List<Wei> computeRewards(final List<Double> rewardPercentiles, final Bloc
final List<Long> transactionsGasUsed = calculateTransactionsGasUsed(block);
final List<TransactionInfo> transactionsInfo =
generateTransactionsInfo(transactions, transactionsGasUsed, baseFee);
return calculateRewards(rewardPercentiles, block, transactionsInfo);

var realRewards = calculateRewards(rewardPercentiles, block, transactionsInfo);

// If the priority fee boundary is set, return the bounded rewards. Otherwise, return the real
// rewards.
if (apiConfiguration.isPriorityFeeLimitingEnabled()) {
return boundRewards(realRewards);
} else {
return realRewards;
}
}

private List<Wei> calculateRewards(
Expand Down Expand Up @@ -235,6 +254,46 @@ private List<Wei> calculateRewards(
return rewards;
}

/**
* This method returns a list of bounded rewards.
*
* @param rewards The list of rewards to be bounded.
* @return The list of bounded rewards.
*/
private List<Wei> boundRewards(final List<Wei> rewards) {
Wei minPriorityFee = miningCoordinator.getMinPriorityFeePerGas();
Wei lowerBound =
minPriorityFee.multiply(apiConfiguration.getLowerBoundPriorityFeeCoefficient()).divide(100);
Wei upperBound =
minPriorityFee.multiply(apiConfiguration.getUpperBoundPriorityFeeCoefficient()).divide(100);

return rewards.stream().map(reward -> boundReward(reward, lowerBound, upperBound)).toList();
}

/**
* This method bounds the reward between a lower and upper limit.
*
* @param reward The reward to be bounded.
* @param lowerBound The lower limit for the reward.
* @param upperBound The upper limit for the reward.
* @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;
}

private List<Long> calculateTransactionsGasUsed(final Block block) {
final List<Long> transactionsGasUsed = new ArrayList<>();
long cumulativeGasUsed = 0L;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,11 @@ protected Map<String, JsonRpcMethod> create() {
blockchainQueries.getWorldStateArchive(),
protocolSchedule,
apiConfiguration.getGasCap())),
new EthFeeHistory(protocolSchedule, blockchainQueries.getBlockchain()),
new EthFeeHistory(
protocolSchedule,
blockchainQueries.getBlockchain(),
miningCoordinator,
apiConfiguration),
new EthGetCode(blockchainQueries),
new EthGetLogs(blockchainQueries, apiConfiguration.getMaxLogsRange()),
new EthGetProof(blockchainQueries),
Expand Down
Loading
Loading