fc_rpc/
debug.rs

1// This file is part of Frontier.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19use std::{marker::PhantomData, sync::Arc};
20
21use ethereum::EnvelopedEncodable;
22use ethereum_types::H256;
23use jsonrpsee::core::{async_trait, RpcResult};
24use rlp::Encodable;
25// Substrate
26use sc_client_api::backend::{Backend, StorageProvider};
27use sp_api::ProvideRuntimeApi;
28use sp_blockchain::HeaderBackend;
29use sp_runtime::traits::Block as BlockT;
30// Frontier
31use fc_rpc_core::{types::*, DebugApiServer};
32use fc_storage::StorageOverride;
33use fp_rpc::EthereumRuntimeRPCApi;
34
35use crate::{cache::EthBlockDataCacheTask, frontier_backend_client, internal_err};
36
37/// Debug API implementation.
38pub struct Debug<B: BlockT, C, BE> {
39	client: Arc<C>,
40	backend: Arc<dyn fc_api::Backend<B>>,
41	storage_override: Arc<dyn StorageOverride<B>>,
42	block_data_cache: Arc<EthBlockDataCacheTask<B>>,
43	_marker: PhantomData<BE>,
44}
45
46impl<B: BlockT, C, BE> Debug<B, C, BE> {
47	pub fn new(
48		client: Arc<C>,
49		backend: Arc<dyn fc_api::Backend<B>>,
50		storage_override: Arc<dyn StorageOverride<B>>,
51		block_data_cache: Arc<EthBlockDataCacheTask<B>>,
52	) -> Self {
53		Self {
54			client,
55			backend,
56			storage_override,
57			block_data_cache,
58			_marker: PhantomData,
59		}
60	}
61
62	async fn block_by(&self, number: BlockNumberOrHash) -> RpcResult<Option<ethereum::BlockV3>>
63	where
64		C: HeaderBackend<B> + StorageProvider<B, BE> + 'static,
65		BE: Backend<B>,
66	{
67		let id = match frontier_backend_client::native_block_id::<B, C>(
68			self.client.as_ref(),
69			self.backend.as_ref(),
70			Some(number),
71		)
72		.await?
73		{
74			Some(id) => id,
75			None => return Ok(None),
76		};
77
78		let substrate_hash = self
79			.client
80			.expect_block_hash_from_id(&id)
81			.map_err(|_| internal_err(format!("Expect block number from id: {id}")))?;
82		let block = self.block_data_cache.current_block(substrate_hash).await;
83		Ok(block)
84	}
85
86	async fn transaction_by(
87		&self,
88		transaction_hash: H256,
89	) -> RpcResult<Option<ethereum::TransactionV3>>
90	where
91		C: HeaderBackend<B> + StorageProvider<B, BE> + 'static,
92		BE: Backend<B>,
93	{
94		let (eth_block_hash, index) = match frontier_backend_client::load_transactions::<B, C>(
95			self.client.as_ref(),
96			self.backend.as_ref(),
97			transaction_hash,
98			true,
99		)
100		.await?
101		{
102			Some((hash, index)) => (hash, index as usize),
103			None => return Ok(None),
104		};
105
106		let substrate_hash = match frontier_backend_client::load_hash::<B, C>(
107			self.client.as_ref(),
108			self.backend.as_ref(),
109			eth_block_hash,
110		)
111		.await?
112		{
113			Some(hash) => hash,
114			None => return Ok(None),
115		};
116
117		let block = self.block_data_cache.current_block(substrate_hash).await;
118		if let Some(block) = block {
119			Ok(Some(block.transactions[index].clone()))
120		} else {
121			Ok(None)
122		}
123	}
124
125	async fn receipts_by(
126		&self,
127		number: BlockNumberOrHash,
128	) -> RpcResult<Option<Vec<ethereum::ReceiptV4>>>
129	where
130		C: HeaderBackend<B> + StorageProvider<B, BE> + 'static,
131		BE: Backend<B>,
132	{
133		let id = match frontier_backend_client::native_block_id::<B, C>(
134			self.client.as_ref(),
135			self.backend.as_ref(),
136			Some(number),
137		)
138		.await?
139		{
140			Some(id) => id,
141			None => return Ok(None),
142		};
143
144		let substrate_hash = self
145			.client
146			.expect_block_hash_from_id(&id)
147			.map_err(|_| internal_err(format!("Expect block number from id: {id}")))?;
148
149		// TODO: use data cache in the future
150		let receipts = self.storage_override.current_receipts(substrate_hash);
151		Ok(receipts)
152	}
153}
154
155#[async_trait]
156impl<B, C, BE> DebugApiServer for Debug<B, C, BE>
157where
158	B: BlockT,
159	C: ProvideRuntimeApi<B>,
160	C::Api: EthereumRuntimeRPCApi<B>,
161	C: HeaderBackend<B> + StorageProvider<B, BE> + 'static,
162	BE: Backend<B> + 'static,
163{
164	async fn raw_header(&self, number: BlockNumberOrHash) -> RpcResult<Option<Bytes>> {
165		let block = self.block_by(number).await?;
166		Ok(block.map(|block| Bytes::new(block.header.rlp_bytes().to_vec())))
167	}
168
169	async fn raw_block(&self, number: BlockNumberOrHash) -> RpcResult<Option<Bytes>> {
170		let block = self.block_by(number).await?;
171		Ok(block.map(|block| Bytes::new(block.rlp_bytes().to_vec())))
172	}
173
174	async fn raw_transaction(&self, hash: H256) -> RpcResult<Option<Bytes>> {
175		let transaction = self.transaction_by(hash).await?;
176		Ok(transaction.map(|transaction| Bytes::new(transaction.encode().to_vec())))
177	}
178
179	async fn raw_receipts(&self, number: BlockNumberOrHash) -> RpcResult<Vec<Bytes>> {
180		let receipts = self.receipts_by(number).await?.unwrap_or_default();
181		Ok(receipts
182			.into_iter()
183			.map(|receipt| Bytes::new(receipt.encode().to_vec()))
184			.collect::<Vec<_>>())
185	}
186
187	fn bad_blocks(&self, _number: BlockNumberOrHash) -> RpcResult<Vec<()>> {
188		// `debug_getBadBlocks` wouldn't really be useful in a Substrate context.
189		// The rationale for that is for debugging multi-client consensus issues, which we'll never face
190		// (we may have multiple clients in the future, but for runtime it's only "multi-wasm-runtime", never "multi-EVM").
191		// We can simply return empty array for this API.
192		Ok(vec![])
193	}
194}