mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-01-29 17:18:08 -05:00
feat: add transaction_receipt (#1919)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -5045,6 +5045,7 @@ dependencies = [
|
||||
"reth-tasks",
|
||||
"reth-transaction-pool",
|
||||
"revm",
|
||||
"revm-primitives",
|
||||
"schnellru",
|
||||
"secp256k1",
|
||||
"serde",
|
||||
|
||||
@@ -98,7 +98,6 @@ where
|
||||
|
||||
// Unimplemented
|
||||
assert!(is_unimplemented(EthApiClient::author(client).await.err().unwrap()));
|
||||
assert!(is_unimplemented(EthApiClient::transaction_receipt(client, hash).await.err().unwrap()));
|
||||
assert!(is_unimplemented(EthApiClient::gas_price(client).await.err().unwrap()));
|
||||
assert!(is_unimplemented(EthApiClient::max_priority_fee_per_gas(client).await.err().unwrap()));
|
||||
assert!(is_unimplemented(EthApiClient::is_mining(client).await.err().unwrap()));
|
||||
|
||||
@@ -28,6 +28,24 @@ pub struct Log {
|
||||
pub removed: bool,
|
||||
}
|
||||
|
||||
impl Log {
|
||||
/// Creates a new rpc Log from a primitive log type from DB
|
||||
pub fn from_primitive(log: reth_primitives::Log) -> Self {
|
||||
Self {
|
||||
address: log.address,
|
||||
topics: log.topics,
|
||||
data: log.data,
|
||||
block_hash: None,
|
||||
block_number: None,
|
||||
transaction_hash: None,
|
||||
transaction_index: None,
|
||||
log_index: None,
|
||||
transaction_log_index: None,
|
||||
removed: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use reth_primitives::{rpc::Log, Address, Bloom, H256, U128, U256, U64};
|
||||
use crate::Log;
|
||||
use reth_primitives::{Address, Bloom, H256, U128, U256, U64};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Transaction receipt
|
||||
@@ -25,7 +26,7 @@ pub struct TransactionReceipt {
|
||||
pub contract_address: Option<Address>,
|
||||
/// Logs emitted by this transaction.
|
||||
pub logs: Vec<Log>,
|
||||
/// The state root
|
||||
/// The post-transaction stateroot (pre Byzantium)
|
||||
///
|
||||
/// EIP98 makes this optional field, if it's missing then skip serializing it
|
||||
#[serde(skip_serializing_if = "Option::is_none", rename = "root")]
|
||||
|
||||
@@ -27,6 +27,7 @@ reth-tasks = { path = "../../tasks" }
|
||||
# eth
|
||||
revm = { version = "3.0.0", features = ["optional_block_gas_limit"] }
|
||||
ethers-core = { git = "https://github.com/gakonst/ethers-rs", features = ["eip712"] }
|
||||
revm-primitives = { version = "1.0", features = ["serde"] }
|
||||
|
||||
|
||||
# rpc
|
||||
|
||||
@@ -21,6 +21,7 @@ use reth_rpc_types::{
|
||||
Work,
|
||||
};
|
||||
use reth_transaction_pool::TransactionPool;
|
||||
|
||||
use serde_json::Value;
|
||||
use std::collections::BTreeMap;
|
||||
use tracing::trace;
|
||||
@@ -160,8 +161,9 @@ where
|
||||
}
|
||||
|
||||
/// Handler for: `eth_getTransactionReceipt`
|
||||
async fn transaction_receipt(&self, _hash: H256) -> Result<Option<TransactionReceipt>> {
|
||||
Err(internal_rpc_err("unimplemented"))
|
||||
async fn transaction_receipt(&self, hash: H256) -> Result<Option<TransactionReceipt>> {
|
||||
trace!(target: "rpc::eth", ?hash, "Serving eth_getTransactionReceipt");
|
||||
Ok(EthTransactions::transaction_receipt(self, hash).await?)
|
||||
}
|
||||
|
||||
/// Handler for: `eth_getBalance`
|
||||
|
||||
@@ -11,14 +11,19 @@ use async_trait::async_trait;
|
||||
use crate::eth::error::SignError;
|
||||
use reth_primitives::{
|
||||
Address, BlockId, BlockNumberOrTag, Bytes, FromRecoveredTransaction, IntoRecoveredTransaction,
|
||||
TransactionSigned, TransactionSignedEcRecovered, H256, U256,
|
||||
Receipt, Transaction as PrimitiveTransaction,
|
||||
TransactionKind::{Call, Create},
|
||||
TransactionMeta, TransactionSigned, TransactionSignedEcRecovered, TxEip1559, TxEip2930,
|
||||
TxLegacy, H256, U128, U256, U64,
|
||||
};
|
||||
use reth_provider::{providers::ChainState, BlockProvider, EvmEnvProvider, StateProviderFactory};
|
||||
use reth_rpc_types::{
|
||||
Index, Transaction, TransactionInfo, TransactionRequest, TypedTransactionRequest,
|
||||
Index, Log, Transaction, TransactionInfo, TransactionReceipt, TransactionRequest,
|
||||
TypedTransactionRequest,
|
||||
};
|
||||
use reth_transaction_pool::{TransactionOrigin, TransactionPool};
|
||||
use revm::primitives::{BlockEnv, CfgEnv};
|
||||
use revm_primitives::utilities::create_address;
|
||||
|
||||
/// Commonly used transaction related functions for the [EthApi] type in the `eth_` namespace
|
||||
#[async_trait::async_trait]
|
||||
@@ -49,6 +54,12 @@ pub trait EthTransactions: Send + Sync {
|
||||
&self,
|
||||
hash: H256,
|
||||
) -> EthResult<Option<(TransactionSource, BlockId)>>;
|
||||
|
||||
/// Returns the transaction receipt for the given hash.
|
||||
///
|
||||
/// Returns None if the transaction does not exist or is pending
|
||||
/// Note: The tx receipt is not available for pending transactions.
|
||||
async fn transaction_receipt(&self, hash: H256) -> EthResult<Option<TransactionReceipt>>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -143,6 +154,20 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn transaction_receipt(&self, hash: H256) -> EthResult<Option<TransactionReceipt>> {
|
||||
let (tx, meta) = match self.client().transaction_by_hash_with_meta(hash)? {
|
||||
Some((tx, meta)) => (tx, meta),
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
let receipt = match self.client().receipt_by_hash(hash)? {
|
||||
Some(recpt) => recpt,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
Self::build_transaction_receipt(tx, meta, receipt).map(Some)
|
||||
}
|
||||
}
|
||||
|
||||
// === impl EthApi ===
|
||||
@@ -240,8 +265,79 @@ where
|
||||
|
||||
Ok(hash)
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function for `eth_getTransactionReceipt`
|
||||
///
|
||||
/// Returns the receipt
|
||||
pub(crate) fn build_transaction_receipt(
|
||||
tx: TransactionSigned,
|
||||
meta: TransactionMeta,
|
||||
mut receipt: Receipt,
|
||||
) -> EthResult<TransactionReceipt> {
|
||||
let transaction =
|
||||
tx.clone().into_ecrecovered().ok_or(EthApiError::InvalidTransactionSignature)?;
|
||||
|
||||
let mut res_receipt = TransactionReceipt {
|
||||
transaction_hash: Some(meta.tx_hash),
|
||||
transaction_index: Some(U256::from(meta.index)),
|
||||
block_hash: Some(meta.block_hash),
|
||||
block_number: Some(U256::from(meta.block_number)),
|
||||
from: transaction.signer(),
|
||||
to: None,
|
||||
cumulative_gas_used: U256::from(receipt.cumulative_gas_used),
|
||||
gas_used: Some(U256::from(0)),
|
||||
contract_address: None,
|
||||
logs: std::mem::take(&mut receipt.logs).into_iter().map(Log::from_primitive).collect(),
|
||||
effective_gas_price: U128::from(0),
|
||||
transaction_type: U256::from(0),
|
||||
// TODO: set state root after the block
|
||||
state_root: None,
|
||||
logs_bloom: receipt.bloom_slow(),
|
||||
status_code: if receipt.success { Some(U64::from(1)) } else { Some(U64::from(0)) },
|
||||
};
|
||||
|
||||
match tx.transaction.kind() {
|
||||
Create => {
|
||||
// set contract address if creation was successful
|
||||
if receipt.success {
|
||||
res_receipt.contract_address =
|
||||
Some(create_address(transaction.signer(), tx.transaction.nonce()));
|
||||
}
|
||||
}
|
||||
Call(addr) => {
|
||||
res_receipt.to = Some(*addr);
|
||||
}
|
||||
}
|
||||
|
||||
match tx.transaction {
|
||||
PrimitiveTransaction::Legacy(TxLegacy { gas_limit, gas_price, .. }) => {
|
||||
// TODO: set actual gas used
|
||||
res_receipt.gas_used = Some(U256::from(gas_limit));
|
||||
res_receipt.transaction_type = U256::from(0);
|
||||
res_receipt.effective_gas_price = U128::from(gas_price);
|
||||
}
|
||||
PrimitiveTransaction::Eip2930(TxEip2930 { gas_limit, gas_price, .. }) => {
|
||||
// TODO: set actual gas used
|
||||
res_receipt.gas_used = Some(U256::from(gas_limit));
|
||||
res_receipt.transaction_type = U256::from(1);
|
||||
res_receipt.effective_gas_price = U128::from(gas_price);
|
||||
}
|
||||
PrimitiveTransaction::Eip1559(TxEip1559 {
|
||||
gas_limit,
|
||||
max_fee_per_gas,
|
||||
max_priority_fee_per_gas,
|
||||
..
|
||||
}) => {
|
||||
// TODO: set actual gas used
|
||||
res_receipt.gas_used = Some(U256::from(gas_limit));
|
||||
res_receipt.transaction_type = U256::from(2);
|
||||
res_receipt.effective_gas_price =
|
||||
U128::from(max_fee_per_gas + max_priority_fee_per_gas)
|
||||
}
|
||||
}
|
||||
Ok(res_receipt)
|
||||
}
|
||||
}
|
||||
/// Represents from where a transaction was fetched.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum TransactionSource {
|
||||
|
||||
Reference in New Issue
Block a user