Skip to content

Commit

Permalink
feat: impl wallet_ namespace
Browse files Browse the repository at this point in the history
  • Loading branch information
onbjerg committed Oct 7, 2024
1 parent fa3f2e7 commit bea5005
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 5 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ reth-provider = { git = "https://github.com/paradigmxyz/reth.git", rev = "784267
reth-revm = { git = "https://github.com/paradigmxyz/reth.git", rev = "7842673", features = [
"optimism",
] }
reth-storage-api = { git = "https://github.com/paradigmxyz/reth.git", rev = "7842673" }
reth-tracing = { git = "https://github.com/paradigmxyz/reth.git", rev = "7842673" }
reth-transaction-pool = { git = "https://github.com/paradigmxyz/reth.git", rev = "7842673" }

Expand All @@ -119,6 +120,7 @@ tracing = "0.1.0"
serde = "1"
serde_json = "1"
once_cell = "1.19"
thiserror = "1"

# misc-testing
rstest = "0.18.2"
1 change: 1 addition & 0 deletions bin/alphanet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 16 additions & 4 deletions bin/alphanet/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -53,12 +54,23 @@ fn main() {
.with_components(AlphaNetNode::components(rollup_args.clone()))
.with_add_ons(OptimismAddOns::new(rollup_args.sequencer_http.clone()))
.extend_rpc_modules(move |ctx| {
let sequencer_client = rollup_args.sequencer_http.map(SequencerClient::new);

// 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.provider().clone(),
ctx.pool().clone(),
sequencer_client.clone(),
ctx.config().chain.chain().id(),
Vec::new(),
)
.into_rpc(),
)?;

Ok(())
})
Expand Down
6 changes: 6 additions & 0 deletions crates/wallet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ categories.workspace = true
alloy-primitives.workspace = true
alloy-rpc-types.workspace = true
jsonrpsee = { workspace = true, features = ["server", "macros"] }
reth-optimism-rpc.workspace = true
reth-primitives.workspace = true
reth-storage-api.workspace = true
reth-transaction-pool.workspace = true
serde = { workspace = true, features = ["derive"] }
thiserror.workspace = true
tracing.workspace = true

[lints]
workspace = true
138 changes: 137 additions & 1 deletion crates/wallet/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,15 @@

#![cfg_attr(not(test), warn(unused_crate_dependencies))]

use alloy_primitives::{map::HashMap, Address, ChainId, TxHash};
use alloy_primitives::{map::HashMap, Address, ChainId, TxHash, TxKind, U256};
use alloy_rpc_types::TransactionRequest;
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use reth_optimism_rpc::SequencerClient;
use reth_primitives::revm_primitives::Bytecode;
use reth_storage_api::StateProvider;
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.
///
Expand All @@ -45,6 +50,12 @@ pub struct Capabilities {
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct WalletCapabilities(pub HashMap<ChainId, Capabilities>);

impl WalletCapabilities {
pub fn get(&self, chain_id: ChainId) -> Option<&Capabilities> {
self.0.get(&chain_id)
}
}

/// AlphaNet `wallet_` RPC namespace.
#[cfg_attr(not(test), rpc(server, namespace = "wallet"))]
#[cfg_attr(test, rpc(server, client, namespace = "wallet"))]
Expand Down Expand Up @@ -77,3 +88,128 @@ pub trait AlphaNetWalletApi {
#[method(name = "sendTransaction")]
fn send_transaction(&self, request: TransactionRequest) -> RpcResult<TxHash>;
}

#[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,
#[error("the destination of the transaction is not a delegated account")]
IllegalDestination,
}

impl From<AlphaNetWalletError> 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<Provider, Pool> {
provider: Provider,
pool: Pool,
sequencer_client: Option<SequencerClient>,
chain_id: ChainId,
capabilities: WalletCapabilities,
}

impl<Provider, Pool> AlphaNetWallet<Provider, Pool> {
pub fn new(
provider: Provider,
pool: Pool,
sequencer_client: Option<SequencerClient>,
chain_id: ChainId,
valid_designations: Vec<Address>,
) -> Self {
let mut caps = HashMap::default();
caps.insert(
chain_id,
Capabilities { delegation: DelegationCapability { addresses: valid_designations } },
);

Self { provider, pool, sequencer_client, chain_id, capabilities: WalletCapabilities(caps) }
}
}

impl<Provider, Pool> AlphaNetWalletApiServer for AlphaNetWallet<Provider, Pool>
where
Pool: TransactionPool + Clone + 'static,
Provider: StateProvider + Send + Sync + 'static,
{
fn get_capabilities(&self) -> RpcResult<WalletCapabilities> {
trace!(target: "rpc::wallet", "Serving wallet_getCapabilities");
Ok(self.capabilities.clone())
}

fn send_transaction(&self, mut request: TransactionRequest) -> RpcResult<TxHash> {
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 and chain id
request.chain_id = Some(self.chain_id);
// gas estimation

let valid_delegations: &[Address] = self
.capabilities
.get(self.chain_id)
.map(|caps| caps.delegation.addresses.as_ref())
.unwrap_or_default();
if let Some(authorizations) = request.authorization_list {
// check that all auth items delegate to a valid address
if authorizations.iter().any(|auth| !valid_delegations.contains(&auth.address)) {
return Err(AlphaNetWalletError::InvalidAuthorization.into());
}
} else {
// if to is set, ensure that it is an account that delegates to a whitelisted address
// if this is not a 7702 tx
match request.to {
Some(TxKind::Call(addr)) => {
if let Ok(Some(code)) = self.provider.account_code(addr) {
match code.0 {
Bytecode::Eip7702(code) => {
// not a whitelisted address
if !valid_delegations.contains(&code.address()) {
return Err(AlphaNetWalletError::IllegalDestination.into());
}
}
// not a 7702 account
_ => return Err(AlphaNetWalletError::IllegalDestination.into()),
}
} else {
// no bytecode
return Err(AlphaNetWalletError::IllegalDestination.into());
}
}
// create tx's disallowed
_ => return Err(AlphaNetWalletError::IllegalDestination.into()),
}
}

// build and sign
// add to pool, or send to sequencer
todo!()
}
}

0 comments on commit bea5005

Please sign in to comment.