referrerpolicy=no-referrer-when-downgrade

substrate_frame_rpc_system/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! System FRAME specific RPC methods.
19
20use std::{fmt::Display, sync::Arc};
21
22use codec::{self, Codec, Decode, Encode};
23use jsonrpsee::{
24	core::{async_trait, RpcResult},
25	proc_macros::rpc,
26	types::error::ErrorObject,
27	Extensions,
28};
29
30use sc_transaction_pool_api::{InPoolTransaction, TransactionPool};
31use sp_api::ApiExt;
32use sp_block_builder::BlockBuilder;
33use sp_blockchain::HeaderBackend;
34use sp_core::{hexdisplay::HexDisplay, Bytes};
35use sp_runtime::{legacy, traits};
36
37pub use frame_system_rpc_runtime_api::AccountNonceApi;
38
39/// System RPC methods.
40#[docify::export]
41#[rpc(client, server)]
42pub trait SystemApi<BlockHash, AccountId, Nonce> {
43	/// Returns the next valid index (aka nonce) for given account.
44	///
45	/// This method takes into consideration all pending transactions
46	/// currently in the pool and if no transactions are found in the pool
47	/// it fallbacks to query the index from the runtime (aka. state nonce).
48	#[method(name = "system_accountNextIndex", aliases = ["account_nextIndex"])]
49	async fn nonce(&self, account: AccountId) -> RpcResult<Nonce>;
50
51	/// Dry run an extrinsic at a given block. Return SCALE encoded ApplyExtrinsicResult.
52	#[method(name = "system_dryRun", aliases = ["system_dryRunAt"], with_extensions)]
53	async fn dry_run(&self, extrinsic: Bytes, at: Option<BlockHash>) -> RpcResult<Bytes>;
54}
55
56/// Error type of this RPC api.
57pub enum Error {
58	/// The transaction was not decodable.
59	DecodeError,
60	/// The call to runtime failed.
61	RuntimeError,
62}
63
64impl From<Error> for i32 {
65	fn from(e: Error) -> i32 {
66		match e {
67			Error::RuntimeError => 1,
68			Error::DecodeError => 2,
69		}
70	}
71}
72
73/// An implementation of System-specific RPC methods on full client.
74pub struct System<P: TransactionPool, C, B> {
75	client: Arc<C>,
76	pool: Arc<P>,
77	_marker: std::marker::PhantomData<B>,
78}
79
80impl<P: TransactionPool, C, B> System<P, C, B> {
81	/// Create new `FullSystem` given client and transaction pool.
82	pub fn new(client: Arc<C>, pool: Arc<P>) -> Self {
83		Self { client, pool, _marker: Default::default() }
84	}
85}
86
87#[async_trait]
88impl<P, C, Block, AccountId, Nonce>
89	SystemApiServer<<Block as traits::Block>::Hash, AccountId, Nonce> for System<P, C, Block>
90where
91	C: sp_api::ProvideRuntimeApi<Block>,
92	C: HeaderBackend<Block>,
93	C: Send + Sync + 'static,
94	C::Api: AccountNonceApi<Block, AccountId, Nonce>,
95	C::Api: BlockBuilder<Block>,
96	P: TransactionPool + 'static,
97	Block: traits::Block,
98	AccountId: Clone + Display + Codec + Send + 'static,
99	Nonce: Clone + Display + Codec + Send + traits::AtLeast32Bit + 'static,
100{
101	async fn nonce(&self, account: AccountId) -> RpcResult<Nonce> {
102		let api = self.client.runtime_api();
103		let best = self.client.info().best_hash;
104
105		let nonce = api.account_nonce(best, account.clone()).map_err(|e| {
106			ErrorObject::owned(
107				Error::RuntimeError.into(),
108				"Unable to query nonce.",
109				Some(e.to_string()),
110			)
111		})?;
112		Ok(adjust_nonce(&*self.pool, account, nonce))
113	}
114
115	async fn dry_run(
116		&self,
117		ext: &Extensions,
118		extrinsic: Bytes,
119		at: Option<<Block as traits::Block>::Hash>,
120	) -> RpcResult<Bytes> {
121		sc_rpc_api::check_if_safe(ext)?;
122
123		let api = self.client.runtime_api();
124		let best_hash = at.unwrap_or_else(||
125			// If the block hash is not supplied assume the best block.
126			self.client.info().best_hash);
127
128		let uxt: <Block as traits::Block>::Extrinsic =
129			Decode::decode(&mut &*extrinsic).map_err(|e| {
130				ErrorObject::owned(
131					Error::DecodeError.into(),
132					"Unable to dry run extrinsic",
133					Some(e.to_string()),
134				)
135			})?;
136
137		let api_version = api
138			.api_version::<dyn BlockBuilder<Block>>(best_hash)
139			.map_err(|e| {
140				ErrorObject::owned(
141					Error::RuntimeError.into(),
142					"Unable to dry run extrinsic.",
143					Some(e.to_string()),
144				)
145			})?
146			.ok_or_else(|| {
147				ErrorObject::owned(
148					Error::RuntimeError.into(),
149					"Unable to dry run extrinsic.",
150					Some(format!("Could not find `BlockBuilder` api for block `{:?}`.", best_hash)),
151				)
152			})?;
153
154		let result = if api_version < 6 {
155			#[allow(deprecated)]
156			api.apply_extrinsic_before_version_6(best_hash, uxt)
157				.map(legacy::byte_sized_error::convert_to_latest)
158				.map_err(|e| {
159					ErrorObject::owned(
160						Error::RuntimeError.into(),
161						"Unable to dry run extrinsic.",
162						Some(e.to_string()),
163					)
164				})?
165		} else {
166			api.apply_extrinsic(best_hash, uxt).map_err(|e| {
167				ErrorObject::owned(
168					Error::RuntimeError.into(),
169					"Unable to dry run extrinsic.",
170					Some(e.to_string()),
171				)
172			})?
173		};
174
175		Ok(Encode::encode(&result).into())
176	}
177}
178
179/// Adjust account nonce from state, so that tx with the nonce will be
180/// placed after all ready txpool transactions.
181fn adjust_nonce<P, AccountId, Nonce>(pool: &P, account: AccountId, nonce: Nonce) -> Nonce
182where
183	P: TransactionPool,
184	AccountId: Clone + std::fmt::Display + Encode,
185	Nonce: Clone + std::fmt::Display + Encode + traits::AtLeast32Bit + 'static,
186{
187	log::debug!(target: "rpc", "State nonce for {}: {}", account, nonce);
188	// Now we need to query the transaction pool
189	// and find transactions originating from the same sender.
190	//
191	// Since extrinsics are opaque to us, we look for them using
192	// `provides` tag. And increment the nonce if we find a transaction
193	// that matches the current one.
194	let mut current_nonce = nonce.clone();
195	let mut current_tag = (account.clone(), nonce).encode();
196	for tx in pool.ready() {
197		log::debug!(
198			target: "rpc",
199			"Current nonce to {}, checking {} vs {:?}",
200			current_nonce,
201			HexDisplay::from(&current_tag),
202			tx.provides().iter().map(|x| format!("{}", HexDisplay::from(x))).collect::<Vec<_>>(),
203		);
204		// since transactions in `ready()` need to be ordered by nonce
205		// it's fine to continue with current iterator.
206		if tx.provides().get(0) == Some(&current_tag) {
207			current_nonce += traits::One::one();
208			current_tag = (account.clone(), current_nonce.clone()).encode();
209		}
210	}
211
212	current_nonce
213}
214
215#[cfg(test)]
216mod tests {
217	use super::*;
218
219	use assert_matches::assert_matches;
220	use futures::executor::block_on;
221	use sc_rpc_api::DenyUnsafe;
222	use sc_transaction_pool::BasicPool;
223	use sp_runtime::{
224		transaction_validity::{InvalidTransaction, TransactionValidityError},
225		ApplyExtrinsicResult,
226	};
227	use substrate_test_runtime_client::{runtime::Transfer, Sr25519Keyring};
228
229	fn deny_unsafe() -> Extensions {
230		let mut ext = Extensions::new();
231		ext.insert(DenyUnsafe::Yes);
232		ext
233	}
234
235	fn allow_unsafe() -> Extensions {
236		let mut ext = Extensions::new();
237		ext.insert(DenyUnsafe::No);
238		ext
239	}
240
241	#[tokio::test]
242	async fn should_return_next_nonce_for_some_account() {
243		sp_tracing::try_init_simple();
244
245		// given
246		let client = Arc::new(substrate_test_runtime_client::new());
247		let spawner = sp_core::testing::TaskExecutor::new();
248		let pool = Arc::from(BasicPool::new_full(
249			Default::default(),
250			true.into(),
251			None,
252			spawner,
253			client.clone(),
254		));
255
256		let source = sp_runtime::transaction_validity::TransactionSource::External;
257		let new_transaction = |nonce: u64| {
258			let t = Transfer {
259				from: Sr25519Keyring::Alice.into(),
260				to: Sr25519Keyring::Bob.into(),
261				amount: 5,
262				nonce,
263			};
264			t.into_unchecked_extrinsic()
265		};
266		let hash_of_block0 = client.info().genesis_hash;
267		// Populate the pool
268		let ext0 = new_transaction(0);
269		block_on(pool.submit_one(hash_of_block0, source, ext0)).unwrap();
270		let ext1 = new_transaction(1);
271		block_on(pool.submit_one(hash_of_block0, source, ext1)).unwrap();
272
273		let accounts = System::new(client, pool);
274
275		// when
276		let nonce = accounts.nonce(Sr25519Keyring::Alice.into()).await;
277
278		// then
279		assert_eq!(nonce.unwrap(), 2);
280	}
281
282	#[tokio::test]
283	async fn dry_run_should_deny_unsafe() {
284		sp_tracing::try_init_simple();
285
286		// given
287		let client = Arc::new(substrate_test_runtime_client::new());
288		let spawner = sp_core::testing::TaskExecutor::new();
289		let pool = Arc::from(BasicPool::new_full(
290			Default::default(),
291			true.into(),
292			None,
293			spawner,
294			client.clone(),
295		));
296
297		let accounts = System::new(client, pool);
298
299		// when
300		let res = accounts.dry_run(&deny_unsafe(), vec![].into(), None).await;
301		assert_matches!(res, Err(e) => {
302			assert!(e.message().contains("RPC call is unsafe to be called externally"));
303		});
304	}
305
306	#[tokio::test]
307	async fn dry_run_should_work() {
308		sp_tracing::try_init_simple();
309
310		// given
311		let client = Arc::new(substrate_test_runtime_client::new());
312		let spawner = sp_core::testing::TaskExecutor::new();
313		let pool = Arc::from(BasicPool::new_full(
314			Default::default(),
315			true.into(),
316			None,
317			spawner,
318			client.clone(),
319		));
320
321		let accounts = System::new(client, pool);
322
323		let tx = Transfer {
324			from: Sr25519Keyring::Alice.into(),
325			to: Sr25519Keyring::Bob.into(),
326			amount: 5,
327			nonce: 0,
328		}
329		.into_unchecked_extrinsic();
330
331		// when
332		let bytes = accounts
333			.dry_run(&allow_unsafe(), tx.encode().into(), None)
334			.await
335			.expect("Call is successful");
336
337		// then
338		let apply_res: ApplyExtrinsicResult = Decode::decode(&mut bytes.as_ref()).unwrap();
339		assert_eq!(apply_res, Ok(Ok(())));
340	}
341
342	#[tokio::test]
343	async fn dry_run_should_indicate_error() {
344		sp_tracing::try_init_simple();
345
346		// given
347		let client = Arc::new(substrate_test_runtime_client::new());
348		let spawner = sp_core::testing::TaskExecutor::new();
349		let pool = Arc::from(BasicPool::new_full(
350			Default::default(),
351			true.into(),
352			None,
353			spawner,
354			client.clone(),
355		));
356
357		let accounts = System::new(client, pool);
358
359		let tx = Transfer {
360			from: Sr25519Keyring::Alice.into(),
361			to: Sr25519Keyring::Bob.into(),
362			amount: 5,
363			nonce: 100,
364		}
365		.into_unchecked_extrinsic();
366
367		// when
368		let bytes = accounts
369			.dry_run(&allow_unsafe(), tx.encode().into(), None)
370			.await
371			.expect("Call is successful");
372
373		// then
374		let apply_res: ApplyExtrinsicResult = Decode::decode(&mut bytes.as_ref()).unwrap();
375		assert_eq!(apply_res, Err(TransactionValidityError::Invalid(InvalidTransaction::Future)));
376	}
377}