reth
reth copied to clipboard
EthApi Access or Instantiation Example for ExEx
Describe the change
There is examples for accessing the installed EthApi and TraceApi from the node outside of an exex as I am showing in the main function, which successfully allows me to simulate transactions for the mempool as shown. For example if an Exex wanted to to access this api, I created a helper function to help instantiate this since the ExEx ctx doesnt seem to have a rpc_registry to access the installed ones.
@mattsse wanted me to make an issue here to get a proper example made, If anyone can show me what I may be doing wrong instantiating this EthApi (or if theres a way to access the built in one) I would love to update the examples and make a PR.
use clap::Parser;
use eyre::Result;
use futures_util::{StreamExt, TryStreamExt};
use reth::api::{FullNodeComponents, NodeTypes};
use reth::chainspec::EthChainSpec;
use reth::primitives::EthereumHardforks;
use reth::rpc::api::eth::helpers::Call;
use reth::rpc::types::state::EvmOverrides;
use reth::rpc::types::BlockNumberOrTag;
use reth::{
builder::NodeHandle, chainspec::EthereumChainSpecParser, cli::Cli,
rpc::types::TransactionRequest, transaction_pool::TransactionPool,
};
use reth_evm::{ConfigureEvm, Evm};
use reth_exex::{ExExContext, ExExEvent};
use reth_node_ethereum::node::EthereumNode;
use reth_rpc::EthApi;
use reth_rpc_eth_types::{
EthStateCache, EthStateCacheConfig, FeeHistoryCache,
FeeHistoryCacheConfig, GasPriceOracle,
};
use reth_rpc_server_types::constants::{
DEFAULT_ETH_PROOF_WINDOW, DEFAULT_MAX_SIMULATE_BLOCKS, DEFAULT_PROOF_PERMITS,
};
use reth_tasks::pool::BlockingTaskPool;
use std::future::Future;
pub(crate) fn create_eth_api<Node>(ctx: &ExExContext<Node>) -> reth_rpc::eth::core::EthApiFor<Node>
where
Node: FullNodeComponents,
{
let provider = ctx.components.provider().clone();
let cache = EthStateCache::spawn_with(
provider.clone(),
EthStateCacheConfig::default(),
ctx.task_executor().clone(),
);
let fee_history_cache = FeeHistoryCache::new(FeeHistoryCacheConfig::default());
let gas_oracle = GasPriceOracle::new(provider.clone(), Default::default(), cache.clone());
EthApi::new(
provider.clone(),
ctx.pool().clone(),
ctx.network().clone(),
cache.clone(),
gas_oracle,
ctx.config.chain.genesis().gas_limit,
DEFAULT_MAX_SIMULATE_BLOCKS,
DEFAULT_ETH_PROOF_WINDOW,
BlockingTaskPool::build().expect("failed to build tracing pool"),
fee_history_cache,
ctx.components.evm_config().clone(),
DEFAULT_PROOF_PERMITS,
)
}
async fn init<Node: FullNodeComponents>(
ctx: ExExContext<Node>,
) -> Result<impl Future<Output = Result<()>>>
where
Node::Types: NodeTypes,
<Node::Types as NodeTypes>::ChainSpec: EthereumHardforks,
{
let eth_api = create_eth_api(&ctx);
let mut pending_transactions = ctx.pool().new_pending_pool_transactions_listener();
let evm_config = ctx.evm_config().clone();
tokio::spawn(async move {
// Waiting for new transactions
while let Some(event) = pending_transactions.next().await {
let tx = event.transaction;
println!("Transaction received: {tx:?}");
if let Some(_recipient) = tx.to() {
let transaction_request =
TransactionRequest::from_recovered_transaction(tx.to_consensus());
let sim = eth_api.spawn_with_call_at(
transaction_request,
BlockNumberOrTag::Latest.into(),
EvmOverrides::default(),
move |db, evm_env, tx_env| {
let mut evm = evm_config.evm_with_env(db, evm_env);
let result = evm.transact(tx_env)?;
Ok(result)
},
);
if let Ok(sim_result) = sim.await {
println!("sim result for transaction: {sim_result:?}");
}
}
}
});
Ok(my_exex(ctx))
}
async fn my_exex<Node: FullNodeComponents>(mut ctx: ExExContext<Node>) -> Result<()>
where
Node::Types: NodeTypes,
<Node::Types as NodeTypes>::ChainSpec: EthereumHardforks,
{
while let Some(notification) = ctx.notifications.try_next().await? {
// match ¬ification {
// ExExNotification::ChainCommitted { new } => {
// info!(committed_chain = ?new.range(), "Received commit");
// }
// ExExNotification::ChainReorged { old, new } => {
// info!(from_chain = ?old.range(), to_chain = ?new.range(), "Received reorg");
// }
// ExExNotification::ChainReverted { old } => {
// info!(reverted_chain = ?old.range(), "Received revert");
// }
// };
if let Some(committed_chain) = notification.committed_chain() {
ctx.events
.send(ExExEvent::FinishedHeight(committed_chain.tip().num_hash()))?;
}
}
Ok(())
}
fn print_type_of<T>(_: &T) {
println!("{}", std::any::type_name::<T>());
}
fn main() {
Cli::<EthereumChainSpecParser>::parse()
.run(|builder, args| async move {
// launch the node
let NodeHandle {
node,
node_exit_future,
} = builder
.node(EthereumNode::default())
.install_exex("my-exex", init)
.launch()
.await?;
let eth_api = node.rpc_registry.eth_api().clone();
let mut pending_transactions = node.pool.new_pending_pool_transactions_listener();
node.task_executor.spawn(Box::pin(async move {
// Waiting for new transactions
while let Some(event) = pending_transactions.next().await {
let tx = event.transaction;
println!("Transaction received: {tx:?}");
if let Some(_recipient) = tx.to() {
let transaction_request =
TransactionRequest::from_recovered_transaction(tx.to_consensus());
let evm_config = node.evm_config.clone();
let sim = eth_api.spawn_with_call_at(
transaction_request,
BlockNumberOrTag::Latest.into(),
EvmOverrides::default(),
move |db, evm_env, tx_env| {
let mut evm = evm_config.evm_with_env(db, evm_env);
let result = evm.transact(tx_env)?;
Ok(result)
},
);
if let Ok(sim_result) = sim.await {
println!("sim result for transaction: {sim_result:?}");
}
}
}
}));
node_exit_future.await
})
.unwrap();
}
The code in the main loop works well, but when I call any useful methods on the EthApi I create in the ExEx like .spawn_with_call_at give me an error:
error[E0599]: the method `spawn_with_call_at` exists for struct `EthApi<<Node as FullNodeTypes>::Provider, ..., ..., ...>`, but its trait bounds were not satisfied
--> mainnet-observer/src/main.rs:77:35
|
77 | let sim = eth_api.spawn_with_call_at(
| --------^^^^^^^^^^^^^^^^^^ method cannot be called due to unsatisfied trait bounds
|
::: /Users/tdettling/.cargo/git/checkouts/reth-e231042ee7db3fb7/ed7da87/crates/rpc/rpc-eth-types/src/error/mod.rs:43:1
|
43 | pub enum EthApiError {
| -------------------- doesn't satisfy `_: FromEvmError<<Node as FullNodeComponents>::Evm>`
|
::: /Users/tdettling/.cargo/git/checkouts/reth-e231042ee7db3fb7/ed7da87/crates/rpc/rpc/src/eth/core.rs:63:1
|
63 | pub struct EthApi<Provider: BlockReader, Pool, Network, EvmConfig> {
| ------------------------------------------------------------------ doesn't satisfy `_: Call`
|
= note: the following trait bounds were not satisfied:
`<<<<Node as FullNodeComponents>::Evm as ConfigureEvm>::BlockExecutorFactory as BlockExecutorFactory>::EvmFactory as EvmFactory>::Tx = TxEnv`
which is required by `EthApi<<Node as FullNodeTypes>::Provider, <Node as FullNodeComponents>::Pool, <Node as FullNodeComponents>::Network, <Node as FullNodeComponents>::Evm>: reth::rpc::api::reth_rpc_eth_api::helpers::Call`
`EthApiError: FromEvmError<<Node as FullNodeComponents>::Evm>`
which is required by `EthApi<<Node as FullNodeTypes>::Provider, <Node as FullNodeComponents>::Pool, <Node as FullNodeComponents>::Network, <Node as FullNodeComponents>::Evm>: reth::rpc::api::reth_rpc_eth_api::helpers::Call`
Additional context
No response