Skip to content

Commit

Permalink
feat: add transaction_receipt (#1919)
Browse files Browse the repository at this point in the history
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
  • Loading branch information
chirag-bgh and mattsse authored Mar 23, 2023
1 parent b443917 commit aaa99f6
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 8 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion crates/rpc/rpc-builder/tests/it/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
Expand Down
18 changes: 18 additions & 0 deletions crates/rpc/rpc-types/src/eth/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down
5 changes: 3 additions & 2 deletions crates/rpc/rpc-types/src/eth/transaction/receipt.rs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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")]
Expand Down
1 change: 1 addition & 0 deletions crates/rpc/rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions crates/rpc/rpc/src/eth/api/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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`
Expand Down
102 changes: 99 additions & 3 deletions crates/rpc/rpc/src/eth/api/transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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 ===
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit aaa99f6

Please sign in to comment.