Skip to content

Commit

Permalink
feat: add debug standard rpc (#1237)
Browse files Browse the repository at this point in the history
- debug_getRawHeader
- debug_getRawBlock
- debug_getRawTransaction
- debug_getRawReceipts
- debug_getBadBlocks
  • Loading branch information
koushiro authored Jan 3, 2024
1 parent 2d6b458 commit 34159d0
Show file tree
Hide file tree
Showing 10 changed files with 289 additions and 21 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

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

49 changes: 49 additions & 0 deletions client/rpc-core/src/debug.rs
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.

//! 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<Option<Bytes>>;

/// Returns an RLP-encoded block with the given number or hash.
#[method(name = "debug_getRawBlock")]
async fn raw_block(&self, number: BlockNumberOrHash) -> RpcResult<Option<Bytes>>;

/// Returns a EIP-2718 binary-encoded transaction with the given hash.
#[method(name = "debug_getRawTransaction")]
async fn raw_transaction(&self, hash: H256) -> RpcResult<Option<Bytes>>;

/// 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<Vec<Bytes>>;

/// 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<Vec<()>>;
}
2 changes: 2 additions & 0 deletions client/rpc-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

pub mod types;

mod debug;
mod eth;
mod eth_pubsub;
mod net;
Expand All @@ -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,
Expand Down
File renamed without changes.
File renamed without changes.
207 changes: 207 additions & 0 deletions client/rpc/src/debug.rs
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.

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<B: BlockT, C, BE> {
client: Arc<C>,
backend: Arc<dyn fc_api::Backend<B>>,
overrides: Arc<OverrideHandle<B>>,
block_data_cache: Arc<EthBlockDataCacheTask<B>>,
_marker: PhantomData<BE>,
}

impl<B: BlockT, C, BE> Debug<B, C, BE> {
pub fn new(
client: Arc<C>,
backend: Arc<dyn fc_api::Backend<B>>,
overrides: Arc<OverrideHandle<B>>,
block_data_cache: Arc<EthBlockDataCacheTask<B>>,
) -> Self {
Self {
client,
backend,
overrides,
block_data_cache,
_marker: PhantomData,
}
}

async fn block_by(&self, number: BlockNumberOrHash) -> RpcResult<Option<ethereum::BlockV2>>
where
C: HeaderBackend<B> + StorageProvider<B, BE> + 'static,
BE: Backend<B>,
{
let id = match frontier_backend_client::native_block_id::<B, C>(
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<Option<ethereum::TransactionV2>>
where
C: HeaderBackend<B> + StorageProvider<B, BE> + 'static,
BE: Backend<B>,
{
let (eth_block_hash, index) = match frontier_backend_client::load_transactions::<B, C>(
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::<B, C>(
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<Option<Vec<ethereum::ReceiptV3>>>
where
C: HeaderBackend<B> + StorageProvider<B, BE> + 'static,
BE: Backend<B>,
{
let id = match frontier_backend_client::native_block_id::<B, C>(
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<B, C, BE> DebugApiServer for Debug<B, C, BE>
where
B: BlockT,
C: ProvideRuntimeApi<B>,
C::Api: EthereumRuntimeRPCApi<B>,
C: HeaderBackend<B> + StorageProvider<B, BE> + 'static,
BE: Backend<B> + 'static,
{
async fn raw_header(&self, number: BlockNumberOrHash) -> RpcResult<Option<Bytes>> {
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<Option<Bytes>> {
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<Option<Bytes>> {
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<Vec<Bytes>> {
let receipts = self.receipts_by(number).await?.unwrap_or_default();
Ok(receipts
.into_iter()
.map(|receipt| Bytes::new(receipt.encode().to_vec()))
.collect::<Vec<_>>())
}

fn bad_blocks(&self, _number: BlockNumberOrHash) -> RpcResult<Vec<()>> {
// `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![])
}
}
2 changes: 1 addition & 1 deletion client/rpc/src/eth/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<B: BlockT, C, BE, A: ChainApi> {
client: Arc<C>,
Expand Down
12 changes: 5 additions & 7 deletions client/rpc/src/eth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.

mod block;
mod cache;
mod client;
mod execute;
mod fee;
Expand Down Expand Up @@ -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<B: BlockT, C>: Send + Sync + 'static {
type EstimateGasAdapter: EstimateGasAdapter + Send + Sync;
Expand Down
12 changes: 7 additions & 5 deletions client/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
)]
#![deny(unused_crate_dependencies)]

mod cache;
mod debug;
mod eth;
mod eth_pubsub;
mod net;
Expand All @@ -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},
Expand All @@ -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,
Expand Down
Loading

0 comments on commit 34159d0

Please sign in to comment.