1use std::{marker::PhantomData, sync::Arc};
20
21use ethereum::TransactionV3 as EthereumTransaction;
22use ethereum_types::{H160, H256, U256};
23use jsonrpsee::core::RpcResult;
24use serde::Serialize;
25use 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;
31use 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 fn collect_txpool_transactions(&self) -> RpcResult<TxPoolTransactions> {
107 let ready_extrinsics = self
109 .pool
110 .ready()
111 .map(|in_pool_tx| in_pool_tx.data().as_ref().clone())
112 .collect();
113
114 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 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}