frequency_service/rpc/
frequency_rpc.rs

1// Strong Documentation Lints
2#![deny(
3	rustdoc::broken_intra_doc_links,
4	rustdoc::missing_crate_level_docs,
5	rustdoc::invalid_codeblock_attributes,
6	missing_docs
7)]
8
9use common_helpers::rpc::map_rpc_result;
10use common_primitives::rpc::RpcEvent;
11use jsonrpsee::{
12	core::{async_trait, RpcResult},
13	proc_macros::rpc,
14	types::error::ErrorObject,
15};
16use parity_scale_codec::{Codec, Decode, Encode};
17use sc_transaction_pool_api::{InPoolTransaction, TransactionPool};
18use sp_api::ProvideRuntimeApi;
19use sp_blockchain::HeaderBackend;
20use sp_runtime::traits::{AtLeast32Bit, Block as BlockT, One};
21use std::sync::Arc;
22use substrate_frame_rpc_system::AccountNonceApi;
23use system_runtime_api::AdditionalRuntimeApi;
24
25/// This is an upper limit to restrict the number of returned nonce holes to eliminate a potential
26/// attack vector
27const MAX_RETURNED_MISSING_NONCE_SIZE: usize = 1000;
28/// Frequency MSA Custom RPC API
29#[rpc(client, server)]
30pub trait FrequencyRpcApi<B: BlockT, AccountId, Nonce> {
31	/// gets the events for a block hash
32	#[method(name = "frequency_getEvents")]
33	fn get_events(&self, at: B::Hash) -> RpcResult<Vec<RpcEvent>>;
34	/// returns a list of missing nonce values from Future transaction pool.
35	#[method(name = "frequency_getMissingNonceValues")]
36	fn get_missing_nonce_values(&self, account: AccountId) -> RpcResult<Vec<Nonce>>;
37}
38
39/// The client handler for the API used by Frequency Service RPC with `jsonrpsee`
40pub struct FrequencyRpcHandler<P: TransactionPool, C, M> {
41	client: Arc<C>,
42	pool: Arc<P>,
43	_marker: std::marker::PhantomData<M>,
44}
45
46impl<P: TransactionPool, C, M> FrequencyRpcHandler<P, C, M> {
47	/// Create new instance with the given reference to the client.
48	pub fn new(client: Arc<C>, pool: Arc<P>) -> Self {
49		Self { client, pool, _marker: Default::default() }
50	}
51}
52
53#[async_trait]
54impl<P, C, Block, AccountId, Nonce> FrequencyRpcApiServer<Block, AccountId, Nonce>
55	for FrequencyRpcHandler<P, C, Block>
56where
57	Block: BlockT,
58	C: HeaderBackend<Block>,
59	C: Send + Sync + 'static,
60	C: ProvideRuntimeApi<Block>,
61	C::Api: AdditionalRuntimeApi<Block>,
62	C::Api: AccountNonceApi<Block, AccountId, Nonce>,
63	P: TransactionPool + 'static,
64	AccountId: Clone + Codec,
65	Nonce: Clone + Encode + Decode + AtLeast32Bit + 'static,
66{
67	fn get_events(&self, at: <Block as BlockT>::Hash) -> RpcResult<Vec<RpcEvent>> {
68		let api = self.client.runtime_api();
69		map_rpc_result(api.get_events(at))
70	}
71
72	fn get_missing_nonce_values(&self, account: AccountId) -> RpcResult<Vec<Nonce>> {
73		let api = self.client.runtime_api();
74		let best = self.client.info().best_hash;
75
76		let nonce = api
77			.account_nonce(best, account.clone())
78			.map_err(|e| ErrorObject::owned(1, "Unable to query nonce.", Some(e.to_string())))?;
79		Ok(get_missing_nonces(&*self.pool, account, nonce))
80	}
81}
82
83/// Finds any missing nonce values inside Future pool and return them as result
84fn get_missing_nonces<P, AccountId, Nonce>(pool: &P, account: AccountId, nonce: Nonce) -> Vec<Nonce>
85where
86	P: TransactionPool,
87	AccountId: Clone + Encode,
88	Nonce: Clone + Encode + Decode + AtLeast32Bit + 'static,
89{
90	// Now we need to query the transaction pool
91	// and find transactions originating from the same sender.
92	// Since extrinsics are opaque to us, we look for them using
93	// `provides` tag. And increment the nonce if we find a transaction
94	// that matches the current one.
95	let mut current_nonce = nonce.clone();
96	let encoded_account = account.clone().encode();
97	let mut current_tag = (account.clone(), nonce).encode();
98	for tx in pool.ready() {
99		// since transactions in `ready()` need to be ordered by nonce
100		// it's fine to continue with current iterator.
101		if tx.provides().get(0) == Some(&current_tag) {
102			current_nonce += One::one();
103			current_tag = (account.clone(), current_nonce.clone()).encode();
104		}
105	}
106
107	let mut result = vec![];
108	let mut my_in_future: Vec<_> = pool
109		.futures()
110		.into_iter()
111		.filter_map(|x| match x.provides().get(0) {
112			// filtering transactions by account
113			Some(tag) if tag.starts_with(&encoded_account) => {
114				if let Ok(nonce) = Nonce::decode(&mut &tag[encoded_account.len()..]) {
115					return Some(nonce)
116				}
117				None
118			},
119			_ => None,
120		})
121		.collect();
122	my_in_future.sort();
123
124	for future_nonce in my_in_future {
125		while current_nonce < future_nonce {
126			result.push(current_nonce.clone());
127			current_nonce += One::one();
128
129			// short circuit if we reached the limit
130			if result.len() == MAX_RETURNED_MISSING_NONCE_SIZE {
131				return result
132			}
133		}
134
135		// progress the current_nonce
136		current_nonce += One::one();
137	}
138
139	result
140}