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, PermittedDelegationSchemas,
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 MaxSchemaGrantsPerDelegation: 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<SchemaId, BlockNumberFor<T>, T::MaxSchemaGrantsPerDelegation>,
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
586 impl<T: Config> BlockNumberProvider for Pallet<T> {
587 type BlockNumber = BlockNumberFor<T>;
588
589 fn current_block_number() -> Self::BlockNumber {
590 frame_system::Pallet::<T>::block_number()
591 }
592 }
593
594 #[pallet::hooks]
595 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
596 fn on_initialize(_block_number: BlockNumberFor<T>) -> Weight {
597 <OffchainIndexEventCount<T>>::set(0u16);
598 let migration_weight = crate::migration::v2::on_initialize_migration::<T>(); T::DbWeight::get().reads_writes(1u64, 1u64).saturating_add(migration_weight)
600 }
601
602 fn offchain_worker(block_number: BlockNumberFor<T>) {
603 log::info!("Running offchain workers! {block_number:?}");
604 do_offchain_worker::<T>(block_number)
605 }
606 }
607
608 #[pallet::call]
609 impl<T: Config> Pallet<T> {
610 #[pallet::call_index(0)]
620 #[pallet::weight(T::WeightInfo::create())]
621 pub fn create(origin: OriginFor<T>) -> DispatchResult {
622 let public_key = ensure_signed(origin)?;
623
624 let (new_msa_id, new_public_key) = Self::create_account(public_key)?;
625
626 let event = Event::MsaCreated { msa_id: new_msa_id, key: new_public_key };
627 offchain_index_event::<T>(Some(&event), new_msa_id);
628 Self::deposit_event(event);
629 Ok(())
630 }
631
632 #[pallet::call_index(1)]
654 #[pallet::weight(T::WeightInfo::create_sponsored_account_with_delegation(
655 add_provider_payload.schema_ids.len() as u32
656 ))]
657 pub fn create_sponsored_account_with_delegation(
658 origin: OriginFor<T>,
659 delegator_key: T::AccountId,
660 proof: MultiSignature,
661 add_provider_payload: AddProvider,
662 ) -> DispatchResult {
663 let provider_key = ensure_signed(origin)?;
664
665 ensure!(
666 Self::verify_signature(&proof, &delegator_key, &add_provider_payload),
667 Error::<T>::InvalidSignature
668 );
669
670 Self::register_signature(&proof, add_provider_payload.expiration.into())?;
671
672 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
673 ensure!(
674 add_provider_payload.authorized_msa_id == provider_msa_id,
675 Error::<T>::UnauthorizedProvider
676 );
677
678 ensure!(
680 Self::is_registered_provider(provider_msa_id),
681 Error::<T>::ProviderNotRegistered
682 );
683
684 let (new_delegator_msa_id, new_delegator_public_key) =
685 Self::create_account(delegator_key)?;
686 Self::add_provider(
687 ProviderId(provider_msa_id),
688 DelegatorId(new_delegator_msa_id),
689 add_provider_payload.schema_ids,
690 )?;
691 let event =
692 Event::MsaCreated { msa_id: new_delegator_msa_id, key: new_delegator_public_key };
693 offchain_index_event::<T>(Some(&event), new_delegator_msa_id);
694 Self::deposit_event(event);
695 Self::deposit_event(Event::DelegationGranted {
696 delegator_id: DelegatorId(new_delegator_msa_id),
697 provider_id: ProviderId(provider_msa_id),
698 });
699 Ok(())
700 }
701
702 #[pallet::call_index(2)]
713 #[pallet::weight(T::WeightInfo::create_provider())]
714 pub fn create_provider(origin: OriginFor<T>, provider_name: Vec<u8>) -> DispatchResult {
715 let provider_key = ensure_signed(origin)?;
716 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
717 let bounded_name: BoundedVec<u8, T::MaxProviderNameSize> =
718 provider_name.try_into().map_err(|_| Error::<T>::ExceedsMaxProviderNameSize)?;
719
720 let entry = ProviderRegistryEntry { default_name: bounded_name, ..Default::default() };
721 Self::ensure_correct_cids(&entry)?;
722 Self::upsert_provider_for(provider_msa_id, entry, false)?;
723 Self::deposit_event(Event::ProviderCreated {
724 provider_id: ProviderId(provider_msa_id),
725 });
726 Ok(())
727 }
728
729 #[pallet::call_index(3)]
750 #[pallet::weight(T::WeightInfo::grant_delegation(add_provider_payload.schema_ids.len() as u32))]
751 pub fn grant_delegation(
752 origin: OriginFor<T>,
753 delegator_key: T::AccountId,
754 proof: MultiSignature,
755 add_provider_payload: AddProvider,
756 ) -> DispatchResult {
757 let provider_key = ensure_signed(origin)?;
758
759 ensure!(
760 Self::verify_signature(&proof, &delegator_key, &add_provider_payload),
761 Error::<T>::AddProviderSignatureVerificationFailed
762 );
763
764 Self::register_signature(&proof, add_provider_payload.expiration.into())?;
765 let (provider_id, delegator_id) =
766 Self::ensure_valid_registered_provider(&delegator_key, &provider_key)?;
767
768 ensure!(
769 add_provider_payload.authorized_msa_id == provider_id.0,
770 Error::<T>::UnauthorizedDelegator
771 );
772
773 Self::upsert_schema_permissions(
774 provider_id,
775 delegator_id,
776 add_provider_payload.schema_ids,
777 )?;
778 Self::deposit_event(Event::DelegationGranted { delegator_id, provider_id });
779
780 Ok(())
781 }
782
783 #[pallet::call_index(4)]
795 #[pallet::weight((T::WeightInfo::revoke_delegation_by_delegator(), DispatchClass::Normal, Pays::No))]
796 pub fn revoke_delegation_by_delegator(
797 origin: OriginFor<T>,
798 #[pallet::compact] provider_msa_id: MessageSourceId,
799 ) -> DispatchResult {
800 let who = ensure_signed(origin)?;
801
802 match PublicKeyToMsaId::<T>::get(&who) {
803 Some(delegator_msa_id) => {
804 let delegator_id = DelegatorId(delegator_msa_id);
805 let provider_id = ProviderId(provider_msa_id);
806 Self::revoke_provider(provider_id, delegator_id)?;
807 Self::deposit_event(Event::DelegationRevoked { delegator_id, provider_id });
808 },
809 None => {
810 log::error!(
811 "TransactionExtension did not catch invalid MSA for account {who:?}, "
812 );
813 },
814 }
815
816 Ok(())
817 }
818
819 #[pallet::call_index(5)]
845 #[pallet::weight(T::WeightInfo::add_public_key_to_msa())]
846 pub fn add_public_key_to_msa(
847 origin: OriginFor<T>,
848 msa_owner_public_key: T::AccountId,
849 msa_owner_proof: MultiSignature,
850 new_key_owner_proof: MultiSignature,
851 add_key_payload: AddKeyData<T>,
852 ) -> DispatchResult {
853 let _ = ensure_signed(origin)?;
854
855 ensure!(
856 Self::verify_signature(&msa_owner_proof, &msa_owner_public_key, &add_key_payload),
857 Error::<T>::MsaOwnershipInvalidSignature
858 );
859
860 ensure!(
861 Self::verify_signature(
862 &new_key_owner_proof,
863 &add_key_payload.new_public_key,
864 &add_key_payload
865 ),
866 Error::<T>::NewKeyOwnershipInvalidSignature
867 );
868
869 Self::register_signature(&msa_owner_proof, add_key_payload.expiration)?;
870 Self::register_signature(&new_key_owner_proof, add_key_payload.expiration)?;
871
872 let msa_id = add_key_payload.msa_id;
873
874 Self::ensure_msa_owner(&msa_owner_public_key, msa_id)?;
875
876 Self::add_key(msa_id, &add_key_payload.new_public_key.clone())?;
877
878 let event =
879 Event::PublicKeyAdded { msa_id, key: add_key_payload.new_public_key.clone() };
880 offchain_index_event::<T>(Some(&event), msa_id);
881 Self::deposit_event(event);
882
883 Ok(())
884 }
885
886 #[pallet::call_index(6)]
901 #[pallet::weight((T::WeightInfo::delete_msa_public_key(), DispatchClass::Normal, Pays::No))]
902 pub fn delete_msa_public_key(
903 origin: OriginFor<T>,
904 public_key_to_delete: T::AccountId,
905 ) -> DispatchResult {
906 let who = ensure_signed(origin)?;
907
908 match PublicKeyToMsaId::<T>::get(&who) {
909 Some(who_msa_id) => {
910 Self::delete_key_for_msa(who_msa_id, &public_key_to_delete)?;
911
912 let event = Event::PublicKeyDeleted { key: public_key_to_delete };
914 offchain_index_event::<T>(Some(&event), who_msa_id);
915 Self::deposit_event(event);
916 },
917 None => {
918 log::error!(
919 "TransactionExtension did not catch invalid MSA for account {who:?}"
920 );
921 },
922 }
923 Ok(())
924 }
925
926 #[pallet::call_index(7)]
938 #[pallet::weight((T::WeightInfo::revoke_delegation_by_provider(), DispatchClass::Normal, Pays::No))]
939 pub fn revoke_delegation_by_provider(
940 origin: OriginFor<T>,
941 #[pallet::compact] delegator: MessageSourceId,
942 ) -> DispatchResult {
943 let who = ensure_signed(origin)?;
944
945 match PublicKeyToMsaId::<T>::get(&who) {
949 Some(msa_id) => {
950 let provider_id = ProviderId(msa_id);
951 let delegator_id = DelegatorId(delegator);
952 Self::revoke_provider(provider_id, delegator_id)?;
953 Self::deposit_event(Event::DelegationRevoked { provider_id, delegator_id })
954 },
955 None => {
956 log::error!(
957 "TransactionExtension did not catch invalid MSA for account {who:?}"
958 );
959 },
960 }
961
962 Ok(())
963 }
964
965 #[pallet::call_index(10)]
987 #[pallet::weight((T::WeightInfo::retire_msa(), DispatchClass::Normal, Pays::No))]
988 pub fn retire_msa(origin: OriginFor<T>) -> DispatchResult {
989 let who = ensure_signed(origin)?;
991
992 match PublicKeyToMsaId::<T>::get(&who) {
995 Some(msa_id) => {
996 Self::delete_key_for_msa(msa_id, &who)?;
997 let event = Event::PublicKeyDeleted { key: who };
998 offchain_index_event::<T>(Some(&event), msa_id);
999 Self::deposit_event(event);
1000 Self::deposit_event(Event::MsaRetired { msa_id });
1001 },
1002 None => {
1003 log::error!(
1004 "TransactionExtension did not catch invalid MSA for account {who:?}"
1005 );
1006 },
1007 }
1008 Ok(())
1009 }
1010
1011 #[pallet::call_index(11)]
1017 #[pallet::weight(T::WeightInfo::propose_to_be_provider_v2())]
1018 #[allow(deprecated)]
1019 #[deprecated(
1020 note = "please use `propose_to_be_provider_v2`, which supports additional provider metadata"
1021 )]
1022 pub fn propose_to_be_provider(
1023 origin: OriginFor<T>,
1024 provider_name: Vec<u8>,
1025 ) -> DispatchResult {
1026 let bounded_name: BoundedVec<u8, T::MaxProviderNameSize> =
1027 provider_name.try_into().map_err(|_| Error::<T>::ExceedsMaxProviderNameSize)?;
1028
1029 let entry = ProviderRegistryEntry { default_name: bounded_name, ..Default::default() };
1030
1031 Self::propose_to_be_provider_v2(origin, entry)
1032 }
1033
1034 #[pallet::call_index(12)]
1044 #[pallet::weight(T::WeightInfo::create_provider_via_governance_v2(0u32, 0u32))]
1045 #[allow(deprecated)]
1046 #[deprecated(
1047 note = "please use `create_provider_via_governance_v2`, which supports additional provider metadata"
1048 )]
1049 pub fn create_provider_via_governance(
1050 origin: OriginFor<T>,
1051 provider_key: T::AccountId,
1052 provider_name: Vec<u8>,
1053 ) -> DispatchResult {
1054 let bounded_name: BoundedVec<u8, T::MaxProviderNameSize> =
1055 provider_name.try_into().map_err(|_| Error::<T>::ExceedsMaxProviderNameSize)?;
1056
1057 let entry = ProviderRegistryEntry { default_name: bounded_name, ..Default::default() };
1058
1059 Self::create_provider_via_governance_v2(origin, provider_key, entry)
1060 }
1061
1062 #[pallet::call_index(13)]
1064 #[pallet::weight(T::WeightInfo::reindex_offchain())]
1065 pub fn reindex_offchain(
1066 origin: OriginFor<T>,
1067 event: OffchainReplayEvent<T>,
1068 ) -> DispatchResult {
1069 let _ = ensure_signed(origin)?;
1070 match event {
1071 OffchainReplayEvent::MsaPallet(MsaOffchainReplayEvent::KeyReIndex {
1072 msa_id,
1073 index_key,
1074 }) => {
1075 match index_key {
1077 Some(key) => {
1078 let event = Event::PublicKeyAdded { msa_id, key };
1079 offchain_index_event::<T>(Some(&event), msa_id);
1080 },
1081 None => {
1082 offchain_index_event::<T>(None, msa_id);
1083 },
1084 }
1085 },
1086 }
1087
1088 Ok(())
1089 }
1090
1091 #[pallet::call_index(14)]
1112 #[pallet::weight((T::WeightInfo::withdraw_tokens(), DispatchClass::Normal, Pays::No))]
1113 pub fn withdraw_tokens(
1114 origin: OriginFor<T>,
1115 _msa_owner_public_key: T::AccountId,
1116 msa_owner_proof: MultiSignature,
1117 authorization_payload: AuthorizedKeyData<T>,
1118 ) -> DispatchResult {
1119 let public_key = ensure_signed(origin)?;
1120
1121 Self::register_signature(&msa_owner_proof, authorization_payload.expiration)?;
1122
1123 let msa_id = authorization_payload.msa_id;
1124
1125 let msa_address = Self::msa_id_to_eth_address(msa_id);
1127
1128 let mut bytes = &EthereumAddressMapper::to_bytes32(&msa_address.0)[..];
1130 let msa_account_id = T::AccountId::decode(&mut bytes).map_err(|_| {
1131 log::error!("Failed to decode MSA account ID from Ethereum address");
1132 Error::<T>::NoKeyExists
1133 })?;
1134
1135 let msa_balance = T::Currency::reducible_balance(
1137 &msa_account_id,
1138 Preservation::Expendable,
1139 Fortitude::Polite,
1140 );
1141 ensure!(msa_balance > Zero::zero(), Error::<T>::InsufficientBalanceToWithdraw);
1142
1143 let result = <T as pallet::Config>::Currency::transfer(
1145 &msa_account_id,
1146 &public_key,
1147 msa_balance,
1148 Preservation::Expendable,
1149 );
1150 ensure!(result.is_ok(), Error::<T>::UnexpectedTokenTransferError);
1151
1152 Ok(())
1153 }
1154
1155 #[pallet::call_index(15)]
1177 #[pallet::weight(T::WeightInfo::add_recovery_commitment())]
1178 pub fn add_recovery_commitment(
1179 origin: OriginFor<T>,
1180 msa_owner_key: T::AccountId,
1181 proof: MultiSignature,
1182 payload: RecoveryCommitmentPayload<T>,
1183 ) -> DispatchResult {
1184 let _origin_key = ensure_signed(origin)?;
1185
1186 ensure!(
1188 Self::verify_signature(&proof, &msa_owner_key, &payload),
1189 Error::<T>::InvalidSignature
1190 );
1191
1192 Self::register_signature(&proof, payload.expiration)?;
1194
1195 let msa_id = Self::ensure_valid_msa_key(&msa_owner_key)?;
1197
1198 MsaIdToRecoveryCommitment::<T>::insert(msa_id, payload.recovery_commitment);
1200 Self::deposit_event(Event::RecoveryCommitmentAdded {
1201 who: msa_owner_key,
1202 msa_id,
1203 recovery_commitment: payload.recovery_commitment,
1204 });
1205
1206 Ok(())
1207 }
1208
1209 #[pallet::call_index(16)]
1219 #[pallet::weight(T::WeightInfo::approve_recovery_provider())]
1220 pub fn approve_recovery_provider(
1221 origin: OriginFor<T>,
1222 provider_key: T::AccountId,
1223 ) -> DispatchResult {
1224 T::RecoveryProviderApprovalOrigin::ensure_origin(origin)?;
1225
1226 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1227 ensure!(
1228 Self::is_registered_provider(provider_msa_id),
1229 Error::<T>::ProviderNotRegistered
1230 );
1231
1232 if Self::is_approved_recovery_provider(&ProviderId(provider_msa_id)) {
1234 return Ok(());
1235 }
1236
1237 RecoveryProviders::<T>::insert(ProviderId(provider_msa_id), true);
1238
1239 Self::deposit_event(Event::RecoveryProviderApproved {
1240 provider_id: ProviderId(provider_msa_id),
1241 });
1242
1243 Ok(())
1244 }
1245
1246 #[pallet::call_index(17)]
1257 #[pallet::weight(T::WeightInfo::remove_recovery_provider())]
1258 pub fn remove_recovery_provider(
1259 origin: OriginFor<T>,
1260 provider: ProviderId,
1261 ) -> DispatchResult {
1262 T::RecoveryProviderApprovalOrigin::ensure_origin(origin)?;
1263
1264 RecoveryProviders::<T>::remove(provider);
1265 Self::deposit_event(Event::RecoveryProviderRemoved { provider_id: provider });
1266 Ok(())
1267 }
1268
1269 #[pallet::call_index(18)]
1284 #[pallet::weight(T::WeightInfo::recover_account())]
1285 pub fn recover_account(
1286 origin: OriginFor<T>,
1287 intermediary_hash_a: RecoveryHash,
1288 intermediary_hash_b: RecoveryHash,
1289 new_control_key_proof: MultiSignature,
1290 add_key_payload: AddKeyData<T>,
1291 ) -> DispatchResult {
1292 let provider_key = ensure_signed(origin)?;
1293
1294 let provider_msa_id = Self::ensure_approved_recovery_provider(&provider_key)?;
1295
1296 let recovery_commitment = Self::ensure_valid_recovery_commitment(
1297 intermediary_hash_a,
1298 intermediary_hash_b,
1299 add_key_payload.msa_id,
1300 )?;
1301
1302 Self::ensure_valid_new_key_owner(&new_control_key_proof, &add_key_payload)?;
1303
1304 Self::add_key(add_key_payload.msa_id, &add_key_payload.new_public_key.clone())?;
1305
1306 let event = Event::PublicKeyAdded {
1307 msa_id: add_key_payload.msa_id,
1308 key: add_key_payload.new_public_key.clone(),
1309 };
1310 offchain_index_event::<T>(Some(&event), add_key_payload.msa_id);
1311 Self::deposit_event(event);
1312
1313 MsaIdToRecoveryCommitment::<T>::remove(add_key_payload.msa_id);
1315
1316 Self::deposit_event(Event::AccountRecovered {
1318 msa_id: add_key_payload.msa_id,
1319 recovery_provider: ProviderId(provider_msa_id),
1320 new_control_key: add_key_payload.new_public_key.clone(),
1321 });
1322
1323 Self::deposit_event(Event::RecoveryCommitmentInvalidated {
1324 msa_id: add_key_payload.msa_id,
1325 recovery_commitment,
1326 });
1327
1328 Ok(())
1329 }
1330
1331 #[pallet::call_index(19)]
1336 #[pallet::weight(T::WeightInfo::propose_to_be_provider_v2())]
1337 pub fn propose_to_be_provider_v2(
1338 origin: OriginFor<T>,
1339 payload: ProviderRegistryEntry<
1340 T::MaxProviderNameSize,
1341 T::MaxLanguageCodeSize,
1342 T::MaxLogoCidSize,
1343 T::MaxLocaleCount,
1344 >,
1345 ) -> DispatchResult {
1346 let proposer = ensure_signed(origin)?;
1347 Self::ensure_valid_msa_key(&proposer)?;
1348
1349 let proposal: Box<T::Proposal> = Box::new(
1350 (Call::<T>::create_provider_via_governance_v2 {
1351 provider_key: proposer.clone(),
1352 payload,
1353 })
1354 .into(),
1355 );
1356 let threshold = 1;
1357 T::ProposalProvider::propose(proposer, threshold, proposal)?;
1358 Ok(())
1359 }
1360
1361 #[pallet::call_index(20)]
1372 #[pallet::weight(T::WeightInfo::create_provider_via_governance_v2(
1373 payload.localized_names.len() as u32,
1374 payload.localized_logo_250_100_png_cids.len() as u32,
1375 ))]
1376 pub fn create_provider_via_governance_v2(
1377 origin: OriginFor<T>,
1378 provider_key: T::AccountId,
1379 payload: ProviderRegistryEntry<
1380 T::MaxProviderNameSize,
1381 T::MaxLanguageCodeSize,
1382 T::MaxLogoCidSize,
1383 T::MaxLocaleCount,
1384 >,
1385 ) -> DispatchResult {
1386 T::CreateProviderViaGovernanceOrigin::ensure_origin(origin)?;
1387 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1388 Self::ensure_correct_cids(&payload)?;
1389 Self::upsert_provider_for(provider_msa_id, payload, false)?;
1390 Self::deposit_event(Event::ProviderCreated {
1391 provider_id: ProviderId(provider_msa_id),
1392 });
1393 Ok(())
1394 }
1395
1396 #[pallet::call_index(21)]
1404 #[pallet::weight(T::WeightInfo::propose_to_add_application(
1405 payload.localized_names.len() as u32,
1406 payload.localized_logo_250_100_png_cids.len() as u32,
1407 ))]
1408 pub fn propose_to_add_application(
1409 origin: OriginFor<T>,
1410 payload: ApplicationContext<
1411 T::MaxProviderNameSize,
1412 T::MaxLanguageCodeSize,
1413 T::MaxLogoCidSize,
1414 T::MaxLocaleCount,
1415 >,
1416 ) -> DispatchResult {
1417 let proposer = ensure_signed(origin)?;
1418 let provider_msa_id = Self::ensure_valid_msa_key(&proposer)?;
1419 ensure!(
1420 Self::is_registered_provider(provider_msa_id),
1421 Error::<T>::ProviderNotRegistered
1422 );
1423 let proposal: Box<T::Proposal> = Box::new(
1424 (Call::<T>::create_application_via_governance {
1425 provider_key: proposer.clone(),
1426 payload,
1427 })
1428 .into(),
1429 );
1430 let threshold = 1;
1431 T::ProposalProvider::propose(proposer, threshold, proposal)?;
1432 Ok(())
1433 }
1434
1435 #[pallet::call_index(22)]
1446 #[pallet::weight(T::WeightInfo::create_application_via_governance(
1447 payload.localized_names.len() as u32,
1448 payload.localized_logo_250_100_png_cids.len() as u32,
1449 ))]
1450 pub fn create_application_via_governance(
1451 origin: OriginFor<T>,
1452 provider_key: T::AccountId,
1453 payload: ApplicationContext<
1454 T::MaxProviderNameSize,
1455 T::MaxLanguageCodeSize,
1456 T::MaxLogoCidSize,
1457 T::MaxLocaleCount,
1458 >,
1459 ) -> DispatchResult {
1460 T::CreateProviderViaGovernanceOrigin::ensure_origin(origin)?;
1461 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1462 ensure!(
1463 Self::is_registered_provider(provider_msa_id),
1464 Error::<T>::ProviderNotRegistered
1465 );
1466 Self::ensure_correct_cids(&payload)?;
1467 let application_id =
1468 Self::create_application_for(ProviderId(provider_msa_id), payload)?;
1469 Self::deposit_event(Event::ApplicationCreated {
1470 provider_id: ProviderId(provider_msa_id),
1471 application_id,
1472 });
1473 Ok(())
1474 }
1475
1476 #[pallet::call_index(23)]
1484 #[pallet::weight(T::WeightInfo::upload_logo())]
1485 pub fn upload_logo(
1486 origin: OriginFor<T>,
1487 logo_cid: BoundedVec<u8, T::MaxLogoCidSize>,
1488 logo_bytes: BoundedVec<u8, T::MaxLogoSize>,
1489 ) -> DispatchResult {
1490 let provider_key = ensure_signed(origin)?;
1491 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1492 ensure!(
1493 Self::is_registered_provider(provider_msa_id),
1494 Error::<T>::ProviderNotRegistered
1495 );
1496 ensure!(ApprovedLogos::<T>::contains_key(&logo_cid), Error::<T>::LogoCidNotApproved);
1497
1498 let input_cid_binary = Self::validate_cid(&logo_cid)?;
1499 let computed_cid_binary =
1500 compute_cid_v1(logo_bytes.as_slice()).ok_or(Error::<T>::InvalidLogoBytes)?;
1501 ensure!(input_cid_binary == computed_cid_binary, Error::<T>::InvalidLogoBytes);
1502 ApprovedLogos::<T>::insert(&logo_cid, logo_bytes);
1503
1504 Self::deposit_event(Event::ApplicationContextUpdated {
1505 provider_id: ProviderId(provider_msa_id),
1506 application_id: None,
1507 });
1508 Ok(())
1509 }
1510
1511 #[pallet::call_index(24)]
1521 #[pallet::weight(T::WeightInfo::update_provider_via_governance(
1522 payload.localized_names.len() as u32,
1523 payload.localized_logo_250_100_png_cids.len() as u32,
1524 ))]
1525 #[allow(clippy::useless_conversion)]
1526 pub fn update_provider_via_governance(
1527 origin: OriginFor<T>,
1528 provider_key: T::AccountId,
1529 payload: ProviderRegistryEntry<
1530 T::MaxProviderNameSize,
1531 T::MaxLanguageCodeSize,
1532 T::MaxLogoCidSize,
1533 T::MaxLocaleCount,
1534 >,
1535 ) -> DispatchResultWithPostInfo {
1536 T::CreateProviderViaGovernanceOrigin::ensure_origin(origin)?;
1537 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1538 Self::ensure_correct_cids(&payload)?;
1539 let base_weight = T::WeightInfo::update_provider_via_governance(
1540 payload.localized_names.len() as u32,
1541 payload.localized_logo_250_100_png_cids.len() as u32,
1542 );
1543 let total_logos_removed = Self::upsert_provider_for(provider_msa_id, payload, true)?;
1544 Self::deposit_event(Event::ProviderUpdated {
1545 provider_id: ProviderId(provider_msa_id),
1546 });
1547 Self::refund_logo_removal_weight_by_count(total_logos_removed, base_weight)
1548 }
1549
1550 #[pallet::call_index(25)]
1563 #[pallet::weight(T::WeightInfo::propose_to_update_provider(
1564 payload.localized_names.len() as u32,
1565 payload.localized_logo_250_100_png_cids.len() as u32,
1566 ))]
1567 pub fn propose_to_update_provider(
1568 origin: OriginFor<T>,
1569 payload: ProviderRegistryEntry<
1570 T::MaxProviderNameSize,
1571 T::MaxLanguageCodeSize,
1572 T::MaxLogoCidSize,
1573 T::MaxLocaleCount,
1574 >,
1575 ) -> DispatchResult {
1576 let proposer = ensure_signed(origin)?;
1577 let provider_msa_id = Self::ensure_valid_msa_key(&proposer)?;
1578 ensure!(
1579 Self::is_registered_provider(provider_msa_id),
1580 Error::<T>::ProviderNotRegistered
1581 );
1582 let proposal: Box<T::Proposal> = Box::new(
1583 (Call::<T>::update_provider_via_governance {
1584 provider_key: proposer.clone(),
1585 payload,
1586 })
1587 .into(),
1588 );
1589 let threshold = 1;
1590 T::ProposalProvider::propose(proposer, threshold, proposal)?;
1591 Ok(())
1592 }
1593
1594 #[pallet::call_index(26)]
1608 #[pallet::weight(T::WeightInfo::update_application_via_governance(
1609 payload.localized_names.len() as u32,
1610 payload.localized_logo_250_100_png_cids.len() as u32,
1611 ))]
1612 #[allow(clippy::useless_conversion)]
1613 pub fn update_application_via_governance(
1614 origin: OriginFor<T>,
1615 provider_key: T::AccountId,
1616 application_index: ApplicationIndex,
1617 payload: ApplicationContext<
1618 T::MaxProviderNameSize,
1619 T::MaxLanguageCodeSize,
1620 T::MaxLogoCidSize,
1621 T::MaxLocaleCount,
1622 >,
1623 ) -> DispatchResultWithPostInfo {
1624 T::CreateProviderViaGovernanceOrigin::ensure_origin(origin)?;
1625 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1626 ensure!(
1627 Self::is_registered_provider(provider_msa_id),
1628 Error::<T>::ProviderNotRegistered
1629 );
1630 Self::ensure_correct_cids(&payload)?;
1631 let base_weight = T::WeightInfo::update_application_via_governance(
1632 payload.localized_names.len() as u32,
1633 payload.localized_logo_250_100_png_cids.len() as u32,
1634 );
1635 let total_logos_removed = Self::upsert_application_for(
1636 ProviderId(provider_msa_id),
1637 application_index,
1638 payload,
1639 )?;
1640 Self::deposit_event(Event::ApplicationContextUpdated {
1641 provider_id: ProviderId(provider_msa_id),
1642 application_id: Some(application_index),
1643 });
1644 Self::refund_logo_removal_weight_by_count(total_logos_removed, base_weight)
1645 }
1646
1647 #[pallet::call_index(27)]
1661 #[pallet::weight(T::WeightInfo::propose_to_update_application(
1662 payload.localized_names.len() as u32,
1663 payload.localized_logo_250_100_png_cids.len() as u32,
1664 ))]
1665 pub fn propose_to_update_application(
1666 origin: OriginFor<T>,
1667 application_index: ApplicationIndex,
1668 payload: ApplicationContext<
1669 T::MaxProviderNameSize,
1670 T::MaxLanguageCodeSize,
1671 T::MaxLogoCidSize,
1672 T::MaxLocaleCount,
1673 >,
1674 ) -> DispatchResult {
1675 let proposer = ensure_signed(origin)?;
1676 let provider_msa_id = Self::ensure_valid_msa_key(&proposer)?;
1677 ensure!(
1678 Self::is_registered_provider(provider_msa_id),
1679 Error::<T>::ProviderNotRegistered
1680 );
1681 ensure!(
1682 ProviderToApplicationRegistry::<T>::contains_key(
1683 ProviderId(provider_msa_id),
1684 application_index
1685 ),
1686 Error::<T>::ApplicationNotFound
1687 );
1688 Self::ensure_correct_cids(&payload)?;
1689 let proposal: Box<T::Proposal> = Box::new(
1690 (Call::<T>::update_application_via_governance {
1691 provider_key: proposer.clone(),
1692 application_index,
1693 payload,
1694 })
1695 .into(),
1696 );
1697 let threshold = 1;
1698 T::ProposalProvider::propose(proposer, threshold, proposal)?;
1699 Ok(())
1700 }
1701
1702 #[pallet::call_index(28)]
1705 #[pallet::weight(T::WeightInfo::create_application_via_governance(
1706 payload.localized_names.len() as u32,
1707 payload.localized_logo_250_100_png_cids.len() as u32,
1708 ))]
1709 pub fn create_application(
1710 origin: OriginFor<T>,
1711 payload: ApplicationContext<
1712 T::MaxProviderNameSize,
1713 T::MaxLanguageCodeSize,
1714 T::MaxLogoCidSize,
1715 T::MaxLocaleCount,
1716 >,
1717 ) -> DispatchResult {
1718 let provider_account = ensure_signed(origin)?;
1719 let provider_msa_id = Self::ensure_valid_msa_key(&provider_account)?;
1720 ensure!(
1721 Self::is_registered_provider(provider_msa_id),
1722 Error::<T>::ProviderNotRegistered
1723 );
1724 Self::ensure_correct_cids(&payload)?;
1725 let application_id =
1726 Self::create_application_for(ProviderId(provider_msa_id), payload)?;
1727 Self::deposit_event(Event::ApplicationCreated {
1728 provider_id: ProviderId(provider_msa_id),
1729 application_id,
1730 });
1731 Ok(())
1732 }
1733 }
1734}
1735
1736impl<T: Config> Pallet<T> {
1737 pub fn is_approved_recovery_provider(provider: &ProviderId) -> bool {
1745 RecoveryProviders::<T>::get(provider).unwrap_or(false)
1746 }
1747
1748 pub fn compute_recovery_commitment(
1757 intermediary_hash_a: RecoveryHash,
1758 intermediary_hash_b: RecoveryHash,
1759 ) -> RecoveryCommitment {
1760 let mut input = Vec::with_capacity(64);
1761 input.extend_from_slice(&intermediary_hash_a);
1762 input.extend_from_slice(&intermediary_hash_b);
1763 keccak_256(&input)
1764 }
1765
1766 fn ensure_approved_recovery_provider(
1778 provider_key: &T::AccountId,
1779 ) -> Result<MessageSourceId, DispatchError> {
1780 let provider_msa_id = Self::ensure_valid_msa_key(provider_key)?;
1781
1782 ensure!(
1783 Self::is_approved_recovery_provider(&ProviderId(provider_msa_id)),
1784 Error::<T>::NotAuthorizedRecoveryProvider
1785 );
1786
1787 Ok(provider_msa_id)
1788 }
1789
1790 fn ensure_valid_recovery_commitment(
1804 intermediary_hash_a: RecoveryHash,
1805 intermediary_hash_b: RecoveryHash,
1806 msa_id: MessageSourceId,
1807 ) -> Result<RecoveryCommitment, DispatchError> {
1808 let recovery_commitment: RecoveryCommitment =
1809 Self::compute_recovery_commitment(intermediary_hash_a, intermediary_hash_b);
1810
1811 let stored_commitment: RecoveryCommitment =
1812 MsaIdToRecoveryCommitment::<T>::get(msa_id).ok_or(Error::<T>::NoRecoveryCommitment)?;
1813
1814 ensure!(recovery_commitment == stored_commitment, Error::<T>::InvalidRecoveryCommitment);
1815
1816 Ok(recovery_commitment)
1817 }
1818
1819 fn ensure_valid_new_key_owner(
1829 new_control_key_proof: &MultiSignature,
1830 add_key_payload: &AddKeyData<T>,
1831 ) -> DispatchResult {
1832 ensure!(
1835 Self::verify_signature(
1836 new_control_key_proof,
1837 &add_key_payload.new_public_key,
1838 add_key_payload
1839 ),
1840 Error::<T>::NewKeyOwnershipInvalidSignature
1841 );
1842 Self::register_signature(new_control_key_proof, add_key_payload.expiration)?;
1843
1844 Ok(())
1845 }
1846
1847 pub fn create_account(
1855 key: T::AccountId,
1856 ) -> Result<(MessageSourceId, T::AccountId), DispatchError> {
1857 let next_msa_id = Self::get_next_msa_id()?;
1858 Self::add_key(next_msa_id, &key)?;
1859 let _ = Self::set_msa_identifier(next_msa_id);
1860
1861 Ok((next_msa_id, key))
1862 }
1863
1864 pub fn get_next_msa_id() -> Result<MessageSourceId, DispatchError> {
1870 let next = CurrentMsaIdentifierMaximum::<T>::get()
1871 .checked_add(1)
1872 .ok_or(Error::<T>::MsaIdOverflow)?;
1873
1874 Ok(next)
1875 }
1876
1877 pub fn set_msa_identifier(identifier: MessageSourceId) -> DispatchResult {
1879 CurrentMsaIdentifierMaximum::<T>::set(identifier);
1880
1881 Ok(())
1882 }
1883
1884 pub fn create_registered_provider(
1886 provider_id: ProviderId,
1887 payload: ProviderRegistryEntry<
1888 T::MaxProviderNameSize,
1889 T::MaxLanguageCodeSize,
1890 T::MaxLogoCidSize,
1891 T::MaxLocaleCount,
1892 >,
1893 ) -> DispatchResult {
1894 ProviderToRegistryEntryV2::<T>::try_mutate(
1895 provider_id,
1896 |maybe_metadata| -> DispatchResult {
1897 ensure!(
1898 maybe_metadata.take().is_none(),
1899 Error::<T>::DuplicateProviderRegistryEntry
1900 );
1901 *maybe_metadata = Some(payload);
1902 Ok(())
1903 },
1904 )
1905 }
1906
1907 pub fn grant_permissions_for_schemas(
1909 delegator_id: DelegatorId,
1910 provider_id: ProviderId,
1911 schema_ids: Vec<SchemaId>,
1912 ) -> DispatchResult {
1913 Self::try_mutate_delegation(delegator_id, provider_id, |delegation, is_new_delegation| {
1914 ensure!(!is_new_delegation, Error::<T>::DelegationNotFound);
1915 Self::ensure_all_schema_ids_are_valid(&schema_ids)?;
1916
1917 PermittedDelegationSchemas::<T>::try_insert_schemas(delegation, schema_ids)?;
1918
1919 Ok(())
1920 })
1921 }
1922
1923 pub fn revoke_permissions_for_schemas(
1925 delegator_id: DelegatorId,
1926 provider_id: ProviderId,
1927 schema_ids: Vec<SchemaId>,
1928 ) -> DispatchResult {
1929 Self::try_mutate_delegation(delegator_id, provider_id, |delegation, is_new_delegation| {
1930 ensure!(!is_new_delegation, Error::<T>::DelegationNotFound);
1931 Self::ensure_all_schema_ids_are_valid(&schema_ids)?;
1932
1933 let current_block = frame_system::Pallet::<T>::block_number();
1934
1935 PermittedDelegationSchemas::<T>::try_get_mut_schemas(
1936 delegation,
1937 schema_ids,
1938 current_block,
1939 )?;
1940
1941 Ok(())
1942 })
1943 }
1944
1945 pub fn add_key(msa_id: MessageSourceId, key: &T::AccountId) -> DispatchResult {
1952 PublicKeyToMsaId::<T>::try_mutate(key, |maybe_msa_id| {
1953 ensure!(maybe_msa_id.is_none(), Error::<T>::KeyAlreadyRegistered);
1954 *maybe_msa_id = Some(msa_id);
1955
1956 <PublicKeyCountForMsaId<T>>::try_mutate(msa_id, |key_count| {
1958 let incremented_key_count =
1960 key_count.checked_add(1).ok_or(ArithmeticError::Overflow)?;
1961
1962 ensure!(
1963 incremented_key_count <= T::MaxPublicKeysPerMsa::get(),
1964 Error::<T>::KeyLimitExceeded
1965 );
1966
1967 *key_count = incremented_key_count;
1968 Ok(())
1969 })
1970 })
1971 }
1972
1973 pub fn ensure_all_schema_ids_are_valid(schema_ids: &[SchemaId]) -> DispatchResult {
1980 ensure!(
1981 schema_ids.len() <= T::MaxSchemaGrantsPerDelegation::get() as usize,
1982 Error::<T>::ExceedsMaxSchemaGrantsPerDelegation
1983 );
1984
1985 let are_schemas_valid = T::SchemaValidator::are_all_schema_ids_valid(schema_ids);
1986
1987 ensure!(are_schemas_valid, Error::<T>::InvalidSchemaId);
1988
1989 Ok(())
1990 }
1991
1992 pub fn is_registered_provider(msa_id: MessageSourceId) -> bool {
1994 ProviderToRegistryEntryV2::<T>::contains_key(ProviderId(msa_id))
1995 }
1996
1997 pub fn ensure_valid_registered_provider(
2007 delegator_key: &T::AccountId,
2008 provider_key: &T::AccountId,
2009 ) -> Result<(ProviderId, DelegatorId), DispatchError> {
2010 let provider_msa_id = Self::ensure_valid_msa_key(provider_key)?;
2011 let delegator_msa_id = Self::ensure_valid_msa_key(delegator_key)?;
2012
2013 ensure!(delegator_msa_id != provider_msa_id, Error::<T>::InvalidSelfProvider);
2015
2016 ensure!(Self::is_registered_provider(provider_msa_id), Error::<T>::ProviderNotRegistered);
2018
2019 Ok((provider_msa_id.into(), delegator_msa_id.into()))
2020 }
2021
2022 pub fn ensure_msa_owner(who: &T::AccountId, msa_id: MessageSourceId) -> DispatchResult {
2029 let provider_msa_id = Self::ensure_valid_msa_key(who)?;
2030 ensure!(provider_msa_id == msa_id, Error::<T>::NotMsaOwner);
2031
2032 Ok(())
2033 }
2034
2035 pub fn verify_signature<P>(
2042 signature: &MultiSignature,
2043 signer: &T::AccountId,
2044 payload: &P,
2045 ) -> bool
2046 where
2047 P: Encode + EIP712Encode,
2048 {
2049 let key = T::ConvertIntoAccountId32::convert((*signer).clone());
2050
2051 check_signature(signature, key, payload)
2052 }
2053
2054 pub fn add_provider(
2060 provider_id: ProviderId,
2061 delegator_id: DelegatorId,
2062 schema_ids: Vec<SchemaId>,
2063 ) -> DispatchResult {
2064 Self::try_mutate_delegation(delegator_id, provider_id, |delegation, is_new_delegation| {
2065 ensure!(is_new_delegation, Error::<T>::DuplicateProvider);
2066 Self::ensure_all_schema_ids_are_valid(&schema_ids)?;
2067
2068 PermittedDelegationSchemas::<T>::try_insert_schemas(delegation, schema_ids)?;
2069
2070 Ok(())
2071 })
2072 }
2073
2074 pub fn upsert_schema_permissions(
2079 provider_id: ProviderId,
2080 delegator_id: DelegatorId,
2081 schema_ids: Vec<SchemaId>,
2082 ) -> DispatchResult {
2083 Self::try_mutate_delegation(delegator_id, provider_id, |delegation, _is_new_delegation| {
2084 Self::ensure_all_schema_ids_are_valid(&schema_ids)?;
2085
2086 let mut revoke_ids: Vec<SchemaId> = Vec::new();
2088 let mut update_ids: Vec<SchemaId> = Vec::new();
2089 let mut insert_ids: Vec<SchemaId> = Vec::new();
2090
2091 let existing_keys = delegation.schema_permissions.keys();
2092
2093 for existing_schema_id in existing_keys {
2094 if !schema_ids.contains(existing_schema_id) {
2095 if let Some(block) = delegation.schema_permissions.get(existing_schema_id) {
2096 if *block == BlockNumberFor::<T>::zero() {
2097 revoke_ids.push(*existing_schema_id);
2098 }
2099 }
2100 }
2101 }
2102 for schema_id in &schema_ids {
2103 if !delegation.schema_permissions.contains_key(schema_id) {
2104 insert_ids.push(*schema_id);
2105 } else {
2106 update_ids.push(*schema_id);
2107 }
2108 }
2109
2110 let current_block = frame_system::Pallet::<T>::block_number();
2111
2112 PermittedDelegationSchemas::<T>::try_get_mut_schemas(
2114 delegation,
2115 revoke_ids,
2116 current_block,
2117 )?;
2118
2119 PermittedDelegationSchemas::<T>::try_get_mut_schemas(
2121 delegation,
2122 update_ids,
2123 BlockNumberFor::<T>::zero(),
2124 )?;
2125
2126 PermittedDelegationSchemas::<T>::try_insert_schemas(delegation, insert_ids)?;
2128 delegation.revoked_at = BlockNumberFor::<T>::zero();
2129 Ok(())
2130 })
2131 }
2132
2133 pub fn upsert_provider_for(
2140 provider_msa_id: MessageSourceId,
2141 payload: ProviderRegistryEntry<
2142 T::MaxProviderNameSize,
2143 T::MaxLanguageCodeSize,
2144 T::MaxLogoCidSize,
2145 T::MaxLocaleCount,
2146 >,
2147 is_update: bool,
2148 ) -> Result<u32, DispatchError> {
2149 let mut total_logos_removed = 0;
2150 ProviderToRegistryEntryV2::<T>::try_mutate(
2151 ProviderId(provider_msa_id),
2152 |maybe_metadata| -> DispatchResult {
2153 if !is_update {
2154 ensure!(
2155 maybe_metadata.take().is_none(),
2156 Error::<T>::DuplicateProviderRegistryEntry
2157 );
2158 } else {
2159 total_logos_removed = Self::remove_logo_storage(maybe_metadata.as_ref())?;
2160 }
2161 Self::update_logo_storage(&payload)?;
2162
2163 *maybe_metadata = Some(payload);
2164 Ok(())
2165 },
2166 )
2167 .map(|_| total_logos_removed)
2168 }
2169
2170 pub fn create_application_for(
2175 provider_msa_id: ProviderId,
2176 payload: ApplicationContext<
2177 T::MaxProviderNameSize,
2178 T::MaxLanguageCodeSize,
2179 T::MaxLogoCidSize,
2180 T::MaxLocaleCount,
2181 >,
2182 ) -> Result<ApplicationIndex, DispatchError> {
2183 Self::update_logo_storage(&payload)?;
2184 let next_application_index = NextApplicationIndex::<T>::get(provider_msa_id);
2185 ensure!(
2186 !ProviderToApplicationRegistry::<T>::contains_key(
2187 provider_msa_id,
2188 next_application_index
2189 ),
2190 Error::<T>::DuplicateApplicationRegistryEntry
2191 );
2192 ProviderToApplicationRegistry::<T>::insert(
2193 provider_msa_id,
2194 next_application_index,
2195 payload,
2196 );
2197 NextApplicationIndex::<T>::insert(provider_msa_id, next_application_index + 1);
2198 Ok(next_application_index)
2199 }
2200
2201 pub fn upsert_application_for(
2206 provider_msa_id: ProviderId,
2207 application_index: ApplicationIndex,
2208 payload: ApplicationContext<
2209 T::MaxProviderNameSize,
2210 T::MaxLanguageCodeSize,
2211 T::MaxLogoCidSize,
2212 T::MaxLocaleCount,
2213 >,
2214 ) -> Result<u32, DispatchError> {
2215 ensure!(
2216 ProviderToApplicationRegistry::<T>::contains_key(provider_msa_id, application_index),
2217 Error::<T>::ApplicationNotFound
2218 );
2219 let mut total_logos_removed = 0;
2220 ProviderToApplicationRegistry::<T>::try_mutate(
2221 provider_msa_id,
2222 application_index,
2223 |maybe_metadata| -> DispatchResult {
2224 total_logos_removed = Self::remove_logo_storage(maybe_metadata.as_ref())?;
2225 Self::update_logo_storage(&payload)?;
2226 *maybe_metadata = Some(payload);
2227 Ok(())
2228 },
2229 )
2230 .map(|_| total_logos_removed)
2231 }
2232
2233 pub fn try_mutate_delegation<R, E: From<DispatchError>>(
2237 delegator_id: DelegatorId,
2238 provider_id: ProviderId,
2239 f: impl FnOnce(
2240 &mut Delegation<SchemaId, BlockNumberFor<T>, T::MaxSchemaGrantsPerDelegation>,
2241 bool,
2242 ) -> Result<R, E>,
2243 ) -> Result<R, E> {
2244 DelegatorAndProviderToDelegation::<T>::try_mutate_exists(
2245 delegator_id,
2246 provider_id,
2247 |maybe_delegation_info| {
2248 let is_new = maybe_delegation_info.is_none();
2249 let mut delegation = maybe_delegation_info.take().unwrap_or_default();
2250
2251 let result = f(&mut delegation, is_new)?;
2252
2253 *maybe_delegation_info = Some(delegation);
2255 Ok(result)
2256 },
2257 )
2258 }
2259
2260 pub fn delete_key_for_msa(msa_id: MessageSourceId, key: &T::AccountId) -> DispatchResult {
2266 PublicKeyToMsaId::<T>::try_mutate_exists(key, |maybe_msa_id| {
2267 ensure!(maybe_msa_id.is_some(), Error::<T>::NoKeyExists);
2268
2269 *maybe_msa_id = None;
2271
2272 <PublicKeyCountForMsaId<T>>::try_mutate_exists(msa_id, |key_count| {
2273 match key_count {
2274 Some(1) => *key_count = None,
2275 Some(count) => *count = *count - 1u8,
2276 None => (),
2277 }
2278
2279 Ok(())
2280 })
2281 })
2282 }
2283
2284 pub fn revoke_provider(provider_id: ProviderId, delegator_id: DelegatorId) -> DispatchResult {
2291 DelegatorAndProviderToDelegation::<T>::try_mutate_exists(
2292 delegator_id,
2293 provider_id,
2294 |maybe_info| -> DispatchResult {
2295 let mut info = maybe_info.take().ok_or(Error::<T>::DelegationNotFound)?;
2296
2297 ensure!(
2298 info.revoked_at == BlockNumberFor::<T>::default(),
2299 Error::<T>::DelegationRevoked
2300 );
2301
2302 let current_block = frame_system::Pallet::<T>::block_number();
2303 info.revoked_at = current_block;
2304 *maybe_info = Some(info);
2305 Ok(())
2306 },
2307 )?;
2308
2309 Ok(())
2310 }
2311
2312 pub fn get_owner_of(key: &T::AccountId) -> Option<MessageSourceId> {
2314 PublicKeyToMsaId::<T>::get(key)
2315 }
2316
2317 pub fn ensure_valid_msa_key(key: &T::AccountId) -> Result<MessageSourceId, DispatchError> {
2319 let msa_id = PublicKeyToMsaId::<T>::get(key).ok_or(Error::<T>::NoKeyExists)?;
2320 Ok(msa_id)
2321 }
2322
2323 pub fn get_granted_schemas_by_msa_id(
2330 delegator: DelegatorId,
2331 provider: Option<ProviderId>,
2332 ) -> Result<Vec<DelegationResponse<SchemaId, BlockNumberFor<T>>>, DispatchError> {
2333 let delegations = match provider {
2334 Some(provider_id) => vec![(
2335 provider_id,
2336 Self::get_delegation_of(delegator, provider_id)
2337 .ok_or(Error::<T>::DelegationNotFound)?,
2338 )],
2339 None => DelegatorAndProviderToDelegation::<T>::iter_prefix(delegator).collect(),
2340 };
2341
2342 let mut result = vec![];
2343 for (provider_id, provider_info) in delegations {
2344 let schema_permissions = provider_info.schema_permissions;
2345 if provider.is_some() && schema_permissions.is_empty() {
2347 return Err(Error::<T>::SchemaNotGranted.into());
2348 }
2349
2350 let mut schema_list = Vec::new();
2351 for (schema_id, revoked_at) in schema_permissions {
2352 if provider_info.revoked_at > BlockNumberFor::<T>::zero() &&
2353 (revoked_at > provider_info.revoked_at ||
2354 revoked_at == BlockNumberFor::<T>::zero())
2355 {
2356 schema_list
2357 .push(SchemaGrant { schema_id, revoked_at: provider_info.revoked_at });
2358 } else {
2359 schema_list.push(SchemaGrant { schema_id, revoked_at });
2360 }
2361 }
2362
2363 result.push(DelegationResponse { provider_id, permissions: schema_list });
2364 }
2365
2366 Ok(result)
2367 }
2368
2369 pub fn msa_id_to_eth_address(id: MessageSourceId) -> H160 {
2375 const DOMAIN_PREFIX: u8 = 0xD9;
2379
2380 lazy_static! {
2381 static ref MSA_ADDRESS_SALT: [u8; 32] = keccak_256(b"MSA Generated");
2383 }
2384 let input_value = id.to_be_bytes();
2385
2386 let mut hash_input = [0u8; 41];
2387 hash_input[0] = DOMAIN_PREFIX;
2388 hash_input[1..9].copy_from_slice(&input_value);
2389 hash_input[9..].copy_from_slice(&(*MSA_ADDRESS_SALT));
2390
2391 let hash = keccak_256(&hash_input);
2392 H160::from_slice(&hash[12..])
2393 }
2394
2395 pub fn validate_eth_address_for_msa(address: &H160, msa_id: MessageSourceId) -> bool {
2397 let generated_address = Self::msa_id_to_eth_address(msa_id);
2398 *address == generated_address
2399 }
2400
2401 pub fn eth_address_to_checksummed_string(address: &H160) -> alloc::string::String {
2405 let addr_bytes = address.0;
2406 let addr_hex = hex::encode(addr_bytes);
2407 let hash = keccak_256(addr_hex.as_bytes());
2408
2409 let mut result = alloc::string::String::with_capacity(42);
2410 result.push_str("0x");
2411
2412 for (i, c) in addr_hex.chars().enumerate() {
2413 let hash_byte = hash[i / 2];
2414 let bit = if i % 2 == 0 { (hash_byte >> 4) & 0xf } else { hash_byte & 0xf };
2415
2416 result.push(if c.is_ascii_hexdigit() && c.is_ascii_alphabetic() {
2417 if bit >= 8 {
2418 c.to_ascii_uppercase()
2419 } else {
2420 c
2421 }
2422 } else {
2423 c
2424 });
2425 }
2426
2427 result
2428 }
2429
2430 pub fn register_signature(
2442 signature: &MultiSignature,
2443 signature_expires_at: BlockNumberFor<T>,
2444 ) -> DispatchResult {
2445 let current_block =
2446 Self::check_signature_against_registry(signature, signature_expires_at)?;
2447
2448 Self::enqueue_signature(signature, signature_expires_at, current_block)
2449 }
2450
2451 pub fn check_signature_against_registry(
2460 signature: &MultiSignature,
2461 signature_expires_at: BlockNumberFor<T>,
2462 ) -> Result<BlockNumberFor<T>, DispatchError> {
2463 let current_block: BlockNumberFor<T> = frame_system::Pallet::<T>::block_number();
2464
2465 let max_lifetime = Self::mortality_block_limit(current_block);
2466 ensure!(max_lifetime > signature_expires_at, Error::<T>::ProofNotYetValid);
2467 ensure!(current_block < signature_expires_at, Error::<T>::ProofHasExpired);
2468
2469 ensure!(
2471 !<PayloadSignatureRegistryList<T>>::contains_key(signature),
2472 Error::<T>::SignatureAlreadySubmitted
2473 );
2474 if let Some(signature_pointer) = PayloadSignatureRegistryPointer::<T>::get() {
2475 ensure!(signature_pointer.newest != *signature, Error::<T>::SignatureAlreadySubmitted);
2476 }
2477
2478 Ok(current_block)
2479 }
2480
2481 fn enqueue_signature(
2504 signature: &MultiSignature,
2505 signature_expires_at: BlockNumberFor<T>,
2506 current_block: BlockNumberFor<T>,
2507 ) -> DispatchResult {
2508 let pointer =
2510 PayloadSignatureRegistryPointer::<T>::get().unwrap_or(SignatureRegistryPointer {
2511 newest: signature.clone(),
2512 newest_expires_at: signature_expires_at,
2513 oldest: signature.clone(),
2514 count: 0,
2515 });
2516
2517 ensure!(
2519 !(pointer.count != 0 && pointer.newest.eq(signature)),
2520 Error::<T>::SignatureAlreadySubmitted
2521 );
2522
2523 let mut oldest: MultiSignature = pointer.oldest.clone();
2525
2526 let is_registry_full: bool = pointer.count == T::MaxSignaturesStored::get().unwrap_or(0);
2528
2529 if is_registry_full {
2531 let (expire_block_number, next_oldest) =
2532 PayloadSignatureRegistryList::<T>::get(pointer.oldest.clone())
2533 .ok_or(Error::<T>::SignatureRegistryCorrupted)?;
2534
2535 ensure!(
2536 current_block.gt(&expire_block_number),
2537 Error::<T>::SignatureRegistryLimitExceeded
2538 );
2539
2540 oldest = next_oldest.clone();
2542
2543 <PayloadSignatureRegistryList<T>>::remove(pointer.oldest);
2544 }
2545
2546 if pointer.count != 0 {
2548 <PayloadSignatureRegistryList<T>>::insert(
2549 pointer.newest,
2550 (pointer.newest_expires_at, signature.clone()),
2551 );
2552 }
2553
2554 PayloadSignatureRegistryPointer::<T>::put(SignatureRegistryPointer {
2556 count: if is_registry_full { pointer.count } else { pointer.count + 1 },
2558 newest: signature.clone(),
2559 newest_expires_at: signature_expires_at,
2560 oldest,
2561 });
2562
2563 Ok(())
2564 }
2565
2566 fn mortality_block_limit(current_block: BlockNumberFor<T>) -> BlockNumberFor<T> {
2570 let mortality_size = T::MortalityWindowSize::get();
2571 current_block + BlockNumberFor::<T>::from(mortality_size)
2572 }
2573
2574 fn validate_cid(in_cid: &[u8]) -> Result<Vec<u8>, DispatchError> {
2581 let cid_str: &str = core::str::from_utf8(in_cid).map_err(|_| Error::<T>::InvalidCid)?;
2583 ensure!(cid_str.len() > 2, Error::<T>::InvalidCid);
2584 ensure!(!cid_str.starts_with("Qm"), Error::<T>::UnsupportedCidVersion);
2586
2587 let cid_b = multibase::decode(cid_str).map_err(|_| Error::<T>::InvalidCid)?.1;
2589 ensure!(Cid::read_bytes(&cid_b[..]).is_ok(), Error::<T>::InvalidCid);
2590
2591 Ok(cid_b)
2592 }
2593
2594 fn update_logo_storage(
2596 payload: &ProviderRegistryEntry<
2597 T::MaxProviderNameSize,
2598 T::MaxLanguageCodeSize,
2599 T::MaxLogoCidSize,
2600 T::MaxLocaleCount,
2601 >,
2602 ) -> DispatchResult {
2603 if !payload.default_logo_250_100_png_cid.is_empty() {
2605 ApprovedLogos::<T>::insert(
2606 payload.default_logo_250_100_png_cid.clone(),
2607 BoundedVec::new(),
2608 );
2609 }
2610
2611 for (_, localized_cid) in &payload.localized_logo_250_100_png_cids {
2613 if !localized_cid.is_empty() {
2614 ApprovedLogos::<T>::insert(localized_cid, BoundedVec::new());
2615 }
2616 }
2617
2618 Ok(())
2619 }
2620
2621 fn remove_logo_storage(
2623 existing_payload: Option<
2624 &ProviderRegistryEntry<
2625 T::MaxProviderNameSize,
2626 T::MaxLanguageCodeSize,
2627 T::MaxLogoCidSize,
2628 T::MaxLocaleCount,
2629 >,
2630 >,
2631 ) -> Result<u32, DispatchError> {
2632 let mut total_logo_removed = 0;
2633 if let Some(payload) = existing_payload {
2634 if !payload.default_logo_250_100_png_cid.is_empty() {
2636 total_logo_removed += 1;
2637 ApprovedLogos::<T>::remove(&payload.default_logo_250_100_png_cid);
2638 }
2639 for (_, localized_cid) in &payload.localized_logo_250_100_png_cids {
2641 if !localized_cid.is_empty() {
2642 total_logo_removed += 1;
2643 ApprovedLogos::<T>::remove(localized_cid);
2644 }
2645 }
2646 }
2647 Ok(total_logo_removed)
2648 }
2649
2650 fn ensure_correct_cids(
2652 payload: &ProviderRegistryEntry<
2653 T::MaxProviderNameSize,
2654 T::MaxLanguageCodeSize,
2655 T::MaxLogoCidSize,
2656 T::MaxLocaleCount,
2657 >,
2658 ) -> DispatchResult {
2659 if !payload.default_logo_250_100_png_cid.is_empty() {
2661 Self::validate_cid(&payload.default_logo_250_100_png_cid)?;
2662 }
2663
2664 for (lang_code, localized_cid) in &payload.localized_logo_250_100_png_cids {
2666 let code_str = core::str::from_utf8(lang_code)
2668 .map_err(|_| Error::<T>::InvalidBCP47LanguageCode)?;
2669
2670 if !Self::is_valid_bcp47(code_str) {
2671 return Err(Error::<T>::InvalidBCP47LanguageCode.into());
2672 }
2673
2674 if !localized_cid.is_empty() {
2676 Self::validate_cid(localized_cid)?;
2677 }
2678 }
2679
2680 for (lang_code, _) in &payload.localized_names {
2682 let code_str = core::str::from_utf8(lang_code)
2684 .map_err(|_| Error::<T>::InvalidBCP47LanguageCode)?;
2685 if !Self::is_valid_bcp47(code_str) {
2686 return Err(Error::<T>::InvalidBCP47LanguageCode.into());
2687 }
2688 }
2689
2690 Ok(())
2691 }
2692
2693 fn is_valid_bcp47(code: &str) -> bool {
2695 if code.is_empty() {
2697 return false;
2698 }
2699 if code.starts_with('-') || code.ends_with('-') || code.contains("--") {
2701 return false;
2702 }
2703 for part in code.split('-') {
2704 let len = part.len();
2705 if len < 2 || !part.chars().all(|c| c.is_ascii_alphanumeric()) {
2706 return false;
2707 }
2708 }
2709 true
2710 }
2711
2712 pub fn get_provider_application_context(
2714 provider_id: ProviderId,
2715 application_id: Option<ApplicationIndex>,
2716 locale: Option<Vec<u8>>,
2717 ) -> Option<ProviderApplicationContext> {
2718 let bounded_locale = locale.and_then(|loc| BoundedVec::try_from(loc).ok());
2719 let provider_or_application_registry = match application_id {
2720 Some(app_id) => ProviderToApplicationRegistry::<T>::get(provider_id, app_id)?,
2721 None => ProviderToRegistryEntryV2::<T>::get(provider_id)?,
2722 };
2723 let default_name = provider_or_application_registry.default_name.to_vec();
2724 let default_logo_cid = provider_or_application_registry.default_logo_250_100_png_cid;
2725 let default_logo_250_100_png_bytes: Option<Vec<u8>> =
2727 ApprovedLogos::<T>::get(default_logo_cid).map(|bv| bv.to_vec());
2728 let mut localized_name: Option<Vec<u8>> = None;
2729 let localized_logo_250_100_png_bytes: Option<Vec<u8>> = bounded_locale.and_then(|locale| {
2731 localized_name = provider_or_application_registry
2733 .localized_names
2734 .get(&locale)
2735 .map(|bv| bv.to_vec());
2736
2737 provider_or_application_registry
2738 .localized_logo_250_100_png_cids
2739 .get(&locale)
2740 .and_then(|cid| ApprovedLogos::<T>::get(cid).map(|bv| bv.to_vec()))
2741 });
2742
2743 Some(ProviderApplicationContext {
2744 default_name,
2745 provider_id,
2746 application_id,
2747 default_logo_250_100_png_bytes,
2748 localized_logo_250_100_png_bytes,
2749 localized_name,
2750 })
2751 }
2752
2753 fn refund_logo_removal_weight_by_count(
2755 total_logos_removed: u32,
2756 base_weight: Weight,
2757 ) -> DispatchResultWithPostInfo {
2758 let max_logos_benchmark_assumed = T::MaxLocaleCount::get();
2760 let removal_over_charged_by =
2761 max_logos_benchmark_assumed.saturating_sub(total_logos_removed);
2762 let weight_per_logo_removal = T::DbWeight::get().writes(1);
2763 let weight_to_refund =
2764 weight_per_logo_removal.saturating_mul(removal_over_charged_by as u64);
2765 let actual_weight_used = base_weight.saturating_sub(weight_to_refund);
2766 Ok(PostDispatchInfo { actual_weight: Some(actual_weight_used), pays_fee: Pays::Yes })
2767 }
2768}
2769
2770#[cfg(feature = "runtime-benchmarks")]
2771impl<T: Config> MsaBenchmarkHelper<T::AccountId> for Pallet<T> {
2772 fn set_delegation_relationship(
2774 provider: ProviderId,
2775 delegator: DelegatorId,
2776 schemas: Vec<SchemaId>,
2777 ) -> DispatchResult {
2778 Self::add_provider(provider, delegator, schemas)?;
2779 Ok(())
2780 }
2781
2782 fn add_key(msa_id: MessageSourceId, key: T::AccountId) -> DispatchResult {
2784 Self::add_key(msa_id, &key)?;
2785 Ok(())
2786 }
2787}
2788
2789#[cfg(feature = "runtime-benchmarks")]
2790impl<T: Config> RegisterProviderBenchmarkHelper for Pallet<T> {
2791 fn create(provider_id: MessageSourceId, name: Vec<u8>) -> DispatchResult {
2793 let name = BoundedVec::<u8, T::MaxProviderNameSize>::try_from(name).expect("error");
2794 let payload = ProviderRegistryEntry {
2795 default_name: name,
2796 localized_names: BoundedBTreeMap::new(),
2797 default_logo_250_100_png_cid: BoundedVec::new(),
2798 localized_logo_250_100_png_cids: BoundedBTreeMap::new(),
2799 };
2800 Self::create_registered_provider(provider_id.into(), payload)?;
2801
2802 Ok(())
2803 }
2804}
2805
2806impl<T: Config> MsaLookup for Pallet<T> {
2807 type AccountId = T::AccountId;
2808
2809 fn get_msa_id(key: &Self::AccountId) -> Option<MessageSourceId> {
2810 Self::get_owner_of(key)
2811 }
2812}
2813
2814impl<T: Config> MsaValidator for Pallet<T> {
2815 type AccountId = T::AccountId;
2816
2817 fn ensure_valid_msa_key(key: &T::AccountId) -> Result<MessageSourceId, DispatchError> {
2818 Self::ensure_valid_msa_key(key)
2819 }
2820}
2821
2822impl<T: Config> ProviderLookup for Pallet<T> {
2823 type BlockNumber = BlockNumberFor<T>;
2824 type MaxSchemaGrantsPerDelegation = T::MaxSchemaGrantsPerDelegation;
2825 type SchemaId = SchemaId;
2826
2827 fn get_delegation_of(
2828 delegator: DelegatorId,
2829 provider: ProviderId,
2830 ) -> Option<Delegation<SchemaId, Self::BlockNumber, Self::MaxSchemaGrantsPerDelegation>> {
2831 DelegatorAndProviderToDelegation::<T>::get(delegator, provider)
2832 }
2833}
2834
2835impl<T: Config> DelegationValidator for Pallet<T> {
2836 type BlockNumber = BlockNumberFor<T>;
2837 type MaxSchemaGrantsPerDelegation = T::MaxSchemaGrantsPerDelegation;
2838 type SchemaId = SchemaId;
2839
2840 fn ensure_valid_delegation(
2850 provider_id: ProviderId,
2851 delegator_id: DelegatorId,
2852 block_number: Option<BlockNumberFor<T>>,
2853 ) -> Result<
2854 Delegation<SchemaId, BlockNumberFor<T>, T::MaxSchemaGrantsPerDelegation>,
2855 DispatchError,
2856 > {
2857 let info = DelegatorAndProviderToDelegation::<T>::get(delegator_id, provider_id)
2858 .ok_or(Error::<T>::DelegationNotFound)?;
2859 let current_block = frame_system::Pallet::<T>::block_number();
2860 let requested_block = match block_number {
2861 Some(block_number) => {
2862 ensure!(
2863 current_block >= block_number,
2864 Error::<T>::CannotPredictValidityPastCurrentBlock
2865 );
2866 block_number
2867 },
2868 None => current_block,
2869 };
2870
2871 if info.revoked_at == BlockNumberFor::<T>::zero() {
2872 return Ok(info);
2873 }
2874 ensure!(info.revoked_at >= requested_block, Error::<T>::DelegationRevoked);
2875
2876 Ok(info)
2877 }
2878}
2879
2880impl<T: Config> TargetValidator for Pallet<T> {
2881 fn validate(target: MessageSourceId) -> bool {
2882 Self::is_registered_provider(target)
2883 }
2884}
2885
2886impl<T: Config> SchemaGrantValidator<BlockNumberFor<T>> for Pallet<T> {
2887 fn ensure_valid_schema_grant(
2896 provider: ProviderId,
2897 delegator: DelegatorId,
2898 schema_id: SchemaId,
2899 block_number: BlockNumberFor<T>,
2900 ) -> DispatchResult {
2901 let provider_info = Self::ensure_valid_delegation(provider, delegator, Some(block_number))?;
2902
2903 let schema_permission_revoked_at_block_number = provider_info
2904 .schema_permissions
2905 .get(&schema_id)
2906 .ok_or(Error::<T>::SchemaNotGranted)?;
2907
2908 if *schema_permission_revoked_at_block_number == BlockNumberFor::<T>::zero() {
2909 return Ok(());
2910 }
2911
2912 ensure!(
2913 block_number <= *schema_permission_revoked_at_block_number,
2914 Error::<T>::SchemaNotGranted
2915 );
2916
2917 Ok(())
2918 }
2919}
2920
2921impl<T: Config> MsaKeyProvider for Pallet<T> {
2922 type AccountId = T::AccountId;
2923 fn key_eligible_for_subsidized_addition(
2929 old_key: Self::AccountId,
2930 new_key: Self::AccountId,
2931 msa_id: MessageSourceId,
2932 ) -> bool {
2933 let new_address32 = T::ConvertIntoAccountId32::convert((new_key).clone());
2934 if EthereumAddressMapper::is_ethereum_address(&new_address32) {
2935 if let Some(stored_msa_id) = Self::get_msa_id(&old_key) {
2936 return stored_msa_id == msa_id && PublicKeyCountForMsaId::<T>::get(msa_id).eq(&1u8);
2937 }
2938 }
2939 false
2940 }
2941}
2942
2943#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)]
2948#[scale_info(skip_type_params(T))]
2949pub struct CheckFreeExtrinsicUse<T: Config + Send + Sync>(PhantomData<T>);
2950
2951impl<T: Config + Send + Sync> CheckFreeExtrinsicUse<T> {
2952 pub fn validate_delegation_by_delegator(
2963 account_id: &T::AccountId,
2964 provider_msa_id: &MessageSourceId,
2965 ) -> TransactionValidity {
2966 const TAG_PREFIX: &str = "DelegatorDelegationRevocation";
2967 let delegator_msa_id: DelegatorId = Pallet::<T>::ensure_valid_msa_key(account_id)
2968 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?
2969 .into();
2970 let provider_msa_id = ProviderId(*provider_msa_id);
2971
2972 Pallet::<T>::ensure_valid_delegation(provider_msa_id, delegator_msa_id, None)
2973 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidDelegation as u8))?;
2974 ValidTransaction::with_tag_prefix(TAG_PREFIX).and_provides(account_id).build()
2975 }
2976
2977 pub fn validate_delegation_by_provider(
2988 account_id: &T::AccountId,
2989 delegator_msa_id: &MessageSourceId,
2990 ) -> TransactionValidity {
2991 const TAG_PREFIX: &str = "ProviderDelegationRevocation";
2992
2993 let provider_msa_id: ProviderId = Pallet::<T>::ensure_valid_msa_key(account_id)
2994 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?
2995 .into();
2996 let delegator_msa_id = DelegatorId(*delegator_msa_id);
2997
2998 Pallet::<T>::ensure_valid_delegation(provider_msa_id, delegator_msa_id, None)
3000 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidDelegation as u8))?;
3001 ValidTransaction::with_tag_prefix(TAG_PREFIX).and_provides(account_id).build()
3002 }
3003
3004 pub fn validate_key_delete(
3017 signing_public_key: &T::AccountId,
3018 public_key_to_delete: &T::AccountId,
3019 ) -> TransactionValidity {
3020 const TAG_PREFIX: &str = "KeyRevocation";
3021
3022 ensure!(
3023 signing_public_key != public_key_to_delete,
3024 InvalidTransaction::Custom(ValidityError::InvalidSelfRemoval as u8)
3025 );
3026
3027 let maybe_owner_msa_id: MessageSourceId =
3028 Pallet::<T>::ensure_valid_msa_key(signing_public_key)
3029 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?;
3030
3031 let msa_id_for_key_to_delete: MessageSourceId =
3032 Pallet::<T>::ensure_valid_msa_key(public_key_to_delete)
3033 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?;
3034
3035 ensure!(
3036 maybe_owner_msa_id == msa_id_for_key_to_delete,
3037 InvalidTransaction::Custom(ValidityError::NotKeyOwner as u8)
3038 );
3039
3040 ValidTransaction::with_tag_prefix(TAG_PREFIX)
3041 .and_provides(signing_public_key)
3042 .build()
3043 }
3044
3045 pub fn ensure_msa_can_retire(account_id: &T::AccountId) -> TransactionValidity {
3061 const TAG_PREFIX: &str = "MSARetirement";
3062 let msa_id = Pallet::<T>::ensure_valid_msa_key(account_id)
3063 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?;
3064
3065 ensure!(
3066 !Pallet::<T>::is_registered_provider(msa_id),
3067 InvalidTransaction::Custom(
3068 ValidityError::InvalidRegisteredProviderCannotBeRetired as u8
3069 )
3070 );
3071
3072 let msa_handle = T::HandleProvider::get_handle_for_msa(msa_id);
3073 ensure!(
3074 msa_handle.is_none(),
3075 InvalidTransaction::Custom(ValidityError::HandleNotRetired as u8)
3076 );
3077
3078 let key_count = PublicKeyCountForMsaId::<T>::get(msa_id);
3079 ensure!(
3080 key_count == 1,
3081 InvalidTransaction::Custom(ValidityError::InvalidMoreThanOneKeyExists as u8)
3082 );
3083
3084 let delegator_id = DelegatorId(msa_id);
3085 let has_active_delegations: bool = DelegatorAndProviderToDelegation::<T>::iter_key_prefix(
3086 delegator_id,
3087 )
3088 .any(|provider_id| {
3089 Pallet::<T>::ensure_valid_delegation(provider_id, delegator_id, None).is_ok()
3090 });
3091
3092 ensure!(
3093 !has_active_delegations,
3094 InvalidTransaction::Custom(ValidityError::InvalidNonZeroProviderDelegations as u8)
3095 );
3096
3097 let msa_address = Pallet::<T>::msa_id_to_eth_address(msa_id);
3099
3100 let mut bytes = &EthereumAddressMapper::to_bytes32(&msa_address.0)[..];
3102 let msa_account_id = T::AccountId::decode(&mut bytes).map_err(|_| {
3103 log::error!("Failed to decode MSA account ID from Ethereum address");
3104 InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8)
3105 })?;
3106
3107 let msa_balance = T::Currency::reducible_balance(
3109 &msa_account_id,
3110 Preservation::Expendable,
3111 Fortitude::Polite,
3112 );
3113 ensure!(
3114 msa_balance == Zero::zero(),
3115 InvalidTransaction::Custom(ValidityError::InvalidMsaHoldingTokenCannotBeRetired as u8)
3116 );
3117
3118 ValidTransaction::with_tag_prefix(TAG_PREFIX).and_provides(account_id).build()
3119 }
3120
3121 pub fn validate_msa_token_withdrawal(
3136 receiver_account_id: &T::AccountId,
3137 msa_owner_public_key: &T::AccountId,
3138 msa_owner_proof: &MultiSignature,
3139 authorization_payload: &AuthorizedKeyData<T>,
3140 ) -> TransactionValidity {
3141 const TAG_PREFIX: &str = "MsaTokenWithdrawal";
3142
3143 ensure!(
3144 *receiver_account_id == authorization_payload.authorized_public_key,
3145 InvalidTransaction::Custom(ValidityError::NotKeyOwner as u8)
3146 );
3147
3148 ensure!(
3149 authorization_payload.discriminant == PayloadTypeDiscriminator::AuthorizedKeyData,
3150 InvalidTransaction::Custom(ValidityError::MsaOwnershipInvalidSignature as u8)
3151 );
3152 ensure!(
3153 Pallet::<T>::verify_signature(
3154 msa_owner_proof,
3155 msa_owner_public_key,
3156 authorization_payload
3157 ),
3158 InvalidTransaction::Custom(ValidityError::MsaOwnershipInvalidSignature as u8)
3159 );
3160
3161 ensure!(
3163 !PublicKeyToMsaId::<T>::contains_key(receiver_account_id),
3164 InvalidTransaction::Custom(ValidityError::IneligibleOrigin as u8)
3165 );
3166
3167 Pallet::<T>::check_signature_against_registry(
3168 msa_owner_proof,
3169 authorization_payload.expiration,
3170 )
3171 .map_err(|_| {
3172 InvalidTransaction::Custom(ValidityError::MsaOwnershipInvalidSignature as u8)
3173 })?;
3174
3175 let msa_id = authorization_payload.msa_id;
3176
3177 Pallet::<T>::ensure_msa_owner(msa_owner_public_key, msa_id)
3178 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?;
3179
3180 let msa_address = Pallet::<T>::msa_id_to_eth_address(msa_id);
3182
3183 let mut bytes = &EthereumAddressMapper::to_bytes32(&msa_address.0)[..];
3185 let msa_account_id = T::AccountId::decode(&mut bytes).map_err(|_| {
3186 log::error!("Failed to decode MSA account ID from Ethereum address");
3187 InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8)
3188 })?;
3189
3190 let msa_balance = T::Currency::reducible_balance(
3192 &msa_account_id,
3193 Preservation::Expendable,
3194 Fortitude::Polite,
3195 );
3196 ensure!(
3197 msa_balance > Zero::zero(),
3198 InvalidTransaction::Custom(ValidityError::InsufficientBalanceToWithdraw as u8)
3199 );
3200 ValidTransaction::with_tag_prefix(TAG_PREFIX)
3201 .and_provides(receiver_account_id)
3202 .build()
3203 }
3204}
3205
3206pub enum ValidityError {
3208 InvalidDelegation,
3210 InvalidMsaKey,
3212 InvalidRegisteredProviderCannotBeRetired,
3214 InvalidMoreThanOneKeyExists,
3216 InvalidSelfRemoval,
3218 NotKeyOwner,
3220 InvalidNonZeroProviderDelegations,
3222 HandleNotRetired,
3224 MsaOwnershipInvalidSignature,
3226 InsufficientBalanceToWithdraw,
3228 IneligibleOrigin,
3230 InvalidMsaHoldingTokenCannotBeRetired,
3232}
3233
3234impl<T: Config + Send + Sync> CheckFreeExtrinsicUse<T> {
3235 pub fn new() -> Self {
3237 Self(PhantomData)
3238 }
3239}
3240
3241impl<T: Config + Send + Sync> core::fmt::Debug for CheckFreeExtrinsicUse<T> {
3242 #[cfg(feature = "std")]
3243 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
3244 write!(f, "CheckFreeExtrinsicUse<{:?}>", self.0)
3245 }
3246 #[cfg(not(feature = "std"))]
3247 fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
3248 Ok(())
3249 }
3250}
3251
3252#[derive(RuntimeDebugNoBound)]
3254pub enum Val {
3255 Valid,
3257 Refund(Weight),
3259}
3260
3261#[derive(RuntimeDebugNoBound)]
3263pub enum Pre {
3264 Valid,
3266 Refund(Weight),
3268}
3269
3270impl<T: Config + Send + Sync> TransactionExtension<T::RuntimeCall> for CheckFreeExtrinsicUse<T>
3271where
3272 T::RuntimeCall: Dispatchable<Info = DispatchInfo> + IsSubType<Call<T>>,
3273 <T as frame_system::Config>::RuntimeOrigin: AsSystemOriginSigner<T::AccountId> + Clone,
3274{
3275 const IDENTIFIER: &'static str = "CheckFreeExtrinsicUse";
3276 type Implicit = ();
3277 type Val = Val;
3278 type Pre = Pre;
3279
3280 fn weight(&self, call: &T::RuntimeCall) -> Weight {
3281 match call.is_sub_type() {
3282 Some(Call::revoke_delegation_by_provider { .. }) =>
3283 T::WeightInfo::check_free_extrinsic_use_revoke_delegation_by_provider(),
3284 Some(Call::revoke_delegation_by_delegator { .. }) =>
3285 T::WeightInfo::check_free_extrinsic_use_revoke_delegation_by_delegator(),
3286 Some(Call::delete_msa_public_key { .. }) =>
3287 T::WeightInfo::check_free_extrinsic_use_delete_msa_public_key(),
3288 Some(Call::retire_msa { .. }) => T::WeightInfo::check_free_extrinsic_use_retire_msa(),
3289 Some(Call::withdraw_tokens { .. }) =>
3290 T::WeightInfo::check_free_extrinsic_use_withdraw_tokens(),
3291 _ => Weight::zero(),
3292 }
3293 }
3294
3295 fn validate(
3296 &self,
3297 origin: <T as frame_system::Config>::RuntimeOrigin,
3298 call: &T::RuntimeCall,
3299 _info: &DispatchInfoOf<T::RuntimeCall>,
3300 _len: usize,
3301 _self_implicit: Self::Implicit,
3302 _inherited_implication: &impl Encode,
3303 _source: TransactionSource,
3304 ) -> ValidateResult<Self::Val, T::RuntimeCall> {
3305 let weight = self.weight(call);
3306 let Some(who) = origin.as_system_origin_signer() else {
3307 return Ok((ValidTransaction::default(), Val::Refund(weight), origin));
3308 };
3309 let validity = match call.is_sub_type() {
3310 Some(Call::revoke_delegation_by_provider { delegator, .. }) =>
3311 Self::validate_delegation_by_provider(who, delegator),
3312 Some(Call::revoke_delegation_by_delegator { provider_msa_id, .. }) =>
3313 Self::validate_delegation_by_delegator(who, provider_msa_id),
3314 Some(Call::delete_msa_public_key { public_key_to_delete, .. }) =>
3315 Self::validate_key_delete(who, public_key_to_delete),
3316 Some(Call::retire_msa { .. }) => Self::ensure_msa_can_retire(who),
3317 Some(Call::withdraw_tokens {
3318 msa_owner_public_key,
3319 msa_owner_proof,
3320 authorization_payload,
3321 }) => Self::validate_msa_token_withdrawal(
3322 who,
3323 msa_owner_public_key,
3324 msa_owner_proof,
3325 authorization_payload,
3326 ),
3327 _ => Ok(Default::default()),
3328 };
3329 validity.map(|v| (v, Val::Valid, origin))
3330 }
3331
3332 fn prepare(
3333 self,
3334 val: Self::Val,
3335 _origin: &<T as frame_system::Config>::RuntimeOrigin,
3336 _call: &T::RuntimeCall,
3337 _info: &DispatchInfoOf<T::RuntimeCall>,
3338 _len: usize,
3339 ) -> Result<Self::Pre, TransactionValidityError> {
3340 match val {
3341 Val::Valid => Ok(Pre::Valid),
3342 Val::Refund(w) => Ok(Pre::Refund(w)),
3343 }
3344 }
3345
3346 fn post_dispatch_details(
3347 pre: Self::Pre,
3348 _info: &DispatchInfo,
3349 _post_info: &PostDispatchInfoOf<T::RuntimeCall>,
3350 _len: usize,
3351 _result: &sp_runtime::DispatchResult,
3352 ) -> Result<Weight, TransactionValidityError> {
3353 match pre {
3354 Pre::Valid => Ok(Weight::zero()),
3355 Pre::Refund(w) => Ok(w),
3356 }
3357 }
3358}