fc_rpc/
lib.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
19#![allow(
20	clippy::too_many_arguments,
21	clippy::large_enum_variant,
22	clippy::manual_range_contains,
23	clippy::explicit_counter_loop,
24	clippy::len_zero,
25	clippy::new_without_default
26)]
27#![warn(unused_crate_dependencies)]
28
29mod cache;
30mod debug;
31mod eth;
32mod eth_pubsub;
33mod net;
34mod signer;
35#[cfg(feature = "txpool")]
36mod txpool;
37mod web3;
38
39#[cfg(feature = "txpool")]
40pub use self::txpool::TxPool;
41pub use self::{
42	cache::{EthBlockDataCacheTask, EthTask},
43	debug::Debug,
44	eth::{format, pending, EstimateGasAdapter, Eth, EthConfig, EthFilter},
45	eth_pubsub::{EthPubSub, EthereumSubIdProvider},
46	net::Net,
47	signer::{EthDevSigner, EthSigner},
48	web3::Web3,
49};
50pub use ethereum::TransactionV3 as EthereumTransaction;
51#[cfg(feature = "txpool")]
52pub use fc_rpc_core::TxPoolApiServer;
53pub use fc_rpc_core::{
54	DebugApiServer, EthApiServer, EthFilterApiServer, EthPubSubApiServer, NetApiServer,
55	Web3ApiServer,
56};
57pub use fc_storage::{overrides::*, StorageOverrideHandler};
58
59pub mod frontier_backend_client {
60	use super::internal_err;
61
62	use ethereum_types::{H160, H256, U256};
63	use jsonrpsee::core::RpcResult;
64	use scale_codec::Encode;
65	// Substrate
66	use sc_client_api::{
67		backend::{Backend, StorageProvider},
68		StorageKey,
69	};
70	use sp_blockchain::HeaderBackend;
71	use sp_io::hashing::{blake2_128, twox_128};
72	use sp_runtime::{
73		generic::BlockId,
74		traits::{Block as BlockT, HashingFor, UniqueSaturatedInto},
75	};
76	use sp_state_machine::OverlayedChanges;
77	// Frontier
78	use fc_rpc_core::types::BlockNumberOrHash;
79
80	/// Implements a default runtime storage override.
81	/// It assumes that the balances and nonces are stored in pallet `system.account`, and
82	/// have `nonce: Index` = `u32` for  and `free: Balance` = `u128`.
83	/// Uses IdentityAddressMapping for the address.
84	pub struct SystemAccountId20StorageOverride<B, C, BE>(pub std::marker::PhantomData<(B, C, BE)>);
85	impl<B, C, BE> fp_rpc::RuntimeStorageOverride<B, C> for SystemAccountId20StorageOverride<B, C, BE>
86	where
87		B: BlockT,
88		C: StorageProvider<B, BE> + Send + Sync,
89		BE: Backend<B>,
90	{
91		fn is_enabled() -> bool {
92			true
93		}
94
95		fn set_overlayed_changes(
96			client: &C,
97			overlayed_changes: &mut OverlayedChanges<HashingFor<B>>,
98			block: B::Hash,
99			_version: u32,
100			address: H160,
101			balance: Option<U256>,
102			nonce: Option<U256>,
103		) {
104			let mut key = [twox_128(b"System"), twox_128(b"Account")]
105				.concat()
106				.to_vec();
107			let account_id = Self::into_account_id_bytes(address);
108			key.extend(blake2_128(&account_id));
109			key.extend(&account_id);
110
111			if let Ok(Some(item)) = client.storage(block, &StorageKey(key.clone())) {
112				let mut new_item = item.0;
113
114				if let Some(nonce) = nonce {
115					new_item.splice(0..4, nonce.low_u32().encode());
116				}
117
118				if let Some(balance) = balance {
119					new_item.splice(16..32, balance.low_u128().encode());
120				}
121
122				overlayed_changes.set_storage(key, Some(new_item));
123			}
124		}
125
126		fn into_account_id_bytes(address: H160) -> Vec<u8> {
127			use pallet_evm::AddressMapping;
128			let address: H160 = pallet_evm::IdentityAddressMapping::into_account_id(address);
129			address.as_ref().to_owned()
130		}
131	}
132
133	/// Implements a runtime storage override.
134	/// It assumes that the balances and nonces are stored in pallet `system.account`, and
135	/// have `nonce: Index` = `u32` for  and `free: Balance` = `u128`.
136	/// USes HashedAddressMapping for the address.
137	pub struct SystemAccountId32StorageOverride<B, C, BE>(pub std::marker::PhantomData<(B, C, BE)>);
138	impl<B, C, BE> fp_rpc::RuntimeStorageOverride<B, C> for SystemAccountId32StorageOverride<B, C, BE>
139	where
140		B: BlockT,
141		C: StorageProvider<B, BE> + Send + Sync,
142		BE: Backend<B>,
143	{
144		fn is_enabled() -> bool {
145			true
146		}
147
148		fn set_overlayed_changes(
149			client: &C,
150			overlayed_changes: &mut OverlayedChanges<HashingFor<B>>,
151			block: B::Hash,
152			_version: u32,
153			address: H160,
154			balance: Option<U256>,
155			nonce: Option<U256>,
156		) {
157			let mut key = [twox_128(b"System"), twox_128(b"Account")]
158				.concat()
159				.to_vec();
160			let account_id = Self::into_account_id_bytes(address);
161			key.extend(blake2_128(&account_id));
162			key.extend(&account_id);
163
164			if let Ok(Some(item)) = client.storage(block, &StorageKey(key.clone())) {
165				let mut new_item = item.0;
166
167				if let Some(nonce) = nonce {
168					new_item.splice(0..4, nonce.low_u32().encode());
169				}
170
171				if let Some(balance) = balance {
172					new_item.splice(16..32, balance.low_u128().encode());
173				}
174
175				overlayed_changes.set_storage(key, Some(new_item));
176			}
177		}
178
179		fn into_account_id_bytes(address: H160) -> Vec<u8> {
180			use pallet_evm::AddressMapping;
181			use sp_core::crypto::ByteArray;
182			use sp_runtime::traits::BlakeTwo256;
183
184			pallet_evm::HashedAddressMapping::<BlakeTwo256>::into_account_id(address)
185				.as_slice()
186				.to_owned()
187		}
188	}
189
190	pub async fn native_block_id<B, C>(
191		client: &C,
192		backend: &dyn fc_api::Backend<B>,
193		number: Option<BlockNumberOrHash>,
194	) -> RpcResult<Option<BlockId<B>>>
195	where
196		B: BlockT,
197		C: HeaderBackend<B> + 'static,
198	{
199		Ok(match number.unwrap_or(BlockNumberOrHash::Latest) {
200			BlockNumberOrHash::Hash { hash, .. } => {
201				if let Ok(Some(hash)) = load_hash::<B, C>(client, backend, hash).await {
202					Some(BlockId::Hash(hash))
203				} else {
204					None
205				}
206			}
207			BlockNumberOrHash::Num(number) => Some(BlockId::Number(number.unique_saturated_into())),
208			BlockNumberOrHash::Latest => match backend.latest_block_hash().await {
209				Ok(hash) => Some(BlockId::Hash(hash)),
210				Err(e) => {
211					log::warn!(target: "rpc", "Failed to get latest block hash from the sql db: {e:?}");
212					Some(BlockId::Hash(client.info().best_hash))
213				}
214			},
215			BlockNumberOrHash::Earliest => Some(BlockId::Hash(client.info().genesis_hash)),
216			BlockNumberOrHash::Pending => None,
217			BlockNumberOrHash::Safe => Some(BlockId::Hash(client.info().finalized_hash)),
218			BlockNumberOrHash::Finalized => Some(BlockId::Hash(client.info().finalized_hash)),
219		})
220	}
221
222	pub async fn load_hash<B, C>(
223		client: &C,
224		backend: &dyn fc_api::Backend<B>,
225		hash: H256,
226	) -> RpcResult<Option<B::Hash>>
227	where
228		B: BlockT,
229		C: HeaderBackend<B> + 'static,
230	{
231		let substrate_hashes = backend
232			.block_hash(&hash)
233			.await
234			.map_err(|err| internal_err(format!("fetch aux store failed: {err:?}")))?;
235
236		if let Some(substrate_hashes) = substrate_hashes {
237			for substrate_hash in substrate_hashes {
238				if is_canon::<B, C>(client, substrate_hash) {
239					return Ok(Some(substrate_hash));
240				}
241			}
242		}
243		Ok(None)
244	}
245
246	pub fn is_canon<B, C>(client: &C, target_hash: B::Hash) -> bool
247	where
248		B: BlockT,
249		C: HeaderBackend<B> + 'static,
250	{
251		if let Ok(Some(number)) = client.number(target_hash) {
252			if let Ok(Some(hash)) = client.hash(number) {
253				return hash == target_hash;
254			}
255		}
256		false
257	}
258
259	pub async fn load_transactions<B, C>(
260		client: &C,
261		backend: &dyn fc_api::Backend<B>,
262		transaction_hash: H256,
263		only_canonical: bool,
264	) -> RpcResult<Option<(H256, u32)>>
265	where
266		B: BlockT,
267		C: HeaderBackend<B> + 'static,
268	{
269		let transaction_metadata = backend
270			.transaction_metadata(&transaction_hash)
271			.await
272			.map_err(|err| internal_err(format!("fetch aux store failed: {err:?}")))?;
273
274		transaction_metadata
275			.iter()
276			.find(|meta| is_canon::<B, C>(client, meta.substrate_block_hash))
277			.map_or_else(
278				|| {
279					if !only_canonical && transaction_metadata.len() > 0 {
280						Ok(Some((
281							transaction_metadata[0].ethereum_block_hash,
282							transaction_metadata[0].ethereum_index,
283						)))
284					} else {
285						Ok(None)
286					}
287				},
288				|meta| Ok(Some((meta.ethereum_block_hash, meta.ethereum_index))),
289			)
290	}
291}
292
293pub fn err<T: ToString>(
294	code: i32,
295	message: T,
296	data: Option<&[u8]>,
297) -> jsonrpsee::types::error::ErrorObjectOwned {
298	jsonrpsee::types::error::ErrorObject::owned(
299		code,
300		message.to_string(),
301		data.map(|bytes| {
302			jsonrpsee::core::to_json_raw_value(&format!("0x{}", hex::encode(bytes)))
303				.expect("fail to serialize data")
304		}),
305	)
306}
307
308pub fn internal_err<T: ToString>(message: T) -> jsonrpsee::types::error::ErrorObjectOwned {
309	err(jsonrpsee::types::error::INTERNAL_ERROR_CODE, message, None)
310}
311
312pub fn internal_err_with_data<T: ToString>(
313	message: T,
314	data: &[u8],
315) -> jsonrpsee::types::error::ErrorObjectOwned {
316	err(
317		jsonrpsee::types::error::INTERNAL_ERROR_CODE,
318		message,
319		Some(data),
320	)
321}
322
323pub fn public_key(transaction: &EthereumTransaction) -> Result<[u8; 64], sp_io::EcdsaVerifyError> {
324	let mut sig = [0u8; 65];
325	let mut msg = [0u8; 32];
326	match transaction {
327		EthereumTransaction::Legacy(t) => {
328			sig[0..32].copy_from_slice(&t.signature.r()[..]);
329			sig[32..64].copy_from_slice(&t.signature.s()[..]);
330			sig[64] = t.signature.standard_v();
331			msg.copy_from_slice(&ethereum::LegacyTransactionMessage::from(t.clone()).hash()[..]);
332		}
333		EthereumTransaction::EIP2930(t) => {
334			sig[0..32].copy_from_slice(&t.signature.r()[..]);
335			sig[32..64].copy_from_slice(&t.signature.s()[..]);
336			sig[64] = t.signature.odd_y_parity() as u8;
337			msg.copy_from_slice(&ethereum::EIP2930TransactionMessage::from(t.clone()).hash()[..]);
338		}
339		EthereumTransaction::EIP1559(t) => {
340			sig[0..32].copy_from_slice(&t.signature.r()[..]);
341			sig[32..64].copy_from_slice(&t.signature.s()[..]);
342			sig[64] = t.signature.odd_y_parity() as u8;
343			msg.copy_from_slice(&ethereum::EIP1559TransactionMessage::from(t.clone()).hash()[..]);
344		}
345		EthereumTransaction::EIP7702(t) => {
346			sig[0..32].copy_from_slice(&t.signature.r()[..]);
347			sig[32..64].copy_from_slice(&t.signature.s()[..]);
348			sig[64] = t.signature.odd_y_parity() as u8;
349			msg.copy_from_slice(&ethereum::EIP7702TransactionMessage::from(t.clone()).hash()[..]);
350		}
351	}
352	sp_io::crypto::secp256k1_ecdsa_recover(&sig, &msg)
353}
354
355#[cfg(test)]
356mod tests {
357	use std::{path::PathBuf, sync::Arc};
358
359	use futures::executor;
360	use sc_block_builder::BlockBuilderBuilder;
361	use sp_blockchain::HeaderBackend;
362	use sp_consensus::BlockOrigin;
363	use sp_runtime::{
364		generic::{Block, Header},
365		traits::{BlakeTwo256, Block as BlockT},
366	};
367	use substrate_test_runtime_client::{
368		prelude::*, DefaultTestClientBuilderExt, TestClientBuilder,
369	};
370	use tempfile::tempdir;
371
372	type OpaqueBlock =
373		Block<Header<u64, BlakeTwo256>, substrate_test_runtime_client::runtime::Extrinsic>;
374
375	fn open_frontier_backend<Block: BlockT, C: HeaderBackend<Block>>(
376		client: Arc<C>,
377		path: PathBuf,
378	) -> Result<Arc<fc_db::kv::Backend<Block, C>>, String> {
379		Ok(Arc::new(fc_db::kv::Backend::<Block, C>::new(
380			client,
381			&fc_db::kv::DatabaseSettings {
382				source: sc_client_db::DatabaseSource::RocksDb {
383					path,
384					cache_size: 0,
385				},
386			},
387		)?))
388	}
389
390	#[test]
391	fn substrate_block_hash_one_to_many_works() {
392		let tmp = tempdir().expect("create a temporary directory");
393		let (client, _) = TestClientBuilder::new()
394			.build_with_native_executor::<substrate_test_runtime_client::runtime::RuntimeApi, _>(
395			None,
396		);
397
398		let client = Arc::new(client);
399
400		// Create a temporary frontier secondary DB.
401		let backend = open_frontier_backend::<OpaqueBlock, _>(client.clone(), tmp.keep())
402			.expect("a temporary db was created");
403
404		// A random ethereum block hash to use
405		let ethereum_block_hash = sp_core::H256::random();
406
407		// G -> A1.
408		let chain = client.chain_info();
409		let mut builder = BlockBuilderBuilder::new(&*client)
410			.on_parent_block(chain.best_hash)
411			.with_parent_block_number(chain.best_number)
412			.build()
413			.unwrap();
414		builder.push_storage_change(vec![1], None).unwrap();
415		let a1 = builder.build().unwrap().block;
416		let a1_hash = a1.header.hash();
417		executor::block_on(client.import(BlockOrigin::Own, a1)).unwrap();
418
419		// A1 -> B1
420		let mut builder = BlockBuilderBuilder::new(&*client)
421			.on_parent_block(a1_hash)
422			.fetch_parent_block_number(&*client)
423			.unwrap()
424			.build()
425			.unwrap();
426		builder.push_storage_change(vec![1], None).unwrap();
427		let b1 = builder.build().unwrap().block;
428		let b1_hash = b1.header.hash();
429		executor::block_on(client.import(BlockOrigin::Own, b1)).unwrap();
430
431		// Map B1
432		let commitment = fc_db::kv::MappingCommitment::<OpaqueBlock> {
433			block_hash: b1_hash,
434			ethereum_block_hash,
435			ethereum_transaction_hashes: vec![],
436		};
437		let _ = backend.mapping().write_hashes(commitment);
438
439		// Expect B1 to be canon
440		assert_eq!(
441			futures::executor::block_on(super::frontier_backend_client::load_hash(
442				client.as_ref(),
443				backend.as_ref(),
444				ethereum_block_hash
445			))
446			.unwrap()
447			.unwrap(),
448			b1_hash,
449		);
450
451		// A1 -> B2
452		let mut builder = BlockBuilderBuilder::new(&*client)
453			.on_parent_block(a1_hash)
454			.fetch_parent_block_number(&*client)
455			.unwrap()
456			.build()
457			.unwrap();
458		builder.push_storage_change(vec![2], None).unwrap();
459		let b2 = builder.build().unwrap().block;
460		let b2_hash = b2.header.hash();
461		executor::block_on(client.import(BlockOrigin::Own, b2)).unwrap();
462
463		// Map B2 to same ethereum hash
464		let commitment = fc_db::kv::MappingCommitment::<OpaqueBlock> {
465			block_hash: b2_hash,
466			ethereum_block_hash,
467			ethereum_transaction_hashes: vec![],
468		};
469		let _ = backend.mapping().write_hashes(commitment);
470
471		// Still expect B1 to be canon
472		assert_eq!(
473			futures::executor::block_on(super::frontier_backend_client::load_hash(
474				client.as_ref(),
475				backend.as_ref(),
476				ethereum_block_hash
477			))
478			.unwrap()
479			.unwrap(),
480			b1_hash,
481		);
482
483		// B2 -> C1. B2 branch is now canon.
484		let mut builder = BlockBuilderBuilder::new(&*client)
485			.on_parent_block(b2_hash)
486			.fetch_parent_block_number(&*client)
487			.unwrap()
488			.build()
489			.unwrap();
490		builder.push_storage_change(vec![1], None).unwrap();
491		let c1 = builder.build().unwrap().block;
492		executor::block_on(client.import(BlockOrigin::Own, c1)).unwrap();
493
494		// Expect B2 to be new canon
495		assert_eq!(
496			futures::executor::block_on(super::frontier_backend_client::load_hash(
497				client.as_ref(),
498				backend.as_ref(),
499				ethereum_block_hash
500			))
501			.unwrap()
502			.unwrap(),
503			b2_hash,
504		);
505	}
506}