From 5d2867f2c5bd2aaa5673f31cf4c13b8f4f1e2c39 Mon Sep 17 00:00:00 2001 From: Jennifer Date: Wed, 25 Sep 2024 13:34:58 +0200 Subject: [PATCH] feat(rpc): Replace TypedTransactionType (#11089) Co-authored-by: Matthias Seitz --- Cargo.lock | 11 +- crates/rpc/rpc-eth-api/Cargo.toml | 1 - crates/rpc/rpc-eth-api/src/helpers/signer.rs | 9 +- .../rpc-eth-api/src/helpers/transaction.rs | 193 ++---------------- .../rpc-types-compat/src/transaction/mod.rs | 4 - .../rpc-types-compat/src/transaction/typed.rs | 58 ------ crates/rpc/rpc-types/Cargo.toml | 10 +- crates/rpc/rpc-types/src/eth/mod.rs | 1 - .../rpc/rpc-types/src/eth/transaction/mod.rs | 3 - .../rpc-types/src/eth/transaction/typed.rs | 113 ---------- crates/rpc/rpc-types/src/lib.rs | 1 - crates/rpc/rpc/Cargo.toml | 7 +- crates/rpc/rpc/src/eth/helpers/signer.rs | 95 ++++++--- 13 files changed, 93 insertions(+), 413 deletions(-) delete mode 100644 crates/rpc/rpc-types-compat/src/transaction/typed.rs delete mode 100644 crates/rpc/rpc-types/src/eth/transaction/mod.rs delete mode 100644 crates/rpc/rpc-types/src/eth/transaction/typed.rs diff --git a/Cargo.lock b/Cargo.lock index b557d4b58..da895a2b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -494,7 +494,6 @@ dependencies = [ "alloy-rlp", "alloy-serde", "alloy-sol-types", - "arbitrary", "cfg-if", "derive_more", "hashbrown 0.14.5", @@ -8404,6 +8403,7 @@ dependencies = [ name = "reth-rpc" version = "1.0.7" dependencies = [ + "alloy-consensus", "alloy-dyn-abi", "alloy-genesis", "alloy-network", @@ -8417,6 +8417,8 @@ dependencies = [ "alloy-rpc-types-trace", "alloy-rpc-types-txpool", "alloy-serde", + "alloy-signer", + "alloy-signer-local", "async-trait", "derive_more", "futures", @@ -8446,7 +8448,6 @@ dependencies = [ "reth-rpc-eth-api", "reth-rpc-eth-types", "reth-rpc-server-types", - "reth-rpc-types", "reth-rpc-types-compat", "reth-tasks", "reth-testing-utils", @@ -8455,7 +8456,6 @@ dependencies = [ "revm", "revm-inspectors", "revm-primitives", - "secp256k1", "serde", "serde_json", "thiserror", @@ -8628,7 +8628,6 @@ dependencies = [ "reth-revm", "reth-rpc-eth-types", "reth-rpc-server-types", - "reth-rpc-types", "reth-rpc-types-compat", "reth-tasks", "reth-transaction-pool", @@ -8718,12 +8717,8 @@ dependencies = [ name = "reth-rpc-types" version = "1.0.7" dependencies = [ - "alloy-primitives", - "alloy-rpc-types", "alloy-rpc-types-engine", - "arbitrary", "jsonrpsee-types", - "rand 0.8.5", ] [[package]] diff --git a/crates/rpc/rpc-eth-api/Cargo.toml b/crates/rpc/rpc-eth-api/Cargo.toml index 88cff48c2..4dd61324c 100644 --- a/crates/rpc/rpc-eth-api/Cargo.toml +++ b/crates/rpc/rpc-eth-api/Cargo.toml @@ -21,7 +21,6 @@ reth-evm.workspace = true reth-primitives.workspace = true reth-provider.workspace = true reth-revm.workspace = true -reth-rpc-types.workspace = true reth-rpc-types-compat.workspace = true reth-tasks = { workspace = true, features = ["rayon"] } reth-transaction-pool.workspace = true diff --git a/crates/rpc/rpc-eth-api/src/helpers/signer.rs b/crates/rpc/rpc-eth-api/src/helpers/signer.rs index 3580410b6..ab11e62d5 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/signer.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/signer.rs @@ -1,13 +1,12 @@ //! An abstraction over ethereum signers. -use std::result; - use alloy_dyn_abi::TypedData; use alloy_primitives::Address; +use alloy_rpc_types_eth::TransactionRequest; use dyn_clone::DynClone; use reth_primitives::{Signature, TransactionSigned}; use reth_rpc_eth_types::SignError; -use reth_rpc_types::TypedTransactionRequest; +use std::result; /// Result returned by [`EthSigner`] methods. pub type Result = result::Result; @@ -27,9 +26,9 @@ pub trait EthSigner: Send + Sync + DynClone { async fn sign(&self, address: Address, message: &[u8]) -> Result; /// signs a transaction request using the given account in request - fn sign_transaction( + async fn sign_transaction( &self, - request: TypedTransactionRequest, + request: TransactionRequest, address: &Address, ) -> Result; diff --git a/crates/rpc/rpc-eth-api/src/helpers/transaction.rs b/crates/rpc/rpc-eth-api/src/helpers/transaction.rs index 60b507af3..a78f2cd33 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/transaction.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/transaction.rs @@ -2,7 +2,8 @@ //! network. use alloy_dyn_abi::TypedData; -use alloy_primitives::{Address, Bytes, TxHash, TxKind, B256, U256}; +use alloy_network::TransactionBuilder; +use alloy_primitives::{Address, Bytes, TxHash, B256}; use alloy_rpc_types::{BlockNumberOrTag, TransactionInfo}; use alloy_rpc_types_eth::transaction::TransactionRequest; use futures::Future; @@ -14,13 +15,6 @@ use reth_rpc_eth_types::{ utils::{binary_search, recover_raw_transaction}, EthApiError, EthStateCache, SignError, TransactionSource, }; -use reth_rpc_types::{ - transaction::{ - EIP1559TransactionRequest, EIP2930TransactionRequest, EIP4844TransactionRequest, - LegacyTransactionRequest, - }, - TypedTransactionRequest, -}; use reth_rpc_types_compat::transaction::{from_recovered, from_recovered_with_block_context}; use reth_transaction_pool::{PoolTransaction, TransactionOrigin, TransactionPool}; @@ -369,172 +363,14 @@ pub trait EthTransactions: LoadTransaction { } let chain_id = self.chain_id(); + request.chain_id = Some(chain_id.to::()); let estimated_gas = self.estimate_gas_at(request.clone(), BlockId::pending(), None).await?; let gas_limit = estimated_gas; + request.set_gas_limit(gas_limit.to::()); - let TransactionRequest { - to, - gas_price, - max_fee_per_gas, - max_priority_fee_per_gas, - gas, - value, - input: data, - nonce, - mut access_list, - max_fee_per_blob_gas, - blob_versioned_hashes, - sidecar, - .. - } = request; - - // todo: remove this inlining after https://github.com/alloy-rs/alloy/pull/183#issuecomment-1928161285 - let transaction = match ( - gas_price, - max_fee_per_gas, - access_list.take(), - max_fee_per_blob_gas, - blob_versioned_hashes, - sidecar, - ) { - // legacy transaction - // gas price required - (Some(_), None, None, None, None, None) => { - Some(TypedTransactionRequest::Legacy(LegacyTransactionRequest { - nonce: nonce.unwrap_or_default(), - gas_price: U256::from(gas_price.unwrap_or_default()), - gas_limit: U256::from(gas.unwrap_or_default()), - value: value.unwrap_or_default(), - input: data.into_input().unwrap_or_default(), - kind: to.unwrap_or(TxKind::Create), - chain_id: None, - })) - } - // EIP2930 - // if only accesslist is set, and no eip1599 fees - (_, None, Some(access_list), None, None, None) => { - Some(TypedTransactionRequest::EIP2930(EIP2930TransactionRequest { - nonce: nonce.unwrap_or_default(), - gas_price: U256::from(gas_price.unwrap_or_default()), - gas_limit: U256::from(gas.unwrap_or_default()), - value: value.unwrap_or_default(), - input: data.into_input().unwrap_or_default(), - kind: to.unwrap_or(TxKind::Create), - chain_id: 0, - access_list, - })) - } - // EIP1559 - // if 4844 fields missing - // gas_price, max_fee_per_gas, access_list, max_fee_per_blob_gas, - // blob_versioned_hashes, sidecar, - (None, _, _, None, None, None) => { - // Empty fields fall back to the canonical transaction schema. - Some(TypedTransactionRequest::EIP1559(EIP1559TransactionRequest { - nonce: nonce.unwrap_or_default(), - max_fee_per_gas: U256::from(max_fee_per_gas.unwrap_or_default()), - max_priority_fee_per_gas: U256::from( - max_priority_fee_per_gas.unwrap_or_default(), - ), - gas_limit: U256::from(gas.unwrap_or_default()), - value: value.unwrap_or_default(), - input: data.into_input().unwrap_or_default(), - kind: to.unwrap_or(TxKind::Create), - chain_id: 0, - access_list: access_list.unwrap_or_default(), - })) - } - // EIP4884 - // all blob fields required - ( - None, - _, - _, - Some(max_fee_per_blob_gas), - Some(blob_versioned_hashes), - Some(sidecar), - ) => { - // As per the EIP, we follow the same semantics as EIP-1559. - Some(TypedTransactionRequest::EIP4844(EIP4844TransactionRequest { - chain_id: 0, - nonce: nonce.unwrap_or_default(), - max_priority_fee_per_gas: U256::from( - max_priority_fee_per_gas.unwrap_or_default(), - ), - max_fee_per_gas: U256::from(max_fee_per_gas.unwrap_or_default()), - gas_limit: U256::from(gas.unwrap_or_default()), - value: value.unwrap_or_default(), - input: data.into_input().unwrap_or_default(), - to: match to { - Some(TxKind::Call(to)) => to, - _ => Address::default(), - }, - access_list: access_list.unwrap_or_default(), - - // eip-4844 specific. - max_fee_per_blob_gas: U256::from(max_fee_per_blob_gas), - blob_versioned_hashes, - sidecar, - })) - } - - _ => None, - }; - - let transaction = match transaction { - Some(TypedTransactionRequest::Legacy(mut req)) => { - req.chain_id = Some(chain_id.to()); - req.gas_limit = gas_limit.saturating_to(); - req.gas_price = self.legacy_gas_price(gas_price.map(U256::from)).await?; - - TypedTransactionRequest::Legacy(req) - } - Some(TypedTransactionRequest::EIP2930(mut req)) => { - req.chain_id = chain_id.to(); - req.gas_limit = gas_limit.saturating_to(); - req.gas_price = self.legacy_gas_price(gas_price.map(U256::from)).await?; - - TypedTransactionRequest::EIP2930(req) - } - Some(TypedTransactionRequest::EIP1559(mut req)) => { - let (max_fee_per_gas, max_priority_fee_per_gas) = self - .eip1559_fees( - max_fee_per_gas.map(U256::from), - max_priority_fee_per_gas.map(U256::from), - ) - .await?; - - req.chain_id = chain_id.to(); - req.gas_limit = gas_limit.saturating_to(); - req.max_fee_per_gas = max_fee_per_gas.saturating_to(); - req.max_priority_fee_per_gas = max_priority_fee_per_gas.saturating_to(); - - TypedTransactionRequest::EIP1559(req) - } - Some(TypedTransactionRequest::EIP4844(mut req)) => { - let (max_fee_per_gas, max_priority_fee_per_gas) = self - .eip1559_fees( - max_fee_per_gas.map(U256::from), - max_priority_fee_per_gas.map(U256::from), - ) - .await?; - - req.max_fee_per_gas = max_fee_per_gas; - req.max_priority_fee_per_gas = max_priority_fee_per_gas; - req.max_fee_per_blob_gas = - self.eip4844_blob_fee(max_fee_per_blob_gas.map(U256::from)).await?; - - req.chain_id = chain_id.to(); - req.gas_limit = gas_limit; - - TypedTransactionRequest::EIP4844(req) - } - None => return Err(EthApiError::ConflictingFeeFieldsInRequest.into()), - }; - - let signed_tx = self.sign_request(&from, transaction)?; + let signed_tx = self.sign_request(&from, request).await?; let recovered = signed_tx.into_ecrecovered().ok_or(EthApiError::InvalidTransactionSignature)?; @@ -555,17 +391,20 @@ pub trait EthTransactions: LoadTransaction { fn sign_request( &self, from: &Address, - request: TypedTransactionRequest, - ) -> Result { - for signer in self.signers().read().iter() { - if signer.is_signer_for(from) { - return match signer.sign_transaction(request, from) { - Ok(tx) => Ok(tx), - Err(e) => Err(e.into_eth_err()), + txn: TransactionRequest, + ) -> impl Future> + Send { + async move { + let signers: Vec<_> = self.signers().read().iter().cloned().collect(); + for signer in signers { + if signer.is_signer_for(from) { + return match signer.sign_transaction(txn, from).await { + Ok(tx) => Ok(tx), + Err(e) => Err(e.into_eth_err()), + } } } + Err(EthApiError::InvalidTransactionSignature.into()) } - Err(EthApiError::InvalidTransactionSignature.into()) } /// Signs given message. Returns the signature. diff --git a/crates/rpc/rpc-types-compat/src/transaction/mod.rs b/crates/rpc/rpc-types-compat/src/transaction/mod.rs index a415a71ba..7e6eb0d42 100644 --- a/crates/rpc/rpc-types-compat/src/transaction/mod.rs +++ b/crates/rpc/rpc-types-compat/src/transaction/mod.rs @@ -1,11 +1,7 @@ //! Compatibility functions for rpc `Transaction` type. - mod signature; -mod typed; pub use signature::*; -pub use typed::*; - use std::fmt; use alloy_rpc_types::{ diff --git a/crates/rpc/rpc-types-compat/src/transaction/typed.rs b/crates/rpc/rpc-types-compat/src/transaction/typed.rs deleted file mode 100644 index ffed5117e..000000000 --- a/crates/rpc/rpc-types-compat/src/transaction/typed.rs +++ /dev/null @@ -1,58 +0,0 @@ -/// Converts a typed transaction request into a primitive transaction. -/// -/// Returns `None` if any of the following are true: -/// - `nonce` is greater than [`u64::MAX`] -/// - `gas_limit` is greater than [`u64::MAX`] -/// - `value` is greater than [`u128::MAX`] -pub fn to_primitive_transaction( - tx_request: reth_rpc_types::TypedTransactionRequest, -) -> Option { - use reth_primitives::{Transaction, TxEip1559, TxEip2930, TxEip4844, TxLegacy}; - use reth_rpc_types::TypedTransactionRequest; - - Some(match tx_request { - TypedTransactionRequest::Legacy(tx) => Transaction::Legacy(TxLegacy { - chain_id: tx.chain_id, - nonce: tx.nonce, - gas_price: tx.gas_price.to(), - gas_limit: tx.gas_limit.try_into().ok()?, - to: tx.kind, - value: tx.value, - input: tx.input, - }), - TypedTransactionRequest::EIP2930(tx) => Transaction::Eip2930(TxEip2930 { - chain_id: tx.chain_id, - nonce: tx.nonce, - gas_price: tx.gas_price.to(), - gas_limit: tx.gas_limit.try_into().ok()?, - to: tx.kind, - value: tx.value, - input: tx.input, - access_list: tx.access_list, - }), - TypedTransactionRequest::EIP1559(tx) => Transaction::Eip1559(TxEip1559 { - chain_id: tx.chain_id, - nonce: tx.nonce, - max_fee_per_gas: tx.max_fee_per_gas.to(), - gas_limit: tx.gas_limit.try_into().ok()?, - to: tx.kind, - value: tx.value, - input: tx.input, - access_list: tx.access_list, - max_priority_fee_per_gas: tx.max_priority_fee_per_gas.to(), - }), - TypedTransactionRequest::EIP4844(tx) => Transaction::Eip4844(TxEip4844 { - chain_id: tx.chain_id, - nonce: tx.nonce, - gas_limit: tx.gas_limit.to(), - max_fee_per_gas: tx.max_fee_per_gas.to(), - max_priority_fee_per_gas: tx.max_priority_fee_per_gas.to(), - to: tx.to, - value: tx.value, - access_list: tx.access_list, - blob_versioned_hashes: tx.blob_versioned_hashes, - max_fee_per_blob_gas: tx.max_fee_per_blob_gas.to(), - input: tx.input, - }), - }) -} diff --git a/crates/rpc/rpc-types/Cargo.toml b/crates/rpc/rpc-types/Cargo.toml index c2c847de1..54840490b 100644 --- a/crates/rpc/rpc-types/Cargo.toml +++ b/crates/rpc/rpc-types/Cargo.toml @@ -14,25 +14,17 @@ workspace = true [dependencies] # ethereum -alloy-primitives = { workspace = true, features = ["rand", "rlp", "serde"] } -alloy-rpc-types.workspace = true alloy-rpc-types-engine = { workspace = true, features = ["std", "serde", "jsonrpsee-types"], optional = true } # misc jsonrpsee-types = { workspace = true, optional = true } [dev-dependencies] -# misc -alloy-primitives = { workspace = true, features = ["rand", "rlp", "serde", "arbitrary"] } -arbitrary = { workspace = true, features = ["derive"] } -rand.workspace = true [features] default = ["jsonrpsee-types"] jsonrpsee-types = [ "dep:jsonrpsee-types", "dep:alloy-rpc-types-engine", - "alloy-rpc-types/jsonrpsee-types", "alloy-rpc-types-engine/jsonrpsee-types", -] -arbitrary = ["alloy-primitives/arbitrary", "alloy-rpc-types/arbitrary"] +] \ No newline at end of file diff --git a/crates/rpc/rpc-types/src/eth/mod.rs b/crates/rpc/rpc-types/src/eth/mod.rs index 3f6c67ac5..0db9f0e41 100644 --- a/crates/rpc/rpc-types/src/eth/mod.rs +++ b/crates/rpc/rpc-types/src/eth/mod.rs @@ -1,7 +1,6 @@ //! Ethereum related types pub(crate) mod error; -pub mod transaction; // re-export #[cfg(feature = "jsonrpsee-types")] diff --git a/crates/rpc/rpc-types/src/eth/transaction/mod.rs b/crates/rpc/rpc-types/src/eth/transaction/mod.rs deleted file mode 100644 index d29411965..000000000 --- a/crates/rpc/rpc-types/src/eth/transaction/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! RPC types for transactions -mod typed; -pub use typed::*; diff --git a/crates/rpc/rpc-types/src/eth/transaction/typed.rs b/crates/rpc/rpc-types/src/eth/transaction/typed.rs deleted file mode 100644 index 578bb2cb0..000000000 --- a/crates/rpc/rpc-types/src/eth/transaction/typed.rs +++ /dev/null @@ -1,113 +0,0 @@ -use alloy_primitives::{Address, Bytes, TxKind, B256, U256}; -use alloy_rpc_types::{AccessList, BlobTransactionSidecar}; - -/// Container type for various Ethereum transaction requests -/// -/// Its variants correspond to specific allowed transactions: -/// 1. Legacy (pre-EIP2718) [`LegacyTransactionRequest`] -/// 2. EIP2930 (state access lists) [`EIP2930TransactionRequest`] -/// 3. EIP1559 [`EIP1559TransactionRequest`] -/// 4. EIP4844 [`EIP4844TransactionRequest`] -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum TypedTransactionRequest { - /// Represents a Legacy (pre-EIP2718) transaction request - Legacy(LegacyTransactionRequest), - /// Represents an EIP2930 (state access lists) transaction request - EIP2930(EIP2930TransactionRequest), - /// Represents an EIP1559 transaction request - EIP1559(EIP1559TransactionRequest), - /// Represents an EIP4844 transaction request - EIP4844(EIP4844TransactionRequest), -} - -/// Represents a legacy transaction request -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct LegacyTransactionRequest { - /// The nonce of the transaction - pub nonce: u64, - /// The gas price for the transaction - pub gas_price: U256, - /// The gas limit for the transaction - pub gas_limit: U256, - /// The kind of transaction (e.g., Call, Create) - pub kind: TxKind, - /// The value of the transaction - pub value: U256, - /// The input data for the transaction - pub input: Bytes, - /// The optional chain ID for the transaction - pub chain_id: Option, -} - -/// Represents an EIP-2930 transaction request -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct EIP2930TransactionRequest { - /// The chain ID for the transaction - pub chain_id: u64, - /// The nonce of the transaction - pub nonce: u64, - /// The gas price for the transaction - pub gas_price: U256, - /// The gas limit for the transaction - pub gas_limit: U256, - /// The kind of transaction (e.g., Call, Create) - pub kind: TxKind, - /// The value of the transaction - pub value: U256, - /// The input data for the transaction - pub input: Bytes, - /// The access list for the transaction - pub access_list: AccessList, -} - -/// Represents an EIP-1559 transaction request -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct EIP1559TransactionRequest { - /// The chain ID for the transaction - pub chain_id: u64, - /// The nonce of the transaction - pub nonce: u64, - /// The maximum priority fee per gas for the transaction - pub max_priority_fee_per_gas: U256, - /// The maximum fee per gas for the transaction - pub max_fee_per_gas: U256, - /// The gas limit for the transaction - pub gas_limit: U256, - /// The kind of transaction (e.g., Call, Create) - pub kind: TxKind, - /// The value of the transaction - pub value: U256, - /// The input data for the transaction - pub input: Bytes, - /// The access list for the transaction - pub access_list: AccessList, -} - -/// Represents an EIP-4844 transaction request -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct EIP4844TransactionRequest { - /// The chain ID for the transaction - pub chain_id: u64, - /// The nonce of the transaction - pub nonce: u64, - /// The maximum priority fee per gas for the transaction - pub max_priority_fee_per_gas: U256, - /// The maximum fee per gas for the transaction - pub max_fee_per_gas: U256, - /// The gas limit for the transaction - pub gas_limit: U256, - /// The recipient of the transaction - pub to: Address, - /// The value of the transaction - pub value: U256, - /// The input data for the transaction - pub input: Bytes, - /// The access list for the transaction - pub access_list: AccessList, - /// The maximum fee per blob gas for the transaction - pub max_fee_per_blob_gas: U256, - /// Versioned hashes associated with the transaction - pub blob_versioned_hashes: Vec, - /// Sidecar information for the transaction - pub sidecar: BlobTransactionSidecar, -} diff --git a/crates/rpc/rpc-types/src/lib.rs b/crates/rpc/rpc-types/src/lib.rs index b455c0acd..591969901 100644 --- a/crates/rpc/rpc-types/src/lib.rs +++ b/crates/rpc/rpc-types/src/lib.rs @@ -15,7 +15,6 @@ mod eth; // Ethereum specific rpc types related to typed transaction requests and the engine API. #[cfg(feature = "jsonrpsee-types")] pub use eth::error::ToRpcError; -pub use eth::transaction::{self, TypedTransactionRequest}; #[cfg(feature = "jsonrpsee-types")] pub use eth::{ engine, diff --git a/crates/rpc/rpc/Cargo.toml b/crates/rpc/rpc/Cargo.toml index 8d986b97e..49a1e512e 100644 --- a/crates/rpc/rpc/Cargo.toml +++ b/crates/rpc/rpc/Cargo.toml @@ -17,7 +17,6 @@ reth-chainspec.workspace = true reth-primitives = { workspace = true, features = ["secp256k1"] } reth-rpc-api.workspace = true reth-rpc-eth-api.workspace = true -reth-rpc-types.workspace = true reth-errors.workspace = true reth-provider.workspace = true reth-transaction-pool.workspace = true @@ -37,13 +36,16 @@ reth-network-types.workspace = true reth-trie.workspace = true # ethereum +alloy-consensus.workspace = true +alloy-signer.workspace = true +alloy-signer-local.workspace = true alloy-dyn-abi.workspace = true alloy-genesis.workspace = true alloy-network.workspace = true alloy-primitives.workspace = true alloy-rlp.workspace = true alloy-rpc-types.workspace = true -alloy-rpc-types-eth.workspace = true +alloy-rpc-types-eth = { workspace = true, features = ["jsonrpsee-types"] } alloy-rpc-types-debug.workspace = true alloy-rpc-types-trace.workspace = true alloy-rpc-types-mev.workspace = true @@ -56,7 +58,6 @@ revm = { workspace = true, features = [ "optional_no_base_fee", ] } revm-primitives = { workspace = true, features = ["serde"] } -secp256k1.workspace = true # rpc jsonrpsee.workspace = true diff --git a/crates/rpc/rpc/src/eth/helpers/signer.rs b/crates/rpc/rpc/src/eth/helpers/signer.rs index ec5e35ece..38048b14c 100644 --- a/crates/rpc/rpc/src/eth/helpers/signer.rs +++ b/crates/rpc/rpc/src/eth/helpers/signer.rs @@ -2,16 +2,17 @@ use std::collections::HashMap; +use crate::EthApi; +use alloy_consensus::TxEnvelope; use alloy_dyn_abi::TypedData; +use alloy_network::{eip2718::Encodable2718, EthereumWallet, TransactionBuilder}; use alloy_primitives::{eip191_hash_message, Address, B256}; -use reth_primitives::{sign_message, Signature, TransactionSigned}; +use alloy_rpc_types_eth::TransactionRequest; +use alloy_signer::SignerSync; +use alloy_signer_local::PrivateKeySigner; +use reth_primitives::{Signature, TransactionSigned}; use reth_rpc_eth_api::helpers::{signer::Result, AddDevSigners, EthSigner}; use reth_rpc_eth_types::SignError; -use reth_rpc_types::TypedTransactionRequest; -use reth_rpc_types_compat::transaction::to_primitive_transaction; -use secp256k1::SecretKey; - -use crate::EthApi; impl AddDevSigners for EthApi @@ -25,7 +26,7 @@ impl AddDevSigners #[derive(Debug, Clone)] pub struct DevSigner { addresses: Vec
, - accounts: HashMap, + accounts: HashMap, } #[allow(dead_code)] @@ -41,23 +42,23 @@ impl DevSigner { pub fn random_signers(num: u32) -> Vec> { let mut signers = Vec::new(); for _ in 0..num { - let (sk, pk) = secp256k1::generate_keypair(&mut rand::thread_rng()); + let sk = PrivateKeySigner::random_with(&mut rand::thread_rng()); - let address = reth_primitives::public_key_to_address(pk); + let address = sk.address(); let addresses = vec![address]; + let accounts = HashMap::from([(address, sk)]); signers.push(Box::new(Self { addresses, accounts }) as Box); } signers } - fn get_key(&self, account: Address) -> Result<&SecretKey> { + fn get_key(&self, account: Address) -> Result<&PrivateKeySigner> { self.accounts.get(&account).ok_or(SignError::NoAccount) } fn sign_hash(&self, hash: B256, account: Address) -> Result { - let secret = self.get_key(account)?; - let signature = sign_message(B256::from_slice(secret.as_ref()), hash); + let signature = self.get_key(account)?.sign_hash_sync(&hash); signature.map_err(|_| SignError::CouldNotSign) } } @@ -79,18 +80,25 @@ impl EthSigner for DevSigner { self.sign_hash(hash, address) } - fn sign_transaction( + async fn sign_transaction( &self, - request: TypedTransactionRequest, + request: TransactionRequest, address: &Address, ) -> Result { - // convert to primitive transaction - let transaction = - to_primitive_transaction(request).ok_or(SignError::InvalidTransactionRequest)?; - let tx_signature_hash = transaction.signature_hash(); - let signature = self.sign_hash(tx_signature_hash, *address)?; + // create local signer wallet from signing key + let signer = self.accounts.get(address).ok_or(SignError::NoAccount)?.clone(); + let wallet = EthereumWallet::from(signer); - Ok(TransactionSigned::from_transaction_and_signature(transaction, signature)) + // build and sign transaction with signer + let txn_envelope: TxEnvelope = + request.build(&wallet).await.map_err(|_| SignError::InvalidTransactionRequest)?; + + // decode transaction into signed transaction type + let encoded = txn_envelope.encoded_2718(); + let txn_signed = TransactionSigned::decode_enveloped(&mut encoded.as_ref()) + .map_err(|_| SignError::InvalidTransactionRequest)?; + + Ok(txn_signed) } fn sign_typed_data(&self, address: Address, payload: &TypedData) -> Result { @@ -101,18 +109,18 @@ impl EthSigner for DevSigner { #[cfg(test)] mod tests { - use std::str::FromStr; - - use alloy_primitives::{Parity, U256}; + use alloy_primitives::{Bytes, Parity, U256}; + use alloy_rpc_types_eth::TransactionInput; + use revm_primitives::TxKind; use super::*; fn build_signer() -> DevSigner { - let addresses = vec![]; - let secret = - SecretKey::from_str("4646464646464646464646464646464646464646464646464646464646464646") - .unwrap(); - let accounts = HashMap::from([(Address::default(), secret)]); + let signer: PrivateKeySigner = + "4646464646464646464646464646464646464646464646464646464646464646".parse().unwrap(); + let address = signer.address(); + let accounts = HashMap::from([(address, signer)]); + let addresses = vec![address]; DevSigner { addresses, accounts } } @@ -184,7 +192,8 @@ mod tests { }"#; let data: TypedData = serde_json::from_str(eip_712_example).unwrap(); let signer = build_signer(); - let sig = signer.sign_typed_data(Address::default(), &data).unwrap(); + let from = *signer.addresses.first().unwrap(); + let sig = signer.sign_typed_data(from, &data).unwrap(); let expected = Signature::new( U256::from_str_radix( "5318aee9942b84885761bb20e768372b76e7ee454fc4d39b59ce07338d15a06c", @@ -205,7 +214,8 @@ mod tests { async fn test_signer() { let message = b"Test message"; let signer = build_signer(); - let sig = signer.sign(Address::default(), message).await.unwrap(); + let from = *signer.addresses.first().unwrap(); + let sig = signer.sign(from, message).await.unwrap(); let expected = Signature::new( U256::from_str_radix( "54313da7432e4058b8d22491b2e7dbb19c7186c35c24155bec0820a8a2bfe0c1", @@ -221,4 +231,29 @@ mod tests { ); assert_eq!(sig, expected) } + + #[tokio::test] + async fn test_sign_transaction() { + let message = b"Test message"; + let signer = build_signer(); + let from = *signer.addresses.first().unwrap(); + let request = TransactionRequest { + chain_id: Some(1u64), + from: Some(from), + to: Some(TxKind::Create), + gas: Some(1000u128), + gas_price: Some(1000u128), + value: Some(U256::from(1000)), + input: TransactionInput { + data: Some(Bytes::from(message.to_vec())), + input: Some(Bytes::from(message.to_vec())), + }, + nonce: Some(0u64), + ..Default::default() + }; + let txn_signed = signer.sign_transaction(request, &from).await; + assert!(txn_signed.is_ok()); + + assert_eq!(Bytes::from(message.to_vec()), txn_signed.unwrap().input().0); + } }