fc_rpc/
txpool.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::TransactionV3 as EthereumTransaction;
22use ethereum_types::{H160, H256, U256};
23use jsonrpsee::core::RpcResult;
24use serde::Serialize;
25// substrate
26use sc_transaction_pool_api::{InPoolTransaction, TransactionPool};
27use sp_api::ProvideRuntimeApi;
28use sp_blockchain::HeaderBackend;
29use sp_core::hashing::keccak_256;
30use sp_runtime::traits::Block as BlockT;
31// Frontier
32use fc_rpc_core::{
33	types::{BuildFrom, Summary, Transaction, TransactionMap, TxPoolResult},
34	TxPoolApiServer,
35};
36use fp_rpc::EthereumRuntimeRPCApi;
37
38use crate::{internal_err, public_key};
39
40struct TxPoolTransactions {
41	ready: Vec<EthereumTransaction>,
42	future: Vec<EthereumTransaction>,
43}
44
45pub struct TxPool<B, C, P> {
46	client: Arc<C>,
47	pool: Arc<P>,
48	_marker: PhantomData<B>,
49}
50
51impl<B, C, P: TransactionPool> Clone for TxPool<B, C, P> {
52	fn clone(&self) -> Self {
53		Self {
54			client: self.client.clone(),
55			pool: self.pool.clone(),
56			_marker: PhantomData,
57		}
58	}
59}
60
61impl<B, C, P> TxPool<B, C, P>
62where
63	B: BlockT,
64	C: ProvideRuntimeApi<B>,
65	C::Api: EthereumRuntimeRPCApi<B>,
66	C: HeaderBackend<B> + 'static,
67	P: TransactionPool<Block = B, Hash = B::Hash> + 'static,
68{
69	fn map_build<T>(&self) -> RpcResult<TxPoolResult<TransactionMap<T>>>
70	where
71		T: BuildFrom + Serialize,
72	{
73		let txns = self.collect_txpool_transactions()?;
74		let pending = Self::build_txn_map::<'_, T>(txns.ready.iter());
75		let queued = Self::build_txn_map::<'_, T>(txns.future.iter());
76		Ok(TxPoolResult { pending, queued })
77	}
78
79	fn build_txn_map<'a, T>(
80		txns: impl Iterator<Item = &'a EthereumTransaction>,
81	) -> TransactionMap<T>
82	where
83		T: BuildFrom + Serialize,
84	{
85		let mut result = TransactionMap::<T>::new();
86		for txn in txns {
87			let nonce = match txn {
88				EthereumTransaction::Legacy(t) => t.nonce,
89				EthereumTransaction::EIP2930(t) => t.nonce,
90				EthereumTransaction::EIP1559(t) => t.nonce,
91				EthereumTransaction::EIP7702(t) => t.nonce,
92			};
93			let from = match public_key(txn) {
94				Ok(pk) => H160::from(H256::from(keccak_256(&pk))),
95				Err(_) => H160::default(),
96			};
97			result
98				.entry(from)
99				.or_default()
100				.insert(nonce, T::build_from(from, txn));
101		}
102		result
103	}
104
105	/// Collect the extrinsics currently in the ready and future queues.
106	fn collect_txpool_transactions(&self) -> RpcResult<TxPoolTransactions> {
107		// Collect extrinsics in the ready validated pool.
108		let ready_extrinsics = self
109			.pool
110			.ready()
111			.map(|in_pool_tx| in_pool_tx.data().as_ref().clone())
112			.collect();
113
114		// Collect extrinsics in the future validated pool.
115		let future_extrinsics = self
116			.pool
117			.futures()
118			.iter()
119			.map(|in_pool_tx| in_pool_tx.data().as_ref().clone())
120			.collect();
121
122		// Use the runtime to match the (here) opaque extrinsics against ethereum transactions.
123		let best_block = self.client.info().best_hash;
124		let api = self.client.runtime_api();
125		let ready = api
126			.extrinsic_filter(best_block, ready_extrinsics)
127			.map_err(|err| internal_err(format!("fetch ready transactions failed: {err}")))?;
128		let future = api
129			.extrinsic_filter(best_block, future_extrinsics)
130			.map_err(|err| internal_err(format!("fetch future transactions failed: {err}")))?;
131
132		Ok(TxPoolTransactions { ready, future })
133	}
134}
135
136impl<B, C, P: TransactionPool> TxPool<B, C, P> {
137	pub fn new(client: Arc<C>, pool: Arc<P>) -> Self {
138		Self {
139			client,
140			pool,
141			_marker: PhantomData,
142		}
143	}
144}
145
146impl<B, C, P> TxPoolApiServer for TxPool<B, C, P>
147where
148	B: BlockT,
149	C: ProvideRuntimeApi<B>,
150	C::Api: EthereumRuntimeRPCApi<B>,
151	C: HeaderBackend<B> + 'static,
152	P: TransactionPool<Block = B, Hash = B::Hash> + 'static,
153{
154	fn content(&self) -> RpcResult<TxPoolResult<TransactionMap<Transaction>>> {
155		self.map_build::<Transaction>()
156	}
157
158	fn inspect(&self) -> RpcResult<TxPoolResult<TransactionMap<Summary>>> {
159		self.map_build::<Summary>()
160	}
161
162	fn status(&self) -> RpcResult<TxPoolResult<U256>> {
163		let status = self.pool.status();
164		Ok(TxPoolResult {
165			pending: U256::from(status.ready),
166			queued: U256::from(status.future),
167		})
168	}
169}