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 #[cfg(not(feature = "no-custom-host-functions"))]
612 fn offchain_worker(block_number: BlockNumberFor<T>) {
613 log::info!("Running offchain workers! {block_number:?}");
614 do_offchain_worker::<T>(block_number)
615 }
616 }
617
618 #[pallet::call]
619 impl<T: Config> Pallet<T> {
620 #[pallet::call_index(0)]
630 #[pallet::weight(T::WeightInfo::create())]
631 pub fn create(origin: OriginFor<T>) -> DispatchResult {
632 let public_key = ensure_signed(origin)?;
633
634 let (new_msa_id, new_public_key) = Self::create_account(public_key)?;
635
636 let event = Event::MsaCreated { msa_id: new_msa_id, key: new_public_key };
637 Self::add_event_to_offchain_index(Some(&event), new_msa_id);
638 Self::deposit_event(event);
639 Ok(())
640 }
641
642 #[pallet::call_index(1)]
664 #[pallet::weight(T::WeightInfo::create_sponsored_account_with_delegation(
665 add_provider_payload.intent_ids.len() as u32
666 ))]
667 pub fn create_sponsored_account_with_delegation(
668 origin: OriginFor<T>,
669 delegator_key: T::AccountId,
670 proof: MultiSignature,
671 add_provider_payload: AddProvider,
672 ) -> DispatchResult {
673 let provider_key = ensure_signed(origin)?;
674
675 ensure!(
676 Self::verify_signature(&proof, &delegator_key, &add_provider_payload),
677 Error::<T>::InvalidSignature
678 );
679
680 Self::register_signature(&proof, add_provider_payload.expiration.into())?;
681
682 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
683 ensure!(
684 add_provider_payload.authorized_msa_id == provider_msa_id,
685 Error::<T>::UnauthorizedProvider
686 );
687
688 ensure!(
690 Self::is_registered_provider(provider_msa_id),
691 Error::<T>::ProviderNotRegistered
692 );
693
694 let (new_delegator_msa_id, new_delegator_public_key) =
695 Self::create_account(delegator_key)?;
696 Self::add_provider(
697 ProviderId(provider_msa_id),
698 DelegatorId(new_delegator_msa_id),
699 add_provider_payload.intent_ids,
700 )?;
701 let event =
702 Event::MsaCreated { msa_id: new_delegator_msa_id, key: new_delegator_public_key };
703 Self::add_event_to_offchain_index(Some(&event), new_delegator_msa_id);
704 Self::deposit_event(event);
705 Self::deposit_event(Event::DelegationGranted {
706 delegator_id: DelegatorId(new_delegator_msa_id),
707 provider_id: ProviderId(provider_msa_id),
708 });
709 Ok(())
710 }
711
712 #[pallet::call_index(2)]
723 #[pallet::weight(T::WeightInfo::create_provider())]
724 pub fn create_provider(origin: OriginFor<T>, provider_name: Vec<u8>) -> DispatchResult {
725 let provider_key = ensure_signed(origin)?;
726 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
727 let bounded_name: BoundedVec<u8, T::MaxProviderNameSize> =
728 provider_name.try_into().map_err(|_| Error::<T>::ExceedsMaxProviderNameSize)?;
729
730 let entry = ProviderRegistryEntry { default_name: bounded_name, ..Default::default() };
731 Self::ensure_correct_cids(&entry)?;
732 Self::upsert_provider_for(provider_msa_id, entry, false)?;
733 Self::deposit_event(Event::ProviderCreated {
734 provider_id: ProviderId(provider_msa_id),
735 });
736 Ok(())
737 }
738
739 #[pallet::call_index(3)]
760 #[pallet::weight(T::WeightInfo::grant_delegation(add_provider_payload.intent_ids.len() as u32))]
761 pub fn grant_delegation(
762 origin: OriginFor<T>,
763 delegator_key: T::AccountId,
764 proof: MultiSignature,
765 add_provider_payload: AddProvider,
766 ) -> DispatchResult {
767 let provider_key = ensure_signed(origin)?;
768
769 ensure!(
770 Self::verify_signature(&proof, &delegator_key, &add_provider_payload),
771 Error::<T>::AddProviderSignatureVerificationFailed
772 );
773
774 Self::register_signature(&proof, add_provider_payload.expiration.into())?;
775 let (provider_id, delegator_id) =
776 Self::ensure_valid_registered_provider(&delegator_key, &provider_key)?;
777
778 ensure!(
779 add_provider_payload.authorized_msa_id == provider_id.0,
780 Error::<T>::UnauthorizedDelegator
781 );
782
783 Self::upsert_intent_permissions(
784 provider_id,
785 delegator_id,
786 add_provider_payload.intent_ids,
787 )?;
788 Self::deposit_event(Event::DelegationGranted { delegator_id, provider_id });
789
790 Ok(())
791 }
792
793 #[pallet::call_index(4)]
805 #[pallet::weight((T::WeightInfo::revoke_delegation_by_delegator(), DispatchClass::Normal, Pays::No))]
806 pub fn revoke_delegation_by_delegator(
807 origin: OriginFor<T>,
808 #[pallet::compact] provider_msa_id: MessageSourceId,
809 ) -> DispatchResult {
810 let who = ensure_signed(origin)?;
811
812 match PublicKeyToMsaId::<T>::get(&who) {
813 Some(delegator_msa_id) => {
814 let delegator_id = DelegatorId(delegator_msa_id);
815 let provider_id = ProviderId(provider_msa_id);
816 Self::revoke_provider(provider_id, delegator_id)?;
817 Self::deposit_event(Event::DelegationRevoked { delegator_id, provider_id });
818 },
819 None => {
820 log::error!(
821 "TransactionExtension did not catch invalid MSA for account {who:?}, "
822 );
823 },
824 }
825
826 Ok(())
827 }
828
829 #[pallet::call_index(5)]
855 #[pallet::weight(T::WeightInfo::add_public_key_to_msa())]
856 pub fn add_public_key_to_msa(
857 origin: OriginFor<T>,
858 msa_owner_public_key: T::AccountId,
859 msa_owner_proof: MultiSignature,
860 new_key_owner_proof: MultiSignature,
861 add_key_payload: AddKeyData<T>,
862 ) -> DispatchResult {
863 let _ = ensure_signed(origin)?;
864
865 ensure!(
866 Self::verify_signature(&msa_owner_proof, &msa_owner_public_key, &add_key_payload),
867 Error::<T>::MsaOwnershipInvalidSignature
868 );
869
870 ensure!(
871 Self::verify_signature(
872 &new_key_owner_proof,
873 &add_key_payload.new_public_key,
874 &add_key_payload
875 ),
876 Error::<T>::NewKeyOwnershipInvalidSignature
877 );
878
879 Self::register_signature(&msa_owner_proof, add_key_payload.expiration)?;
880 Self::register_signature(&new_key_owner_proof, add_key_payload.expiration)?;
881
882 let msa_id = add_key_payload.msa_id;
883
884 Self::ensure_msa_owner(&msa_owner_public_key, msa_id)?;
885
886 Self::add_key(msa_id, &add_key_payload.new_public_key.clone())?;
887
888 let event =
889 Event::PublicKeyAdded { msa_id, key: add_key_payload.new_public_key.clone() };
890 Self::add_event_to_offchain_index(Some(&event), msa_id);
891 Self::deposit_event(event);
892
893 Ok(())
894 }
895
896 #[pallet::call_index(6)]
911 #[pallet::weight((T::WeightInfo::delete_msa_public_key(), DispatchClass::Normal, Pays::No))]
912 pub fn delete_msa_public_key(
913 origin: OriginFor<T>,
914 public_key_to_delete: T::AccountId,
915 ) -> DispatchResult {
916 let who = ensure_signed(origin)?;
917
918 match PublicKeyToMsaId::<T>::get(&who) {
919 Some(who_msa_id) => {
920 Self::delete_key_for_msa(who_msa_id, &public_key_to_delete)?;
921
922 let event = Event::PublicKeyDeleted { key: public_key_to_delete };
924 Self::add_event_to_offchain_index(Some(&event), who_msa_id);
925 Self::deposit_event(event);
926 },
927 None => {
928 log::error!(
929 "TransactionExtension did not catch invalid MSA for account {who:?}"
930 );
931 },
932 }
933 Ok(())
934 }
935
936 #[pallet::call_index(7)]
948 #[pallet::weight((T::WeightInfo::revoke_delegation_by_provider(), DispatchClass::Normal, Pays::No))]
949 pub fn revoke_delegation_by_provider(
950 origin: OriginFor<T>,
951 #[pallet::compact] delegator: MessageSourceId,
952 ) -> DispatchResult {
953 let who = ensure_signed(origin)?;
954
955 match PublicKeyToMsaId::<T>::get(&who) {
959 Some(msa_id) => {
960 let provider_id = ProviderId(msa_id);
961 let delegator_id = DelegatorId(delegator);
962 Self::revoke_provider(provider_id, delegator_id)?;
963 Self::deposit_event(Event::DelegationRevoked { provider_id, delegator_id })
964 },
965 None => {
966 log::error!(
967 "TransactionExtension did not catch invalid MSA for account {who:?}"
968 );
969 },
970 }
971
972 Ok(())
973 }
974
975 #[pallet::call_index(10)]
997 #[pallet::weight((T::WeightInfo::retire_msa(), DispatchClass::Normal, Pays::No))]
998 pub fn retire_msa(origin: OriginFor<T>) -> DispatchResult {
999 let who = ensure_signed(origin)?;
1001
1002 match PublicKeyToMsaId::<T>::get(&who) {
1005 Some(msa_id) => {
1006 Self::delete_key_for_msa(msa_id, &who)?;
1007 let event = Event::PublicKeyDeleted { key: who };
1008 Self::add_event_to_offchain_index(Some(&event), msa_id);
1009 Self::deposit_event(event);
1010 Self::deposit_event(Event::MsaRetired { msa_id });
1011 },
1012 None => {
1013 log::error!(
1014 "TransactionExtension did not catch invalid MSA for account {who:?}"
1015 );
1016 },
1017 }
1018 Ok(())
1019 }
1020
1021 #[pallet::call_index(11)]
1027 #[pallet::weight(T::WeightInfo::propose_to_be_provider_v2())]
1028 #[allow(deprecated)]
1029 #[deprecated(
1030 note = "please use `propose_to_be_provider_v2`, which supports additional provider metadata"
1031 )]
1032 pub fn propose_to_be_provider(
1033 origin: OriginFor<T>,
1034 provider_name: Vec<u8>,
1035 ) -> DispatchResult {
1036 let bounded_name: BoundedVec<u8, T::MaxProviderNameSize> =
1037 provider_name.try_into().map_err(|_| Error::<T>::ExceedsMaxProviderNameSize)?;
1038
1039 let entry = ProviderRegistryEntry { default_name: bounded_name, ..Default::default() };
1040
1041 Self::propose_to_be_provider_v2(origin, entry)
1042 }
1043
1044 #[pallet::call_index(12)]
1054 #[pallet::weight(T::WeightInfo::create_provider_via_governance_v2(0u32, 0u32))]
1055 #[allow(deprecated)]
1056 #[deprecated(
1057 note = "please use `create_provider_via_governance_v2`, which supports additional provider metadata"
1058 )]
1059 pub fn create_provider_via_governance(
1060 origin: OriginFor<T>,
1061 provider_key: T::AccountId,
1062 provider_name: Vec<u8>,
1063 ) -> DispatchResult {
1064 let bounded_name: BoundedVec<u8, T::MaxProviderNameSize> =
1065 provider_name.try_into().map_err(|_| Error::<T>::ExceedsMaxProviderNameSize)?;
1066
1067 let entry = ProviderRegistryEntry { default_name: bounded_name, ..Default::default() };
1068
1069 Self::create_provider_via_governance_v2(origin, provider_key, entry)
1070 }
1071
1072 #[pallet::call_index(13)]
1074 #[pallet::weight(T::WeightInfo::reindex_offchain())]
1075 pub fn reindex_offchain(
1076 origin: OriginFor<T>,
1077 event: OffchainReplayEvent<T>,
1078 ) -> DispatchResult {
1079 let _ = ensure_signed(origin)?;
1080 match event {
1081 OffchainReplayEvent::MsaPallet(MsaOffchainReplayEvent::KeyReIndex {
1082 msa_id,
1083 index_key,
1084 }) => {
1085 match index_key {
1087 Some(key) => {
1088 let event = Event::PublicKeyAdded { msa_id, key };
1089 Self::add_event_to_offchain_index(Some(&event), msa_id);
1090 },
1091 None => {
1092 Self::add_event_to_offchain_index(None, msa_id);
1093 },
1094 }
1095 },
1096 }
1097
1098 Ok(())
1099 }
1100
1101 #[pallet::call_index(14)]
1122 #[pallet::weight((T::WeightInfo::withdraw_tokens(), DispatchClass::Normal, Pays::No))]
1123 pub fn withdraw_tokens(
1124 origin: OriginFor<T>,
1125 _msa_owner_public_key: T::AccountId,
1126 msa_owner_proof: MultiSignature,
1127 authorization_payload: AuthorizedKeyData<T>,
1128 ) -> DispatchResult {
1129 let public_key = ensure_signed(origin)?;
1130
1131 Self::register_signature(&msa_owner_proof, authorization_payload.expiration)?;
1132
1133 let msa_id = authorization_payload.msa_id;
1134
1135 let msa_address = Self::msa_id_to_eth_address(msa_id);
1137
1138 let mut bytes = &EthereumAddressMapper::to_bytes32(&msa_address.0)[..];
1140 let msa_account_id = T::AccountId::decode(&mut bytes).map_err(|_| {
1141 log::error!("Failed to decode MSA account ID from Ethereum address");
1142 Error::<T>::NoKeyExists
1143 })?;
1144
1145 let msa_balance = T::Currency::reducible_balance(
1147 &msa_account_id,
1148 Preservation::Expendable,
1149 Fortitude::Polite,
1150 );
1151 ensure!(msa_balance > Zero::zero(), Error::<T>::InsufficientBalanceToWithdraw);
1152
1153 let result = <T as pallet::Config>::Currency::transfer(
1155 &msa_account_id,
1156 &public_key,
1157 msa_balance,
1158 Preservation::Expendable,
1159 );
1160 ensure!(result.is_ok(), Error::<T>::UnexpectedTokenTransferError);
1161
1162 Ok(())
1163 }
1164
1165 #[pallet::call_index(15)]
1187 #[pallet::weight(T::WeightInfo::add_recovery_commitment())]
1188 pub fn add_recovery_commitment(
1189 origin: OriginFor<T>,
1190 msa_owner_key: T::AccountId,
1191 proof: MultiSignature,
1192 payload: RecoveryCommitmentPayload<T>,
1193 ) -> DispatchResult {
1194 let _origin_key = ensure_signed(origin)?;
1195
1196 ensure!(
1198 Self::verify_signature(&proof, &msa_owner_key, &payload),
1199 Error::<T>::InvalidSignature
1200 );
1201
1202 Self::register_signature(&proof, payload.expiration)?;
1204
1205 let msa_id = Self::ensure_valid_msa_key(&msa_owner_key)?;
1207
1208 MsaIdToRecoveryCommitment::<T>::insert(msa_id, payload.recovery_commitment);
1210 Self::deposit_event(Event::RecoveryCommitmentAdded {
1211 who: msa_owner_key,
1212 msa_id,
1213 recovery_commitment: payload.recovery_commitment,
1214 });
1215
1216 Ok(())
1217 }
1218
1219 #[pallet::call_index(16)]
1229 #[pallet::weight(T::WeightInfo::approve_recovery_provider())]
1230 pub fn approve_recovery_provider(
1231 origin: OriginFor<T>,
1232 provider_key: T::AccountId,
1233 ) -> DispatchResult {
1234 T::RecoveryProviderApprovalOrigin::ensure_origin(origin)?;
1235
1236 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1237 ensure!(
1238 Self::is_registered_provider(provider_msa_id),
1239 Error::<T>::ProviderNotRegistered
1240 );
1241
1242 if Self::is_approved_recovery_provider(&ProviderId(provider_msa_id)) {
1244 return Ok(());
1245 }
1246
1247 RecoveryProviders::<T>::insert(ProviderId(provider_msa_id), true);
1248
1249 Self::deposit_event(Event::RecoveryProviderApproved {
1250 provider_id: ProviderId(provider_msa_id),
1251 });
1252
1253 Ok(())
1254 }
1255
1256 #[pallet::call_index(17)]
1267 #[pallet::weight(T::WeightInfo::remove_recovery_provider())]
1268 pub fn remove_recovery_provider(
1269 origin: OriginFor<T>,
1270 provider: ProviderId,
1271 ) -> DispatchResult {
1272 T::RecoveryProviderApprovalOrigin::ensure_origin(origin)?;
1273
1274 RecoveryProviders::<T>::remove(provider);
1275 Self::deposit_event(Event::RecoveryProviderRemoved { provider_id: provider });
1276 Ok(())
1277 }
1278
1279 #[pallet::call_index(18)]
1294 #[pallet::weight(T::WeightInfo::recover_account())]
1295 pub fn recover_account(
1296 origin: OriginFor<T>,
1297 intermediary_hash_a: RecoveryHash,
1298 intermediary_hash_b: RecoveryHash,
1299 new_control_key_proof: MultiSignature,
1300 add_key_payload: AddKeyData<T>,
1301 ) -> DispatchResult {
1302 let provider_key = ensure_signed(origin)?;
1303
1304 let provider_msa_id = Self::ensure_approved_recovery_provider(&provider_key)?;
1305
1306 let recovery_commitment = Self::ensure_valid_recovery_commitment(
1307 intermediary_hash_a,
1308 intermediary_hash_b,
1309 add_key_payload.msa_id,
1310 )?;
1311
1312 Self::ensure_valid_new_key_owner(&new_control_key_proof, &add_key_payload)?;
1313
1314 Self::add_key(add_key_payload.msa_id, &add_key_payload.new_public_key.clone())?;
1315
1316 let event = Event::PublicKeyAdded {
1317 msa_id: add_key_payload.msa_id,
1318 key: add_key_payload.new_public_key.clone(),
1319 };
1320 Self::add_event_to_offchain_index(Some(&event), add_key_payload.msa_id);
1321 Self::deposit_event(event);
1322
1323 MsaIdToRecoveryCommitment::<T>::remove(add_key_payload.msa_id);
1325
1326 Self::deposit_event(Event::AccountRecovered {
1328 msa_id: add_key_payload.msa_id,
1329 recovery_provider: ProviderId(provider_msa_id),
1330 new_control_key: add_key_payload.new_public_key.clone(),
1331 });
1332
1333 Self::deposit_event(Event::RecoveryCommitmentInvalidated {
1334 msa_id: add_key_payload.msa_id,
1335 recovery_commitment,
1336 });
1337
1338 Ok(())
1339 }
1340
1341 #[pallet::call_index(19)]
1346 #[pallet::weight(T::WeightInfo::propose_to_be_provider_v2())]
1347 pub fn propose_to_be_provider_v2(
1348 origin: OriginFor<T>,
1349 payload: ProviderRegistryEntry<
1350 T::MaxProviderNameSize,
1351 T::MaxLanguageCodeSize,
1352 T::MaxLogoCidSize,
1353 T::MaxLocaleCount,
1354 >,
1355 ) -> DispatchResult {
1356 let proposer = ensure_signed(origin)?;
1357 Self::ensure_valid_msa_key(&proposer)?;
1358
1359 let proposal: Box<T::Proposal> = Box::new(
1360 (Call::<T>::create_provider_via_governance_v2 {
1361 provider_key: proposer.clone(),
1362 payload,
1363 })
1364 .into(),
1365 );
1366 let threshold = 1;
1367 T::ProposalProvider::propose(proposer, threshold, proposal)?;
1368 Ok(())
1369 }
1370
1371 #[pallet::call_index(20)]
1382 #[pallet::weight(T::WeightInfo::create_provider_via_governance_v2(
1383 payload.localized_names.len() as u32,
1384 payload.localized_logo_250_100_png_cids.len() as u32,
1385 ))]
1386 pub fn create_provider_via_governance_v2(
1387 origin: OriginFor<T>,
1388 provider_key: T::AccountId,
1389 payload: ProviderRegistryEntry<
1390 T::MaxProviderNameSize,
1391 T::MaxLanguageCodeSize,
1392 T::MaxLogoCidSize,
1393 T::MaxLocaleCount,
1394 >,
1395 ) -> DispatchResult {
1396 T::CreateProviderViaGovernanceOrigin::ensure_origin(origin)?;
1397 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1398 Self::ensure_correct_cids(&payload)?;
1399 Self::upsert_provider_for(provider_msa_id, payload, false)?;
1400 Self::deposit_event(Event::ProviderCreated {
1401 provider_id: ProviderId(provider_msa_id),
1402 });
1403 Ok(())
1404 }
1405
1406 #[pallet::call_index(21)]
1414 #[pallet::weight(T::WeightInfo::propose_to_add_application(
1415 payload.localized_names.len() as u32,
1416 payload.localized_logo_250_100_png_cids.len() as u32,
1417 ))]
1418 pub fn propose_to_add_application(
1419 origin: OriginFor<T>,
1420 payload: ApplicationContext<
1421 T::MaxProviderNameSize,
1422 T::MaxLanguageCodeSize,
1423 T::MaxLogoCidSize,
1424 T::MaxLocaleCount,
1425 >,
1426 ) -> DispatchResult {
1427 let proposer = ensure_signed(origin)?;
1428 let provider_msa_id = Self::ensure_valid_msa_key(&proposer)?;
1429 ensure!(
1430 Self::is_registered_provider(provider_msa_id),
1431 Error::<T>::ProviderNotRegistered
1432 );
1433 let proposal: Box<T::Proposal> = Box::new(
1434 (Call::<T>::create_application_via_governance {
1435 provider_key: proposer.clone(),
1436 payload,
1437 })
1438 .into(),
1439 );
1440 let threshold = 1;
1441 T::ProposalProvider::propose(proposer, threshold, proposal)?;
1442 Ok(())
1443 }
1444
1445 #[pallet::call_index(22)]
1456 #[pallet::weight(T::WeightInfo::create_application_via_governance(
1457 payload.localized_names.len() as u32,
1458 payload.localized_logo_250_100_png_cids.len() as u32,
1459 ))]
1460 pub fn create_application_via_governance(
1461 origin: OriginFor<T>,
1462 provider_key: T::AccountId,
1463 payload: ApplicationContext<
1464 T::MaxProviderNameSize,
1465 T::MaxLanguageCodeSize,
1466 T::MaxLogoCidSize,
1467 T::MaxLocaleCount,
1468 >,
1469 ) -> DispatchResult {
1470 T::CreateProviderViaGovernanceOrigin::ensure_origin(origin)?;
1471 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1472 ensure!(
1473 Self::is_registered_provider(provider_msa_id),
1474 Error::<T>::ProviderNotRegistered
1475 );
1476 Self::ensure_correct_cids(&payload)?;
1477 let application_id =
1478 Self::create_application_for(ProviderId(provider_msa_id), payload)?;
1479 Self::deposit_event(Event::ApplicationCreated {
1480 provider_id: ProviderId(provider_msa_id),
1481 application_id,
1482 });
1483 Ok(())
1484 }
1485
1486 #[pallet::call_index(23)]
1494 #[pallet::weight(T::WeightInfo::upload_logo())]
1495 pub fn upload_logo(
1496 origin: OriginFor<T>,
1497 logo_cid: BoundedVec<u8, T::MaxLogoCidSize>,
1498 logo_bytes: BoundedVec<u8, T::MaxLogoSize>,
1499 ) -> DispatchResult {
1500 let provider_key = ensure_signed(origin)?;
1501 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1502 ensure!(
1503 Self::is_registered_provider(provider_msa_id),
1504 Error::<T>::ProviderNotRegistered
1505 );
1506 ensure!(ApprovedLogos::<T>::contains_key(&logo_cid), Error::<T>::LogoCidNotApproved);
1507
1508 let input_cid_binary = Self::validate_cid(&logo_cid)?;
1509 let computed_cid_binary =
1510 compute_cid_v1(logo_bytes.as_slice()).ok_or(Error::<T>::InvalidLogoBytes)?;
1511 ensure!(input_cid_binary == computed_cid_binary, Error::<T>::InvalidLogoBytes);
1512 ApprovedLogos::<T>::insert(&logo_cid, logo_bytes);
1513
1514 Self::deposit_event(Event::ApplicationContextUpdated {
1515 provider_id: ProviderId(provider_msa_id),
1516 application_id: None,
1517 });
1518 Ok(())
1519 }
1520
1521 #[pallet::call_index(24)]
1531 #[pallet::weight(T::WeightInfo::update_provider_via_governance(
1532 payload.localized_names.len() as u32,
1533 payload.localized_logo_250_100_png_cids.len() as u32,
1534 ))]
1535 #[allow(clippy::useless_conversion)]
1536 pub fn update_provider_via_governance(
1537 origin: OriginFor<T>,
1538 provider_key: T::AccountId,
1539 payload: ProviderRegistryEntry<
1540 T::MaxProviderNameSize,
1541 T::MaxLanguageCodeSize,
1542 T::MaxLogoCidSize,
1543 T::MaxLocaleCount,
1544 >,
1545 ) -> DispatchResultWithPostInfo {
1546 T::CreateProviderViaGovernanceOrigin::ensure_origin(origin)?;
1547 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1548 Self::ensure_correct_cids(&payload)?;
1549 let base_weight = T::WeightInfo::update_provider_via_governance(
1550 payload.localized_names.len() as u32,
1551 payload.localized_logo_250_100_png_cids.len() as u32,
1552 );
1553 let total_logos_removed = Self::upsert_provider_for(provider_msa_id, payload, true)?;
1554 Self::deposit_event(Event::ProviderUpdated {
1555 provider_id: ProviderId(provider_msa_id),
1556 });
1557 Self::refund_logo_removal_weight_by_count(total_logos_removed, base_weight)
1558 }
1559
1560 #[pallet::call_index(25)]
1573 #[pallet::weight(T::WeightInfo::propose_to_update_provider(
1574 payload.localized_names.len() as u32,
1575 payload.localized_logo_250_100_png_cids.len() as u32,
1576 ))]
1577 pub fn propose_to_update_provider(
1578 origin: OriginFor<T>,
1579 payload: ProviderRegistryEntry<
1580 T::MaxProviderNameSize,
1581 T::MaxLanguageCodeSize,
1582 T::MaxLogoCidSize,
1583 T::MaxLocaleCount,
1584 >,
1585 ) -> DispatchResult {
1586 let proposer = ensure_signed(origin)?;
1587 let provider_msa_id = Self::ensure_valid_msa_key(&proposer)?;
1588 ensure!(
1589 Self::is_registered_provider(provider_msa_id),
1590 Error::<T>::ProviderNotRegistered
1591 );
1592 let proposal: Box<T::Proposal> = Box::new(
1593 (Call::<T>::update_provider_via_governance {
1594 provider_key: proposer.clone(),
1595 payload,
1596 })
1597 .into(),
1598 );
1599 let threshold = 1;
1600 T::ProposalProvider::propose(proposer, threshold, proposal)?;
1601 Ok(())
1602 }
1603
1604 #[pallet::call_index(26)]
1618 #[pallet::weight(T::WeightInfo::update_application_via_governance(
1619 payload.localized_names.len() as u32,
1620 payload.localized_logo_250_100_png_cids.len() as u32,
1621 ))]
1622 #[allow(clippy::useless_conversion)]
1623 pub fn update_application_via_governance(
1624 origin: OriginFor<T>,
1625 provider_key: T::AccountId,
1626 application_index: ApplicationIndex,
1627 payload: ApplicationContext<
1628 T::MaxProviderNameSize,
1629 T::MaxLanguageCodeSize,
1630 T::MaxLogoCidSize,
1631 T::MaxLocaleCount,
1632 >,
1633 ) -> DispatchResultWithPostInfo {
1634 T::CreateProviderViaGovernanceOrigin::ensure_origin(origin)?;
1635 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1636 ensure!(
1637 Self::is_registered_provider(provider_msa_id),
1638 Error::<T>::ProviderNotRegistered
1639 );
1640 Self::ensure_correct_cids(&payload)?;
1641 let base_weight = T::WeightInfo::update_application_via_governance(
1642 payload.localized_names.len() as u32,
1643 payload.localized_logo_250_100_png_cids.len() as u32,
1644 );
1645 let total_logos_removed = Self::upsert_application_for(
1646 ProviderId(provider_msa_id),
1647 application_index,
1648 payload,
1649 )?;
1650 Self::deposit_event(Event::ApplicationContextUpdated {
1651 provider_id: ProviderId(provider_msa_id),
1652 application_id: Some(application_index),
1653 });
1654 Self::refund_logo_removal_weight_by_count(total_logos_removed, base_weight)
1655 }
1656
1657 #[pallet::call_index(27)]
1671 #[pallet::weight(T::WeightInfo::propose_to_update_application(
1672 payload.localized_names.len() as u32,
1673 payload.localized_logo_250_100_png_cids.len() as u32,
1674 ))]
1675 pub fn propose_to_update_application(
1676 origin: OriginFor<T>,
1677 application_index: ApplicationIndex,
1678 payload: ApplicationContext<
1679 T::MaxProviderNameSize,
1680 T::MaxLanguageCodeSize,
1681 T::MaxLogoCidSize,
1682 T::MaxLocaleCount,
1683 >,
1684 ) -> DispatchResult {
1685 let proposer = ensure_signed(origin)?;
1686 let provider_msa_id = Self::ensure_valid_msa_key(&proposer)?;
1687 ensure!(
1688 Self::is_registered_provider(provider_msa_id),
1689 Error::<T>::ProviderNotRegistered
1690 );
1691 ensure!(
1692 ProviderToApplicationRegistry::<T>::contains_key(
1693 ProviderId(provider_msa_id),
1694 application_index
1695 ),
1696 Error::<T>::ApplicationNotFound
1697 );
1698 Self::ensure_correct_cids(&payload)?;
1699 let proposal: Box<T::Proposal> = Box::new(
1700 (Call::<T>::update_application_via_governance {
1701 provider_key: proposer.clone(),
1702 application_index,
1703 payload,
1704 })
1705 .into(),
1706 );
1707 let threshold = 1;
1708 T::ProposalProvider::propose(proposer, threshold, proposal)?;
1709 Ok(())
1710 }
1711
1712 #[pallet::call_index(28)]
1715 #[pallet::weight(T::WeightInfo::create_application_via_governance(
1716 payload.localized_names.len() as u32,
1717 payload.localized_logo_250_100_png_cids.len() as u32,
1718 ))]
1719 pub fn create_application(
1720 origin: OriginFor<T>,
1721 payload: ApplicationContext<
1722 T::MaxProviderNameSize,
1723 T::MaxLanguageCodeSize,
1724 T::MaxLogoCidSize,
1725 T::MaxLocaleCount,
1726 >,
1727 ) -> DispatchResult {
1728 let provider_account = ensure_signed(origin)?;
1729 let provider_msa_id = Self::ensure_valid_msa_key(&provider_account)?;
1730 ensure!(
1731 Self::is_registered_provider(provider_msa_id),
1732 Error::<T>::ProviderNotRegistered
1733 );
1734 Self::ensure_correct_cids(&payload)?;
1735 let application_id =
1736 Self::create_application_for(ProviderId(provider_msa_id), payload)?;
1737 Self::deposit_event(Event::ApplicationCreated {
1738 provider_id: ProviderId(provider_msa_id),
1739 application_id,
1740 });
1741 Ok(())
1742 }
1743 }
1744}
1745
1746impl<T: Config> Pallet<T> {
1747 pub fn is_approved_recovery_provider(provider: &ProviderId) -> bool {
1755 RecoveryProviders::<T>::get(provider).unwrap_or(false)
1756 }
1757
1758 pub fn compute_recovery_commitment(
1767 intermediary_hash_a: RecoveryHash,
1768 intermediary_hash_b: RecoveryHash,
1769 ) -> RecoveryCommitment {
1770 let mut input = Vec::with_capacity(64);
1771 input.extend_from_slice(&intermediary_hash_a);
1772 input.extend_from_slice(&intermediary_hash_b);
1773 keccak_256(&input)
1774 }
1775
1776 fn ensure_approved_recovery_provider(
1788 provider_key: &T::AccountId,
1789 ) -> Result<MessageSourceId, DispatchError> {
1790 let provider_msa_id = Self::ensure_valid_msa_key(provider_key)?;
1791
1792 ensure!(
1793 Self::is_approved_recovery_provider(&ProviderId(provider_msa_id)),
1794 Error::<T>::NotAuthorizedRecoveryProvider
1795 );
1796
1797 Ok(provider_msa_id)
1798 }
1799
1800 fn ensure_valid_recovery_commitment(
1814 intermediary_hash_a: RecoveryHash,
1815 intermediary_hash_b: RecoveryHash,
1816 msa_id: MessageSourceId,
1817 ) -> Result<RecoveryCommitment, DispatchError> {
1818 let recovery_commitment: RecoveryCommitment =
1819 Self::compute_recovery_commitment(intermediary_hash_a, intermediary_hash_b);
1820
1821 let stored_commitment: RecoveryCommitment =
1822 MsaIdToRecoveryCommitment::<T>::get(msa_id).ok_or(Error::<T>::NoRecoveryCommitment)?;
1823
1824 ensure!(recovery_commitment == stored_commitment, Error::<T>::InvalidRecoveryCommitment);
1825
1826 Ok(recovery_commitment)
1827 }
1828
1829 fn ensure_valid_new_key_owner(
1839 new_control_key_proof: &MultiSignature,
1840 add_key_payload: &AddKeyData<T>,
1841 ) -> DispatchResult {
1842 ensure!(
1845 Self::verify_signature(
1846 new_control_key_proof,
1847 &add_key_payload.new_public_key,
1848 add_key_payload
1849 ),
1850 Error::<T>::NewKeyOwnershipInvalidSignature
1851 );
1852 Self::register_signature(new_control_key_proof, add_key_payload.expiration)?;
1853
1854 Ok(())
1855 }
1856
1857 pub fn create_account(
1865 key: T::AccountId,
1866 ) -> Result<(MessageSourceId, T::AccountId), DispatchError> {
1867 let next_msa_id = Self::get_next_msa_id()?;
1868 Self::add_key(next_msa_id, &key)?;
1869 let _ = Self::set_msa_identifier(next_msa_id);
1870
1871 Ok((next_msa_id, key))
1872 }
1873
1874 pub fn get_next_msa_id() -> Result<MessageSourceId, DispatchError> {
1880 let next = CurrentMsaIdentifierMaximum::<T>::get()
1881 .checked_add(1)
1882 .ok_or(Error::<T>::MsaIdOverflow)?;
1883
1884 Ok(next)
1885 }
1886
1887 pub fn set_msa_identifier(identifier: MessageSourceId) -> DispatchResult {
1889 CurrentMsaIdentifierMaximum::<T>::set(identifier);
1890
1891 Ok(())
1892 }
1893
1894 pub fn create_registered_provider(
1896 provider_id: ProviderId,
1897 payload: ProviderRegistryEntry<
1898 T::MaxProviderNameSize,
1899 T::MaxLanguageCodeSize,
1900 T::MaxLogoCidSize,
1901 T::MaxLocaleCount,
1902 >,
1903 ) -> DispatchResult {
1904 ProviderToRegistryEntryV2::<T>::try_mutate(
1905 provider_id,
1906 |maybe_metadata| -> DispatchResult {
1907 ensure!(
1908 maybe_metadata.take().is_none(),
1909 Error::<T>::DuplicateProviderRegistryEntry
1910 );
1911 *maybe_metadata = Some(payload);
1912 Ok(())
1913 },
1914 )
1915 }
1916
1917 #[cfg(test)]
1919 pub fn grant_permissions_for_intents(
1920 delegator_id: DelegatorId,
1921 provider_id: ProviderId,
1922 intent_ids: Vec<IntentId>,
1923 ) -> DispatchResult {
1924 Self::try_mutate_delegation(delegator_id, provider_id, |delegation, is_new_delegation| {
1925 ensure!(!is_new_delegation, Error::<T>::DelegationNotFound);
1926 Self::ensure_all_intent_ids_are_valid(&intent_ids)?;
1927
1928 PermittedDelegationIntents::<T>::try_insert_intents(delegation, intent_ids)?;
1929
1930 Ok(())
1931 })
1932 }
1933
1934 pub fn revoke_permissions_for_intents(
1936 delegator_id: DelegatorId,
1937 provider_id: ProviderId,
1938 intent_ids: Vec<IntentId>,
1939 ) -> DispatchResult {
1940 Self::try_mutate_delegation(delegator_id, provider_id, |delegation, is_new_delegation| {
1941 ensure!(!is_new_delegation, Error::<T>::DelegationNotFound);
1942 Self::ensure_all_intent_ids_are_valid(&intent_ids)?;
1943
1944 let current_block = frame_system::Pallet::<T>::block_number();
1945
1946 PermittedDelegationIntents::<T>::try_get_mut_intents(
1947 delegation,
1948 intent_ids,
1949 current_block,
1950 )?;
1951
1952 Ok(())
1953 })
1954 }
1955
1956 pub fn add_key(msa_id: MessageSourceId, key: &T::AccountId) -> DispatchResult {
1963 PublicKeyToMsaId::<T>::try_mutate(key, |maybe_msa_id| {
1964 ensure!(maybe_msa_id.is_none(), Error::<T>::KeyAlreadyRegistered);
1965 *maybe_msa_id = Some(msa_id);
1966
1967 <PublicKeyCountForMsaId<T>>::try_mutate(msa_id, |key_count| {
1969 let incremented_key_count =
1971 key_count.checked_add(1).ok_or(ArithmeticError::Overflow)?;
1972
1973 ensure!(
1974 incremented_key_count <= T::MaxPublicKeysPerMsa::get(),
1975 Error::<T>::KeyLimitExceeded
1976 );
1977
1978 *key_count = incremented_key_count;
1979 Ok(())
1980 })
1981 })
1982 }
1983
1984 pub fn ensure_all_intent_ids_are_valid(intent_ids: &[IntentId]) -> DispatchResult {
1991 ensure!(
1992 intent_ids.len() <= T::MaxGrantsPerDelegation::get() as usize,
1993 Error::<T>::ExceedsMaxGrantsPerDelegation
1994 );
1995
1996 let all_valid = T::SchemaValidator::are_all_intent_ids_valid(intent_ids);
1997
1998 ensure!(all_valid, Error::<T>::InvalidIntentId);
1999
2000 Ok(())
2001 }
2002
2003 pub fn is_registered_provider(msa_id: MessageSourceId) -> bool {
2005 ProviderToRegistryEntryV2::<T>::contains_key(ProviderId(msa_id))
2006 }
2007
2008 pub fn ensure_valid_registered_provider(
2018 delegator_key: &T::AccountId,
2019 provider_key: &T::AccountId,
2020 ) -> Result<(ProviderId, DelegatorId), DispatchError> {
2021 let provider_msa_id = Self::ensure_valid_msa_key(provider_key)?;
2022 let delegator_msa_id = Self::ensure_valid_msa_key(delegator_key)?;
2023
2024 ensure!(delegator_msa_id != provider_msa_id, Error::<T>::InvalidSelfProvider);
2026
2027 ensure!(Self::is_registered_provider(provider_msa_id), Error::<T>::ProviderNotRegistered);
2029
2030 Ok((provider_msa_id.into(), delegator_msa_id.into()))
2031 }
2032
2033 pub fn ensure_msa_owner(who: &T::AccountId, msa_id: MessageSourceId) -> DispatchResult {
2040 let provider_msa_id = Self::ensure_valid_msa_key(who)?;
2041 ensure!(provider_msa_id == msa_id, Error::<T>::NotMsaOwner);
2042
2043 Ok(())
2044 }
2045
2046 pub fn verify_signature<P>(
2053 signature: &MultiSignature,
2054 signer: &T::AccountId,
2055 payload: &P,
2056 ) -> bool
2057 where
2058 P: Encode + EIP712Encode,
2059 {
2060 let key = T::ConvertIntoAccountId32::convert((*signer).clone());
2061
2062 check_signature(signature, key, payload)
2063 }
2064
2065 pub fn add_provider(
2071 provider_id: ProviderId,
2072 delegator_id: DelegatorId,
2073 intent_ids: Vec<IntentId>,
2074 ) -> DispatchResult {
2075 Self::try_mutate_delegation(delegator_id, provider_id, |delegation, is_new_delegation| {
2076 ensure!(is_new_delegation, Error::<T>::DuplicateProvider);
2077 Self::ensure_all_intent_ids_are_valid(&intent_ids)?;
2078
2079 PermittedDelegationIntents::<T>::try_insert_intents(delegation, intent_ids)?;
2080
2081 Ok(())
2082 })
2083 }
2084
2085 pub fn upsert_intent_permissions(
2090 provider_id: ProviderId,
2091 delegator_id: DelegatorId,
2092 intent_ids: Vec<IntentId>,
2093 ) -> DispatchResult {
2094 Self::try_mutate_delegation(delegator_id, provider_id, |delegation, _is_new_delegation| {
2095 Self::ensure_all_intent_ids_are_valid(&intent_ids)?;
2096
2097 let mut revoke_ids: Vec<IntentId> = Vec::new();
2099 let mut update_ids: Vec<IntentId> = Vec::new();
2100 let mut insert_ids: Vec<IntentId> = Vec::new();
2101
2102 let existing_keys = delegation.permissions.keys();
2103
2104 for existing_intent_id in existing_keys {
2105 if !intent_ids.contains(existing_intent_id) {
2106 if let Some(block) = delegation.permissions.get(existing_intent_id) {
2107 if *block == BlockNumberFor::<T>::zero() {
2108 revoke_ids.push(*existing_intent_id);
2109 }
2110 }
2111 }
2112 }
2113 for intent_id in &intent_ids {
2114 if !delegation.permissions.contains_key(intent_id) {
2115 insert_ids.push(*intent_id);
2116 } else {
2117 update_ids.push(*intent_id);
2118 }
2119 }
2120
2121 let current_block = frame_system::Pallet::<T>::block_number();
2122
2123 PermittedDelegationIntents::<T>::try_get_mut_intents(
2125 delegation,
2126 revoke_ids,
2127 current_block,
2128 )?;
2129
2130 PermittedDelegationIntents::<T>::try_get_mut_intents(
2132 delegation,
2133 update_ids,
2134 BlockNumberFor::<T>::zero(),
2135 )?;
2136
2137 PermittedDelegationIntents::<T>::try_insert_intents(delegation, insert_ids)?;
2139 delegation.revoked_at = BlockNumberFor::<T>::zero();
2140 Ok(())
2141 })
2142 }
2143
2144 pub fn upsert_provider_for(
2151 provider_msa_id: MessageSourceId,
2152 payload: ProviderRegistryEntry<
2153 T::MaxProviderNameSize,
2154 T::MaxLanguageCodeSize,
2155 T::MaxLogoCidSize,
2156 T::MaxLocaleCount,
2157 >,
2158 is_update: bool,
2159 ) -> Result<u32, DispatchError> {
2160 let mut total_logos_removed = 0;
2161 ProviderToRegistryEntryV2::<T>::try_mutate(
2162 ProviderId(provider_msa_id),
2163 |maybe_metadata| -> DispatchResult {
2164 if !is_update {
2165 ensure!(
2166 maybe_metadata.take().is_none(),
2167 Error::<T>::DuplicateProviderRegistryEntry
2168 );
2169 } else {
2170 total_logos_removed = Self::remove_logo_storage(maybe_metadata.as_ref())?;
2171 }
2172 Self::update_logo_storage(&payload)?;
2173
2174 *maybe_metadata = Some(payload);
2175 Ok(())
2176 },
2177 )
2178 .map(|_| total_logos_removed)
2179 }
2180
2181 pub fn create_application_for(
2186 provider_msa_id: ProviderId,
2187 payload: ApplicationContext<
2188 T::MaxProviderNameSize,
2189 T::MaxLanguageCodeSize,
2190 T::MaxLogoCidSize,
2191 T::MaxLocaleCount,
2192 >,
2193 ) -> Result<ApplicationIndex, DispatchError> {
2194 Self::update_logo_storage(&payload)?;
2195 let next_application_index = NextApplicationIndex::<T>::get(provider_msa_id);
2196 ensure!(
2197 !ProviderToApplicationRegistry::<T>::contains_key(
2198 provider_msa_id,
2199 next_application_index
2200 ),
2201 Error::<T>::DuplicateApplicationRegistryEntry
2202 );
2203 ProviderToApplicationRegistry::<T>::insert(
2204 provider_msa_id,
2205 next_application_index,
2206 payload,
2207 );
2208 NextApplicationIndex::<T>::insert(provider_msa_id, next_application_index + 1);
2209 Ok(next_application_index)
2210 }
2211
2212 pub fn upsert_application_for(
2217 provider_msa_id: ProviderId,
2218 application_index: ApplicationIndex,
2219 payload: ApplicationContext<
2220 T::MaxProviderNameSize,
2221 T::MaxLanguageCodeSize,
2222 T::MaxLogoCidSize,
2223 T::MaxLocaleCount,
2224 >,
2225 ) -> Result<u32, DispatchError> {
2226 ensure!(
2227 ProviderToApplicationRegistry::<T>::contains_key(provider_msa_id, application_index),
2228 Error::<T>::ApplicationNotFound
2229 );
2230 let mut total_logos_removed = 0;
2231 ProviderToApplicationRegistry::<T>::try_mutate(
2232 provider_msa_id,
2233 application_index,
2234 |maybe_metadata| -> DispatchResult {
2235 total_logos_removed = Self::remove_logo_storage(maybe_metadata.as_ref())?;
2236 Self::update_logo_storage(&payload)?;
2237 *maybe_metadata = Some(payload);
2238 Ok(())
2239 },
2240 )
2241 .map(|_| total_logos_removed)
2242 }
2243
2244 pub fn try_mutate_delegation<R, E: From<DispatchError>>(
2248 delegator_id: DelegatorId,
2249 provider_id: ProviderId,
2250 f: impl FnOnce(
2251 &mut Delegation<IntentId, BlockNumberFor<T>, T::MaxGrantsPerDelegation>,
2252 bool,
2253 ) -> Result<R, E>,
2254 ) -> Result<R, E> {
2255 DelegatorAndProviderToDelegation::<T>::try_mutate_exists(
2256 delegator_id,
2257 provider_id,
2258 |maybe_delegation_info| {
2259 let is_new = maybe_delegation_info.is_none();
2260 let mut delegation = maybe_delegation_info.take().unwrap_or_default();
2261
2262 let result = f(&mut delegation, is_new)?;
2263
2264 *maybe_delegation_info = Some(delegation);
2266 Ok(result)
2267 },
2268 )
2269 }
2270
2271 pub fn delete_key_for_msa(msa_id: MessageSourceId, key: &T::AccountId) -> DispatchResult {
2277 PublicKeyToMsaId::<T>::try_mutate_exists(key, |maybe_msa_id| {
2278 ensure!(maybe_msa_id.is_some(), Error::<T>::NoKeyExists);
2279
2280 *maybe_msa_id = None;
2282
2283 <PublicKeyCountForMsaId<T>>::try_mutate_exists(msa_id, |key_count| {
2284 match key_count {
2285 Some(1) => *key_count = None,
2286 Some(count) => *count = *count - 1u8,
2287 None => (),
2288 }
2289
2290 Ok(())
2291 })
2292 })
2293 }
2294
2295 pub fn revoke_provider(provider_id: ProviderId, delegator_id: DelegatorId) -> DispatchResult {
2302 DelegatorAndProviderToDelegation::<T>::try_mutate_exists(
2303 delegator_id,
2304 provider_id,
2305 |maybe_info| -> DispatchResult {
2306 let mut info = maybe_info.take().ok_or(Error::<T>::DelegationNotFound)?;
2307
2308 ensure!(
2309 info.revoked_at == BlockNumberFor::<T>::default(),
2310 Error::<T>::DelegationRevoked
2311 );
2312
2313 let current_block = frame_system::Pallet::<T>::block_number();
2314 info.revoked_at = current_block;
2315 *maybe_info = Some(info);
2316 Ok(())
2317 },
2318 )?;
2319
2320 Ok(())
2321 }
2322
2323 pub fn get_owner_of(key: &T::AccountId) -> Option<MessageSourceId> {
2325 PublicKeyToMsaId::<T>::get(key)
2326 }
2327
2328 pub fn ensure_valid_msa_key(key: &T::AccountId) -> Result<MessageSourceId, DispatchError> {
2330 let msa_id = PublicKeyToMsaId::<T>::get(key).ok_or(Error::<T>::NoKeyExists)?;
2331 Ok(msa_id)
2332 }
2333
2334 pub fn get_granted_intents_by_msa_id(
2341 delegator: DelegatorId,
2342 provider: Option<ProviderId>,
2343 ) -> Result<Vec<DelegationResponse<IntentId, BlockNumberFor<T>>>, DispatchError> {
2344 let delegations = match provider {
2345 Some(provider_id) => vec![(
2346 provider_id,
2347 Self::get_delegation_of(delegator, provider_id)
2348 .ok_or(Error::<T>::DelegationNotFound)?,
2349 )],
2350 None => DelegatorAndProviderToDelegation::<T>::iter_prefix(delegator).collect(),
2351 };
2352
2353 let mut result = vec![];
2354 for (provider_id, provider_info) in delegations {
2355 let intent_permissions = provider_info.permissions;
2356 if provider.is_some() && intent_permissions.is_empty() {
2358 return Err(Error::<T>::PermissionNotGranted.into());
2359 }
2360
2361 let mut intent_list = Vec::new();
2362 for (granted_id, revoked_at) in intent_permissions {
2363 let effective_revoked_at = match (provider_info.revoked_at, revoked_at) {
2368 (provider_revoked_at, _) if provider_revoked_at.is_zero() => revoked_at,
2369 (_, revoked_at) if revoked_at.is_zero() => provider_info.revoked_at,
2370 _ => core::cmp::min(revoked_at, provider_info.revoked_at),
2371 };
2372 intent_list.push(DelegationGrant {
2373 granted_id,
2374 explicit_revoked_at: revoked_at,
2375 revoked_at: effective_revoked_at,
2376 });
2377 }
2378
2379 result.push(DelegationResponse {
2380 provider_id,
2381 permissions: intent_list,
2382 revoked_at: provider_info.revoked_at,
2383 });
2384 }
2385
2386 Ok(result)
2387 }
2388
2389 pub fn msa_id_to_eth_address(id: MessageSourceId) -> H160 {
2395 const DOMAIN_PREFIX: u8 = 0xD9;
2399
2400 lazy_static! {
2401 static ref MSA_ADDRESS_SALT: [u8; 32] = keccak_256(b"MSA Generated");
2403 }
2404 let input_value = id.to_be_bytes();
2405
2406 let mut hash_input = [0u8; 41];
2407 hash_input[0] = DOMAIN_PREFIX;
2408 hash_input[1..9].copy_from_slice(&input_value);
2409 hash_input[9..].copy_from_slice(&(*MSA_ADDRESS_SALT));
2410
2411 let hash = keccak_256(&hash_input);
2412 H160::from_slice(&hash[12..])
2413 }
2414
2415 pub fn validate_eth_address_for_msa(address: &H160, msa_id: MessageSourceId) -> bool {
2417 let generated_address = Self::msa_id_to_eth_address(msa_id);
2418 *address == generated_address
2419 }
2420
2421 pub fn eth_address_to_checksummed_string(address: &H160) -> alloc::string::String {
2425 let addr_bytes = address.0;
2426 let addr_hex = hex::encode(addr_bytes);
2427 let hash = keccak_256(addr_hex.as_bytes());
2428
2429 let mut result = alloc::string::String::with_capacity(42);
2430 result.push_str("0x");
2431
2432 for (i, c) in addr_hex.chars().enumerate() {
2433 let hash_byte = hash[i / 2];
2434 let bit = if i % 2 == 0 { (hash_byte >> 4) & 0xf } else { hash_byte & 0xf };
2435
2436 result.push(if c.is_ascii_hexdigit() && c.is_ascii_alphabetic() {
2437 if bit >= 8 {
2438 c.to_ascii_uppercase()
2439 } else {
2440 c
2441 }
2442 } else {
2443 c
2444 });
2445 }
2446
2447 result
2448 }
2449
2450 pub fn register_signature(
2462 signature: &MultiSignature,
2463 signature_expires_at: BlockNumberFor<T>,
2464 ) -> DispatchResult {
2465 let current_block =
2466 Self::check_signature_against_registry(signature, signature_expires_at)?;
2467
2468 Self::enqueue_signature(signature, signature_expires_at, current_block)
2469 }
2470
2471 pub fn check_signature_against_registry(
2480 signature: &MultiSignature,
2481 signature_expires_at: BlockNumberFor<T>,
2482 ) -> Result<BlockNumberFor<T>, DispatchError> {
2483 let current_block: BlockNumberFor<T> = frame_system::Pallet::<T>::block_number();
2484
2485 let max_lifetime = Self::mortality_block_limit(current_block);
2486 ensure!(max_lifetime > signature_expires_at, Error::<T>::ProofNotYetValid);
2487 ensure!(current_block < signature_expires_at, Error::<T>::ProofHasExpired);
2488
2489 ensure!(
2491 !<PayloadSignatureRegistryList<T>>::contains_key(signature),
2492 Error::<T>::SignatureAlreadySubmitted
2493 );
2494 if let Some(signature_pointer) = PayloadSignatureRegistryPointer::<T>::get() {
2495 ensure!(signature_pointer.newest != *signature, Error::<T>::SignatureAlreadySubmitted);
2496 }
2497
2498 Ok(current_block)
2499 }
2500
2501 fn enqueue_signature(
2524 signature: &MultiSignature,
2525 signature_expires_at: BlockNumberFor<T>,
2526 current_block: BlockNumberFor<T>,
2527 ) -> DispatchResult {
2528 let pointer =
2530 PayloadSignatureRegistryPointer::<T>::get().unwrap_or(SignatureRegistryPointer {
2531 newest: signature.clone(),
2532 newest_expires_at: signature_expires_at,
2533 oldest: signature.clone(),
2534 count: 0,
2535 });
2536
2537 ensure!(
2539 !(pointer.count != 0 && pointer.newest.eq(signature)),
2540 Error::<T>::SignatureAlreadySubmitted
2541 );
2542
2543 let mut oldest: MultiSignature = pointer.oldest.clone();
2545
2546 let is_registry_full: bool = pointer.count == T::MaxSignaturesStored::get().unwrap_or(0);
2548
2549 if is_registry_full {
2551 let (expire_block_number, next_oldest) =
2552 PayloadSignatureRegistryList::<T>::get(pointer.oldest.clone())
2553 .ok_or(Error::<T>::SignatureRegistryCorrupted)?;
2554
2555 ensure!(
2556 current_block.gt(&expire_block_number),
2557 Error::<T>::SignatureRegistryLimitExceeded
2558 );
2559
2560 oldest = next_oldest.clone();
2562
2563 <PayloadSignatureRegistryList<T>>::remove(pointer.oldest);
2564 }
2565
2566 if pointer.count != 0 {
2568 <PayloadSignatureRegistryList<T>>::insert(
2569 pointer.newest,
2570 (pointer.newest_expires_at, signature.clone()),
2571 );
2572 }
2573
2574 PayloadSignatureRegistryPointer::<T>::put(SignatureRegistryPointer {
2576 count: if is_registry_full { pointer.count } else { pointer.count + 1 },
2578 newest: signature.clone(),
2579 newest_expires_at: signature_expires_at,
2580 oldest,
2581 });
2582
2583 Ok(())
2584 }
2585
2586 fn mortality_block_limit(current_block: BlockNumberFor<T>) -> BlockNumberFor<T> {
2590 let mortality_size = T::MortalityWindowSize::get();
2591 current_block + BlockNumberFor::<T>::from(mortality_size)
2592 }
2593
2594 fn validate_cid(in_cid: &[u8]) -> Result<Vec<u8>, DispatchError> {
2601 let cid_str: &str = core::str::from_utf8(in_cid).map_err(|_| Error::<T>::InvalidCid)?;
2603 ensure!(cid_str.len() > 2, Error::<T>::InvalidCid);
2604 ensure!(!cid_str.starts_with("Qm"), Error::<T>::UnsupportedCidVersion);
2606
2607 let cid_b = multibase::decode(cid_str).map_err(|_| Error::<T>::InvalidCid)?.1;
2609 ensure!(Cid::read_bytes(&cid_b[..]).is_ok(), Error::<T>::InvalidCid);
2610
2611 Ok(cid_b)
2612 }
2613
2614 fn update_logo_storage(
2616 payload: &ProviderRegistryEntry<
2617 T::MaxProviderNameSize,
2618 T::MaxLanguageCodeSize,
2619 T::MaxLogoCidSize,
2620 T::MaxLocaleCount,
2621 >,
2622 ) -> DispatchResult {
2623 if !payload.default_logo_250_100_png_cid.is_empty() {
2625 ApprovedLogos::<T>::insert(
2626 payload.default_logo_250_100_png_cid.clone(),
2627 BoundedVec::new(),
2628 );
2629 }
2630
2631 for (_, localized_cid) in &payload.localized_logo_250_100_png_cids {
2633 if !localized_cid.is_empty() {
2634 ApprovedLogos::<T>::insert(localized_cid, BoundedVec::new());
2635 }
2636 }
2637
2638 Ok(())
2639 }
2640
2641 fn remove_logo_storage(
2643 existing_payload: Option<
2644 &ProviderRegistryEntry<
2645 T::MaxProviderNameSize,
2646 T::MaxLanguageCodeSize,
2647 T::MaxLogoCidSize,
2648 T::MaxLocaleCount,
2649 >,
2650 >,
2651 ) -> Result<u32, DispatchError> {
2652 let mut total_logo_removed = 0;
2653 if let Some(payload) = existing_payload {
2654 if !payload.default_logo_250_100_png_cid.is_empty() {
2656 total_logo_removed += 1;
2657 ApprovedLogos::<T>::remove(&payload.default_logo_250_100_png_cid);
2658 }
2659 for (_, localized_cid) in &payload.localized_logo_250_100_png_cids {
2661 if !localized_cid.is_empty() {
2662 total_logo_removed += 1;
2663 ApprovedLogos::<T>::remove(localized_cid);
2664 }
2665 }
2666 }
2667 Ok(total_logo_removed)
2668 }
2669
2670 fn ensure_correct_cids(
2672 payload: &ProviderRegistryEntry<
2673 T::MaxProviderNameSize,
2674 T::MaxLanguageCodeSize,
2675 T::MaxLogoCidSize,
2676 T::MaxLocaleCount,
2677 >,
2678 ) -> DispatchResult {
2679 if !payload.default_logo_250_100_png_cid.is_empty() {
2681 Self::validate_cid(&payload.default_logo_250_100_png_cid)?;
2682 }
2683
2684 for (lang_code, localized_cid) in &payload.localized_logo_250_100_png_cids {
2686 let code_str = core::str::from_utf8(lang_code)
2688 .map_err(|_| Error::<T>::InvalidBCP47LanguageCode)?;
2689
2690 if !Self::is_valid_bcp47(code_str) {
2691 return Err(Error::<T>::InvalidBCP47LanguageCode.into());
2692 }
2693
2694 if !localized_cid.is_empty() {
2696 Self::validate_cid(localized_cid)?;
2697 }
2698 }
2699
2700 for (lang_code, _) in &payload.localized_names {
2702 let code_str = core::str::from_utf8(lang_code)
2704 .map_err(|_| Error::<T>::InvalidBCP47LanguageCode)?;
2705 if !Self::is_valid_bcp47(code_str) {
2706 return Err(Error::<T>::InvalidBCP47LanguageCode.into());
2707 }
2708 }
2709
2710 Ok(())
2711 }
2712
2713 fn is_valid_bcp47(code: &str) -> bool {
2715 if code.is_empty() {
2717 return false;
2718 }
2719 if code.starts_with('-') || code.ends_with('-') || code.contains("--") {
2721 return false;
2722 }
2723 for part in code.split('-') {
2724 let len = part.len();
2725 if len < 2 || !part.chars().all(|c| c.is_ascii_alphanumeric()) {
2726 return false;
2727 }
2728 }
2729 true
2730 }
2731
2732 pub fn get_provider_application_context(
2734 provider_id: ProviderId,
2735 application_id: Option<ApplicationIndex>,
2736 locale: Option<Vec<u8>>,
2737 ) -> Option<ProviderApplicationContext> {
2738 let bounded_locale = locale.and_then(|loc| BoundedVec::try_from(loc).ok());
2739 let provider_or_application_registry = match application_id {
2740 Some(app_id) => ProviderToApplicationRegistry::<T>::get(provider_id, app_id)?,
2741 None => ProviderToRegistryEntryV2::<T>::get(provider_id)?,
2742 };
2743 let default_name = provider_or_application_registry.default_name.to_vec();
2744 let default_logo_cid = provider_or_application_registry.default_logo_250_100_png_cid;
2745 let default_logo_250_100_png_bytes: Option<Vec<u8>> =
2747 ApprovedLogos::<T>::get(default_logo_cid).map(|bv| bv.to_vec());
2748 let mut localized_name: Option<Vec<u8>> = None;
2749 let localized_logo_250_100_png_bytes: Option<Vec<u8>> = bounded_locale.and_then(|locale| {
2751 localized_name = provider_or_application_registry
2753 .localized_names
2754 .get(&locale)
2755 .map(|bv| bv.to_vec());
2756
2757 provider_or_application_registry
2758 .localized_logo_250_100_png_cids
2759 .get(&locale)
2760 .and_then(|cid| ApprovedLogos::<T>::get(cid).map(|bv| bv.to_vec()))
2761 });
2762
2763 Some(ProviderApplicationContext {
2764 default_name,
2765 provider_id,
2766 application_id,
2767 default_logo_250_100_png_bytes,
2768 localized_logo_250_100_png_bytes,
2769 localized_name,
2770 })
2771 }
2772
2773 fn refund_logo_removal_weight_by_count(
2775 total_logos_removed: u32,
2776 base_weight: Weight,
2777 ) -> DispatchResultWithPostInfo {
2778 let max_logos_benchmark_assumed = T::MaxLocaleCount::get();
2780 let removal_over_charged_by =
2781 max_logos_benchmark_assumed.saturating_sub(total_logos_removed);
2782 let weight_per_logo_removal = T::DbWeight::get().writes(1);
2783 let weight_to_refund =
2784 weight_per_logo_removal.saturating_mul(removal_over_charged_by as u64);
2785 let actual_weight_used = base_weight.saturating_sub(weight_to_refund);
2786 Ok(PostDispatchInfo { actual_weight: Some(actual_weight_used), pays_fee: Pays::Yes })
2787 }
2788
2789 fn add_event_to_offchain_index(event: Option<&Event<T>>, msa_id: MessageSourceId) {
2791 #[cfg(not(feature = "no-custom-host-functions"))]
2792 offchain_index_event::<T>(event, msa_id);
2793 }
2794}
2795
2796#[cfg(feature = "runtime-benchmarks")]
2797impl<T: Config> MsaBenchmarkHelper<T::AccountId> for Pallet<T> {
2798 fn set_delegation_relationship(
2800 provider: ProviderId,
2801 delegator: DelegatorId,
2802 intents: Vec<IntentId>,
2803 ) -> DispatchResult {
2804 Self::add_provider(provider, delegator, intents)?;
2805 Ok(())
2806 }
2807
2808 fn add_key(msa_id: MessageSourceId, key: T::AccountId) -> DispatchResult {
2810 Self::add_key(msa_id, &key)?;
2811 Ok(())
2812 }
2813
2814 fn create_msa(keys: T::AccountId) -> Result<MessageSourceId, DispatchError> {
2815 let (msa_id, _) = Self::create_account(keys)?;
2816 Ok(msa_id)
2817 }
2818}
2819
2820#[cfg(feature = "runtime-benchmarks")]
2821impl<T: Config> RegisterProviderBenchmarkHelper for Pallet<T> {
2822 fn create(provider_id: MessageSourceId, name: Vec<u8>) -> DispatchResult {
2824 let name = BoundedVec::<u8, T::MaxProviderNameSize>::try_from(name).expect("error");
2825 let payload = ProviderRegistryEntry {
2826 default_name: name,
2827 localized_names: BoundedBTreeMap::new(),
2828 default_logo_250_100_png_cid: BoundedVec::new(),
2829 localized_logo_250_100_png_cids: BoundedBTreeMap::new(),
2830 };
2831 Self::create_registered_provider(provider_id.into(), payload)?;
2832
2833 Ok(())
2834 }
2835}
2836
2837impl<T: Config> MsaLookup for Pallet<T> {
2838 type AccountId = T::AccountId;
2839
2840 fn get_msa_id(key: &Self::AccountId) -> Option<MessageSourceId> {
2841 Self::get_owner_of(key)
2842 }
2843
2844 fn get_max_msa_id() -> MessageSourceId {
2845 CurrentMsaIdentifierMaximum::<T>::get()
2846 }
2847}
2848
2849impl<T: Config> MsaValidator for Pallet<T> {
2850 type AccountId = T::AccountId;
2851
2852 fn ensure_valid_msa_key(key: &T::AccountId) -> Result<MessageSourceId, DispatchError> {
2853 Self::ensure_valid_msa_key(key)
2854 }
2855}
2856
2857impl<T: Config> ProviderLookup for Pallet<T> {
2858 type BlockNumber = BlockNumberFor<T>;
2859 type MaxGrantsPerDelegation = T::MaxGrantsPerDelegation;
2860 type DelegationId = IntentId;
2861
2862 fn get_delegation_of(
2863 delegator: DelegatorId,
2864 provider: ProviderId,
2865 ) -> Option<Delegation<Self::DelegationId, Self::BlockNumber, Self::MaxGrantsPerDelegation>> {
2866 DelegatorAndProviderToDelegation::<T>::get(delegator, provider)
2867 }
2868}
2869
2870impl<T: Config> DelegationValidator for Pallet<T> {
2871 type BlockNumber = BlockNumberFor<T>;
2872 type MaxGrantsPerDelegation = T::MaxGrantsPerDelegation;
2873 type DelegationIdType = IntentId;
2874
2875 fn ensure_valid_delegation(
2885 provider_id: ProviderId,
2886 delegator_id: DelegatorId,
2887 block_number: Option<Self::BlockNumber>,
2888 ) -> Result<
2889 Delegation<Self::DelegationIdType, Self::BlockNumber, Self::MaxGrantsPerDelegation>,
2890 DispatchError,
2891 > {
2892 let info = DelegatorAndProviderToDelegation::<T>::get(delegator_id, provider_id)
2893 .ok_or(Error::<T>::DelegationNotFound)?;
2894 let current_block = frame_system::Pallet::<T>::block_number();
2895 let requested_block = match block_number {
2896 Some(block_number) => {
2897 ensure!(
2898 current_block >= block_number,
2899 Error::<T>::CannotPredictValidityPastCurrentBlock
2900 );
2901 block_number
2902 },
2903 None => current_block,
2904 };
2905
2906 if info.revoked_at == Self::BlockNumber::zero() {
2907 return Ok(info);
2908 }
2909 ensure!(info.revoked_at >= requested_block, Error::<T>::DelegationRevoked);
2910
2911 Ok(info)
2912 }
2913}
2914
2915impl<T: Config> TargetValidator for Pallet<T> {
2916 fn validate(target: MessageSourceId) -> bool {
2917 Self::is_registered_provider(target)
2918 }
2919}
2920
2921impl<T: Config> GrantValidator<IntentId, BlockNumberFor<T>> for Pallet<T> {
2922 fn ensure_valid_grant(
2931 provider: ProviderId,
2932 delegator: DelegatorId,
2933 intent_id: IntentId,
2934 block_number: BlockNumberFor<T>,
2935 ) -> DispatchResult {
2936 let provider_info = Self::ensure_valid_delegation(provider, delegator, Some(block_number))?;
2937
2938 let permission_revoked_at_block_number = provider_info
2939 .permissions
2940 .get(&intent_id)
2941 .ok_or(Error::<T>::PermissionNotGranted)?;
2942
2943 if *permission_revoked_at_block_number == BlockNumberFor::<T>::zero() {
2944 return Ok(());
2945 }
2946
2947 ensure!(
2948 block_number <= *permission_revoked_at_block_number,
2949 Error::<T>::PermissionNotGranted
2950 );
2951
2952 Ok(())
2953 }
2954}
2955
2956impl<T: Config> MsaKeyProvider for Pallet<T> {
2957 type AccountId = T::AccountId;
2958 fn key_eligible_for_subsidized_addition(
2964 old_key: Self::AccountId,
2965 new_key: Self::AccountId,
2966 msa_id: MessageSourceId,
2967 ) -> bool {
2968 let new_address32 = T::ConvertIntoAccountId32::convert((new_key).clone());
2969 if EthereumAddressMapper::is_ethereum_address(&new_address32) {
2970 if let Some(stored_msa_id) = Self::get_msa_id(&old_key) {
2971 return stored_msa_id == msa_id && PublicKeyCountForMsaId::<T>::get(msa_id).eq(&1u8);
2972 }
2973 }
2974 false
2975 }
2976}
2977
2978#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)]
2983#[scale_info(skip_type_params(T))]
2984pub struct CheckFreeExtrinsicUse<T: Config + Send + Sync>(PhantomData<T>);
2985
2986impl<T: Config + Send + Sync> CheckFreeExtrinsicUse<T> {
2987 pub fn validate_delegation_by_delegator(
2998 account_id: &T::AccountId,
2999 provider_msa_id: &MessageSourceId,
3000 ) -> TransactionValidity {
3001 const TAG_PREFIX: &str = "DelegatorDelegationRevocation";
3002 let delegator_msa_id: DelegatorId = Pallet::<T>::ensure_valid_msa_key(account_id)
3003 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?
3004 .into();
3005 let provider_msa_id = ProviderId(*provider_msa_id);
3006
3007 Pallet::<T>::ensure_valid_delegation(provider_msa_id, delegator_msa_id, None)
3008 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidDelegation as u8))?;
3009 ValidTransaction::with_tag_prefix(TAG_PREFIX).and_provides(account_id).build()
3010 }
3011
3012 pub fn validate_delegation_by_provider(
3023 account_id: &T::AccountId,
3024 delegator_msa_id: &MessageSourceId,
3025 ) -> TransactionValidity {
3026 const TAG_PREFIX: &str = "ProviderDelegationRevocation";
3027
3028 let provider_msa_id: ProviderId = Pallet::<T>::ensure_valid_msa_key(account_id)
3029 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?
3030 .into();
3031 let delegator_msa_id = DelegatorId(*delegator_msa_id);
3032
3033 Pallet::<T>::ensure_valid_delegation(provider_msa_id, delegator_msa_id, None)
3035 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidDelegation as u8))?;
3036 ValidTransaction::with_tag_prefix(TAG_PREFIX).and_provides(account_id).build()
3037 }
3038
3039 pub fn validate_key_delete(
3052 signing_public_key: &T::AccountId,
3053 public_key_to_delete: &T::AccountId,
3054 ) -> TransactionValidity {
3055 const TAG_PREFIX: &str = "KeyRevocation";
3056
3057 ensure!(
3058 signing_public_key != public_key_to_delete,
3059 InvalidTransaction::Custom(ValidityError::InvalidSelfRemoval as u8)
3060 );
3061
3062 let maybe_owner_msa_id: MessageSourceId =
3063 Pallet::<T>::ensure_valid_msa_key(signing_public_key)
3064 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?;
3065
3066 let msa_id_for_key_to_delete: MessageSourceId =
3067 Pallet::<T>::ensure_valid_msa_key(public_key_to_delete)
3068 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?;
3069
3070 ensure!(
3071 maybe_owner_msa_id == msa_id_for_key_to_delete,
3072 InvalidTransaction::Custom(ValidityError::NotKeyOwner as u8)
3073 );
3074
3075 ValidTransaction::with_tag_prefix(TAG_PREFIX)
3076 .and_provides(signing_public_key)
3077 .build()
3078 }
3079
3080 pub fn ensure_msa_can_retire(account_id: &T::AccountId) -> TransactionValidity {
3096 const TAG_PREFIX: &str = "MSARetirement";
3097 let msa_id = Pallet::<T>::ensure_valid_msa_key(account_id)
3098 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?;
3099
3100 ensure!(
3101 !Pallet::<T>::is_registered_provider(msa_id),
3102 InvalidTransaction::Custom(
3103 ValidityError::InvalidRegisteredProviderCannotBeRetired as u8
3104 )
3105 );
3106
3107 let msa_handle = T::HandleProvider::get_handle_for_msa(msa_id);
3108 ensure!(
3109 msa_handle.is_none(),
3110 InvalidTransaction::Custom(ValidityError::HandleNotRetired as u8)
3111 );
3112
3113 let key_count = PublicKeyCountForMsaId::<T>::get(msa_id);
3114 ensure!(
3115 key_count == 1,
3116 InvalidTransaction::Custom(ValidityError::InvalidMoreThanOneKeyExists as u8)
3117 );
3118
3119 let delegator_id = DelegatorId(msa_id);
3120 let has_active_delegations: bool = DelegatorAndProviderToDelegation::<T>::iter_key_prefix(
3121 delegator_id,
3122 )
3123 .any(|provider_id| {
3124 Pallet::<T>::ensure_valid_delegation(provider_id, delegator_id, None).is_ok()
3125 });
3126
3127 ensure!(
3128 !has_active_delegations,
3129 InvalidTransaction::Custom(ValidityError::InvalidNonZeroProviderDelegations as u8)
3130 );
3131
3132 let msa_address = Pallet::<T>::msa_id_to_eth_address(msa_id);
3134
3135 let mut bytes = &EthereumAddressMapper::to_bytes32(&msa_address.0)[..];
3137 let msa_account_id = T::AccountId::decode(&mut bytes).map_err(|_| {
3138 log::error!("Failed to decode MSA account ID from Ethereum address");
3139 InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8)
3140 })?;
3141
3142 let msa_balance = T::Currency::reducible_balance(
3144 &msa_account_id,
3145 Preservation::Expendable,
3146 Fortitude::Polite,
3147 );
3148 ensure!(
3149 msa_balance == Zero::zero(),
3150 InvalidTransaction::Custom(ValidityError::InvalidMsaHoldingTokenCannotBeRetired as u8)
3151 );
3152
3153 ValidTransaction::with_tag_prefix(TAG_PREFIX).and_provides(account_id).build()
3154 }
3155
3156 pub fn validate_msa_token_withdrawal(
3171 receiver_account_id: &T::AccountId,
3172 msa_owner_public_key: &T::AccountId,
3173 msa_owner_proof: &MultiSignature,
3174 authorization_payload: &AuthorizedKeyData<T>,
3175 ) -> TransactionValidity {
3176 const TAG_PREFIX: &str = "MsaTokenWithdrawal";
3177
3178 ensure!(
3179 *receiver_account_id == authorization_payload.authorized_public_key,
3180 InvalidTransaction::Custom(ValidityError::NotKeyOwner as u8)
3181 );
3182
3183 ensure!(
3184 authorization_payload.discriminant == PayloadTypeDiscriminator::AuthorizedKeyData,
3185 InvalidTransaction::Custom(ValidityError::MsaOwnershipInvalidSignature as u8)
3186 );
3187 ensure!(
3188 Pallet::<T>::verify_signature(
3189 msa_owner_proof,
3190 msa_owner_public_key,
3191 authorization_payload
3192 ),
3193 InvalidTransaction::Custom(ValidityError::MsaOwnershipInvalidSignature as u8)
3194 );
3195
3196 ensure!(
3198 !PublicKeyToMsaId::<T>::contains_key(receiver_account_id),
3199 InvalidTransaction::Custom(ValidityError::IneligibleOrigin as u8)
3200 );
3201
3202 Pallet::<T>::check_signature_against_registry(
3203 msa_owner_proof,
3204 authorization_payload.expiration,
3205 )
3206 .map_err(|_| {
3207 InvalidTransaction::Custom(ValidityError::MsaOwnershipInvalidSignature as u8)
3208 })?;
3209
3210 let msa_id = authorization_payload.msa_id;
3211
3212 Pallet::<T>::ensure_msa_owner(msa_owner_public_key, msa_id)
3213 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?;
3214
3215 let msa_address = Pallet::<T>::msa_id_to_eth_address(msa_id);
3217
3218 let mut bytes = &EthereumAddressMapper::to_bytes32(&msa_address.0)[..];
3220 let msa_account_id = T::AccountId::decode(&mut bytes).map_err(|_| {
3221 log::error!("Failed to decode MSA account ID from Ethereum address");
3222 InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8)
3223 })?;
3224
3225 let msa_balance = T::Currency::reducible_balance(
3227 &msa_account_id,
3228 Preservation::Expendable,
3229 Fortitude::Polite,
3230 );
3231 ensure!(
3232 msa_balance > Zero::zero(),
3233 InvalidTransaction::Custom(ValidityError::InsufficientBalanceToWithdraw as u8)
3234 );
3235 ValidTransaction::with_tag_prefix(TAG_PREFIX)
3236 .and_provides(receiver_account_id)
3237 .build()
3238 }
3239}
3240
3241pub enum ValidityError {
3243 InvalidDelegation,
3245 InvalidMsaKey,
3247 InvalidRegisteredProviderCannotBeRetired,
3249 InvalidMoreThanOneKeyExists,
3251 InvalidSelfRemoval,
3253 NotKeyOwner,
3255 InvalidNonZeroProviderDelegations,
3257 HandleNotRetired,
3259 MsaOwnershipInvalidSignature,
3261 InsufficientBalanceToWithdraw,
3263 IneligibleOrigin,
3265 InvalidMsaHoldingTokenCannotBeRetired,
3267}
3268
3269impl<T: Config + Send + Sync> CheckFreeExtrinsicUse<T> {
3270 pub fn new() -> Self {
3272 Self(PhantomData)
3273 }
3274}
3275
3276impl<T: Config + Send + Sync> core::fmt::Debug for CheckFreeExtrinsicUse<T> {
3277 #[cfg(feature = "std")]
3278 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
3279 write!(f, "CheckFreeExtrinsicUse<{:?}>", self.0)
3280 }
3281 #[cfg(not(feature = "std"))]
3282 fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
3283 Ok(())
3284 }
3285}
3286
3287#[derive(RuntimeDebugNoBound)]
3289pub enum Val {
3290 Valid,
3292 Refund(Weight),
3294}
3295
3296#[derive(RuntimeDebugNoBound)]
3298pub enum Pre {
3299 Valid,
3301 Refund(Weight),
3303}
3304
3305impl<T: Config + Send + Sync> TransactionExtension<T::RuntimeCall> for CheckFreeExtrinsicUse<T>
3306where
3307 T::RuntimeCall: Dispatchable<Info = DispatchInfo> + IsSubType<Call<T>>,
3308 <T as frame_system::Config>::RuntimeOrigin: AsSystemOriginSigner<T::AccountId> + Clone,
3309{
3310 const IDENTIFIER: &'static str = "CheckFreeExtrinsicUse";
3311 type Implicit = ();
3312 type Val = Val;
3313 type Pre = Pre;
3314
3315 fn weight(&self, call: &T::RuntimeCall) -> Weight {
3316 match call.is_sub_type() {
3317 Some(Call::revoke_delegation_by_provider { .. }) =>
3318 T::WeightInfo::check_free_extrinsic_use_revoke_delegation_by_provider(),
3319 Some(Call::revoke_delegation_by_delegator { .. }) =>
3320 T::WeightInfo::check_free_extrinsic_use_revoke_delegation_by_delegator(),
3321 Some(Call::delete_msa_public_key { .. }) =>
3322 T::WeightInfo::check_free_extrinsic_use_delete_msa_public_key(),
3323 Some(Call::retire_msa { .. }) => T::WeightInfo::check_free_extrinsic_use_retire_msa(),
3324 Some(Call::withdraw_tokens { .. }) =>
3325 T::WeightInfo::check_free_extrinsic_use_withdraw_tokens(),
3326 _ => Weight::zero(),
3327 }
3328 }
3329
3330 fn validate(
3331 &self,
3332 origin: <T as frame_system::Config>::RuntimeOrigin,
3333 call: &T::RuntimeCall,
3334 _info: &DispatchInfoOf<T::RuntimeCall>,
3335 _len: usize,
3336 _self_implicit: Self::Implicit,
3337 _inherited_implication: &impl Encode,
3338 _source: TransactionSource,
3339 ) -> ValidateResult<Self::Val, T::RuntimeCall> {
3340 let weight = self.weight(call);
3341 let Some(who) = origin.as_system_origin_signer() else {
3342 return Ok((ValidTransaction::default(), Val::Refund(weight), origin));
3343 };
3344 let validity = match call.is_sub_type() {
3345 Some(Call::revoke_delegation_by_provider { delegator, .. }) =>
3346 Self::validate_delegation_by_provider(who, delegator),
3347 Some(Call::revoke_delegation_by_delegator { provider_msa_id, .. }) =>
3348 Self::validate_delegation_by_delegator(who, provider_msa_id),
3349 Some(Call::delete_msa_public_key { public_key_to_delete, .. }) =>
3350 Self::validate_key_delete(who, public_key_to_delete),
3351 Some(Call::retire_msa { .. }) => Self::ensure_msa_can_retire(who),
3352 Some(Call::withdraw_tokens {
3353 msa_owner_public_key,
3354 msa_owner_proof,
3355 authorization_payload,
3356 }) => Self::validate_msa_token_withdrawal(
3357 who,
3358 msa_owner_public_key,
3359 msa_owner_proof,
3360 authorization_payload,
3361 ),
3362 _ => Ok(Default::default()),
3363 };
3364 validity.map(|v| (v, Val::Valid, origin))
3365 }
3366
3367 fn prepare(
3368 self,
3369 val: Self::Val,
3370 _origin: &<T as frame_system::Config>::RuntimeOrigin,
3371 _call: &T::RuntimeCall,
3372 _info: &DispatchInfoOf<T::RuntimeCall>,
3373 _len: usize,
3374 ) -> Result<Self::Pre, TransactionValidityError> {
3375 match val {
3376 Val::Valid => Ok(Pre::Valid),
3377 Val::Refund(w) => Ok(Pre::Refund(w)),
3378 }
3379 }
3380
3381 fn post_dispatch_details(
3382 pre: Self::Pre,
3383 _info: &DispatchInfo,
3384 _post_info: &PostDispatchInfoOf<T::RuntimeCall>,
3385 _len: usize,
3386 _result: &sp_runtime::DispatchResult,
3387 ) -> Result<Weight, TransactionValidityError> {
3388 match pre {
3389 Pre::Valid => Ok(Weight::zero()),
3390 Pre::Refund(w) => Ok(w),
3391 }
3392 }
3393}