pallet_msa/
lib.rs

1//! Message Source Account Management
2//!
3//! ## Quick Links
4//! - [Configuration: `Config`](Config)
5//! - [Extrinsics: `Call`](Call)
6//! - [Runtime API: `MsaRuntimeApi`](../pallet_msa_runtime_api/trait.MsaRuntimeApi.html)
7//! - [Custom RPC API: `MsaApiServer`](../pallet_msa_rpc/trait.MsaApiServer.html)
8//! - [Event Enum: `Event`](Event)
9//! - [Error Enum: `Error`](Error)
10#![doc = include_str!("../README.md")]
11//!
12//! ## Implementations
13//!
14//! - [`MsaLookup`](../common_primitives/msa/trait.MsaLookup.html): Functions for accessing MSAs.
15//! - [`MsaValidator`](../common_primitives/msa/trait.MsaValidator.html): Functions for validating MSAs.
16//! - [`ProviderLookup`](../common_primitives/msa/trait.ProviderLookup.html): Functions for accessing Provider info.
17//! - [`DelegationValidator`](../common_primitives/msa/trait.DelegationValidator.html): Functions for validating delegations.
18//! - [`SchemaGrantValidator`](../common_primitives/msa/trait.SchemaGrantValidator.html): Functions for validating schema grants.
19//!
20//! ## Assumptions
21//!
22//! - Total MSA keys should be less than the constant `Config::MSA::MaxPublicKeysPerMsa`.
23//! - Maximum schemas, for which any single provider has publishing rights on behalf of a single user, must be less than `Config::MSA::MaxSchemaGrantsPerDelegation`
24#![allow(clippy::expect_used)]
25#![cfg_attr(not(feature = "std"), no_std)]
26// Strong Documentation Lints
27#![deny(
28	rustdoc::broken_intra_doc_links,
29	rustdoc::missing_crate_level_docs,
30	rustdoc::invalid_codeblock_attributes,
31	missing_docs
32)]
33
34extern crate alloc;
35use frame_support::{
36	dispatch::{DispatchInfo, DispatchResult, PostDispatchInfo},
37	pallet_prelude::*,
38	traits::{
39		tokens::{
40			fungible::{Inspect as InspectFungible, Mutate},
41			Fortitude, Preservation,
42		},
43		IsSubType,
44	},
45};
46use lazy_static::lazy_static;
47use parity_scale_codec::{Decode, Encode};
48
49use common_runtime::signature::check_signature;
50
51#[cfg(feature = "runtime-benchmarks")]
52use common_primitives::benchmarks::{MsaBenchmarkHelper, RegisterProviderBenchmarkHelper};
53
54use alloc::{boxed::Box, vec, vec::Vec};
55use common_primitives::{
56	capacity::TargetValidator,
57	handles::HandleProvider,
58	msa::*,
59	node::{EIP712Encode, ProposalProvider},
60	schema::{SchemaId, SchemaValidator},
61	signatures::{AccountAddressMapper, EthereumAddressMapper},
62};
63use frame_system::pallet_prelude::*;
64use scale_info::TypeInfo;
65use sp_core::crypto::AccountId32;
66use sp_io::hashing::keccak_256;
67#[allow(deprecated)]
68#[allow(unused)]
69use sp_runtime::{
70	traits::{
71		AsSystemOriginSigner, BlockNumberProvider, Convert, DispatchInfoOf, Dispatchable,
72		PostDispatchInfoOf, TransactionExtension, ValidateResult, Zero,
73	},
74	ArithmeticError, DispatchError, MultiSignature, Weight,
75};
76
77pub use pallet::*;
78pub use types::{
79	AddKeyData, AddProvider, AuthorizedKeyData, PermittedDelegationSchemas, RecoveryCommitment,
80	RecoveryCommitmentPayload, EMPTY_FUNCTION,
81};
82pub use weights::*;
83
84/// Offchain storage for MSA pallet
85pub mod offchain_storage;
86use crate::types::{PayloadTypeDiscriminator, RecoveryHash};
87pub use offchain_storage::*;
88
89#[cfg(feature = "runtime-benchmarks")]
90mod benchmarking;
91
92#[cfg(test)]
93mod tests;
94
95pub mod types;
96
97pub mod weights;
98
99#[frame_support::pallet]
100pub mod pallet {
101	use crate::types::RecoveryHash;
102
103	use super::*;
104
105	#[pallet::config]
106	pub trait Config: frame_system::Config {
107		/// The overarching event type.
108		#[allow(deprecated)]
109		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
110
111		/// Weight information for extrinsics in this pallet.
112		type WeightInfo: WeightInfo;
113
114		/// AccountId truncated to 32 bytes
115		type ConvertIntoAccountId32: Convert<Self::AccountId, AccountId32>;
116
117		/// Maximum count of keys allowed per MSA
118		#[pallet::constant]
119		type MaxPublicKeysPerMsa: Get<u8>;
120
121		/// Maximum count of schemas granted for publishing data per Provider
122		#[pallet::constant]
123		type MaxSchemaGrantsPerDelegation: Get<u32>;
124
125		/// Maximum provider name size allowed per MSA association
126		#[pallet::constant]
127		type MaxProviderNameSize: Get<u32>;
128
129		/// A type that will supply schema related information.
130		type SchemaValidator: SchemaValidator<SchemaId>;
131
132		/// A type that will supply `Handle` related information.
133		type HandleProvider: HandleProvider;
134
135		/// The number of blocks before a signature can be ejected from the PayloadSignatureRegistryList
136		#[pallet::constant]
137		type MortalityWindowSize: Get<u32>;
138
139		/// The maximum number of signatures that can be stored in PayloadSignatureRegistryList.
140		#[pallet::constant]
141		type MaxSignaturesStored: Get<Option<u32>>;
142
143		/// The origin that is allowed to create providers via governance
144		type CreateProviderViaGovernanceOrigin: EnsureOrigin<Self::RuntimeOrigin>;
145
146		/// The origin that is allowed to approve recovery providers
147		type RecoveryProviderApprovalOrigin: EnsureOrigin<Self::RuntimeOrigin>;
148
149		/// The runtime call dispatch type.
150		type Proposal: Parameter
151			+ Dispatchable<RuntimeOrigin = Self::RuntimeOrigin, PostInfo = PostDispatchInfo>
152			+ From<Call<Self>>;
153
154		/// The Council proposal provider interface
155		type ProposalProvider: ProposalProvider<Self::AccountId, Self::Proposal>;
156
157		/// Currency type for managing MSA account balances
158		type Currency: Mutate<Self::AccountId> + InspectFungible<Self::AccountId>;
159	}
160
161	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
162
163	#[pallet::pallet]
164	#[pallet::storage_version(STORAGE_VERSION)]
165	pub struct Pallet<T>(_);
166
167	/// Storage type for the current MSA identifier maximum.
168	/// We need to track this value because the identifier maximum
169	/// is incremented each time a new identifier is created.
170	/// - Value: The current maximum MSA Id
171	#[pallet::storage]
172	pub type CurrentMsaIdentifierMaximum<T> = StorageValue<_, MessageSourceId, ValueQuery>;
173
174	/// Storage type for mapping the relationship between a Delegator and its Provider.
175	/// - Keys: Delegator MSA, Provider MSA
176	/// - Value: [`Delegation`](common_primitives::msa::Delegation)
177	#[pallet::storage]
178	pub type DelegatorAndProviderToDelegation<T: Config> = StorageDoubleMap<
179		_,
180		Twox64Concat,
181		DelegatorId,
182		Twox64Concat,
183		ProviderId,
184		Delegation<SchemaId, BlockNumberFor<T>, T::MaxSchemaGrantsPerDelegation>,
185		OptionQuery,
186	>;
187
188	/// Storage type for approved recovery providers
189	/// - Key: Provider MSA Id
190	/// - Value: [`bool`]
191	#[pallet::storage]
192	pub type RecoveryProviders<T: Config> =
193		StorageMap<_, Twox64Concat, ProviderId, bool, OptionQuery>;
194
195	/// Provider registration information
196	/// - Key: Provider MSA Id
197	/// - Value: [`ProviderRegistryEntry`](common_primitives::msa::ProviderRegistryEntry)
198	#[pallet::storage]
199	pub type ProviderToRegistryEntry<T: Config> = StorageMap<
200		_,
201		Twox64Concat,
202		ProviderId,
203		ProviderRegistryEntry<T::MaxProviderNameSize>,
204		OptionQuery,
205	>;
206
207	/// Storage type for key to MSA information
208	/// - Key: AccountId
209	/// - Value: [`MessageSourceId`]
210	#[pallet::storage]
211	pub type PublicKeyToMsaId<T: Config> =
212		StorageMap<_, Twox64Concat, T::AccountId, MessageSourceId, OptionQuery>;
213
214	/// Storage type for a reference counter of the number of keys associated to an MSA
215	/// - Key: MSA Id
216	/// - Value: [`u8`] Counter of Keys associated with the MSA
217	#[pallet::storage]
218	pub(super) type PublicKeyCountForMsaId<T: Config> =
219		StorageMap<_, Twox64Concat, MessageSourceId, u8, ValueQuery>;
220
221	/// PayloadSignatureRegistryList is used to prevent replay attacks for extrinsics
222	/// that take an externally-signed payload.
223	/// For this to work, the payload must include a mortality block number, which
224	/// is used in lieu of a monotonically increasing nonce.
225	///
226	/// The list is forwardly linked. (Example has a list size of 3)
227	/// - signature, forward pointer -> n = new signature
228	/// - 1,2 -> 2,3 (oldest)
229	/// - 2,3 -> 3,4
230	/// - 3,4 -> 4,5
231	/// -   5 -> n (newest in pointer data)
232	/// ### Storage
233	/// - Key: Signature
234	/// - Value: Tuple
235	///     - `BlockNumber` when the keyed signature can be ejected from the registry
236	///     - [`MultiSignature`] the forward linked list pointer. This pointer is the next "newest" value.
237	#[pallet::storage]
238	pub(super) type PayloadSignatureRegistryList<T: Config> = StorageMap<
239		_,                                   // prefix
240		Twox64Concat,                        // hasher for key1
241		MultiSignature, // An externally-created Signature for an external payload, provided by an extrinsic
242		(BlockNumberFor<T>, MultiSignature), // An actual flipping block number and the oldest signature at write time
243		OptionQuery,                         // The type for the query
244		GetDefault,                          // OnEmpty return type, defaults to None
245		T::MaxSignaturesStored,              // Maximum total signatures to store
246	>;
247
248	/// This is the pointer for the Payload Signature Registry
249	/// Contains the pointers to the data stored in the `PayloadSignatureRegistryList`
250	/// - Value: [`SignatureRegistryPointer`]
251	#[pallet::storage]
252	pub(super) type PayloadSignatureRegistryPointer<T: Config> =
253		StorageValue<_, SignatureRegistryPointer<BlockNumberFor<T>>>;
254
255	/// A temporary storage for looking up relevant events from off-chain index
256	/// At the start of the next block this storage is set to 0
257	#[pallet::storage]
258	#[pallet::whitelist_storage]
259	pub(super) type OffchainIndexEventCount<T: Config> = StorageValue<_, u16, ValueQuery>;
260
261	/// Storage type for mapping MSA IDs to their Recovery Commitments
262	/// - Key: MessageSourceId
263	/// - Value: Recovery Commitment (32 bytes)
264	#[pallet::storage]
265	pub type MsaIdToRecoveryCommitment<T: Config> =
266		StorageMap<_, Twox64Concat, MessageSourceId, RecoveryCommitment, OptionQuery>;
267
268	#[pallet::event]
269	#[pallet::generate_deposit(pub (super) fn deposit_event)]
270	pub enum Event<T: Config> {
271		/// A new Message Service Account was created with a new MessageSourceId
272		MsaCreated {
273			/// The MSA for the Event
274			msa_id: MessageSourceId,
275
276			/// The key added to the MSA
277			key: T::AccountId,
278		},
279		/// An AccountId has been associated with a MessageSourceId
280		PublicKeyAdded {
281			/// The MSA for the Event
282			msa_id: MessageSourceId,
283
284			/// The key added to the MSA
285			key: T::AccountId,
286		},
287		/// An AccountId had all permissions revoked from its MessageSourceId
288		PublicKeyDeleted {
289			/// The key no longer approved for the associated MSA
290			key: T::AccountId,
291		},
292		/// A delegation relationship was added with the given provider and delegator
293		DelegationGranted {
294			/// The Provider MSA Id
295			provider_id: ProviderId,
296
297			/// The Delegator MSA Id
298			delegator_id: DelegatorId,
299		},
300		/// A Provider-MSA relationship was registered
301		ProviderCreated {
302			/// The MSA id associated with the provider
303			provider_id: ProviderId,
304		},
305		/// The Delegator revoked its delegation to the Provider
306		DelegationRevoked {
307			/// The Provider MSA Id
308			provider_id: ProviderId,
309
310			/// The Delegator MSA Id
311			delegator_id: DelegatorId,
312		},
313		/// The MSA has been retired.
314		MsaRetired {
315			/// The MSA id for the Event
316			msa_id: MessageSourceId,
317		},
318		/// A an update to the delegation occurred (ex. schema permissions where updated).
319		DelegationUpdated {
320			/// The Provider MSA Id
321			provider_id: ProviderId,
322
323			/// The Delegator MSA Id
324			delegator_id: DelegatorId,
325		},
326		/// A Recovery Commitment was added to an MSA
327		RecoveryCommitmentAdded {
328			/// The MSA owner key that added the commitment
329			who: T::AccountId,
330
331			/// The MSA id for the Event
332			msa_id: MessageSourceId,
333
334			/// The Recovery Commitment that was added
335			recovery_commitment: RecoveryCommitment,
336		},
337		/// A recovery provider has been approved.
338		RecoveryProviderApproved {
339			/// The provider account ID
340			provider_id: ProviderId,
341		},
342		/// A recovery provider has been removed.
343		RecoveryProviderRemoved {
344			/// The provider account ID
345			provider_id: ProviderId,
346		},
347		/// An account was recovered with a new control key
348		AccountRecovered {
349			/// The MSA id that was recovered
350			msa_id: MessageSourceId,
351			/// The recovery provider that performed the recovery
352			recovery_provider: ProviderId,
353			/// The new control key added to the MSA
354			new_control_key: T::AccountId,
355		},
356		/// A Recovery Commitment was invalidated after use or removal
357		RecoveryCommitmentInvalidated {
358			/// The MSA id for which the commitment was invalidated
359			msa_id: MessageSourceId,
360			/// The Recovery Commitment that was invalidated
361			recovery_commitment: RecoveryCommitment,
362		},
363	}
364
365	#[pallet::error]
366	pub enum Error<T> {
367		/// Tried to add a key that was already registered to an MSA
368		KeyAlreadyRegistered,
369
370		/// MsaId values have reached the maximum
371		MsaIdOverflow,
372
373		/// Cryptographic signature verification failed
374		MsaOwnershipInvalidSignature,
375
376		/// Ony the MSA Owner may perform the operation
377		NotMsaOwner,
378
379		/// Cryptographic signature failed verification
380		InvalidSignature,
381
382		/// Only the KeyOwner may perform the operation
383		NotKeyOwner,
384
385		/// An operation was attempted with an unknown Key
386		NoKeyExists,
387
388		/// The number of key values has reached its maximum
389		KeyLimitExceeded,
390
391		/// A transaction's Origin (AccountId) may not remove itself
392		InvalidSelfRemoval,
393
394		/// An MSA may not be its own delegate
395		InvalidSelfProvider,
396
397		/// An invalid schema Id was provided
398		InvalidSchemaId,
399
400		/// The delegation relationship already exists for the given MSA Ids
401		DuplicateProvider,
402
403		/// Cryptographic signature verification failed for adding the Provider as delegate
404		AddProviderSignatureVerificationFailed,
405
406		/// Origin attempted to add a delegate for someone else's MSA
407		UnauthorizedDelegator,
408
409		/// Origin attempted to add a different delegate than what was in the payload
410		UnauthorizedProvider,
411
412		/// The operation was attempted with a revoked delegation
413		DelegationRevoked,
414
415		/// The operation was attempted with an unknown delegation
416		DelegationNotFound,
417
418		/// The MSA id submitted for provider creation has already been associated with a provider
419		DuplicateProviderRegistryEntry,
420
421		/// The maximum length for a provider name has been exceeded
422		ExceedsMaxProviderNameSize,
423
424		/// The maximum number of schema grants has been exceeded
425		ExceedsMaxSchemaGrantsPerDelegation,
426
427		/// Provider is not permitted to publish for given schema_id
428		SchemaNotGranted,
429
430		/// The operation was attempted with a non-provider MSA
431		ProviderNotRegistered,
432
433		/// The submitted proof has expired; the current block is less the expiration block
434		ProofHasExpired,
435
436		/// The submitted proof expiration block is too far in the future
437		ProofNotYetValid,
438
439		/// Attempted to add a signature when the signature is already in the registry
440		SignatureAlreadySubmitted,
441
442		/// Cryptographic signature verification failed for proving ownership of new public-key.
443		NewKeyOwnershipInvalidSignature,
444
445		/// Attempted to request validity of schema permission or delegation in the future.
446		CannotPredictValidityPastCurrentBlock,
447
448		/// Attempted to add a new signature to a full signature registry
449		SignatureRegistryLimitExceeded,
450
451		/// Attempted to add a new signature to a corrupt signature registry
452		SignatureRegistryCorrupted,
453
454		/// Account has no/insufficient balance to withdraw
455		InsufficientBalanceToWithdraw,
456
457		/// Fund transfer error
458		UnexpectedTokenTransferError,
459
460		/// The caller is not authorized as a recovery provider
461		NotAuthorizedRecoveryProvider,
462
463		/// The provided recovery commitment does not match the stored one
464		InvalidRecoveryCommitment,
465
466		/// No recovery commitment exists for the given MSA
467		NoRecoveryCommitment,
468	}
469
470	impl<T: Config> BlockNumberProvider for Pallet<T> {
471		type BlockNumber = BlockNumberFor<T>;
472
473		fn current_block_number() -> Self::BlockNumber {
474			frame_system::Pallet::<T>::block_number()
475		}
476	}
477
478	#[pallet::hooks]
479	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
480		fn on_initialize(_block_number: BlockNumberFor<T>) -> Weight {
481			<OffchainIndexEventCount<T>>::set(0u16);
482			// allocates 1 read and 1 write for any access of `MSAEventCount` in every block
483			T::DbWeight::get().reads_writes(1u64, 1u64)
484		}
485
486		fn offchain_worker(block_number: BlockNumberFor<T>) {
487			log::info!("Running offchain workers! {block_number:?}");
488			do_offchain_worker::<T>(block_number)
489		}
490	}
491
492	#[pallet::call]
493	impl<T: Config> Pallet<T> {
494		/// Creates an MSA for the Origin (sender of the transaction).  Origin is assigned an MSA ID.
495		///
496		/// # Events
497		/// * [`Event::MsaCreated`]
498		///
499		/// # Errors
500		///
501		/// * [`Error::KeyAlreadyRegistered`] - MSA is already registered to the Origin.
502		///
503		#[pallet::call_index(0)]
504		#[pallet::weight(T::WeightInfo::create())]
505		pub fn create(origin: OriginFor<T>) -> DispatchResult {
506			let public_key = ensure_signed(origin)?;
507
508			let (new_msa_id, new_public_key) =
509				Self::create_account(public_key, |_| -> DispatchResult { Ok(()) })?;
510
511			let event = Event::MsaCreated { msa_id: new_msa_id, key: new_public_key };
512			offchain_index_event::<T>(Some(&event), new_msa_id);
513			Self::deposit_event(event);
514			Ok(())
515		}
516
517		/// `Origin` MSA creates an MSA on behalf of `delegator_key`, creates a Delegation with the `delegator_key`'s MSA as the Delegator and `origin` as `Provider`. Deposits events [`MsaCreated`](Event::MsaCreated) and [`DelegationGranted`](Event::DelegationGranted).
518		///
519		/// # Remarks
520		/// * Origin MUST be the provider
521		/// * Signatures should be over the [`AddProvider`] struct
522		///
523		/// # Events
524		/// * [`Event::MsaCreated`]
525		/// * [`Event::DelegationGranted`]
526		///
527		/// # Errors
528		///
529		/// * [`Error::UnauthorizedProvider`] - payload's MSA does not match given provider MSA.
530		/// * [`Error::InvalidSignature`] - `proof` verification fails; `delegator_key` must have signed `add_provider_payload`
531		/// * [`Error::NoKeyExists`] - there is no MSA for `origin`.
532		/// * [`Error::KeyAlreadyRegistered`] - there is already an MSA for `delegator_key`.
533		/// * [`Error::ProviderNotRegistered`] - the a non-provider MSA is used as the provider
534		/// * [`Error::ProofNotYetValid`] - `add_provider_payload` expiration is too far in the future
535		/// * [`Error::ProofHasExpired`] - `add_provider_payload` expiration is in the past
536		/// * [`Error::SignatureAlreadySubmitted`] - signature has already been used
537		///
538		#[pallet::call_index(1)]
539		#[pallet::weight(T::WeightInfo::create_sponsored_account_with_delegation(
540		add_provider_payload.schema_ids.len() as u32
541		))]
542		pub fn create_sponsored_account_with_delegation(
543			origin: OriginFor<T>,
544			delegator_key: T::AccountId,
545			proof: MultiSignature,
546			add_provider_payload: AddProvider,
547		) -> DispatchResult {
548			let provider_key = ensure_signed(origin)?;
549
550			ensure!(
551				Self::verify_signature(&proof, &delegator_key, &add_provider_payload),
552				Error::<T>::InvalidSignature
553			);
554
555			Self::register_signature(&proof, add_provider_payload.expiration.into())?;
556
557			let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
558			ensure!(
559				add_provider_payload.authorized_msa_id == provider_msa_id,
560				Error::<T>::UnauthorizedProvider
561			);
562
563			// Verify that the provider is a registered provider
564			ensure!(
565				Self::is_registered_provider(provider_msa_id),
566				Error::<T>::ProviderNotRegistered
567			);
568
569			let (new_delegator_msa_id, new_delegator_public_key) =
570				Self::create_account(delegator_key, |new_msa_id| -> DispatchResult {
571					Self::add_provider(
572						ProviderId(provider_msa_id),
573						DelegatorId(new_msa_id),
574						add_provider_payload.schema_ids,
575					)?;
576					Ok(())
577				})?;
578
579			let event =
580				Event::MsaCreated { msa_id: new_delegator_msa_id, key: new_delegator_public_key };
581			offchain_index_event::<T>(Some(&event), new_delegator_msa_id);
582			Self::deposit_event(event);
583			Self::deposit_event(Event::DelegationGranted {
584				delegator_id: DelegatorId(new_delegator_msa_id),
585				provider_id: ProviderId(provider_msa_id),
586			});
587			Ok(())
588		}
589
590		/// Adds an association between MSA id and ProviderRegistryEntry. As of now, the
591		/// only piece of metadata we are recording is provider name.
592		///
593		/// # Events
594		/// * [`Event::ProviderCreated`]
595		///
596		/// # Errors
597		/// * [`Error::NoKeyExists`] - origin does not have an MSA
598		/// * [`Error::ExceedsMaxProviderNameSize`] - Too long of a provider name
599		/// * [`Error::DuplicateProviderRegistryEntry`] - a ProviderRegistryEntry associated with the given MSA id already exists.
600		///
601		#[pallet::call_index(2)]
602		#[pallet::weight(T::WeightInfo::create_provider())]
603		pub fn create_provider(origin: OriginFor<T>, provider_name: Vec<u8>) -> DispatchResult {
604			let provider_key = ensure_signed(origin)?;
605			let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
606			Self::create_provider_for(provider_msa_id, provider_name)?;
607			Self::deposit_event(Event::ProviderCreated {
608				provider_id: ProviderId(provider_msa_id),
609			});
610			Ok(())
611		}
612
613		/// Creates a new Delegation for an existing MSA, with `origin` as the Provider and `delegator_key` is the delegator.
614		/// Since it is being sent on the Delegator's behalf, it requires the Delegator to authorize the new Delegation.
615		///
616		/// # Remarks
617		/// * Origin MUST be the provider
618		/// * Signatures should be over the [`AddProvider`] struct
619		///
620		/// # Events
621		/// * [`Event::DelegationGranted`]
622		///
623		/// # Errors
624		/// * [`Error::AddProviderSignatureVerificationFailed`] - `origin`'s MSA ID does not equal `add_provider_payload.authorized_msa_id`.
625		/// * [`Error::DuplicateProvider`] - there is already a Delegation for `origin` MSA and `delegator_key` MSA.
626		/// * [`Error::UnauthorizedProvider`] - `add_provider_payload.authorized_msa_id`  does not match MSA ID of `delegator_key`.
627		/// * [`Error::InvalidSelfProvider`] - Cannot delegate to the same MSA
628		/// * [`Error::InvalidSignature`] - `proof` verification fails; `delegator_key` must have signed `add_provider_payload`
629		/// * [`Error::NoKeyExists`] - there is no MSA for `origin` or `delegator_key`.
630		/// * [`Error::ProviderNotRegistered`] - the a non-provider MSA is used as the provider
631		/// * [`Error::UnauthorizedDelegator`] - Origin attempted to add a delegate for someone else's MSA
632		///
633		#[pallet::call_index(3)]
634		#[pallet::weight(T::WeightInfo::grant_delegation(add_provider_payload.schema_ids.len() as u32))]
635		pub fn grant_delegation(
636			origin: OriginFor<T>,
637			delegator_key: T::AccountId,
638			proof: MultiSignature,
639			add_provider_payload: AddProvider,
640		) -> DispatchResult {
641			let provider_key = ensure_signed(origin)?;
642
643			ensure!(
644				Self::verify_signature(&proof, &delegator_key, &add_provider_payload),
645				Error::<T>::AddProviderSignatureVerificationFailed
646			);
647
648			Self::register_signature(&proof, add_provider_payload.expiration.into())?;
649			let (provider_id, delegator_id) =
650				Self::ensure_valid_registered_provider(&delegator_key, &provider_key)?;
651
652			ensure!(
653				add_provider_payload.authorized_msa_id == provider_id.0,
654				Error::<T>::UnauthorizedDelegator
655			);
656
657			Self::upsert_schema_permissions(
658				provider_id,
659				delegator_id,
660				add_provider_payload.schema_ids,
661			)?;
662			Self::deposit_event(Event::DelegationGranted { delegator_id, provider_id });
663
664			Ok(())
665		}
666
667		/// Delegator (Origin) MSA terminates a delegation relationship with the `Provider` MSA. Deposits event[`DelegationRevoked`](Event::DelegationRevoked).
668		///
669		/// # Events
670		/// * [`Event::DelegationRevoked`]
671		///
672		/// # Errors
673		///
674		/// * [`Error::NoKeyExists`] - origin does not have an MSA
675		/// * [`Error::DelegationRevoked`] - the delegation has already been revoked.
676		/// * [`Error::DelegationNotFound`] - there is not delegation relationship between Origin and Delegator or Origin and Delegator are the same.
677		///
678		#[pallet::call_index(4)]
679		#[pallet::weight((T::WeightInfo::revoke_delegation_by_delegator(), DispatchClass::Normal, Pays::No))]
680		pub fn revoke_delegation_by_delegator(
681			origin: OriginFor<T>,
682			#[pallet::compact] provider_msa_id: MessageSourceId,
683		) -> DispatchResult {
684			let who = ensure_signed(origin)?;
685
686			match PublicKeyToMsaId::<T>::get(&who) {
687				Some(delegator_msa_id) => {
688					let delegator_id = DelegatorId(delegator_msa_id);
689					let provider_id = ProviderId(provider_msa_id);
690					Self::revoke_provider(provider_id, delegator_id)?;
691					Self::deposit_event(Event::DelegationRevoked { delegator_id, provider_id });
692				},
693				None => {
694					log::error!(
695						"TransactionExtension did not catch invalid MSA for account {who:?}, "
696					);
697				},
698			}
699
700			Ok(())
701		}
702
703		/// Adds a new public key to an existing Message Source Account (MSA). This functionality enables the MSA owner to manage multiple keys
704		/// for their account or rotate keys for enhanced security.
705		///
706		/// The `origin` parameter represents the account from which the function is called and can be either the MSA owner's account or a delegated provider's account,
707		/// depending on the intended use.
708		///
709		/// The function requires two signatures: `msa_owner_proof` and `new_key_owner_proof`, which serve as proofs of ownership for the existing MSA
710		/// and the new public key account, respectively.  This ensures that a new key cannot be added without permission of both the MSA owner and the owner of the new key.
711		///
712		/// The necessary information for the key addition, the new public key and the MSA ID, is contained in the `add_key_payload` parameter of type [AddKeyData].
713		/// It also contains an expiration block number for both proofs, ensuring they are valid and must be greater than the current block.
714		///
715		/// # Events
716		/// * [`Event::PublicKeyAdded`]
717		///
718		/// # Errors
719		///
720		/// * [`Error::MsaOwnershipInvalidSignature`] - `key` is not a valid signer of the provided `add_key_payload`.
721		/// * [`Error::NewKeyOwnershipInvalidSignature`] - `key` is not a valid signer of the provided `add_key_payload`.
722		/// * [`Error::NoKeyExists`] - the MSA id for the account in `add_key_payload` does not exist.
723		/// * [`Error::NotMsaOwner`] - Origin's MSA is not the same as 'add_key_payload` MSA. Essentially you can only add a key to your own MSA.
724		/// * [`Error::ProofHasExpired`] - the current block is less than the `expired` block number set in `AddKeyData`.
725		/// * [`Error::ProofNotYetValid`] - the `expired` block number set in `AddKeyData` is greater than the current block number plus mortality_block_limit().
726		/// * [`Error::SignatureAlreadySubmitted`] - signature has already been used.
727		///
728		#[pallet::call_index(5)]
729		#[pallet::weight(T::WeightInfo::add_public_key_to_msa())]
730		pub fn add_public_key_to_msa(
731			origin: OriginFor<T>,
732			msa_owner_public_key: T::AccountId,
733			msa_owner_proof: MultiSignature,
734			new_key_owner_proof: MultiSignature,
735			add_key_payload: AddKeyData<T>,
736		) -> DispatchResult {
737			let _ = ensure_signed(origin)?;
738
739			ensure!(
740				Self::verify_signature(&msa_owner_proof, &msa_owner_public_key, &add_key_payload),
741				Error::<T>::MsaOwnershipInvalidSignature
742			);
743
744			ensure!(
745				Self::verify_signature(
746					&new_key_owner_proof,
747					&add_key_payload.new_public_key,
748					&add_key_payload
749				),
750				Error::<T>::NewKeyOwnershipInvalidSignature
751			);
752
753			Self::register_signature(&msa_owner_proof, add_key_payload.expiration)?;
754			Self::register_signature(&new_key_owner_proof, add_key_payload.expiration)?;
755
756			let msa_id = add_key_payload.msa_id;
757
758			Self::ensure_msa_owner(&msa_owner_public_key, msa_id)?;
759
760			Self::add_key(
761				msa_id,
762				&add_key_payload.new_public_key.clone(),
763				|msa_id| -> DispatchResult {
764					let event = Event::PublicKeyAdded {
765						msa_id,
766						key: add_key_payload.new_public_key.clone(),
767					};
768					offchain_index_event::<T>(Some(&event), msa_id);
769					Self::deposit_event(event);
770					Ok(())
771				},
772			)?;
773
774			Ok(())
775		}
776
777		/// Remove a key associated with an MSA by expiring it at the current block.
778		///
779		/// # Remarks
780		/// * Removal of key deletes the association of the key with the MSA.
781		/// * The key can be re-added to same or another MSA if needed.
782		///
783		/// # Events
784		/// * [`Event::PublicKeyDeleted`]
785		///
786		/// # Errors
787		/// * [`Error::InvalidSelfRemoval`] - `origin` and `key` are the same.
788		/// * [`Error::NotKeyOwner`] - `origin` does not own the MSA ID associated with `key`.
789		/// * [`Error::NoKeyExists`] - `origin` or `key` are not associated with `origin`'s MSA ID.
790		///
791		#[pallet::call_index(6)]
792		#[pallet::weight((T::WeightInfo::delete_msa_public_key(), DispatchClass::Normal, Pays::No))]
793		pub fn delete_msa_public_key(
794			origin: OriginFor<T>,
795			public_key_to_delete: T::AccountId,
796		) -> DispatchResult {
797			let who = ensure_signed(origin)?;
798
799			match PublicKeyToMsaId::<T>::get(&who) {
800				Some(who_msa_id) => {
801					Self::delete_key_for_msa(who_msa_id, &public_key_to_delete)?;
802
803					// Deposit the event
804					let event = Event::PublicKeyDeleted { key: public_key_to_delete };
805					offchain_index_event::<T>(Some(&event), who_msa_id);
806					Self::deposit_event(event);
807				},
808				None => {
809					log::error!(
810						"TransactionExtension did not catch invalid MSA for account {who:?}"
811					);
812				},
813			}
814			Ok(())
815		}
816
817		/// Provider MSA terminates Delegation with a Delegator MSA by expiring the Delegation at the current block.
818		///
819		/// # Events
820		/// * [`Event::DelegationRevoked`]
821		///
822		/// # Errors
823		///
824		/// * [`Error::NoKeyExists`] - `provider_key` does not have an MSA key.
825		/// * [`Error::DelegationRevoked`] - delegation is already revoked
826		/// * [`Error::DelegationNotFound`] - no Delegation found between origin MSA and delegator MSA.
827		///
828		#[pallet::call_index(7)]
829		#[pallet::weight((T::WeightInfo::revoke_delegation_by_provider(), DispatchClass::Normal, Pays::No))]
830		pub fn revoke_delegation_by_provider(
831			origin: OriginFor<T>,
832			#[pallet::compact] delegator: MessageSourceId,
833		) -> DispatchResult {
834			let who = ensure_signed(origin)?;
835
836			// Revoke delegation relationship entry in the delegation registry by expiring it
837			// at the current block
838			// validity checks are in TransactionExtension so in theory this should never error.
839			match PublicKeyToMsaId::<T>::get(&who) {
840				Some(msa_id) => {
841					let provider_id = ProviderId(msa_id);
842					let delegator_id = DelegatorId(delegator);
843					Self::revoke_provider(provider_id, delegator_id)?;
844					Self::deposit_event(Event::DelegationRevoked { provider_id, delegator_id })
845				},
846				None => {
847					log::error!(
848						"TransactionExtension did not catch invalid MSA for account {who:?}"
849					);
850				},
851			}
852
853			Ok(())
854		}
855
856		// REMOVED grant_schema_permissions() at call index 8
857		// REMOVED revoke_schema_permissions() at call index 9
858
859		/// Retires a MSA
860		///
861		/// When a user wants to disassociate themselves from Frequency, they can retire their MSA for free provided that:
862		///  (1) They own the MSA
863		///  (2) The MSA is not a registered provider.
864		///  (3) They retire their user handle (if they have one)
865		///  (4) There is only one account key
866		///  (5) The user has already deleted all delegations to providers
867		///
868		/// This does not currently remove any messages related to the MSA.
869		///
870		/// # Events
871		/// * [`Event::PublicKeyDeleted`]
872		/// * [`Event::MsaRetired`]
873		///
874		/// # Errors
875		/// * [`Error::NoKeyExists`] - `delegator` does not have an MSA key.
876		///
877		#[pallet::call_index(10)]
878		#[pallet::weight((T::WeightInfo::retire_msa(), DispatchClass::Normal, Pays::No))]
879		pub fn retire_msa(origin: OriginFor<T>) -> DispatchResult {
880			// Check and get the account id from the origin
881			let who = ensure_signed(origin)?;
882
883			// Delete the last and only account key and deposit the "PublicKeyDeleted" event
884			// check for valid MSA is in TransactionExtension.
885			match PublicKeyToMsaId::<T>::get(&who) {
886				Some(msa_id) => {
887					Self::delete_key_for_msa(msa_id, &who)?;
888					let event = Event::PublicKeyDeleted { key: who };
889					offchain_index_event::<T>(Some(&event), msa_id);
890					Self::deposit_event(event);
891					Self::deposit_event(Event::MsaRetired { msa_id });
892				},
893				None => {
894					log::error!(
895						"TransactionExtension did not catch invalid MSA for account {who:?}"
896					);
897				},
898			}
899			Ok(())
900		}
901
902		/// Propose to be a provider.  Creates a proposal for council approval to create a provider from a MSA
903		///
904		/// # Errors
905		/// - [`NoKeyExists`](Error::NoKeyExists) - If there is not MSA for `origin`.
906		#[pallet::call_index(11)]
907		#[pallet::weight(T::WeightInfo::propose_to_be_provider())]
908		pub fn propose_to_be_provider(
909			origin: OriginFor<T>,
910			provider_name: Vec<u8>,
911		) -> DispatchResult {
912			let bounded_name: BoundedVec<u8, T::MaxProviderNameSize> =
913				provider_name.try_into().map_err(|_| Error::<T>::ExceedsMaxProviderNameSize)?;
914
915			let proposer = ensure_signed(origin)?;
916			Self::ensure_valid_msa_key(&proposer)?;
917
918			let proposal: Box<T::Proposal> = Box::new(
919				(Call::<T>::create_provider_via_governance {
920					provider_key: proposer.clone(),
921					provider_name: bounded_name.into(),
922				})
923				.into(),
924			);
925			let threshold = 1;
926			T::ProposalProvider::propose(proposer, threshold, proposal)?;
927			Ok(())
928		}
929
930		/// Create a provider by means of governance approval
931		///
932		/// # Events
933		/// * [`Event::ProviderCreated`]
934		///
935		/// # Errors
936		/// * [`Error::NoKeyExists`] - account does not have an MSA
937		/// * [`Error::ExceedsMaxProviderNameSize`] - Too long of a provider name
938		/// * [`Error::DuplicateProviderRegistryEntry`] - a ProviderRegistryEntry associated with the given MSA id already exists.
939		#[pallet::call_index(12)]
940		#[pallet::weight(T::WeightInfo::create_provider_via_governance())]
941		pub fn create_provider_via_governance(
942			origin: OriginFor<T>,
943			provider_key: T::AccountId,
944			provider_name: Vec<u8>,
945		) -> DispatchResult {
946			T::CreateProviderViaGovernanceOrigin::ensure_origin(origin)?;
947			let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
948			Self::create_provider_for(provider_msa_id, provider_name)?;
949			Self::deposit_event(Event::ProviderCreated {
950				provider_id: ProviderId(provider_msa_id),
951			});
952			Ok(())
953		}
954
955		/// A generic endpoint to replay any offchain related event to fix any potential issues
956		#[pallet::call_index(13)]
957		#[pallet::weight(T::WeightInfo::reindex_offchain())]
958		pub fn reindex_offchain(
959			origin: OriginFor<T>,
960			event: OffchainReplayEvent<T>,
961		) -> DispatchResult {
962			let _ = ensure_signed(origin)?;
963			match event {
964				OffchainReplayEvent::MsaPallet(MsaOffchainReplayEvent::KeyReIndex {
965					msa_id,
966					index_key,
967				}) => {
968					// don't need to check existence of msa_id since it would get checked on offchain side
969					match index_key {
970						Some(key) => {
971							let event = Event::PublicKeyAdded { msa_id, key };
972							offchain_index_event::<T>(Some(&event), msa_id);
973						},
974						None => {
975							offchain_index_event::<T>(None, msa_id);
976						},
977					}
978				},
979			}
980
981			Ok(())
982		}
983
984		/// Withdraw all available tokens from the account associated with the MSA, to the account of the caller.
985		///
986		/// The `origin` parameter represents the account from which the function is called and must be the account receiving the tokens.
987		///
988		/// The function requires one signature: `msa_owner_proof`, which serve as proof that the owner of the MSA authorizes the transaction.
989		///
990		/// The necessary information for the withdrawal authorization, the destination account and the MSA ID, are contained in the `authorization_payload` parameter of type [AddKeyData].
991		/// It also contains an expiration block number for the proof, ensuring it is valid and must be greater than the current block.
992		///
993		/// # Events
994		/// * [`pallet_balances::Event::<T,I>::Transfer`](https://docs.rs/pallet-balances/latest/pallet_balances/pallet/enum.Event.html#variant.Transfer) - Transfer token event
995		///
996		/// # Errors
997		///
998		/// * [`Error::ProofHasExpired`] - the current block is less than the `expired` block number set in `AddKeyData`.
999		/// * [`Error::ProofNotYetValid`] - the `expired` block number set in `AddKeyData` is greater than the current block number plus mortality_block_limit().
1000		/// * [`Error::SignatureAlreadySubmitted`] - signature has already been used.
1001		/// * [`Error::InsufficientBalanceToWithdraw`] - the MSA account has not balance to withdraw
1002		/// * [`Error::UnexpectedTokenTransferError`] - the token transfer failed
1003		///
1004		#[pallet::call_index(14)]
1005		#[pallet::weight((T::WeightInfo::withdraw_tokens(), DispatchClass::Normal, Pays::No))]
1006		pub fn withdraw_tokens(
1007			origin: OriginFor<T>,
1008			_msa_owner_public_key: T::AccountId,
1009			msa_owner_proof: MultiSignature,
1010			authorization_payload: AuthorizedKeyData<T>,
1011		) -> DispatchResult {
1012			let public_key = ensure_signed(origin)?;
1013
1014			Self::register_signature(&msa_owner_proof, authorization_payload.expiration)?;
1015
1016			let msa_id = authorization_payload.msa_id;
1017
1018			// - Get account address for MSA
1019			let msa_address = Self::msa_id_to_eth_address(msa_id);
1020
1021			// - Convert to AccountId
1022			let mut bytes = &EthereumAddressMapper::to_bytes32(&msa_address.0)[..];
1023			let msa_account_id = T::AccountId::decode(&mut bytes).map_err(|_| {
1024				log::error!("Failed to decode MSA account ID from Ethereum address");
1025				Error::<T>::NoKeyExists
1026			})?;
1027
1028			// Get balance to transfer
1029			let msa_balance = T::Currency::reducible_balance(
1030				&msa_account_id,
1031				Preservation::Expendable,
1032				Fortitude::Polite,
1033			);
1034			ensure!(msa_balance > Zero::zero(), Error::<T>::InsufficientBalanceToWithdraw);
1035
1036			// Transfer balance to the caller
1037			let result = <T as pallet::Config>::Currency::transfer(
1038				&msa_account_id,
1039				&public_key,
1040				msa_balance,
1041				Preservation::Expendable,
1042			);
1043			ensure!(result.is_ok(), Error::<T>::UnexpectedTokenTransferError);
1044
1045			Ok(())
1046		}
1047
1048		/// Adds a Recovery Commitment to an MSA. The Recovery Commitment is a cryptographic commitment
1049		/// that can be used later in a recovery process to prove ownership or authorization.
1050		///
1051		/// This function allows the owner of an MSA to create a Recovery Commitment hash that is stored
1052		/// on-chain. The commitment must be signed by the MSA owner to prove authorization.
1053		///
1054		/// # Remarks
1055		/// * The `origin` can be any signed account but the `msa_owner_key` must be the actual owner
1056		/// * Signatures should be over the [`RecoveryCommitmentPayload`] struct
1057		/// * The Recovery Commitment is stored as a hash mapping from MSA ID to commitment value
1058		///
1059		/// # Events
1060		/// * [`Event::RecoveryCommitmentAdded`]
1061		///
1062		/// # Errors
1063		/// * [`Error::InvalidSignature`] - `proof` verification fails; `msa_owner_key` must have signed `payload`
1064		/// * [`Error::NoKeyExists`] - there is no MSA for `msa_owner_key`
1065		/// * [`Error::ProofNotYetValid`] - `payload` expiration is too far in the future
1066		/// * [`Error::ProofHasExpired`] - `payload` expiration is in the past
1067		/// * [`Error::SignatureAlreadySubmitted`] - signature has already been used
1068		///
1069		#[pallet::call_index(15)]
1070		#[pallet::weight(T::WeightInfo::add_recovery_commitment())]
1071		pub fn add_recovery_commitment(
1072			origin: OriginFor<T>,
1073			msa_owner_key: T::AccountId,
1074			proof: MultiSignature,
1075			payload: RecoveryCommitmentPayload<T>,
1076		) -> DispatchResult {
1077			let _origin_key = ensure_signed(origin)?;
1078
1079			// Verify that the MsaId owner has signed the payload
1080			ensure!(
1081				Self::verify_signature(&proof, &msa_owner_key, &payload),
1082				Error::<T>::InvalidSignature
1083			);
1084
1085			// Register the signature to prevent replay attacks
1086			Self::register_signature(&proof, payload.expiration)?;
1087
1088			// Recover the MSA ID from the msa_owner_key
1089			let msa_id = Self::ensure_valid_msa_key(&msa_owner_key)?;
1090
1091			// Store the new RecoveryCommitment
1092			MsaIdToRecoveryCommitment::<T>::insert(msa_id, payload.recovery_commitment);
1093			Self::deposit_event(Event::RecoveryCommitmentAdded {
1094				who: msa_owner_key,
1095				msa_id,
1096				recovery_commitment: payload.recovery_commitment,
1097			});
1098
1099			Ok(())
1100		}
1101
1102		/// Approves a recovery provider via governance.
1103		/// Only governance can approve recovery providers.
1104		///
1105		/// # Events
1106		/// * [`Event::RecoveryProviderApproved`]
1107		///
1108		/// # Errors
1109		/// * [`DispatchError::BadOrigin`] - Caller is not authorized to approve recovery providers.
1110		///
1111		#[pallet::call_index(16)]
1112		#[pallet::weight(T::WeightInfo::approve_recovery_provider())]
1113		pub fn approve_recovery_provider(
1114			origin: OriginFor<T>,
1115			provider_key: T::AccountId,
1116		) -> DispatchResult {
1117			T::RecoveryProviderApprovalOrigin::ensure_origin(origin)?;
1118
1119			let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1120			ensure!(
1121				Self::is_registered_provider(provider_msa_id),
1122				Error::<T>::ProviderNotRegistered
1123			);
1124
1125			// If the provider is already approved, do nothing
1126			if Self::is_approved_recovery_provider(&ProviderId(provider_msa_id)) {
1127				return Ok(());
1128			}
1129
1130			RecoveryProviders::<T>::insert(ProviderId(provider_msa_id), true);
1131
1132			Self::deposit_event(Event::RecoveryProviderApproved {
1133				provider_id: ProviderId(provider_msa_id),
1134			});
1135
1136			Ok(())
1137		}
1138
1139		/// Removes a recovery provider via governance.
1140		/// Only governance can remove recovery providers.
1141		///
1142		/// # Events
1143		/// * [`Event::RecoveryProviderRemoved`]
1144		///
1145		/// # Errors
1146		///
1147		/// * [`DispatchError::BadOrigin`] - Caller is not authorized to remove recovery providers.
1148		///
1149		#[pallet::call_index(17)]
1150		#[pallet::weight(T::WeightInfo::remove_recovery_provider())]
1151		pub fn remove_recovery_provider(
1152			origin: OriginFor<T>,
1153			provider: ProviderId,
1154		) -> DispatchResult {
1155			T::RecoveryProviderApprovalOrigin::ensure_origin(origin)?;
1156
1157			RecoveryProviders::<T>::remove(provider);
1158			Self::deposit_event(Event::RecoveryProviderRemoved { provider_id: provider });
1159			Ok(())
1160		}
1161
1162		/// Recover an MSA account with a new control key.
1163		/// This extrinsic is called by approved Recovery Providers after verifying
1164		/// the user's Recovery Intermediary Hashes and Authentication Contact.
1165		///
1166		/// # Events
1167		/// * [`Event::AccountRecovered`]
1168		/// * [`Event::RecoveryCommitmentInvalidated`]
1169		///
1170		/// # Errors
1171		/// * [`Error::NotAuthorizedRecoveryProvider`] - Caller is not an approved Recovery Provider.
1172		/// * [`Error::ProviderNotRegistered`] - Recovery Provider is not registered.
1173		/// * [`Error::NoRecoveryCommitment`] - No Recovery Commitment exists for the MSA.
1174		/// * [`Error::InvalidRecoveryCommitment`] - Recovery Commitment does not match.
1175		///
1176		#[pallet::call_index(18)]
1177		#[pallet::weight(T::WeightInfo::recover_account())]
1178		pub fn recover_account(
1179			origin: OriginFor<T>,
1180			intermediary_hash_a: RecoveryHash,
1181			intermediary_hash_b: RecoveryHash,
1182			new_control_key_proof: MultiSignature,
1183			add_key_payload: AddKeyData<T>,
1184		) -> DispatchResult {
1185			let provider_key = ensure_signed(origin)?;
1186
1187			let provider_msa_id = Self::ensure_approved_recovery_provider(&provider_key)?;
1188
1189			let recovery_commitment = Self::ensure_valid_recovery_commitment(
1190				intermediary_hash_a,
1191				intermediary_hash_b,
1192				add_key_payload.msa_id,
1193			)?;
1194
1195			Self::ensure_valid_new_key_owner(&new_control_key_proof, &add_key_payload)?;
1196
1197			Self::add_key(
1198				add_key_payload.msa_id,
1199				&add_key_payload.new_public_key.clone(),
1200				|msa_id| -> DispatchResult {
1201					let event = Event::PublicKeyAdded {
1202						msa_id,
1203						key: add_key_payload.new_public_key.clone(),
1204					};
1205					offchain_index_event::<T>(Some(&event), msa_id);
1206					Self::deposit_event(event);
1207					Ok(())
1208				},
1209			)?;
1210
1211			// Invalidate the recovery commitment (single-use requirement)
1212			MsaIdToRecoveryCommitment::<T>::remove(add_key_payload.msa_id);
1213
1214			// Emit events
1215			Self::deposit_event(Event::AccountRecovered {
1216				msa_id: add_key_payload.msa_id,
1217				recovery_provider: ProviderId(provider_msa_id),
1218				new_control_key: add_key_payload.new_public_key.clone(),
1219			});
1220
1221			Self::deposit_event(Event::RecoveryCommitmentInvalidated {
1222				msa_id: add_key_payload.msa_id,
1223				recovery_commitment,
1224			});
1225
1226			Ok(())
1227		}
1228	}
1229}
1230
1231impl<T: Config> Pallet<T> {
1232	/// Check if a recovery provider is approved
1233	///
1234	/// # Arguments
1235	/// * `provider`: The provider to check
1236	///
1237	/// # Returns
1238	/// * [`bool`] - True if the provider is approved, false otherwise
1239	pub fn is_approved_recovery_provider(provider: &ProviderId) -> bool {
1240		RecoveryProviders::<T>::get(provider).unwrap_or(false)
1241	}
1242
1243	/// Compute the Recovery Commitment from Intermediary Hashes
1244	///
1245	/// # Arguments
1246	/// * `intermediary_hash_a`: Hash of the Recovery Secret
1247	/// * `intermediary_hash_b`: Hash of the Recovery Secret + Authentication Contact
1248	///
1249	/// # Returns
1250	/// * [`RecoveryCommitment`] - The computed Recovery Commitment
1251	pub fn compute_recovery_commitment(
1252		intermediary_hash_a: RecoveryHash,
1253		intermediary_hash_b: RecoveryHash,
1254	) -> RecoveryCommitment {
1255		let mut input = Vec::with_capacity(64);
1256		input.extend_from_slice(&intermediary_hash_a);
1257		input.extend_from_slice(&intermediary_hash_b);
1258		keccak_256(&input)
1259	}
1260
1261	/// Ensure that the provider is approved for recovery operations
1262	///
1263	/// # Arguments
1264	/// * `provider_key`: The provider's account key
1265	///
1266	/// # Returns
1267	/// * [`MessageSourceId`] - The provider's MSA ID if approved
1268	///
1269	/// # Errors
1270	/// * [`Error::NoKeyExists`] - Provider key is not associated with an MSA
1271	/// * [`Error::NotAuthorizedRecoveryProvider`] - Provider is not approved for recovery
1272	fn ensure_approved_recovery_provider(
1273		provider_key: &T::AccountId,
1274	) -> Result<MessageSourceId, DispatchError> {
1275		let provider_msa_id = Self::ensure_valid_msa_key(provider_key)?;
1276
1277		ensure!(
1278			Self::is_approved_recovery_provider(&ProviderId(provider_msa_id)),
1279			Error::<T>::NotAuthorizedRecoveryProvider
1280		);
1281
1282		Ok(provider_msa_id)
1283	}
1284
1285	/// Ensure that the recovery commitment is valid
1286	///
1287	/// # Arguments
1288	/// * `intermediary_hash_a`: Hash of the Recovery Secret
1289	/// * `intermediary_hash_b`: Hash of the Recovery Secret + Authentication Contact
1290	/// * `msa_id`: The MSA ID to verify the commitment for
1291	///
1292	/// # Returns
1293	/// * [`RecoveryCommitment`] - The validated recovery commitment
1294	///
1295	/// # Errors
1296	/// * [`Error::NoRecoveryCommitment`] - No Recovery Commitment exists for the MSA
1297	/// * [`Error::InvalidRecoveryCommitment`] - Recovery Commitment does not match
1298	fn ensure_valid_recovery_commitment(
1299		intermediary_hash_a: RecoveryHash,
1300		intermediary_hash_b: RecoveryHash,
1301		msa_id: MessageSourceId,
1302	) -> Result<RecoveryCommitment, DispatchError> {
1303		let recovery_commitment: RecoveryCommitment =
1304			Self::compute_recovery_commitment(intermediary_hash_a, intermediary_hash_b);
1305
1306		let stored_commitment: RecoveryCommitment =
1307			MsaIdToRecoveryCommitment::<T>::get(msa_id).ok_or(Error::<T>::NoRecoveryCommitment)?;
1308
1309		ensure!(recovery_commitment == stored_commitment, Error::<T>::InvalidRecoveryCommitment);
1310
1311		Ok(recovery_commitment)
1312	}
1313
1314	/// Ensure that the new key owner signature is valid
1315	///
1316	/// # Arguments
1317	/// * `new_control_key_proof`: Signature proof from the new control key
1318	/// * `add_key_payload`: Payload containing the new key and related data
1319	///
1320	/// # Errors
1321	/// * [`Error::NewKeyOwnershipInvalidSignature`] - Invalid signature from new key owner
1322	/// * [`Error::SignatureAlreadySubmitted`] - Signature has already been used
1323	fn ensure_valid_new_key_owner(
1324		new_control_key_proof: &MultiSignature,
1325		add_key_payload: &AddKeyData<T>,
1326	) -> DispatchResult {
1327		// The original MSA owner key is not available, therefore we cannot reuse the `add_public_key_to_msa` logic
1328		// to verify the signatures, only the new control key signature is verified.
1329		ensure!(
1330			Self::verify_signature(
1331				new_control_key_proof,
1332				&add_key_payload.new_public_key,
1333				add_key_payload
1334			),
1335			Error::<T>::NewKeyOwnershipInvalidSignature
1336		);
1337		Self::register_signature(new_control_key_proof, add_key_payload.expiration)?;
1338
1339		Ok(())
1340	}
1341
1342	/// Create the account for the `key`
1343	///
1344	/// # Errors
1345	/// * [`Error::MsaIdOverflow`]
1346	/// * [`Error::KeyLimitExceeded`]
1347	/// * [`Error::KeyAlreadyRegistered`]
1348	///
1349	pub fn create_account<F>(
1350		key: T::AccountId,
1351		on_success: F,
1352	) -> Result<(MessageSourceId, T::AccountId), DispatchError>
1353	where
1354		F: FnOnce(MessageSourceId) -> DispatchResult,
1355	{
1356		let next_msa_id = Self::get_next_msa_id()?;
1357		Self::add_key(next_msa_id, &key, on_success)?;
1358		let _ = Self::set_msa_identifier(next_msa_id);
1359
1360		Ok((next_msa_id, key))
1361	}
1362
1363	/// Generate the next MSA Id
1364	///
1365	/// # Errors
1366	/// * [`Error::MsaIdOverflow`]
1367	///
1368	pub fn get_next_msa_id() -> Result<MessageSourceId, DispatchError> {
1369		let next = CurrentMsaIdentifierMaximum::<T>::get()
1370			.checked_add(1)
1371			.ok_or(Error::<T>::MsaIdOverflow)?;
1372
1373		Ok(next)
1374	}
1375
1376	/// Set the current identifier in storage
1377	pub fn set_msa_identifier(identifier: MessageSourceId) -> DispatchResult {
1378		CurrentMsaIdentifierMaximum::<T>::set(identifier);
1379
1380		Ok(())
1381	}
1382
1383	/// Create Register Provider
1384	pub fn create_registered_provider(
1385		provider_id: ProviderId,
1386		name: BoundedVec<u8, T::MaxProviderNameSize>,
1387	) -> DispatchResult {
1388		ProviderToRegistryEntry::<T>::try_mutate(provider_id, |maybe_metadata| -> DispatchResult {
1389			ensure!(maybe_metadata.take().is_none(), Error::<T>::DuplicateProviderRegistryEntry);
1390			*maybe_metadata = Some(ProviderRegistryEntry { provider_name: name });
1391			Ok(())
1392		})
1393	}
1394
1395	/// Adds a list of schema permissions to a delegation relationship.
1396	pub fn grant_permissions_for_schemas(
1397		delegator_id: DelegatorId,
1398		provider_id: ProviderId,
1399		schema_ids: Vec<SchemaId>,
1400	) -> DispatchResult {
1401		Self::try_mutate_delegation(delegator_id, provider_id, |delegation, is_new_delegation| {
1402			ensure!(!is_new_delegation, Error::<T>::DelegationNotFound);
1403			Self::ensure_all_schema_ids_are_valid(&schema_ids)?;
1404
1405			PermittedDelegationSchemas::<T>::try_insert_schemas(delegation, schema_ids)?;
1406
1407			Ok(())
1408		})
1409	}
1410
1411	/// Revokes a list of schema permissions from a delegation relationship.
1412	pub fn revoke_permissions_for_schemas(
1413		delegator_id: DelegatorId,
1414		provider_id: ProviderId,
1415		schema_ids: Vec<SchemaId>,
1416	) -> DispatchResult {
1417		Self::try_mutate_delegation(delegator_id, provider_id, |delegation, is_new_delegation| {
1418			ensure!(!is_new_delegation, Error::<T>::DelegationNotFound);
1419			Self::ensure_all_schema_ids_are_valid(&schema_ids)?;
1420
1421			let current_block = frame_system::Pallet::<T>::block_number();
1422
1423			PermittedDelegationSchemas::<T>::try_get_mut_schemas(
1424				delegation,
1425				schema_ids,
1426				current_block,
1427			)?;
1428
1429			Ok(())
1430		})
1431	}
1432
1433	/// Add a new key to the MSA
1434	///
1435	/// # Errors
1436	/// * [`Error::KeyLimitExceeded`]
1437	/// * [`Error::KeyAlreadyRegistered`]
1438	///
1439	pub fn add_key<F>(msa_id: MessageSourceId, key: &T::AccountId, on_success: F) -> DispatchResult
1440	where
1441		F: FnOnce(MessageSourceId) -> DispatchResult,
1442	{
1443		PublicKeyToMsaId::<T>::try_mutate(key, |maybe_msa_id| {
1444			ensure!(maybe_msa_id.is_none(), Error::<T>::KeyAlreadyRegistered);
1445			*maybe_msa_id = Some(msa_id);
1446
1447			// Increment the key counter
1448			<PublicKeyCountForMsaId<T>>::try_mutate(msa_id, |key_count| {
1449				// key_count:u8 should default to 0 if it does not exist
1450				let incremented_key_count =
1451					key_count.checked_add(1).ok_or(ArithmeticError::Overflow)?;
1452
1453				ensure!(
1454					incremented_key_count <= T::MaxPublicKeysPerMsa::get(),
1455					Error::<T>::KeyLimitExceeded
1456				);
1457
1458				*key_count = incremented_key_count;
1459				on_success(msa_id)
1460			})
1461		})
1462	}
1463
1464	/// Check that schema ids are all valid
1465	///
1466	/// # Errors
1467	/// * [`Error::InvalidSchemaId`]
1468	/// * [`Error::ExceedsMaxSchemaGrantsPerDelegation`]
1469	///
1470	pub fn ensure_all_schema_ids_are_valid(schema_ids: &[SchemaId]) -> DispatchResult {
1471		ensure!(
1472			schema_ids.len() <= T::MaxSchemaGrantsPerDelegation::get() as usize,
1473			Error::<T>::ExceedsMaxSchemaGrantsPerDelegation
1474		);
1475
1476		let are_schemas_valid = T::SchemaValidator::are_all_schema_ids_valid(schema_ids);
1477
1478		ensure!(are_schemas_valid, Error::<T>::InvalidSchemaId);
1479
1480		Ok(())
1481	}
1482
1483	/// Returns if provider is registered by checking if the [`ProviderToRegistryEntry`] contains the MSA id
1484	pub fn is_registered_provider(msa_id: MessageSourceId) -> bool {
1485		ProviderToRegistryEntry::<T>::contains_key(ProviderId(msa_id))
1486	}
1487
1488	/// Checks that a provider and delegator keys are valid
1489	/// and that a provider and delegator are not the same
1490	/// and that a provider has authorized a delegator to create a delegation relationship.
1491	///
1492	/// # Errors
1493	/// * [`Error::ProviderNotRegistered`]
1494	/// * [`Error::InvalidSelfProvider`]
1495	/// * [`Error::NoKeyExists`]
1496	///
1497	pub fn ensure_valid_registered_provider(
1498		delegator_key: &T::AccountId,
1499		provider_key: &T::AccountId,
1500	) -> Result<(ProviderId, DelegatorId), DispatchError> {
1501		let provider_msa_id = Self::ensure_valid_msa_key(provider_key)?;
1502		let delegator_msa_id = Self::ensure_valid_msa_key(delegator_key)?;
1503
1504		// Ensure that the delegator is not the provider.  You cannot delegate to yourself.
1505		ensure!(delegator_msa_id != provider_msa_id, Error::<T>::InvalidSelfProvider);
1506
1507		// Verify that the provider is a registered provider
1508		ensure!(Self::is_registered_provider(provider_msa_id), Error::<T>::ProviderNotRegistered);
1509
1510		Ok((provider_msa_id.into(), delegator_msa_id.into()))
1511	}
1512
1513	/// Checks that the MSA for `who` is the same as `msa_id`
1514	///
1515	/// # Errors
1516	/// * [`Error::NotMsaOwner`]
1517	/// * [`Error::NoKeyExists`]
1518	///
1519	pub fn ensure_msa_owner(who: &T::AccountId, msa_id: MessageSourceId) -> DispatchResult {
1520		let provider_msa_id = Self::ensure_valid_msa_key(who)?;
1521		ensure!(provider_msa_id == msa_id, Error::<T>::NotMsaOwner);
1522
1523		Ok(())
1524	}
1525
1526	/// Verify the `signature` was signed by `signer` on `payload` by a wallet
1527	/// Note the `wrap_binary_data` follows the Polkadot wallet pattern of wrapping with `<Byte>` tags.
1528	///
1529	/// # Errors
1530	/// * [`Error::InvalidSignature`]
1531	///
1532	pub fn verify_signature<P>(
1533		signature: &MultiSignature,
1534		signer: &T::AccountId,
1535		payload: &P,
1536	) -> bool
1537	where
1538		P: Encode + EIP712Encode,
1539	{
1540		let key = T::ConvertIntoAccountId32::convert((*signer).clone());
1541
1542		check_signature(signature, key, payload)
1543	}
1544
1545	/// Add a provider to a delegator with the default permissions
1546	///
1547	/// # Errors
1548	/// * [`Error::ExceedsMaxSchemaGrantsPerDelegation`]
1549	///
1550	pub fn add_provider(
1551		provider_id: ProviderId,
1552		delegator_id: DelegatorId,
1553		schema_ids: Vec<SchemaId>,
1554	) -> DispatchResult {
1555		Self::try_mutate_delegation(delegator_id, provider_id, |delegation, is_new_delegation| {
1556			ensure!(is_new_delegation, Error::<T>::DuplicateProvider);
1557			Self::ensure_all_schema_ids_are_valid(&schema_ids)?;
1558
1559			PermittedDelegationSchemas::<T>::try_insert_schemas(delegation, schema_ids)?;
1560
1561			Ok(())
1562		})
1563	}
1564
1565	/// Modify delegation's schema permissions
1566	///
1567	/// # Errors
1568	/// * [`Error::ExceedsMaxSchemaGrantsPerDelegation`]
1569	pub fn upsert_schema_permissions(
1570		provider_id: ProviderId,
1571		delegator_id: DelegatorId,
1572		schema_ids: Vec<SchemaId>,
1573	) -> DispatchResult {
1574		Self::try_mutate_delegation(delegator_id, provider_id, |delegation, _is_new_delegation| {
1575			Self::ensure_all_schema_ids_are_valid(&schema_ids)?;
1576
1577			// Create revoke and insert lists
1578			let mut revoke_ids: Vec<SchemaId> = Vec::new();
1579			let mut update_ids: Vec<SchemaId> = Vec::new();
1580			let mut insert_ids: Vec<SchemaId> = Vec::new();
1581
1582			let existing_keys = delegation.schema_permissions.keys();
1583
1584			for existing_schema_id in existing_keys {
1585				if !schema_ids.contains(existing_schema_id) {
1586					if let Some(block) = delegation.schema_permissions.get(existing_schema_id) {
1587						if *block == BlockNumberFor::<T>::zero() {
1588							revoke_ids.push(*existing_schema_id);
1589						}
1590					}
1591				}
1592			}
1593			for schema_id in &schema_ids {
1594				if !delegation.schema_permissions.contains_key(schema_id) {
1595					insert_ids.push(*schema_id);
1596				} else {
1597					update_ids.push(*schema_id);
1598				}
1599			}
1600
1601			let current_block = frame_system::Pallet::<T>::block_number();
1602
1603			// Revoke any that are not in the new list that are not already revoked
1604			PermittedDelegationSchemas::<T>::try_get_mut_schemas(
1605				delegation,
1606				revoke_ids,
1607				current_block,
1608			)?;
1609
1610			// Update any that are in the list but are not new
1611			PermittedDelegationSchemas::<T>::try_get_mut_schemas(
1612				delegation,
1613				update_ids,
1614				BlockNumberFor::<T>::zero(),
1615			)?;
1616
1617			// Insert any new ones that are not in the existing list
1618			PermittedDelegationSchemas::<T>::try_insert_schemas(delegation, insert_ids)?;
1619			delegation.revoked_at = BlockNumberFor::<T>::zero();
1620			Ok(())
1621		})
1622	}
1623
1624	/// Adds an association between MSA id and ProviderRegistryEntry. As of now, the
1625	/// only piece of metadata we are recording is provider name.
1626	///
1627	/// # Errors
1628	/// * [`Error::ExceedsMaxProviderNameSize`] - Too long of a provider name
1629	/// * [`Error::DuplicateProviderRegistryEntry`] - a ProviderRegistryEntry associated with the given MSA id already exists.
1630	///
1631	pub fn create_provider_for(
1632		provider_msa_id: MessageSourceId,
1633		provider_name: Vec<u8>,
1634	) -> DispatchResult {
1635		let bounded_name: BoundedVec<u8, T::MaxProviderNameSize> =
1636			provider_name.try_into().map_err(|_| Error::<T>::ExceedsMaxProviderNameSize)?;
1637
1638		ProviderToRegistryEntry::<T>::try_mutate(
1639			ProviderId(provider_msa_id),
1640			|maybe_metadata| -> DispatchResult {
1641				ensure!(
1642					maybe_metadata.take().is_none(),
1643					Error::<T>::DuplicateProviderRegistryEntry
1644				);
1645				*maybe_metadata = Some(ProviderRegistryEntry { provider_name: bounded_name });
1646				Ok(())
1647			},
1648		)?;
1649		Ok(())
1650	}
1651
1652	/// Mutates the delegation relationship storage item only when the supplied function returns an 'Ok()' result.
1653	/// The callback function 'f' takes the value (a delegation) and a reference to a boolean variable. This callback
1654	/// sets the boolean variable to 'true' if the value is to be inserted and to 'false' if it is to be updated.
1655	pub fn try_mutate_delegation<R, E: From<DispatchError>>(
1656		delegator_id: DelegatorId,
1657		provider_id: ProviderId,
1658		f: impl FnOnce(
1659			&mut Delegation<SchemaId, BlockNumberFor<T>, T::MaxSchemaGrantsPerDelegation>,
1660			bool,
1661		) -> Result<R, E>,
1662	) -> Result<R, E> {
1663		DelegatorAndProviderToDelegation::<T>::try_mutate_exists(
1664			delegator_id,
1665			provider_id,
1666			|maybe_delegation_info| {
1667				let is_new = maybe_delegation_info.is_none();
1668				let mut delegation = maybe_delegation_info.take().unwrap_or_default();
1669
1670				let result = f(&mut delegation, is_new)?;
1671
1672				// only set the value if execution of 'f' is successful
1673				*maybe_delegation_info = Some(delegation);
1674				Ok(result)
1675			},
1676		)
1677	}
1678
1679	/// Deletes a key associated with a given MSA
1680	///
1681	/// # Errors
1682	/// * [`Error::NoKeyExists`]
1683	///
1684	pub fn delete_key_for_msa(msa_id: MessageSourceId, key: &T::AccountId) -> DispatchResult {
1685		PublicKeyToMsaId::<T>::try_mutate_exists(key, |maybe_msa_id| {
1686			ensure!(maybe_msa_id.is_some(), Error::<T>::NoKeyExists);
1687
1688			// Delete the key if it exists
1689			*maybe_msa_id = None;
1690
1691			<PublicKeyCountForMsaId<T>>::try_mutate_exists(msa_id, |key_count| {
1692				match key_count {
1693					Some(1) => *key_count = None,
1694					Some(count) => *count = *count - 1u8,
1695					None => (),
1696				}
1697
1698				Ok(())
1699			})
1700		})
1701	}
1702
1703	/// Revoke the grant for permissions from the delegator to the provider
1704	///
1705	/// # Errors
1706	/// * [`Error::DelegationRevoked`]
1707	/// * [`Error::DelegationNotFound`]
1708	///
1709	pub fn revoke_provider(provider_id: ProviderId, delegator_id: DelegatorId) -> DispatchResult {
1710		DelegatorAndProviderToDelegation::<T>::try_mutate_exists(
1711			delegator_id,
1712			provider_id,
1713			|maybe_info| -> DispatchResult {
1714				let mut info = maybe_info.take().ok_or(Error::<T>::DelegationNotFound)?;
1715
1716				ensure!(
1717					info.revoked_at == BlockNumberFor::<T>::default(),
1718					Error::<T>::DelegationRevoked
1719				);
1720
1721				let current_block = frame_system::Pallet::<T>::block_number();
1722				info.revoked_at = current_block;
1723				*maybe_info = Some(info);
1724				Ok(())
1725			},
1726		)?;
1727
1728		Ok(())
1729	}
1730
1731	/// Retrieves the MSA Id for a given `AccountId`
1732	pub fn get_owner_of(key: &T::AccountId) -> Option<MessageSourceId> {
1733		PublicKeyToMsaId::<T>::get(key)
1734	}
1735
1736	/// Retrieve MSA Id associated with `key` or return `NoKeyExists`
1737	pub fn ensure_valid_msa_key(key: &T::AccountId) -> Result<MessageSourceId, DispatchError> {
1738		let msa_id = PublicKeyToMsaId::<T>::get(key).ok_or(Error::<T>::NoKeyExists)?;
1739		Ok(msa_id)
1740	}
1741
1742	/// Get a list of Schema Ids that a provider has been granted access to
1743	///
1744	/// # Errors
1745	/// * [`Error::DelegationNotFound`]
1746	/// * [`Error::SchemaNotGranted`]
1747	///
1748	pub fn get_granted_schemas_by_msa_id(
1749		delegator: DelegatorId,
1750		provider: Option<ProviderId>,
1751	) -> Result<Vec<DelegationResponse<SchemaId, BlockNumberFor<T>>>, DispatchError> {
1752		let delegations = match provider {
1753			Some(provider_id) => vec![(
1754				provider_id,
1755				Self::get_delegation_of(delegator, provider_id)
1756					.ok_or(Error::<T>::DelegationNotFound)?,
1757			)],
1758			None => DelegatorAndProviderToDelegation::<T>::iter_prefix(delegator).collect(),
1759		};
1760
1761		let mut result = vec![];
1762		for (provider_id, provider_info) in delegations {
1763			let schema_permissions = provider_info.schema_permissions;
1764			// checking only if this is called for a specific provider
1765			if provider.is_some() && schema_permissions.is_empty() {
1766				return Err(Error::<T>::SchemaNotGranted.into());
1767			}
1768
1769			let mut schema_list = Vec::new();
1770			for (schema_id, revoked_at) in schema_permissions {
1771				if provider_info.revoked_at > BlockNumberFor::<T>::zero() &&
1772					(revoked_at > provider_info.revoked_at ||
1773						revoked_at == BlockNumberFor::<T>::zero())
1774				{
1775					schema_list
1776						.push(SchemaGrant { schema_id, revoked_at: provider_info.revoked_at });
1777				} else {
1778					schema_list.push(SchemaGrant { schema_id, revoked_at });
1779				}
1780			}
1781
1782			result.push(DelegationResponse { provider_id, permissions: schema_list });
1783		}
1784
1785		Ok(result)
1786	}
1787
1788	/// Converts an MSA ID into a synthetic Ethereum address (raw 20-byte format) by
1789	/// taking the last 20 bytes of the keccak256 hash of the following:
1790	/// [0..1]: 0xD9 (first byte of the keccak256 hash of the domain prefix "Frequency")
1791	/// [1..9]:   u64 (big endian)
1792	/// [9..41]:  keccack256("MSA Generated")
1793	pub fn msa_id_to_eth_address(id: MessageSourceId) -> H160 {
1794		/// First byte of the keccak256 hash of the domain prefix "Frequency"
1795		/// This "domain separator" ensures that the generated address will not collide with Ethereum addresses
1796		/// generated by the standard 'CREATE2' opcode.
1797		const DOMAIN_PREFIX: u8 = 0xD9;
1798
1799		lazy_static! {
1800			/// Salt used to generate MSA addresses
1801			static ref MSA_ADDRESS_SALT: [u8; 32] = keccak_256(b"MSA Generated");
1802		}
1803		let input_value = id.to_be_bytes();
1804
1805		let mut hash_input = [0u8; 41];
1806		hash_input[0] = DOMAIN_PREFIX;
1807		hash_input[1..9].copy_from_slice(&input_value);
1808		hash_input[9..].copy_from_slice(&(*MSA_ADDRESS_SALT));
1809
1810		let hash = keccak_256(&hash_input);
1811		H160::from_slice(&hash[12..])
1812	}
1813
1814	/// Returns a boolean indicating whether the given Ethereum address was generated from the given MSA ID.
1815	pub fn validate_eth_address_for_msa(address: &H160, msa_id: MessageSourceId) -> bool {
1816		let generated_address = Self::msa_id_to_eth_address(msa_id);
1817		*address == generated_address
1818	}
1819
1820	/// Converts a 20-byte synthetic Ethereum address into a checksummed string format,
1821	/// using ERC-55 checksum rules.
1822	/// Formats a 20-byte address into an EIP-55 checksummed `0x...` string.
1823	pub fn eth_address_to_checksummed_string(address: &H160) -> alloc::string::String {
1824		let addr_bytes = address.0;
1825		let addr_hex = hex::encode(addr_bytes);
1826		let hash = keccak_256(addr_hex.as_bytes());
1827
1828		let mut result = alloc::string::String::with_capacity(42);
1829		result.push_str("0x");
1830
1831		for (i, c) in addr_hex.chars().enumerate() {
1832			let hash_byte = hash[i / 2];
1833			let bit = if i % 2 == 0 { (hash_byte >> 4) & 0xf } else { hash_byte & 0xf };
1834
1835			result.push(if c.is_ascii_hexdigit() && c.is_ascii_alphabetic() {
1836				if bit >= 8 {
1837					c.to_ascii_uppercase()
1838				} else {
1839					c
1840				}
1841			} else {
1842				c
1843			});
1844		}
1845
1846		result
1847	}
1848
1849	/// Adds a signature to the `PayloadSignatureRegistryList`
1850	/// Check that mortality_block is within bounds. If so, proceed and add the new entry.
1851	/// Raises `SignatureAlreadySubmitted` if the signature exists in the registry.
1852	/// Raises `SignatureRegistryLimitExceeded` if the oldest signature of the list has not yet expired.
1853	///
1854	/// # Errors
1855	/// * [`Error::ProofNotYetValid`]
1856	/// * [`Error::ProofHasExpired`]
1857	/// * [`Error::SignatureAlreadySubmitted`]
1858	/// * [`Error::SignatureRegistryLimitExceeded`]
1859	///
1860	pub fn register_signature(
1861		signature: &MultiSignature,
1862		signature_expires_at: BlockNumberFor<T>,
1863	) -> DispatchResult {
1864		let current_block =
1865			Self::check_signature_against_registry(signature, signature_expires_at)?;
1866
1867		Self::enqueue_signature(signature, signature_expires_at, current_block)
1868	}
1869
1870	/// Check that mortality_block is within bounds.
1871	/// Raises `SignatureAlreadySubmitted` if the signature exists in the registry.
1872	///
1873	/// # Errors
1874	/// * [`Error::ProofNotYetValid`]
1875	/// * [`Error::ProofHasExpired`]
1876	/// * [`Error::SignatureAlreadySubmitted`]
1877	///
1878	pub fn check_signature_against_registry(
1879		signature: &MultiSignature,
1880		signature_expires_at: BlockNumberFor<T>,
1881	) -> Result<BlockNumberFor<T>, DispatchError> {
1882		let current_block: BlockNumberFor<T> = frame_system::Pallet::<T>::block_number();
1883
1884		let max_lifetime = Self::mortality_block_limit(current_block);
1885		ensure!(max_lifetime > signature_expires_at, Error::<T>::ProofNotYetValid);
1886		ensure!(current_block < signature_expires_at, Error::<T>::ProofHasExpired);
1887
1888		// Make sure it is not in the registry
1889		ensure!(
1890			!<PayloadSignatureRegistryList<T>>::contains_key(signature),
1891			Error::<T>::SignatureAlreadySubmitted
1892		);
1893		if let Some(signature_pointer) = PayloadSignatureRegistryPointer::<T>::get() {
1894			ensure!(signature_pointer.newest != *signature, Error::<T>::SignatureAlreadySubmitted);
1895		}
1896
1897		Ok(current_block)
1898	}
1899
1900	/// Do the actual enqueuing into the list storage and update the pointer
1901	///
1902	/// The signature registry consist of two storage items:
1903	/// - `PayloadSignatureRegistryList` - a linked list of signatures, in which each entry
1904	///   points to the next newest signature. The list is stored as a mapping of
1905	///   `MultiSignature` to a tuple of `(BlockNumberFor<T>, MultiSignature)`.
1906	///   The tuple contains the expiration block number and the next signature in the list.
1907	/// - `PayloadSignatureRegistryPointer` - a struct containing the newest signature,
1908	///   the oldest signature, the count of signatures, and the expiration block number of the newest signature.
1909	///
1910	/// NOTE: 'newest' and 'oldest' refer to the order in which the signatures were added to the list,
1911	/// which is not necessarily the order in which they expire.
1912	///
1913	/// Example: (key [signature], value [next newest signature])
1914	/// - `1,2 (oldest)`
1915	/// - `2,3`
1916	/// - `3,4`
1917	/// - 4 (newest in pointer storage)`
1918	///
1919	// DEVELOPER NOTE: As currently implemented, the signature registry list will continue to grow until it reaches
1920	// the maximum number of signatures, at which point it will remain at that size, only ever replacing the oldest
1921	// signature with the newest one. This is a trade-off between storage space and performance.
1922	fn enqueue_signature(
1923		signature: &MultiSignature,
1924		signature_expires_at: BlockNumberFor<T>,
1925		current_block: BlockNumberFor<T>,
1926	) -> DispatchResult {
1927		// Get the current pointer, or if this is the initialization, generate an empty pointer
1928		let pointer =
1929			PayloadSignatureRegistryPointer::<T>::get().unwrap_or(SignatureRegistryPointer {
1930				newest: signature.clone(),
1931				newest_expires_at: signature_expires_at,
1932				oldest: signature.clone(),
1933				count: 0,
1934			});
1935
1936		// Make sure it is not sitting as the `newest` in the pointer
1937		ensure!(
1938			!(pointer.count != 0 && pointer.newest.eq(signature)),
1939			Error::<T>::SignatureAlreadySubmitted
1940		);
1941
1942		// Default to the current oldest signature in case we are still filling up
1943		let mut oldest: MultiSignature = pointer.oldest.clone();
1944
1945		// We are now wanting to overwrite prior signatures
1946		let is_registry_full: bool = pointer.count == T::MaxSignaturesStored::get().unwrap_or(0);
1947
1948		// Maybe remove the oldest signature and update the oldest
1949		if is_registry_full {
1950			let (expire_block_number, next_oldest) =
1951				PayloadSignatureRegistryList::<T>::get(pointer.oldest.clone())
1952					.ok_or(Error::<T>::SignatureRegistryCorrupted)?;
1953
1954			ensure!(
1955				current_block.gt(&expire_block_number),
1956				Error::<T>::SignatureRegistryLimitExceeded
1957			);
1958
1959			// Move the oldest in the list to the next oldest signature
1960			oldest = next_oldest.clone();
1961
1962			<PayloadSignatureRegistryList<T>>::remove(pointer.oldest);
1963		}
1964
1965		// Add the newest signature if we are not the first
1966		if pointer.count != 0 {
1967			<PayloadSignatureRegistryList<T>>::insert(
1968				pointer.newest,
1969				(pointer.newest_expires_at, signature.clone()),
1970			);
1971		}
1972
1973		// Update the pointer.newest to have the signature that just came in
1974		PayloadSignatureRegistryPointer::<T>::put(SignatureRegistryPointer {
1975			// The count doesn't change if list is full
1976			count: if is_registry_full { pointer.count } else { pointer.count + 1 },
1977			newest: signature.clone(),
1978			newest_expires_at: signature_expires_at,
1979			oldest,
1980		});
1981
1982		Ok(())
1983	}
1984
1985	/// The furthest in the future a mortality_block value is allowed
1986	/// to be for current_block
1987	/// This is calculated to be past the risk of a replay attack
1988	fn mortality_block_limit(current_block: BlockNumberFor<T>) -> BlockNumberFor<T> {
1989		let mortality_size = T::MortalityWindowSize::get();
1990		current_block + BlockNumberFor::<T>::from(mortality_size)
1991	}
1992}
1993
1994#[cfg(feature = "runtime-benchmarks")]
1995impl<T: Config> MsaBenchmarkHelper<T::AccountId> for Pallet<T> {
1996	/// adds delegation relationship with permitted schema ids
1997	fn set_delegation_relationship(
1998		provider: ProviderId,
1999		delegator: DelegatorId,
2000		schemas: Vec<SchemaId>,
2001	) -> DispatchResult {
2002		Self::add_provider(provider, delegator, schemas)?;
2003		Ok(())
2004	}
2005
2006	/// adds a new key to specified msa
2007	fn add_key(msa_id: MessageSourceId, key: T::AccountId) -> DispatchResult {
2008		Self::add_key(msa_id, &key, EMPTY_FUNCTION)?;
2009		Ok(())
2010	}
2011}
2012
2013#[cfg(feature = "runtime-benchmarks")]
2014impl<T: Config> RegisterProviderBenchmarkHelper for Pallet<T> {
2015	/// Create a registered provider for benchmarks
2016	fn create(provider_id: MessageSourceId, name: Vec<u8>) -> DispatchResult {
2017		let name = BoundedVec::<u8, T::MaxProviderNameSize>::try_from(name).expect("error");
2018		Self::create_registered_provider(provider_id.into(), name)?;
2019
2020		Ok(())
2021	}
2022}
2023
2024impl<T: Config> MsaLookup for Pallet<T> {
2025	type AccountId = T::AccountId;
2026
2027	fn get_msa_id(key: &Self::AccountId) -> Option<MessageSourceId> {
2028		Self::get_owner_of(key)
2029	}
2030}
2031
2032impl<T: Config> MsaValidator for Pallet<T> {
2033	type AccountId = T::AccountId;
2034
2035	fn ensure_valid_msa_key(key: &T::AccountId) -> Result<MessageSourceId, DispatchError> {
2036		Self::ensure_valid_msa_key(key)
2037	}
2038}
2039
2040impl<T: Config> ProviderLookup for Pallet<T> {
2041	type BlockNumber = BlockNumberFor<T>;
2042	type MaxSchemaGrantsPerDelegation = T::MaxSchemaGrantsPerDelegation;
2043	type SchemaId = SchemaId;
2044
2045	fn get_delegation_of(
2046		delegator: DelegatorId,
2047		provider: ProviderId,
2048	) -> Option<Delegation<SchemaId, Self::BlockNumber, Self::MaxSchemaGrantsPerDelegation>> {
2049		DelegatorAndProviderToDelegation::<T>::get(delegator, provider)
2050	}
2051}
2052
2053impl<T: Config> DelegationValidator for Pallet<T> {
2054	type BlockNumber = BlockNumberFor<T>;
2055	type MaxSchemaGrantsPerDelegation = T::MaxSchemaGrantsPerDelegation;
2056	type SchemaId = SchemaId;
2057
2058	/// Check that the delegator has an active delegation to the provider.
2059	/// `block_number`: Provide `None` to know if the delegation is active at the current block.
2060	///                 Provide Some(N) to know if the delegation was or will be active at block N.
2061	///
2062	/// # Errors
2063	/// * [`Error::DelegationNotFound`]
2064	/// * [`Error::DelegationRevoked`]
2065	/// * [`Error::CannotPredictValidityPastCurrentBlock`]
2066	///
2067	fn ensure_valid_delegation(
2068		provider_id: ProviderId,
2069		delegator_id: DelegatorId,
2070		block_number: Option<BlockNumberFor<T>>,
2071	) -> Result<
2072		Delegation<SchemaId, BlockNumberFor<T>, T::MaxSchemaGrantsPerDelegation>,
2073		DispatchError,
2074	> {
2075		let info = DelegatorAndProviderToDelegation::<T>::get(delegator_id, provider_id)
2076			.ok_or(Error::<T>::DelegationNotFound)?;
2077		let current_block = frame_system::Pallet::<T>::block_number();
2078		let requested_block = match block_number {
2079			Some(block_number) => {
2080				ensure!(
2081					current_block >= block_number,
2082					Error::<T>::CannotPredictValidityPastCurrentBlock
2083				);
2084				block_number
2085			},
2086			None => current_block,
2087		};
2088
2089		if info.revoked_at == BlockNumberFor::<T>::zero() {
2090			return Ok(info);
2091		}
2092		ensure!(info.revoked_at >= requested_block, Error::<T>::DelegationRevoked);
2093
2094		Ok(info)
2095	}
2096}
2097
2098impl<T: Config> TargetValidator for Pallet<T> {
2099	fn validate(target: MessageSourceId) -> bool {
2100		Self::is_registered_provider(target)
2101	}
2102}
2103
2104impl<T: Config> SchemaGrantValidator<BlockNumberFor<T>> for Pallet<T> {
2105	/// Check if provider is allowed to publish for a given schema_id for a given delegator
2106	///
2107	/// # Errors
2108	/// * [`Error::DelegationNotFound`]
2109	/// * [`Error::DelegationRevoked`]
2110	/// * [`Error::SchemaNotGranted`]
2111	/// * [`Error::CannotPredictValidityPastCurrentBlock`]
2112	///
2113	fn ensure_valid_schema_grant(
2114		provider: ProviderId,
2115		delegator: DelegatorId,
2116		schema_id: SchemaId,
2117		block_number: BlockNumberFor<T>,
2118	) -> DispatchResult {
2119		let provider_info = Self::ensure_valid_delegation(provider, delegator, Some(block_number))?;
2120
2121		let schema_permission_revoked_at_block_number = provider_info
2122			.schema_permissions
2123			.get(&schema_id)
2124			.ok_or(Error::<T>::SchemaNotGranted)?;
2125
2126		if *schema_permission_revoked_at_block_number == BlockNumberFor::<T>::zero() {
2127			return Ok(());
2128		}
2129
2130		ensure!(
2131			block_number <= *schema_permission_revoked_at_block_number,
2132			Error::<T>::SchemaNotGranted
2133		);
2134
2135		Ok(())
2136	}
2137}
2138
2139impl<T: Config> MsaKeyProvider for Pallet<T> {
2140	type AccountId = T::AccountId;
2141	// Returns true if ALL of the following are true:
2142	// - The new key is Ethereum-compatible
2143	// - Msa exists
2144	// - The stored msa_id for the key == `msa_id`
2145	// - It has only one key associated with it
2146	fn key_eligible_for_subsidized_addition(
2147		old_key: Self::AccountId,
2148		new_key: Self::AccountId,
2149		msa_id: MessageSourceId,
2150	) -> bool {
2151		let new_address32 = T::ConvertIntoAccountId32::convert((new_key).clone());
2152		if EthereumAddressMapper::is_ethereum_address(&new_address32) {
2153			if let Some(stored_msa_id) = Self::get_msa_id(&old_key) {
2154				return stored_msa_id == msa_id && PublicKeyCountForMsaId::<T>::get(msa_id).eq(&1u8);
2155			}
2156		}
2157		false
2158	}
2159}
2160
2161/// The TransactionExtension trait is implemented on CheckFreeExtrinsicUse to validate that a provider
2162/// has not already been revoked if the calling extrinsic is revoking a provider to an MSA. The
2163/// purpose of this is to ensure that the revoke_delegation_by_delegator extrinsic cannot be
2164/// repeatedly called and flood the network.
2165#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)]
2166#[scale_info(skip_type_params(T))]
2167pub struct CheckFreeExtrinsicUse<T: Config + Send + Sync>(PhantomData<T>);
2168
2169impl<T: Config + Send + Sync> CheckFreeExtrinsicUse<T> {
2170	/// Validates the delegation by making sure that the MSA ids used are valid and the delegation is
2171	/// is still active. Returns a `ValidTransaction` or wrapped [`ValidityError`]
2172	/// # Arguments:
2173	/// * `account_id`: the account id of the delegator that is revoking the delegation relationship
2174	/// *  `provider_msa_id` the MSA ID of the provider (the "other end" of the delegation).
2175	///
2176	/// # Errors
2177	/// * [`ValidityError::InvalidMsaKey`] - if  `account_id` does not have an MSA
2178	/// * [`ValidityError::InvalidDelegation`] - if the delegation with `delegator_msa_id` is invalid
2179	///
2180	pub fn validate_delegation_by_delegator(
2181		account_id: &T::AccountId,
2182		provider_msa_id: &MessageSourceId,
2183	) -> TransactionValidity {
2184		const TAG_PREFIX: &str = "DelegatorDelegationRevocation";
2185		let delegator_msa_id: DelegatorId = Pallet::<T>::ensure_valid_msa_key(account_id)
2186			.map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?
2187			.into();
2188		let provider_msa_id = ProviderId(*provider_msa_id);
2189
2190		Pallet::<T>::ensure_valid_delegation(provider_msa_id, delegator_msa_id, None)
2191			.map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidDelegation as u8))?;
2192		ValidTransaction::with_tag_prefix(TAG_PREFIX).and_provides(account_id).build()
2193	}
2194
2195	/// Validates the delegation by making sure that the MSA ids used are valid and that the delegation
2196	/// is still active. Returns a `ValidTransaction` or wrapped [`ValidityError`]
2197	/// # Arguments:
2198	/// * `account_id`: the account id of the provider that is revoking the delegation relationship
2199	/// *  `delegator_msa_id` the MSA ID of the delegator (the "other end" of the delegation).
2200	///
2201	/// # Errors
2202	/// * [`ValidityError::InvalidMsaKey`] - if  `account_id` does not have an MSA
2203	/// * [`ValidityError::InvalidDelegation`] - if the delegation with `delegator_msa_id` is invalid
2204	///
2205	pub fn validate_delegation_by_provider(
2206		account_id: &T::AccountId,
2207		delegator_msa_id: &MessageSourceId,
2208	) -> TransactionValidity {
2209		const TAG_PREFIX: &str = "ProviderDelegationRevocation";
2210
2211		let provider_msa_id: ProviderId = Pallet::<T>::ensure_valid_msa_key(account_id)
2212			.map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?
2213			.into();
2214		let delegator_msa_id = DelegatorId(*delegator_msa_id);
2215
2216		// Verify the delegation exists and is active
2217		Pallet::<T>::ensure_valid_delegation(provider_msa_id, delegator_msa_id, None)
2218			.map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidDelegation as u8))?;
2219		ValidTransaction::with_tag_prefix(TAG_PREFIX).and_provides(account_id).build()
2220	}
2221
2222	/// Validates that a key being revoked is both valid and owned by a valid MSA account.
2223	/// Returns a `ValidTransaction` or wrapped [`ValidityError::InvalidMsaKey`]
2224	/// Arguments:
2225	/// * `signing_public_key`: the account id calling for revoking the key, and which
2226	///   owns the msa also associated with `key`
2227	/// * `public_key_to_delete`: the account id to revoke as an access key for account_id's msa
2228	///
2229	/// # Errors
2230	/// * [`ValidityError::InvalidSelfRemoval`] - if `signing_public_key` and `public_key_to_delete` are the same.
2231	/// * [`ValidityError::InvalidMsaKey`] - if  `account_id` does not have an MSA or if
2232	///   'public_key_to_delete' does not have an MSA.
2233	/// * [`ValidityError::NotKeyOwner`] - if the `signing_public_key` and `public_key_to_delete` do not belong to the same MSA ID.
2234	pub fn validate_key_delete(
2235		signing_public_key: &T::AccountId,
2236		public_key_to_delete: &T::AccountId,
2237	) -> TransactionValidity {
2238		const TAG_PREFIX: &str = "KeyRevocation";
2239
2240		ensure!(
2241			signing_public_key != public_key_to_delete,
2242			InvalidTransaction::Custom(ValidityError::InvalidSelfRemoval as u8)
2243		);
2244
2245		let maybe_owner_msa_id: MessageSourceId =
2246			Pallet::<T>::ensure_valid_msa_key(signing_public_key)
2247				.map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?;
2248
2249		let msa_id_for_key_to_delete: MessageSourceId =
2250			Pallet::<T>::ensure_valid_msa_key(public_key_to_delete)
2251				.map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?;
2252
2253		ensure!(
2254			maybe_owner_msa_id == msa_id_for_key_to_delete,
2255			InvalidTransaction::Custom(ValidityError::NotKeyOwner as u8)
2256		);
2257
2258		ValidTransaction::with_tag_prefix(TAG_PREFIX)
2259			.and_provides(signing_public_key)
2260			.build()
2261	}
2262
2263	/// Validates that a MSA being retired exists, does not belong to a registered provider,
2264	/// that `account_id` is the only access key associated with the MSA,
2265	/// does not have a token balance associated with it,
2266	/// and that there are no delegations to providers.
2267	/// Returns a `ValidTransaction` or wrapped [`ValidityError]
2268	/// # Arguments:
2269	/// * account_id: the account id associated with the MSA to retire
2270	///
2271	/// # Errors
2272	/// * [`ValidityError::InvalidMsaKey`]
2273	/// * [`ValidityError::InvalidRegisteredProviderCannotBeRetired`]
2274	/// * [`ValidityError::InvalidMoreThanOneKeyExists`]
2275	/// * [`ValidityError::InvalidNonZeroProviderDelegations`]
2276	/// * [`ValidityError::InvalidMsaHoldingTokenCannotBeRetired`]
2277	///
2278	pub fn ensure_msa_can_retire(account_id: &T::AccountId) -> TransactionValidity {
2279		const TAG_PREFIX: &str = "MSARetirement";
2280		let msa_id = Pallet::<T>::ensure_valid_msa_key(account_id)
2281			.map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?;
2282
2283		ensure!(
2284			!Pallet::<T>::is_registered_provider(msa_id),
2285			InvalidTransaction::Custom(
2286				ValidityError::InvalidRegisteredProviderCannotBeRetired as u8
2287			)
2288		);
2289
2290		let msa_handle = T::HandleProvider::get_handle_for_msa(msa_id);
2291		ensure!(
2292			msa_handle.is_none(),
2293			InvalidTransaction::Custom(ValidityError::HandleNotRetired as u8)
2294		);
2295
2296		let key_count = PublicKeyCountForMsaId::<T>::get(msa_id);
2297		ensure!(
2298			key_count == 1,
2299			InvalidTransaction::Custom(ValidityError::InvalidMoreThanOneKeyExists as u8)
2300		);
2301
2302		let delegator_id = DelegatorId(msa_id);
2303		let has_active_delegations: bool = DelegatorAndProviderToDelegation::<T>::iter_key_prefix(
2304			delegator_id,
2305		)
2306		.any(|provider_id| {
2307			Pallet::<T>::ensure_valid_delegation(provider_id, delegator_id, None).is_ok()
2308		});
2309
2310		ensure!(
2311			!has_active_delegations,
2312			InvalidTransaction::Custom(ValidityError::InvalidNonZeroProviderDelegations as u8)
2313		);
2314
2315		// - Get account address for MSA
2316		let msa_address = Pallet::<T>::msa_id_to_eth_address(msa_id);
2317
2318		// - Convert to AccountId
2319		let mut bytes = &EthereumAddressMapper::to_bytes32(&msa_address.0)[..];
2320		let msa_account_id = T::AccountId::decode(&mut bytes).map_err(|_| {
2321			log::error!("Failed to decode MSA account ID from Ethereum address");
2322			InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8)
2323		})?;
2324
2325		// - Check that the MSA does not have a token balance
2326		let msa_balance = T::Currency::reducible_balance(
2327			&msa_account_id,
2328			Preservation::Expendable,
2329			Fortitude::Polite,
2330		);
2331		ensure!(
2332			msa_balance == Zero::zero(),
2333			InvalidTransaction::Custom(ValidityError::InvalidMsaHoldingTokenCannotBeRetired as u8)
2334		);
2335
2336		ValidTransaction::with_tag_prefix(TAG_PREFIX).and_provides(account_id).build()
2337	}
2338
2339	/// Validates that a request to withdraw tokens from an MSA passes the following checks:
2340	/// - Receiver (origin) is NOT a control key for any MSA
2341	/// - Signed payload matches MSA ID and signature validation
2342	/// - Signature is not a duplicate
2343	/// - MSA has tokens available to be withdrawn
2344	///
2345	/// # Errors
2346	///
2347	/// `[ValidityError::NotKeyOwner]` - transaction origin does not match the authorized public key
2348	/// `[ValidityError::MsaOwnershipInvalidSignature]` - payload verification failed (bad signature, duplicate signature, invalid payload, payload/signature mismatch)
2349	/// `[ValidityError::IneligibleOrigin]` - transaction origin is an MSA control key
2350	/// `[ValidityError::InsufficientBalanceToWithdraw]` - MSA balance is zero
2351	/// `[ValidityError::InvalidMsaKey]` - signing MSA control key does not match MSA ID in payload
2352	///
2353	pub fn validate_msa_token_withdrawal(
2354		receiver_account_id: &T::AccountId,
2355		msa_owner_public_key: &T::AccountId,
2356		msa_owner_proof: &MultiSignature,
2357		authorization_payload: &AuthorizedKeyData<T>,
2358	) -> TransactionValidity {
2359		const TAG_PREFIX: &str = "MsaTokenWithdrawal";
2360
2361		ensure!(
2362			*receiver_account_id == authorization_payload.authorized_public_key,
2363			InvalidTransaction::Custom(ValidityError::NotKeyOwner as u8)
2364		);
2365
2366		ensure!(
2367			authorization_payload.discriminant == PayloadTypeDiscriminator::AuthorizedKeyData,
2368			InvalidTransaction::Custom(ValidityError::MsaOwnershipInvalidSignature as u8)
2369		);
2370		ensure!(
2371			Pallet::<T>::verify_signature(
2372				msa_owner_proof,
2373				msa_owner_public_key,
2374				authorization_payload
2375			),
2376			InvalidTransaction::Custom(ValidityError::MsaOwnershipInvalidSignature as u8)
2377		);
2378
2379		// Origin must NOT be an MSA control key
2380		ensure!(
2381			!PublicKeyToMsaId::<T>::contains_key(receiver_account_id),
2382			InvalidTransaction::Custom(ValidityError::IneligibleOrigin as u8)
2383		);
2384
2385		Pallet::<T>::check_signature_against_registry(
2386			msa_owner_proof,
2387			authorization_payload.expiration,
2388		)
2389		.map_err(|_| {
2390			InvalidTransaction::Custom(ValidityError::MsaOwnershipInvalidSignature as u8)
2391		})?;
2392
2393		let msa_id = authorization_payload.msa_id;
2394
2395		Pallet::<T>::ensure_msa_owner(msa_owner_public_key, msa_id)
2396			.map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?;
2397
2398		// - Get account address for MSA
2399		let msa_address = Pallet::<T>::msa_id_to_eth_address(msa_id);
2400
2401		// - Convert to AccountId
2402		let mut bytes = &EthereumAddressMapper::to_bytes32(&msa_address.0)[..];
2403		let msa_account_id = T::AccountId::decode(&mut bytes).map_err(|_| {
2404			log::error!("Failed to decode MSA account ID from Ethereum address");
2405			InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8)
2406		})?;
2407
2408		// - Check that the MSA has a balance to withdraw
2409		let msa_balance = T::Currency::reducible_balance(
2410			&msa_account_id,
2411			Preservation::Expendable,
2412			Fortitude::Polite,
2413		);
2414		ensure!(
2415			msa_balance > Zero::zero(),
2416			InvalidTransaction::Custom(ValidityError::InsufficientBalanceToWithdraw as u8)
2417		);
2418		ValidTransaction::with_tag_prefix(TAG_PREFIX)
2419			.and_provides(receiver_account_id)
2420			.build()
2421	}
2422}
2423
2424/// Errors related to the validity of the CheckFreeExtrinsicUse signed extension.
2425pub enum ValidityError {
2426	/// Delegation to provider is not found or expired.
2427	InvalidDelegation,
2428	/// MSA key as been revoked.
2429	InvalidMsaKey,
2430	/// Cannot retire a registered provider MSA
2431	InvalidRegisteredProviderCannotBeRetired,
2432	/// More than one account key exists for the MSA during retire attempt
2433	InvalidMoreThanOneKeyExists,
2434	/// A transaction's Origin (AccountId) may not remove itself
2435	InvalidSelfRemoval,
2436	/// NotKeyOwner
2437	NotKeyOwner,
2438	/// InvalidNonZeroProviderDelegations
2439	InvalidNonZeroProviderDelegations,
2440	/// HandleNotRetired
2441	HandleNotRetired,
2442	/// Cryptographic signature verification failed
2443	MsaOwnershipInvalidSignature,
2444	/// MSA balance is zero
2445	InsufficientBalanceToWithdraw,
2446	/// Origin is ineleligible for the current transaction
2447	IneligibleOrigin,
2448	/// Cannot retire an MSA that has a token balance
2449	InvalidMsaHoldingTokenCannotBeRetired,
2450}
2451
2452impl<T: Config + Send + Sync> CheckFreeExtrinsicUse<T> {
2453	/// Create new `TransactionExtension` to check runtime version.
2454	pub fn new() -> Self {
2455		Self(PhantomData)
2456	}
2457}
2458
2459impl<T: Config + Send + Sync> core::fmt::Debug for CheckFreeExtrinsicUse<T> {
2460	#[cfg(feature = "std")]
2461	fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
2462		write!(f, "CheckFreeExtrinsicUse<{:?}>", self.0)
2463	}
2464	#[cfg(not(feature = "std"))]
2465	fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
2466		Ok(())
2467	}
2468}
2469
2470/// The info passed between the validate and prepare steps for the `CheckFreeExtrinsicUse` extension.
2471#[derive(RuntimeDebugNoBound)]
2472pub enum Val {
2473	/// Valid transaction, no weight refund.
2474	Valid,
2475	/// Weight refund for the transaction.
2476	Refund(Weight),
2477}
2478
2479/// The info passed between the prepare and post-dispatch steps for the `CheckFreeExtrinsicUse` extension.
2480#[derive(RuntimeDebugNoBound)]
2481pub enum Pre {
2482	/// Valid transaction, no weight refund.
2483	Valid,
2484	/// Weight refund for the transaction.
2485	Refund(Weight),
2486}
2487
2488impl<T: Config + Send + Sync> TransactionExtension<T::RuntimeCall> for CheckFreeExtrinsicUse<T>
2489where
2490	T::RuntimeCall: Dispatchable<Info = DispatchInfo> + IsSubType<Call<T>>,
2491	<T as frame_system::Config>::RuntimeOrigin: AsSystemOriginSigner<T::AccountId> + Clone,
2492{
2493	const IDENTIFIER: &'static str = "CheckFreeExtrinsicUse";
2494	type Implicit = ();
2495	type Val = Val;
2496	type Pre = Pre;
2497
2498	fn weight(&self, call: &T::RuntimeCall) -> Weight {
2499		match call.is_sub_type() {
2500			Some(Call::revoke_delegation_by_provider { .. }) =>
2501				T::WeightInfo::check_free_extrinsic_use_revoke_delegation_by_provider(),
2502			Some(Call::revoke_delegation_by_delegator { .. }) =>
2503				T::WeightInfo::check_free_extrinsic_use_revoke_delegation_by_delegator(),
2504			Some(Call::delete_msa_public_key { .. }) =>
2505				T::WeightInfo::check_free_extrinsic_use_delete_msa_public_key(),
2506			Some(Call::retire_msa { .. }) => T::WeightInfo::check_free_extrinsic_use_retire_msa(),
2507			Some(Call::withdraw_tokens { .. }) =>
2508				T::WeightInfo::check_free_extrinsic_use_withdraw_tokens(),
2509			_ => Weight::zero(),
2510		}
2511	}
2512
2513	fn validate(
2514		&self,
2515		origin: <T as frame_system::Config>::RuntimeOrigin,
2516		call: &T::RuntimeCall,
2517		_info: &DispatchInfoOf<T::RuntimeCall>,
2518		_len: usize,
2519		_self_implicit: Self::Implicit,
2520		_inherited_implication: &impl Encode,
2521		_source: TransactionSource,
2522	) -> ValidateResult<Self::Val, T::RuntimeCall> {
2523		let weight = self.weight(call);
2524		let Some(who) = origin.as_system_origin_signer() else {
2525			return Ok((ValidTransaction::default(), Val::Refund(weight), origin));
2526		};
2527		let validity = match call.is_sub_type() {
2528			Some(Call::revoke_delegation_by_provider { delegator, .. }) =>
2529				Self::validate_delegation_by_provider(who, delegator),
2530			Some(Call::revoke_delegation_by_delegator { provider_msa_id, .. }) =>
2531				Self::validate_delegation_by_delegator(who, provider_msa_id),
2532			Some(Call::delete_msa_public_key { public_key_to_delete, .. }) =>
2533				Self::validate_key_delete(who, public_key_to_delete),
2534			Some(Call::retire_msa { .. }) => Self::ensure_msa_can_retire(who),
2535			Some(Call::withdraw_tokens {
2536				msa_owner_public_key,
2537				msa_owner_proof,
2538				authorization_payload,
2539			}) => Self::validate_msa_token_withdrawal(
2540				who,
2541				msa_owner_public_key,
2542				msa_owner_proof,
2543				authorization_payload,
2544			),
2545			_ => Ok(Default::default()),
2546		};
2547		validity.map(|v| (v, Val::Valid, origin))
2548	}
2549
2550	fn prepare(
2551		self,
2552		val: Self::Val,
2553		_origin: &<T as frame_system::Config>::RuntimeOrigin,
2554		_call: &T::RuntimeCall,
2555		_info: &DispatchInfoOf<T::RuntimeCall>,
2556		_len: usize,
2557	) -> Result<Self::Pre, TransactionValidityError> {
2558		match val {
2559			Val::Valid => Ok(Pre::Valid),
2560			Val::Refund(w) => Ok(Pre::Refund(w)),
2561		}
2562	}
2563
2564	fn post_dispatch_details(
2565		pre: Self::Pre,
2566		_info: &DispatchInfo,
2567		_post_info: &PostDispatchInfoOf<T::RuntimeCall>,
2568		_len: usize,
2569		_result: &sp_runtime::DispatchResult,
2570	) -> Result<Weight, TransactionValidityError> {
2571		match pre {
2572			Pre::Valid => Ok(Weight::zero()),
2573			Pre::Refund(w) => Ok(w),
2574		}
2575	}
2576}