pallet_msa/
types.rs

1//! Types for the MSA Pallet
2#![cfg_attr(not(feature = "std"), no_std)]
3
4use super::*;
5use parity_scale_codec::{Decode, Encode};
6
7use core::fmt::Debug;
8
9pub use common_primitives::msa::{
10	Delegation, DelegatorId, KeyInfoResponse, MessageSourceId, ProviderId,
11};
12use common_primitives::{node::BlockNumber, schema::SchemaId};
13
14use common_primitives::{
15	signatures::{get_eip712_encoding_prefix, AccountAddressMapper, EthereumAddressMapper},
16	utils::to_abi_compatible_number,
17};
18use scale_info::TypeInfo;
19use sp_core::U256;
20
21/// Dispatch Empty
22pub const EMPTY_FUNCTION: fn(MessageSourceId) -> DispatchResult = |_| Ok(());
23
24/// A type definition for the payload for the following operation:
25/// -  Adding an MSA key - `pallet_msa::add_public_key_to_msa`
26#[derive(
27	TypeInfo, RuntimeDebugNoBound, Clone, Decode, DecodeWithMemTracking, Encode, PartialEq, Eq,
28)]
29#[scale_info(skip_type_params(T))]
30pub struct AddKeyData<T: Config> {
31	/// Message Source Account identifier
32	pub msa_id: MessageSourceId,
33	/// The block number at which a signed proof of this payload expires.
34	pub expiration: BlockNumberFor<T>,
35	/// The public key to be added.
36	pub new_public_key: T::AccountId,
37}
38
39impl<T: Config> EIP712Encode for AddKeyData<T> {
40	fn encode_eip_712(&self, chain_id: u32) -> Box<[u8]> {
41		lazy_static! {
42			// signed payload
43			static ref MAIN_TYPE_HASH: [u8; 32] = sp_io::hashing::keccak_256(
44				b"AddKeyData(uint64 msaId,uint32 expiration,address newPublicKey)",
45			);
46		}
47		// get prefix and domain separator
48		let prefix_domain_separator: Box<[u8]> =
49			get_eip712_encoding_prefix("0xcccccccccccccccccccccccccccccccccccccccc", chain_id);
50		let coded_owner_msa_id = to_abi_compatible_number(self.msa_id);
51		let expiration: U256 = self.expiration.into();
52		let coded_expiration = to_abi_compatible_number(expiration.as_u128());
53		let converted_public_key = T::ConvertIntoAccountId32::convert(self.new_public_key.clone());
54		let mut zero_prefixed_address = [0u8; 32];
55		zero_prefixed_address[12..]
56			.copy_from_slice(&EthereumAddressMapper::to_ethereum_address(converted_public_key).0);
57		let message = sp_io::hashing::keccak_256(
58			&[
59				MAIN_TYPE_HASH.as_slice(),
60				&coded_owner_msa_id,
61				&coded_expiration,
62				&zero_prefixed_address,
63			]
64			.concat(),
65		);
66		let combined = [prefix_domain_separator.as_ref(), &message].concat();
67		combined.into_boxed_slice()
68	}
69}
70
71/// Type discriminator enum for signed payloads
72#[derive(
73	Encode, Decode, DecodeWithMemTracking, Clone, Copy, PartialEq, Eq, RuntimeDebugNoBound, TypeInfo,
74)]
75pub enum PayloadTypeDiscriminator {
76	/// Unknown payload type
77	Unknown,
78	/// AuthorizedKeyData discriminator
79	AuthorizedKeyData,
80	/// RecoverCommitmentPayload discriminator
81	RecoveryCommitmentPayload,
82}
83
84/// A type definition for the payload for authorizing a public key for the following operations:
85/// -  Authorizing a token withdrawal to an address associated with a public key - `pallet_msa::withdraw_tokens`
86#[derive(
87	TypeInfo, RuntimeDebugNoBound, Clone, Decode, DecodeWithMemTracking, Encode, PartialEq, Eq,
88)]
89#[scale_info(skip_type_params(T))]
90pub struct AuthorizedKeyData<T: Config> {
91	/// type discriminator, because otherwise this struct has the same SCALE encoding as `AddKeyData`
92	pub discriminant: PayloadTypeDiscriminator,
93	/// Message Source Account identifier
94	pub msa_id: MessageSourceId,
95	/// The block number at which a signed proof of this payload expires.
96	pub expiration: BlockNumberFor<T>,
97	/// The public key to be added that is authorized as the target of the current operation
98	pub authorized_public_key: T::AccountId,
99}
100
101impl<T: Config> EIP712Encode for AuthorizedKeyData<T> {
102	fn encode_eip_712(&self, chain_id: u32) -> Box<[u8]> {
103		lazy_static! {
104			// signed payload
105			static ref MAIN_TYPE_HASH: [u8; 32] = sp_io::hashing::keccak_256(
106				b"AuthorizedKeyData(uint64 msaId,uint32 expiration,address authorizedPublicKey)",
107			);
108		}
109		// get prefix and domain separator
110		let prefix_domain_separator: Box<[u8]> =
111			get_eip712_encoding_prefix("0xcccccccccccccccccccccccccccccccccccccccc", chain_id);
112		let coded_owner_msa_id = to_abi_compatible_number(self.msa_id);
113		let expiration: U256 = self.expiration.into();
114		let coded_expiration = to_abi_compatible_number(expiration.as_u128());
115		let converted_public_key =
116			T::ConvertIntoAccountId32::convert(self.authorized_public_key.clone());
117		let mut zero_prefixed_address = [0u8; 32];
118		zero_prefixed_address[12..]
119			.copy_from_slice(&EthereumAddressMapper::to_ethereum_address(converted_public_key).0);
120		let message = sp_io::hashing::keccak_256(
121			&[
122				MAIN_TYPE_HASH.as_slice(),
123				&coded_owner_msa_id,
124				&coded_expiration,
125				&zero_prefixed_address,
126			]
127			.concat(),
128		);
129		let combined = [prefix_domain_separator.as_ref(), &message].concat();
130		combined.into_boxed_slice()
131	}
132}
133
134/// Structure that is signed for granting permissions to a Provider
135#[derive(TypeInfo, Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, Eq)]
136pub struct AddProvider {
137	/// The provider being granted permissions
138	pub authorized_msa_id: MessageSourceId,
139	/// Schemas for which publishing grants are authorized.
140	/// This is private intended for internal use only.
141	pub schema_ids: Vec<SchemaId>,
142	/// The block number at which the proof for grant_delegation expires.
143	pub expiration: BlockNumber,
144}
145
146impl EIP712Encode for AddProvider {
147	fn encode_eip_712(&self, chain_id: u32) -> Box<[u8]> {
148		lazy_static! {
149			// signed payload
150			static ref MAIN_TYPE_HASH: [u8; 32] = sp_io::hashing::keccak_256(
151				b"AddProvider(uint64 authorizedMsaId,uint16[] schemaIds,uint32 expiration)"
152			);
153		}
154		// get prefix and domain separator
155		let prefix_domain_separator: Box<[u8]> =
156			get_eip712_encoding_prefix("0xcccccccccccccccccccccccccccccccccccccccc", chain_id);
157		let coded_authorized_msa_id = to_abi_compatible_number(self.authorized_msa_id);
158		let schema_ids: Vec<u8> = self
159			.schema_ids
160			.iter()
161			.flat_map(|schema_id| to_abi_compatible_number(*schema_id))
162			.collect();
163		let schema_ids = sp_io::hashing::keccak_256(&schema_ids);
164		let coded_expiration = to_abi_compatible_number(self.expiration);
165		let message = sp_io::hashing::keccak_256(
166			&[MAIN_TYPE_HASH.as_slice(), &coded_authorized_msa_id, &schema_ids, &coded_expiration]
167				.concat(),
168		);
169		let combined = [prefix_domain_separator.as_ref(), &message].concat();
170		combined.into_boxed_slice()
171	}
172}
173
174impl AddProvider {
175	/// Create new `AddProvider`
176	pub fn new(
177		authorized_msa_id: MessageSourceId,
178		schema_ids: Option<Vec<SchemaId>>,
179		expiration: BlockNumber,
180	) -> Self {
181		let schema_ids = schema_ids.unwrap_or_default();
182
183		Self { authorized_msa_id, schema_ids, expiration }
184	}
185}
186
187/// A type definition for hash types used in the MSA Recovery System.
188pub type RecoveryHash = [u8; 32]; // 32 bytes for
189
190/// A type definition for the Recovery Commitment data for the following operation:
191/// -  Adding a Recovery Commitment - `pallet_msa::add_recovery_commitment
192pub type RecoveryCommitment = RecoveryHash; // 32 bytes for the recovery commitment
193
194/// A type definition for the payload for the Recovery Commitment operation:
195/// -  Adding a Recovery Commitment - `pallet_msa::add_recovery_commitment`
196#[derive(
197	TypeInfo, RuntimeDebugNoBound, Clone, Decode, DecodeWithMemTracking, Encode, PartialEq, Eq,
198)]
199#[scale_info(skip_type_params(T))]
200pub struct RecoveryCommitmentPayload<T: Config> {
201	/// type discriminator
202	pub discriminant: PayloadTypeDiscriminator,
203	/// The Recovery Commitment (32 bytes)
204	pub recovery_commitment: RecoveryCommitment,
205	/// The block number at which a signed proof of this payload expires.
206	pub expiration: BlockNumberFor<T>,
207}
208
209impl<T: Config> EIP712Encode for RecoveryCommitmentPayload<T> {
210	fn encode_eip_712(&self, chain_id: u32) -> Box<[u8]> {
211		lazy_static! {
212			// signed payload
213			static ref MAIN_TYPE_HASH: [u8; 32] = sp_io::hashing::keccak_256(
214				b"RecoveryCommitmentPayload(bytes recoveryCommitment,uint32 expiration)",
215			);
216		}
217		// get prefix and domain separator
218		let prefix_domain_separator: Box<[u8]> =
219			get_eip712_encoding_prefix("0xcccccccccccccccccccccccccccccccccccccccc", chain_id);
220		let hashed_recovery = sp_io::hashing::keccak_256(&self.recovery_commitment);
221		let expiration: U256 = self.expiration.into();
222		let coded_expiration = to_abi_compatible_number(expiration.as_u128());
223		let message = sp_io::hashing::keccak_256(
224			&[MAIN_TYPE_HASH.as_slice(), hashed_recovery.as_slice(), &coded_expiration].concat(),
225		);
226		let combined = [prefix_domain_separator.as_ref(), &message].concat();
227		combined.into_boxed_slice()
228	}
229}
230
231/// The interface for mutating schemas permissions in a delegation relationship.
232pub trait PermittedDelegationSchemas<T: Config> {
233	/// Attempt to insert a new schema. Dispatches error when the max allowed schemas are exceeded.
234	fn try_insert_schema(&mut self, schema_id: SchemaId) -> Result<(), DispatchError>;
235
236	/// Attempt to insert a collection of schemas. Dispatches error when the max allowed schemas are exceeded.
237	fn try_insert_schemas(&mut self, schema_ids: Vec<SchemaId>) -> Result<(), DispatchError> {
238		for schema_id in schema_ids.into_iter() {
239			self.try_insert_schema(schema_id)?;
240		}
241
242		Ok(())
243	}
244
245	/// Attempt get and mutate a collection of schemas. Dispatches error when a schema cannot be found.
246	fn try_get_mut_schemas(
247		&mut self,
248		schema_ids: Vec<SchemaId>,
249		block_number: BlockNumberFor<T>,
250	) -> Result<(), DispatchError> {
251		for schema_id in schema_ids.into_iter() {
252			self.try_get_mut_schema(schema_id, block_number)?;
253		}
254		Ok(())
255	}
256
257	/// Attempt get and mutate a schema. Dispatches error when a schema cannot be found.
258	fn try_get_mut_schema(
259		&mut self,
260		schema_id: SchemaId,
261		block_number: BlockNumberFor<T>,
262	) -> Result<(), DispatchError>;
263}
264
265/// Implementation of SchemaPermission trait on Delegation type.
266impl<T: Config> PermittedDelegationSchemas<T>
267	for Delegation<SchemaId, BlockNumberFor<T>, T::MaxSchemaGrantsPerDelegation>
268{
269	/// Attempt to insert a new schema. Dispatches error when the max allowed schemas are exceeded.
270	fn try_insert_schema(&mut self, schema_id: SchemaId) -> Result<(), DispatchError> {
271		self.schema_permissions
272			.try_insert(schema_id, Default::default())
273			.map_err(|_| Error::<T>::ExceedsMaxSchemaGrantsPerDelegation)?;
274		Ok(())
275	}
276
277	/// Attempt get and mutate a schema. Dispatches error when a schema cannot be found.
278	fn try_get_mut_schema(
279		&mut self,
280		schema_id: SchemaId,
281		block_number: BlockNumberFor<T>,
282	) -> Result<(), DispatchError> {
283		let schema = self
284			.schema_permissions
285			.get_mut(&schema_id)
286			.ok_or(Error::<T>::SchemaNotGranted)?;
287
288		*schema = block_number;
289
290		Ok(())
291	}
292}