mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-01-29 00:58:11 -05:00
chore: rm leftover mods (#9188)
This commit is contained in:
@@ -1,55 +0,0 @@
|
||||
//! Spawns a blocking task. CPU heavy tasks are executed with the `rayon` library. IO heavy tasks
|
||||
//! are executed on the `tokio` runtime.
|
||||
|
||||
use futures::Future;
|
||||
use reth_tasks::{pool::BlockingTaskPool, TaskSpawner};
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
use crate::{EthApiError, EthResult};
|
||||
|
||||
/// Executes code on a blocking thread.
|
||||
pub trait SpawnBlocking: Clone + Send + Sync + 'static {
|
||||
/// Returns a handle for spawning IO heavy blocking tasks.
|
||||
///
|
||||
/// Runtime access in default trait method implementations.
|
||||
fn io_task_spawner(&self) -> impl TaskSpawner;
|
||||
|
||||
/// Returns a handle for spawning CPU heavy blocking tasks.
|
||||
///
|
||||
/// Thread pool access in default trait method implementations.
|
||||
fn tracing_task_pool(&self) -> &BlockingTaskPool;
|
||||
|
||||
/// Executes the future on a new blocking task.
|
||||
///
|
||||
/// Note: This is expected for futures that are dominated by blocking IO operations, for tracing
|
||||
/// or CPU bound operations in general use [`spawn_tracing`](Self::spawn_tracing).
|
||||
fn spawn_blocking_io<F, R>(&self, f: F) -> impl Future<Output = EthResult<R>> + Send
|
||||
where
|
||||
F: FnOnce(Self) -> EthResult<R> + Send + 'static,
|
||||
R: Send + 'static,
|
||||
{
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let this = self.clone();
|
||||
self.io_task_spawner().spawn_blocking(Box::pin(async move {
|
||||
let res = async move { f(this) }.await;
|
||||
let _ = tx.send(res);
|
||||
}));
|
||||
|
||||
async move { rx.await.map_err(|_| EthApiError::InternalEthError)? }
|
||||
}
|
||||
|
||||
/// Executes a blocking task on the tracing pool.
|
||||
///
|
||||
/// Note: This is expected for futures that are predominantly CPU bound, as it uses `rayon`
|
||||
/// under the hood, for blocking IO futures use [`spawn_blocking`](Self::spawn_blocking_io). See
|
||||
/// <https://ryhl.io/blog/async-what-is-blocking/>.
|
||||
fn spawn_tracing<F, R>(&self, f: F) -> impl Future<Output = EthResult<R>> + Send
|
||||
where
|
||||
F: FnOnce(Self) -> EthResult<R> + Send + 'static,
|
||||
R: Send + 'static,
|
||||
{
|
||||
let this = self.clone();
|
||||
let fut = self.tracing_task_pool().spawn(move || f(this));
|
||||
async move { fut.await.map_err(|_| EthApiError::InternalBlockingTaskError)? }
|
||||
}
|
||||
}
|
||||
@@ -1,346 +0,0 @@
|
||||
//! Loads fee history from database. Helper trait for `eth_` fee and transaction RPC methods.
|
||||
|
||||
use futures::Future;
|
||||
use reth_primitives::U256;
|
||||
use reth_provider::{BlockIdReader, BlockReaderIdExt, ChainSpecProvider, HeaderProvider};
|
||||
use reth_rpc_types::{BlockNumberOrTag, FeeHistory};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{
|
||||
fee_history::calculate_reward_percentiles_for_block, api::LoadBlock, EthApiError,
|
||||
EthResult, EthStateCache, FeeHistoryCache, FeeHistoryEntry, GasPriceOracle,
|
||||
RpcInvalidTransactionError,
|
||||
};
|
||||
|
||||
/// Fee related functions for the [`EthApiServer`](crate::EthApiServer) trait in the
|
||||
/// `eth_` namespace.
|
||||
pub trait EthFees: LoadFee {
|
||||
/// Returns a suggestion for a gas price for legacy transactions.
|
||||
///
|
||||
/// See also: <https://github.com/ethereum/pm/issues/328#issuecomment-853234014>
|
||||
fn gas_price(&self) -> impl Future<Output = EthResult<U256>> + Send
|
||||
where
|
||||
Self: LoadBlock,
|
||||
{
|
||||
LoadFee::gas_price(self)
|
||||
}
|
||||
|
||||
/// Returns a suggestion for a base fee for blob transactions.
|
||||
fn blob_base_fee(&self) -> impl Future<Output = EthResult<U256>> + Send
|
||||
where
|
||||
Self: LoadBlock,
|
||||
{
|
||||
LoadFee::blob_base_fee(self)
|
||||
}
|
||||
|
||||
/// Returns a suggestion for the priority fee (the tip)
|
||||
fn suggested_priority_fee(&self) -> impl Future<Output = EthResult<U256>> + Send
|
||||
where
|
||||
Self: 'static,
|
||||
{
|
||||
LoadFee::suggested_priority_fee(self)
|
||||
}
|
||||
|
||||
/// Reports the fee history, for the given amount of blocks, up until the given newest block.
|
||||
///
|
||||
/// If `reward_percentiles` are provided the [`FeeHistory`] will include the _approximated_
|
||||
/// rewards for the requested range.
|
||||
fn fee_history(
|
||||
&self,
|
||||
mut block_count: u64,
|
||||
newest_block: BlockNumberOrTag,
|
||||
reward_percentiles: Option<Vec<f64>>,
|
||||
) -> impl Future<Output = EthResult<FeeHistory>> + Send {
|
||||
async move {
|
||||
if block_count == 0 {
|
||||
return Ok(FeeHistory::default())
|
||||
}
|
||||
|
||||
// See https://github.com/ethereum/go-ethereum/blob/2754b197c935ee63101cbbca2752338246384fec/eth/gasprice/feehistory.go#L218C8-L225
|
||||
let max_fee_history = if reward_percentiles.is_none() {
|
||||
self.gas_oracle().config().max_header_history
|
||||
} else {
|
||||
self.gas_oracle().config().max_block_history
|
||||
};
|
||||
|
||||
if block_count > max_fee_history {
|
||||
debug!(
|
||||
requested = block_count,
|
||||
truncated = max_fee_history,
|
||||
"Sanitizing fee history block count"
|
||||
);
|
||||
block_count = max_fee_history
|
||||
}
|
||||
|
||||
let Some(end_block) =
|
||||
LoadFee::provider(self).block_number_for_id(newest_block.into())?
|
||||
else {
|
||||
return Err(EthApiError::UnknownBlockNumber)
|
||||
};
|
||||
|
||||
// need to add 1 to the end block to get the correct (inclusive) range
|
||||
let end_block_plus = end_block + 1;
|
||||
// Ensure that we would not be querying outside of genesis
|
||||
if end_block_plus < block_count {
|
||||
block_count = end_block_plus;
|
||||
}
|
||||
|
||||
// If reward percentiles were specified, we
|
||||
// need to validate that they are monotonically
|
||||
// increasing and 0 <= p <= 100
|
||||
// Note: The types used ensure that the percentiles are never < 0
|
||||
if let Some(percentiles) = &reward_percentiles {
|
||||
if percentiles.windows(2).any(|w| w[0] > w[1] || w[0] > 100.) {
|
||||
return Err(EthApiError::InvalidRewardPercentiles)
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch the headers and ensure we got all of them
|
||||
//
|
||||
// Treat a request for 1 block as a request for `newest_block..=newest_block`,
|
||||
// otherwise `newest_block - 2
|
||||
// NOTE: We ensured that block count is capped
|
||||
let start_block = end_block_plus - block_count;
|
||||
|
||||
// Collect base fees, gas usage ratios and (optionally) reward percentile data
|
||||
let mut base_fee_per_gas: Vec<u128> = Vec::new();
|
||||
let mut gas_used_ratio: Vec<f64> = Vec::new();
|
||||
|
||||
let mut base_fee_per_blob_gas: Vec<u128> = Vec::new();
|
||||
let mut blob_gas_used_ratio: Vec<f64> = Vec::new();
|
||||
|
||||
let mut rewards: Vec<Vec<u128>> = Vec::new();
|
||||
|
||||
// Check if the requested range is within the cache bounds
|
||||
let fee_entries = self.fee_history_cache().get_history(start_block, end_block).await;
|
||||
|
||||
if let Some(fee_entries) = fee_entries {
|
||||
if fee_entries.len() != block_count as usize {
|
||||
return Err(EthApiError::InvalidBlockRange)
|
||||
}
|
||||
|
||||
for entry in &fee_entries {
|
||||
base_fee_per_gas.push(entry.base_fee_per_gas as u128);
|
||||
gas_used_ratio.push(entry.gas_used_ratio);
|
||||
base_fee_per_blob_gas.push(entry.base_fee_per_blob_gas.unwrap_or_default());
|
||||
blob_gas_used_ratio.push(entry.blob_gas_used_ratio);
|
||||
|
||||
if let Some(percentiles) = &reward_percentiles {
|
||||
let mut block_rewards = Vec::with_capacity(percentiles.len());
|
||||
for &percentile in percentiles {
|
||||
block_rewards.push(self.approximate_percentile(entry, percentile));
|
||||
}
|
||||
rewards.push(block_rewards);
|
||||
}
|
||||
}
|
||||
let last_entry = fee_entries.last().expect("is not empty");
|
||||
|
||||
// Also need to include the `base_fee_per_gas` and `base_fee_per_blob_gas` for the
|
||||
// next block
|
||||
base_fee_per_gas
|
||||
.push(last_entry.next_block_base_fee(&LoadFee::provider(self).chain_spec())
|
||||
as u128);
|
||||
|
||||
base_fee_per_blob_gas.push(last_entry.next_block_blob_fee().unwrap_or_default());
|
||||
} else {
|
||||
// read the requested header range
|
||||
let headers = LoadFee::provider(self).sealed_headers_range(start_block..=end_block)?;
|
||||
if headers.len() != block_count as usize {
|
||||
return Err(EthApiError::InvalidBlockRange)
|
||||
}
|
||||
|
||||
for header in &headers {
|
||||
base_fee_per_gas.push(header.base_fee_per_gas.unwrap_or_default() as u128);
|
||||
gas_used_ratio.push(header.gas_used as f64 / header.gas_limit as f64);
|
||||
base_fee_per_blob_gas.push(header.blob_fee().unwrap_or_default());
|
||||
blob_gas_used_ratio.push(
|
||||
header.blob_gas_used.unwrap_or_default() as f64 /
|
||||
reth_primitives::constants::eip4844::MAX_DATA_GAS_PER_BLOCK as f64,
|
||||
);
|
||||
|
||||
// Percentiles were specified, so we need to collect reward percentile ino
|
||||
if let Some(percentiles) = &reward_percentiles {
|
||||
let (transactions, receipts) = LoadFee::cache(self)
|
||||
.get_transactions_and_receipts(header.hash())
|
||||
.await?
|
||||
.ok_or(EthApiError::InvalidBlockRange)?;
|
||||
rewards.push(
|
||||
calculate_reward_percentiles_for_block(
|
||||
percentiles,
|
||||
header.gas_used,
|
||||
header.base_fee_per_gas.unwrap_or_default(),
|
||||
&transactions,
|
||||
&receipts,
|
||||
)
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// The spec states that `base_fee_per_gas` "[..] includes the next block after the
|
||||
// newest of the returned range, because this value can be derived from the
|
||||
// newest block"
|
||||
//
|
||||
// The unwrap is safe since we checked earlier that we got at least 1 header.
|
||||
let last_header = headers.last().expect("is present");
|
||||
base_fee_per_gas.push(
|
||||
LoadFee::provider(self).chain_spec().base_fee_params_at_timestamp(last_header.timestamp).next_block_base_fee(
|
||||
last_header.gas_used as u128,
|
||||
last_header.gas_limit as u128,
|
||||
last_header.base_fee_per_gas.unwrap_or_default() as u128,
|
||||
));
|
||||
|
||||
// Same goes for the `base_fee_per_blob_gas`:
|
||||
// > "[..] includes the next block after the newest of the returned range, because this value can be derived from the newest block.
|
||||
base_fee_per_blob_gas
|
||||
.push(last_header.next_block_blob_fee().unwrap_or_default());
|
||||
};
|
||||
|
||||
Ok(FeeHistory {
|
||||
base_fee_per_gas,
|
||||
gas_used_ratio,
|
||||
base_fee_per_blob_gas,
|
||||
blob_gas_used_ratio,
|
||||
oldest_block: start_block,
|
||||
reward: reward_percentiles.map(|_| rewards),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Approximates reward at a given percentile for a specific block
|
||||
/// Based on the configured resolution
|
||||
fn approximate_percentile(&self, entry: &FeeHistoryEntry, requested_percentile: f64) -> u128 {
|
||||
let resolution = self.fee_history_cache().resolution();
|
||||
let rounded_percentile =
|
||||
(requested_percentile * resolution as f64).round() / resolution as f64;
|
||||
let clamped_percentile = rounded_percentile.clamp(0.0, 100.0);
|
||||
|
||||
// Calculate the index in the precomputed rewards array
|
||||
let index = (clamped_percentile / (1.0 / resolution as f64)).round() as usize;
|
||||
// Fetch the reward from the FeeHistoryEntry
|
||||
entry.rewards.get(index).cloned().unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads fee from database.
|
||||
///
|
||||
/// Behaviour shared by several `eth_` RPC methods, not exclusive to `eth_` fees RPC methods.
|
||||
pub trait LoadFee: LoadBlock {
|
||||
// Returns a handle for reading data from disk.
|
||||
///
|
||||
/// Data access in default (L1) trait method implementations.
|
||||
fn provider(&self) -> impl BlockIdReader + HeaderProvider + ChainSpecProvider;
|
||||
|
||||
/// Returns a handle for reading data from memory.
|
||||
///
|
||||
/// Data access in default (L1) trait method implementations.
|
||||
fn cache(&self) -> &EthStateCache;
|
||||
|
||||
/// Returns a handle for reading gas price.
|
||||
///
|
||||
/// Data access in default (L1) trait method implementations.
|
||||
fn gas_oracle(&self) -> &GasPriceOracle<impl BlockReaderIdExt>;
|
||||
|
||||
/// Returns a handle for reading fee history data from memory.
|
||||
///
|
||||
/// Data access in default (L1) trait method implementations.
|
||||
fn fee_history_cache(&self) -> &FeeHistoryCache;
|
||||
|
||||
/// Returns the gas price if it is set, otherwise fetches a suggested gas price for legacy
|
||||
/// transactions.
|
||||
fn legacy_gas_price(
|
||||
&self,
|
||||
gas_price: Option<U256>,
|
||||
) -> impl Future<Output = EthResult<U256>> + Send {
|
||||
async move {
|
||||
match gas_price {
|
||||
Some(gas_price) => Ok(gas_price),
|
||||
None => {
|
||||
// fetch a suggested gas price
|
||||
self.gas_price().await
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the EIP-1559 fees if they are set, otherwise fetches a suggested gas price for
|
||||
/// EIP-1559 transactions.
|
||||
///
|
||||
/// Returns (`max_fee`, `priority_fee`)
|
||||
fn eip1559_fees(
|
||||
&self,
|
||||
max_fee_per_gas: Option<U256>,
|
||||
max_priority_fee_per_gas: Option<U256>,
|
||||
) -> impl Future<Output = EthResult<(U256, U256)>> + Send {
|
||||
async move {
|
||||
let max_fee_per_gas = match max_fee_per_gas {
|
||||
Some(max_fee_per_gas) => max_fee_per_gas,
|
||||
None => {
|
||||
// fetch pending base fee
|
||||
let base_fee = self
|
||||
.block(BlockNumberOrTag::Pending)
|
||||
.await?
|
||||
.ok_or(EthApiError::UnknownBlockNumber)?
|
||||
.base_fee_per_gas
|
||||
.ok_or_else(|| {
|
||||
EthApiError::InvalidTransaction(
|
||||
RpcInvalidTransactionError::TxTypeNotSupported,
|
||||
)
|
||||
})?;
|
||||
U256::from(base_fee)
|
||||
}
|
||||
};
|
||||
|
||||
let max_priority_fee_per_gas = match max_priority_fee_per_gas {
|
||||
Some(max_priority_fee_per_gas) => max_priority_fee_per_gas,
|
||||
None => self.suggested_priority_fee().await?,
|
||||
};
|
||||
Ok((max_fee_per_gas, max_priority_fee_per_gas))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the EIP-4844 blob fee if it is set, otherwise fetches a blob fee.
|
||||
fn eip4844_blob_fee(
|
||||
&self,
|
||||
blob_fee: Option<U256>,
|
||||
) -> impl Future<Output = EthResult<U256>> + Send {
|
||||
async move {
|
||||
match blob_fee {
|
||||
Some(blob_fee) => Ok(blob_fee),
|
||||
None => self.blob_base_fee().await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a suggestion for a gas price for legacy transactions.
|
||||
///
|
||||
/// See also: <https://github.com/ethereum/pm/issues/328#issuecomment-853234014>
|
||||
fn gas_price(&self) -> impl Future<Output = EthResult<U256>> + Send {
|
||||
let header = self.block(BlockNumberOrTag::Latest);
|
||||
let suggested_tip = self.suggested_priority_fee();
|
||||
async move {
|
||||
let (header, suggested_tip) = futures::try_join!(header, suggested_tip)?;
|
||||
let base_fee = header.and_then(|h| h.base_fee_per_gas).unwrap_or_default();
|
||||
Ok(suggested_tip + U256::from(base_fee))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a suggestion for a base fee for blob transactions.
|
||||
fn blob_base_fee(&self) -> impl Future<Output = EthResult<U256>> + Send {
|
||||
async move {
|
||||
self.block(BlockNumberOrTag::Latest)
|
||||
.await?
|
||||
.and_then(|h: reth_primitives::SealedBlock| h.next_block_blob_fee())
|
||||
.ok_or(EthApiError::ExcessBlobGasNotSet)
|
||||
.map(U256::from)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a suggestion for the priority fee (the tip)
|
||||
fn suggested_priority_fee(&self) -> impl Future<Output = EthResult<U256>> + Send
|
||||
where
|
||||
Self: 'static,
|
||||
{
|
||||
self.gas_oracle().suggest_tip_cap()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user