mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
chore: spawn more eth calls and add docs (#2715)
Co-authored-by: Bjerg <onbjerg@users.noreply.github.com>
This commit is contained in:
@ -50,7 +50,8 @@ pub enum BlockExecutionError {
|
||||
MissingTotalDifficulty { hash: H256 },
|
||||
|
||||
/// Only used for TestExecutor
|
||||
#[cfg(feature = "test-utils")]
|
||||
///
|
||||
/// Note: this is not feature gated for convenience.
|
||||
#[error("Execution unavailable for tests")]
|
||||
UnavailableForTest,
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
use crate::{
|
||||
eth::{
|
||||
error::{EthApiError, EthResult, InvalidTransactionError, RevertError},
|
||||
error::{ensure_success, EthApiError, EthResult, InvalidTransactionError, RevertError},
|
||||
revm_utils::{
|
||||
build_call_evm_env, cap_tx_gas_limit_with_caller_allowance, get_precompiles, inspect,
|
||||
transact,
|
||||
@ -13,13 +13,13 @@ use crate::{
|
||||
};
|
||||
use ethers_core::utils::get_contract_address;
|
||||
use reth_network_api::NetworkInfo;
|
||||
use reth_primitives::{AccessList, BlockId, BlockNumberOrTag, U256};
|
||||
use reth_primitives::{AccessList, BlockId, BlockNumberOrTag, Bytes, U256};
|
||||
use reth_provider::{BlockProviderIdExt, EvmEnvProvider, StateProvider, StateProviderFactory};
|
||||
use reth_revm::{
|
||||
access_list::AccessListInspector,
|
||||
database::{State, SubState},
|
||||
};
|
||||
use reth_rpc_types::CallRequest;
|
||||
use reth_rpc_types::{state::StateOverride, CallRequest};
|
||||
use reth_transaction_pool::TransactionPool;
|
||||
use revm::{
|
||||
db::{CacheDB, DatabaseRef},
|
||||
@ -48,6 +48,24 @@ where
|
||||
self.estimate_gas_with(cfg, block_env, request, state)
|
||||
}
|
||||
|
||||
/// Executes the call request (`eth_call`) and returns the output
|
||||
pub(crate) async fn call(
|
||||
&self,
|
||||
request: CallRequest,
|
||||
block_number: Option<BlockId>,
|
||||
state_overrides: Option<StateOverride>,
|
||||
) -> EthResult<Bytes> {
|
||||
let (res, _env) = self
|
||||
.transact_call_at(
|
||||
request,
|
||||
block_number.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)),
|
||||
state_overrides,
|
||||
)
|
||||
.await?;
|
||||
|
||||
ensure_success(res.result)
|
||||
}
|
||||
|
||||
/// Estimates the gas usage of the `request` with the state.
|
||||
///
|
||||
/// This will execute the [CallRequest] and find the best gas limit via binary search
|
||||
|
||||
@ -126,6 +126,9 @@ where
|
||||
}
|
||||
|
||||
/// Executes the future on a new blocking task.
|
||||
///
|
||||
/// This accepts a closure that creates a new future using a clone of this type and spawns the
|
||||
/// future onto a new task that is allowed to block.
|
||||
pub(crate) async fn on_blocking_task<C, F, R>(&self, c: C) -> EthResult<R>
|
||||
where
|
||||
C: FnOnce(Self) -> F,
|
||||
|
||||
@ -3,10 +3,7 @@
|
||||
|
||||
use super::EthApiSpec;
|
||||
use crate::{
|
||||
eth::{
|
||||
api::{EthApi, EthTransactions},
|
||||
error::ensure_success,
|
||||
},
|
||||
eth::api::{EthApi, EthTransactions},
|
||||
result::{internal_rpc_err, ToRpcResult},
|
||||
};
|
||||
use jsonrpsee::core::RpcResult as Result;
|
||||
@ -177,7 +174,7 @@ where
|
||||
/// Handler for: `eth_getBalance`
|
||||
async fn balance(&self, address: Address, block_number: Option<BlockId>) -> Result<U256> {
|
||||
trace!(target: "rpc::eth", ?address, ?block_number, "Serving eth_getBalance");
|
||||
Ok(EthApi::balance(self, address, block_number)?)
|
||||
Ok(self.on_blocking_task(|this| async move { this.balance(address, block_number) }).await?)
|
||||
}
|
||||
|
||||
/// Handler for: `eth_getStorageAt`
|
||||
@ -188,7 +185,9 @@ where
|
||||
block_number: Option<BlockId>,
|
||||
) -> Result<H256> {
|
||||
trace!(target: "rpc::eth", ?address, ?block_number, "Serving eth_getStorageAt");
|
||||
Ok(EthApi::storage_at(self, address, index, block_number).await?)
|
||||
Ok(self
|
||||
.on_blocking_task(|this| async move { this.storage_at(address, index, block_number) })
|
||||
.await?)
|
||||
}
|
||||
|
||||
/// Handler for: `eth_getTransactionCount`
|
||||
@ -198,13 +197,19 @@ where
|
||||
block_number: Option<BlockId>,
|
||||
) -> Result<U256> {
|
||||
trace!(target: "rpc::eth", ?address, ?block_number, "Serving eth_getTransactionCount");
|
||||
Ok(EthApi::get_transaction_count(self, address, block_number)?)
|
||||
Ok(self
|
||||
.on_blocking_task(
|
||||
|this| async move { this.get_transaction_count(address, block_number) },
|
||||
)
|
||||
.await?)
|
||||
}
|
||||
|
||||
/// Handler for: `eth_getCode`
|
||||
async fn get_code(&self, address: Address, block_number: Option<BlockId>) -> Result<Bytes> {
|
||||
trace!(target: "rpc::eth", ?address, ?block_number, "Serving eth_getCode");
|
||||
Ok(EthApi::get_code(self, address, block_number)?)
|
||||
Ok(self
|
||||
.on_blocking_task(|this| async move { this.get_code(address, block_number) })
|
||||
.await?)
|
||||
}
|
||||
|
||||
/// Handler for: `eth_call`
|
||||
@ -215,15 +220,11 @@ where
|
||||
state_overrides: Option<StateOverride>,
|
||||
) -> Result<Bytes> {
|
||||
trace!(target: "rpc::eth", ?request, ?block_number, ?state_overrides, "Serving eth_call");
|
||||
let (res, _env) = self
|
||||
.transact_call_at(
|
||||
request,
|
||||
block_number.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)),
|
||||
state_overrides,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(ensure_success(res.result)?)
|
||||
Ok(self
|
||||
.on_blocking_task(|this| async move {
|
||||
this.call(request, block_number, state_overrides).await
|
||||
})
|
||||
.await?)
|
||||
}
|
||||
|
||||
/// Handler for: `eth_createAccessList`
|
||||
@ -233,11 +234,15 @@ where
|
||||
block_number: Option<BlockId>,
|
||||
) -> Result<AccessListWithGasUsed> {
|
||||
trace!(target: "rpc::eth", ?request, ?block_number, "Serving eth_createAccessList");
|
||||
Ok(self
|
||||
.on_blocking_task(|this| async move {
|
||||
let block_id = block_number.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest));
|
||||
let access_list = self.create_access_list_at(request.clone(), block_number).await?;
|
||||
let access_list = this.create_access_list_at(request.clone(), block_number).await?;
|
||||
request.access_list = Some(access_list.clone());
|
||||
let gas_used = self.estimate_gas_at(request, block_id).await?;
|
||||
let gas_used = this.estimate_gas_at(request, block_id).await?;
|
||||
Ok(AccessListWithGasUsed { access_list, gas_used })
|
||||
})
|
||||
.await?)
|
||||
}
|
||||
|
||||
/// Handler for: `eth_estimateGas`
|
||||
@ -247,11 +252,14 @@ where
|
||||
block_number: Option<BlockId>,
|
||||
) -> Result<U256> {
|
||||
trace!(target: "rpc::eth", ?request, ?block_number, "Serving eth_estimateGas");
|
||||
Ok(EthApi::estimate_gas_at(
|
||||
self,
|
||||
Ok(self
|
||||
.on_blocking_task(|this| async move {
|
||||
this.estimate_gas_at(
|
||||
request,
|
||||
block_number.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)),
|
||||
)
|
||||
.await
|
||||
})
|
||||
.await?)
|
||||
}
|
||||
|
||||
|
||||
@ -71,18 +71,15 @@ where
|
||||
Ok(U256::from(state.account_nonce(address)?.unwrap_or_default()))
|
||||
}
|
||||
|
||||
pub(crate) async fn storage_at(
|
||||
pub(crate) fn storage_at(
|
||||
&self,
|
||||
address: Address,
|
||||
index: JsonStorageKey,
|
||||
block_id: Option<BlockId>,
|
||||
) -> EthResult<H256> {
|
||||
self.on_blocking_task(|this| async move {
|
||||
let state = this.state_at_block_id_or_latest(block_id)?;
|
||||
let state = self.state_at_block_id_or_latest(block_id)?;
|
||||
let value = state.storage(address, index.0)?.unwrap_or_default();
|
||||
Ok(H256(value.to_be_bytes()))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
@ -169,7 +166,7 @@ mod tests {
|
||||
GasPriceOracle::new(NoopProvider::default(), Default::default(), cache),
|
||||
);
|
||||
let address = Address::random();
|
||||
let storage = eth_api.storage_at(address, U256::ZERO.into(), None).await.unwrap();
|
||||
let storage = eth_api.storage_at(address, U256::ZERO.into(), None).unwrap();
|
||||
assert_eq!(storage, U256::ZERO.into());
|
||||
|
||||
// === Mock ===
|
||||
@ -190,7 +187,7 @@ mod tests {
|
||||
);
|
||||
|
||||
let storage_key: U256 = storage_key.into();
|
||||
let storage = eth_api.storage_at(address, storage_key.into(), None).await.unwrap();
|
||||
let storage = eth_api.storage_at(address, storage_key.into(), None).unwrap();
|
||||
assert_eq!(storage, storage_value.into());
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,21 @@
|
||||
//! Reth RPC implementation
|
||||
//!
|
||||
//! Provides the implementation of all RPC interfaces.
|
||||
//!
|
||||
//!
|
||||
//! ## Note on blocking behaviour
|
||||
//!
|
||||
//! All async RPC handlers must non-blocking, see also [What is blocking](https://ryhl.io/blog/async-what-is-blocking/).
|
||||
//!
|
||||
//! A lot of the RPC are using a mix of async and direct calls to the database, which are blocking
|
||||
//! and can reduce overall performance of all concurrent requests handled via the jsonrpsee server.
|
||||
//!
|
||||
//! To avoid this, all blocking or CPU intensive handlers must be spawned to a separate task. See
|
||||
//! the [EthApi] handler implementations for examples. The rpc-api traits make no use of the
|
||||
//! available jsonrpsee `blocking` attribute to give implementors more freedom because the
|
||||
//! `blocking` attribute and async handlers are mutually exclusive. However, as mentioned above, a
|
||||
//! lot of handlers make use of async functions, caching for example, but are also using blocking
|
||||
//! disk-io, hence these calls are spawned as futures to a blocking task manually.
|
||||
|
||||
mod admin;
|
||||
mod call_guard;
|
||||
|
||||
Reference in New Issue
Block a user