1#![doc = include_str!("../README.md")]
11#![allow(clippy::expect_used)]
25#![cfg_attr(not(feature = "std"), no_std)]
26#![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 cid::Cid;
36use core::fmt::Debug;
37use frame_support::{
38 dispatch::{DispatchInfo, DispatchResult, PostDispatchInfo},
39 pallet_prelude::*,
40 traits::{
41 tokens::{
42 fungible::{Inspect as InspectFungible, Mutate},
43 Fortitude, Preservation,
44 },
45 Get, IsSubType,
46 },
47};
48use lazy_static::lazy_static;
49use parity_scale_codec::{Decode, Encode};
50
51use common_runtime::signature::check_signature;
52
53#[cfg(feature = "runtime-benchmarks")]
54use common_primitives::benchmarks::{MsaBenchmarkHelper, RegisterProviderBenchmarkHelper};
55
56use alloc::{boxed::Box, vec, vec::Vec};
57use common_primitives::{
58 capacity::TargetValidator,
59 cid::compute_cid_v1,
60 handles::HandleProvider,
61 msa::*,
62 node::{EIP712Encode, ProposalProvider},
63 schema::{SchemaId, SchemaValidator},
64 signatures::{AccountAddressMapper, EthereumAddressMapper},
65};
66use frame_system::pallet_prelude::*;
67pub use pallet::*;
68use scale_info::TypeInfo;
69use sp_core::crypto::AccountId32;
70use sp_io::hashing::keccak_256;
71#[allow(unused)]
72use sp_runtime::{
73 traits::{
74 AsSystemOriginSigner, BlockNumberProvider, Convert, DispatchInfoOf, Dispatchable,
75 PostDispatchInfoOf, TransactionExtension, ValidateResult, Zero,
76 },
77 ArithmeticError, DispatchError, MultiSignature, Weight,
78};
79pub use types::{
80 AddKeyData, AddProvider, ApplicationIndex, AuthorizedKeyData, PermittedDelegationIntents,
81 RecoveryCommitment, RecoveryCommitmentPayload,
82};
83pub use weights::*;
84
85pub mod offchain_storage;
87use crate::types::{LogoCid, PayloadTypeDiscriminator, RecoveryHash};
88pub use offchain_storage::*;
89
90#[cfg(feature = "runtime-benchmarks")]
91mod benchmarking;
92
93#[cfg(test)]
94mod tests;
95
96pub mod types;
97
98pub mod weights;
99
100#[allow(missing_docs)]
102pub mod migration;
103
104#[frame_support::pallet]
105pub mod pallet {
106 use crate::types::RecoveryHash;
107
108 use super::*;
109
110 #[pallet::config]
111 pub trait Config: frame_system::Config {
112 #[allow(deprecated)]
114 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
115
116 type WeightInfo: WeightInfo;
118
119 type ConvertIntoAccountId32: Convert<Self::AccountId, AccountId32>;
121
122 #[pallet::constant]
124 type MaxPublicKeysPerMsa: Get<u8>;
125
126 #[pallet::constant]
128 type MaxGrantsPerDelegation: Get<u32>;
129
130 #[pallet::constant]
132 type MaxProviderNameSize: Get<u32> + Clone + Debug + PartialEq + Eq;
133
134 type SchemaValidator: SchemaValidator<SchemaId>;
136
137 type HandleProvider: HandleProvider;
139
140 #[pallet::constant]
142 type MortalityWindowSize: Get<u32>;
143
144 #[pallet::constant]
146 type MaxSignaturesStored: Get<Option<u32>>;
147
148 type CreateProviderViaGovernanceOrigin: EnsureOrigin<Self::RuntimeOrigin>;
150
151 type RecoveryProviderApprovalOrigin: EnsureOrigin<Self::RuntimeOrigin>;
153
154 type Proposal: Parameter
156 + Dispatchable<RuntimeOrigin = Self::RuntimeOrigin, PostInfo = PostDispatchInfo>
157 + From<Call<Self>>;
158
159 type ProposalProvider: ProposalProvider<Self::AccountId, Self::Proposal>;
161
162 type Currency: Mutate<Self::AccountId> + InspectFungible<Self::AccountId>;
164
165 #[pallet::constant]
167 type MaxLanguageCodeSize: Get<u32> + Clone + Debug + PartialEq + Eq;
168
169 #[pallet::constant]
171 type MaxLogoSize: Get<u32> + Clone + Debug + PartialEq + Eq;
172
173 #[pallet::constant]
175 type MaxLogoCidSize: Get<u32> + Clone + Debug + PartialEq + Eq;
176
177 #[pallet::constant]
179 type MaxLocaleCount: Get<u32> + Clone + Debug + PartialEq + Eq;
180 }
181
182 pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(2);
184
185 #[pallet::pallet]
186 #[pallet::storage_version(STORAGE_VERSION)]
187 pub struct Pallet<T>(_);
188
189 #[pallet::storage]
194 pub type CurrentMsaIdentifierMaximum<T> = StorageValue<_, MessageSourceId, ValueQuery>;
195
196 #[pallet::storage]
200 pub type DelegatorAndProviderToDelegation<T: Config> = StorageDoubleMap<
201 _,
202 Twox64Concat,
203 DelegatorId,
204 Twox64Concat,
205 ProviderId,
206 Delegation<IntentId, BlockNumberFor<T>, T::MaxGrantsPerDelegation>,
207 OptionQuery,
208 >;
209
210 #[pallet::storage]
214 pub type RecoveryProviders<T: Config> =
215 StorageMap<_, Twox64Concat, ProviderId, bool, OptionQuery>;
216
217 #[deprecated(
221 note = "Use ProviderToRegistryEntryV2 instead, this will removed from frequency version 1.17.6"
222 )]
223 #[pallet::storage]
224 pub type ProviderToRegistryEntry<T: Config> = StorageMap<
225 _,
226 Twox64Concat,
227 ProviderId,
228 crate::migration::v1::ProviderRegistryEntry<T::MaxProviderNameSize>,
229 OptionQuery,
230 >;
231
232 #[pallet::storage]
236 pub type ProviderToRegistryEntryV2<T: Config> = StorageMap<
237 _,
238 Twox64Concat,
239 ProviderId,
240 ProviderRegistryEntry<
241 T::MaxProviderNameSize,
242 T::MaxLanguageCodeSize,
243 T::MaxLogoCidSize,
244 T::MaxLocaleCount,
245 >,
246 OptionQuery,
247 >;
248
249 #[pallet::storage]
253 pub type PublicKeyToMsaId<T: Config> =
254 StorageMap<_, Twox64Concat, T::AccountId, MessageSourceId, OptionQuery>;
255
256 #[pallet::storage]
260 pub(super) type PublicKeyCountForMsaId<T: Config> =
261 StorageMap<_, Twox64Concat, MessageSourceId, u8, ValueQuery>;
262
263 #[pallet::storage]
280 pub(super) type PayloadSignatureRegistryList<T: Config> = StorageMap<
281 _, Twox64Concat, MultiSignature, (BlockNumberFor<T>, MultiSignature), OptionQuery, GetDefault, T::MaxSignaturesStored, >;
289
290 #[pallet::storage]
294 pub(super) type PayloadSignatureRegistryPointer<T: Config> =
295 StorageValue<_, SignatureRegistryPointer<BlockNumberFor<T>>>;
296
297 #[pallet::storage]
300 #[pallet::whitelist_storage]
301 pub(super) type OffchainIndexEventCount<T: Config> = StorageValue<_, u16, ValueQuery>;
302
303 #[pallet::storage]
307 pub type MsaIdToRecoveryCommitment<T: Config> =
308 StorageMap<_, Twox64Concat, MessageSourceId, RecoveryCommitment, OptionQuery>;
309
310 #[pallet::storage]
314 #[pallet::getter(fn approved_logos)]
315 pub type ApprovedLogos<T: Config> =
316 StorageMap<_, Twox64Concat, LogoCid<T>, BoundedVec<u8, T::MaxLogoSize>, OptionQuery>;
317
318 #[pallet::storage]
321 #[pallet::getter(fn next_application_index)]
322 pub(super) type NextApplicationIndex<T: Config> =
323 StorageMap<_, Twox64Concat, ProviderId, ApplicationIndex, ValueQuery>;
324
325 #[pallet::storage]
328 #[pallet::getter(fn provider_applications)]
329 pub(super) type ProviderToApplicationRegistry<T: Config> = StorageDoubleMap<
330 _,
331 Twox64Concat,
332 ProviderId,
333 Twox64Concat,
334 ApplicationIndex,
335 ApplicationContext<
336 T::MaxProviderNameSize,
337 T::MaxLanguageCodeSize,
338 T::MaxLogoCidSize,
339 T::MaxLocaleCount,
340 >,
341 OptionQuery,
342 >;
343
344 #[pallet::event]
345 #[pallet::generate_deposit(pub (super) fn deposit_event)]
346 pub enum Event<T: Config> {
347 MsaCreated {
349 msa_id: MessageSourceId,
351
352 key: T::AccountId,
354 },
355 PublicKeyAdded {
357 msa_id: MessageSourceId,
359
360 key: T::AccountId,
362 },
363 PublicKeyDeleted {
365 key: T::AccountId,
367 },
368 DelegationGranted {
370 provider_id: ProviderId,
372
373 delegator_id: DelegatorId,
375 },
376 ProviderCreated {
378 provider_id: ProviderId,
380 },
381 DelegationRevoked {
383 provider_id: ProviderId,
385
386 delegator_id: DelegatorId,
388 },
389 MsaRetired {
391 msa_id: MessageSourceId,
393 },
394 DelegationUpdated {
396 provider_id: ProviderId,
398
399 delegator_id: DelegatorId,
401 },
402 RecoveryCommitmentAdded {
404 who: T::AccountId,
406
407 msa_id: MessageSourceId,
409
410 recovery_commitment: RecoveryCommitment,
412 },
413 RecoveryProviderApproved {
415 provider_id: ProviderId,
417 },
418 RecoveryProviderRemoved {
420 provider_id: ProviderId,
422 },
423 AccountRecovered {
425 msa_id: MessageSourceId,
427 recovery_provider: ProviderId,
429 new_control_key: T::AccountId,
431 },
432 RecoveryCommitmentInvalidated {
434 msa_id: MessageSourceId,
436 recovery_commitment: RecoveryCommitment,
438 },
439 ApplicationCreated {
441 provider_id: ProviderId,
443 application_id: ApplicationIndex,
445 },
446 ApplicationContextUpdated {
448 provider_id: ProviderId,
450 application_id: Option<ApplicationIndex>,
452 },
453 ProviderUpdated {
455 provider_id: ProviderId,
457 },
458 }
459
460 #[pallet::error]
461 pub enum Error<T> {
462 KeyAlreadyRegistered,
464
465 MsaIdOverflow,
467
468 MsaOwnershipInvalidSignature,
470
471 NotMsaOwner,
473
474 InvalidSignature,
476
477 NotKeyOwner,
479
480 NoKeyExists,
482
483 KeyLimitExceeded,
485
486 InvalidSelfRemoval,
488
489 InvalidSelfProvider,
491
492 InvalidSchemaId,
494
495 DuplicateProvider,
497
498 AddProviderSignatureVerificationFailed,
500
501 UnauthorizedDelegator,
503
504 UnauthorizedProvider,
506
507 DelegationRevoked,
509
510 DelegationNotFound,
512
513 DuplicateProviderRegistryEntry,
515
516 ExceedsMaxProviderNameSize,
518
519 ExceedsMaxSchemaGrantsPerDelegation,
521
522 SchemaNotGranted,
524
525 ProviderNotRegistered,
527
528 ProofHasExpired,
530
531 ProofNotYetValid,
533
534 SignatureAlreadySubmitted,
536
537 NewKeyOwnershipInvalidSignature,
539
540 CannotPredictValidityPastCurrentBlock,
542
543 SignatureRegistryLimitExceeded,
545
546 SignatureRegistryCorrupted,
548
549 InsufficientBalanceToWithdraw,
551
552 UnexpectedTokenTransferError,
554
555 NotAuthorizedRecoveryProvider,
557
558 InvalidRecoveryCommitment,
560
561 NoRecoveryCommitment,
563
564 InvalidCid,
566
567 UnsupportedCidVersion,
569
570 InvalidBCP47LanguageCode,
572
573 DuplicateApplicationRegistryEntry,
575
576 LogoCidNotApproved,
578
579 InvalidLogoBytes,
581
582 ApplicationNotFound,
584
585 ExceedsMaxGrantsPerDelegation,
587
588 InvalidIntentId,
590
591 PermissionNotGranted,
593 }
594
595 impl<T: Config> BlockNumberProvider for Pallet<T> {
596 type BlockNumber = BlockNumberFor<T>;
597
598 fn current_block_number() -> Self::BlockNumber {
599 frame_system::Pallet::<T>::block_number()
600 }
601 }
602
603 #[pallet::hooks]
604 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
605 fn on_initialize(_block_number: BlockNumberFor<T>) -> Weight {
606 <OffchainIndexEventCount<T>>::set(0u16);
607 let migration_weight = crate::migration::v2::on_initialize_migration::<T>(); T::DbWeight::get().reads_writes(1u64, 1u64).saturating_add(migration_weight)
609 }
610
611 fn offchain_worker(block_number: BlockNumberFor<T>) {
612 log::info!("Running offchain workers! {block_number:?}");
613 do_offchain_worker::<T>(block_number)
614 }
615 }
616
617 #[pallet::call]
618 impl<T: Config> Pallet<T> {
619 #[pallet::call_index(0)]
629 #[pallet::weight(T::WeightInfo::create())]
630 pub fn create(origin: OriginFor<T>) -> DispatchResult {
631 let public_key = ensure_signed(origin)?;
632
633 let (new_msa_id, new_public_key) = Self::create_account(public_key)?;
634
635 let event = Event::MsaCreated { msa_id: new_msa_id, key: new_public_key };
636 offchain_index_event::<T>(Some(&event), new_msa_id);
637 Self::deposit_event(event);
638 Ok(())
639 }
640
641 #[pallet::call_index(1)]
663 #[pallet::weight(T::WeightInfo::create_sponsored_account_with_delegation(
664 add_provider_payload.intent_ids.len() as u32
665 ))]
666 pub fn create_sponsored_account_with_delegation(
667 origin: OriginFor<T>,
668 delegator_key: T::AccountId,
669 proof: MultiSignature,
670 add_provider_payload: AddProvider,
671 ) -> DispatchResult {
672 let provider_key = ensure_signed(origin)?;
673
674 ensure!(
675 Self::verify_signature(&proof, &delegator_key, &add_provider_payload),
676 Error::<T>::InvalidSignature
677 );
678
679 Self::register_signature(&proof, add_provider_payload.expiration.into())?;
680
681 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
682 ensure!(
683 add_provider_payload.authorized_msa_id == provider_msa_id,
684 Error::<T>::UnauthorizedProvider
685 );
686
687 ensure!(
689 Self::is_registered_provider(provider_msa_id),
690 Error::<T>::ProviderNotRegistered
691 );
692
693 let (new_delegator_msa_id, new_delegator_public_key) =
694 Self::create_account(delegator_key)?;
695 Self::add_provider(
696 ProviderId(provider_msa_id),
697 DelegatorId(new_delegator_msa_id),
698 add_provider_payload.intent_ids,
699 )?;
700 let event =
701 Event::MsaCreated { msa_id: new_delegator_msa_id, key: new_delegator_public_key };
702 offchain_index_event::<T>(Some(&event), new_delegator_msa_id);
703 Self::deposit_event(event);
704 Self::deposit_event(Event::DelegationGranted {
705 delegator_id: DelegatorId(new_delegator_msa_id),
706 provider_id: ProviderId(provider_msa_id),
707 });
708 Ok(())
709 }
710
711 #[pallet::call_index(2)]
722 #[pallet::weight(T::WeightInfo::create_provider())]
723 pub fn create_provider(origin: OriginFor<T>, provider_name: Vec<u8>) -> DispatchResult {
724 let provider_key = ensure_signed(origin)?;
725 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
726 let bounded_name: BoundedVec<u8, T::MaxProviderNameSize> =
727 provider_name.try_into().map_err(|_| Error::<T>::ExceedsMaxProviderNameSize)?;
728
729 let entry = ProviderRegistryEntry { default_name: bounded_name, ..Default::default() };
730 Self::ensure_correct_cids(&entry)?;
731 Self::upsert_provider_for(provider_msa_id, entry, false)?;
732 Self::deposit_event(Event::ProviderCreated {
733 provider_id: ProviderId(provider_msa_id),
734 });
735 Ok(())
736 }
737
738 #[pallet::call_index(3)]
759 #[pallet::weight(T::WeightInfo::grant_delegation(add_provider_payload.intent_ids.len() as u32))]
760 pub fn grant_delegation(
761 origin: OriginFor<T>,
762 delegator_key: T::AccountId,
763 proof: MultiSignature,
764 add_provider_payload: AddProvider,
765 ) -> DispatchResult {
766 let provider_key = ensure_signed(origin)?;
767
768 ensure!(
769 Self::verify_signature(&proof, &delegator_key, &add_provider_payload),
770 Error::<T>::AddProviderSignatureVerificationFailed
771 );
772
773 Self::register_signature(&proof, add_provider_payload.expiration.into())?;
774 let (provider_id, delegator_id) =
775 Self::ensure_valid_registered_provider(&delegator_key, &provider_key)?;
776
777 ensure!(
778 add_provider_payload.authorized_msa_id == provider_id.0,
779 Error::<T>::UnauthorizedDelegator
780 );
781
782 Self::upsert_intent_permissions(
783 provider_id,
784 delegator_id,
785 add_provider_payload.intent_ids,
786 )?;
787 Self::deposit_event(Event::DelegationGranted { delegator_id, provider_id });
788
789 Ok(())
790 }
791
792 #[pallet::call_index(4)]
804 #[pallet::weight((T::WeightInfo::revoke_delegation_by_delegator(), DispatchClass::Normal, Pays::No))]
805 pub fn revoke_delegation_by_delegator(
806 origin: OriginFor<T>,
807 #[pallet::compact] provider_msa_id: MessageSourceId,
808 ) -> DispatchResult {
809 let who = ensure_signed(origin)?;
810
811 match PublicKeyToMsaId::<T>::get(&who) {
812 Some(delegator_msa_id) => {
813 let delegator_id = DelegatorId(delegator_msa_id);
814 let provider_id = ProviderId(provider_msa_id);
815 Self::revoke_provider(provider_id, delegator_id)?;
816 Self::deposit_event(Event::DelegationRevoked { delegator_id, provider_id });
817 },
818 None => {
819 log::error!(
820 "TransactionExtension did not catch invalid MSA for account {who:?}, "
821 );
822 },
823 }
824
825 Ok(())
826 }
827
828 #[pallet::call_index(5)]
854 #[pallet::weight(T::WeightInfo::add_public_key_to_msa())]
855 pub fn add_public_key_to_msa(
856 origin: OriginFor<T>,
857 msa_owner_public_key: T::AccountId,
858 msa_owner_proof: MultiSignature,
859 new_key_owner_proof: MultiSignature,
860 add_key_payload: AddKeyData<T>,
861 ) -> DispatchResult {
862 let _ = ensure_signed(origin)?;
863
864 ensure!(
865 Self::verify_signature(&msa_owner_proof, &msa_owner_public_key, &add_key_payload),
866 Error::<T>::MsaOwnershipInvalidSignature
867 );
868
869 ensure!(
870 Self::verify_signature(
871 &new_key_owner_proof,
872 &add_key_payload.new_public_key,
873 &add_key_payload
874 ),
875 Error::<T>::NewKeyOwnershipInvalidSignature
876 );
877
878 Self::register_signature(&msa_owner_proof, add_key_payload.expiration)?;
879 Self::register_signature(&new_key_owner_proof, add_key_payload.expiration)?;
880
881 let msa_id = add_key_payload.msa_id;
882
883 Self::ensure_msa_owner(&msa_owner_public_key, msa_id)?;
884
885 Self::add_key(msa_id, &add_key_payload.new_public_key.clone())?;
886
887 let event =
888 Event::PublicKeyAdded { msa_id, key: add_key_payload.new_public_key.clone() };
889 offchain_index_event::<T>(Some(&event), msa_id);
890 Self::deposit_event(event);
891
892 Ok(())
893 }
894
895 #[pallet::call_index(6)]
910 #[pallet::weight((T::WeightInfo::delete_msa_public_key(), DispatchClass::Normal, Pays::No))]
911 pub fn delete_msa_public_key(
912 origin: OriginFor<T>,
913 public_key_to_delete: T::AccountId,
914 ) -> DispatchResult {
915 let who = ensure_signed(origin)?;
916
917 match PublicKeyToMsaId::<T>::get(&who) {
918 Some(who_msa_id) => {
919 Self::delete_key_for_msa(who_msa_id, &public_key_to_delete)?;
920
921 let event = Event::PublicKeyDeleted { key: public_key_to_delete };
923 offchain_index_event::<T>(Some(&event), who_msa_id);
924 Self::deposit_event(event);
925 },
926 None => {
927 log::error!(
928 "TransactionExtension did not catch invalid MSA for account {who:?}"
929 );
930 },
931 }
932 Ok(())
933 }
934
935 #[pallet::call_index(7)]
947 #[pallet::weight((T::WeightInfo::revoke_delegation_by_provider(), DispatchClass::Normal, Pays::No))]
948 pub fn revoke_delegation_by_provider(
949 origin: OriginFor<T>,
950 #[pallet::compact] delegator: MessageSourceId,
951 ) -> DispatchResult {
952 let who = ensure_signed(origin)?;
953
954 match PublicKeyToMsaId::<T>::get(&who) {
958 Some(msa_id) => {
959 let provider_id = ProviderId(msa_id);
960 let delegator_id = DelegatorId(delegator);
961 Self::revoke_provider(provider_id, delegator_id)?;
962 Self::deposit_event(Event::DelegationRevoked { provider_id, delegator_id })
963 },
964 None => {
965 log::error!(
966 "TransactionExtension did not catch invalid MSA for account {who:?}"
967 );
968 },
969 }
970
971 Ok(())
972 }
973
974 #[pallet::call_index(10)]
996 #[pallet::weight((T::WeightInfo::retire_msa(), DispatchClass::Normal, Pays::No))]
997 pub fn retire_msa(origin: OriginFor<T>) -> DispatchResult {
998 let who = ensure_signed(origin)?;
1000
1001 match PublicKeyToMsaId::<T>::get(&who) {
1004 Some(msa_id) => {
1005 Self::delete_key_for_msa(msa_id, &who)?;
1006 let event = Event::PublicKeyDeleted { key: who };
1007 offchain_index_event::<T>(Some(&event), msa_id);
1008 Self::deposit_event(event);
1009 Self::deposit_event(Event::MsaRetired { msa_id });
1010 },
1011 None => {
1012 log::error!(
1013 "TransactionExtension did not catch invalid MSA for account {who:?}"
1014 );
1015 },
1016 }
1017 Ok(())
1018 }
1019
1020 #[pallet::call_index(11)]
1026 #[pallet::weight(T::WeightInfo::propose_to_be_provider_v2())]
1027 #[allow(deprecated)]
1028 #[deprecated(
1029 note = "please use `propose_to_be_provider_v2`, which supports additional provider metadata"
1030 )]
1031 pub fn propose_to_be_provider(
1032 origin: OriginFor<T>,
1033 provider_name: Vec<u8>,
1034 ) -> DispatchResult {
1035 let bounded_name: BoundedVec<u8, T::MaxProviderNameSize> =
1036 provider_name.try_into().map_err(|_| Error::<T>::ExceedsMaxProviderNameSize)?;
1037
1038 let entry = ProviderRegistryEntry { default_name: bounded_name, ..Default::default() };
1039
1040 Self::propose_to_be_provider_v2(origin, entry)
1041 }
1042
1043 #[pallet::call_index(12)]
1053 #[pallet::weight(T::WeightInfo::create_provider_via_governance_v2(0u32, 0u32))]
1054 #[allow(deprecated)]
1055 #[deprecated(
1056 note = "please use `create_provider_via_governance_v2`, which supports additional provider metadata"
1057 )]
1058 pub fn create_provider_via_governance(
1059 origin: OriginFor<T>,
1060 provider_key: T::AccountId,
1061 provider_name: Vec<u8>,
1062 ) -> DispatchResult {
1063 let bounded_name: BoundedVec<u8, T::MaxProviderNameSize> =
1064 provider_name.try_into().map_err(|_| Error::<T>::ExceedsMaxProviderNameSize)?;
1065
1066 let entry = ProviderRegistryEntry { default_name: bounded_name, ..Default::default() };
1067
1068 Self::create_provider_via_governance_v2(origin, provider_key, entry)
1069 }
1070
1071 #[pallet::call_index(13)]
1073 #[pallet::weight(T::WeightInfo::reindex_offchain())]
1074 pub fn reindex_offchain(
1075 origin: OriginFor<T>,
1076 event: OffchainReplayEvent<T>,
1077 ) -> DispatchResult {
1078 let _ = ensure_signed(origin)?;
1079 match event {
1080 OffchainReplayEvent::MsaPallet(MsaOffchainReplayEvent::KeyReIndex {
1081 msa_id,
1082 index_key,
1083 }) => {
1084 match index_key {
1086 Some(key) => {
1087 let event = Event::PublicKeyAdded { msa_id, key };
1088 offchain_index_event::<T>(Some(&event), msa_id);
1089 },
1090 None => {
1091 offchain_index_event::<T>(None, msa_id);
1092 },
1093 }
1094 },
1095 }
1096
1097 Ok(())
1098 }
1099
1100 #[pallet::call_index(14)]
1121 #[pallet::weight((T::WeightInfo::withdraw_tokens(), DispatchClass::Normal, Pays::No))]
1122 pub fn withdraw_tokens(
1123 origin: OriginFor<T>,
1124 _msa_owner_public_key: T::AccountId,
1125 msa_owner_proof: MultiSignature,
1126 authorization_payload: AuthorizedKeyData<T>,
1127 ) -> DispatchResult {
1128 let public_key = ensure_signed(origin)?;
1129
1130 Self::register_signature(&msa_owner_proof, authorization_payload.expiration)?;
1131
1132 let msa_id = authorization_payload.msa_id;
1133
1134 let msa_address = Self::msa_id_to_eth_address(msa_id);
1136
1137 let mut bytes = &EthereumAddressMapper::to_bytes32(&msa_address.0)[..];
1139 let msa_account_id = T::AccountId::decode(&mut bytes).map_err(|_| {
1140 log::error!("Failed to decode MSA account ID from Ethereum address");
1141 Error::<T>::NoKeyExists
1142 })?;
1143
1144 let msa_balance = T::Currency::reducible_balance(
1146 &msa_account_id,
1147 Preservation::Expendable,
1148 Fortitude::Polite,
1149 );
1150 ensure!(msa_balance > Zero::zero(), Error::<T>::InsufficientBalanceToWithdraw);
1151
1152 let result = <T as pallet::Config>::Currency::transfer(
1154 &msa_account_id,
1155 &public_key,
1156 msa_balance,
1157 Preservation::Expendable,
1158 );
1159 ensure!(result.is_ok(), Error::<T>::UnexpectedTokenTransferError);
1160
1161 Ok(())
1162 }
1163
1164 #[pallet::call_index(15)]
1186 #[pallet::weight(T::WeightInfo::add_recovery_commitment())]
1187 pub fn add_recovery_commitment(
1188 origin: OriginFor<T>,
1189 msa_owner_key: T::AccountId,
1190 proof: MultiSignature,
1191 payload: RecoveryCommitmentPayload<T>,
1192 ) -> DispatchResult {
1193 let _origin_key = ensure_signed(origin)?;
1194
1195 ensure!(
1197 Self::verify_signature(&proof, &msa_owner_key, &payload),
1198 Error::<T>::InvalidSignature
1199 );
1200
1201 Self::register_signature(&proof, payload.expiration)?;
1203
1204 let msa_id = Self::ensure_valid_msa_key(&msa_owner_key)?;
1206
1207 MsaIdToRecoveryCommitment::<T>::insert(msa_id, payload.recovery_commitment);
1209 Self::deposit_event(Event::RecoveryCommitmentAdded {
1210 who: msa_owner_key,
1211 msa_id,
1212 recovery_commitment: payload.recovery_commitment,
1213 });
1214
1215 Ok(())
1216 }
1217
1218 #[pallet::call_index(16)]
1228 #[pallet::weight(T::WeightInfo::approve_recovery_provider())]
1229 pub fn approve_recovery_provider(
1230 origin: OriginFor<T>,
1231 provider_key: T::AccountId,
1232 ) -> DispatchResult {
1233 T::RecoveryProviderApprovalOrigin::ensure_origin(origin)?;
1234
1235 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1236 ensure!(
1237 Self::is_registered_provider(provider_msa_id),
1238 Error::<T>::ProviderNotRegistered
1239 );
1240
1241 if Self::is_approved_recovery_provider(&ProviderId(provider_msa_id)) {
1243 return Ok(());
1244 }
1245
1246 RecoveryProviders::<T>::insert(ProviderId(provider_msa_id), true);
1247
1248 Self::deposit_event(Event::RecoveryProviderApproved {
1249 provider_id: ProviderId(provider_msa_id),
1250 });
1251
1252 Ok(())
1253 }
1254
1255 #[pallet::call_index(17)]
1266 #[pallet::weight(T::WeightInfo::remove_recovery_provider())]
1267 pub fn remove_recovery_provider(
1268 origin: OriginFor<T>,
1269 provider: ProviderId,
1270 ) -> DispatchResult {
1271 T::RecoveryProviderApprovalOrigin::ensure_origin(origin)?;
1272
1273 RecoveryProviders::<T>::remove(provider);
1274 Self::deposit_event(Event::RecoveryProviderRemoved { provider_id: provider });
1275 Ok(())
1276 }
1277
1278 #[pallet::call_index(18)]
1293 #[pallet::weight(T::WeightInfo::recover_account())]
1294 pub fn recover_account(
1295 origin: OriginFor<T>,
1296 intermediary_hash_a: RecoveryHash,
1297 intermediary_hash_b: RecoveryHash,
1298 new_control_key_proof: MultiSignature,
1299 add_key_payload: AddKeyData<T>,
1300 ) -> DispatchResult {
1301 let provider_key = ensure_signed(origin)?;
1302
1303 let provider_msa_id = Self::ensure_approved_recovery_provider(&provider_key)?;
1304
1305 let recovery_commitment = Self::ensure_valid_recovery_commitment(
1306 intermediary_hash_a,
1307 intermediary_hash_b,
1308 add_key_payload.msa_id,
1309 )?;
1310
1311 Self::ensure_valid_new_key_owner(&new_control_key_proof, &add_key_payload)?;
1312
1313 Self::add_key(add_key_payload.msa_id, &add_key_payload.new_public_key.clone())?;
1314
1315 let event = Event::PublicKeyAdded {
1316 msa_id: add_key_payload.msa_id,
1317 key: add_key_payload.new_public_key.clone(),
1318 };
1319 offchain_index_event::<T>(Some(&event), add_key_payload.msa_id);
1320 Self::deposit_event(event);
1321
1322 MsaIdToRecoveryCommitment::<T>::remove(add_key_payload.msa_id);
1324
1325 Self::deposit_event(Event::AccountRecovered {
1327 msa_id: add_key_payload.msa_id,
1328 recovery_provider: ProviderId(provider_msa_id),
1329 new_control_key: add_key_payload.new_public_key.clone(),
1330 });
1331
1332 Self::deposit_event(Event::RecoveryCommitmentInvalidated {
1333 msa_id: add_key_payload.msa_id,
1334 recovery_commitment,
1335 });
1336
1337 Ok(())
1338 }
1339
1340 #[pallet::call_index(19)]
1345 #[pallet::weight(T::WeightInfo::propose_to_be_provider_v2())]
1346 pub fn propose_to_be_provider_v2(
1347 origin: OriginFor<T>,
1348 payload: ProviderRegistryEntry<
1349 T::MaxProviderNameSize,
1350 T::MaxLanguageCodeSize,
1351 T::MaxLogoCidSize,
1352 T::MaxLocaleCount,
1353 >,
1354 ) -> DispatchResult {
1355 let proposer = ensure_signed(origin)?;
1356 Self::ensure_valid_msa_key(&proposer)?;
1357
1358 let proposal: Box<T::Proposal> = Box::new(
1359 (Call::<T>::create_provider_via_governance_v2 {
1360 provider_key: proposer.clone(),
1361 payload,
1362 })
1363 .into(),
1364 );
1365 let threshold = 1;
1366 T::ProposalProvider::propose(proposer, threshold, proposal)?;
1367 Ok(())
1368 }
1369
1370 #[pallet::call_index(20)]
1381 #[pallet::weight(T::WeightInfo::create_provider_via_governance_v2(
1382 payload.localized_names.len() as u32,
1383 payload.localized_logo_250_100_png_cids.len() as u32,
1384 ))]
1385 pub fn create_provider_via_governance_v2(
1386 origin: OriginFor<T>,
1387 provider_key: T::AccountId,
1388 payload: ProviderRegistryEntry<
1389 T::MaxProviderNameSize,
1390 T::MaxLanguageCodeSize,
1391 T::MaxLogoCidSize,
1392 T::MaxLocaleCount,
1393 >,
1394 ) -> DispatchResult {
1395 T::CreateProviderViaGovernanceOrigin::ensure_origin(origin)?;
1396 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1397 Self::ensure_correct_cids(&payload)?;
1398 Self::upsert_provider_for(provider_msa_id, payload, false)?;
1399 Self::deposit_event(Event::ProviderCreated {
1400 provider_id: ProviderId(provider_msa_id),
1401 });
1402 Ok(())
1403 }
1404
1405 #[pallet::call_index(21)]
1413 #[pallet::weight(T::WeightInfo::propose_to_add_application(
1414 payload.localized_names.len() as u32,
1415 payload.localized_logo_250_100_png_cids.len() as u32,
1416 ))]
1417 pub fn propose_to_add_application(
1418 origin: OriginFor<T>,
1419 payload: ApplicationContext<
1420 T::MaxProviderNameSize,
1421 T::MaxLanguageCodeSize,
1422 T::MaxLogoCidSize,
1423 T::MaxLocaleCount,
1424 >,
1425 ) -> DispatchResult {
1426 let proposer = ensure_signed(origin)?;
1427 let provider_msa_id = Self::ensure_valid_msa_key(&proposer)?;
1428 ensure!(
1429 Self::is_registered_provider(provider_msa_id),
1430 Error::<T>::ProviderNotRegistered
1431 );
1432 let proposal: Box<T::Proposal> = Box::new(
1433 (Call::<T>::create_application_via_governance {
1434 provider_key: proposer.clone(),
1435 payload,
1436 })
1437 .into(),
1438 );
1439 let threshold = 1;
1440 T::ProposalProvider::propose(proposer, threshold, proposal)?;
1441 Ok(())
1442 }
1443
1444 #[pallet::call_index(22)]
1455 #[pallet::weight(T::WeightInfo::create_application_via_governance(
1456 payload.localized_names.len() as u32,
1457 payload.localized_logo_250_100_png_cids.len() as u32,
1458 ))]
1459 pub fn create_application_via_governance(
1460 origin: OriginFor<T>,
1461 provider_key: T::AccountId,
1462 payload: ApplicationContext<
1463 T::MaxProviderNameSize,
1464 T::MaxLanguageCodeSize,
1465 T::MaxLogoCidSize,
1466 T::MaxLocaleCount,
1467 >,
1468 ) -> DispatchResult {
1469 T::CreateProviderViaGovernanceOrigin::ensure_origin(origin)?;
1470 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1471 ensure!(
1472 Self::is_registered_provider(provider_msa_id),
1473 Error::<T>::ProviderNotRegistered
1474 );
1475 Self::ensure_correct_cids(&payload)?;
1476 let application_id =
1477 Self::create_application_for(ProviderId(provider_msa_id), payload)?;
1478 Self::deposit_event(Event::ApplicationCreated {
1479 provider_id: ProviderId(provider_msa_id),
1480 application_id,
1481 });
1482 Ok(())
1483 }
1484
1485 #[pallet::call_index(23)]
1493 #[pallet::weight(T::WeightInfo::upload_logo())]
1494 pub fn upload_logo(
1495 origin: OriginFor<T>,
1496 logo_cid: BoundedVec<u8, T::MaxLogoCidSize>,
1497 logo_bytes: BoundedVec<u8, T::MaxLogoSize>,
1498 ) -> DispatchResult {
1499 let provider_key = ensure_signed(origin)?;
1500 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1501 ensure!(
1502 Self::is_registered_provider(provider_msa_id),
1503 Error::<T>::ProviderNotRegistered
1504 );
1505 ensure!(ApprovedLogos::<T>::contains_key(&logo_cid), Error::<T>::LogoCidNotApproved);
1506
1507 let input_cid_binary = Self::validate_cid(&logo_cid)?;
1508 let computed_cid_binary =
1509 compute_cid_v1(logo_bytes.as_slice()).ok_or(Error::<T>::InvalidLogoBytes)?;
1510 ensure!(input_cid_binary == computed_cid_binary, Error::<T>::InvalidLogoBytes);
1511 ApprovedLogos::<T>::insert(&logo_cid, logo_bytes);
1512
1513 Self::deposit_event(Event::ApplicationContextUpdated {
1514 provider_id: ProviderId(provider_msa_id),
1515 application_id: None,
1516 });
1517 Ok(())
1518 }
1519
1520 #[pallet::call_index(24)]
1530 #[pallet::weight(T::WeightInfo::update_provider_via_governance(
1531 payload.localized_names.len() as u32,
1532 payload.localized_logo_250_100_png_cids.len() as u32,
1533 ))]
1534 #[allow(clippy::useless_conversion)]
1535 pub fn update_provider_via_governance(
1536 origin: OriginFor<T>,
1537 provider_key: T::AccountId,
1538 payload: ProviderRegistryEntry<
1539 T::MaxProviderNameSize,
1540 T::MaxLanguageCodeSize,
1541 T::MaxLogoCidSize,
1542 T::MaxLocaleCount,
1543 >,
1544 ) -> DispatchResultWithPostInfo {
1545 T::CreateProviderViaGovernanceOrigin::ensure_origin(origin)?;
1546 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1547 Self::ensure_correct_cids(&payload)?;
1548 let base_weight = T::WeightInfo::update_provider_via_governance(
1549 payload.localized_names.len() as u32,
1550 payload.localized_logo_250_100_png_cids.len() as u32,
1551 );
1552 let total_logos_removed = Self::upsert_provider_for(provider_msa_id, payload, true)?;
1553 Self::deposit_event(Event::ProviderUpdated {
1554 provider_id: ProviderId(provider_msa_id),
1555 });
1556 Self::refund_logo_removal_weight_by_count(total_logos_removed, base_weight)
1557 }
1558
1559 #[pallet::call_index(25)]
1572 #[pallet::weight(T::WeightInfo::propose_to_update_provider(
1573 payload.localized_names.len() as u32,
1574 payload.localized_logo_250_100_png_cids.len() as u32,
1575 ))]
1576 pub fn propose_to_update_provider(
1577 origin: OriginFor<T>,
1578 payload: ProviderRegistryEntry<
1579 T::MaxProviderNameSize,
1580 T::MaxLanguageCodeSize,
1581 T::MaxLogoCidSize,
1582 T::MaxLocaleCount,
1583 >,
1584 ) -> DispatchResult {
1585 let proposer = ensure_signed(origin)?;
1586 let provider_msa_id = Self::ensure_valid_msa_key(&proposer)?;
1587 ensure!(
1588 Self::is_registered_provider(provider_msa_id),
1589 Error::<T>::ProviderNotRegistered
1590 );
1591 let proposal: Box<T::Proposal> = Box::new(
1592 (Call::<T>::update_provider_via_governance {
1593 provider_key: proposer.clone(),
1594 payload,
1595 })
1596 .into(),
1597 );
1598 let threshold = 1;
1599 T::ProposalProvider::propose(proposer, threshold, proposal)?;
1600 Ok(())
1601 }
1602
1603 #[pallet::call_index(26)]
1617 #[pallet::weight(T::WeightInfo::update_application_via_governance(
1618 payload.localized_names.len() as u32,
1619 payload.localized_logo_250_100_png_cids.len() as u32,
1620 ))]
1621 #[allow(clippy::useless_conversion)]
1622 pub fn update_application_via_governance(
1623 origin: OriginFor<T>,
1624 provider_key: T::AccountId,
1625 application_index: ApplicationIndex,
1626 payload: ApplicationContext<
1627 T::MaxProviderNameSize,
1628 T::MaxLanguageCodeSize,
1629 T::MaxLogoCidSize,
1630 T::MaxLocaleCount,
1631 >,
1632 ) -> DispatchResultWithPostInfo {
1633 T::CreateProviderViaGovernanceOrigin::ensure_origin(origin)?;
1634 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1635 ensure!(
1636 Self::is_registered_provider(provider_msa_id),
1637 Error::<T>::ProviderNotRegistered
1638 );
1639 Self::ensure_correct_cids(&payload)?;
1640 let base_weight = T::WeightInfo::update_application_via_governance(
1641 payload.localized_names.len() as u32,
1642 payload.localized_logo_250_100_png_cids.len() as u32,
1643 );
1644 let total_logos_removed = Self::upsert_application_for(
1645 ProviderId(provider_msa_id),
1646 application_index,
1647 payload,
1648 )?;
1649 Self::deposit_event(Event::ApplicationContextUpdated {
1650 provider_id: ProviderId(provider_msa_id),
1651 application_id: Some(application_index),
1652 });
1653 Self::refund_logo_removal_weight_by_count(total_logos_removed, base_weight)
1654 }
1655
1656 #[pallet::call_index(27)]
1670 #[pallet::weight(T::WeightInfo::propose_to_update_application(
1671 payload.localized_names.len() as u32,
1672 payload.localized_logo_250_100_png_cids.len() as u32,
1673 ))]
1674 pub fn propose_to_update_application(
1675 origin: OriginFor<T>,
1676 application_index: ApplicationIndex,
1677 payload: ApplicationContext<
1678 T::MaxProviderNameSize,
1679 T::MaxLanguageCodeSize,
1680 T::MaxLogoCidSize,
1681 T::MaxLocaleCount,
1682 >,
1683 ) -> DispatchResult {
1684 let proposer = ensure_signed(origin)?;
1685 let provider_msa_id = Self::ensure_valid_msa_key(&proposer)?;
1686 ensure!(
1687 Self::is_registered_provider(provider_msa_id),
1688 Error::<T>::ProviderNotRegistered
1689 );
1690 ensure!(
1691 ProviderToApplicationRegistry::<T>::contains_key(
1692 ProviderId(provider_msa_id),
1693 application_index
1694 ),
1695 Error::<T>::ApplicationNotFound
1696 );
1697 Self::ensure_correct_cids(&payload)?;
1698 let proposal: Box<T::Proposal> = Box::new(
1699 (Call::<T>::update_application_via_governance {
1700 provider_key: proposer.clone(),
1701 application_index,
1702 payload,
1703 })
1704 .into(),
1705 );
1706 let threshold = 1;
1707 T::ProposalProvider::propose(proposer, threshold, proposal)?;
1708 Ok(())
1709 }
1710
1711 #[pallet::call_index(28)]
1714 #[pallet::weight(T::WeightInfo::create_application_via_governance(
1715 payload.localized_names.len() as u32,
1716 payload.localized_logo_250_100_png_cids.len() as u32,
1717 ))]
1718 pub fn create_application(
1719 origin: OriginFor<T>,
1720 payload: ApplicationContext<
1721 T::MaxProviderNameSize,
1722 T::MaxLanguageCodeSize,
1723 T::MaxLogoCidSize,
1724 T::MaxLocaleCount,
1725 >,
1726 ) -> DispatchResult {
1727 let provider_account = ensure_signed(origin)?;
1728 let provider_msa_id = Self::ensure_valid_msa_key(&provider_account)?;
1729 ensure!(
1730 Self::is_registered_provider(provider_msa_id),
1731 Error::<T>::ProviderNotRegistered
1732 );
1733 Self::ensure_correct_cids(&payload)?;
1734 let application_id =
1735 Self::create_application_for(ProviderId(provider_msa_id), payload)?;
1736 Self::deposit_event(Event::ApplicationCreated {
1737 provider_id: ProviderId(provider_msa_id),
1738 application_id,
1739 });
1740 Ok(())
1741 }
1742 }
1743}
1744
1745impl<T: Config> Pallet<T> {
1746 pub fn is_approved_recovery_provider(provider: &ProviderId) -> bool {
1754 RecoveryProviders::<T>::get(provider).unwrap_or(false)
1755 }
1756
1757 pub fn compute_recovery_commitment(
1766 intermediary_hash_a: RecoveryHash,
1767 intermediary_hash_b: RecoveryHash,
1768 ) -> RecoveryCommitment {
1769 let mut input = Vec::with_capacity(64);
1770 input.extend_from_slice(&intermediary_hash_a);
1771 input.extend_from_slice(&intermediary_hash_b);
1772 keccak_256(&input)
1773 }
1774
1775 fn ensure_approved_recovery_provider(
1787 provider_key: &T::AccountId,
1788 ) -> Result<MessageSourceId, DispatchError> {
1789 let provider_msa_id = Self::ensure_valid_msa_key(provider_key)?;
1790
1791 ensure!(
1792 Self::is_approved_recovery_provider(&ProviderId(provider_msa_id)),
1793 Error::<T>::NotAuthorizedRecoveryProvider
1794 );
1795
1796 Ok(provider_msa_id)
1797 }
1798
1799 fn ensure_valid_recovery_commitment(
1813 intermediary_hash_a: RecoveryHash,
1814 intermediary_hash_b: RecoveryHash,
1815 msa_id: MessageSourceId,
1816 ) -> Result<RecoveryCommitment, DispatchError> {
1817 let recovery_commitment: RecoveryCommitment =
1818 Self::compute_recovery_commitment(intermediary_hash_a, intermediary_hash_b);
1819
1820 let stored_commitment: RecoveryCommitment =
1821 MsaIdToRecoveryCommitment::<T>::get(msa_id).ok_or(Error::<T>::NoRecoveryCommitment)?;
1822
1823 ensure!(recovery_commitment == stored_commitment, Error::<T>::InvalidRecoveryCommitment);
1824
1825 Ok(recovery_commitment)
1826 }
1827
1828 fn ensure_valid_new_key_owner(
1838 new_control_key_proof: &MultiSignature,
1839 add_key_payload: &AddKeyData<T>,
1840 ) -> DispatchResult {
1841 ensure!(
1844 Self::verify_signature(
1845 new_control_key_proof,
1846 &add_key_payload.new_public_key,
1847 add_key_payload
1848 ),
1849 Error::<T>::NewKeyOwnershipInvalidSignature
1850 );
1851 Self::register_signature(new_control_key_proof, add_key_payload.expiration)?;
1852
1853 Ok(())
1854 }
1855
1856 pub fn create_account(
1864 key: T::AccountId,
1865 ) -> Result<(MessageSourceId, T::AccountId), DispatchError> {
1866 let next_msa_id = Self::get_next_msa_id()?;
1867 Self::add_key(next_msa_id, &key)?;
1868 let _ = Self::set_msa_identifier(next_msa_id);
1869
1870 Ok((next_msa_id, key))
1871 }
1872
1873 pub fn get_next_msa_id() -> Result<MessageSourceId, DispatchError> {
1879 let next = CurrentMsaIdentifierMaximum::<T>::get()
1880 .checked_add(1)
1881 .ok_or(Error::<T>::MsaIdOverflow)?;
1882
1883 Ok(next)
1884 }
1885
1886 pub fn set_msa_identifier(identifier: MessageSourceId) -> DispatchResult {
1888 CurrentMsaIdentifierMaximum::<T>::set(identifier);
1889
1890 Ok(())
1891 }
1892
1893 pub fn create_registered_provider(
1895 provider_id: ProviderId,
1896 payload: ProviderRegistryEntry<
1897 T::MaxProviderNameSize,
1898 T::MaxLanguageCodeSize,
1899 T::MaxLogoCidSize,
1900 T::MaxLocaleCount,
1901 >,
1902 ) -> DispatchResult {
1903 ProviderToRegistryEntryV2::<T>::try_mutate(
1904 provider_id,
1905 |maybe_metadata| -> DispatchResult {
1906 ensure!(
1907 maybe_metadata.take().is_none(),
1908 Error::<T>::DuplicateProviderRegistryEntry
1909 );
1910 *maybe_metadata = Some(payload);
1911 Ok(())
1912 },
1913 )
1914 }
1915
1916 #[cfg(test)]
1918 pub fn grant_permissions_for_intents(
1919 delegator_id: DelegatorId,
1920 provider_id: ProviderId,
1921 intent_ids: Vec<IntentId>,
1922 ) -> DispatchResult {
1923 Self::try_mutate_delegation(delegator_id, provider_id, |delegation, is_new_delegation| {
1924 ensure!(!is_new_delegation, Error::<T>::DelegationNotFound);
1925 Self::ensure_all_intent_ids_are_valid(&intent_ids)?;
1926
1927 PermittedDelegationIntents::<T>::try_insert_intents(delegation, intent_ids)?;
1928
1929 Ok(())
1930 })
1931 }
1932
1933 pub fn revoke_permissions_for_intents(
1935 delegator_id: DelegatorId,
1936 provider_id: ProviderId,
1937 intent_ids: Vec<IntentId>,
1938 ) -> DispatchResult {
1939 Self::try_mutate_delegation(delegator_id, provider_id, |delegation, is_new_delegation| {
1940 ensure!(!is_new_delegation, Error::<T>::DelegationNotFound);
1941 Self::ensure_all_intent_ids_are_valid(&intent_ids)?;
1942
1943 let current_block = frame_system::Pallet::<T>::block_number();
1944
1945 PermittedDelegationIntents::<T>::try_get_mut_intents(
1946 delegation,
1947 intent_ids,
1948 current_block,
1949 )?;
1950
1951 Ok(())
1952 })
1953 }
1954
1955 pub fn add_key(msa_id: MessageSourceId, key: &T::AccountId) -> DispatchResult {
1962 PublicKeyToMsaId::<T>::try_mutate(key, |maybe_msa_id| {
1963 ensure!(maybe_msa_id.is_none(), Error::<T>::KeyAlreadyRegistered);
1964 *maybe_msa_id = Some(msa_id);
1965
1966 <PublicKeyCountForMsaId<T>>::try_mutate(msa_id, |key_count| {
1968 let incremented_key_count =
1970 key_count.checked_add(1).ok_or(ArithmeticError::Overflow)?;
1971
1972 ensure!(
1973 incremented_key_count <= T::MaxPublicKeysPerMsa::get(),
1974 Error::<T>::KeyLimitExceeded
1975 );
1976
1977 *key_count = incremented_key_count;
1978 Ok(())
1979 })
1980 })
1981 }
1982
1983 pub fn ensure_all_intent_ids_are_valid(intent_ids: &[IntentId]) -> DispatchResult {
1990 ensure!(
1991 intent_ids.len() <= T::MaxGrantsPerDelegation::get() as usize,
1992 Error::<T>::ExceedsMaxGrantsPerDelegation
1993 );
1994
1995 let all_valid = T::SchemaValidator::are_all_intent_ids_valid(intent_ids);
1996
1997 ensure!(all_valid, Error::<T>::InvalidIntentId);
1998
1999 Ok(())
2000 }
2001
2002 pub fn is_registered_provider(msa_id: MessageSourceId) -> bool {
2004 ProviderToRegistryEntryV2::<T>::contains_key(ProviderId(msa_id))
2005 }
2006
2007 pub fn ensure_valid_registered_provider(
2017 delegator_key: &T::AccountId,
2018 provider_key: &T::AccountId,
2019 ) -> Result<(ProviderId, DelegatorId), DispatchError> {
2020 let provider_msa_id = Self::ensure_valid_msa_key(provider_key)?;
2021 let delegator_msa_id = Self::ensure_valid_msa_key(delegator_key)?;
2022
2023 ensure!(delegator_msa_id != provider_msa_id, Error::<T>::InvalidSelfProvider);
2025
2026 ensure!(Self::is_registered_provider(provider_msa_id), Error::<T>::ProviderNotRegistered);
2028
2029 Ok((provider_msa_id.into(), delegator_msa_id.into()))
2030 }
2031
2032 pub fn ensure_msa_owner(who: &T::AccountId, msa_id: MessageSourceId) -> DispatchResult {
2039 let provider_msa_id = Self::ensure_valid_msa_key(who)?;
2040 ensure!(provider_msa_id == msa_id, Error::<T>::NotMsaOwner);
2041
2042 Ok(())
2043 }
2044
2045 pub fn verify_signature<P>(
2052 signature: &MultiSignature,
2053 signer: &T::AccountId,
2054 payload: &P,
2055 ) -> bool
2056 where
2057 P: Encode + EIP712Encode,
2058 {
2059 let key = T::ConvertIntoAccountId32::convert((*signer).clone());
2060
2061 check_signature(signature, key, payload)
2062 }
2063
2064 pub fn add_provider(
2070 provider_id: ProviderId,
2071 delegator_id: DelegatorId,
2072 intent_ids: Vec<IntentId>,
2073 ) -> DispatchResult {
2074 Self::try_mutate_delegation(delegator_id, provider_id, |delegation, is_new_delegation| {
2075 ensure!(is_new_delegation, Error::<T>::DuplicateProvider);
2076 Self::ensure_all_intent_ids_are_valid(&intent_ids)?;
2077
2078 PermittedDelegationIntents::<T>::try_insert_intents(delegation, intent_ids)?;
2079
2080 Ok(())
2081 })
2082 }
2083
2084 pub fn upsert_intent_permissions(
2089 provider_id: ProviderId,
2090 delegator_id: DelegatorId,
2091 intent_ids: Vec<IntentId>,
2092 ) -> DispatchResult {
2093 Self::try_mutate_delegation(delegator_id, provider_id, |delegation, _is_new_delegation| {
2094 Self::ensure_all_intent_ids_are_valid(&intent_ids)?;
2095
2096 let mut revoke_ids: Vec<IntentId> = Vec::new();
2098 let mut update_ids: Vec<IntentId> = Vec::new();
2099 let mut insert_ids: Vec<IntentId> = Vec::new();
2100
2101 let existing_keys = delegation.permissions.keys();
2102
2103 for existing_intent_id in existing_keys {
2104 if !intent_ids.contains(existing_intent_id) {
2105 if let Some(block) = delegation.permissions.get(existing_intent_id) {
2106 if *block == BlockNumberFor::<T>::zero() {
2107 revoke_ids.push(*existing_intent_id);
2108 }
2109 }
2110 }
2111 }
2112 for intent_id in &intent_ids {
2113 if !delegation.permissions.contains_key(intent_id) {
2114 insert_ids.push(*intent_id);
2115 } else {
2116 update_ids.push(*intent_id);
2117 }
2118 }
2119
2120 let current_block = frame_system::Pallet::<T>::block_number();
2121
2122 PermittedDelegationIntents::<T>::try_get_mut_intents(
2124 delegation,
2125 revoke_ids,
2126 current_block,
2127 )?;
2128
2129 PermittedDelegationIntents::<T>::try_get_mut_intents(
2131 delegation,
2132 update_ids,
2133 BlockNumberFor::<T>::zero(),
2134 )?;
2135
2136 PermittedDelegationIntents::<T>::try_insert_intents(delegation, insert_ids)?;
2138 delegation.revoked_at = BlockNumberFor::<T>::zero();
2139 Ok(())
2140 })
2141 }
2142
2143 pub fn upsert_provider_for(
2150 provider_msa_id: MessageSourceId,
2151 payload: ProviderRegistryEntry<
2152 T::MaxProviderNameSize,
2153 T::MaxLanguageCodeSize,
2154 T::MaxLogoCidSize,
2155 T::MaxLocaleCount,
2156 >,
2157 is_update: bool,
2158 ) -> Result<u32, DispatchError> {
2159 let mut total_logos_removed = 0;
2160 ProviderToRegistryEntryV2::<T>::try_mutate(
2161 ProviderId(provider_msa_id),
2162 |maybe_metadata| -> DispatchResult {
2163 if !is_update {
2164 ensure!(
2165 maybe_metadata.take().is_none(),
2166 Error::<T>::DuplicateProviderRegistryEntry
2167 );
2168 } else {
2169 total_logos_removed = Self::remove_logo_storage(maybe_metadata.as_ref())?;
2170 }
2171 Self::update_logo_storage(&payload)?;
2172
2173 *maybe_metadata = Some(payload);
2174 Ok(())
2175 },
2176 )
2177 .map(|_| total_logos_removed)
2178 }
2179
2180 pub fn create_application_for(
2185 provider_msa_id: ProviderId,
2186 payload: ApplicationContext<
2187 T::MaxProviderNameSize,
2188 T::MaxLanguageCodeSize,
2189 T::MaxLogoCidSize,
2190 T::MaxLocaleCount,
2191 >,
2192 ) -> Result<ApplicationIndex, DispatchError> {
2193 Self::update_logo_storage(&payload)?;
2194 let next_application_index = NextApplicationIndex::<T>::get(provider_msa_id);
2195 ensure!(
2196 !ProviderToApplicationRegistry::<T>::contains_key(
2197 provider_msa_id,
2198 next_application_index
2199 ),
2200 Error::<T>::DuplicateApplicationRegistryEntry
2201 );
2202 ProviderToApplicationRegistry::<T>::insert(
2203 provider_msa_id,
2204 next_application_index,
2205 payload,
2206 );
2207 NextApplicationIndex::<T>::insert(provider_msa_id, next_application_index + 1);
2208 Ok(next_application_index)
2209 }
2210
2211 pub fn upsert_application_for(
2216 provider_msa_id: ProviderId,
2217 application_index: ApplicationIndex,
2218 payload: ApplicationContext<
2219 T::MaxProviderNameSize,
2220 T::MaxLanguageCodeSize,
2221 T::MaxLogoCidSize,
2222 T::MaxLocaleCount,
2223 >,
2224 ) -> Result<u32, DispatchError> {
2225 ensure!(
2226 ProviderToApplicationRegistry::<T>::contains_key(provider_msa_id, application_index),
2227 Error::<T>::ApplicationNotFound
2228 );
2229 let mut total_logos_removed = 0;
2230 ProviderToApplicationRegistry::<T>::try_mutate(
2231 provider_msa_id,
2232 application_index,
2233 |maybe_metadata| -> DispatchResult {
2234 total_logos_removed = Self::remove_logo_storage(maybe_metadata.as_ref())?;
2235 Self::update_logo_storage(&payload)?;
2236 *maybe_metadata = Some(payload);
2237 Ok(())
2238 },
2239 )
2240 .map(|_| total_logos_removed)
2241 }
2242
2243 pub fn try_mutate_delegation<R, E: From<DispatchError>>(
2247 delegator_id: DelegatorId,
2248 provider_id: ProviderId,
2249 f: impl FnOnce(
2250 &mut Delegation<IntentId, BlockNumberFor<T>, T::MaxGrantsPerDelegation>,
2251 bool,
2252 ) -> Result<R, E>,
2253 ) -> Result<R, E> {
2254 DelegatorAndProviderToDelegation::<T>::try_mutate_exists(
2255 delegator_id,
2256 provider_id,
2257 |maybe_delegation_info| {
2258 let is_new = maybe_delegation_info.is_none();
2259 let mut delegation = maybe_delegation_info.take().unwrap_or_default();
2260
2261 let result = f(&mut delegation, is_new)?;
2262
2263 *maybe_delegation_info = Some(delegation);
2265 Ok(result)
2266 },
2267 )
2268 }
2269
2270 pub fn delete_key_for_msa(msa_id: MessageSourceId, key: &T::AccountId) -> DispatchResult {
2276 PublicKeyToMsaId::<T>::try_mutate_exists(key, |maybe_msa_id| {
2277 ensure!(maybe_msa_id.is_some(), Error::<T>::NoKeyExists);
2278
2279 *maybe_msa_id = None;
2281
2282 <PublicKeyCountForMsaId<T>>::try_mutate_exists(msa_id, |key_count| {
2283 match key_count {
2284 Some(1) => *key_count = None,
2285 Some(count) => *count = *count - 1u8,
2286 None => (),
2287 }
2288
2289 Ok(())
2290 })
2291 })
2292 }
2293
2294 pub fn revoke_provider(provider_id: ProviderId, delegator_id: DelegatorId) -> DispatchResult {
2301 DelegatorAndProviderToDelegation::<T>::try_mutate_exists(
2302 delegator_id,
2303 provider_id,
2304 |maybe_info| -> DispatchResult {
2305 let mut info = maybe_info.take().ok_or(Error::<T>::DelegationNotFound)?;
2306
2307 ensure!(
2308 info.revoked_at == BlockNumberFor::<T>::default(),
2309 Error::<T>::DelegationRevoked
2310 );
2311
2312 let current_block = frame_system::Pallet::<T>::block_number();
2313 info.revoked_at = current_block;
2314 *maybe_info = Some(info);
2315 Ok(())
2316 },
2317 )?;
2318
2319 Ok(())
2320 }
2321
2322 pub fn get_owner_of(key: &T::AccountId) -> Option<MessageSourceId> {
2324 PublicKeyToMsaId::<T>::get(key)
2325 }
2326
2327 pub fn ensure_valid_msa_key(key: &T::AccountId) -> Result<MessageSourceId, DispatchError> {
2329 let msa_id = PublicKeyToMsaId::<T>::get(key).ok_or(Error::<T>::NoKeyExists)?;
2330 Ok(msa_id)
2331 }
2332
2333 pub fn get_granted_intents_by_msa_id(
2340 delegator: DelegatorId,
2341 provider: Option<ProviderId>,
2342 ) -> Result<Vec<DelegationResponse<IntentId, BlockNumberFor<T>>>, DispatchError> {
2343 let delegations = match provider {
2344 Some(provider_id) => vec![(
2345 provider_id,
2346 Self::get_delegation_of(delegator, provider_id)
2347 .ok_or(Error::<T>::DelegationNotFound)?,
2348 )],
2349 None => DelegatorAndProviderToDelegation::<T>::iter_prefix(delegator).collect(),
2350 };
2351
2352 let mut result = vec![];
2353 for (provider_id, provider_info) in delegations {
2354 let intent_permissions = provider_info.permissions;
2355 if provider.is_some() && intent_permissions.is_empty() {
2357 return Err(Error::<T>::PermissionNotGranted.into());
2358 }
2359
2360 let mut intent_list = Vec::new();
2361 for (intent_id, revoked_at) in intent_permissions {
2362 if provider_info.revoked_at > BlockNumberFor::<T>::zero() &&
2363 (revoked_at > provider_info.revoked_at ||
2364 revoked_at == BlockNumberFor::<T>::zero())
2365 {
2366 intent_list.push(DelegationGrant {
2367 granted_id: intent_id,
2368 revoked_at: provider_info.revoked_at,
2369 });
2370 } else {
2371 intent_list.push(DelegationGrant { granted_id: intent_id, revoked_at });
2372 }
2373 }
2374
2375 result.push(DelegationResponse { provider_id, permissions: intent_list });
2376 }
2377
2378 Ok(result)
2379 }
2380
2381 pub fn msa_id_to_eth_address(id: MessageSourceId) -> H160 {
2387 const DOMAIN_PREFIX: u8 = 0xD9;
2391
2392 lazy_static! {
2393 static ref MSA_ADDRESS_SALT: [u8; 32] = keccak_256(b"MSA Generated");
2395 }
2396 let input_value = id.to_be_bytes();
2397
2398 let mut hash_input = [0u8; 41];
2399 hash_input[0] = DOMAIN_PREFIX;
2400 hash_input[1..9].copy_from_slice(&input_value);
2401 hash_input[9..].copy_from_slice(&(*MSA_ADDRESS_SALT));
2402
2403 let hash = keccak_256(&hash_input);
2404 H160::from_slice(&hash[12..])
2405 }
2406
2407 pub fn validate_eth_address_for_msa(address: &H160, msa_id: MessageSourceId) -> bool {
2409 let generated_address = Self::msa_id_to_eth_address(msa_id);
2410 *address == generated_address
2411 }
2412
2413 pub fn eth_address_to_checksummed_string(address: &H160) -> alloc::string::String {
2417 let addr_bytes = address.0;
2418 let addr_hex = hex::encode(addr_bytes);
2419 let hash = keccak_256(addr_hex.as_bytes());
2420
2421 let mut result = alloc::string::String::with_capacity(42);
2422 result.push_str("0x");
2423
2424 for (i, c) in addr_hex.chars().enumerate() {
2425 let hash_byte = hash[i / 2];
2426 let bit = if i % 2 == 0 { (hash_byte >> 4) & 0xf } else { hash_byte & 0xf };
2427
2428 result.push(if c.is_ascii_hexdigit() && c.is_ascii_alphabetic() {
2429 if bit >= 8 {
2430 c.to_ascii_uppercase()
2431 } else {
2432 c
2433 }
2434 } else {
2435 c
2436 });
2437 }
2438
2439 result
2440 }
2441
2442 pub fn register_signature(
2454 signature: &MultiSignature,
2455 signature_expires_at: BlockNumberFor<T>,
2456 ) -> DispatchResult {
2457 let current_block =
2458 Self::check_signature_against_registry(signature, signature_expires_at)?;
2459
2460 Self::enqueue_signature(signature, signature_expires_at, current_block)
2461 }
2462
2463 pub fn check_signature_against_registry(
2472 signature: &MultiSignature,
2473 signature_expires_at: BlockNumberFor<T>,
2474 ) -> Result<BlockNumberFor<T>, DispatchError> {
2475 let current_block: BlockNumberFor<T> = frame_system::Pallet::<T>::block_number();
2476
2477 let max_lifetime = Self::mortality_block_limit(current_block);
2478 ensure!(max_lifetime > signature_expires_at, Error::<T>::ProofNotYetValid);
2479 ensure!(current_block < signature_expires_at, Error::<T>::ProofHasExpired);
2480
2481 ensure!(
2483 !<PayloadSignatureRegistryList<T>>::contains_key(signature),
2484 Error::<T>::SignatureAlreadySubmitted
2485 );
2486 if let Some(signature_pointer) = PayloadSignatureRegistryPointer::<T>::get() {
2487 ensure!(signature_pointer.newest != *signature, Error::<T>::SignatureAlreadySubmitted);
2488 }
2489
2490 Ok(current_block)
2491 }
2492
2493 fn enqueue_signature(
2516 signature: &MultiSignature,
2517 signature_expires_at: BlockNumberFor<T>,
2518 current_block: BlockNumberFor<T>,
2519 ) -> DispatchResult {
2520 let pointer =
2522 PayloadSignatureRegistryPointer::<T>::get().unwrap_or(SignatureRegistryPointer {
2523 newest: signature.clone(),
2524 newest_expires_at: signature_expires_at,
2525 oldest: signature.clone(),
2526 count: 0,
2527 });
2528
2529 ensure!(
2531 !(pointer.count != 0 && pointer.newest.eq(signature)),
2532 Error::<T>::SignatureAlreadySubmitted
2533 );
2534
2535 let mut oldest: MultiSignature = pointer.oldest.clone();
2537
2538 let is_registry_full: bool = pointer.count == T::MaxSignaturesStored::get().unwrap_or(0);
2540
2541 if is_registry_full {
2543 let (expire_block_number, next_oldest) =
2544 PayloadSignatureRegistryList::<T>::get(pointer.oldest.clone())
2545 .ok_or(Error::<T>::SignatureRegistryCorrupted)?;
2546
2547 ensure!(
2548 current_block.gt(&expire_block_number),
2549 Error::<T>::SignatureRegistryLimitExceeded
2550 );
2551
2552 oldest = next_oldest.clone();
2554
2555 <PayloadSignatureRegistryList<T>>::remove(pointer.oldest);
2556 }
2557
2558 if pointer.count != 0 {
2560 <PayloadSignatureRegistryList<T>>::insert(
2561 pointer.newest,
2562 (pointer.newest_expires_at, signature.clone()),
2563 );
2564 }
2565
2566 PayloadSignatureRegistryPointer::<T>::put(SignatureRegistryPointer {
2568 count: if is_registry_full { pointer.count } else { pointer.count + 1 },
2570 newest: signature.clone(),
2571 newest_expires_at: signature_expires_at,
2572 oldest,
2573 });
2574
2575 Ok(())
2576 }
2577
2578 fn mortality_block_limit(current_block: BlockNumberFor<T>) -> BlockNumberFor<T> {
2582 let mortality_size = T::MortalityWindowSize::get();
2583 current_block + BlockNumberFor::<T>::from(mortality_size)
2584 }
2585
2586 fn validate_cid(in_cid: &[u8]) -> Result<Vec<u8>, DispatchError> {
2593 let cid_str: &str = core::str::from_utf8(in_cid).map_err(|_| Error::<T>::InvalidCid)?;
2595 ensure!(cid_str.len() > 2, Error::<T>::InvalidCid);
2596 ensure!(!cid_str.starts_with("Qm"), Error::<T>::UnsupportedCidVersion);
2598
2599 let cid_b = multibase::decode(cid_str).map_err(|_| Error::<T>::InvalidCid)?.1;
2601 ensure!(Cid::read_bytes(&cid_b[..]).is_ok(), Error::<T>::InvalidCid);
2602
2603 Ok(cid_b)
2604 }
2605
2606 fn update_logo_storage(
2608 payload: &ProviderRegistryEntry<
2609 T::MaxProviderNameSize,
2610 T::MaxLanguageCodeSize,
2611 T::MaxLogoCidSize,
2612 T::MaxLocaleCount,
2613 >,
2614 ) -> DispatchResult {
2615 if !payload.default_logo_250_100_png_cid.is_empty() {
2617 ApprovedLogos::<T>::insert(
2618 payload.default_logo_250_100_png_cid.clone(),
2619 BoundedVec::new(),
2620 );
2621 }
2622
2623 for (_, localized_cid) in &payload.localized_logo_250_100_png_cids {
2625 if !localized_cid.is_empty() {
2626 ApprovedLogos::<T>::insert(localized_cid, BoundedVec::new());
2627 }
2628 }
2629
2630 Ok(())
2631 }
2632
2633 fn remove_logo_storage(
2635 existing_payload: Option<
2636 &ProviderRegistryEntry<
2637 T::MaxProviderNameSize,
2638 T::MaxLanguageCodeSize,
2639 T::MaxLogoCidSize,
2640 T::MaxLocaleCount,
2641 >,
2642 >,
2643 ) -> Result<u32, DispatchError> {
2644 let mut total_logo_removed = 0;
2645 if let Some(payload) = existing_payload {
2646 if !payload.default_logo_250_100_png_cid.is_empty() {
2648 total_logo_removed += 1;
2649 ApprovedLogos::<T>::remove(&payload.default_logo_250_100_png_cid);
2650 }
2651 for (_, localized_cid) in &payload.localized_logo_250_100_png_cids {
2653 if !localized_cid.is_empty() {
2654 total_logo_removed += 1;
2655 ApprovedLogos::<T>::remove(localized_cid);
2656 }
2657 }
2658 }
2659 Ok(total_logo_removed)
2660 }
2661
2662 fn ensure_correct_cids(
2664 payload: &ProviderRegistryEntry<
2665 T::MaxProviderNameSize,
2666 T::MaxLanguageCodeSize,
2667 T::MaxLogoCidSize,
2668 T::MaxLocaleCount,
2669 >,
2670 ) -> DispatchResult {
2671 if !payload.default_logo_250_100_png_cid.is_empty() {
2673 Self::validate_cid(&payload.default_logo_250_100_png_cid)?;
2674 }
2675
2676 for (lang_code, localized_cid) in &payload.localized_logo_250_100_png_cids {
2678 let code_str = core::str::from_utf8(lang_code)
2680 .map_err(|_| Error::<T>::InvalidBCP47LanguageCode)?;
2681
2682 if !Self::is_valid_bcp47(code_str) {
2683 return Err(Error::<T>::InvalidBCP47LanguageCode.into());
2684 }
2685
2686 if !localized_cid.is_empty() {
2688 Self::validate_cid(localized_cid)?;
2689 }
2690 }
2691
2692 for (lang_code, _) in &payload.localized_names {
2694 let code_str = core::str::from_utf8(lang_code)
2696 .map_err(|_| Error::<T>::InvalidBCP47LanguageCode)?;
2697 if !Self::is_valid_bcp47(code_str) {
2698 return Err(Error::<T>::InvalidBCP47LanguageCode.into());
2699 }
2700 }
2701
2702 Ok(())
2703 }
2704
2705 fn is_valid_bcp47(code: &str) -> bool {
2707 if code.is_empty() {
2709 return false;
2710 }
2711 if code.starts_with('-') || code.ends_with('-') || code.contains("--") {
2713 return false;
2714 }
2715 for part in code.split('-') {
2716 let len = part.len();
2717 if len < 2 || !part.chars().all(|c| c.is_ascii_alphanumeric()) {
2718 return false;
2719 }
2720 }
2721 true
2722 }
2723
2724 pub fn get_provider_application_context(
2726 provider_id: ProviderId,
2727 application_id: Option<ApplicationIndex>,
2728 locale: Option<Vec<u8>>,
2729 ) -> Option<ProviderApplicationContext> {
2730 let bounded_locale = locale.and_then(|loc| BoundedVec::try_from(loc).ok());
2731 let provider_or_application_registry = match application_id {
2732 Some(app_id) => ProviderToApplicationRegistry::<T>::get(provider_id, app_id)?,
2733 None => ProviderToRegistryEntryV2::<T>::get(provider_id)?,
2734 };
2735 let default_name = provider_or_application_registry.default_name.to_vec();
2736 let default_logo_cid = provider_or_application_registry.default_logo_250_100_png_cid;
2737 let default_logo_250_100_png_bytes: Option<Vec<u8>> =
2739 ApprovedLogos::<T>::get(default_logo_cid).map(|bv| bv.to_vec());
2740 let mut localized_name: Option<Vec<u8>> = None;
2741 let localized_logo_250_100_png_bytes: Option<Vec<u8>> = bounded_locale.and_then(|locale| {
2743 localized_name = provider_or_application_registry
2745 .localized_names
2746 .get(&locale)
2747 .map(|bv| bv.to_vec());
2748
2749 provider_or_application_registry
2750 .localized_logo_250_100_png_cids
2751 .get(&locale)
2752 .and_then(|cid| ApprovedLogos::<T>::get(cid).map(|bv| bv.to_vec()))
2753 });
2754
2755 Some(ProviderApplicationContext {
2756 default_name,
2757 provider_id,
2758 application_id,
2759 default_logo_250_100_png_bytes,
2760 localized_logo_250_100_png_bytes,
2761 localized_name,
2762 })
2763 }
2764
2765 fn refund_logo_removal_weight_by_count(
2767 total_logos_removed: u32,
2768 base_weight: Weight,
2769 ) -> DispatchResultWithPostInfo {
2770 let max_logos_benchmark_assumed = T::MaxLocaleCount::get();
2772 let removal_over_charged_by =
2773 max_logos_benchmark_assumed.saturating_sub(total_logos_removed);
2774 let weight_per_logo_removal = T::DbWeight::get().writes(1);
2775 let weight_to_refund =
2776 weight_per_logo_removal.saturating_mul(removal_over_charged_by as u64);
2777 let actual_weight_used = base_weight.saturating_sub(weight_to_refund);
2778 Ok(PostDispatchInfo { actual_weight: Some(actual_weight_used), pays_fee: Pays::Yes })
2779 }
2780}
2781
2782#[cfg(feature = "runtime-benchmarks")]
2783impl<T: Config> MsaBenchmarkHelper<T::AccountId> for Pallet<T> {
2784 fn set_delegation_relationship(
2786 provider: ProviderId,
2787 delegator: DelegatorId,
2788 intents: Vec<IntentId>,
2789 ) -> DispatchResult {
2790 Self::add_provider(provider, delegator, intents)?;
2791 Ok(())
2792 }
2793
2794 fn add_key(msa_id: MessageSourceId, key: T::AccountId) -> DispatchResult {
2796 Self::add_key(msa_id, &key)?;
2797 Ok(())
2798 }
2799
2800 fn create_msa(keys: T::AccountId) -> Result<MessageSourceId, DispatchError> {
2801 let (msa_id, _) = Self::create_account(keys)?;
2802 Ok(msa_id)
2803 }
2804}
2805
2806#[cfg(feature = "runtime-benchmarks")]
2807impl<T: Config> RegisterProviderBenchmarkHelper for Pallet<T> {
2808 fn create(provider_id: MessageSourceId, name: Vec<u8>) -> DispatchResult {
2810 let name = BoundedVec::<u8, T::MaxProviderNameSize>::try_from(name).expect("error");
2811 let payload = ProviderRegistryEntry {
2812 default_name: name,
2813 localized_names: BoundedBTreeMap::new(),
2814 default_logo_250_100_png_cid: BoundedVec::new(),
2815 localized_logo_250_100_png_cids: BoundedBTreeMap::new(),
2816 };
2817 Self::create_registered_provider(provider_id.into(), payload)?;
2818
2819 Ok(())
2820 }
2821}
2822
2823impl<T: Config> MsaLookup for Pallet<T> {
2824 type AccountId = T::AccountId;
2825
2826 fn get_msa_id(key: &Self::AccountId) -> Option<MessageSourceId> {
2827 Self::get_owner_of(key)
2828 }
2829
2830 fn get_max_msa_id() -> MessageSourceId {
2831 CurrentMsaIdentifierMaximum::<T>::get()
2832 }
2833}
2834
2835impl<T: Config> MsaValidator for Pallet<T> {
2836 type AccountId = T::AccountId;
2837
2838 fn ensure_valid_msa_key(key: &T::AccountId) -> Result<MessageSourceId, DispatchError> {
2839 Self::ensure_valid_msa_key(key)
2840 }
2841}
2842
2843impl<T: Config> ProviderLookup for Pallet<T> {
2844 type BlockNumber = BlockNumberFor<T>;
2845 type MaxGrantsPerDelegation = T::MaxGrantsPerDelegation;
2846 type DelegationId = IntentId;
2847
2848 fn get_delegation_of(
2849 delegator: DelegatorId,
2850 provider: ProviderId,
2851 ) -> Option<Delegation<Self::DelegationId, Self::BlockNumber, Self::MaxGrantsPerDelegation>> {
2852 DelegatorAndProviderToDelegation::<T>::get(delegator, provider)
2853 }
2854}
2855
2856impl<T: Config> DelegationValidator for Pallet<T> {
2857 type BlockNumber = BlockNumberFor<T>;
2858 type MaxGrantsPerDelegation = T::MaxGrantsPerDelegation;
2859 type DelegationIdType = IntentId;
2860
2861 fn ensure_valid_delegation(
2871 provider_id: ProviderId,
2872 delegator_id: DelegatorId,
2873 block_number: Option<Self::BlockNumber>,
2874 ) -> Result<
2875 Delegation<Self::DelegationIdType, Self::BlockNumber, Self::MaxGrantsPerDelegation>,
2876 DispatchError,
2877 > {
2878 let info = DelegatorAndProviderToDelegation::<T>::get(delegator_id, provider_id)
2879 .ok_or(Error::<T>::DelegationNotFound)?;
2880 let current_block = frame_system::Pallet::<T>::block_number();
2881 let requested_block = match block_number {
2882 Some(block_number) => {
2883 ensure!(
2884 current_block >= block_number,
2885 Error::<T>::CannotPredictValidityPastCurrentBlock
2886 );
2887 block_number
2888 },
2889 None => current_block,
2890 };
2891
2892 if info.revoked_at == Self::BlockNumber::zero() {
2893 return Ok(info);
2894 }
2895 ensure!(info.revoked_at >= requested_block, Error::<T>::DelegationRevoked);
2896
2897 Ok(info)
2898 }
2899}
2900
2901impl<T: Config> TargetValidator for Pallet<T> {
2902 fn validate(target: MessageSourceId) -> bool {
2903 Self::is_registered_provider(target)
2904 }
2905}
2906
2907impl<T: Config> GrantValidator<IntentId, BlockNumberFor<T>> for Pallet<T> {
2908 fn ensure_valid_grant(
2917 provider: ProviderId,
2918 delegator: DelegatorId,
2919 intent_id: IntentId,
2920 block_number: BlockNumberFor<T>,
2921 ) -> DispatchResult {
2922 let provider_info = Self::ensure_valid_delegation(provider, delegator, Some(block_number))?;
2923
2924 let permission_revoked_at_block_number = provider_info
2925 .permissions
2926 .get(&intent_id)
2927 .ok_or(Error::<T>::PermissionNotGranted)?;
2928
2929 if *permission_revoked_at_block_number == BlockNumberFor::<T>::zero() {
2930 return Ok(());
2931 }
2932
2933 ensure!(
2934 block_number <= *permission_revoked_at_block_number,
2935 Error::<T>::PermissionNotGranted
2936 );
2937
2938 Ok(())
2939 }
2940}
2941
2942impl<T: Config> MsaKeyProvider for Pallet<T> {
2943 type AccountId = T::AccountId;
2944 fn key_eligible_for_subsidized_addition(
2950 old_key: Self::AccountId,
2951 new_key: Self::AccountId,
2952 msa_id: MessageSourceId,
2953 ) -> bool {
2954 let new_address32 = T::ConvertIntoAccountId32::convert((new_key).clone());
2955 if EthereumAddressMapper::is_ethereum_address(&new_address32) {
2956 if let Some(stored_msa_id) = Self::get_msa_id(&old_key) {
2957 return stored_msa_id == msa_id && PublicKeyCountForMsaId::<T>::get(msa_id).eq(&1u8);
2958 }
2959 }
2960 false
2961 }
2962}
2963
2964#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)]
2969#[scale_info(skip_type_params(T))]
2970pub struct CheckFreeExtrinsicUse<T: Config + Send + Sync>(PhantomData<T>);
2971
2972impl<T: Config + Send + Sync> CheckFreeExtrinsicUse<T> {
2973 pub fn validate_delegation_by_delegator(
2984 account_id: &T::AccountId,
2985 provider_msa_id: &MessageSourceId,
2986 ) -> TransactionValidity {
2987 const TAG_PREFIX: &str = "DelegatorDelegationRevocation";
2988 let delegator_msa_id: DelegatorId = Pallet::<T>::ensure_valid_msa_key(account_id)
2989 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?
2990 .into();
2991 let provider_msa_id = ProviderId(*provider_msa_id);
2992
2993 Pallet::<T>::ensure_valid_delegation(provider_msa_id, delegator_msa_id, None)
2994 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidDelegation as u8))?;
2995 ValidTransaction::with_tag_prefix(TAG_PREFIX).and_provides(account_id).build()
2996 }
2997
2998 pub fn validate_delegation_by_provider(
3009 account_id: &T::AccountId,
3010 delegator_msa_id: &MessageSourceId,
3011 ) -> TransactionValidity {
3012 const TAG_PREFIX: &str = "ProviderDelegationRevocation";
3013
3014 let provider_msa_id: ProviderId = Pallet::<T>::ensure_valid_msa_key(account_id)
3015 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?
3016 .into();
3017 let delegator_msa_id = DelegatorId(*delegator_msa_id);
3018
3019 Pallet::<T>::ensure_valid_delegation(provider_msa_id, delegator_msa_id, None)
3021 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidDelegation as u8))?;
3022 ValidTransaction::with_tag_prefix(TAG_PREFIX).and_provides(account_id).build()
3023 }
3024
3025 pub fn validate_key_delete(
3038 signing_public_key: &T::AccountId,
3039 public_key_to_delete: &T::AccountId,
3040 ) -> TransactionValidity {
3041 const TAG_PREFIX: &str = "KeyRevocation";
3042
3043 ensure!(
3044 signing_public_key != public_key_to_delete,
3045 InvalidTransaction::Custom(ValidityError::InvalidSelfRemoval as u8)
3046 );
3047
3048 let maybe_owner_msa_id: MessageSourceId =
3049 Pallet::<T>::ensure_valid_msa_key(signing_public_key)
3050 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?;
3051
3052 let msa_id_for_key_to_delete: MessageSourceId =
3053 Pallet::<T>::ensure_valid_msa_key(public_key_to_delete)
3054 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?;
3055
3056 ensure!(
3057 maybe_owner_msa_id == msa_id_for_key_to_delete,
3058 InvalidTransaction::Custom(ValidityError::NotKeyOwner as u8)
3059 );
3060
3061 ValidTransaction::with_tag_prefix(TAG_PREFIX)
3062 .and_provides(signing_public_key)
3063 .build()
3064 }
3065
3066 pub fn ensure_msa_can_retire(account_id: &T::AccountId) -> TransactionValidity {
3082 const TAG_PREFIX: &str = "MSARetirement";
3083 let msa_id = Pallet::<T>::ensure_valid_msa_key(account_id)
3084 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?;
3085
3086 ensure!(
3087 !Pallet::<T>::is_registered_provider(msa_id),
3088 InvalidTransaction::Custom(
3089 ValidityError::InvalidRegisteredProviderCannotBeRetired as u8
3090 )
3091 );
3092
3093 let msa_handle = T::HandleProvider::get_handle_for_msa(msa_id);
3094 ensure!(
3095 msa_handle.is_none(),
3096 InvalidTransaction::Custom(ValidityError::HandleNotRetired as u8)
3097 );
3098
3099 let key_count = PublicKeyCountForMsaId::<T>::get(msa_id);
3100 ensure!(
3101 key_count == 1,
3102 InvalidTransaction::Custom(ValidityError::InvalidMoreThanOneKeyExists as u8)
3103 );
3104
3105 let delegator_id = DelegatorId(msa_id);
3106 let has_active_delegations: bool = DelegatorAndProviderToDelegation::<T>::iter_key_prefix(
3107 delegator_id,
3108 )
3109 .any(|provider_id| {
3110 Pallet::<T>::ensure_valid_delegation(provider_id, delegator_id, None).is_ok()
3111 });
3112
3113 ensure!(
3114 !has_active_delegations,
3115 InvalidTransaction::Custom(ValidityError::InvalidNonZeroProviderDelegations as u8)
3116 );
3117
3118 let msa_address = Pallet::<T>::msa_id_to_eth_address(msa_id);
3120
3121 let mut bytes = &EthereumAddressMapper::to_bytes32(&msa_address.0)[..];
3123 let msa_account_id = T::AccountId::decode(&mut bytes).map_err(|_| {
3124 log::error!("Failed to decode MSA account ID from Ethereum address");
3125 InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8)
3126 })?;
3127
3128 let msa_balance = T::Currency::reducible_balance(
3130 &msa_account_id,
3131 Preservation::Expendable,
3132 Fortitude::Polite,
3133 );
3134 ensure!(
3135 msa_balance == Zero::zero(),
3136 InvalidTransaction::Custom(ValidityError::InvalidMsaHoldingTokenCannotBeRetired as u8)
3137 );
3138
3139 ValidTransaction::with_tag_prefix(TAG_PREFIX).and_provides(account_id).build()
3140 }
3141
3142 pub fn validate_msa_token_withdrawal(
3157 receiver_account_id: &T::AccountId,
3158 msa_owner_public_key: &T::AccountId,
3159 msa_owner_proof: &MultiSignature,
3160 authorization_payload: &AuthorizedKeyData<T>,
3161 ) -> TransactionValidity {
3162 const TAG_PREFIX: &str = "MsaTokenWithdrawal";
3163
3164 ensure!(
3165 *receiver_account_id == authorization_payload.authorized_public_key,
3166 InvalidTransaction::Custom(ValidityError::NotKeyOwner as u8)
3167 );
3168
3169 ensure!(
3170 authorization_payload.discriminant == PayloadTypeDiscriminator::AuthorizedKeyData,
3171 InvalidTransaction::Custom(ValidityError::MsaOwnershipInvalidSignature as u8)
3172 );
3173 ensure!(
3174 Pallet::<T>::verify_signature(
3175 msa_owner_proof,
3176 msa_owner_public_key,
3177 authorization_payload
3178 ),
3179 InvalidTransaction::Custom(ValidityError::MsaOwnershipInvalidSignature as u8)
3180 );
3181
3182 ensure!(
3184 !PublicKeyToMsaId::<T>::contains_key(receiver_account_id),
3185 InvalidTransaction::Custom(ValidityError::IneligibleOrigin as u8)
3186 );
3187
3188 Pallet::<T>::check_signature_against_registry(
3189 msa_owner_proof,
3190 authorization_payload.expiration,
3191 )
3192 .map_err(|_| {
3193 InvalidTransaction::Custom(ValidityError::MsaOwnershipInvalidSignature as u8)
3194 })?;
3195
3196 let msa_id = authorization_payload.msa_id;
3197
3198 Pallet::<T>::ensure_msa_owner(msa_owner_public_key, msa_id)
3199 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?;
3200
3201 let msa_address = Pallet::<T>::msa_id_to_eth_address(msa_id);
3203
3204 let mut bytes = &EthereumAddressMapper::to_bytes32(&msa_address.0)[..];
3206 let msa_account_id = T::AccountId::decode(&mut bytes).map_err(|_| {
3207 log::error!("Failed to decode MSA account ID from Ethereum address");
3208 InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8)
3209 })?;
3210
3211 let msa_balance = T::Currency::reducible_balance(
3213 &msa_account_id,
3214 Preservation::Expendable,
3215 Fortitude::Polite,
3216 );
3217 ensure!(
3218 msa_balance > Zero::zero(),
3219 InvalidTransaction::Custom(ValidityError::InsufficientBalanceToWithdraw as u8)
3220 );
3221 ValidTransaction::with_tag_prefix(TAG_PREFIX)
3222 .and_provides(receiver_account_id)
3223 .build()
3224 }
3225}
3226
3227pub enum ValidityError {
3229 InvalidDelegation,
3231 InvalidMsaKey,
3233 InvalidRegisteredProviderCannotBeRetired,
3235 InvalidMoreThanOneKeyExists,
3237 InvalidSelfRemoval,
3239 NotKeyOwner,
3241 InvalidNonZeroProviderDelegations,
3243 HandleNotRetired,
3245 MsaOwnershipInvalidSignature,
3247 InsufficientBalanceToWithdraw,
3249 IneligibleOrigin,
3251 InvalidMsaHoldingTokenCannotBeRetired,
3253}
3254
3255impl<T: Config + Send + Sync> CheckFreeExtrinsicUse<T> {
3256 pub fn new() -> Self {
3258 Self(PhantomData)
3259 }
3260}
3261
3262impl<T: Config + Send + Sync> core::fmt::Debug for CheckFreeExtrinsicUse<T> {
3263 #[cfg(feature = "std")]
3264 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
3265 write!(f, "CheckFreeExtrinsicUse<{:?}>", self.0)
3266 }
3267 #[cfg(not(feature = "std"))]
3268 fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
3269 Ok(())
3270 }
3271}
3272
3273#[derive(RuntimeDebugNoBound)]
3275pub enum Val {
3276 Valid,
3278 Refund(Weight),
3280}
3281
3282#[derive(RuntimeDebugNoBound)]
3284pub enum Pre {
3285 Valid,
3287 Refund(Weight),
3289}
3290
3291impl<T: Config + Send + Sync> TransactionExtension<T::RuntimeCall> for CheckFreeExtrinsicUse<T>
3292where
3293 T::RuntimeCall: Dispatchable<Info = DispatchInfo> + IsSubType<Call<T>>,
3294 <T as frame_system::Config>::RuntimeOrigin: AsSystemOriginSigner<T::AccountId> + Clone,
3295{
3296 const IDENTIFIER: &'static str = "CheckFreeExtrinsicUse";
3297 type Implicit = ();
3298 type Val = Val;
3299 type Pre = Pre;
3300
3301 fn weight(&self, call: &T::RuntimeCall) -> Weight {
3302 match call.is_sub_type() {
3303 Some(Call::revoke_delegation_by_provider { .. }) =>
3304 T::WeightInfo::check_free_extrinsic_use_revoke_delegation_by_provider(),
3305 Some(Call::revoke_delegation_by_delegator { .. }) =>
3306 T::WeightInfo::check_free_extrinsic_use_revoke_delegation_by_delegator(),
3307 Some(Call::delete_msa_public_key { .. }) =>
3308 T::WeightInfo::check_free_extrinsic_use_delete_msa_public_key(),
3309 Some(Call::retire_msa { .. }) => T::WeightInfo::check_free_extrinsic_use_retire_msa(),
3310 Some(Call::withdraw_tokens { .. }) =>
3311 T::WeightInfo::check_free_extrinsic_use_withdraw_tokens(),
3312 _ => Weight::zero(),
3313 }
3314 }
3315
3316 fn validate(
3317 &self,
3318 origin: <T as frame_system::Config>::RuntimeOrigin,
3319 call: &T::RuntimeCall,
3320 _info: &DispatchInfoOf<T::RuntimeCall>,
3321 _len: usize,
3322 _self_implicit: Self::Implicit,
3323 _inherited_implication: &impl Encode,
3324 _source: TransactionSource,
3325 ) -> ValidateResult<Self::Val, T::RuntimeCall> {
3326 let weight = self.weight(call);
3327 let Some(who) = origin.as_system_origin_signer() else {
3328 return Ok((ValidTransaction::default(), Val::Refund(weight), origin));
3329 };
3330 let validity = match call.is_sub_type() {
3331 Some(Call::revoke_delegation_by_provider { delegator, .. }) =>
3332 Self::validate_delegation_by_provider(who, delegator),
3333 Some(Call::revoke_delegation_by_delegator { provider_msa_id, .. }) =>
3334 Self::validate_delegation_by_delegator(who, provider_msa_id),
3335 Some(Call::delete_msa_public_key { public_key_to_delete, .. }) =>
3336 Self::validate_key_delete(who, public_key_to_delete),
3337 Some(Call::retire_msa { .. }) => Self::ensure_msa_can_retire(who),
3338 Some(Call::withdraw_tokens {
3339 msa_owner_public_key,
3340 msa_owner_proof,
3341 authorization_payload,
3342 }) => Self::validate_msa_token_withdrawal(
3343 who,
3344 msa_owner_public_key,
3345 msa_owner_proof,
3346 authorization_payload,
3347 ),
3348 _ => Ok(Default::default()),
3349 };
3350 validity.map(|v| (v, Val::Valid, origin))
3351 }
3352
3353 fn prepare(
3354 self,
3355 val: Self::Val,
3356 _origin: &<T as frame_system::Config>::RuntimeOrigin,
3357 _call: &T::RuntimeCall,
3358 _info: &DispatchInfoOf<T::RuntimeCall>,
3359 _len: usize,
3360 ) -> Result<Self::Pre, TransactionValidityError> {
3361 match val {
3362 Val::Valid => Ok(Pre::Valid),
3363 Val::Refund(w) => Ok(Pre::Refund(w)),
3364 }
3365 }
3366
3367 fn post_dispatch_details(
3368 pre: Self::Pre,
3369 _info: &DispatchInfo,
3370 _post_info: &PostDispatchInfoOf<T::RuntimeCall>,
3371 _len: usize,
3372 _result: &sp_runtime::DispatchResult,
3373 ) -> Result<Weight, TransactionValidityError> {
3374 match pre {
3375 Pre::Valid => Ok(Weight::zero()),
3376 Pre::Refund(w) => Ok(w),
3377 }
3378 }
3379}