mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat(gas_oracle): Add Lru Cache for block values of recent blocks (#5062)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -6184,6 +6184,7 @@ dependencies = [
|
||||
"assert_matches",
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"derive_more",
|
||||
"futures",
|
||||
"http",
|
||||
"http-body",
|
||||
|
||||
@ -68,6 +68,7 @@ tracing.workspace = true
|
||||
tracing-futures = "0.2"
|
||||
schnellru = "0.2"
|
||||
futures.workspace = true
|
||||
derive_more = "0.99"
|
||||
|
||||
[dev-dependencies]
|
||||
jsonrpsee = { workspace = true, features = ["client"] }
|
||||
|
||||
@ -4,9 +4,12 @@ use crate::eth::{
|
||||
cache::EthStateCache,
|
||||
error::{EthApiError, EthResult, RpcInvalidTransactionError},
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use reth_primitives::{constants::GWEI_TO_WEI, BlockNumberOrTag, B256, U256};
|
||||
use reth_provider::BlockReaderIdExt;
|
||||
use schnellru::{ByLength, LruMap};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::warn;
|
||||
|
||||
@ -90,8 +93,9 @@ pub struct GasPriceOracle<Provider> {
|
||||
oracle_config: GasPriceOracleConfig,
|
||||
/// The price under which the sample will be ignored.
|
||||
ignore_price: Option<u128>,
|
||||
/// The latest calculated price and its block hash
|
||||
last_price: Mutex<GasPriceOracleResult>,
|
||||
/// Stores the latest calculated price and its block hash and Cache stores the lowest effective
|
||||
/// tip values of recent blocks
|
||||
inner: Mutex<GasPriceOracleInner>,
|
||||
}
|
||||
|
||||
impl<Provider> GasPriceOracle<Provider>
|
||||
@ -111,7 +115,16 @@ where
|
||||
}
|
||||
let ignore_price = oracle_config.ignore_price.map(|price| price.saturating_to());
|
||||
|
||||
Self { provider, oracle_config, last_price: Default::default(), cache, ignore_price }
|
||||
// this is the number of blocks that we will cache the values for
|
||||
let cached_values = (oracle_config.blocks * 5).max(oracle_config.max_block_history as u32);
|
||||
let inner = Mutex::new(GasPriceOracleInner {
|
||||
last_price: Default::default(),
|
||||
lowest_effective_tip_cache: EffectiveTipLruCache(LruMap::new(ByLength::new(
|
||||
cached_values,
|
||||
))),
|
||||
});
|
||||
|
||||
Self { provider, oracle_config, cache, ignore_price, inner }
|
||||
}
|
||||
|
||||
/// Returns the configuration of the gas price oracle.
|
||||
@ -126,11 +139,11 @@ where
|
||||
.sealed_header_by_number_or_tag(BlockNumberOrTag::Latest)?
|
||||
.ok_or(EthApiError::UnknownBlockNumber)?;
|
||||
|
||||
let mut last_price = self.last_price.lock().await;
|
||||
let mut inner = self.inner.lock().await;
|
||||
|
||||
// if we have stored a last price, then we check whether or not it was for the same head
|
||||
if last_price.block_hash == header.hash {
|
||||
return Ok(last_price.price)
|
||||
if inner.last_price.block_hash == header.hash {
|
||||
return Ok(inner.last_price.price)
|
||||
}
|
||||
|
||||
// if all responses are empty, then we can return a maximum of 2*check_block blocks' worth
|
||||
@ -150,13 +163,24 @@ where
|
||||
};
|
||||
|
||||
for _ in 0..max_blocks {
|
||||
let (parent_hash, block_values) = self
|
||||
.get_block_values(current_hash, SAMPLE_NUMBER)
|
||||
.await?
|
||||
.ok_or(EthApiError::UnknownBlockNumber)?;
|
||||
// Check if current hash is in cache
|
||||
let (parent_hash, block_values) =
|
||||
if let Some(vals) = inner.lowest_effective_tip_cache.get(¤t_hash) {
|
||||
vals.to_owned()
|
||||
} else {
|
||||
// Otherwise we fetch it using get_block_values
|
||||
let (parent_hash, block_values) = self
|
||||
.get_block_values(current_hash, SAMPLE_NUMBER)
|
||||
.await?
|
||||
.ok_or(EthApiError::UnknownBlockNumber)?;
|
||||
inner
|
||||
.lowest_effective_tip_cache
|
||||
.insert(current_hash, (parent_hash, block_values.clone()));
|
||||
(parent_hash, block_values)
|
||||
};
|
||||
|
||||
if block_values.is_empty() {
|
||||
results.push(U256::from(last_price.price));
|
||||
results.push(U256::from(inner.last_price.price));
|
||||
} else {
|
||||
results.extend(block_values);
|
||||
populated_blocks += 1;
|
||||
@ -171,7 +195,7 @@ where
|
||||
}
|
||||
|
||||
// sort results then take the configured percentile result
|
||||
let mut price = last_price.price;
|
||||
let mut price = inner.last_price.price;
|
||||
if !results.is_empty() {
|
||||
results.sort_unstable();
|
||||
price = *results
|
||||
@ -186,7 +210,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
*last_price = GasPriceOracleResult { block_hash: header.hash, price };
|
||||
inner.last_price = GasPriceOracleResult { block_hash: header.hash, price };
|
||||
|
||||
Ok(price)
|
||||
}
|
||||
@ -253,6 +277,26 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Container type for mutable inner state of the [GasPriceOracle]
|
||||
#[derive(Debug)]
|
||||
struct GasPriceOracleInner {
|
||||
last_price: GasPriceOracleResult,
|
||||
lowest_effective_tip_cache: EffectiveTipLruCache,
|
||||
}
|
||||
|
||||
/// Wrapper struct for LruMap
|
||||
#[derive(Deref, DerefMut)]
|
||||
pub struct EffectiveTipLruCache(LruMap<B256, (B256, Vec<U256>), ByLength>);
|
||||
|
||||
impl Debug for EffectiveTipLruCache {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("EffectiveTipLruCache")
|
||||
.field("cache_length", &self.len())
|
||||
.field("cache_memory_usage", &self.memory_usage())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores the last result that the oracle returned
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GasPriceOracleResult {
|
||||
|
||||
Reference in New Issue
Block a user