1#![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 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 use fc_rpc_core::types::BlockNumberOrHash;
79
80 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 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(ðereum::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(ðereum::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(ðereum::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(ðereum::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 let backend = open_frontier_backend::<OpaqueBlock, _>(client.clone(), tmp.keep())
402 .expect("a temporary db was created");
403
404 let ethereum_block_hash = sp_core::H256::random();
406
407 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 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 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 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 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 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 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 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 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}