From 886ba01b3ba392a33af1e36892225f6dda9f071d Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Fri, 4 Oct 2024 03:25:22 +0200 Subject: [PATCH] feat: impl `wallet_` namespace --- Cargo.lock | 5 +++ Cargo.toml | 1 + bin/alphanet/Cargo.toml | 1 + bin/alphanet/src/main.rs | 22 ++++++++-- crates/wallet/Cargo.toml | 4 ++ crates/wallet/src/lib.rs | 94 +++++++++++++++++++++++++++++++++++++++- 6 files changed, 122 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c2c029f..e83a457 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -772,6 +772,7 @@ name = "alphanet" version = "0.0.0" dependencies = [ "alphanet-node", + "alphanet-wallet", "clap", "reth-cli-util", "reth-node-builder", @@ -844,7 +845,11 @@ dependencies = [ "alloy-primitives", "alloy-rpc-types", "jsonrpsee", + "reth-optimism-rpc", + "reth-transaction-pool", "serde", + "thiserror", + "tracing", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 68fbd29..7b87a02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -119,6 +119,7 @@ tracing = "0.1.0" serde = "1" serde_json = "1" once_cell = "1.19" +thiserror = "1" # misc-testing rstest = "0.18.2" diff --git a/bin/alphanet/Cargo.toml b/bin/alphanet/Cargo.toml index a15ef6b..0ac20ae 100644 --- a/bin/alphanet/Cargo.toml +++ b/bin/alphanet/Cargo.toml @@ -13,6 +13,7 @@ workspace = true [dependencies] alphanet-node.workspace = true +alphanet-wallet.workspace = true tracing.workspace = true reth-cli-util.workspace = true reth-node-builder.workspace = true diff --git a/bin/alphanet/src/main.rs b/bin/alphanet/src/main.rs index b26d056..4bd5f44 100644 --- a/bin/alphanet/src/main.rs +++ b/bin/alphanet/src/main.rs @@ -24,6 +24,7 @@ //! - `min-trace-logs`: Disables all logs below `trace` level. use alphanet_node::{chainspec::AlphanetChainSpecParser, node::AlphaNetNode}; +use alphanet_wallet::{AlphaNetWallet, AlphaNetWalletApiServer}; use clap::Parser; use reth_node_builder::{engine_tree_config::TreeConfig, EngineNodeLauncher}; use reth_optimism_cli::Cli; @@ -53,13 +54,26 @@ fn main() { .with_components(AlphaNetNode::components(rollup_args.clone())) .with_add_ons::() .extend_rpc_modules(move |ctx| { + let sequencer_client = rollup_args + .sequencer_http + .map(|sequencer_http| SequencerClient::new(sequencer_http)); + // register sequencer tx forwarder - if let Some(sequencer_http) = rollup_args.sequencer_http.clone() { - ctx.registry - .eth_api() - .set_sequencer_client(SequencerClient::new(sequencer_http))?; + if let Some(sequencer_client) = sequencer_client.clone() { + ctx.registry.eth_api().set_sequencer_client(sequencer_client)?; } + // register alphanet wallet namespace + ctx.modules.merge_configured( + AlphaNetWallet::new( + ctx.pool().clone(), + sequencer_client.clone(), + ctx.config().chain.chain().id(), + Vec::new(), + ) + .into_rpc(), + )?; + Ok(()) }) .launch_with_fn(|builder| { diff --git a/crates/wallet/Cargo.toml b/crates/wallet/Cargo.toml index 44bdf84..6cc5066 100644 --- a/crates/wallet/Cargo.toml +++ b/crates/wallet/Cargo.toml @@ -13,7 +13,11 @@ categories.workspace = true alloy-primitives.workspace = true alloy-rpc-types.workspace = true jsonrpsee = { workspace = true, features = ["server", "macros"] } +reth-transaction-pool.workspace = true +reth-optimism-rpc.workspace = true serde = { workspace = true, features = ["derive"] } +thiserror.workspace = true +tracing.workspace = true [lints] workspace = true diff --git a/crates/wallet/src/lib.rs b/crates/wallet/src/lib.rs index 39411e5..23f6e5b 100644 --- a/crates/wallet/src/lib.rs +++ b/crates/wallet/src/lib.rs @@ -17,10 +17,13 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] -use alloy_primitives::{map::HashMap, Address, ChainId, TxHash}; +use alloy_primitives::{map::HashMap, Address, ChainId, TxHash, U256}; use alloy_rpc_types::TransactionRequest; use jsonrpsee::{core::RpcResult, proc_macros::rpc}; +use reth_optimism_rpc::SequencerClient; +use reth_transaction_pool::TransactionPool; use serde::{Deserialize, Serialize}; +use tracing::trace; /// The capability to perform [EIP-7702][eip-7702] delegations, sponsored by the sequencer. /// @@ -77,3 +80,92 @@ pub trait AlphaNetWalletApi { #[method(name = "sendTransaction")] fn send_transaction(&self, request: TransactionRequest) -> RpcResult; } + +#[derive(Debug, thiserror::Error)] +pub enum AlphaNetWalletError { + #[error("tx value not zero")] + ValueNotZero, + #[error("tx from field is set")] + FromSet, + #[error("tx nonce is set")] + NonceSet, + #[error("invalid authorization address")] + InvalidAuthorization, + #[error("the authority of an authorization item is the sequencer")] + AuthorityIsSequencer, +} + +impl From for jsonrpsee::types::error::ErrorObject<'static> { + fn from(error: AlphaNetWalletError) -> Self { + jsonrpsee::types::error::ErrorObject::owned::<()>( + jsonrpsee::types::error::INVALID_PARAMS_CODE, + error.to_string(), + None, + ) + } +} + +pub struct AlphaNetWallet { + pool: Pool, + sequencer_client: Option, + capabilities: WalletCapabilities, +} + +impl AlphaNetWallet { + pub fn new( + pool: Pool, + sequencer_client: Option, + chain_id: ChainId, + valid_designations: Vec
, + ) -> Self { + let mut caps = HashMap::default(); + caps.insert( + chain_id, + Capabilities { delegation: DelegationCapability { addresses: valid_designations } }, + ); + + Self { pool, sequencer_client, capabilities: WalletCapabilities(caps) } + } +} + +impl AlphaNetWalletApiServer for AlphaNetWallet +where + Pool: TransactionPool + Clone + 'static, +{ + fn get_capabilities(&self) -> RpcResult { + trace!(target: "rpc::wallet", "Serving wallet_getCapabilities"); + Ok(self.capabilities.clone()) + } + + fn send_transaction(&self, request: TransactionRequest) -> RpcResult { + trace!(target: "rpc::wallet", ?request, "Serving wallet_sendTransaction"); + + // reject transactions that have a non-zero value to prevent draining the sequencer. + if request.value.is_some_and(|val| val > U256::ZERO) { + return Err(AlphaNetWalletError::ValueNotZero.into()); + } + + // reject transactions that have from set, as this will be the sequencer. + if request.from.is_some() { + return Err(AlphaNetWalletError::FromSet.into()); + } + + // reject transaction requests that have nonce set, as this is managed by the sequencer. + if request.nonce.is_some() { + return Err(AlphaNetWalletError::NonceSet.into()); + } + // set nonce + + if let Some(authorizations) = request.authorization_list { + // todo: check auth.address is valid + // todo: disallow eip-7702 item containing sequencer addr + if authorizations.iter().any(|auth| false) { + // reject, invalid contract + } + } + + // build and sign + // add to pool, or send to sequencer + todo!() + } +}