1use 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#[rpc(client, server)]
46pub trait SimnodeApi {
47 #[method(name = "simnode_authorExtrinsic")]
50 fn author_extrinsic(&self, call: Bytes, account: String) -> RpcResult<Bytes>;
51
52 #[method(name = "simnode_revertBlocks")]
54 fn revert_blocks(&self, n: u32) -> RpcResult<()>;
55
56 #[method(name = "simnode_upgradeSignal")]
59 async fn upgrade_signal(&self, go_ahead: bool) -> RpcResult<()>;
60}
61
62pub struct SimnodeRpcHandler<T: ChainInfo> {
64 client: Arc<FullClientFor<T>>,
66 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 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 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}