Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## 21.7.0-RC2

### Additions and Improvements
- eth_feeHistory API for wallet providers [\#2432](https://github.com/hyperledger/besu/pull/2432)
### Bug Fixes
- Ibft2 could create invalid RoundChange messages in some circumstances containing duplicate prepares [\#2449](https://github.com/hyperledger/besu/pull/2449)

Expand Down
18 changes: 10 additions & 8 deletions besu/src/test/java/org/hyperledger/besu/PrivacyReorgTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ public class PrivacyReorgTest {
Bytes.fromBase64String("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=");

private static final String FIRST_BLOCK_WITH_NO_TRANSACTIONS_STATE_ROOT =
"0xe368938a01d983e331eb0e4ea61224726d06075c1ad525569b369f664067ff26";
"0xb0784ff11dffceac824188583f20f3b8bb4ca275e033b3b1c0e280915743be7f";
private static final String FIRST_BLOCK_WITH_SINGLE_TRANSACTION_STATE_ROOT =
"0x9c88988f9602184efc538cf1c2f482a6b8757ff918d234602884dc8e3b983edd";
"0xe33629724501c0bc271a2b6858da64d3e92048d7e0cd019c5646770330694ff4";
private static final String BLOCK_WITH_SINGLE_TRANSACTION_RECEIPTS_ROOT =
"0xc8267b3f9ed36df3ff8adb51a6d030716f23eeb50270e7fce8d9822ffa7f0461";
private static final String STATE_ROOT_AFTER_TRANSACTION_APPENDED_TO_EMPTY_STATE =
Expand Down Expand Up @@ -221,7 +221,7 @@ public void privacyGroupHeadIsTracked() {
.contains(expected);

final String secondBlockStateRoot =
"0xd86a520e49caf215e7e4028262924db50540a5b26e415ab7c944e46a0c01d704";
"0x57a19f52a9ff4405428b3e605e136662d5c1b6be6f84f98f3b8c42ddac5139c2";
final Block secondBlock =
gen.block(getBlockOptionsNoTransaction(firstBlock, secondBlockStateRoot));

Expand Down Expand Up @@ -278,7 +278,7 @@ public void reorgToShorterChain() {
gen.block(getBlockOptionsNoTransaction(blockchain.getGenesisBlock(), firstBlockStateRoot));

final String secondBlockStateRoot =
"0x7e887f91d2a6205f4a643701aba022c2db0bac5ab235102ab7477edd7a8a4317";
"0xb3d70bce4428fb9b4549240b2130c4eea12c4ea36ae13108ed21289366a8d65f";
final Block secondBlock =
gen.block(
getBlockOptionsWithTransaction(
Expand All @@ -305,7 +305,7 @@ public void reorgToShorterChain() {
.plus(blockchain.getBlockByNumber(2).get().getHeader().getDifficulty());

final String forkBlockStateRoot =
"0x486b886bde6472e8d706f8eb4fb6378ebbdceb4848a5a8d69a726575b22e41b6";
"0x5c0adcdde38d38b4365c238c4ba05bf9ebfdf506f749b884b67003f375e43e4b";
final Block forkBlock =
gen.block(
getBlockOptionsNoTransactionWithDifficulty(
Expand Down Expand Up @@ -355,7 +355,7 @@ public void reorgToLongerChain() {
privateStateRootResolver, blockchain, STATE_ROOT_AFTER_TRANSACTION_APPENDED_TO_EMPTY_STATE);

final String secondForkBlockStateRoot =
"0x2c37a360a700c614b10c980138f64be9ad66fc4a14cd5145199cd0d8ec43d51d";
"0x76b8747d05fabcc87b2cfba519da0b1359b29fe7553bfd4837746edf31fb95fc";
final Block secondForkBlock =
gen.block(
getBlockOptionsNoTransactionWithDifficulty(
Expand All @@ -373,7 +373,7 @@ public void reorgToLongerChain() {

// Add another private transaction
final String thirdForkBlockStateRoot =
"0x8fe42678733e6099e7b10b9b1d4684b8f2ce3d6479cb122ea12932ef304a1793";
"0xcae9fa05107c1501c1962239f729d2f34186414abbaeb0dd1a3e0a6c899f79a3";
final Block thirdForkBlock =
gen.block(
getBlockOptionsWithTransactionAndDifficulty(
Expand All @@ -385,7 +385,8 @@ public void reorgToLongerChain() {
appendBlock(besuController, blockchain, protocolContext, thirdForkBlock);

// Check that the private state did change after reorg
assertPrivateStateRoot(privateStateRootResolver, blockchain, EMPTY_ROOT_HASH);
assertPrivateStateRoot(
privateStateRootResolver, blockchain, STATE_ROOT_AFTER_TRANSACTION_APPENDED_TO_EMPTY_STATE);
}

@SuppressWarnings("unchecked")
Expand Down Expand Up @@ -532,6 +533,7 @@ private BlockDataGenerator.BlockOptions getBlockOptionsWithTransactionAndDifficu
private BlockDataGenerator.BlockOptions getBlockOptions(
final BlockDataGenerator.BlockOptions blockOptions, final Block parentBlock) {
return blockOptions
.setBaseFee(Optional.empty())
.setBlockNumber(parentBlock.getHeader().getNumber() + 1)
.setParentHash(parentBlock.getHash())
.hasOmmers(false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.core.fees.EIP1559;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
Expand Down Expand Up @@ -150,7 +151,7 @@ public void setUp() {
syncState,
Wei.ZERO,
txPoolConfig,
Optional.empty());
Optional.of(new EIP1559(0)));

serviceImpl = new BesuEventsImpl(blockchain, blockBroadcaster, transactionPool, syncState);
}
Expand Down
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,188 @@
/*
* 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.jsonrpc.internal.results.ImmutableFeeHistoryResult;
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.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.Optional;
import java.util.stream.LongStream;
import java.util.stream.Stream;

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 Object requestId = request.getRequest().getId();

final long blockCount = request.getRequiredParameter(0, Long.class);
Comment thread
RatanRSur marked this conversation as resolved.
if (blockCount < 1 || blockCount > 1024) {
return new JsonRpcErrorResponse(requestId, JsonRpcError.INVALID_PARAMS);
}
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(
chainHeadBlockNumber /* both latest and pending use the head block until we have pending block support */);

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)
Comment thread
garyschulte marked this conversation as 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 =
blockHeaders.stream()
.map(blockHeader -> blockHeader.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);
Comment thread
garyschulte marked this conversation as 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(blockchain::getBlockByNumber)
.flatMap(Optional::stream)
Comment thread
garyschulte marked this conversation as resolved.
.map(
block ->
computeRewards(
rewardPercentiles.stream().sorted().collect(toUnmodifiableList()),
block))
.collect(toUnmodifiableList()));

final ImmutableFeeHistoryResult.Builder feeHistoryResultBuilder =
ImmutableFeeHistoryResult.builder()
.oldestBlock(oldestBlock)
.baseFeePerGas(
Stream.concat(explicitlyRequestedBaseFees.stream(), Stream.of(nextBaseFee))
.collect(toUnmodifiableList()))
.gasUsedRatio(gasUsedRatios);
maybeRewards.ifPresent(feeHistoryResultBuilder::reward);
return new JsonRpcSuccessResponse(requestId, feeHistoryResultBuilder.build());
}

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());

// We need to weight the percentile of rewards by the gas used in the transaction.
// That's why we're keeping track of the cumulative gas used and checking to see which
// percentile markers we've passed
Comment thread
garyschulte marked this conversation as resolved.
final ArrayList<Long> rewards = new ArrayList<>();
int rewardPercentileIndex = 0;
long gasUsed = 0;
for (final Transaction transaction : transactionsAscendingEffectiveGasFee) {

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

while (rewardPercentileIndex < rewardPercentiles.size()
&& 100.0 * gasUsed / block.getHeader().getGasUsed()
>= rewardPercentiles.get(rewardPercentileIndex)) {
rewards.add(transaction.getEffectivePriorityFeePerGas(baseFee));
rewardPercentileIndex++;
}
}
return rewards;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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.results;

import java.util.List;
import javax.annotation.Nullable;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.immutables.value.Value;

@Value.Immutable
@JsonInclude(JsonInclude.Include.NON_NULL)
public interface FeeHistoryResult {

@JsonProperty("oldestBlock")
long getOldestBlock();

@JsonProperty("baseFeePerGas")
List<Long> getBaseFeePerGas();

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

@Nullable
@JsonProperty("reward")
List<List<Long>> getReward();
}
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
Original file line number Diff line number Diff line change
Expand Up @@ -1699,8 +1699,13 @@ private void assertTransactionResultMatchesTransaction(
assertThat(result.getValue("to")).isNull();
}
assertThat(Wei.fromHexString(result.getString("value"))).isEqualTo(transaction.getValue());
assertThat(Wei.fromHexString(result.getString("gasPrice")))
.isEqualTo(transaction.getGasPrice().get());
assertThat(Optional.ofNullable(result.getString("gasPrice")).map(Wei::fromHexString))
.isEqualTo(transaction.getGasPrice());
assertThat(Optional.ofNullable(result.getString("maxFeePerGas")).map(Wei::fromHexString))
.isEqualTo(transaction.getMaxFeePerGas());
assertThat(
Optional.ofNullable(result.getString("maxPriorityFeePerGas")).map(Wei::fromHexString))
.isEqualTo(transaction.getMaxPriorityFeePerGas());
assertThat(Long.decode(result.getString("gas"))).isEqualTo(transaction.getGasLimit());
assertThat(Bytes.fromHexString(result.getString("input"))).isEqualTo(transaction.getPayload());
}
Expand Down
Loading