mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat(rpc): Replace TypedTransactionType (#11089)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
11
Cargo.lock
generated
11
Cargo.lock
generated
@ -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]]
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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<T> = result::Result<T, SignError>;
|
||||
@ -27,9 +26,9 @@ pub trait EthSigner: Send + Sync + DynClone {
|
||||
async fn sign(&self, address: Address, message: &[u8]) -> Result<Signature>;
|
||||
|
||||
/// 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<TransactionSigned>;
|
||||
|
||||
|
||||
@ -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::<u64>());
|
||||
|
||||
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::<u128>());
|
||||
|
||||
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<TransactionSigned, Self::Error> {
|
||||
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<Output = Result<TransactionSigned, Self::Error>> + 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.
|
||||
|
||||
@ -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::{
|
||||
|
||||
@ -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<reth_primitives::Transaction> {
|
||||
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,
|
||||
}),
|
||||
})
|
||||
}
|
||||
@ -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"]
|
||||
]
|
||||
@ -1,7 +1,6 @@
|
||||
//! Ethereum related types
|
||||
|
||||
pub(crate) mod error;
|
||||
pub mod transaction;
|
||||
|
||||
// re-export
|
||||
#[cfg(feature = "jsonrpsee-types")]
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
//! RPC types for transactions
|
||||
mod typed;
|
||||
pub use typed::*;
|
||||
@ -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<u64>,
|
||||
}
|
||||
|
||||
/// 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<B256>,
|
||||
/// Sidecar information for the transaction
|
||||
pub sidecar: BlobTransactionSidecar,
|
||||
}
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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<Provider, Pool, Network, EvmConfig> AddDevSigners
|
||||
for EthApi<Provider, Pool, Network, EvmConfig>
|
||||
@ -25,7 +26,7 @@ impl<Provider, Pool, Network, EvmConfig> AddDevSigners
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DevSigner {
|
||||
addresses: Vec<Address>,
|
||||
accounts: HashMap<Address, SecretKey>,
|
||||
accounts: HashMap<Address, PrivateKeySigner>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@ -41,23 +42,23 @@ impl DevSigner {
|
||||
pub fn random_signers(num: u32) -> Vec<Box<dyn EthSigner + 'static>> {
|
||||
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<dyn EthSigner>);
|
||||
}
|
||||
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<Signature> {
|
||||
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<TransactionSigned> {
|
||||
// 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<Signature> {
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user