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