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

eth_feeHistory #2432

Closed
wants to merge 39 commits into from
Closed
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
62dd31f
beginning
RatanRSur Jun 14, 2021
ec78644
fee history class and other stuff
RatanRSur Jun 14, 2021
27a79f1
get first 3 fields working
RatanRSur Jun 14, 2021
e250c30
add a bunch of stuff to the failing test
RatanRSur Jun 14, 2021
22dd003
add headers
RatanRSur Jun 14, 2021
dedf4dc
spotless
RatanRSur Jun 14, 2021
b5a6470
get all fields test working
RatanRSur Jun 16, 2021
3f7ac5f
0 for base fee
RatanRSur Jun 16, 2021
97a147c
add extra base fee
RatanRSur Jun 17, 2021
1470c40
update names to latest spec
RatanRSur Jun 17, 2021
7f61250
test block count higher than chain head
RatanRSur Jun 17, 2021
70fdcca
add test for all zeros
RatanRSur Jun 17, 2021
15ce6b5
error on block too high
RatanRSur Jun 17, 2021
8b3a7e0
spotless
RatanRSur Jun 17, 2021
4a4a354
get rpc spec test wired up and failing
RatanRSur Jun 17, 2021
ad89e50
fix tests
RatanRSur Jun 17, 2021
d0051dc
spotless
RatanRSur Jun 17, 2021
60b5184
sorting and comments
RatanRSur Jun 17, 2021
c55e7a9
remove redundant assignment
RatanRSur Jun 17, 2021
760338f
fix transaction selector
RatanRSur Jun 17, 2021
b169d79
make test presume 1559
RatanRSur Jun 17, 2021
cfb06bc
fix range errors
RatanRSur Jun 17, 2021
253259f
switch to Immutable
RatanRSur Jun 17, 2021
7ba5890
add fork block stuff
RatanRSur Jun 17, 2021
783ac54
spotless
RatanRSur Jun 17, 2021
4c89cdf
Merge branch 'master' of github.com:hyperledger/besu into fee-history
RatanRSur Jun 17, 2021
8aa77a6
test upper bound for block count
RatanRSur Jun 17, 2021
4aa9cc5
cleanup
RatanRSur Jun 17, 2021
07f5f71
Merge branch 'master' of github.com:hyperledger/besu into fee-history
RatanRSur Jun 21, 2021
ae2c951
fix reorg tests
RatanRSur Jun 21, 2021
3afe531
Merge branch 'master' of github.com:hyperledger/besu into fee-history
RatanRSur Jun 21, 2021
cc96aad
fix tests
RatanRSur Jun 21, 2021
b07979c
fix epfpg calc
RatanRSur Jun 21, 2021
89cdd45
fix lambda names
RatanRSur Jun 22, 2021
a65895d
remove unnecessary check
RatanRSur Jun 22, 2021
47de7f9
changelog
RatanRSur Jun 22, 2021
ce02ef3
Merge branch 'master' into fee-history
garyschulte Jun 22, 2021
7e31430
Merge branch 'master' of github.com:hyperledger/besu into fee-history
RatanRSur Jun 22, 2021
a098f81
update ref tests
RatanRSur Jun 22, 2021
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 @@ -71,6 +71,7 @@ public enum RpcMethod {
ETH_CHAIN_ID("eth_chainId"),
ETH_COINBASE("eth_coinbase"),
ETH_ESTIMATE_GAS("eth_estimateGas"),
ETH_FEE_HISTORY("eth_feeHistory"),
ETH_GAS_PRICE("eth_gasPrice"),
ETH_GET_BALANCE("eth_getBalance"),
ETH_GET_BLOCK_BY_HASH("eth_getBlockByHash"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;

import static java.util.stream.Collectors.toUnmodifiableList;

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;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
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.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.LongStream;
import java.util.stream.Stream;

import com.fasterxml.jackson.annotation.JsonInclude;

public class EthFeeHistory implements JsonRpcMethod {
private final ProtocolSchedule protocolSchedule;
private final BlockchainQueries blockchainQueries;
private final Blockchain blockchain;

public EthFeeHistory(
final ProtocolSchedule protocolSchedule, final BlockchainQueries blockchainQueries) {
this.protocolSchedule = protocolSchedule;
this.blockchainQueries = blockchainQueries;
this.blockchain = blockchainQueries.getBlockchain();
}

@Override
public String getName() {
return RpcMethod.ETH_FEE_HISTORY.getMethodName();
}

@Override
public JsonRpcResponse response(final JsonRpcRequestContext request) {
final long blockCount = request.getRequiredParameter(0, Long.class);
RatanRSur marked this conversation as resolved.
Show resolved Hide resolved
final BlockParameter highestBlock = request.getRequiredParameter(1, BlockParameter.class);
final Optional<List<Double>> maybeRewardPercentiles =
request.getOptionalParameter(2, Double[].class).map(Arrays::asList);

final long chainHeadBlockNumber = blockchain.getChainHeadBlockNumber();
final long resolvedHighestBlockNumber =
highestBlock
.getNumber()
.orElse(
highestBlock.isEarliest()
RatanRSur marked this conversation as resolved.
Show resolved Hide resolved
? BlockHeader.GENESIS_BLOCK_NUMBER
: chainHeadBlockNumber /* both latest and pending use the head block until we have pending block support */);

final Object requestId = request.getRequest().getId();
if (resolvedHighestBlockNumber > chainHeadBlockNumber) {
return new JsonRpcErrorResponse(requestId, JsonRpcError.INVALID_PARAMS);
}

final long oldestBlock = Math.max(0, resolvedHighestBlockNumber - (blockCount - 1));

final List<BlockHeader> blockHeaders =
LongStream.range(oldestBlock, oldestBlock + blockCount)
garyschulte marked this conversation as resolved.
Show resolved Hide resolved
.mapToObj(blockchain::getBlockHeader)
.flatMap(Optional::stream)
.collect(toUnmodifiableList());

// we return the base fees for the blocks requested and 1 more because we can always compute it
final List<Long> explicitlyRequestedBaseFees =
LongStream.range(oldestBlock, oldestBlock + blockCount)
garyschulte marked this conversation as resolved.
Show resolved Hide resolved
.mapToObj(blockchain::getBlockHeader)
.map(
maybeBlockHeader ->
maybeBlockHeader.flatMap(ProcessableBlockHeader::getBaseFee).orElse(0L))
.collect(toUnmodifiableList());
final long nextBlockNumber = resolvedHighestBlockNumber + 1;
final Long nextBaseFee =
blockchain
.getBlockHeader(nextBlockNumber)
.map(blockHeader -> blockHeader.getBaseFee().orElse(0L))
.orElseGet(
() ->
protocolSchedule
.getByBlockNumber(nextBlockNumber)
.getEip1559()
.map(
eip1559 -> {
final BlockHeader lastBlockHeader =
blockHeaders.get(blockHeaders.size() - 1);
garyschulte marked this conversation as resolved.
Show resolved Hide resolved
return eip1559.computeBaseFee(
nextBlockNumber,
explicitlyRequestedBaseFees.get(
explicitlyRequestedBaseFees.size() - 1),
lastBlockHeader.getGasUsed(),
eip1559.targetGasUsed(lastBlockHeader));
})
.orElse(0L));

final List<Double> gasUsedRatios =
blockHeaders.stream()
.map(blockHeader -> blockHeader.getGasUsed() / (double) blockHeader.getGasLimit())
.collect(toUnmodifiableList());

final Optional<List<List<Long>>> maybeRewards =
maybeRewardPercentiles.map(
rewardPercentiles ->
LongStream.range(oldestBlock, oldestBlock + blockCount)
.mapToObj(blockNumber -> blockchain.getBlockByNumber(blockNumber).get())
garyschulte marked this conversation as resolved.
Show resolved Hide resolved
.map(block -> computeRewards(rewardPercentiles, block))
.collect(toUnmodifiableList()));

return new JsonRpcSuccessResponse(
requestId,
new FeeHistory(
oldestBlock,
Stream.concat(explicitlyRequestedBaseFees.stream(), Stream.of(nextBaseFee))
.collect(toUnmodifiableList()),
gasUsedRatios,
maybeRewards));
}

private List<Long> computeRewards(
final List<Double> rewardPercentiles, final org.hyperledger.besu.ethereum.core.Block block) {
final List<Transaction> transactions = block.getBody().getTransactions();
if (transactions.isEmpty()) {
// all 0's for empty block
return LongStream.generate(() -> 0)
.limit(rewardPercentiles.size())
.boxed()
.collect(toUnmodifiableList());
}

final Optional<Long> baseFee = block.getHeader().getBaseFee();
final List<Transaction> transactionsAscendingEffectiveGasFee =
transactions.stream()
.sorted(
Comparator.comparing(
transaction -> transaction.getEffectivePriorityFeePerGas(baseFee)))
.collect(toUnmodifiableList());

final ArrayList<Long> rewards = new ArrayList<>();
int rewardIndex = 0;
long gasUsed = 0;
for (final Transaction transaction : transactionsAscendingEffectiveGasFee) {

gasUsed +=
blockchainQueries
.transactionReceiptByTransactionHash(transaction.getHash())
.get()
.getGasUsed();

while (rewardIndex < rewardPercentiles.size()
&& 100.0 * gasUsed / block.getHeader().getGasUsed()
>= rewardPercentiles.get(rewardIndex)) {
rewards.add(transaction.getEffectivePriorityFeePerGas(baseFee));
rewardIndex++;
}
}
return rewards;
}

@JsonInclude(JsonInclude.Include.NON_NULL)
public static class FeeHistory {
garyschulte marked this conversation as resolved.
Show resolved Hide resolved

private final long oldestBlock;
private final List<Long> baseFeePerGas;
private final List<Double> gasUsedRatio;
private final List<List<Long>> reward;

public FeeHistory(
final long oldestBlock,
final List<Long> baseFeePerGas,
final List<Double> gasUsedRatio,
final Optional<List<List<Long>>> reward) {
this.oldestBlock = oldestBlock;
this.baseFeePerGas = baseFeePerGas;
this.gasUsedRatio = gasUsedRatio;
this.reward = reward.orElse(null);
}

public long getOldestBlock() {
return oldestBlock;
}

public List<Long> getBaseFeePerGas() {
return baseFeePerGas;
}

public List<Double> getGasUsedRatio() {
return gasUsedRatio;
}

public List<List<Long>> getReward() {
return reward;
}

@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final FeeHistory that = (FeeHistory) o;
return oldestBlock == that.oldestBlock
&& baseFeePerGas.equals(that.baseFeePerGas)
&& gasUsedRatio.equals(that.gasUsedRatio)
&& reward.equals(that.reward);
}

@Override
public int hashCode() {
return Objects.hash(oldestBlock, baseFeePerGas, gasUsedRatio, reward);
}

@Override
public String toString() {
return String.format(
"FeeHistory{firstBlock=%d, baseFees=%s, gasUsedRatios=%s, rewards=%s}",
oldestBlock, baseFeePerGas, gasUsedRatio, reward);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.EthChainId;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.EthCoinbase;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.EthEstimateGas;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.EthFeeHistory;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.EthGasPrice;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.EthGetBalance;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.EthGetBlockByHash;
Expand Down Expand Up @@ -128,6 +129,7 @@ protected Map<String, JsonRpcMethod> create() {
blockchainQueries.getWorldStateArchive(),
protocolSchedule,
privacyParameters)),
new EthFeeHistory(protocolSchedule, blockchainQueries),
new EthGetCode(blockchainQueries, Optional.of(privacyParameters)),
new EthGetLogs(blockchainQueries),
new EthGetProof(blockchainQueries),
Expand Down
Loading