sc_simnode/
rpc.rs

1// Copyright (C) 2023 Polytope Labs (Caymans) Ltd.
2// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
3
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17//! Simnode RPC Subsystem
18
19use polkadot_sdk::*;
20
21use crate::{
22	client::{FullClientFor, UncheckedExtrinsicFor},
23	with_state, ChainInfo,
24};
25use async_trait::async_trait;
26use codec::Encode;
27use jsonrpsee::{core::RpcResult, proc_macros::rpc, types::ErrorObjectOwned};
28use sc_client_api::Backend;
29use sc_service::TFullBackend;
30use simnode_runtime_api::CreateTransactionApi;
31use sp_api::{ApiExt, ConstructRuntimeApi, ProvideRuntimeApi};
32use sp_blockchain::HeaderBackend;
33use sp_core::{
34	crypto::{AccountId32, Ss58Codec},
35	Bytes,
36};
37use sp_runtime::{
38	traits::{Block as BlockT, Header},
39	MultiAddress, MultiSignature,
40};
41
42use std::sync::Arc;
43
44/// Simnode RPC methods.
45#[rpc(client, server)]
46pub trait SimnodeApi {
47	/// Constructs an extrinsic with an empty signature and the given AccountId as the Signer using
48	/// simnode's runtime api.
49	#[method(name = "simnode_authorExtrinsic")]
50	fn author_extrinsic(&self, call: Bytes, account: String) -> RpcResult<Bytes>;
51
52	/// reverts `n` number of blocks and their state from the chain.
53	#[method(name = "simnode_revertBlocks")]
54	fn revert_blocks(&self, n: u32) -> RpcResult<()>;
55
56	/// Insert the [`UpgradeGoAhead`] command into the inherents as if it came from the relay chain.
57	/// This greenlights/aborts a pending runtime upgrade.
58	#[method(name = "simnode_upgradeSignal")]
59	async fn upgrade_signal(&self, go_ahead: bool) -> RpcResult<()>;
60}
61
62/// Handler implementation for Simnode RPC API.
63pub struct SimnodeRpcHandler<T: ChainInfo> {
64	/// Client type
65	client: Arc<FullClientFor<T>>,
66	/// Backend type.
67	backend: Arc<TFullBackend<T::Block>>,
68}
69
70impl<T> SimnodeRpcHandler<T>
71where
72	T: ChainInfo,
73	<T::RuntimeApi as ConstructRuntimeApi<T::Block, FullClientFor<T>>>::RuntimeApi:
74		CreateTransactionApi<
75			T::Block,
76			<T::Runtime as frame_system::Config>::RuntimeCall,
77			<T::Runtime as frame_system::Config>::AccountId,
78		>,
79	<T::Runtime as frame_system::Config>::AccountId: From<AccountId32>,
80{
81	/// Creates a new instance of simnode's RPC handler.
82	pub fn new(client: Arc<FullClientFor<T>>, backend: Arc<TFullBackend<T::Block>>) -> Self {
83		Self { client, backend }
84	}
85
86	fn author_extrinsic(&self, call: Bytes, account: String) -> RpcResult<Vec<u8>> {
87		let at = self.client.info().best_hash;
88
89		let has_api = self
90			.client
91			.runtime_api()
92			.has_api::<dyn CreateTransactionApi<
93				T::Block,
94				<T::Runtime as frame_system::Config>::RuntimeCall,
95				<T::Runtime as frame_system::Config>::AccountId,
96			>>(at)
97			.map_err(|e| {
98				ErrorObjectOwned::owned::<&str>(
99					1000,
100					format!("failed read runtime api: {e:?}"),
101					None,
102				)
103			})?;
104
105		let ext = if has_api {
106			let call = codec::Decode::decode(&mut &call.0[..]).map_err(|e| {
107				ErrorObjectOwned::owned::<&str>(1001, format!("failed to decode call: {e:?}"), None)
108			})?;
109			let account = AccountId32::from_string(&account).map_err(|e| {
110				ErrorObjectOwned::owned::<&str>(
111					1002,
112					format!("failed to decode account: {e:?}"),
113					None,
114				)
115			})?;
116			self.client
117				.runtime_api()
118				.create_transaction(at, account.into(), call)
119				.map_err(|e| {
120					ErrorObjectOwned::owned::<&str>(
121						1003,
122						format!("CreateTransactionApi is unimplemented: {e:?}"),
123						None,
124					)
125				})?
126		} else {
127			let call = codec::Decode::decode(&mut &call.0[..]).map_err(|e| {
128				ErrorObjectOwned::owned::<&str>(1004, format!("failed to decode call: {e:?}"), None)
129			})?;
130			let account = AccountId32::from_string(&account).map_err(|e| {
131				ErrorObjectOwned::owned::<&str>(
132					1005,
133					format!("failed to decode account: {e:?}"),
134					None,
135				)
136			})?;
137			let extra = self.with_state(None, || T::signed_extras(account.clone().into()));
138			let ext = UncheckedExtrinsicFor::<T>::new_signed(
139				call,
140				MultiAddress::Id(account.into()),
141				MultiSignature::Sr25519(sp_core::sr25519::Signature::from_raw([0u8; 64])),
142				extra,
143			);
144			ext.encode()
145		};
146
147		Ok(ext)
148	}
149
150	/// Runs the given closure in an externalities provided environment, over the blockchain state
151	pub fn with_state<R>(
152		&self,
153		id: Option<<T::Block as BlockT>::Hash>,
154		closure: impl FnOnce() -> R,
155	) -> R {
156		with_state::<T, R>(self.client.clone(), id, closure)
157	}
158
159	fn revert_blocks(&self, n: u32) -> RpcResult<()> {
160		self.backend.revert(n.into(), true).map_err(|e| {
161			ErrorObjectOwned::owned::<&str>(2050, format!("failed to revert blocks: {e:?}"), None)
162		})?;
163
164		Ok(())
165	}
166}
167
168#[async_trait]
169impl<T> SimnodeApiServer for SimnodeRpcHandler<T>
170where
171	T: ChainInfo + Send + Sync + 'static,
172	<T::RuntimeApi as ConstructRuntimeApi<T::Block, FullClientFor<T>>>::RuntimeApi:
173		CreateTransactionApi<
174			T::Block,
175			<T::Runtime as frame_system::Config>::RuntimeCall,
176			<T::Runtime as frame_system::Config>::AccountId,
177		>,
178	<T::Runtime as frame_system::Config>::AccountId: From<AccountId32>,
179	<<T::Block as BlockT>::Header as Header>::Number: num_traits::cast::AsPrimitive<u32>,
180{
181	fn author_extrinsic(&self, call: Bytes, account: String) -> RpcResult<Bytes> {
182		Ok(self.author_extrinsic(call, account)?.into())
183	}
184
185	fn revert_blocks(&self, n: u32) -> RpcResult<()> {
186		self.revert_blocks(n)
187	}
188
189	async fn upgrade_signal(&self, _go_ahead: bool) -> RpcResult<()> {
190		Err(ErrorObjectOwned::owned::<&str>(
191			3050,
192			format!("standalone runtimes don't need permission to upgrade their runtime"),
193			None,
194		))
195	}
196}