From f57a95b52f4f3faad2c9fccfd797bf5c889ca02b Mon Sep 17 00:00:00 2001 From: 0xOsiris Date: Thu, 5 Dec 2024 15:41:02 -0700 Subject: [PATCH] full conditional evaluation within rpc --- .../bin/world-chain-builder.rs | 10 +- world-chain-builder/src/rpc/bundle.rs | 96 +++++++++++-------- 2 files changed, 65 insertions(+), 41 deletions(-) diff --git a/world-chain-builder/bin/world-chain-builder.rs b/world-chain-builder/bin/world-chain-builder.rs index 4d98cb22..90f74d7c 100644 --- a/world-chain-builder/bin/world-chain-builder.rs +++ b/world-chain-builder/bin/world-chain-builder.rs @@ -3,6 +3,8 @@ use reth_optimism_cli::chainspec::OpChainSpecParser; use reth_optimism_cli::Cli; use world_chain_builder::node::args::ExtArgs; use world_chain_builder::node::builder::WorldChainBuilder; +use world_chain_builder::rpc::bundle::EthTransactionsExtServer; +use world_chain_builder::rpc::bundle::WorldChainEthApiExt; #[cfg(all(feature = "jemalloc", unix))] #[global_allocator] @@ -23,7 +25,6 @@ fn main() { if std::env::var_os("RUST_LOG").is_none() { std::env::set_var("RUST_LOG", "info,reth=info"); } - if let Err(err) = Cli::::parse().run(|builder, builder_args| async move { let data_dir = builder.config().datadir(); @@ -32,6 +33,13 @@ fn main() { builder_args.clone(), data_dir.data_dir(), )?) + .extend_rpc_modules(move |ctx| { + let provider = ctx.provider().clone(); + let pool = ctx.pool().clone(); // Can we clone here? + let eth_api_ext = WorldChainEthApiExt::new(pool, provider); + ctx.modules.merge_configured(eth_api_ext.into_rpc())?; + Ok(()) + }) .launch() .await?; diff --git a/world-chain-builder/src/rpc/bundle.rs b/world-chain-builder/src/rpc/bundle.rs index 3897f172..0ec579b8 100644 --- a/world-chain-builder/src/rpc/bundle.rs +++ b/world-chain-builder/src/rpc/bundle.rs @@ -1,17 +1,13 @@ -use crate::pool::tx::WorldChainPooledTransaction; +use crate::{pool::tx::WorldChainPooledTransaction, primitives::recover_raw_transaction}; use alloy_eips::BlockId; use alloy_rpc_types::erc4337::{AccountStorage, ConditionalOptions}; -use derive_more::derive::Deref; use jsonrpsee::{ core::{async_trait, RpcResult}, proc_macros::rpc, types::{ErrorCode, ErrorObjectOwned}, }; -use reth::{ - rpc::{api::eth::helpers::{EthTransactions, LoadTransaction}, eth::RpcNodeCore}, - transaction_pool::TransactionPool, -}; +use reth::transaction_pool::{PoolTransaction, TransactionOrigin, TransactionPool}; use reth_provider::{BlockReaderIdExt, StateProviderFactory}; use revm_primitives::{Address, Bytes, FixedBytes, HashMap, B256}; @@ -19,7 +15,7 @@ use revm_primitives::{Address, Bytes, FixedBytes, HashMap, B256}; #[cfg_attr(not(test), rpc(server, namespace = "eth"))] #[cfg_attr(test, rpc(server, client, namespace = "eth"))] #[async_trait] -pub trait EthTransactionsExt: LoadTransaction { +pub trait EthTransactionsExt { #[method(name = "sendRawTransactionConditional")] async fn send_raw_transaction_conditional( &self, @@ -31,21 +27,17 @@ pub trait EthTransactionsExt: LoadTransaction { /// WorldChainEthApi Extension for ERC-4337 Conditionally Included /// /// Bundled Transactions -#[derive(Clone, Deref, Debug)] -pub struct WorldChainEthApiExt { - #[deref] - inner: S, +#[derive(Clone, Debug)] +pub struct WorldChainEthApiExt { + pool: Pool, + client: Client, } #[async_trait] -impl EthTransactionsExtServer for WorldChainEthApiExt +impl EthTransactionsExtServer for WorldChainEthApiExt where - Self: LoadTransaction< - Pool: TransactionPool, - Provider: BlockReaderIdExt + StateProviderFactory, - >, - S: EthTransactions, - ::Provider: StateProviderFactory + Pool: TransactionPool + Clone + 'static, + Client: BlockReaderIdExt + StateProviderFactory + 'static, { async fn send_raw_transaction_conditional( &self, @@ -53,28 +45,39 @@ where options: ConditionalOptions, ) -> RpcResult { self.validate_options(options)?; - self.inner - .send_raw_transaction(tx) + let (recovered, _) = recover_raw_transaction(tx.clone())?; + let pool_transaction = WorldChainPooledTransaction::from_pooled(recovered); + + // submit the transaction to the pool with a `Local` origin + let hash = self + .pool() + .add_transaction(TransactionOrigin::Local, pool_transaction) .await - .map_err(Into::into) + .map_err(|_| ErrorObjectOwned::from(ErrorCode::InternalError))?; + + Ok(hash) } } -impl WorldChainEthApiExt +impl WorldChainEthApiExt where - Self: LoadTransaction< - Pool: TransactionPool, - Provider: BlockReaderIdExt + StateProviderFactory, - >, - S: EthTransactions, - ::Provider: StateProviderFactory + Pool: TransactionPool + Clone + 'static, + Client: BlockReaderIdExt + StateProviderFactory + 'static, { - pub fn new(inner: S) -> Self { - Self { inner } + pub fn new(pool: Pool, client: Client) -> Self { + Self { pool, client } + } + + pub fn provider(&self) -> &Client { + &self.client + } + + pub fn pool(&self) -> &Pool { + &self.pool } /// Validates the conditional inclusion options provided by the client. - /// + /// /// reference for the implementation /// See also pub fn validate_options(&self, options: ConditionalOptions) -> RpcResult<()> { @@ -112,29 +115,42 @@ where } /// Validates the account storage slots/storage root provided by the client - /// + /// /// Matches the current state of the account storage slots/storage root. /// TODO: We need to throttle the number of accounts that can be validated at once for DOS protection. - pub fn validate_known_accounts(&self, known_accounts: HashMap) -> RpcResult<()> { + pub fn validate_known_accounts( + &self, + known_accounts: HashMap, + ) -> RpcResult<()> { let state = self - .provider().state_by_block_id(BlockId::latest()).map_err(|_| ErrorObjectOwned::from(ErrorCode::InternalError))?; + .provider() + .state_by_block_id(BlockId::latest()) + .map_err(|_| ErrorObjectOwned::from(ErrorCode::InternalError))?; for (address, storage) in known_accounts.iter() { match storage { AccountStorage::Slots(slots) => { for (slot, value) in slots.iter() { - let current = state.storage(*address, slot.clone().into()).map_err(|_| ErrorObjectOwned::from(ErrorCode::InternalError))?; + let current = state + .storage(*address, slot.clone().into()) + .map_err(|_| ErrorObjectOwned::from(ErrorCode::InternalError))?; if let Some(current) = current { - if FixedBytes::<32>::from_slice(¤t.to_be_bytes::<32>()) != *value { - return Err(ErrorObjectOwned::from(ErrorCode::from(-32003))); + if FixedBytes::<32>::from_slice(¤t.to_be_bytes::<32>()) != *value + { + return Err(ErrorCode::from(-32003).into()); } } else { - return Err(ErrorObjectOwned::from(ErrorCode::from(-32003))); + return Err(ErrorCode::from(-32003).into()); } } } - AccountStorage::RootHash(root) => { - + AccountStorage::RootHash(expected) => { + let root = state + .storage_root(*address, Default::default()) + .map_err(|_| ErrorObjectOwned::from(ErrorCode::InternalError))?; + if *expected != root { + return Err(ErrorCode::from(-32003).into()); + } } } }