Skip to content

Commit

Permalink
New cli options to limit rewards return by eth_feeHistory (hyperledge…
Browse files Browse the repository at this point in the history
…r#6202)

Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net>
Signed-off-by: jflo <justin+github@florentine.us>
  • Loading branch information
Gabriel-Trintinalia authored and jflo committed Dec 12, 2023
1 parent 4d21d5f commit 003ad3f
Show file tree
Hide file tree
Showing 15 changed files with 323 additions and 30 deletions.
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"},
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

0 comments on commit 003ad3f

Please sign in to comment.