Skip to content

Commit

Permalink
full conditional evaluation within rpc
Browse files Browse the repository at this point in the history
  • Loading branch information
0xOsiris committed Dec 5, 2024
1 parent cabf7bf commit f57a95b
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 41 deletions.
10 changes: 9 additions & 1 deletion world-chain-builder/bin/world-chain-builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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::<OpChainSpecParser, ExtArgs>::parse().run(|builder, builder_args| async move {
let data_dir = builder.config().datadir();
Expand All @@ -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?;

Expand Down
96 changes: 56 additions & 40 deletions world-chain-builder/src/rpc/bundle.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
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};

/// Trait interface for `eth_sendRawTransactionConditional`
#[cfg_attr(not(test), rpc(server, namespace = "eth"))]
#[cfg_attr(test, rpc(server, client, namespace = "eth"))]
#[async_trait]
pub trait EthTransactionsExt: LoadTransaction<Provider = BlockReaderIdExt> {
pub trait EthTransactionsExt {
#[method(name = "sendRawTransactionConditional")]
async fn send_raw_transaction_conditional(
&self,
Expand All @@ -31,50 +27,57 @@ pub trait EthTransactionsExt: LoadTransaction<Provider = BlockReaderIdExt> {
/// WorldChainEthApi Extension for ERC-4337 Conditionally Included
///
/// Bundled Transactions
#[derive(Clone, Deref, Debug)]
pub struct WorldChainEthApiExt<S: EthTransactions> {
#[deref]
inner: S,
#[derive(Clone, Debug)]
pub struct WorldChainEthApiExt<Pool, Client> {
pool: Pool,
client: Client,
}

#[async_trait]
impl<S> EthTransactionsExtServer for WorldChainEthApiExt<S>
impl<Pool, Client> EthTransactionsExtServer for WorldChainEthApiExt<Pool, Client>
where
Self: LoadTransaction<
Pool: TransactionPool<Transaction = WorldChainPooledTransaction>,
Provider: BlockReaderIdExt + StateProviderFactory,
>,
S: EthTransactions,
<S as RpcNodeCore>::Provider: StateProviderFactory
Pool: TransactionPool<Transaction = WorldChainPooledTransaction> + Clone + 'static,
Client: BlockReaderIdExt + StateProviderFactory + 'static,
{
async fn send_raw_transaction_conditional(
&self,
tx: Bytes,
options: ConditionalOptions,
) -> RpcResult<B256> {
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<S> WorldChainEthApiExt<S>
impl<Pool, Client> WorldChainEthApiExt<Pool, Client>
where
Self: LoadTransaction<
Pool: TransactionPool<Transaction = WorldChainPooledTransaction>,
Provider: BlockReaderIdExt + StateProviderFactory,
>,
S: EthTransactions,
<S as RpcNodeCore>::Provider: StateProviderFactory
Pool: TransactionPool<Transaction = WorldChainPooledTransaction> + 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 <https://notes.ethereum.org/@yoav/SkaX2lS9j#>
/// See also <https://pkg.go.dev/github.com/aK0nshin/go-ethereum/arbitrum_types#ConditionalOptions>
pub fn validate_options(&self, options: ConditionalOptions) -> RpcResult<()> {
Expand Down Expand Up @@ -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<Address, AccountStorage>) -> RpcResult<()> {
pub fn validate_known_accounts(
&self,
known_accounts: HashMap<Address, AccountStorage>,
) -> 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(&current.to_be_bytes::<32>()) != *value {
return Err(ErrorObjectOwned::from(ErrorCode::from(-32003)));
if FixedBytes::<32>::from_slice(&current.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());
}
}
}
}
Expand Down

0 comments on commit f57a95b

Please sign in to comment.