Skip to content

Commit

Permalink
rpcdaemon: fix gas and rewards in eth_feeHistory (#1751)
Browse files Browse the repository at this point in the history
  • Loading branch information
lupin012 committed Jan 11, 2024
1 parent 8f4e46a commit bcbdadd
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 34 deletions.
6 changes: 5 additions & 1 deletion silkworm/rpc/commands/eth_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2144,10 +2144,14 @@ Task<void> EthereumRpcApi::handle_fee_history(const nlohmann::json& request, nlo
return core::get_receipts(tx_database, block_with_hash);
};

rpc::fee_history::LatestBlockProvider latest_block_provider = [&tx_database]() {
return core::get_block_number(core::kLatestBlockId, tx_database);
};

auto chain_config = co_await chain_storage->read_chain_config();
ensure(chain_config.has_value(), "cannot read chain config");

rpc::fee_history::FeeHistoryOracle oracle{*chain_config, block_provider, receipts_provider};
rpc::fee_history::FeeHistoryOracle oracle{*chain_config, block_provider, receipts_provider, latest_block_provider};

const auto block_number = co_await core::get_block_number(newest_block, tx_database);
const auto fee_history = co_await oracle.fee_history(block_number, block_count, reward_percentiles);
Expand Down
4 changes: 2 additions & 2 deletions silkworm/rpc/commands/eth_api_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,10 @@ TEST_CASE_METHOD(test::RpcApiE2ETest, "fuzzy: eth_feeHistory sigsegv valid input
"jsonrpc":"2.0",
"id":1,
"result":{
"baseFeePerGas":["0x3b9aca00","0x342770c0","0x2db08786","0x2db08786"],
"baseFeePerGas":["0x3b9aca00","0x342770c0","0x2db08786","0x2806be9d"],
"gasUsedRatio":[0.0,0.0042,0.0042],
"oldestBlock":"0x0",
"reward":[[],["0x342770c1","0x342770c1"],["0x2db08787","0x2db08787"]]}
"reward":[["0x0","0x0"],["0x1","0x1"],["0x1","0x1"]]}
})"_json);
}
#endif // SILKWORM_SANITIZE
Expand Down
66 changes: 43 additions & 23 deletions silkworm/rpc/core/fee_history_oracle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,27 @@ void to_json(nlohmann::json& json, const Rewards& rewards) {
}

void to_json(nlohmann::json& json, const FeeHistory& fh) {
json["baseFeePerGas"] = nlohmann::json::array();
for (const auto& fee : fh.base_fees_per_gas) {
json["baseFeePerGas"].push_back(to_quantity(fee));
if (fh.gas_used_ratio.empty()) {
json["gasUsedRatio"] = nullptr;
} else {
json["gasUsedRatio"] = fh.gas_used_ratio;
}

json["gasUsedRatio"] = fh.gas_used_ratio;
json["oldestBlock"] = to_quantity(fh.oldest_block);

json["reward"] = nlohmann::json::array();
for (const auto& rewards : fh.rewards) {
nlohmann::json item;
to_json(item, rewards);
json["reward"].push_back(item);
if (!fh.base_fees_per_gas.empty()) {
json["baseFeePerGas"] = nlohmann::json::array();
for (const auto& fee : fh.base_fees_per_gas) {
json["baseFeePerGas"].push_back(to_quantity(fee));
}
}

if (!fh.rewards.empty()) {
json["reward"] = nlohmann::json::array();
for (const auto& rewards : fh.rewards) {
nlohmann::json item;
to_json(item, rewards);
json["reward"].push_back(item);
}
}
}

Expand Down Expand Up @@ -82,7 +90,9 @@ Task<FeeHistory> FeeHistoryOracle::fee_history(BlockNum newest_block,
const auto max_history = reward_percentiles.empty() ? kDefaultMaxHeaderHistory : kDefaultMaxBlockHistory;

const auto block_range = co_await resolve_block_range(newest_block, block_count, max_history);

if (block_range.num_blocks == 0) {
co_return fee_history;
}
fee_history.rewards.resize(block_range.num_blocks);
fee_history.base_fees_per_gas.resize(block_range.num_blocks + 1);
fee_history.gas_used_ratio.resize(block_range.num_blocks);
Expand Down Expand Up @@ -135,6 +145,7 @@ Task<FeeHistory> FeeHistoryOracle::fee_history(BlockNum newest_block,
}
fee_history.base_fees_per_gas.resize(first_missing + 1);
fee_history.gas_used_ratio.resize(first_missing);
fee_history.oldest_block = oldest_block_number;

co_return fee_history;
}
Expand All @@ -146,8 +157,9 @@ Task<BlockRange> FeeHistoryOracle::resolve_block_range(BlockNum newest_block, ui
}

if (max_history != 0) {
const auto latest_block_number = co_await latest_block_provider_();
// Limit retrieval to the given number of latest blocks
const auto too_old_count = newest_block - max_history + block_count;
const auto too_old_count = latest_block_number - max_history - newest_block + block_count;
if (too_old_count > 0) {
// too_old_count is the number of requested blocks that are too old to be served
if (block_count > too_old_count) {
Expand All @@ -168,20 +180,24 @@ Task<BlockRange> FeeHistoryOracle::resolve_block_range(BlockNum newest_block, ui
co_return BlockRange{block_count, newest_block, block_with_hash, receipts};
}

bool sort_by_reward(std::pair<intx::uint256, uint64_t>& p1, const std::pair<intx::uint256, uint64_t>& p2) {
return (p1.first < p2.first);
}

Task<void> FeeHistoryOracle::process_block(BlockFees& block_fees, const std::vector<int8_t>& reward_percentiles) {
auto& header = block_fees.block->block.header;
block_fees.base_fee = header.base_fee_per_gas.value_or(0);

block_fees.gas_used_ratio = static_cast<double>(header.gas_used) / static_cast<double>(header.gas_limit);

const auto parent_block = co_await block_provider_(header.number - 1);
const auto parent_block = co_await block_provider_(header.number + 1);
if (!parent_block) {
co_return;
}

const auto evmc_revision = config_.revision(parent_block->block.header.number, parent_block->block.header.timestamp);
block_fees.next_base_fee = 0;
if (evmc_revision >= EVMC_LONDON) {
block_fees.next_base_fee = protocol::expected_base_fee_per_gas(parent_block->block.header);
block_fees.next_base_fee = protocol::expected_base_fee_per_gas(header);
}

if (reward_percentiles.empty()) {
Expand All @@ -193,25 +209,29 @@ Task<void> FeeHistoryOracle::process_block(BlockFees& block_fees, const std::vec

if (block_fees.block->block.transactions.empty()) {
std::fill(block_fees.rewards.begin(), block_fees.rewards.end(), 0);
// return an all zero row if there are no transactions to gather data from
for (size_t idx = 0; idx < reward_percentiles.size(); idx++) {
block_fees.rewards.push_back(0);
}
co_return;
}

std::map<intx::uint256, uint64_t> rewards_and_gas;
std::vector<std::pair<intx::uint256, uint64_t> > rewards_and_gas;
for (size_t idx = 0; idx < block_fees.block->block.transactions.size(); idx++) {
const auto reward = block_fees.block->block.transactions[idx].effective_gas_price(block_fees.base_fee);
rewards_and_gas.emplace(reward, block_fees.receipts[idx].gas_used);
auto& txn = block_fees.block->block.transactions[idx];
const auto reward{txn.max_fee_per_gas >= block_fees.base_fee ? txn.effective_gas_price(block_fees.base_fee) - block_fees.base_fee
: txn.max_priority_fee_per_gas};
rewards_and_gas.push_back(std::make_pair(reward, block_fees.receipts[idx].gas_used));
}
sort(rewards_and_gas.begin(), rewards_and_gas.end(), sort_by_reward);

auto index = rewards_and_gas.begin();
const auto last = --rewards_and_gas.end();
auto sum_gas_used = index->second;
for (const auto percentile : reward_percentiles) {
const uint64_t threshold_gas_used = header.gas_used * static_cast<uint8_t>(percentile) / 100;
while (index != last) {
while (sum_gas_used < threshold_gas_used && index != last) {
index++;
if (sum_gas_used < threshold_gas_used) {
sum_gas_used += index->second;
}
sum_gas_used += index->second;
}
block_fees.rewards.push_back(index->first);
}
Expand Down
11 changes: 7 additions & 4 deletions silkworm/rpc/core/fee_history_oracle.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ namespace silkworm::rpc::fee_history {

using BlockProvider = std::function<Task<std::shared_ptr<silkworm::BlockWithHash>>(BlockNum)>;
using ReceiptsProvider = std::function<Task<rpc::Receipts>(const BlockWithHash&)>;
using LatestBlockProvider = std::function<Task<uint64_t>()>;

using Rewards = std::vector<intx::uint256>;

Expand Down Expand Up @@ -66,8 +67,9 @@ struct BlockFees {

class FeeHistoryOracle {
public:
explicit FeeHistoryOracle(const silkworm::ChainConfig& config, const BlockProvider& block_provider, ReceiptsProvider& receipts_provider)
: config_{config}, block_provider_(block_provider), receipts_provider_(receipts_provider) {}
explicit FeeHistoryOracle(const silkworm::ChainConfig& config, const BlockProvider& block_provider, ReceiptsProvider& receipts_provider,
LatestBlockProvider& latest_block_provider)
: config_{config}, block_provider_(block_provider), receipts_provider_(receipts_provider), latest_block_provider_{latest_block_provider} {}
virtual ~FeeHistoryOracle() = default;

FeeHistoryOracle(const FeeHistoryOracle&) = delete;
Expand All @@ -77,15 +79,16 @@ class FeeHistoryOracle {

private:
static inline const std::uint32_t kDefaultMaxFeeHistory = 1024;
static inline const std::uint32_t kDefaultMaxHeaderHistory = 300;
static inline const std::uint32_t kDefaultMaxBlockHistory = 5;
static inline const std::uint32_t kDefaultMaxHeaderHistory = 0;
static inline const std::uint32_t kDefaultMaxBlockHistory = 0;

Task<BlockRange> resolve_block_range(BlockNum newest_block, uint64_t block_count, uint64_t max_history);
Task<void> process_block(BlockFees& block_fees, const std::vector<int8_t>& reward_percentiles);

const silkworm::ChainConfig& config_;
const BlockProvider& block_provider_;
const ReceiptsProvider& receipts_provider_;
const LatestBlockProvider& latest_block_provider_;
};

} // namespace silkworm::rpc::fee_history
6 changes: 2 additions & 4 deletions silkworm/rpc/core/fee_history_oracle_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,8 @@ TEST_CASE("FeeHistory: json serialization") {
FeeHistory fh;

CHECK(nlohmann::json(fh) == R"({
"baseFeePerGas":[],
"gasUsedRatio":[],
"oldestBlock":"0x0",
"reward":[]
"gasUsedRatio":null,
"oldestBlock":"0x0"
})"_json);
}

Expand Down

0 comments on commit bcbdadd

Please sign in to comment.