Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = ["crates/codecs", "crates/codecs-derive", "crates/primitives-traits", "crates/zstd-compressors"]
members = ["crates/codecs", "crates/codecs-derive", "crates/primitives-traits", "crates/rpc-traits", "crates/zstd-compressors"]
resolver = "3"

[workspace.package]
Expand All @@ -15,6 +15,7 @@ repository = "https://github.com/paradigmxyz/reth-core"
reth-codecs = { version = "0.1.0", path = "crates/codecs", default-features = false }
reth-codecs-derive = { version = "0.1.0", path = "crates/codecs-derive" }
reth-primitives-traits = { version = "0.1.0", path = "crates/primitives-traits", default-features = false }
reth-rpc-traits = { version = "0.1.0", path = "crates/rpc-traits", default-features = false }
reth-zstd-compressors = { version = "0.1.0", path = "crates/zstd-compressors", default-features = false }

# eth/alloy
Expand All @@ -24,6 +25,8 @@ alloy-rpc-types-eth = { version = "1.7.3", default-features = false }
alloy-trie = { version = "0.9.4", default-features = false }
alloy-consensus = { version = "1.7.3", default-features = false }
alloy-eips = { version = "1.7.3", default-features = false }
alloy-network = { version = "1.8.2", default-features = false }
alloy-signer = { version = "1.8.2", default-features = false }
alloy-genesis = { version = "1.7.3", default-features = false }
revm-primitives = { version = "22.1.0", default-features = false }
revm-bytecode = { version = "9.0.0", default-features = false }
Expand Down
36 changes: 36 additions & 0 deletions crates/rpc-traits/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[package]
name = "reth-rpc-traits"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
description = "RPC conversion traits for Ethereum types."

[lints]
workspace = true

[dependencies]
# reth
reth-primitives-traits = { workspace = true, default-features = false }

# ethereum
alloy-primitives.workspace = true
alloy-consensus.workspace = true
alloy-rpc-types-eth.workspace = true
alloy-network.workspace = true
alloy-signer.workspace = true

# error
thiserror.workspace = true

[features]
default = ["std"]
std = [
"alloy-primitives/std",
"alloy-consensus/std",
"alloy-rpc-types-eth/std",
"reth-primitives-traits/std",
"thiserror/std",
]
15 changes: 15 additions & 0 deletions crates/rpc-traits/src/header.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use alloy_consensus::Sealable;
use alloy_primitives::U256;
use reth_primitives_traits::SealedHeader;

/// Conversion trait for obtaining RPC header from a consensus header.
pub trait FromConsensusHeader<T> {
/// Takes a consensus header and converts it into `self`.
fn from_consensus_header(header: SealedHeader<T>, block_size: usize) -> Self;
}

impl<T: Sealable> FromConsensusHeader<T> for alloy_rpc_types_eth::Header<T> {
fn from_consensus_header(header: SealedHeader<T>, block_size: usize) -> Self {
Self::from_consensus(header.into(), None, Some(U256::from(block_size)))
}
}
21 changes: 21 additions & 0 deletions crates/rpc-traits/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//! RPC conversion traits for Ethereum types.
//!
//! This crate provides traits for converting between consensus-layer types and RPC response types.

#![doc(
html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
)]
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(not(feature = "std"), no_std)]

mod signable;
pub use signable::{SignTxRequestError, SignableTxRequest};

mod header;
pub use header::FromConsensusHeader;

mod transaction;
pub use transaction::{FromConsensusTx, TryIntoSimTx, TxInfoMapper};
39 changes: 39 additions & 0 deletions crates/rpc-traits/src/signable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use core::{fmt::Debug, future::Future};

use alloy_consensus::{EthereumTxEnvelope, SignableTransaction, TxEip4844};
use alloy_network::TxSigner;
use alloy_primitives::Signature;
use alloy_rpc_types_eth::TransactionRequest;

/// Error for [`SignableTxRequest`] trait.
#[derive(Debug, thiserror::Error)]
pub enum SignTxRequestError {
/// The transaction request is invalid.
#[error("invalid transaction request")]
InvalidTransactionRequest,

/// The signer is not supported.
#[error(transparent)]
SignerNotSupported(#[from] alloy_signer::Error),
}

/// An abstraction over transaction requests that can be signed.
pub trait SignableTxRequest<T>: Send + Sync + 'static {
/// Attempts to build a transaction request and sign it with the given signer.
fn try_build_and_sign(
self,
signer: impl TxSigner<Signature> + Send,
) -> impl Future<Output = Result<T, SignTxRequestError>> + Send;
}

impl SignableTxRequest<EthereumTxEnvelope<TxEip4844>> for TransactionRequest {
async fn try_build_and_sign(
self,
signer: impl TxSigner<Signature> + Send,
) -> Result<EthereumTxEnvelope<TxEip4844>, SignTxRequestError> {
let mut tx =
self.build_typed_tx().map_err(|_| SignTxRequestError::InvalidTransactionRequest)?;
let signature = signer.sign_transaction(&mut tx).await?;
Ok(tx.into_signed(signature).into())
}
}
78 changes: 78 additions & 0 deletions crates/rpc-traits/src/transaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use alloy_consensus::{error::ValueError, transaction::Recovered, EthereumTxEnvelope, TxEip4844};
use alloy_primitives::Address;
use alloy_rpc_types_eth::{Transaction, TransactionInfo, TransactionRequest};
use core::{convert::Infallible, error};

/// Converts `T` into `self`.
///
/// Should create an RPC transaction response object based on a consensus transaction, its signer
/// [`Address`] and an additional context [`FromConsensusTx::TxInfo`].
pub trait FromConsensusTx<T>: Sized {
/// An additional context, usually [`TransactionInfo`] in a wrapper that carries some
/// implementation specific extra information.
type TxInfo;
/// An associated RPC conversion error.
type Err: error::Error;

/// Performs the conversion consuming `tx` with `signer` and `tx_info`. See [`FromConsensusTx`]
/// for details.
fn from_consensus_tx(tx: T, signer: Address, tx_info: Self::TxInfo) -> Result<Self, Self::Err>;
}

impl<TxIn: alloy_consensus::Transaction, T: alloy_consensus::Transaction + From<TxIn>>
FromConsensusTx<TxIn> for Transaction<T>
{
type TxInfo = TransactionInfo;
type Err = Infallible;

fn from_consensus_tx(
tx: TxIn,
signer: Address,
tx_info: Self::TxInfo,
) -> Result<Self, Self::Err> {
Ok(Self::from_transaction(Recovered::new_unchecked(tx.into(), signer), tx_info))
}
}

/// Converts `self` into `T`.
///
/// Should create a fake transaction for simulation using [`TransactionRequest`].
pub trait TryIntoSimTx<T>
where
Self: Sized,
{
/// Performs the conversion.
///
/// Should return a signed typed transaction envelope for the [`eth_simulateV1`] endpoint with a
/// dummy signature or an error if [required fields] are missing.
///
/// [`eth_simulateV1`]: <https://github.com/ethereum/execution-apis/pull/484>
/// [required fields]: TransactionRequest::buildable_type
fn try_into_sim_tx(self) -> Result<T, ValueError<Self>>;
}

impl TryIntoSimTx<EthereumTxEnvelope<TxEip4844>> for TransactionRequest {
fn try_into_sim_tx(self) -> Result<EthereumTxEnvelope<TxEip4844>, ValueError<Self>> {
Self::build_typed_simulate_transaction(self)
}
}

/// Adds extra context to [`TransactionInfo`].
pub trait TxInfoMapper<T> {
/// An associated output type that carries [`TransactionInfo`] with some extra context.
type Out;
/// An associated error that can occur during the mapping.
type Err;

/// Performs the conversion.
fn try_map(&self, tx: &T, tx_info: TransactionInfo) -> Result<Self::Out, Self::Err>;
}

impl<T> TxInfoMapper<T> for () {
type Out = TransactionInfo;
type Err = Infallible;

fn try_map(&self, _tx: &T, tx_info: TransactionInfo) -> Result<Self::Out, Self::Err> {
Ok(tx_info)
}
}