diff --git a/Cargo.lock b/Cargo.lock
index 8d8515b47e..3fe930f9ba 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1024,9 +1024,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
-version = "1.4.0"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
+checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "bzip2-sys"
diff --git a/client/rpc-core/src/debug.rs b/client/rpc-core/src/debug.rs
new file mode 100644
index 0000000000..2dd02d5a03
--- /dev/null
+++ b/client/rpc-core/src/debug.rs
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+//! Debug rpc interface.
+
+use ethereum_types::H256;
+use jsonrpsee::{core::RpcResult, proc_macros::rpc};
+
+use crate::types::{BlockNumberOrHash, Bytes};
+
+/// Net rpc interface.
+#[rpc(server)]
+#[async_trait]
+pub trait DebugApi {
+ /// Returns an RLP-encoded header with the given number or hash.
+ #[method(name = "debug_getRawHeader")]
+ async fn raw_header(&self, number: BlockNumberOrHash) -> RpcResult>;
+
+ /// Returns an RLP-encoded block with the given number or hash.
+ #[method(name = "debug_getRawBlock")]
+ async fn raw_block(&self, number: BlockNumberOrHash) -> RpcResult >;
+
+ /// Returns a EIP-2718 binary-encoded transaction with the given hash.
+ #[method(name = "debug_getRawTransaction")]
+ async fn raw_transaction(&self, hash: H256) -> RpcResult >;
+
+ /// Returns an array of EIP-2718 binary-encoded receipts with the given number of hash.
+ #[method(name = "debug_getRawReceipts")]
+ async fn raw_receipts(&self, number: BlockNumberOrHash) -> RpcResult>;
+
+ /// Returns an array of recent bad blocks that the client has seen on the network.
+ #[method(name = "debug_getBadBlocks")]
+ fn bad_blocks(&self, number: BlockNumberOrHash) -> RpcResult>;
+}
diff --git a/client/rpc-core/src/lib.rs b/client/rpc-core/src/lib.rs
index 7f29588ba8..d1b927c605 100644
--- a/client/rpc-core/src/lib.rs
+++ b/client/rpc-core/src/lib.rs
@@ -20,6 +20,7 @@
pub mod types;
+mod debug;
mod eth;
mod eth_pubsub;
mod net;
@@ -30,6 +31,7 @@ mod web3;
#[cfg(feature = "txpool")]
pub use self::txpool::TxPoolApiServer;
pub use self::{
+ debug::DebugApiServer,
eth::{EthApiServer, EthFilterApiServer},
eth_pubsub::EthPubSubApiServer,
net::NetApiServer,
diff --git a/client/rpc/src/eth/cache/lru_cache.rs b/client/rpc/src/cache/lru_cache.rs
similarity index 100%
rename from client/rpc/src/eth/cache/lru_cache.rs
rename to client/rpc/src/cache/lru_cache.rs
diff --git a/client/rpc/src/eth/cache/mod.rs b/client/rpc/src/cache/mod.rs
similarity index 100%
rename from client/rpc/src/eth/cache/mod.rs
rename to client/rpc/src/cache/mod.rs
diff --git a/client/rpc/src/debug.rs b/client/rpc/src/debug.rs
new file mode 100644
index 0000000000..ee25661dbf
--- /dev/null
+++ b/client/rpc/src/debug.rs
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+use std::{marker::PhantomData, sync::Arc};
+
+use ethereum::EnvelopedEncodable;
+use ethereum_types::H256;
+use jsonrpsee::core::{async_trait, RpcResult};
+use rlp::Encodable;
+// Substrate
+use sc_client_api::backend::{Backend, StorageProvider};
+use sp_api::ProvideRuntimeApi;
+use sp_blockchain::HeaderBackend;
+use sp_runtime::traits::Block as BlockT;
+// Frontier
+use fc_rpc_core::{types::*, DebugApiServer};
+use fc_storage::OverrideHandle;
+use fp_rpc::EthereumRuntimeRPCApi;
+
+use crate::{cache::EthBlockDataCacheTask, frontier_backend_client, internal_err};
+
+/// Debug API implementation.
+pub struct Debug {
+ client: Arc,
+ backend: Arc>,
+ overrides: Arc>,
+ block_data_cache: Arc>,
+ _marker: PhantomData,
+}
+
+impl Debug {
+ pub fn new(
+ client: Arc,
+ backend: Arc>,
+ overrides: Arc>,
+ block_data_cache: Arc>,
+ ) -> Self {
+ Self {
+ client,
+ backend,
+ overrides,
+ block_data_cache,
+ _marker: PhantomData,
+ }
+ }
+
+ async fn block_by(&self, number: BlockNumberOrHash) -> RpcResult>
+ where
+ C: HeaderBackend + StorageProvider + 'static,
+ BE: Backend,
+ {
+ let id = match frontier_backend_client::native_block_id::(
+ self.client.as_ref(),
+ self.backend.as_ref(),
+ Some(number),
+ )
+ .await?
+ {
+ Some(id) => id,
+ None => return Ok(None),
+ };
+
+ let substrate_hash = self
+ .client
+ .expect_block_hash_from_id(&id)
+ .map_err(|_| internal_err(format!("Expect block number from id: {}", id)))?;
+ let schema = fc_storage::onchain_storage_schema(self.client.as_ref(), substrate_hash);
+ let block = self
+ .block_data_cache
+ .current_block(schema, substrate_hash)
+ .await;
+ Ok(block)
+ }
+
+ async fn transaction_by(
+ &self,
+ transaction_hash: H256,
+ ) -> RpcResult>
+ where
+ C: HeaderBackend + StorageProvider + 'static,
+ BE: Backend,
+ {
+ let (eth_block_hash, index) = match frontier_backend_client::load_transactions::(
+ self.client.as_ref(),
+ self.backend.as_ref(),
+ transaction_hash,
+ true,
+ )
+ .await?
+ {
+ Some((hash, index)) => (hash, index as usize),
+ None => return Ok(None),
+ };
+
+ let substrate_hash = match frontier_backend_client::load_hash::(
+ self.client.as_ref(),
+ self.backend.as_ref(),
+ eth_block_hash,
+ )
+ .await?
+ {
+ Some(hash) => hash,
+ None => return Ok(None),
+ };
+
+ let schema = fc_storage::onchain_storage_schema(self.client.as_ref(), substrate_hash);
+ let block = self
+ .block_data_cache
+ .current_block(schema, substrate_hash)
+ .await;
+ if let Some(block) = block {
+ Ok(Some(block.transactions[index].clone()))
+ } else {
+ Ok(None)
+ }
+ }
+
+ async fn receipts_by(
+ &self,
+ number: BlockNumberOrHash,
+ ) -> RpcResult>>
+ where
+ C: HeaderBackend + StorageProvider + 'static,
+ BE: Backend,
+ {
+ let id = match frontier_backend_client::native_block_id::(
+ self.client.as_ref(),
+ self.backend.as_ref(),
+ Some(number),
+ )
+ .await?
+ {
+ Some(id) => id,
+ None => return Ok(None),
+ };
+
+ let substrate_hash = self
+ .client
+ .expect_block_hash_from_id(&id)
+ .map_err(|_| internal_err(format!("Expect block number from id: {}", id)))?;
+
+ let schema = fc_storage::onchain_storage_schema(self.client.as_ref(), substrate_hash);
+ let handler = self
+ .overrides
+ .schemas
+ .get(&schema)
+ .unwrap_or(&self.overrides.fallback);
+ let receipts = handler.current_receipts(substrate_hash);
+ Ok(receipts)
+ }
+}
+
+#[async_trait]
+impl DebugApiServer for Debug
+where
+ B: BlockT,
+ C: ProvideRuntimeApi,
+ C::Api: EthereumRuntimeRPCApi,
+ C: HeaderBackend + StorageProvider + 'static,
+ BE: Backend + 'static,
+{
+ async fn raw_header(&self, number: BlockNumberOrHash) -> RpcResult> {
+ let block = self.block_by(number).await?;
+ Ok(block.map(|block| Bytes::new(block.header.rlp_bytes().to_vec())))
+ }
+
+ async fn raw_block(&self, number: BlockNumberOrHash) -> RpcResult > {
+ let block = self.block_by(number).await?;
+ Ok(block.map(|block| Bytes::new(block.rlp_bytes().to_vec())))
+ }
+
+ async fn raw_transaction(&self, hash: H256) -> RpcResult > {
+ let transaction = self.transaction_by(hash).await?;
+ Ok(transaction.map(|transaction| Bytes::new(transaction.encode().to_vec())))
+ }
+
+ async fn raw_receipts(&self, number: BlockNumberOrHash) -> RpcResult> {
+ let receipts = self.receipts_by(number).await?.unwrap_or_default();
+ Ok(receipts
+ .into_iter()
+ .map(|receipt| Bytes::new(receipt.encode().to_vec()))
+ .collect::>())
+ }
+
+ fn bad_blocks(&self, _number: BlockNumberOrHash) -> RpcResult> {
+ // `debug_getBadBlocks` wouldn't really be useful in a Substrate context.
+ // The rationale for that is for debugging multi-client consensus issues, which we'll never face
+ // (we may have multiple clients in the future, but for runtime it's only "multi-wasm-runtime", never "multi-EVM").
+ // We can simply return empty array for this API.
+ Ok(vec![])
+ }
+}
diff --git a/client/rpc/src/eth/filter.rs b/client/rpc/src/eth/filter.rs
index b0a34465ec..f6ac476ca6 100644
--- a/client/rpc/src/eth/filter.rs
+++ b/client/rpc/src/eth/filter.rs
@@ -41,7 +41,7 @@ use sp_runtime::{
use fc_rpc_core::{types::*, EthFilterApiServer};
use fp_rpc::{EthereumRuntimeRPCApi, TransactionStatus};
-use crate::{eth::cache::EthBlockDataCacheTask, frontier_backend_client, internal_err};
+use crate::{cache::EthBlockDataCacheTask, frontier_backend_client, internal_err};
pub struct EthFilter {
client: Arc,
diff --git a/client/rpc/src/eth/mod.rs b/client/rpc/src/eth/mod.rs
index 16961fb7dd..d40817d40c 100644
--- a/client/rpc/src/eth/mod.rs
+++ b/client/rpc/src/eth/mod.rs
@@ -17,7 +17,6 @@
// along with this program. If not, see .
mod block;
-mod cache;
mod client;
mod execute;
mod fee;
@@ -53,14 +52,13 @@ use fp_rpc::{
RuntimeStorageOverride, TransactionStatus,
};
-use crate::{frontier_backend_client, internal_err, public_key, signer::EthSigner};
-
-pub use self::{
- cache::{EthBlockDataCacheTask, EthTask},
- execute::EstimateGasAdapter,
- filter::EthFilter,
+use crate::{
+ cache::EthBlockDataCacheTask, frontier_backend_client, internal_err, public_key,
+ signer::EthSigner,
};
+pub use self::{execute::EstimateGasAdapter, filter::EthFilter};
+
// Configuration trait for RPC configuration.
pub trait EthConfig: Send + Sync + 'static {
type EstimateGasAdapter: EstimateGasAdapter + Send + Sync;
diff --git a/client/rpc/src/lib.rs b/client/rpc/src/lib.rs
index 2a217a41b5..46f82a3ed1 100644
--- a/client/rpc/src/lib.rs
+++ b/client/rpc/src/lib.rs
@@ -26,6 +26,8 @@
)]
#![deny(unused_crate_dependencies)]
+mod cache;
+mod debug;
mod eth;
mod eth_pubsub;
mod net;
@@ -37,10 +39,9 @@ mod web3;
#[cfg(feature = "txpool")]
pub use self::txpool::TxPool;
pub use self::{
- eth::{
- format, pending, EstimateGasAdapter, Eth, EthBlockDataCacheTask, EthConfig, EthFilter,
- EthTask,
- },
+ cache::{EthBlockDataCacheTask, EthTask},
+ debug::Debug,
+ eth::{format, pending, EstimateGasAdapter, Eth, EthConfig, EthFilter},
eth_pubsub::{EthPubSub, EthereumSubIdProvider},
net::Net,
signer::{EthDevSigner, EthSigner},
@@ -50,7 +51,8 @@ pub use ethereum::TransactionV2 as EthereumTransaction;
#[cfg(feature = "txpool")]
pub use fc_rpc_core::TxPoolApiServer;
pub use fc_rpc_core::{
- EthApiServer, EthFilterApiServer, EthPubSubApiServer, NetApiServer, Web3ApiServer,
+ DebugApiServer, EthApiServer, EthFilterApiServer, EthPubSubApiServer, NetApiServer,
+ Web3ApiServer,
};
pub use fc_storage::{
OverrideHandle, RuntimeApiStorageOverride, SchemaV1Override, SchemaV2Override,
diff --git a/template/node/src/rpc/eth.rs b/template/node/src/rpc/eth.rs
index caf7e478ef..ff4c80b327 100644
--- a/template/node/src/rpc/eth.rs
+++ b/template/node/src/rpc/eth.rs
@@ -94,9 +94,9 @@ where
EC: EthConfig,
{
use fc_rpc::{
- pending::AuraConsensusDataProvider, Eth, EthApiServer, EthDevSigner, EthFilter,
- EthFilterApiServer, EthPubSub, EthPubSubApiServer, EthSigner, Net, NetApiServer, Web3,
- Web3ApiServer,
+ pending::AuraConsensusDataProvider, Debug, DebugApiServer, Eth, EthApiServer, EthDevSigner,
+ EthFilter, EthFilterApiServer, EthPubSub, EthPubSubApiServer, EthSigner, Net, NetApiServer,
+ Web3, Web3ApiServer,
};
#[cfg(feature = "txpool")]
use fc_rpc::{TxPool, TxPoolApiServer};
@@ -154,12 +154,12 @@ where
io.merge(
EthFilter::new(
client.clone(),
- frontier_backend,
+ frontier_backend.clone(),
graph.clone(),
filter_pool,
500_usize, // max stored filters
max_past_logs,
- block_data_cache,
+ block_data_cache.clone(),
)
.into_rpc(),
)?;
@@ -171,7 +171,7 @@ where
client.clone(),
sync,
subscription_task_executor,
- overrides,
+ overrides.clone(),
pubsub_notification_sinks,
)
.into_rpc(),
@@ -189,6 +189,16 @@ where
io.merge(Web3::new(client.clone()).into_rpc())?;
+ io.merge(
+ Debug::new(
+ client.clone(),
+ frontier_backend,
+ overrides,
+ block_data_cache,
+ )
+ .into_rpc(),
+ )?;
+
#[cfg(feature = "txpool")]
io.merge(TxPool::new(client, graph).into_rpc())?;