pallet_msa_rpc/
lib.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
9//! Custom APIs for [MSA](../pallet_msa/index.html)
10
11use common_helpers::rpc::map_rpc_result;
12use common_primitives::{
13	msa::{
14		DelegationResponse, DelegatorId, KeyInfoResponse, MessageSourceId, ProviderId, SchemaGrant,
15	},
16	node::BlockNumber,
17	offchain::get_msa_account_storage_key_name,
18	schema::SchemaId,
19};
20use jsonrpsee::{
21	core::{async_trait, RpcResult},
22	proc_macros::rpc,
23	tracing::warn,
24	types::{error::ErrorObjectOwned, ErrorObject},
25};
26use pallet_msa_runtime_api::MsaRuntimeApi;
27use parity_scale_codec::{Codec, Decode, Encode};
28use parking_lot::RwLock;
29use rayon::prelude::*;
30use sp_api::ProvideRuntimeApi;
31use sp_blockchain::HeaderBackend;
32use sp_core::{bytes::from_hex, sr25519, Bytes};
33use sp_runtime::traits::{Block as BlockT, Verify};
34use std::sync::Arc;
35
36#[cfg(test)]
37mod tests;
38
39/// A key used to sign the payloads for offchain key overrides
40static AUTH_PUBLIC_KEY: &str = "0x90aa6dfa192c0999ea47397c137507a4f11d45371c54bd5cdbba6f13da24b416";
41
42/// Frequency MSA Custom RPC API
43#[rpc(client, server)]
44pub trait MsaApi<BlockHash, AccountId> {
45	/// Check for a list of delegations
46	/// Given a single provider, test a list of potential delegators
47	/// At a given block number
48	#[method(name = "msa_checkDelegations")]
49	fn check_delegations(
50		&self,
51		delegator_msa_ids: Vec<DelegatorId>,
52		provider_msa_id: ProviderId,
53		block_number: BlockNumber,
54		schema_id: Option<SchemaId>,
55	) -> RpcResult<Vec<(DelegatorId, bool)>>;
56
57	/// Retrieve the list of currently granted schemas given a delegator and provider pair
58	#[method(name = "msa_grantedSchemaIdsByMsaId")]
59	fn get_granted_schemas_by_msa_id(
60		&self,
61		delegator_msa_id: DelegatorId,
62		provider_msa_id: ProviderId,
63	) -> RpcResult<Option<Vec<SchemaGrant<SchemaId, BlockNumber>>>>;
64
65	/// Retrieve the list of all delegations for a MsaId
66	#[method(name = "msa_getAllGrantedDelegationsByMsaId")]
67	fn get_all_granted_delegations_by_msa_id(
68		&self,
69		delegator_msa_id: DelegatorId,
70	) -> RpcResult<Vec<DelegationResponse<SchemaId, BlockNumber>>>;
71
72	/// Retrieve the list of keys for msa id
73	#[method(name = "msa_getKeysByMsaId")]
74	fn get_keys_by_msa_id(
75		&self,
76		msa_id: MessageSourceId,
77	) -> RpcResult<Option<KeyInfoResponse<AccountId>>>;
78
79	/// Set the list of keys for msa id
80	#[method(name = "msa_setKeysByMsaId")]
81	fn set_keys_by_msa_id(
82		&self,
83		msa_id: MessageSourceId,
84		public_keys: Vec<AccountId>,
85		signature_hex: String,
86	) -> RpcResult<()>;
87}
88
89/// The client handler for the API used by Frequency Service RPC with `jsonrpsee`
90pub struct MsaHandler<C, M, OffchainDB> {
91	client: Arc<C>,
92	offchain: Arc<RwLock<Option<OffchainDB>>>,
93	_marker: std::marker::PhantomData<M>,
94}
95
96impl<C, M, OffchainDB> MsaHandler<C, M, OffchainDB>
97where
98	OffchainDB: Send + Sync,
99{
100	/// Create new instance with the given reference to the client.
101	pub fn new(client: Arc<C>, offchain: Option<OffchainDB>) -> Self {
102		Self { client, offchain: Arc::new(RwLock::new(offchain)), _marker: Default::default() }
103	}
104}
105
106/// Errors that occur on the client RPC
107#[derive(Debug)]
108pub enum MsaOffchainRpcError {
109	/// Error acquiring lock
110	ErrorAcquiringLock,
111	/// Error decoding data
112	ErrorDecodingData,
113	/// Offchain indexing is not enabled
114	OffchainIndexingNotEnabled,
115	/// Invalid signature
116	InvalidSignature,
117}
118
119impl From<MsaOffchainRpcError> for ErrorObjectOwned {
120	fn from(e: MsaOffchainRpcError) -> Self {
121		let msg = format!("{e:?}");
122
123		match e {
124			MsaOffchainRpcError::ErrorAcquiringLock => ErrorObject::owned(1, msg, None::<()>),
125			MsaOffchainRpcError::ErrorDecodingData => ErrorObject::owned(2, msg, None::<()>),
126			MsaOffchainRpcError::OffchainIndexingNotEnabled =>
127				ErrorObject::owned(3, msg, None::<()>),
128			MsaOffchainRpcError::InvalidSignature => ErrorObject::owned(4, msg, None::<()>),
129		}
130	}
131}
132
133#[async_trait]
134impl<C, Block, OffchainDB, AccountId> MsaApiServer<<Block as BlockT>::Hash, AccountId>
135	for MsaHandler<C, Block, OffchainDB>
136where
137	Block: BlockT,
138	C: Send + Sync + 'static,
139	C: ProvideRuntimeApi<Block>,
140	C: HeaderBackend<Block>,
141	C::Api: MsaRuntimeApi<Block, AccountId>,
142	AccountId: Codec,
143	OffchainDB: sp_core::offchain::OffchainStorage + 'static,
144{
145	fn check_delegations(
146		&self,
147		delegator_msa_ids: Vec<DelegatorId>,
148		provider_msa_id: ProviderId,
149		block_number: BlockNumber,
150		schema_id: Option<SchemaId>,
151	) -> RpcResult<Vec<(DelegatorId, bool)>> {
152		let at = self.client.info().best_hash;
153		let results = delegator_msa_ids
154			.par_iter()
155			.map(|delegator_msa_id| {
156				let api = self.client.runtime_api();
157				// api.has_delegation returns  Result<bool, ApiError>), so _or(false) should not happen,
158				// but just in case, protect against panic
159				let has_delegation: bool = match api.has_delegation(
160					at,
161					*delegator_msa_id,
162					provider_msa_id,
163					block_number,
164					schema_id,
165				) {
166					Ok(result) => result,
167					Err(e) => {
168						warn!("ApiError from has_delegation! {:?}", e);
169						false
170					},
171				};
172				(*delegator_msa_id, has_delegation)
173			})
174			.collect();
175		Ok(results)
176	}
177
178	fn get_granted_schemas_by_msa_id(
179		&self,
180		delegator_msa_id: DelegatorId,
181		provider_msa_id: ProviderId,
182	) -> RpcResult<Option<Vec<SchemaGrant<SchemaId, BlockNumber>>>> {
183		let api = self.client.runtime_api();
184		let at = self.client.info().best_hash;
185		let runtime_api_result =
186			api.get_granted_schemas_by_msa_id(at, delegator_msa_id, provider_msa_id);
187		map_rpc_result(runtime_api_result)
188	}
189
190	fn get_all_granted_delegations_by_msa_id(
191		&self,
192		delegator_msa_id: DelegatorId,
193	) -> RpcResult<Vec<DelegationResponse<SchemaId, BlockNumber>>> {
194		let api = self.client.runtime_api();
195		let at = self.client.info().best_hash;
196		let runtime_api_result = api.get_all_granted_delegations_by_msa_id(at, delegator_msa_id);
197		map_rpc_result(runtime_api_result)
198	}
199
200	fn get_keys_by_msa_id(
201		&self,
202		msa_id: MessageSourceId,
203	) -> RpcResult<Option<KeyInfoResponse<AccountId>>> {
204		let msa_key = get_msa_account_storage_key_name(msa_id);
205		let reader = self.offchain.try_read().ok_or(MsaOffchainRpcError::ErrorAcquiringLock)?;
206		let raw: Option<Bytes> = reader
207			.as_ref()
208			.ok_or(MsaOffchainRpcError::OffchainIndexingNotEnabled)?
209			.get(sp_offchain::STORAGE_PREFIX, &msa_key)
210			.map(Into::into);
211		if let Some(rr) = raw {
212			let inside = rr.0;
213			let keys = Vec::<AccountId>::decode(&mut &inside[..])
214				.map_err(|_| MsaOffchainRpcError::ErrorDecodingData)?;
215			return Ok(Some(KeyInfoResponse { msa_id, msa_keys: keys }))
216		}
217		Ok(None)
218	}
219
220	fn set_keys_by_msa_id(
221		&self,
222		msa_id: MessageSourceId,
223		public_keys: Vec<AccountId>,
224		signature_hex: String,
225	) -> RpcResult<()> {
226		let sig = sr25519::Signature::from_raw(
227			from_hex(&signature_hex)
228				.map_err(|_| MsaOffchainRpcError::ErrorDecodingData)?
229				.try_into()
230				.map_err(|_| MsaOffchainRpcError::ErrorDecodingData)?,
231		);
232		let auth_key = from_hex(AUTH_PUBLIC_KEY)
233			.map_err(|_| MsaOffchainRpcError::ErrorDecodingData)?
234			.try_into()
235			.map_err(|_| MsaOffchainRpcError::ErrorDecodingData)?;
236		let auth_key = sr25519::Public::from_raw(auth_key);
237
238		let payload = (msa_id, &public_keys).encode();
239
240		if sig.verify(&payload[..], &auth_key) {
241			let msa_key = get_msa_account_storage_key_name(msa_id);
242			let mut writer =
243				self.offchain.try_write().ok_or(MsaOffchainRpcError::ErrorAcquiringLock)?;
244			writer.as_mut().ok_or(MsaOffchainRpcError::OffchainIndexingNotEnabled)?.set(
245				sp_offchain::STORAGE_PREFIX,
246				&msa_key,
247				&public_keys.encode(),
248			);
249			return Ok(())
250		}
251
252		Err(MsaOffchainRpcError::InvalidSignature.into())
253	}
254}