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

Add blobs to eth_feeHistory #6679

Merged
Show file tree
Hide file tree
Changes from 4 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
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;

import static java.util.stream.Collectors.toUnmodifiableList;
import static org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessBlobGasCalculator.calculateExcessBlobGasForParent;

import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
Expand All @@ -36,14 +37,17 @@
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
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 java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.LongStream;
import java.util.stream.Stream;

Expand Down Expand Up @@ -104,15 +108,23 @@ public JsonRpcResponse response(final JsonRpcRequestContext request) {

final List<BlockHeader> blockHeaderRange = getBlockHeaders(firstBlock, lastBlock);
final List<Wei> requestedBaseFees = getBaseFees(blockHeaderRange);
final List<Wei> requestedBlobBaseFees = getBlobBaseFees(blockHeaderRange);
final Wei nextBaseFee =
getNextBaseFee(highestBlockNumber, chainHeadHeader, requestedBaseFees, blockHeaderRange);
final List<Double> gasUsedRatios = getGasUsedRatios(blockHeaderRange);
final List<Double> blobGasUsedRatios = getBlobGasUsedRatios(blockHeaderRange);
final Optional<List<List<Wei>>> maybeRewards =
maybeRewardPercentiles.map(rewards -> getRewards(rewards, blockHeaderRange));
return new JsonRpcSuccessResponse(
requestId,
createFeeHistoryResult(
firstBlock, requestedBaseFees, nextBaseFee, gasUsedRatios, maybeRewards));
firstBlock,
requestedBaseFees,
requestedBlobBaseFees,
nextBaseFee,
gasUsedRatios,
blobGasUsedRatios,
maybeRewards));
}

private Wei getNextBaseFee(
Expand Down Expand Up @@ -326,31 +338,88 @@ private List<BlockHeader> getBlockHeaders(final long oldestBlock, final long las
}

private List<Wei> getBaseFees(final List<BlockHeader> blockHeaders) {
// we return the base fees for the blocks requested and 1 more because we can always compute it
return blockHeaders.stream()
.map(blockHeader -> blockHeader.getBaseFee().orElse(Wei.ZERO))
.toList();
}

private List<Wei> getBlobBaseFees(final List<BlockHeader> blockHeaders) {
if (blockHeaders.isEmpty()) {
return Collections.emptyList();
}
// Calculate the BlobFee for the requested range
List<Wei> baseFeesPerBlobGas =
blockHeaders.stream().map(this::getBlobGasFee).collect(Collectors.toList());

// Calculate the next blob base fee and add it to the list
Wei nextBlobBaseFee = getNextBlobFee(blockHeaders.get(blockHeaders.size() - 1));
baseFeesPerBlobGas.add(nextBlobBaseFee);

return baseFeesPerBlobGas;
}

private Wei getBlobGasFee(final BlockHeader header) {
return blockchain
.getBlockHeader(header.getParentHash())
.map(parent -> getBlobGasFee(protocolSchedule.getByBlockHeader(header), parent))
.orElse(Wei.ZERO);
}

private Wei getBlobGasFee(final ProtocolSpec spec, final BlockHeader parent) {
return spec.getFeeMarket().blobGasPricePerGas(calculateExcessBlobGasForParent(spec, parent));
}

private Wei getNextBlobFee(final BlockHeader header) {
// Attempt to retrieve the next header based on the current header's number.
long nextBlockNumber = header.getNumber() + 1;
return blockchain
.getBlockHeader(nextBlockNumber)
.map(nextHeader -> getBlobGasFee(protocolSchedule.getByBlockHeader(nextHeader), header))
// If the next header is not present, calculate the fee using the current time.
.orElseGet(
() ->
getBlobGasFee(
protocolSchedule.getForNextBlockHeader(header, System.currentTimeMillis()),
header));
}

private List<Double> getGasUsedRatios(final List<BlockHeader> blockHeaders) {
return blockHeaders.stream()
.map(blockHeader -> blockHeader.getGasUsed() / (double) blockHeader.getGasLimit())
.toList();
}

private List<Double> getBlobGasUsedRatios(final List<BlockHeader> blockHeaders) {
return blockHeaders.stream().map(this::calculateBlobGasUsedRatio).toList();
}

private double calculateBlobGasUsedRatio(final BlockHeader blockHeader) {
ProtocolSpec spec = protocolSchedule.getByBlockHeader(blockHeader);
long blobGasUsed = blockHeader.getBlobGasUsed().orElse(0L);
double currentBlobGasLimit = spec.getGasLimitCalculator().currentBlobGasLimit();
Gabriel-Trintinalia marked this conversation as resolved.
Show resolved Hide resolved
if (currentBlobGasLimit == 0) {
return 0;
}
return blobGasUsed / currentBlobGasLimit;
}

private FeeHistory.FeeHistoryResult createFeeHistoryResult(
final long oldestBlock,
final List<Wei> explicitlyRequestedBaseFees,
final List<Wei> requestedBlobBaseFees,
final Wei nextBaseFee,
final List<Double> gasUsedRatios,
final List<Double> blobGasUsedRatio,
final Optional<List<List<Wei>>> maybeRewards) {
return FeeHistory.FeeHistoryResult.from(
ImmutableFeeHistory.builder()
.oldestBlock(oldestBlock)
.baseFeePerGas(
Stream.concat(explicitlyRequestedBaseFees.stream(), Stream.of(nextBaseFee))
.collect(toUnmodifiableList()))
.baseFeePerBlobGas(requestedBlobBaseFees)
.gasUsedRatio(gasUsedRatios)
.blobGasUsedRatio(blobGasUsedRatio)
.reward(maybeRewards)
.build());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,12 @@ public interface FeeHistory {

List<Wei> getBaseFeePerGas();

List<Wei> getBaseFeePerBlobGas();

List<Double> getGasUsedRatio();

List<Double> getBlobGasUsedRatio();

Optional<List<List<Wei>>> getReward();

@Value.Immutable
Expand All @@ -47,9 +51,15 @@ interface FeeHistoryResult {
@JsonProperty("baseFeePerGas")
List<String> getBaseFeePerGas();

@JsonProperty("baseFeePerBlobGas")
List<String> getBaseFeePerBlobGas();

@JsonProperty("gasUsedRatio")
List<Double> getGasUsedRatio();

@JsonProperty("blobGasUsedRatio")
List<Double> getBlobGasUsedRatio();

@Nullable
@JsonProperty("reward")
List<List<String>> getReward();
Expand All @@ -60,7 +70,9 @@ static FeeHistoryResult from(final FeeHistory feeHistory) {
feeHistory.getBaseFeePerGas().stream()
.map(Quantity::create)
.collect(toUnmodifiableList()),
feeHistory.getBaseFeePerBlobGas().stream().map(Quantity::create).toList(),
feeHistory.getGasUsedRatio(),
feeHistory.getBlobGasUsedRatio(),
feeHistory
.getReward()
.map(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.hyperledger.besu.consensus.merge.blockcreation.MergeCoordinator;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.GasLimitCalculator;
import org.hyperledger.besu.ethereum.api.ApiConfiguration;
import org.hyperledger.besu.ethereum.api.ImmutableApiConfiguration;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
Expand All @@ -50,6 +51,7 @@
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.evm.gascalculator.LondonGasCalculator;

import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -80,6 +82,14 @@ public void setUp() {
miningCoordinator = mock(MergeCoordinator.class);
when(miningCoordinator.getMinPriorityFeePerGas()).thenReturn(Wei.ONE);

final ProtocolSpec londonSpec = mock(ProtocolSpec.class);
when(londonSpec.getGasCalculator()).thenReturn(new LondonGasCalculator());
when(londonSpec.getFeeMarket()).thenReturn(FeeMarket.london(5));
when(londonSpec.getGasLimitCalculator()).thenReturn(mock(GasLimitCalculator.class));

when(protocolSchedule.getByBlockHeader(any())).thenReturn(londonSpec);
when(protocolSchedule.getForNextBlockHeader(any(), anyLong())).thenReturn(londonSpec);

method =
new EthFeeHistory(
protocolSchedule,
Expand All @@ -90,9 +100,6 @@ public void setUp() {

@Test
public void params() {
final ProtocolSpec londonSpec = mock(ProtocolSpec.class);
when(londonSpec.getFeeMarket()).thenReturn(FeeMarket.london(5));
when(protocolSchedule.getForNextBlockHeader(any(), anyLong())).thenReturn(londonSpec);
// should fail because no required params given
assertThatThrownBy(this::feeHistoryRequest).isInstanceOf(InvalidJsonRpcParameters.class);
// should fail because newestBlock not given
Expand All @@ -110,12 +117,7 @@ public void params() {

@Test
public void allFieldsPresentForLatestBlock() {
final ProtocolSpec londonSpec = mock(ProtocolSpec.class);
when(londonSpec.getFeeMarket()).thenReturn(FeeMarket.london(5));
when(protocolSchedule.getForNextBlockHeader(
eq(blockchain.getChainHeadHeader()),
eq(blockchain.getChainHeadHeader().getTimestamp())))
.thenReturn(londonSpec);

final Object latest =
((JsonRpcSuccessResponse) feeHistoryRequest("0x1", "latest", new double[] {100.0}))
.getResult();
Expand All @@ -126,6 +128,8 @@ public void allFieldsPresentForLatestBlock() {
.oldestBlock(10)
.baseFeePerGas(List.of(Wei.of(25496L), Wei.of(28683L)))
.gasUsedRatio(List.of(0.9999999992132459))
.baseFeePerBlobGas(List.of(Wei.of(0), Wei.of(0)))
.blobGasUsedRatio(List.of(0.0))
.reward(List.of(List.of(Wei.of(1524763764L))))
.build()));
}
Expand Down Expand Up @@ -250,29 +254,19 @@ public void blockCountBounds() {

@Test
public void doesntGoPastChainHeadWithHighBlockCount() {
final ProtocolSpec londonSpec = mock(ProtocolSpec.class);
when(londonSpec.getFeeMarket()).thenReturn(FeeMarket.london(5));
when(protocolSchedule.getForNextBlockHeader(
eq(blockchain.getChainHeadHeader()),
eq(blockchain.getChainHeadHeader().getTimestamp())))
.thenReturn(londonSpec);
final FeeHistory.FeeHistoryResult result =
(ImmutableFeeHistoryResult)
((JsonRpcSuccessResponse) feeHistoryRequest("0x14", "latest")).getResult();
assertThat(Long.decode(result.getOldestBlock())).isEqualTo(0);
assertThat(result.getBaseFeePerGas()).hasSize(12);
assertThat(result.getGasUsedRatio()).hasSize(11);
assertThat(result.getBaseFeePerBlobGas()).hasSize(12);
assertThat(result.getBlobGasUsedRatio()).hasSize(11);
assertThat(result.getReward()).isNull();
}

@Test
public void feeValuesAreInTheBlockCountAndHighestBlock() {
final ProtocolSpec londonSpec = mock(ProtocolSpec.class);
when(londonSpec.getFeeMarket()).thenReturn(FeeMarket.london(5));
when(protocolSchedule.getForNextBlockHeader(
eq(blockchain.getChainHeadHeader()),
eq(blockchain.getChainHeadHeader().getTimestamp())))
.thenReturn(londonSpec);
double[] percentile = new double[] {100.0};

final Object ninth =
Expand All @@ -286,12 +280,6 @@ public void feeValuesAreInTheBlockCountAndHighestBlock() {

@Test
public void feeValuesDontGoPastHighestBlock() {
final ProtocolSpec londonSpec = mock(ProtocolSpec.class);
when(londonSpec.getFeeMarket()).thenReturn(FeeMarket.london(5));
when(protocolSchedule.getForNextBlockHeader(
eq(blockchain.getChainHeadHeader()),
eq(blockchain.getChainHeadHeader().getTimestamp())))
.thenReturn(londonSpec);
double[] percentile = new double[] {100.0};

final Object second =
Expand Down Expand Up @@ -345,6 +333,10 @@ private void assertFeeMetadataSize(final Object feeObject, final int blockCount)
assertThat(((ImmutableFeeHistoryResult) feeObject).getReward().size()).isEqualTo(blockCount);
assertThat(((ImmutableFeeHistoryResult) feeObject).getGasUsedRatio().size())
.isEqualTo(blockCount);
assertThat(((ImmutableFeeHistoryResult) feeObject).getBaseFeePerBlobGas().size())
.isEqualTo(blockCount + 1);
assertThat(((ImmutableFeeHistoryResult) feeObject).getBlobGasUsedRatio().size())
.isEqualTo(blockCount);
}

private JsonRpcResponse feeHistoryRequest(final Object... params) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@
"0x3437004a",
"0x2dbc88c1"
],
"baseFeePerBlobGas" : [ "0x0", "0x1", "0x1" ],
"gasUsedRatio": [
0.004079142040086682,
0.003713085594819442
],
"blobGasUsedRatio" : [ 0.0, 0.3333333333333333 ],
Gabriel-Trintinalia marked this conversation as resolved.
Show resolved Hide resolved
"reward": [
[
"0x3b9aca00",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@
"0x3437004a",
"0x2dbc88c1"
],
"baseFeePerBlobGas" : [ "0x0", "0x1", "0x1" ],
"gasUsedRatio": [
0.004079142040086682,
0.003713085594819442
]
],
"blobGasUsedRatio" : [ 0.0, 0.3333333333333333 ]
}
},
"statusCode": 200
Expand Down
Loading