Skip to content

Commit

Permalink
Eth RPC updates
Browse files Browse the repository at this point in the history
  • Loading branch information
pgherveou committed Jan 14, 2025
1 parent f016602 commit 946db27
Show file tree
Hide file tree
Showing 15 changed files with 286 additions and 38 deletions.

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

3 changes: 2 additions & 1 deletion substrate/frame/revive/rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ required-features = ["example"]
anyhow = { workspace = true }
clap = { workspace = true, features = ["derive"] }
codec = { workspace = true, features = ["derive"] }
ethabi = { version = "18.0.0" }
futures = { workspace = true, features = ["thread-pool"] }
hex = { workspace = true }
jsonrpsee = { workspace = true, features = ["full"] }
Expand Down Expand Up @@ -78,8 +77,10 @@ tokio = { workspace = true, features = ["full"] }
example = ["rlp", "subxt-signer"]

[dev-dependencies]
ethabi = { version = "18.0.0" }
env_logger = { workspace = true }
pallet-revive-fixtures = { workspace = true, default-features = true }
static_init = { workspace = true }
substrate-cli-test-utils = { workspace = true }
subxt-signer = { workspace = true, features = ["unstable-eth"] }

Binary file modified substrate/frame/revive/rpc/revive_chain.metadata
Binary file not shown.
8 changes: 8 additions & 0 deletions substrate/frame/revive/rpc/src/apis.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
mod debug_apis;
pub use debug_apis::*;

mod execution_apis;
pub use execution_apis::*;

mod health_api;
pub use health_api::*;
66 changes: 66 additions & 0 deletions substrate/frame/revive/rpc/src/apis/debug_apis.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use crate::*;
use jsonrpsee::{core::RpcResult, proc_macros::rpc};

/// Debug Ethererum JSON-RPC apis.
#[rpc(server, client)]
pub trait DebugRpc {
/// Returns the tracing of the execution of a specific block using its number.
///
/// ## References
///
/// - https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug#debugtraceblockbynumber
/// - https://docs.alchemy.com/reference/what-is-trace_block
/// - https://docs.chainstack.com/reference/ethereum-traceblockbynumber
#[method(name = "debug_traceBlockByNumber")]
async fn trace_block_by_number(
&self,
block: Option<BlockNumberOrTag>,
tracer_config: TracerConfig,
) -> RpcResult<Vec<TransactionTrace>>;

/// Returns a transaction's traces by replaying it. This method provides a detailed
/// breakdown of every step in the execution of a transaction
///
/// ## References
///
/// - https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug#debugtracetransaction
/// - https://docs.alchemy.com/reference/debug-tracetransaction
/// - https://docs.chainstack.com/reference/ethereum-tracetransaction
#[method(name = "debug_traceTransaction")]
async fn trace_transaction(
&self,
transaction_hash: H256,
tracer_config: TracerConfig,
) -> RpcResult<CallTrace<U256, Bytes>>;
}

pub struct DebugRpcServerImpl {
client: client::Client,
}

impl DebugRpcServerImpl {
pub fn new(client: client::Client) -> Self {
Self { client }
}
}

#[async_trait]
impl DebugRpcServer for DebugRpcServerImpl {
async fn trace_block_by_number(
&self,
block: Option<BlockNumberOrTag>,
tracer_config: TracerConfig,
) -> RpcResult<Vec<TransactionTrace>> {
let traces = self.client.trace_block_by_number(block, tracer_config).await?;
Ok(traces)
}

async fn trace_transaction(
&self,
transaction_hash: H256,
tracer_config: TracerConfig,
) -> RpcResult<CallTrace<U256, Bytes>> {
let traces = self.client.trace_transaction(transaction_hash, tracer_config).await?;
Ok(traces)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
//! Generated JSON-RPC methods.
#![allow(missing_docs)]

use super::*;
use crate::*;
use jsonrpsee::{core::RpcResult, proc_macros::rpc};

#[rpc(server, client)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
// limitations under the License.
//! Heatlh JSON-RPC methods.
use super::*;
use crate::*;
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use sc_rpc_api::system::helpers::Health;

Expand Down
8 changes: 5 additions & 3 deletions substrate/frame/revive/rpc/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
use crate::{
client::{connect, Client},
BlockInfoProvider, BlockInfoProviderImpl, CacheReceiptProvider, DBReceiptProvider,
EthRpcServer, EthRpcServerImpl, ReceiptProvider, SystemHealthRpcServer,
SystemHealthRpcServerImpl,
DebugRpcServer, DebugRpcServerImpl, EthRpcServer, EthRpcServerImpl, ReceiptProvider,
SystemHealthRpcServer, SystemHealthRpcServerImpl,
};
use clap::Parser;
use futures::{pin_mut, FutureExt};
Expand Down Expand Up @@ -203,10 +203,12 @@ fn rpc_module(is_dev: bool, client: Client) -> Result<RpcModule<()>, sc_service:
.with_accounts(if is_dev { vec![crate::Account::default()] } else { vec![] })
.into_rpc();

let health_api = SystemHealthRpcServerImpl::new(client).into_rpc();
let health_api = SystemHealthRpcServerImpl::new(client.clone()).into_rpc();
let debug_api = DebugRpcServerImpl::new(client).into_rpc();

let mut module = RpcModule::new(());
module.merge(eth_api).map_err(|e| sc_service::Error::Application(e.into()))?;
module.merge(health_api).map_err(|e| sc_service::Error::Application(e.into()))?;
module.merge(debug_api).map_err(|e| sc_service::Error::Application(e.into()))?;
Ok(module)
}
72 changes: 67 additions & 5 deletions substrate/frame/revive/rpc/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ use crate::{
use jsonrpsee::types::{error::CALL_EXECUTION_FAILED_CODE, ErrorObjectOwned};
use pallet_revive::{
evm::{
extract_revert_message, Block, BlockNumberOrTag, BlockNumberOrTagOrHash,
GenericTransaction, ReceiptInfo, SyncingProgress, SyncingStatus, TransactionSigned, H160,
H256, U256,
extract_revert_message, Block, BlockNumberOrTag, BlockNumberOrTagOrHash, Bytes, CallTrace,
GenericTransaction, ReceiptInfo, SyncingProgress, SyncingStatus, TracerConfig,
TransactionSigned, TransactionTrace, H160, H256, U256,
},
EthTransactError, EthTransactInfo,
};
Expand Down Expand Up @@ -467,7 +467,7 @@ impl Client {
pub async fn receipt_by_hash_and_index(
&self,
block_hash: &H256,
transaction_index: &U256,
transaction_index: u32,
) -> Option<ReceiptInfo> {
self.receipt_provider
.receipt_by_block_hash_and_index(block_hash, transaction_index)
Expand All @@ -479,7 +479,7 @@ impl Client {
}

/// Get receipts count per block.
pub async fn receipts_count_per_block(&self, block_hash: &SubstrateBlockHash) -> Option<usize> {
pub async fn receipts_count_per_block(&self, block_hash: &SubstrateBlockHash) -> Option<u32> {
self.receipt_provider.receipts_count_per_block(block_hash).await
}

Expand Down Expand Up @@ -627,6 +627,68 @@ impl Client {
self.block_provider.block_by_number(block_number).await
}

/// Get the transaction traces for the given block.
pub async fn trace_block_by_number(
&self,
block: Option<BlockNumberOrTag>,
tracer_config: TracerConfig,
) -> Result<Vec<TransactionTrace>, ClientError> {
let hash = match block {
Some(BlockNumberOrTag::U256(n)) => {
let block_number: SubstrateBlockNumber =
n.try_into().map_err(|_| ClientError::ConversionFailed)?;
self.get_block_hash(block_number).await?
},
Some(BlockNumberOrTag::BlockTag(_)) | None =>
self.latest_block().await.map(|b| b.hash()),
};
let hash = hash.ok_or(ClientError::BlockNotFound)?;

let rpc_client = RpcClient::new(self.rpc_client.clone());
let params = subxt::rpc_params!["ReviveApi_trace_tx", hash, tracer_config];
let traces: Vec<(u32, pallet_revive::evm::EthTraces)> =
rpc_client.request("state_debugBlock", params).await?;

let mut hashes = self
.receipt_provider
.block_transaction_hashes(&hash)
.await
.ok_or(ClientError::EthExtrinsicNotFound)?;

let traces = traces
.into_iter()
.filter_map(|(index, traces)| {
Some(TransactionTrace { tx_hash: hashes.remove(&index)?, result: traces })
})
.collect();

Ok(traces)
}

/// Get the transaction traces for the given transaction.
pub async fn trace_transaction(
&self,
transaction_hash: H256,
tracer_config: TracerConfig,
) -> Result<CallTrace<U256, Bytes>, ClientError> {
let rpc_client = RpcClient::new(self.rpc_client.clone());

let receipt = self
.receipt_provider
.receipt_by_hash(&transaction_hash)
.await
.ok_or(ClientError::EthExtrinsicNotFound)?;

let params = subxt::rpc_params![
"ReviveApi_trace_tx",
tracer_config,
receipt.block_hash,
receipt.transaction_index
];
let traces = rpc_client.request("state_debugBlock", params).await?;
Ok(traces)
}

/// Get the EVM block for the given hash.
pub async fn evm_block(
&self,
Expand Down
12 changes: 6 additions & 6 deletions substrate/frame/revive/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub mod cli;
pub mod client;
pub mod example;
pub mod subxt_client;
pub mod tracing;

#[cfg(test)]
mod tests;
Expand All @@ -42,11 +43,8 @@ pub use block_info_provider::*;
mod receipt_provider;
pub use receipt_provider::*;

mod rpc_health;
pub use rpc_health::*;

mod rpc_methods_gen;
pub use rpc_methods_gen::*;
mod apis;
pub use apis::*;

pub const LOG_TARGET: &str = "eth-rpc";

Expand Down Expand Up @@ -299,8 +297,10 @@ impl EthRpcServer for EthRpcServerImpl {
block_hash: H256,
transaction_index: U256,
) -> RpcResult<Option<TransactionInfo>> {
let transaction_index: u32 =
transaction_index.try_into().map_err(|_| EthRpcError::ConversionError)?;
let Some(receipt) =
self.client.receipt_by_hash_and_index(&block_hash, &transaction_index).await
self.client.receipt_by_hash_and_index(&block_hash, transaction_index).await
else {
return Ok(None);
};
Expand Down
23 changes: 17 additions & 6 deletions substrate/frame/revive/rpc/src/receipt_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use pallet_revive::{
evm::{GenericTransaction, Log, ReceiptInfo, TransactionSigned, H256, U256},
};
use sp_core::keccak_256;
use std::collections::HashMap;
use tokio::join;

mod cache;
Expand All @@ -49,15 +50,18 @@ pub trait ReceiptProvider: Send + Sync {
/// Remove receipts with the given block hash.
async fn remove(&self, block_hash: &H256);

/// Return all transaction hashes for the given block hash.
async fn block_transaction_hashes(&self, block_hash: &H256) -> Option<HashMap<u32, H256>>;

/// Get the receipt for the given block hash and transaction index.
async fn receipt_by_block_hash_and_index(
&self,
block_hash: &H256,
transaction_index: &U256,
transaction_index: u32,
) -> Option<ReceiptInfo>;

/// Get the number of receipts per block.
async fn receipts_count_per_block(&self, block_hash: &H256) -> Option<usize>;
async fn receipts_count_per_block(&self, block_hash: &H256) -> Option<u32>;

/// Get the receipt for the given transaction hash.
async fn receipt_by_hash(&self, transaction_hash: &H256) -> Option<ReceiptInfo>;
Expand All @@ -79,7 +83,7 @@ impl<Main: ReceiptProvider, Fallback: ReceiptProvider> ReceiptProvider for (Main
async fn receipt_by_block_hash_and_index(
&self,
block_hash: &H256,
transaction_index: &U256,
transaction_index: u32,
) -> Option<ReceiptInfo> {
if let Some(receipt) =
self.0.receipt_by_block_hash_and_index(block_hash, transaction_index).await
Expand All @@ -90,13 +94,20 @@ impl<Main: ReceiptProvider, Fallback: ReceiptProvider> ReceiptProvider for (Main
self.1.receipt_by_block_hash_and_index(block_hash, transaction_index).await
}

async fn receipts_count_per_block(&self, block_hash: &H256) -> Option<usize> {
async fn receipts_count_per_block(&self, block_hash: &H256) -> Option<u32> {
if let Some(count) = self.0.receipts_count_per_block(block_hash).await {
return Some(count);
}
self.1.receipts_count_per_block(block_hash).await
}

async fn block_transaction_hashes(&self, block_hash: &H256) -> Option<HashMap<u32, H256>> {
if let Some(hashes) = self.0.block_transaction_hashes(block_hash).await {
return Some(hashes);
}
self.1.block_transaction_hashes(block_hash).await
}

async fn receipt_by_hash(&self, hash: &H256) -> Option<ReceiptInfo> {
if let Some(receipt) = self.0.receipt_by_hash(hash).await {
return Some(receipt);
Expand Down Expand Up @@ -225,12 +236,12 @@ pub async fn extract_receipts_from_block(
/// Extract receipt from transaction
pub async fn extract_receipts_from_transaction(
block: &SubstrateBlock,
transaction_index: usize,
transaction_index: u32,
) -> Result<(TransactionSigned, ReceiptInfo), ClientError> {
let extrinsics = block.extrinsics().await?;
let ext = extrinsics
.iter()
.nth(transaction_index)
.nth(transaction_index as usize)
.ok_or(ClientError::EthExtrinsicNotFound)?;

let call = ext
Expand Down
Loading

0 comments on commit 946db27

Please sign in to comment.