feeHistory implementation (#484)

* feeHistory implementation

* code review fix
This commit is contained in:
Dusan Stanivukovic 2021-07-14 21:21:28 +02:00 committed by GitHub
parent eb42f0c5d9
commit 87603926b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 240 additions and 4 deletions

View File

@ -59,8 +59,8 @@ use v1::{
traits::Eth,
types::{
block_number_to_id, Block, BlockNumber, BlockTransactions, Bytes, CallRequest, EthAccount,
Filter, Index, Log, Receipt, RichBlock, StorageProof, SyncInfo, SyncStatus, Transaction,
Work,
EthFeeHistory, Filter, Index, Log, Receipt, RichBlock, StorageProof, SyncInfo, SyncStatus,
Transaction, Work,
},
};
@ -696,6 +696,205 @@ where
)))
}
fn fee_history(
&self,
mut block_count: U256,
newest_block: BlockNumber,
reward_percentiles: Option<Vec<f64>>,
) -> BoxFuture<EthFeeHistory> {
let mut result = EthFeeHistory::default();
if block_count < 1.into() {
return Box::new(future::done(Ok(result)));
}
if block_count > 1024.into() {
block_count = 1024.into();
}
let latest_block = self.client.chain_info().best_block_number;
let pending_block = self.client.chain_info().best_block_number + 1;
let last_block = match newest_block {
BlockNumber::Hash {
hash: _,
require_canonical: _,
} => 0,
BlockNumber::Num(number) => {
if number <= pending_block {
number
} else {
0
}
}
BlockNumber::Latest => latest_block,
BlockNumber::Earliest => 0,
BlockNumber::Pending => pending_block,
};
let first_block = if last_block >= block_count.as_u64() - 1 {
last_block - (block_count.as_u64() - 1)
} else {
0
};
result.oldest_block = BlockNumber::Num(first_block);
let get_block_header = |i| {
self.client
.block_header(BlockId::Number(i))
.ok_or_else(errors::state_pruned)
.and_then(|h| {
h.decode(self.client.engine().params().eip1559_transition)
.map_err(errors::decode)
})
};
let calculate_base_fee = |h| {
self.client
.engine()
.calculate_base_fee(&h)
.unwrap_or_default()
};
let calculate_gas_used_ratio = |h: &Header| {
let gas_used = match self.client.block_receipts(&h.hash()) {
Some(receipts) => receipts
.receipts
.last()
.map_or(U256::zero(), |r| r.gas_used),
None => 0.into(),
};
(gas_used.as_u64() as f64) / (h.gas_limit().as_u64() as f64)
};
let get_block_transactions = |i| match self.client.block_body(BlockId::Number(i)) {
Some(body) => Some(body.transactions()),
None => None,
};
let reward_percentiles = reward_percentiles.unwrap_or_default();
let mut reward_final = vec![];
for i in first_block..=last_block + 1 {
let is_last = i == last_block + 1;
if i < pending_block {
match get_block_header(i) {
Ok(h) => {
let base_fee = h.base_fee();
result.base_fee_per_gas.push(base_fee.unwrap_or_default());
if !is_last {
result.gas_used_ratio.push(calculate_gas_used_ratio(&h));
if reward_percentiles.len() > 0 {
let mut gas_and_reward: Vec<(U256, U256)> = vec![];
if let Some(txs) = get_block_transactions(i) {
if let Some(receipt) = self.client.block_receipts(&h.hash()) {
if txs.len() == receipt.receipts.len() {
for i in 0..txs.len() {
let gas_used = if i == 0 {
receipt.receipts[i].gas_used
} else {
receipt.receipts[i].gas_used
- receipt.receipts[i - 1].gas_used
};
gas_and_reward.push((
gas_used,
txs[i].effective_gas_price(base_fee)
- base_fee.unwrap_or_default(),
));
}
}
}
}
gas_and_reward.sort_by(|a, b| a.1.cmp(&b.1));
reward_final.push(
reward_percentiles
.iter()
.map(|p| {
let target_gas = U256::from(
((h.gas_used().as_u64() as f64) * p / 100.0) as u64,
);
let mut sum_gas = U256::default();
for pair in &gas_and_reward {
sum_gas += pair.0;
if target_gas <= sum_gas {
return pair.1;
}
}
0.into()
})
.collect(),
);
}
}
}
Err(_) => break, //reorg happened, skip rest of the blocks
}
} else if i == pending_block {
match self.miner.pending_block_header(i - 1) {
Some(h) => {
result
.base_fee_per_gas
.push(h.base_fee().unwrap_or_default());
if !is_last {
result.gas_used_ratio.push(calculate_gas_used_ratio(&h));
if reward_percentiles.len() > 0 {
//zero values since can't be calculated for pending block
reward_final.push(vec![0.into(); reward_percentiles.len()]);
}
}
}
None => {
//calculate base fee based on the latest block
match get_block_header(i - 1) {
Ok(h) => {
result.base_fee_per_gas.push(calculate_base_fee(h));
if !is_last {
result.gas_used_ratio.push(0.into());
if reward_percentiles.len() > 0 {
//zero values since can't be calculated for pending block
reward_final.push(vec![0.into(); reward_percentiles.len()]);
}
}
}
Err(_) => break, //reorg happened, skip rest of the blocks
}
}
}
} else if i == pending_block + 1 {
//calculate base fee based on the pending block, if exist
match self.miner.pending_block_header(i - 1) {
Some(h) => {
result.base_fee_per_gas.push(calculate_base_fee(h));
}
None => {
result.base_fee_per_gas.push(0.into());
}
}
} else {
unreachable!();
};
}
if !reward_final.is_empty() {
result.reward = Some(reward_final);
}
Box::new(future::done(Ok(result)))
}
fn accounts(&self) -> Result<Vec<H160>> {
self.deprecation_notice
.print("eth_accounts", deprecated::msgs::ACCOUNTS);

View File

@ -20,8 +20,8 @@ use jsonrpc_core::{BoxFuture, Result};
use jsonrpc_derive::rpc;
use v1::types::{
BlockNumber, Bytes, CallRequest, EthAccount, Filter, FilterChanges, Index, Log, Receipt,
RichBlock, SyncStatus, Transaction, Work,
BlockNumber, Bytes, CallRequest, EthAccount, EthFeeHistory, Filter, FilterChanges, Index, Log,
Receipt, RichBlock, SyncStatus, Transaction, Work,
};
/// Eth rpc interface.
@ -60,6 +60,11 @@ pub trait Eth {
#[rpc(name = "eth_gasPrice")]
fn gas_price(&self) -> BoxFuture<U256>;
/// Returns transaction fee history.
#[rpc(name = "eth_feeHistory")]
fn fee_history(&self, _: U256, _: BlockNumber, _: Option<Vec<f64>>)
-> BoxFuture<EthFeeHistory>;
/// Returns accounts list.
#[rpc(name = "eth_accounts")]
fn accounts(&self) -> Result<Vec<H160>>;

View File

@ -0,0 +1,30 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
//! Return types for RPC calls
use ethereum_types::U256;
use v1::types::BlockNumber;
/// Account information.
#[derive(Debug, Default, Clone, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EthFeeHistory {
pub oldest_block: BlockNumber,
pub base_fee_per_gas: Vec<U256>,
pub gas_used_ratio: Vec<f64>,
pub reward: Option<Vec<Vec<U256>>>,
}

View File

@ -29,6 +29,7 @@ pub use self::{
},
derivation::{Derive, DeriveHash, DeriveHierarchical},
eip191::{EIP191Version, PresignedTransaction},
fee_history::EthFeeHistory,
filter::{Filter, FilterChanges},
histogram::Histogram,
index::Index,
@ -62,6 +63,7 @@ mod call_request;
mod confirmations;
mod derivation;
mod eip191;
mod fee_history;
mod filter;
mod histogram;
mod index;