feat(rpc): add eth transactions trait (#1751)

This commit is contained in:
Matthias Seitz
2023-03-14 13:55:35 +01:00
committed by GitHub
parent 6c12ccb6e1
commit a0ff24b691
6 changed files with 107 additions and 51 deletions

View File

@@ -87,9 +87,9 @@ impl Transaction {
tx
}
/// Create a new rpc transaction result for a pending signed transaction, setting block
/// Create a new rpc transaction result for a _pending_ signed transaction, setting block
/// environment related fields to `None`.
pub(crate) fn from_recovered(tx: TransactionSignedEcRecovered) -> Self {
pub fn from_recovered(tx: TransactionSignedEcRecovered) -> Self {
let signer = tx.signer();
let signed_tx = tx.into_signed();

View File

@@ -3,29 +3,25 @@
//! The entire implementation of the namespace is quite large, hence it is divided across several
//! files.
use crate::eth::signer::EthSigner;
use crate::eth::{cache::EthStateCache, error::EthResult, signer::EthSigner};
use async_trait::async_trait;
use reth_interfaces::Result;
use reth_network_api::NetworkInfo;
use reth_primitives::{
Address, BlockId, BlockNumberOrTag, ChainInfo, TransactionSigned, H256, U64,
};
use reth_primitives::{Address, BlockId, BlockNumberOrTag, ChainInfo, H256, U64};
use reth_provider::{
BlockProvider, EvmEnvProvider, StateProvider as StateProviderTrait, StateProviderFactory,
providers::ChainState, BlockProvider, EvmEnvProvider, StateProvider as StateProviderTrait,
StateProviderFactory,
};
use std::{num::NonZeroUsize, ops::Deref};
use crate::eth::{cache::EthStateCache, error::EthResult};
use reth_provider::providers::ChainState;
use reth_rpc_types::FeeHistoryCache;
use reth_transaction_pool::TransactionPool;
use std::sync::Arc;
use std::{num::NonZeroUsize, ops::Deref, sync::Arc};
mod block;
mod call;
mod server;
mod state;
mod transactions;
pub use transactions::{EthTransactions, TransactionSource};
/// Cache limit of block-level fee history for `eth_feeHistory` RPC method.
const FEE_HISTORY_CACHE_LIMIT: usize = 2048;
@@ -34,7 +30,7 @@ const FEE_HISTORY_CACHE_LIMIT: usize = 2048;
///
/// Defines core functionality of the `eth` API implementation.
#[async_trait]
pub trait EthApiSpec: Send + Sync {
pub trait EthApiSpec: EthTransactions + Send + Sync {
/// Returns the current ethereum protocol version.
async fn protocol_version(&self) -> Result<U64>;
@@ -46,9 +42,6 @@ pub trait EthApiSpec: Send + Sync {
/// Returns a list of addresses owned by client.
fn accounts(&self) -> Vec<Address>;
/// Returns the transaction by hash
async fn transaction_by_hash(&self, hash: H256) -> Result<Option<TransactionSigned>>;
}
/// `Eth` API implementation.
@@ -243,10 +236,6 @@ where
fn accounts(&self) -> Vec<Address> {
self.inner.signers.iter().flat_map(|s| s.accounts()).collect()
}
async fn transaction_by_hash(&self, hash: H256) -> Result<Option<TransactionSigned>> {
self.client().transaction_by_hash(hash)
}
}
/// Container type `EthApi`

View File

@@ -3,7 +3,10 @@
use super::EthApiSpec;
use crate::{
eth::{api::EthApi, error::EthApiError},
eth::{
api::{EthApi, EthTransactions},
error::EthApiError,
},
result::{internal_rpc_err, ToRpcResult},
};
use jsonrpsee::core::RpcResult as Result;
@@ -118,7 +121,7 @@ where
/// Handler for: `eth_getTransactionByHash`
async fn transaction_by_hash(&self, hash: H256) -> Result<Option<reth_rpc_types::Transaction>> {
Ok(EthApi::transaction_by_hash(self, hash).await?)
Ok(EthTransactions::transaction_by_hash(self, hash).await?.map(Into::into))
}
/// Handler for: `eth_getTransactionByBlockHashAndIndex`

View File

@@ -3,12 +3,62 @@ use crate::{
eth::error::{EthApiError, EthResult},
EthApi,
};
use reth_primitives::{BlockId, Bytes, FromRecoveredTransaction, TransactionSigned, H256};
use reth_provider::{BlockProvider, EvmEnvProvider, StateProviderFactory};
use async_trait::async_trait;
use reth_primitives::{
BlockId, Bytes, FromRecoveredTransaction, IntoRecoveredTransaction, TransactionSigned,
TransactionSignedEcRecovered, H256, U256,
};
use reth_provider::{BlockProvider, EvmEnvProvider, StateProviderFactory, TransactionsProvider};
use reth_rlp::Decodable;
use reth_rpc_types::{Index, Transaction, TransactionRequest};
use reth_transaction_pool::{TransactionOrigin, TransactionPool};
/// Commonly used transaction related functions for the [EthApi] type in the `eth_` namespace
#[async_trait::async_trait]
pub trait EthTransactions: Send + Sync {
/// Returns the transaction by hash.
///
/// Checks the pool and state.
///
/// Returns `Ok(None)` if no matching transaction was found.
async fn transaction_by_hash(&self, hash: H256) -> EthResult<Option<TransactionSource>>;
}
#[async_trait]
impl<Client, Pool, Network> EthTransactions for EthApi<Client, Pool, Network>
where
Pool: TransactionPool + Clone + 'static,
Client: TransactionsProvider + 'static,
Network: Send + Sync + 'static,
{
async fn transaction_by_hash(&self, hash: H256) -> EthResult<Option<TransactionSource>> {
if let Some(tx) = self.pool().get(&hash).map(|tx| tx.transaction.to_recovered_transaction())
{
return Ok(Some(TransactionSource::Pool(tx)))
}
match self.client().transaction_by_hash(hash)? {
None => Ok(None),
Some(tx) => {
let transaction =
tx.into_ecrecovered().ok_or(EthApiError::InvalidTransactionSignature)?;
let tx = TransactionSource::Database {
transaction,
// TODO: this is just stubbed out for now still need to fully implement tx =>
// block
index: 0,
block_hash: Default::default(),
block_number: 0,
};
Ok(Some(tx))
}
}
}
}
// === impl EthApi ===
impl<Client, Pool, Network> EthApi<Client, Pool, Network>
where
Pool: TransactionPool + 'static,
@@ -19,28 +69,6 @@ where
unimplemented!()
}
/// Finds a given [Transaction] by its hash.
///
/// Returns `Ok(None)` if no matching transaction was found.
pub(crate) async fn transaction_by_hash(&self, hash: H256) -> EthResult<Option<Transaction>> {
match self.client().transaction_by_hash(hash)? {
None => Ok(None),
Some(tx) => {
let tx = tx.into_ecrecovered().ok_or(EthApiError::InvalidTransactionSignature)?;
let tx = Transaction::from_recovered_with_block_context(
tx,
// TODO: this is just stubbed out for now still need to fully implement tx =>
// block
H256::default(),
u64::default(),
Index::default().into(),
);
Ok(Some(tx))
}
}
}
/// Get Transaction by [BlockId] and the index of the transaction within that Block.
///
/// Returns `Ok(None)` if the block does not exist, or the block as fewer transactions
@@ -94,6 +122,40 @@ where
}
}
/// Represents from where a transaction was fetched.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum TransactionSource {
/// Transaction exists in the pool (Pending)
Pool(TransactionSignedEcRecovered),
/// Transaction already executed
Database {
/// Transaction fetched via provider
transaction: TransactionSignedEcRecovered,
/// Index of the transaction in the block
index: usize,
/// Hash of the block.
block_hash: H256,
/// Number of the block.
block_number: u64,
},
}
impl From<TransactionSource> for Transaction {
fn from(value: TransactionSource) -> Self {
match value {
TransactionSource::Pool(tx) => Transaction::from_recovered(tx),
TransactionSource::Database { transaction, index, block_hash, block_number } => {
Transaction::from_recovered_with_block_context(
transaction,
block_hash,
block_number,
U256::from(index),
)
}
}
}
}
#[cfg(test)]
mod tests {
use crate::eth::cache::EthStateCache;

View File

@@ -8,12 +8,12 @@ use reth_transaction_pool::error::{InvalidPoolTransactionError, PoolError};
use revm::primitives::{EVMError, Halt};
/// Result alias
pub(crate) type EthResult<T> = Result<T, EthApiError>;
pub type EthResult<T> = Result<T, EthApiError>;
/// Errors that can occur when interacting with the `eth_` namespace
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub(crate) enum EthApiError {
pub enum EthApiError {
/// When a raw transaction is empty
#[error("Empty transaction data")]
EmptyRawTransactionData,
@@ -161,6 +161,7 @@ pub enum InvalidTransactionError {
/// Unspecific evm halt error
#[error("EVM error {0:?}")]
EvmHalt(Halt),
/// Invalid chain id set for the transaction.
#[error("Invalid chain id")]
InvalidChainId,
}
@@ -268,7 +269,8 @@ impl std::error::Error for RevertError {}
/// A helper error type that's mainly used to mirror `geth` Txpool's error messages
#[derive(Debug, thiserror::Error)]
pub(crate) enum RpcPoolError {
#[allow(missing_docs)]
pub enum RpcPoolError {
#[error("already known")]
AlreadyKnown,
#[error("invalid sender")]

View File

@@ -2,14 +2,14 @@
mod api;
pub mod cache;
pub(crate) mod error;
pub mod error;
mod filter;
mod id_provider;
mod pubsub;
pub(crate) mod revm_utils;
mod signer;
pub use api::{EthApi, EthApiSpec};
pub use api::{EthApi, EthApiSpec, EthTransactions, TransactionSource};
pub use filter::EthFilter;
pub use id_provider::EthSubscriptionIdProvider;
pub use pubsub::EthPubSub;