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
85#[cfg(not(feature = "no-custom-host-functions"))]
87pub mod offchain_storage;
88use crate::types::{LogoCid, PayloadTypeDiscriminator, RecoveryHash};
89#[cfg(not(feature = "no-custom-host-functions"))]
90pub use offchain_storage::*;
91
92#[cfg(feature = "runtime-benchmarks")]
93mod benchmarking;
94
95#[cfg(test)]
96mod tests;
97
98pub mod types;
99
100pub mod weights;
101
102#[allow(missing_docs)]
104pub mod migration;
105
106#[frame_support::pallet]
107pub mod pallet {
108 use crate::types::RecoveryHash;
109
110 use super::*;
111
112 #[pallet::config]
113 pub trait Config: frame_system::Config {
114 #[allow(deprecated)]
116 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
117
118 type WeightInfo: WeightInfo;
120
121 type ConvertIntoAccountId32: Convert<Self::AccountId, AccountId32>;
123
124 #[pallet::constant]
126 type MaxPublicKeysPerMsa: Get<u8>;
127
128 #[pallet::constant]
130 type MaxGrantsPerDelegation: Get<u32>;
131
132 #[pallet::constant]
134 type MaxProviderNameSize: Get<u32> + Clone + Debug + PartialEq + Eq;
135
136 type SchemaValidator: SchemaValidator<SchemaId>;
138
139 type HandleProvider: HandleProvider;
141
142 #[pallet::constant]
144 type MortalityWindowSize: Get<u32>;
145
146 #[pallet::constant]
148 type MaxSignaturesStored: Get<Option<u32>>;
149
150 type CreateProviderViaGovernanceOrigin: EnsureOrigin<Self::RuntimeOrigin>;
152
153 type RecoveryProviderApprovalOrigin: EnsureOrigin<Self::RuntimeOrigin>;
155
156 type Proposal: Parameter
158 + Dispatchable<RuntimeOrigin = Self::RuntimeOrigin, PostInfo = PostDispatchInfo>
159 + From<Call<Self>>;
160
161 type ProposalProvider: ProposalProvider<Self::AccountId, Self::Proposal>;
163
164 type Currency: Mutate<Self::AccountId> + InspectFungible<Self::AccountId>;
166
167 #[pallet::constant]
169 type MaxLanguageCodeSize: Get<u32> + Clone + Debug + PartialEq + Eq;
170
171 #[pallet::constant]
173 type MaxLogoSize: Get<u32> + Clone + Debug + PartialEq + Eq;
174
175 #[pallet::constant]
177 type MaxLogoCidSize: Get<u32> + Clone + Debug + PartialEq + Eq;
178
179 #[pallet::constant]
181 type MaxLocaleCount: Get<u32> + Clone + Debug + PartialEq + Eq;
182 }
183
184 pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(2);
186
187 #[pallet::pallet]
188 #[pallet::storage_version(STORAGE_VERSION)]
189 pub struct Pallet<T>(_);
190
191 #[pallet::storage]
196 pub type CurrentMsaIdentifierMaximum<T> = StorageValue<_, MessageSourceId, ValueQuery>;
197
198 #[pallet::storage]
202 pub type DelegatorAndProviderToDelegation<T: Config> = StorageDoubleMap<
203 _,
204 Twox64Concat,
205 DelegatorId,
206 Twox64Concat,
207 ProviderId,
208 Delegation<IntentId, BlockNumberFor<T>, T::MaxGrantsPerDelegation>,
209 OptionQuery,
210 >;
211
212 #[pallet::storage]
216 pub type RecoveryProviders<T: Config> =
217 StorageMap<_, Twox64Concat, ProviderId, bool, OptionQuery>;
218
219 #[deprecated(
223 note = "Use ProviderToRegistryEntryV2 instead, this will removed from frequency version 1.17.6"
224 )]
225 #[pallet::storage]
226 pub type ProviderToRegistryEntry<T: Config> = StorageMap<
227 _,
228 Twox64Concat,
229 ProviderId,
230 crate::migration::v1::ProviderRegistryEntry<T::MaxProviderNameSize>,
231 OptionQuery,
232 >;
233
234 #[pallet::storage]
238 pub type ProviderToRegistryEntryV2<T: Config> = StorageMap<
239 _,
240 Twox64Concat,
241 ProviderId,
242 ProviderRegistryEntry<
243 T::MaxProviderNameSize,
244 T::MaxLanguageCodeSize,
245 T::MaxLogoCidSize,
246 T::MaxLocaleCount,
247 >,
248 OptionQuery,
249 >;
250
251 #[pallet::storage]
255 pub type PublicKeyToMsaId<T: Config> =
256 StorageMap<_, Twox64Concat, T::AccountId, MessageSourceId, OptionQuery>;
257
258 #[pallet::storage]
262 pub(super) type PublicKeyCountForMsaId<T: Config> =
263 StorageMap<_, Twox64Concat, MessageSourceId, u8, ValueQuery>;
264
265 #[pallet::storage]
282 pub(super) type PayloadSignatureRegistryList<T: Config> = StorageMap<
283 _, Twox64Concat, MultiSignature, (BlockNumberFor<T>, MultiSignature), OptionQuery, GetDefault, T::MaxSignaturesStored, >;
291
292 #[pallet::storage]
296 pub(super) type PayloadSignatureRegistryPointer<T: Config> =
297 StorageValue<_, SignatureRegistryPointer<BlockNumberFor<T>>>;
298
299 #[pallet::storage]
302 #[pallet::whitelist_storage]
303 pub(super) type OffchainIndexEventCount<T: Config> = StorageValue<_, u16, ValueQuery>;
304
305 #[pallet::storage]
309 pub type MsaIdToRecoveryCommitment<T: Config> =
310 StorageMap<_, Twox64Concat, MessageSourceId, RecoveryCommitment, OptionQuery>;
311
312 #[pallet::storage]
316 #[pallet::getter(fn approved_logos)]
317 pub type ApprovedLogos<T: Config> =
318 StorageMap<_, Twox64Concat, LogoCid<T>, BoundedVec<u8, T::MaxLogoSize>, OptionQuery>;
319
320 #[pallet::storage]
323 #[pallet::getter(fn next_application_index)]
324 pub(super) type NextApplicationIndex<T: Config> =
325 StorageMap<_, Twox64Concat, ProviderId, ApplicationIndex, ValueQuery>;
326
327 #[pallet::storage]
330 #[pallet::getter(fn provider_applications)]
331 pub(super) type ProviderToApplicationRegistry<T: Config> = StorageDoubleMap<
332 _,
333 Twox64Concat,
334 ProviderId,
335 Twox64Concat,
336 ApplicationIndex,
337 ApplicationContext<
338 T::MaxProviderNameSize,
339 T::MaxLanguageCodeSize,
340 T::MaxLogoCidSize,
341 T::MaxLocaleCount,
342 >,
343 OptionQuery,
344 >;
345
346 #[pallet::event]
347 #[pallet::generate_deposit(pub (super) fn deposit_event)]
348 pub enum Event<T: Config> {
349 MsaCreated {
351 msa_id: MessageSourceId,
353
354 key: T::AccountId,
356 },
357 PublicKeyAdded {
359 msa_id: MessageSourceId,
361
362 key: T::AccountId,
364 },
365 PublicKeyDeleted {
367 key: T::AccountId,
369 },
370 DelegationGranted {
372 provider_id: ProviderId,
374
375 delegator_id: DelegatorId,
377 },
378 ProviderCreated {
380 provider_id: ProviderId,
382 },
383 DelegationRevoked {
385 provider_id: ProviderId,
387
388 delegator_id: DelegatorId,
390 },
391 MsaRetired {
393 msa_id: MessageSourceId,
395 },
396 DelegationUpdated {
398 provider_id: ProviderId,
400
401 delegator_id: DelegatorId,
403 },
404 RecoveryCommitmentAdded {
406 who: T::AccountId,
408
409 msa_id: MessageSourceId,
411
412 recovery_commitment: RecoveryCommitment,
414 },
415 RecoveryProviderApproved {
417 provider_id: ProviderId,
419 },
420 RecoveryProviderRemoved {
422 provider_id: ProviderId,
424 },
425 AccountRecovered {
427 msa_id: MessageSourceId,
429 recovery_provider: ProviderId,
431 new_control_key: T::AccountId,
433 },
434 RecoveryCommitmentInvalidated {
436 msa_id: MessageSourceId,
438 recovery_commitment: RecoveryCommitment,
440 },
441 ApplicationCreated {
443 provider_id: ProviderId,
445 application_id: ApplicationIndex,
447 },
448 ApplicationContextUpdated {
450 provider_id: ProviderId,
452 application_id: Option<ApplicationIndex>,
454 },
455 ProviderUpdated {
457 provider_id: ProviderId,
459 },
460 }
461
462 #[pallet::error]
463 pub enum Error<T> {
464 KeyAlreadyRegistered,
466
467 MsaIdOverflow,
469
470 MsaOwnershipInvalidSignature,
472
473 NotMsaOwner,
475
476 InvalidSignature,
478
479 NotKeyOwner,
481
482 NoKeyExists,
484
485 KeyLimitExceeded,
487
488 InvalidSelfRemoval,
490
491 InvalidSelfProvider,
493
494 InvalidSchemaId,
496
497 DuplicateProvider,
499
500 AddProviderSignatureVerificationFailed,
502
503 UnauthorizedDelegator,
505
506 UnauthorizedProvider,
508
509 DelegationRevoked,
511
512 DelegationNotFound,
514
515 DuplicateProviderRegistryEntry,
517
518 ExceedsMaxProviderNameSize,
520
521 ExceedsMaxSchemaGrantsPerDelegation,
523
524 SchemaNotGranted,
526
527 ProviderNotRegistered,
529
530 ProofHasExpired,
532
533 ProofNotYetValid,
535
536 SignatureAlreadySubmitted,
538
539 NewKeyOwnershipInvalidSignature,
541
542 CannotPredictValidityPastCurrentBlock,
544
545 SignatureRegistryLimitExceeded,
547
548 SignatureRegistryCorrupted,
550
551 InsufficientBalanceToWithdraw,
553
554 UnexpectedTokenTransferError,
556
557 NotAuthorizedRecoveryProvider,
559
560 InvalidRecoveryCommitment,
562
563 NoRecoveryCommitment,
565
566 InvalidCid,
568
569 UnsupportedCidVersion,
571
572 InvalidBCP47LanguageCode,
574
575 DuplicateApplicationRegistryEntry,
577
578 LogoCidNotApproved,
580
581 InvalidLogoBytes,
583
584 ApplicationNotFound,
586
587 ExceedsMaxGrantsPerDelegation,
589
590 InvalidIntentId,
592
593 PermissionNotGranted,
595 }
596
597 impl<T: Config> BlockNumberProvider for Pallet<T> {
598 type BlockNumber = BlockNumberFor<T>;
599
600 fn current_block_number() -> Self::BlockNumber {
601 frame_system::Pallet::<T>::block_number()
602 }
603 }
604
605 #[pallet::hooks]
606 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
607 fn on_initialize(_block_number: BlockNumberFor<T>) -> Weight {
608 <OffchainIndexEventCount<T>>::set(0u16);
609 let migration_weight = crate::migration::v2::on_initialize_migration::<T>(); T::DbWeight::get().reads_writes(1u64, 1u64).saturating_add(migration_weight)
611 }
612
613 #[cfg(not(feature = "no-custom-host-functions"))]
614 fn offchain_worker(block_number: BlockNumberFor<T>) {
615 log::info!("Running offchain workers! {block_number:?}");
616 do_offchain_worker::<T>(block_number)
617 }
618 }
619
620 #[pallet::call]
621 impl<T: Config> Pallet<T> {
622 #[pallet::call_index(0)]
632 #[pallet::weight(T::WeightInfo::create())]
633 pub fn create(origin: OriginFor<T>) -> DispatchResult {
634 let public_key = ensure_signed(origin)?;
635
636 let (new_msa_id, new_public_key) = Self::create_account(public_key)?;
637
638 let event = Event::MsaCreated { msa_id: new_msa_id, key: new_public_key };
639 Self::add_event_to_offchain_index(Some(&event), new_msa_id);
640 Self::deposit_event(event);
641 Ok(())
642 }
643
644 #[pallet::call_index(1)]
666 #[pallet::weight(T::WeightInfo::create_sponsored_account_with_delegation(
667 add_provider_payload.intent_ids.len() as u32
668 ))]
669 pub fn create_sponsored_account_with_delegation(
670 origin: OriginFor<T>,
671 delegator_key: T::AccountId,
672 proof: MultiSignature,
673 add_provider_payload: AddProvider,
674 ) -> DispatchResult {
675 let provider_key = ensure_signed(origin)?;
676
677 ensure!(
678 Self::verify_signature(&proof, &delegator_key, &add_provider_payload),
679 Error::<T>::InvalidSignature
680 );
681
682 Self::register_signature(&proof, add_provider_payload.expiration.into())?;
683
684 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
685 ensure!(
686 add_provider_payload.authorized_msa_id == provider_msa_id,
687 Error::<T>::UnauthorizedProvider
688 );
689
690 ensure!(
692 Self::is_registered_provider(provider_msa_id),
693 Error::<T>::ProviderNotRegistered
694 );
695
696 let (new_delegator_msa_id, new_delegator_public_key) =
697 Self::create_account(delegator_key)?;
698 Self::add_provider(
699 ProviderId(provider_msa_id),
700 DelegatorId(new_delegator_msa_id),
701 add_provider_payload.intent_ids,
702 )?;
703 let event =
704 Event::MsaCreated { msa_id: new_delegator_msa_id, key: new_delegator_public_key };
705 Self::add_event_to_offchain_index(Some(&event), new_delegator_msa_id);
706 Self::deposit_event(event);
707 Self::deposit_event(Event::DelegationGranted {
708 delegator_id: DelegatorId(new_delegator_msa_id),
709 provider_id: ProviderId(provider_msa_id),
710 });
711 Ok(())
712 }
713
714 #[pallet::call_index(2)]
725 #[pallet::weight(T::WeightInfo::create_provider())]
726 pub fn create_provider(origin: OriginFor<T>, provider_name: Vec<u8>) -> DispatchResult {
727 let provider_key = ensure_signed(origin)?;
728 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
729 let bounded_name: BoundedVec<u8, T::MaxProviderNameSize> =
730 provider_name.try_into().map_err(|_| Error::<T>::ExceedsMaxProviderNameSize)?;
731
732 let entry = ProviderRegistryEntry { default_name: bounded_name, ..Default::default() };
733 Self::ensure_correct_cids(&entry)?;
734 Self::upsert_provider_for(provider_msa_id, entry, false)?;
735 Self::deposit_event(Event::ProviderCreated {
736 provider_id: ProviderId(provider_msa_id),
737 });
738 Ok(())
739 }
740
741 #[pallet::call_index(3)]
762 #[pallet::weight(T::WeightInfo::grant_delegation(add_provider_payload.intent_ids.len() as u32))]
763 pub fn grant_delegation(
764 origin: OriginFor<T>,
765 delegator_key: T::AccountId,
766 proof: MultiSignature,
767 add_provider_payload: AddProvider,
768 ) -> DispatchResult {
769 let provider_key = ensure_signed(origin)?;
770
771 ensure!(
772 Self::verify_signature(&proof, &delegator_key, &add_provider_payload),
773 Error::<T>::AddProviderSignatureVerificationFailed
774 );
775
776 Self::register_signature(&proof, add_provider_payload.expiration.into())?;
777 let (provider_id, delegator_id) =
778 Self::ensure_valid_registered_provider(&delegator_key, &provider_key)?;
779
780 ensure!(
781 add_provider_payload.authorized_msa_id == provider_id.0,
782 Error::<T>::UnauthorizedDelegator
783 );
784
785 Self::upsert_intent_permissions(
786 provider_id,
787 delegator_id,
788 add_provider_payload.intent_ids,
789 )?;
790 Self::deposit_event(Event::DelegationGranted { delegator_id, provider_id });
791
792 Ok(())
793 }
794
795 #[pallet::call_index(4)]
807 #[pallet::weight((T::WeightInfo::revoke_delegation_by_delegator(), DispatchClass::Normal, Pays::No))]
808 pub fn revoke_delegation_by_delegator(
809 origin: OriginFor<T>,
810 #[pallet::compact] provider_msa_id: MessageSourceId,
811 ) -> DispatchResult {
812 let who = ensure_signed(origin)?;
813
814 match PublicKeyToMsaId::<T>::get(&who) {
815 Some(delegator_msa_id) => {
816 let delegator_id = DelegatorId(delegator_msa_id);
817 let provider_id = ProviderId(provider_msa_id);
818 Self::revoke_provider(provider_id, delegator_id)?;
819 Self::deposit_event(Event::DelegationRevoked { delegator_id, provider_id });
820 },
821 None => {
822 log::error!(
823 "TransactionExtension did not catch invalid MSA for account {who:?}, "
824 );
825 },
826 }
827
828 Ok(())
829 }
830
831 #[pallet::call_index(5)]
857 #[pallet::weight(T::WeightInfo::add_public_key_to_msa())]
858 pub fn add_public_key_to_msa(
859 origin: OriginFor<T>,
860 msa_owner_public_key: T::AccountId,
861 msa_owner_proof: MultiSignature,
862 new_key_owner_proof: MultiSignature,
863 add_key_payload: AddKeyData<T>,
864 ) -> DispatchResult {
865 let _ = ensure_signed(origin)?;
866
867 ensure!(
868 Self::verify_signature(&msa_owner_proof, &msa_owner_public_key, &add_key_payload),
869 Error::<T>::MsaOwnershipInvalidSignature
870 );
871
872 ensure!(
873 Self::verify_signature(
874 &new_key_owner_proof,
875 &add_key_payload.new_public_key,
876 &add_key_payload
877 ),
878 Error::<T>::NewKeyOwnershipInvalidSignature
879 );
880
881 Self::register_signature(&msa_owner_proof, add_key_payload.expiration)?;
882 Self::register_signature(&new_key_owner_proof, add_key_payload.expiration)?;
883
884 let msa_id = add_key_payload.msa_id;
885
886 Self::ensure_msa_owner(&msa_owner_public_key, msa_id)?;
887
888 Self::add_key(msa_id, &add_key_payload.new_public_key.clone())?;
889
890 let event =
891 Event::PublicKeyAdded { msa_id, key: add_key_payload.new_public_key.clone() };
892 Self::add_event_to_offchain_index(Some(&event), msa_id);
893 Self::deposit_event(event);
894
895 Ok(())
896 }
897
898 #[pallet::call_index(6)]
913 #[pallet::weight((T::WeightInfo::delete_msa_public_key(), DispatchClass::Normal, Pays::No))]
914 pub fn delete_msa_public_key(
915 origin: OriginFor<T>,
916 public_key_to_delete: T::AccountId,
917 ) -> DispatchResult {
918 let who = ensure_signed(origin)?;
919
920 match PublicKeyToMsaId::<T>::get(&who) {
921 Some(who_msa_id) => {
922 Self::delete_key_for_msa(who_msa_id, &public_key_to_delete)?;
923
924 let event = Event::PublicKeyDeleted { key: public_key_to_delete };
926 Self::add_event_to_offchain_index(Some(&event), who_msa_id);
927 Self::deposit_event(event);
928 },
929 None => {
930 log::error!(
931 "TransactionExtension did not catch invalid MSA for account {who:?}"
932 );
933 },
934 }
935 Ok(())
936 }
937
938 #[pallet::call_index(7)]
950 #[pallet::weight((T::WeightInfo::revoke_delegation_by_provider(), DispatchClass::Normal, Pays::No))]
951 pub fn revoke_delegation_by_provider(
952 origin: OriginFor<T>,
953 #[pallet::compact] delegator: MessageSourceId,
954 ) -> DispatchResult {
955 let who = ensure_signed(origin)?;
956
957 match PublicKeyToMsaId::<T>::get(&who) {
961 Some(msa_id) => {
962 let provider_id = ProviderId(msa_id);
963 let delegator_id = DelegatorId(delegator);
964 Self::revoke_provider(provider_id, delegator_id)?;
965 Self::deposit_event(Event::DelegationRevoked { provider_id, delegator_id })
966 },
967 None => {
968 log::error!(
969 "TransactionExtension did not catch invalid MSA for account {who:?}"
970 );
971 },
972 }
973
974 Ok(())
975 }
976
977 #[pallet::call_index(10)]
999 #[pallet::weight((T::WeightInfo::retire_msa(), DispatchClass::Normal, Pays::No))]
1000 pub fn retire_msa(origin: OriginFor<T>) -> DispatchResult {
1001 let who = ensure_signed(origin)?;
1003
1004 match PublicKeyToMsaId::<T>::get(&who) {
1007 Some(msa_id) => {
1008 Self::delete_key_for_msa(msa_id, &who)?;
1009 let event = Event::PublicKeyDeleted { key: who };
1010 Self::add_event_to_offchain_index(Some(&event), msa_id);
1011 Self::deposit_event(event);
1012 Self::deposit_event(Event::MsaRetired { msa_id });
1013 },
1014 None => {
1015 log::error!(
1016 "TransactionExtension did not catch invalid MSA for account {who:?}"
1017 );
1018 },
1019 }
1020 Ok(())
1021 }
1022
1023 #[pallet::call_index(11)]
1029 #[pallet::weight(T::WeightInfo::propose_to_be_provider_v2())]
1030 #[allow(deprecated)]
1031 #[deprecated(
1032 note = "please use `propose_to_be_provider_v2`, which supports additional provider metadata"
1033 )]
1034 pub fn propose_to_be_provider(
1035 origin: OriginFor<T>,
1036 provider_name: Vec<u8>,
1037 ) -> DispatchResult {
1038 let bounded_name: BoundedVec<u8, T::MaxProviderNameSize> =
1039 provider_name.try_into().map_err(|_| Error::<T>::ExceedsMaxProviderNameSize)?;
1040
1041 let entry = ProviderRegistryEntry { default_name: bounded_name, ..Default::default() };
1042
1043 Self::propose_to_be_provider_v2(origin, entry)
1044 }
1045
1046 #[pallet::call_index(12)]
1056 #[pallet::weight(T::WeightInfo::create_provider_via_governance_v2(0u32, 0u32))]
1057 #[allow(deprecated)]
1058 #[deprecated(
1059 note = "please use `create_provider_via_governance_v2`, which supports additional provider metadata"
1060 )]
1061 pub fn create_provider_via_governance(
1062 origin: OriginFor<T>,
1063 provider_key: T::AccountId,
1064 provider_name: Vec<u8>,
1065 ) -> DispatchResult {
1066 let bounded_name: BoundedVec<u8, T::MaxProviderNameSize> =
1067 provider_name.try_into().map_err(|_| Error::<T>::ExceedsMaxProviderNameSize)?;
1068
1069 let entry = ProviderRegistryEntry { default_name: bounded_name, ..Default::default() };
1070
1071 Self::create_provider_via_governance_v2(origin, provider_key, entry)
1072 }
1073
1074 #[pallet::call_index(13)]
1076 #[pallet::weight(T::WeightInfo::reindex_offchain())]
1077 pub fn reindex_offchain(
1078 origin: OriginFor<T>,
1079 event: OffchainReplayEvent<T>,
1080 ) -> DispatchResult {
1081 let _ = ensure_signed(origin)?;
1082 match event {
1083 OffchainReplayEvent::MsaPallet(MsaOffchainReplayEvent::KeyReIndex {
1084 msa_id,
1085 index_key,
1086 }) => {
1087 match index_key {
1089 Some(key) => {
1090 let event = Event::PublicKeyAdded { msa_id, key };
1091 Self::add_event_to_offchain_index(Some(&event), msa_id);
1092 },
1093 None => {
1094 Self::add_event_to_offchain_index(None, msa_id);
1095 },
1096 }
1097 },
1098 }
1099
1100 Ok(())
1101 }
1102
1103 #[pallet::call_index(14)]
1124 #[pallet::weight((T::WeightInfo::withdraw_tokens(), DispatchClass::Normal, Pays::No))]
1125 pub fn withdraw_tokens(
1126 origin: OriginFor<T>,
1127 _msa_owner_public_key: T::AccountId,
1128 msa_owner_proof: MultiSignature,
1129 authorization_payload: AuthorizedKeyData<T>,
1130 ) -> DispatchResult {
1131 let public_key = ensure_signed(origin)?;
1132
1133 Self::register_signature(&msa_owner_proof, authorization_payload.expiration)?;
1134
1135 let msa_id = authorization_payload.msa_id;
1136
1137 let msa_address = Self::msa_id_to_eth_address(msa_id);
1139
1140 let mut bytes = &EthereumAddressMapper::to_bytes32(&msa_address.0)[..];
1142 let msa_account_id = T::AccountId::decode(&mut bytes).map_err(|_| {
1143 log::error!("Failed to decode MSA account ID from Ethereum address");
1144 Error::<T>::NoKeyExists
1145 })?;
1146
1147 let msa_balance = T::Currency::reducible_balance(
1149 &msa_account_id,
1150 Preservation::Expendable,
1151 Fortitude::Polite,
1152 );
1153 ensure!(msa_balance > Zero::zero(), Error::<T>::InsufficientBalanceToWithdraw);
1154
1155 let result = <T as pallet::Config>::Currency::transfer(
1157 &msa_account_id,
1158 &public_key,
1159 msa_balance,
1160 Preservation::Expendable,
1161 );
1162 ensure!(result.is_ok(), Error::<T>::UnexpectedTokenTransferError);
1163
1164 Ok(())
1165 }
1166
1167 #[pallet::call_index(15)]
1189 #[pallet::weight(T::WeightInfo::add_recovery_commitment())]
1190 pub fn add_recovery_commitment(
1191 origin: OriginFor<T>,
1192 msa_owner_key: T::AccountId,
1193 proof: MultiSignature,
1194 payload: RecoveryCommitmentPayload<T>,
1195 ) -> DispatchResult {
1196 let _origin_key = ensure_signed(origin)?;
1197
1198 ensure!(
1200 Self::verify_signature(&proof, &msa_owner_key, &payload),
1201 Error::<T>::InvalidSignature
1202 );
1203
1204 Self::register_signature(&proof, payload.expiration)?;
1206
1207 let msa_id = Self::ensure_valid_msa_key(&msa_owner_key)?;
1209
1210 MsaIdToRecoveryCommitment::<T>::insert(msa_id, payload.recovery_commitment);
1212 Self::deposit_event(Event::RecoveryCommitmentAdded {
1213 who: msa_owner_key,
1214 msa_id,
1215 recovery_commitment: payload.recovery_commitment,
1216 });
1217
1218 Ok(())
1219 }
1220
1221 #[pallet::call_index(16)]
1231 #[pallet::weight(T::WeightInfo::approve_recovery_provider())]
1232 pub fn approve_recovery_provider(
1233 origin: OriginFor<T>,
1234 provider_key: T::AccountId,
1235 ) -> DispatchResult {
1236 T::RecoveryProviderApprovalOrigin::ensure_origin(origin)?;
1237
1238 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1239 ensure!(
1240 Self::is_registered_provider(provider_msa_id),
1241 Error::<T>::ProviderNotRegistered
1242 );
1243
1244 if Self::is_approved_recovery_provider(&ProviderId(provider_msa_id)) {
1246 return Ok(());
1247 }
1248
1249 RecoveryProviders::<T>::insert(ProviderId(provider_msa_id), true);
1250
1251 Self::deposit_event(Event::RecoveryProviderApproved {
1252 provider_id: ProviderId(provider_msa_id),
1253 });
1254
1255 Ok(())
1256 }
1257
1258 #[pallet::call_index(17)]
1269 #[pallet::weight(T::WeightInfo::remove_recovery_provider())]
1270 pub fn remove_recovery_provider(
1271 origin: OriginFor<T>,
1272 provider: ProviderId,
1273 ) -> DispatchResult {
1274 T::RecoveryProviderApprovalOrigin::ensure_origin(origin)?;
1275
1276 RecoveryProviders::<T>::remove(provider);
1277 Self::deposit_event(Event::RecoveryProviderRemoved { provider_id: provider });
1278 Ok(())
1279 }
1280
1281 #[pallet::call_index(18)]
1296 #[pallet::weight(T::WeightInfo::recover_account())]
1297 pub fn recover_account(
1298 origin: OriginFor<T>,
1299 intermediary_hash_a: RecoveryHash,
1300 intermediary_hash_b: RecoveryHash,
1301 new_control_key_proof: MultiSignature,
1302 add_key_payload: AddKeyData<T>,
1303 ) -> DispatchResult {
1304 let provider_key = ensure_signed(origin)?;
1305
1306 let provider_msa_id = Self::ensure_approved_recovery_provider(&provider_key)?;
1307
1308 let recovery_commitment = Self::ensure_valid_recovery_commitment(
1309 intermediary_hash_a,
1310 intermediary_hash_b,
1311 add_key_payload.msa_id,
1312 )?;
1313
1314 Self::ensure_valid_new_key_owner(&new_control_key_proof, &add_key_payload)?;
1315
1316 Self::add_key(add_key_payload.msa_id, &add_key_payload.new_public_key.clone())?;
1317
1318 let event = Event::PublicKeyAdded {
1319 msa_id: add_key_payload.msa_id,
1320 key: add_key_payload.new_public_key.clone(),
1321 };
1322 Self::add_event_to_offchain_index(Some(&event), add_key_payload.msa_id);
1323 Self::deposit_event(event);
1324
1325 MsaIdToRecoveryCommitment::<T>::remove(add_key_payload.msa_id);
1327
1328 Self::deposit_event(Event::AccountRecovered {
1330 msa_id: add_key_payload.msa_id,
1331 recovery_provider: ProviderId(provider_msa_id),
1332 new_control_key: add_key_payload.new_public_key.clone(),
1333 });
1334
1335 Self::deposit_event(Event::RecoveryCommitmentInvalidated {
1336 msa_id: add_key_payload.msa_id,
1337 recovery_commitment,
1338 });
1339
1340 Ok(())
1341 }
1342
1343 #[pallet::call_index(19)]
1348 #[pallet::weight(T::WeightInfo::propose_to_be_provider_v2())]
1349 pub fn propose_to_be_provider_v2(
1350 origin: OriginFor<T>,
1351 payload: ProviderRegistryEntry<
1352 T::MaxProviderNameSize,
1353 T::MaxLanguageCodeSize,
1354 T::MaxLogoCidSize,
1355 T::MaxLocaleCount,
1356 >,
1357 ) -> DispatchResult {
1358 let proposer = ensure_signed(origin)?;
1359 Self::ensure_valid_msa_key(&proposer)?;
1360
1361 let proposal: Box<T::Proposal> = Box::new(
1362 (Call::<T>::create_provider_via_governance_v2 {
1363 provider_key: proposer.clone(),
1364 payload,
1365 })
1366 .into(),
1367 );
1368 let threshold = 1;
1369 T::ProposalProvider::propose(proposer, threshold, proposal)?;
1370 Ok(())
1371 }
1372
1373 #[pallet::call_index(20)]
1384 #[pallet::weight(T::WeightInfo::create_provider_via_governance_v2(
1385 payload.localized_names.len() as u32,
1386 payload.localized_logo_250_100_png_cids.len() as u32,
1387 ))]
1388 pub fn create_provider_via_governance_v2(
1389 origin: OriginFor<T>,
1390 provider_key: T::AccountId,
1391 payload: ProviderRegistryEntry<
1392 T::MaxProviderNameSize,
1393 T::MaxLanguageCodeSize,
1394 T::MaxLogoCidSize,
1395 T::MaxLocaleCount,
1396 >,
1397 ) -> DispatchResult {
1398 T::CreateProviderViaGovernanceOrigin::ensure_origin(origin)?;
1399 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1400 Self::ensure_correct_cids(&payload)?;
1401 Self::upsert_provider_for(provider_msa_id, payload, false)?;
1402 Self::deposit_event(Event::ProviderCreated {
1403 provider_id: ProviderId(provider_msa_id),
1404 });
1405 Ok(())
1406 }
1407
1408 #[pallet::call_index(21)]
1416 #[pallet::weight(T::WeightInfo::propose_to_add_application(
1417 payload.localized_names.len() as u32,
1418 payload.localized_logo_250_100_png_cids.len() as u32,
1419 ))]
1420 pub fn propose_to_add_application(
1421 origin: OriginFor<T>,
1422 payload: ApplicationContext<
1423 T::MaxProviderNameSize,
1424 T::MaxLanguageCodeSize,
1425 T::MaxLogoCidSize,
1426 T::MaxLocaleCount,
1427 >,
1428 ) -> DispatchResult {
1429 let proposer = ensure_signed(origin)?;
1430 let provider_msa_id = Self::ensure_valid_msa_key(&proposer)?;
1431 ensure!(
1432 Self::is_registered_provider(provider_msa_id),
1433 Error::<T>::ProviderNotRegistered
1434 );
1435 let proposal: Box<T::Proposal> = Box::new(
1436 (Call::<T>::create_application_via_governance {
1437 provider_key: proposer.clone(),
1438 payload,
1439 })
1440 .into(),
1441 );
1442 let threshold = 1;
1443 T::ProposalProvider::propose(proposer, threshold, proposal)?;
1444 Ok(())
1445 }
1446
1447 #[pallet::call_index(22)]
1458 #[pallet::weight(T::WeightInfo::create_application_via_governance(
1459 payload.localized_names.len() as u32,
1460 payload.localized_logo_250_100_png_cids.len() as u32,
1461 ))]
1462 pub fn create_application_via_governance(
1463 origin: OriginFor<T>,
1464 provider_key: T::AccountId,
1465 payload: ApplicationContext<
1466 T::MaxProviderNameSize,
1467 T::MaxLanguageCodeSize,
1468 T::MaxLogoCidSize,
1469 T::MaxLocaleCount,
1470 >,
1471 ) -> DispatchResult {
1472 T::CreateProviderViaGovernanceOrigin::ensure_origin(origin)?;
1473 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1474 ensure!(
1475 Self::is_registered_provider(provider_msa_id),
1476 Error::<T>::ProviderNotRegistered
1477 );
1478 Self::ensure_correct_cids(&payload)?;
1479 let application_id =
1480 Self::create_application_for(ProviderId(provider_msa_id), payload)?;
1481 Self::deposit_event(Event::ApplicationCreated {
1482 provider_id: ProviderId(provider_msa_id),
1483 application_id,
1484 });
1485 Ok(())
1486 }
1487
1488 #[pallet::call_index(23)]
1496 #[pallet::weight(T::WeightInfo::upload_logo())]
1497 pub fn upload_logo(
1498 origin: OriginFor<T>,
1499 logo_cid: BoundedVec<u8, T::MaxLogoCidSize>,
1500 logo_bytes: BoundedVec<u8, T::MaxLogoSize>,
1501 ) -> DispatchResult {
1502 let provider_key = ensure_signed(origin)?;
1503 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1504 ensure!(
1505 Self::is_registered_provider(provider_msa_id),
1506 Error::<T>::ProviderNotRegistered
1507 );
1508 ensure!(ApprovedLogos::<T>::contains_key(&logo_cid), Error::<T>::LogoCidNotApproved);
1509
1510 let input_cid_binary = Self::validate_cid(&logo_cid)?;
1511 let computed_cid_binary =
1512 compute_cid_v1(logo_bytes.as_slice()).ok_or(Error::<T>::InvalidLogoBytes)?;
1513 ensure!(input_cid_binary == computed_cid_binary, Error::<T>::InvalidLogoBytes);
1514 ApprovedLogos::<T>::insert(&logo_cid, logo_bytes);
1515
1516 Self::deposit_event(Event::ApplicationContextUpdated {
1517 provider_id: ProviderId(provider_msa_id),
1518 application_id: None,
1519 });
1520 Ok(())
1521 }
1522
1523 #[pallet::call_index(24)]
1533 #[pallet::weight(T::WeightInfo::update_provider_via_governance(
1534 payload.localized_names.len() as u32,
1535 payload.localized_logo_250_100_png_cids.len() as u32,
1536 ))]
1537 #[allow(clippy::useless_conversion)]
1538 pub fn update_provider_via_governance(
1539 origin: OriginFor<T>,
1540 provider_key: T::AccountId,
1541 payload: ProviderRegistryEntry<
1542 T::MaxProviderNameSize,
1543 T::MaxLanguageCodeSize,
1544 T::MaxLogoCidSize,
1545 T::MaxLocaleCount,
1546 >,
1547 ) -> DispatchResultWithPostInfo {
1548 T::CreateProviderViaGovernanceOrigin::ensure_origin(origin)?;
1549 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1550 Self::ensure_correct_cids(&payload)?;
1551 let base_weight = T::WeightInfo::update_provider_via_governance(
1552 payload.localized_names.len() as u32,
1553 payload.localized_logo_250_100_png_cids.len() as u32,
1554 );
1555 let total_logos_removed = Self::upsert_provider_for(provider_msa_id, payload, true)?;
1556 Self::deposit_event(Event::ProviderUpdated {
1557 provider_id: ProviderId(provider_msa_id),
1558 });
1559 Self::refund_logo_removal_weight_by_count(total_logos_removed, base_weight)
1560 }
1561
1562 #[pallet::call_index(25)]
1575 #[pallet::weight(T::WeightInfo::propose_to_update_provider(
1576 payload.localized_names.len() as u32,
1577 payload.localized_logo_250_100_png_cids.len() as u32,
1578 ))]
1579 pub fn propose_to_update_provider(
1580 origin: OriginFor<T>,
1581 payload: ProviderRegistryEntry<
1582 T::MaxProviderNameSize,
1583 T::MaxLanguageCodeSize,
1584 T::MaxLogoCidSize,
1585 T::MaxLocaleCount,
1586 >,
1587 ) -> DispatchResult {
1588 let proposer = ensure_signed(origin)?;
1589 let provider_msa_id = Self::ensure_valid_msa_key(&proposer)?;
1590 ensure!(
1591 Self::is_registered_provider(provider_msa_id),
1592 Error::<T>::ProviderNotRegistered
1593 );
1594 let proposal: Box<T::Proposal> = Box::new(
1595 (Call::<T>::update_provider_via_governance {
1596 provider_key: proposer.clone(),
1597 payload,
1598 })
1599 .into(),
1600 );
1601 let threshold = 1;
1602 T::ProposalProvider::propose(proposer, threshold, proposal)?;
1603 Ok(())
1604 }
1605
1606 #[pallet::call_index(26)]
1620 #[pallet::weight(T::WeightInfo::update_application_via_governance(
1621 payload.localized_names.len() as u32,
1622 payload.localized_logo_250_100_png_cids.len() as u32,
1623 ))]
1624 #[allow(clippy::useless_conversion)]
1625 pub fn update_application_via_governance(
1626 origin: OriginFor<T>,
1627 provider_key: T::AccountId,
1628 application_index: ApplicationIndex,
1629 payload: ApplicationContext<
1630 T::MaxProviderNameSize,
1631 T::MaxLanguageCodeSize,
1632 T::MaxLogoCidSize,
1633 T::MaxLocaleCount,
1634 >,
1635 ) -> DispatchResultWithPostInfo {
1636 T::CreateProviderViaGovernanceOrigin::ensure_origin(origin)?;
1637 let provider_msa_id = Self::ensure_valid_msa_key(&provider_key)?;
1638 ensure!(
1639 Self::is_registered_provider(provider_msa_id),
1640 Error::<T>::ProviderNotRegistered
1641 );
1642 Self::ensure_correct_cids(&payload)?;
1643 let base_weight = T::WeightInfo::update_application_via_governance(
1644 payload.localized_names.len() as u32,
1645 payload.localized_logo_250_100_png_cids.len() as u32,
1646 );
1647 let total_logos_removed = Self::upsert_application_for(
1648 ProviderId(provider_msa_id),
1649 application_index,
1650 payload,
1651 )?;
1652 Self::deposit_event(Event::ApplicationContextUpdated {
1653 provider_id: ProviderId(provider_msa_id),
1654 application_id: Some(application_index),
1655 });
1656 Self::refund_logo_removal_weight_by_count(total_logos_removed, base_weight)
1657 }
1658
1659 #[pallet::call_index(27)]
1673 #[pallet::weight(T::WeightInfo::propose_to_update_application(
1674 payload.localized_names.len() as u32,
1675 payload.localized_logo_250_100_png_cids.len() as u32,
1676 ))]
1677 pub fn propose_to_update_application(
1678 origin: OriginFor<T>,
1679 application_index: ApplicationIndex,
1680 payload: ApplicationContext<
1681 T::MaxProviderNameSize,
1682 T::MaxLanguageCodeSize,
1683 T::MaxLogoCidSize,
1684 T::MaxLocaleCount,
1685 >,
1686 ) -> DispatchResult {
1687 let proposer = ensure_signed(origin)?;
1688 let provider_msa_id = Self::ensure_valid_msa_key(&proposer)?;
1689 ensure!(
1690 Self::is_registered_provider(provider_msa_id),
1691 Error::<T>::ProviderNotRegistered
1692 );
1693 ensure!(
1694 ProviderToApplicationRegistry::<T>::contains_key(
1695 ProviderId(provider_msa_id),
1696 application_index
1697 ),
1698 Error::<T>::ApplicationNotFound
1699 );
1700 Self::ensure_correct_cids(&payload)?;
1701 let proposal: Box<T::Proposal> = Box::new(
1702 (Call::<T>::update_application_via_governance {
1703 provider_key: proposer.clone(),
1704 application_index,
1705 payload,
1706 })
1707 .into(),
1708 );
1709 let threshold = 1;
1710 T::ProposalProvider::propose(proposer, threshold, proposal)?;
1711 Ok(())
1712 }
1713
1714 #[pallet::call_index(28)]
1717 #[pallet::weight(T::WeightInfo::create_application_via_governance(
1718 payload.localized_names.len() as u32,
1719 payload.localized_logo_250_100_png_cids.len() as u32,
1720 ))]
1721 pub fn create_application(
1722 origin: OriginFor<T>,
1723 payload: ApplicationContext<
1724 T::MaxProviderNameSize,
1725 T::MaxLanguageCodeSize,
1726 T::MaxLogoCidSize,
1727 T::MaxLocaleCount,
1728 >,
1729 ) -> DispatchResult {
1730 let provider_account = ensure_signed(origin)?;
1731 let provider_msa_id = Self::ensure_valid_msa_key(&provider_account)?;
1732 ensure!(
1733 Self::is_registered_provider(provider_msa_id),
1734 Error::<T>::ProviderNotRegistered
1735 );
1736 Self::ensure_correct_cids(&payload)?;
1737 let application_id =
1738 Self::create_application_for(ProviderId(provider_msa_id), payload)?;
1739 Self::deposit_event(Event::ApplicationCreated {
1740 provider_id: ProviderId(provider_msa_id),
1741 application_id,
1742 });
1743 Ok(())
1744 }
1745 }
1746}
1747
1748impl<T: Config> Pallet<T> {
1749 pub fn is_approved_recovery_provider(provider: &ProviderId) -> bool {
1757 RecoveryProviders::<T>::get(provider).unwrap_or(false)
1758 }
1759
1760 pub fn compute_recovery_commitment(
1769 intermediary_hash_a: RecoveryHash,
1770 intermediary_hash_b: RecoveryHash,
1771 ) -> RecoveryCommitment {
1772 let mut input = Vec::with_capacity(64);
1773 input.extend_from_slice(&intermediary_hash_a);
1774 input.extend_from_slice(&intermediary_hash_b);
1775 keccak_256(&input)
1776 }
1777
1778 fn ensure_approved_recovery_provider(
1790 provider_key: &T::AccountId,
1791 ) -> Result<MessageSourceId, DispatchError> {
1792 let provider_msa_id = Self::ensure_valid_msa_key(provider_key)?;
1793
1794 ensure!(
1795 Self::is_approved_recovery_provider(&ProviderId(provider_msa_id)),
1796 Error::<T>::NotAuthorizedRecoveryProvider
1797 );
1798
1799 Ok(provider_msa_id)
1800 }
1801
1802 fn ensure_valid_recovery_commitment(
1816 intermediary_hash_a: RecoveryHash,
1817 intermediary_hash_b: RecoveryHash,
1818 msa_id: MessageSourceId,
1819 ) -> Result<RecoveryCommitment, DispatchError> {
1820 let recovery_commitment: RecoveryCommitment =
1821 Self::compute_recovery_commitment(intermediary_hash_a, intermediary_hash_b);
1822
1823 let stored_commitment: RecoveryCommitment =
1824 MsaIdToRecoveryCommitment::<T>::get(msa_id).ok_or(Error::<T>::NoRecoveryCommitment)?;
1825
1826 ensure!(recovery_commitment == stored_commitment, Error::<T>::InvalidRecoveryCommitment);
1827
1828 Ok(recovery_commitment)
1829 }
1830
1831 fn ensure_valid_new_key_owner(
1841 new_control_key_proof: &MultiSignature,
1842 add_key_payload: &AddKeyData<T>,
1843 ) -> DispatchResult {
1844 ensure!(
1847 Self::verify_signature(
1848 new_control_key_proof,
1849 &add_key_payload.new_public_key,
1850 add_key_payload
1851 ),
1852 Error::<T>::NewKeyOwnershipInvalidSignature
1853 );
1854 Self::register_signature(new_control_key_proof, add_key_payload.expiration)?;
1855
1856 Ok(())
1857 }
1858
1859 pub fn create_account(
1867 key: T::AccountId,
1868 ) -> Result<(MessageSourceId, T::AccountId), DispatchError> {
1869 let next_msa_id = Self::get_next_msa_id()?;
1870 Self::add_key(next_msa_id, &key)?;
1871 let _ = Self::set_msa_identifier(next_msa_id);
1872
1873 Ok((next_msa_id, key))
1874 }
1875
1876 pub fn get_next_msa_id() -> Result<MessageSourceId, DispatchError> {
1882 let next = CurrentMsaIdentifierMaximum::<T>::get()
1883 .checked_add(1)
1884 .ok_or(Error::<T>::MsaIdOverflow)?;
1885
1886 Ok(next)
1887 }
1888
1889 pub fn set_msa_identifier(identifier: MessageSourceId) -> DispatchResult {
1891 CurrentMsaIdentifierMaximum::<T>::set(identifier);
1892
1893 Ok(())
1894 }
1895
1896 pub fn create_registered_provider(
1898 provider_id: ProviderId,
1899 payload: ProviderRegistryEntry<
1900 T::MaxProviderNameSize,
1901 T::MaxLanguageCodeSize,
1902 T::MaxLogoCidSize,
1903 T::MaxLocaleCount,
1904 >,
1905 ) -> DispatchResult {
1906 ProviderToRegistryEntryV2::<T>::try_mutate(
1907 provider_id,
1908 |maybe_metadata| -> DispatchResult {
1909 ensure!(
1910 maybe_metadata.take().is_none(),
1911 Error::<T>::DuplicateProviderRegistryEntry
1912 );
1913 *maybe_metadata = Some(payload);
1914 Ok(())
1915 },
1916 )
1917 }
1918
1919 #[cfg(test)]
1921 pub fn grant_permissions_for_intents(
1922 delegator_id: DelegatorId,
1923 provider_id: ProviderId,
1924 intent_ids: Vec<IntentId>,
1925 ) -> DispatchResult {
1926 Self::try_mutate_delegation(delegator_id, provider_id, |delegation, is_new_delegation| {
1927 ensure!(!is_new_delegation, Error::<T>::DelegationNotFound);
1928 Self::ensure_all_intent_ids_are_valid(&intent_ids)?;
1929
1930 PermittedDelegationIntents::<T>::try_insert_intents(delegation, intent_ids)?;
1931
1932 Ok(())
1933 })
1934 }
1935
1936 pub fn revoke_permissions_for_intents(
1938 delegator_id: DelegatorId,
1939 provider_id: ProviderId,
1940 intent_ids: Vec<IntentId>,
1941 ) -> DispatchResult {
1942 Self::try_mutate_delegation(delegator_id, provider_id, |delegation, is_new_delegation| {
1943 ensure!(!is_new_delegation, Error::<T>::DelegationNotFound);
1944 Self::ensure_all_intent_ids_are_valid(&intent_ids)?;
1945
1946 let current_block = frame_system::Pallet::<T>::block_number();
1947
1948 PermittedDelegationIntents::<T>::try_get_mut_intents(
1949 delegation,
1950 intent_ids,
1951 current_block,
1952 )?;
1953
1954 Ok(())
1955 })
1956 }
1957
1958 pub fn add_key(msa_id: MessageSourceId, key: &T::AccountId) -> DispatchResult {
1965 PublicKeyToMsaId::<T>::try_mutate(key, |maybe_msa_id| {
1966 ensure!(maybe_msa_id.is_none(), Error::<T>::KeyAlreadyRegistered);
1967 *maybe_msa_id = Some(msa_id);
1968
1969 <PublicKeyCountForMsaId<T>>::try_mutate(msa_id, |key_count| {
1971 let incremented_key_count =
1973 key_count.checked_add(1).ok_or(ArithmeticError::Overflow)?;
1974
1975 ensure!(
1976 incremented_key_count <= T::MaxPublicKeysPerMsa::get(),
1977 Error::<T>::KeyLimitExceeded
1978 );
1979
1980 *key_count = incremented_key_count;
1981 Ok(())
1982 })
1983 })
1984 }
1985
1986 pub fn ensure_all_intent_ids_are_valid(intent_ids: &[IntentId]) -> DispatchResult {
1993 ensure!(
1994 intent_ids.len() <= T::MaxGrantsPerDelegation::get() as usize,
1995 Error::<T>::ExceedsMaxGrantsPerDelegation
1996 );
1997
1998 let all_valid = T::SchemaValidator::are_all_intent_ids_valid(intent_ids);
1999
2000 ensure!(all_valid, Error::<T>::InvalidIntentId);
2001
2002 Ok(())
2003 }
2004
2005 pub fn is_registered_provider(msa_id: MessageSourceId) -> bool {
2007 ProviderToRegistryEntryV2::<T>::contains_key(ProviderId(msa_id))
2008 }
2009
2010 pub fn ensure_valid_registered_provider(
2020 delegator_key: &T::AccountId,
2021 provider_key: &T::AccountId,
2022 ) -> Result<(ProviderId, DelegatorId), DispatchError> {
2023 let provider_msa_id = Self::ensure_valid_msa_key(provider_key)?;
2024 let delegator_msa_id = Self::ensure_valid_msa_key(delegator_key)?;
2025
2026 ensure!(delegator_msa_id != provider_msa_id, Error::<T>::InvalidSelfProvider);
2028
2029 ensure!(Self::is_registered_provider(provider_msa_id), Error::<T>::ProviderNotRegistered);
2031
2032 Ok((provider_msa_id.into(), delegator_msa_id.into()))
2033 }
2034
2035 pub fn ensure_msa_owner(who: &T::AccountId, msa_id: MessageSourceId) -> DispatchResult {
2042 let provider_msa_id = Self::ensure_valid_msa_key(who)?;
2043 ensure!(provider_msa_id == msa_id, Error::<T>::NotMsaOwner);
2044
2045 Ok(())
2046 }
2047
2048 pub fn verify_signature<P>(
2055 signature: &MultiSignature,
2056 signer: &T::AccountId,
2057 payload: &P,
2058 ) -> bool
2059 where
2060 P: Encode + EIP712Encode,
2061 {
2062 let key = T::ConvertIntoAccountId32::convert((*signer).clone());
2063
2064 check_signature(signature, key, payload)
2065 }
2066
2067 pub fn add_provider(
2073 provider_id: ProviderId,
2074 delegator_id: DelegatorId,
2075 intent_ids: Vec<IntentId>,
2076 ) -> DispatchResult {
2077 Self::try_mutate_delegation(delegator_id, provider_id, |delegation, is_new_delegation| {
2078 ensure!(is_new_delegation, Error::<T>::DuplicateProvider);
2079 Self::ensure_all_intent_ids_are_valid(&intent_ids)?;
2080
2081 PermittedDelegationIntents::<T>::try_insert_intents(delegation, intent_ids)?;
2082
2083 Ok(())
2084 })
2085 }
2086
2087 pub fn upsert_intent_permissions(
2092 provider_id: ProviderId,
2093 delegator_id: DelegatorId,
2094 intent_ids: Vec<IntentId>,
2095 ) -> DispatchResult {
2096 Self::try_mutate_delegation(delegator_id, provider_id, |delegation, _is_new_delegation| {
2097 Self::ensure_all_intent_ids_are_valid(&intent_ids)?;
2098
2099 let mut revoke_ids: Vec<IntentId> = Vec::new();
2101 let mut update_ids: Vec<IntentId> = Vec::new();
2102 let mut insert_ids: Vec<IntentId> = Vec::new();
2103
2104 let existing_keys = delegation.permissions.keys();
2105
2106 for existing_intent_id in existing_keys {
2107 if !intent_ids.contains(existing_intent_id) {
2108 if let Some(block) = delegation.permissions.get(existing_intent_id) {
2109 if *block == BlockNumberFor::<T>::zero() {
2110 revoke_ids.push(*existing_intent_id);
2111 }
2112 }
2113 }
2114 }
2115 for intent_id in &intent_ids {
2116 if !delegation.permissions.contains_key(intent_id) {
2117 insert_ids.push(*intent_id);
2118 } else {
2119 update_ids.push(*intent_id);
2120 }
2121 }
2122
2123 let current_block = frame_system::Pallet::<T>::block_number();
2124
2125 PermittedDelegationIntents::<T>::try_get_mut_intents(
2127 delegation,
2128 revoke_ids,
2129 current_block,
2130 )?;
2131
2132 PermittedDelegationIntents::<T>::try_get_mut_intents(
2134 delegation,
2135 update_ids,
2136 BlockNumberFor::<T>::zero(),
2137 )?;
2138
2139 PermittedDelegationIntents::<T>::try_insert_intents(delegation, insert_ids)?;
2141 delegation.revoked_at = BlockNumberFor::<T>::zero();
2142 Ok(())
2143 })
2144 }
2145
2146 pub fn upsert_provider_for(
2153 provider_msa_id: MessageSourceId,
2154 payload: ProviderRegistryEntry<
2155 T::MaxProviderNameSize,
2156 T::MaxLanguageCodeSize,
2157 T::MaxLogoCidSize,
2158 T::MaxLocaleCount,
2159 >,
2160 is_update: bool,
2161 ) -> Result<u32, DispatchError> {
2162 let mut total_logos_removed = 0;
2163 ProviderToRegistryEntryV2::<T>::try_mutate(
2164 ProviderId(provider_msa_id),
2165 |maybe_metadata| -> DispatchResult {
2166 if !is_update {
2167 ensure!(
2168 maybe_metadata.take().is_none(),
2169 Error::<T>::DuplicateProviderRegistryEntry
2170 );
2171 } else {
2172 total_logos_removed = Self::remove_logo_storage(maybe_metadata.as_ref())?;
2173 }
2174 Self::update_logo_storage(&payload)?;
2175
2176 *maybe_metadata = Some(payload);
2177 Ok(())
2178 },
2179 )
2180 .map(|_| total_logos_removed)
2181 }
2182
2183 pub fn create_application_for(
2188 provider_msa_id: ProviderId,
2189 payload: ApplicationContext<
2190 T::MaxProviderNameSize,
2191 T::MaxLanguageCodeSize,
2192 T::MaxLogoCidSize,
2193 T::MaxLocaleCount,
2194 >,
2195 ) -> Result<ApplicationIndex, DispatchError> {
2196 Self::update_logo_storage(&payload)?;
2197 let next_application_index = NextApplicationIndex::<T>::get(provider_msa_id);
2198 ensure!(
2199 !ProviderToApplicationRegistry::<T>::contains_key(
2200 provider_msa_id,
2201 next_application_index
2202 ),
2203 Error::<T>::DuplicateApplicationRegistryEntry
2204 );
2205 ProviderToApplicationRegistry::<T>::insert(
2206 provider_msa_id,
2207 next_application_index,
2208 payload,
2209 );
2210 NextApplicationIndex::<T>::insert(provider_msa_id, next_application_index + 1);
2211 Ok(next_application_index)
2212 }
2213
2214 pub fn upsert_application_for(
2219 provider_msa_id: ProviderId,
2220 application_index: ApplicationIndex,
2221 payload: ApplicationContext<
2222 T::MaxProviderNameSize,
2223 T::MaxLanguageCodeSize,
2224 T::MaxLogoCidSize,
2225 T::MaxLocaleCount,
2226 >,
2227 ) -> Result<u32, DispatchError> {
2228 ensure!(
2229 ProviderToApplicationRegistry::<T>::contains_key(provider_msa_id, application_index),
2230 Error::<T>::ApplicationNotFound
2231 );
2232 let mut total_logos_removed = 0;
2233 ProviderToApplicationRegistry::<T>::try_mutate(
2234 provider_msa_id,
2235 application_index,
2236 |maybe_metadata| -> DispatchResult {
2237 total_logos_removed = Self::remove_logo_storage(maybe_metadata.as_ref())?;
2238 Self::update_logo_storage(&payload)?;
2239 *maybe_metadata = Some(payload);
2240 Ok(())
2241 },
2242 )
2243 .map(|_| total_logos_removed)
2244 }
2245
2246 pub fn try_mutate_delegation<R, E: From<DispatchError>>(
2250 delegator_id: DelegatorId,
2251 provider_id: ProviderId,
2252 f: impl FnOnce(
2253 &mut Delegation<IntentId, BlockNumberFor<T>, T::MaxGrantsPerDelegation>,
2254 bool,
2255 ) -> Result<R, E>,
2256 ) -> Result<R, E> {
2257 DelegatorAndProviderToDelegation::<T>::try_mutate_exists(
2258 delegator_id,
2259 provider_id,
2260 |maybe_delegation_info| {
2261 let is_new = maybe_delegation_info.is_none();
2262 let mut delegation = maybe_delegation_info.take().unwrap_or_default();
2263
2264 let result = f(&mut delegation, is_new)?;
2265
2266 *maybe_delegation_info = Some(delegation);
2268 Ok(result)
2269 },
2270 )
2271 }
2272
2273 pub fn delete_key_for_msa(msa_id: MessageSourceId, key: &T::AccountId) -> DispatchResult {
2279 PublicKeyToMsaId::<T>::try_mutate_exists(key, |maybe_msa_id| {
2280 ensure!(maybe_msa_id.is_some(), Error::<T>::NoKeyExists);
2281
2282 *maybe_msa_id = None;
2284
2285 <PublicKeyCountForMsaId<T>>::try_mutate_exists(msa_id, |key_count| {
2286 match key_count {
2287 Some(1) => *key_count = None,
2288 Some(count) => *count = *count - 1u8,
2289 None => (),
2290 }
2291
2292 Ok(())
2293 })
2294 })
2295 }
2296
2297 pub fn revoke_provider(provider_id: ProviderId, delegator_id: DelegatorId) -> DispatchResult {
2304 DelegatorAndProviderToDelegation::<T>::try_mutate_exists(
2305 delegator_id,
2306 provider_id,
2307 |maybe_info| -> DispatchResult {
2308 let mut info = maybe_info.take().ok_or(Error::<T>::DelegationNotFound)?;
2309
2310 ensure!(
2311 info.revoked_at == BlockNumberFor::<T>::default(),
2312 Error::<T>::DelegationRevoked
2313 );
2314
2315 let current_block = frame_system::Pallet::<T>::block_number();
2316 info.revoked_at = current_block;
2317 *maybe_info = Some(info);
2318 Ok(())
2319 },
2320 )?;
2321
2322 Ok(())
2323 }
2324
2325 pub fn get_owner_of(key: &T::AccountId) -> Option<MessageSourceId> {
2327 PublicKeyToMsaId::<T>::get(key)
2328 }
2329
2330 pub fn ensure_valid_msa_key(key: &T::AccountId) -> Result<MessageSourceId, DispatchError> {
2332 let msa_id = PublicKeyToMsaId::<T>::get(key).ok_or(Error::<T>::NoKeyExists)?;
2333 Ok(msa_id)
2334 }
2335
2336 pub fn get_granted_intents_by_msa_id(
2343 delegator: DelegatorId,
2344 provider: Option<ProviderId>,
2345 ) -> Result<Vec<DelegationResponse<IntentId, BlockNumberFor<T>>>, DispatchError> {
2346 let delegations = match provider {
2347 Some(provider_id) => vec![(
2348 provider_id,
2349 Self::get_delegation_of(delegator, provider_id)
2350 .ok_or(Error::<T>::DelegationNotFound)?,
2351 )],
2352 None => DelegatorAndProviderToDelegation::<T>::iter_prefix(delegator).collect(),
2353 };
2354
2355 let mut result = vec![];
2356 for (provider_id, provider_info) in delegations {
2357 let intent_permissions = provider_info.permissions;
2358 if provider.is_some() && intent_permissions.is_empty() {
2360 return Err(Error::<T>::PermissionNotGranted.into());
2361 }
2362
2363 let mut intent_list = Vec::new();
2364 for (granted_id, revoked_at) in intent_permissions {
2365 let effective_revoked_at = match (provider_info.revoked_at, revoked_at) {
2370 (provider_revoked_at, _) if provider_revoked_at.is_zero() => revoked_at,
2371 (_, revoked_at) if revoked_at.is_zero() => provider_info.revoked_at,
2372 _ => core::cmp::min(revoked_at, provider_info.revoked_at),
2373 };
2374 intent_list.push(DelegationGrant {
2375 granted_id,
2376 explicit_revoked_at: revoked_at,
2377 revoked_at: effective_revoked_at,
2378 });
2379 }
2380
2381 result.push(DelegationResponse {
2382 provider_id,
2383 permissions: intent_list,
2384 revoked_at: provider_info.revoked_at,
2385 });
2386 }
2387
2388 Ok(result)
2389 }
2390
2391 pub fn msa_id_to_eth_address(id: MessageSourceId) -> H160 {
2397 const DOMAIN_PREFIX: u8 = 0xD9;
2401
2402 lazy_static! {
2403 static ref MSA_ADDRESS_SALT: [u8; 32] = keccak_256(b"MSA Generated");
2405 }
2406 let input_value = id.to_be_bytes();
2407
2408 let mut hash_input = [0u8; 41];
2409 hash_input[0] = DOMAIN_PREFIX;
2410 hash_input[1..9].copy_from_slice(&input_value);
2411 hash_input[9..].copy_from_slice(&(*MSA_ADDRESS_SALT));
2412
2413 let hash = keccak_256(&hash_input);
2414 H160::from_slice(&hash[12..])
2415 }
2416
2417 pub fn validate_eth_address_for_msa(address: &H160, msa_id: MessageSourceId) -> bool {
2419 let generated_address = Self::msa_id_to_eth_address(msa_id);
2420 *address == generated_address
2421 }
2422
2423 pub fn eth_address_to_checksummed_string(address: &H160) -> alloc::string::String {
2427 let addr_bytes = address.0;
2428 let addr_hex = hex::encode(addr_bytes);
2429 let hash = keccak_256(addr_hex.as_bytes());
2430
2431 let mut result = alloc::string::String::with_capacity(42);
2432 result.push_str("0x");
2433
2434 for (i, c) in addr_hex.chars().enumerate() {
2435 let hash_byte = hash[i / 2];
2436 let bit = if i % 2 == 0 { (hash_byte >> 4) & 0xf } else { hash_byte & 0xf };
2437
2438 result.push(if c.is_ascii_hexdigit() && c.is_ascii_alphabetic() {
2439 if bit >= 8 {
2440 c.to_ascii_uppercase()
2441 } else {
2442 c
2443 }
2444 } else {
2445 c
2446 });
2447 }
2448
2449 result
2450 }
2451
2452 pub fn register_signature(
2464 signature: &MultiSignature,
2465 signature_expires_at: BlockNumberFor<T>,
2466 ) -> DispatchResult {
2467 let current_block =
2468 Self::check_signature_against_registry(signature, signature_expires_at)?;
2469
2470 Self::enqueue_signature(signature, signature_expires_at, current_block)
2471 }
2472
2473 pub fn check_signature_against_registry(
2482 signature: &MultiSignature,
2483 signature_expires_at: BlockNumberFor<T>,
2484 ) -> Result<BlockNumberFor<T>, DispatchError> {
2485 let current_block: BlockNumberFor<T> = frame_system::Pallet::<T>::block_number();
2486
2487 let max_lifetime = Self::mortality_block_limit(current_block);
2488 ensure!(max_lifetime > signature_expires_at, Error::<T>::ProofNotYetValid);
2489 ensure!(current_block < signature_expires_at, Error::<T>::ProofHasExpired);
2490
2491 ensure!(
2493 !<PayloadSignatureRegistryList<T>>::contains_key(signature),
2494 Error::<T>::SignatureAlreadySubmitted
2495 );
2496 if let Some(signature_pointer) = PayloadSignatureRegistryPointer::<T>::get() {
2497 ensure!(signature_pointer.newest != *signature, Error::<T>::SignatureAlreadySubmitted);
2498 }
2499
2500 Ok(current_block)
2501 }
2502
2503 fn enqueue_signature(
2526 signature: &MultiSignature,
2527 signature_expires_at: BlockNumberFor<T>,
2528 current_block: BlockNumberFor<T>,
2529 ) -> DispatchResult {
2530 let pointer =
2532 PayloadSignatureRegistryPointer::<T>::get().unwrap_or(SignatureRegistryPointer {
2533 newest: signature.clone(),
2534 newest_expires_at: signature_expires_at,
2535 oldest: signature.clone(),
2536 count: 0,
2537 });
2538
2539 ensure!(
2541 !(pointer.count != 0 && pointer.newest.eq(signature)),
2542 Error::<T>::SignatureAlreadySubmitted
2543 );
2544
2545 let mut oldest: MultiSignature = pointer.oldest.clone();
2547
2548 let is_registry_full: bool = pointer.count == T::MaxSignaturesStored::get().unwrap_or(0);
2550
2551 if is_registry_full {
2553 let (expire_block_number, next_oldest) =
2554 PayloadSignatureRegistryList::<T>::get(pointer.oldest.clone())
2555 .ok_or(Error::<T>::SignatureRegistryCorrupted)?;
2556
2557 ensure!(
2558 current_block.gt(&expire_block_number),
2559 Error::<T>::SignatureRegistryLimitExceeded
2560 );
2561
2562 oldest = next_oldest.clone();
2564
2565 <PayloadSignatureRegistryList<T>>::remove(pointer.oldest);
2566 }
2567
2568 if pointer.count != 0 {
2570 <PayloadSignatureRegistryList<T>>::insert(
2571 pointer.newest,
2572 (pointer.newest_expires_at, signature.clone()),
2573 );
2574 }
2575
2576 PayloadSignatureRegistryPointer::<T>::put(SignatureRegistryPointer {
2578 count: if is_registry_full { pointer.count } else { pointer.count + 1 },
2580 newest: signature.clone(),
2581 newest_expires_at: signature_expires_at,
2582 oldest,
2583 });
2584
2585 Ok(())
2586 }
2587
2588 fn mortality_block_limit(current_block: BlockNumberFor<T>) -> BlockNumberFor<T> {
2592 let mortality_size = T::MortalityWindowSize::get();
2593 current_block + BlockNumberFor::<T>::from(mortality_size)
2594 }
2595
2596 fn validate_cid(in_cid: &[u8]) -> Result<Vec<u8>, DispatchError> {
2603 let cid_str: &str = core::str::from_utf8(in_cid).map_err(|_| Error::<T>::InvalidCid)?;
2605 ensure!(cid_str.len() > 2, Error::<T>::InvalidCid);
2606 ensure!(!cid_str.starts_with("Qm"), Error::<T>::UnsupportedCidVersion);
2608
2609 let cid_b = multibase::decode(cid_str).map_err(|_| Error::<T>::InvalidCid)?.1;
2611 ensure!(Cid::read_bytes(&cid_b[..]).is_ok(), Error::<T>::InvalidCid);
2612
2613 Ok(cid_b)
2614 }
2615
2616 fn update_logo_storage(
2618 payload: &ProviderRegistryEntry<
2619 T::MaxProviderNameSize,
2620 T::MaxLanguageCodeSize,
2621 T::MaxLogoCidSize,
2622 T::MaxLocaleCount,
2623 >,
2624 ) -> DispatchResult {
2625 if !payload.default_logo_250_100_png_cid.is_empty() {
2627 ApprovedLogos::<T>::insert(
2628 payload.default_logo_250_100_png_cid.clone(),
2629 BoundedVec::new(),
2630 );
2631 }
2632
2633 for (_, localized_cid) in &payload.localized_logo_250_100_png_cids {
2635 if !localized_cid.is_empty() {
2636 ApprovedLogos::<T>::insert(localized_cid, BoundedVec::new());
2637 }
2638 }
2639
2640 Ok(())
2641 }
2642
2643 fn remove_logo_storage(
2645 existing_payload: Option<
2646 &ProviderRegistryEntry<
2647 T::MaxProviderNameSize,
2648 T::MaxLanguageCodeSize,
2649 T::MaxLogoCidSize,
2650 T::MaxLocaleCount,
2651 >,
2652 >,
2653 ) -> Result<u32, DispatchError> {
2654 let mut total_logo_removed = 0;
2655 if let Some(payload) = existing_payload {
2656 if !payload.default_logo_250_100_png_cid.is_empty() {
2658 total_logo_removed += 1;
2659 ApprovedLogos::<T>::remove(&payload.default_logo_250_100_png_cid);
2660 }
2661 for (_, localized_cid) in &payload.localized_logo_250_100_png_cids {
2663 if !localized_cid.is_empty() {
2664 total_logo_removed += 1;
2665 ApprovedLogos::<T>::remove(localized_cid);
2666 }
2667 }
2668 }
2669 Ok(total_logo_removed)
2670 }
2671
2672 fn ensure_correct_cids(
2674 payload: &ProviderRegistryEntry<
2675 T::MaxProviderNameSize,
2676 T::MaxLanguageCodeSize,
2677 T::MaxLogoCidSize,
2678 T::MaxLocaleCount,
2679 >,
2680 ) -> DispatchResult {
2681 if !payload.default_logo_250_100_png_cid.is_empty() {
2683 Self::validate_cid(&payload.default_logo_250_100_png_cid)?;
2684 }
2685
2686 for (lang_code, localized_cid) in &payload.localized_logo_250_100_png_cids {
2688 let code_str = core::str::from_utf8(lang_code)
2690 .map_err(|_| Error::<T>::InvalidBCP47LanguageCode)?;
2691
2692 if !Self::is_valid_bcp47(code_str) {
2693 return Err(Error::<T>::InvalidBCP47LanguageCode.into());
2694 }
2695
2696 if !localized_cid.is_empty() {
2698 Self::validate_cid(localized_cid)?;
2699 }
2700 }
2701
2702 for (lang_code, _) in &payload.localized_names {
2704 let code_str = core::str::from_utf8(lang_code)
2706 .map_err(|_| Error::<T>::InvalidBCP47LanguageCode)?;
2707 if !Self::is_valid_bcp47(code_str) {
2708 return Err(Error::<T>::InvalidBCP47LanguageCode.into());
2709 }
2710 }
2711
2712 Ok(())
2713 }
2714
2715 fn is_valid_bcp47(code: &str) -> bool {
2717 if code.is_empty() {
2719 return false;
2720 }
2721 if code.starts_with('-') || code.ends_with('-') || code.contains("--") {
2723 return false;
2724 }
2725 for part in code.split('-') {
2726 let len = part.len();
2727 if len < 2 || !part.chars().all(|c| c.is_ascii_alphanumeric()) {
2728 return false;
2729 }
2730 }
2731 true
2732 }
2733
2734 pub fn get_provider_application_context(
2736 provider_id: ProviderId,
2737 application_id: Option<ApplicationIndex>,
2738 locale: Option<Vec<u8>>,
2739 ) -> Option<ProviderApplicationContext> {
2740 let bounded_locale = locale.and_then(|loc| BoundedVec::try_from(loc).ok());
2741 let provider_or_application_registry = match application_id {
2742 Some(app_id) => ProviderToApplicationRegistry::<T>::get(provider_id, app_id)?,
2743 None => ProviderToRegistryEntryV2::<T>::get(provider_id)?,
2744 };
2745 let default_name = provider_or_application_registry.default_name.to_vec();
2746 let default_logo_cid = provider_or_application_registry.default_logo_250_100_png_cid;
2747 let default_logo_250_100_png_bytes: Option<Vec<u8>> =
2749 ApprovedLogos::<T>::get(default_logo_cid).map(|bv| bv.to_vec());
2750 let mut localized_name: Option<Vec<u8>> = None;
2751 let localized_logo_250_100_png_bytes: Option<Vec<u8>> = bounded_locale.and_then(|locale| {
2753 localized_name = provider_or_application_registry
2755 .localized_names
2756 .get(&locale)
2757 .map(|bv| bv.to_vec());
2758
2759 provider_or_application_registry
2760 .localized_logo_250_100_png_cids
2761 .get(&locale)
2762 .and_then(|cid| ApprovedLogos::<T>::get(cid).map(|bv| bv.to_vec()))
2763 });
2764
2765 Some(ProviderApplicationContext {
2766 default_name,
2767 provider_id,
2768 application_id,
2769 default_logo_250_100_png_bytes,
2770 localized_logo_250_100_png_bytes,
2771 localized_name,
2772 })
2773 }
2774
2775 fn refund_logo_removal_weight_by_count(
2777 total_logos_removed: u32,
2778 base_weight: Weight,
2779 ) -> DispatchResultWithPostInfo {
2780 let max_logos_benchmark_assumed = T::MaxLocaleCount::get();
2782 let removal_over_charged_by =
2783 max_logos_benchmark_assumed.saturating_sub(total_logos_removed);
2784 let weight_per_logo_removal = T::DbWeight::get().writes(1);
2785 let weight_to_refund =
2786 weight_per_logo_removal.saturating_mul(removal_over_charged_by as u64);
2787 let actual_weight_used = base_weight.saturating_sub(weight_to_refund);
2788 Ok(PostDispatchInfo { actual_weight: Some(actual_weight_used), pays_fee: Pays::Yes })
2789 }
2790
2791 fn add_event_to_offchain_index(event: Option<&Event<T>>, msa_id: MessageSourceId) {
2793 #[cfg(not(feature = "no-custom-host-functions"))]
2794 offchain_index_event::<T>(event, msa_id);
2795 }
2796}
2797
2798#[cfg(feature = "runtime-benchmarks")]
2799impl<T: Config> MsaBenchmarkHelper<T::AccountId> for Pallet<T> {
2800 fn set_delegation_relationship(
2802 provider: ProviderId,
2803 delegator: DelegatorId,
2804 intents: Vec<IntentId>,
2805 ) -> DispatchResult {
2806 Self::add_provider(provider, delegator, intents)?;
2807 Ok(())
2808 }
2809
2810 fn add_key(msa_id: MessageSourceId, key: T::AccountId) -> DispatchResult {
2812 Self::add_key(msa_id, &key)?;
2813 Ok(())
2814 }
2815
2816 fn create_msa(keys: T::AccountId) -> Result<MessageSourceId, DispatchError> {
2817 let (msa_id, _) = Self::create_account(keys)?;
2818 Ok(msa_id)
2819 }
2820}
2821
2822#[cfg(feature = "runtime-benchmarks")]
2823impl<T: Config> RegisterProviderBenchmarkHelper for Pallet<T> {
2824 fn create(provider_id: MessageSourceId, name: Vec<u8>) -> DispatchResult {
2826 let name = BoundedVec::<u8, T::MaxProviderNameSize>::try_from(name).expect("error");
2827 let payload = ProviderRegistryEntry {
2828 default_name: name,
2829 localized_names: BoundedBTreeMap::new(),
2830 default_logo_250_100_png_cid: BoundedVec::new(),
2831 localized_logo_250_100_png_cids: BoundedBTreeMap::new(),
2832 };
2833 Self::create_registered_provider(provider_id.into(), payload)?;
2834
2835 Ok(())
2836 }
2837}
2838
2839impl<T: Config> MsaLookup for Pallet<T> {
2840 type AccountId = T::AccountId;
2841
2842 fn get_msa_id(key: &Self::AccountId) -> Option<MessageSourceId> {
2843 Self::get_owner_of(key)
2844 }
2845
2846 fn get_max_msa_id() -> MessageSourceId {
2847 CurrentMsaIdentifierMaximum::<T>::get()
2848 }
2849}
2850
2851impl<T: Config> MsaValidator for Pallet<T> {
2852 type AccountId = T::AccountId;
2853
2854 fn ensure_valid_msa_key(key: &T::AccountId) -> Result<MessageSourceId, DispatchError> {
2855 Self::ensure_valid_msa_key(key)
2856 }
2857}
2858
2859impl<T: Config> ProviderLookup for Pallet<T> {
2860 type BlockNumber = BlockNumberFor<T>;
2861 type MaxGrantsPerDelegation = T::MaxGrantsPerDelegation;
2862 type DelegationId = IntentId;
2863
2864 fn get_delegation_of(
2865 delegator: DelegatorId,
2866 provider: ProviderId,
2867 ) -> Option<Delegation<Self::DelegationId, Self::BlockNumber, Self::MaxGrantsPerDelegation>> {
2868 DelegatorAndProviderToDelegation::<T>::get(delegator, provider)
2869 }
2870}
2871
2872impl<T: Config> DelegationValidator for Pallet<T> {
2873 type BlockNumber = BlockNumberFor<T>;
2874 type MaxGrantsPerDelegation = T::MaxGrantsPerDelegation;
2875 type DelegationIdType = IntentId;
2876
2877 fn ensure_valid_delegation(
2887 provider_id: ProviderId,
2888 delegator_id: DelegatorId,
2889 block_number: Option<Self::BlockNumber>,
2890 ) -> Result<
2891 Delegation<Self::DelegationIdType, Self::BlockNumber, Self::MaxGrantsPerDelegation>,
2892 DispatchError,
2893 > {
2894 let info = DelegatorAndProviderToDelegation::<T>::get(delegator_id, provider_id)
2895 .ok_or(Error::<T>::DelegationNotFound)?;
2896 let current_block = frame_system::Pallet::<T>::block_number();
2897 let requested_block = match block_number {
2898 Some(block_number) => {
2899 ensure!(
2900 current_block >= block_number,
2901 Error::<T>::CannotPredictValidityPastCurrentBlock
2902 );
2903 block_number
2904 },
2905 None => current_block,
2906 };
2907
2908 if info.revoked_at == Self::BlockNumber::zero() {
2909 return Ok(info);
2910 }
2911 ensure!(info.revoked_at >= requested_block, Error::<T>::DelegationRevoked);
2912
2913 Ok(info)
2914 }
2915}
2916
2917impl<T: Config> TargetValidator for Pallet<T> {
2918 fn validate(target: MessageSourceId) -> bool {
2919 Self::is_registered_provider(target)
2920 }
2921}
2922
2923impl<T: Config> GrantValidator<IntentId, BlockNumberFor<T>> for Pallet<T> {
2924 fn ensure_valid_grant(
2933 provider: ProviderId,
2934 delegator: DelegatorId,
2935 intent_id: IntentId,
2936 block_number: BlockNumberFor<T>,
2937 ) -> DispatchResult {
2938 let provider_info = Self::ensure_valid_delegation(provider, delegator, Some(block_number))?;
2939
2940 let permission_revoked_at_block_number = provider_info
2941 .permissions
2942 .get(&intent_id)
2943 .ok_or(Error::<T>::PermissionNotGranted)?;
2944
2945 if *permission_revoked_at_block_number == BlockNumberFor::<T>::zero() {
2946 return Ok(());
2947 }
2948
2949 ensure!(
2950 block_number <= *permission_revoked_at_block_number,
2951 Error::<T>::PermissionNotGranted
2952 );
2953
2954 Ok(())
2955 }
2956}
2957
2958impl<T: Config> MsaKeyProvider for Pallet<T> {
2959 type AccountId = T::AccountId;
2960 fn key_eligible_for_subsidized_addition(
2966 old_key: Self::AccountId,
2967 new_key: Self::AccountId,
2968 msa_id: MessageSourceId,
2969 ) -> bool {
2970 let new_address32 = T::ConvertIntoAccountId32::convert((new_key).clone());
2971 if EthereumAddressMapper::is_ethereum_address(&new_address32) {
2972 if let Some(stored_msa_id) = Self::get_msa_id(&old_key) {
2973 return stored_msa_id == msa_id && PublicKeyCountForMsaId::<T>::get(msa_id).eq(&1u8);
2974 }
2975 }
2976 false
2977 }
2978}
2979
2980#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)]
2985#[scale_info(skip_type_params(T))]
2986pub struct CheckFreeExtrinsicUse<T: Config + Send + Sync>(PhantomData<T>);
2987
2988impl<T: Config + Send + Sync> CheckFreeExtrinsicUse<T> {
2989 pub fn validate_delegation_by_delegator(
3000 account_id: &T::AccountId,
3001 provider_msa_id: &MessageSourceId,
3002 ) -> TransactionValidity {
3003 const TAG_PREFIX: &str = "DelegatorDelegationRevocation";
3004 let delegator_msa_id: DelegatorId = Pallet::<T>::ensure_valid_msa_key(account_id)
3005 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?
3006 .into();
3007 let provider_msa_id = ProviderId(*provider_msa_id);
3008
3009 Pallet::<T>::ensure_valid_delegation(provider_msa_id, delegator_msa_id, None)
3010 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidDelegation as u8))?;
3011 ValidTransaction::with_tag_prefix(TAG_PREFIX).and_provides(account_id).build()
3012 }
3013
3014 pub fn validate_delegation_by_provider(
3025 account_id: &T::AccountId,
3026 delegator_msa_id: &MessageSourceId,
3027 ) -> TransactionValidity {
3028 const TAG_PREFIX: &str = "ProviderDelegationRevocation";
3029
3030 let provider_msa_id: ProviderId = Pallet::<T>::ensure_valid_msa_key(account_id)
3031 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?
3032 .into();
3033 let delegator_msa_id = DelegatorId(*delegator_msa_id);
3034
3035 Pallet::<T>::ensure_valid_delegation(provider_msa_id, delegator_msa_id, None)
3037 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidDelegation as u8))?;
3038 ValidTransaction::with_tag_prefix(TAG_PREFIX).and_provides(account_id).build()
3039 }
3040
3041 pub fn validate_key_delete(
3054 signing_public_key: &T::AccountId,
3055 public_key_to_delete: &T::AccountId,
3056 ) -> TransactionValidity {
3057 const TAG_PREFIX: &str = "KeyRevocation";
3058
3059 ensure!(
3060 signing_public_key != public_key_to_delete,
3061 InvalidTransaction::Custom(ValidityError::InvalidSelfRemoval as u8)
3062 );
3063
3064 let maybe_owner_msa_id: MessageSourceId =
3065 Pallet::<T>::ensure_valid_msa_key(signing_public_key)
3066 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?;
3067
3068 let msa_id_for_key_to_delete: MessageSourceId =
3069 Pallet::<T>::ensure_valid_msa_key(public_key_to_delete)
3070 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?;
3071
3072 ensure!(
3073 maybe_owner_msa_id == msa_id_for_key_to_delete,
3074 InvalidTransaction::Custom(ValidityError::NotKeyOwner as u8)
3075 );
3076
3077 ValidTransaction::with_tag_prefix(TAG_PREFIX)
3078 .and_provides(signing_public_key)
3079 .build()
3080 }
3081
3082 pub fn ensure_msa_can_retire(account_id: &T::AccountId) -> TransactionValidity {
3098 const TAG_PREFIX: &str = "MSARetirement";
3099 let msa_id = Pallet::<T>::ensure_valid_msa_key(account_id)
3100 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?;
3101
3102 ensure!(
3103 !Pallet::<T>::is_registered_provider(msa_id),
3104 InvalidTransaction::Custom(
3105 ValidityError::InvalidRegisteredProviderCannotBeRetired as u8
3106 )
3107 );
3108
3109 let msa_handle = T::HandleProvider::get_handle_for_msa(msa_id);
3110 ensure!(
3111 msa_handle.is_none(),
3112 InvalidTransaction::Custom(ValidityError::HandleNotRetired as u8)
3113 );
3114
3115 let key_count = PublicKeyCountForMsaId::<T>::get(msa_id);
3116 ensure!(
3117 key_count == 1,
3118 InvalidTransaction::Custom(ValidityError::InvalidMoreThanOneKeyExists as u8)
3119 );
3120
3121 let delegator_id = DelegatorId(msa_id);
3122 let has_active_delegations: bool = DelegatorAndProviderToDelegation::<T>::iter_key_prefix(
3123 delegator_id,
3124 )
3125 .any(|provider_id| {
3126 Pallet::<T>::ensure_valid_delegation(provider_id, delegator_id, None).is_ok()
3127 });
3128
3129 ensure!(
3130 !has_active_delegations,
3131 InvalidTransaction::Custom(ValidityError::InvalidNonZeroProviderDelegations as u8)
3132 );
3133
3134 let msa_address = Pallet::<T>::msa_id_to_eth_address(msa_id);
3136
3137 let mut bytes = &EthereumAddressMapper::to_bytes32(&msa_address.0)[..];
3139 let msa_account_id = T::AccountId::decode(&mut bytes).map_err(|_| {
3140 log::error!("Failed to decode MSA account ID from Ethereum address");
3141 InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8)
3142 })?;
3143
3144 let msa_balance = T::Currency::reducible_balance(
3146 &msa_account_id,
3147 Preservation::Expendable,
3148 Fortitude::Polite,
3149 );
3150 ensure!(
3151 msa_balance == Zero::zero(),
3152 InvalidTransaction::Custom(ValidityError::InvalidMsaHoldingTokenCannotBeRetired as u8)
3153 );
3154
3155 ValidTransaction::with_tag_prefix(TAG_PREFIX).and_provides(account_id).build()
3156 }
3157
3158 pub fn validate_msa_token_withdrawal(
3173 receiver_account_id: &T::AccountId,
3174 msa_owner_public_key: &T::AccountId,
3175 msa_owner_proof: &MultiSignature,
3176 authorization_payload: &AuthorizedKeyData<T>,
3177 ) -> TransactionValidity {
3178 const TAG_PREFIX: &str = "MsaTokenWithdrawal";
3179
3180 ensure!(
3181 *receiver_account_id == authorization_payload.authorized_public_key,
3182 InvalidTransaction::Custom(ValidityError::NotKeyOwner as u8)
3183 );
3184
3185 ensure!(
3186 authorization_payload.discriminant == PayloadTypeDiscriminator::AuthorizedKeyData,
3187 InvalidTransaction::Custom(ValidityError::MsaOwnershipInvalidSignature as u8)
3188 );
3189 ensure!(
3190 Pallet::<T>::verify_signature(
3191 msa_owner_proof,
3192 msa_owner_public_key,
3193 authorization_payload
3194 ),
3195 InvalidTransaction::Custom(ValidityError::MsaOwnershipInvalidSignature as u8)
3196 );
3197
3198 ensure!(
3200 !PublicKeyToMsaId::<T>::contains_key(receiver_account_id),
3201 InvalidTransaction::Custom(ValidityError::IneligibleOrigin as u8)
3202 );
3203
3204 Pallet::<T>::check_signature_against_registry(
3205 msa_owner_proof,
3206 authorization_payload.expiration,
3207 )
3208 .map_err(|_| {
3209 InvalidTransaction::Custom(ValidityError::MsaOwnershipInvalidSignature as u8)
3210 })?;
3211
3212 let msa_id = authorization_payload.msa_id;
3213
3214 Pallet::<T>::ensure_msa_owner(msa_owner_public_key, msa_id)
3215 .map_err(|_| InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8))?;
3216
3217 let msa_address = Pallet::<T>::msa_id_to_eth_address(msa_id);
3219
3220 let mut bytes = &EthereumAddressMapper::to_bytes32(&msa_address.0)[..];
3222 let msa_account_id = T::AccountId::decode(&mut bytes).map_err(|_| {
3223 log::error!("Failed to decode MSA account ID from Ethereum address");
3224 InvalidTransaction::Custom(ValidityError::InvalidMsaKey as u8)
3225 })?;
3226
3227 let msa_balance = T::Currency::reducible_balance(
3229 &msa_account_id,
3230 Preservation::Expendable,
3231 Fortitude::Polite,
3232 );
3233 ensure!(
3234 msa_balance > Zero::zero(),
3235 InvalidTransaction::Custom(ValidityError::InsufficientBalanceToWithdraw as u8)
3236 );
3237 ValidTransaction::with_tag_prefix(TAG_PREFIX)
3238 .and_provides(receiver_account_id)
3239 .build()
3240 }
3241}
3242
3243pub enum ValidityError {
3245 InvalidDelegation,
3247 InvalidMsaKey,
3249 InvalidRegisteredProviderCannotBeRetired,
3251 InvalidMoreThanOneKeyExists,
3253 InvalidSelfRemoval,
3255 NotKeyOwner,
3257 InvalidNonZeroProviderDelegations,
3259 HandleNotRetired,
3261 MsaOwnershipInvalidSignature,
3263 InsufficientBalanceToWithdraw,
3265 IneligibleOrigin,
3267 InvalidMsaHoldingTokenCannotBeRetired,
3269}
3270
3271impl<T: Config + Send + Sync> CheckFreeExtrinsicUse<T> {
3272 pub fn new() -> Self {
3274 Self(PhantomData)
3275 }
3276}
3277
3278impl<T: Config + Send + Sync> core::fmt::Debug for CheckFreeExtrinsicUse<T> {
3279 #[cfg(feature = "std")]
3280 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
3281 write!(f, "CheckFreeExtrinsicUse<{:?}>", self.0)
3282 }
3283 #[cfg(not(feature = "std"))]
3284 fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
3285 Ok(())
3286 }
3287}
3288
3289#[derive(RuntimeDebugNoBound)]
3291pub enum Val {
3292 Valid,
3294 Refund(Weight),
3296}
3297
3298#[derive(RuntimeDebugNoBound)]
3300pub enum Pre {
3301 Valid,
3303 Refund(Weight),
3305}
3306
3307impl<T: Config + Send + Sync> TransactionExtension<T::RuntimeCall> for CheckFreeExtrinsicUse<T>
3308where
3309 T::RuntimeCall: Dispatchable<Info = DispatchInfo> + IsSubType<Call<T>>,
3310 <T as frame_system::Config>::RuntimeOrigin: AsSystemOriginSigner<T::AccountId> + Clone,
3311{
3312 const IDENTIFIER: &'static str = "CheckFreeExtrinsicUse";
3313 type Implicit = ();
3314 type Val = Val;
3315 type Pre = Pre;
3316
3317 fn weight(&self, call: &T::RuntimeCall) -> Weight {
3318 match call.is_sub_type() {
3319 Some(Call::revoke_delegation_by_provider { .. }) =>
3320 T::WeightInfo::check_free_extrinsic_use_revoke_delegation_by_provider(),
3321 Some(Call::revoke_delegation_by_delegator { .. }) =>
3322 T::WeightInfo::check_free_extrinsic_use_revoke_delegation_by_delegator(),
3323 Some(Call::delete_msa_public_key { .. }) =>
3324 T::WeightInfo::check_free_extrinsic_use_delete_msa_public_key(),
3325 Some(Call::retire_msa { .. }) => T::WeightInfo::check_free_extrinsic_use_retire_msa(),
3326 Some(Call::withdraw_tokens { .. }) =>
3327 T::WeightInfo::check_free_extrinsic_use_withdraw_tokens(),
3328 _ => Weight::zero(),
3329 }
3330 }
3331
3332 fn validate(
3333 &self,
3334 origin: <T as frame_system::Config>::RuntimeOrigin,
3335 call: &T::RuntimeCall,
3336 _info: &DispatchInfoOf<T::RuntimeCall>,
3337 _len: usize,
3338 _self_implicit: Self::Implicit,
3339 _inherited_implication: &impl Encode,
3340 _source: TransactionSource,
3341 ) -> ValidateResult<Self::Val, T::RuntimeCall> {
3342 let weight = self.weight(call);
3343 let Some(who) = origin.as_system_origin_signer() else {
3344 return Ok((ValidTransaction::default(), Val::Refund(weight), origin));
3345 };
3346 let validity = match call.is_sub_type() {
3347 Some(Call::revoke_delegation_by_provider { delegator, .. }) =>
3348 Self::validate_delegation_by_provider(who, delegator),
3349 Some(Call::revoke_delegation_by_delegator { provider_msa_id, .. }) =>
3350 Self::validate_delegation_by_delegator(who, provider_msa_id),
3351 Some(Call::delete_msa_public_key { public_key_to_delete, .. }) =>
3352 Self::validate_key_delete(who, public_key_to_delete),
3353 Some(Call::retire_msa { .. }) => Self::ensure_msa_can_retire(who),
3354 Some(Call::withdraw_tokens {
3355 msa_owner_public_key,
3356 msa_owner_proof,
3357 authorization_payload,
3358 }) => Self::validate_msa_token_withdrawal(
3359 who,
3360 msa_owner_public_key,
3361 msa_owner_proof,
3362 authorization_payload,
3363 ),
3364 _ => Ok(Default::default()),
3365 };
3366 validity.map(|v| (v, Val::Valid, origin))
3367 }
3368
3369 fn prepare(
3370 self,
3371 val: Self::Val,
3372 _origin: &<T as frame_system::Config>::RuntimeOrigin,
3373 _call: &T::RuntimeCall,
3374 _info: &DispatchInfoOf<T::RuntimeCall>,
3375 _len: usize,
3376 ) -> Result<Self::Pre, TransactionValidityError> {
3377 match val {
3378 Val::Valid => Ok(Pre::Valid),
3379 Val::Refund(w) => Ok(Pre::Refund(w)),
3380 }
3381 }
3382
3383 fn post_dispatch_details(
3384 pre: Self::Pre,
3385 _info: &DispatchInfo,
3386 _post_info: &PostDispatchInfoOf<T::RuntimeCall>,
3387 _len: usize,
3388 _result: &sp_runtime::DispatchResult,
3389 ) -> Result<Weight, TransactionValidityError> {
3390 match pre {
3391 Pre::Valid => Ok(Weight::zero()),
3392 Pre::Refund(w) => Ok(w),
3393 }
3394 }
3395}