1use crate::*;
21use codec::{Decode, DecodeWithMemTracking, Encode};
22use core::fmt;
23use frame_support::{
24 ensure, pallet_prelude::TransactionSource, traits::reality::Context, weights::Weight,
25 CloneNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound,
26};
27use frame_system::{CheckNonce, ValidNonceInfo};
28use scale_info::TypeInfo;
29use sp_core::twox_64;
30use sp_runtime::{
31 traits::{DispatchInfoOf, TransactionExtension, ValidateResult},
32 transaction_validity::{InvalidTransaction, TransactionValidityError, ValidTransaction},
33 Saturating,
34};
35
36#[derive(
38 Encode, Decode, TypeInfo, EqNoBound, CloneNoBound, PartialEqNoBound, DecodeWithMemTracking,
39)]
40#[scale_info(skip_type_params(T))]
41pub enum AsPersonInfo<T: Config + Send + Sync> {
42 AsPersonalAliasWithAccount(T::Nonce),
44 AsPersonalAliasWithProof(<T::Crypto as GenerateVerifiable>::Proof, RingIndex, Context),
54 AsPersonalIdentityWithProof(<T::Crypto as GenerateVerifiable>::Signature, PersonalId),
64 AsPersonalIdentityWithAccount(T::Nonce),
66}
67
68#[derive(
70 Encode,
71 Decode,
72 TypeInfo,
73 EqNoBound,
74 CloneNoBound,
75 PartialEqNoBound,
76 DefaultNoBound,
77 DecodeWithMemTracking,
78)]
79#[scale_info(skip_type_params(T))]
80pub struct AsPerson<T: Config + Send + Sync>(Option<AsPersonInfo<T>>);
81
82impl<T: Config + Send + Sync> fmt::Debug for AsPerson<T> {
83 #[cfg(feature = "std")]
84 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85 write!(f, "AsPerson")
86 }
87
88 #[cfg(not(feature = "std"))]
89 fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
90 Ok(())
91 }
92}
93
94impl<T: Config + Send + Sync> AsPerson<T> {
95 pub fn new(explicit: Option<AsPersonInfo<T>>) -> Self {
96 Self(explicit)
97 }
98}
99
100pub enum Val<T: Config + Send + Sync> {
102 NotUsing,
103 UsingProof,
104 UsingAccount(T::AccountId, T::Nonce),
105}
106
107impl<T: Config + Send + Sync> TransactionExtension<<T as frame_system::Config>::RuntimeCall>
108 for AsPerson<T>
109{
110 const IDENTIFIER: &'static str = "AsPerson";
111 type Implicit = ();
112
113 type Val = Val<T>;
114 type Pre = ();
115
116 fn weight(&self, _call: &<T as frame_system::Config>::RuntimeCall) -> Weight {
117 match self.0 {
118 None => Weight::zero(),
120 Some(AsPersonInfo::AsPersonalAliasWithAccount(_)) =>
122 T::WeightInfo::as_person_alias_with_account(),
123 Some(AsPersonInfo::AsPersonalAliasWithProof(_, _, _)) =>
125 T::WeightInfo::as_person_alias_with_proof(),
126 Some(AsPersonInfo::AsPersonalIdentityWithProof(_, _)) =>
128 T::WeightInfo::as_person_identity_with_proof(),
129 Some(AsPersonInfo::AsPersonalIdentityWithAccount(_)) =>
131 T::WeightInfo::as_person_identity_with_account(),
132 }
133 }
134
135 fn validate(
136 &self,
137 origin: <T as frame_system::Config>::RuntimeOrigin,
138 call: &<T as frame_system::Config>::RuntimeCall,
139 _info: &DispatchInfoOf<<T as frame_system::Config>::RuntimeCall>,
140 _len: usize,
141 _self_implicit: Self::Implicit,
142 inherited_implication: &impl Encode,
143 _source: TransactionSource,
144 ) -> ValidateResult<Self::Val, <T as frame_system::Config>::RuntimeCall> {
145 match &self.0 {
146 Some(AsPersonInfo::AsPersonalAliasWithAccount(nonce)) => {
147 let Some(frame_system::Origin::<T>::Signed(who)) = origin.as_system_ref() else {
148 return Err(InvalidTransaction::BadSigner.into());
149 };
150 let who = who.clone();
151
152 let rev_ca = AccountToAlias::<T>::get(&who).ok_or(InvalidTransaction::BadSigner)?;
153 ensure!(
154 Root::<T>::get(rev_ca.ring)
155 .is_some_and(|ring| ring.revision == rev_ca.revision),
156 InvalidTransaction::BadSigner,
157 );
158
159 let local_origin = Origin::PersonalAlias(rev_ca);
160 let mut origin = origin;
161 origin.set_caller_from(local_origin);
162
163 let ValidNonceInfo { requires, provides } =
164 CheckNonce::<T>::validate_nonce_for_account(&who, *nonce)?;
165 let validity = ValidTransaction { requires, provides, ..Default::default() };
166
167 Ok((validity, Val::UsingAccount(who, *nonce), origin))
168 },
169 Some(AsPersonInfo::AsPersonalIdentityWithAccount(nonce)) => {
170 let Some(frame_system::Origin::<T>::Signed(who)) = origin.as_system_ref() else {
171 return Err(InvalidTransaction::BadSigner.into());
172 };
173 let who = who.clone();
174
175 let id =
176 AccountToPersonalId::<T>::get(&who).ok_or(InvalidTransaction::BadSigner)?;
177 let local_origin = Origin::PersonalIdentity(id);
178 let mut origin = origin;
179 origin.set_caller_from(local_origin);
180
181 let ValidNonceInfo { requires, provides } =
182 CheckNonce::<T>::validate_nonce_for_account(&who, *nonce)?;
183 let validity = ValidTransaction { requires, provides, ..Default::default() };
184
185 Ok((validity, Val::UsingAccount(who, *nonce), origin))
186 },
187 Some(AsPersonInfo::AsPersonalAliasWithProof(proof, ring_index, context)) => {
188 ensure!(
189 matches!(origin.as_system_ref(), Some(frame_system::RawOrigin::None)),
190 InvalidTransaction::BadSigner
191 );
192
193 let Some(Call::<T>::set_alias_account { account, call_valid_at }) =
194 call.is_sub_type()
195 else {
196 return Err(InvalidTransaction::Call.into());
197 };
198
199 let ring = Root::<T>::get(ring_index).ok_or(InvalidTransaction::Call)?;
200 let now = frame_system::Pallet::<T>::block_number();
201 if now < *call_valid_at {
202 return Err(InvalidTransaction::Future.into());
203 }
204 let time_tolerance = Pallet::<T>::account_setup_time_tolerance();
205 if now > call_valid_at.saturating_add(time_tolerance) {
206 return Err(InvalidTransaction::Stale.into());
207 }
208
209 let msg = inherited_implication.using_encoded(sp_io::hashing::blake2_256);
210
211 let alias = T::Crypto::validate(proof, &ring.root, &context[..], &msg[..])
212 .map_err(|_| InvalidTransaction::BadProof)?;
213
214 let rev_ca = RevisedContextualAlias {
215 revision: ring.revision,
216 ring: *ring_index,
217 ca: ContextualAlias { alias, context: *context },
218 };
219
220 if AccountToAlias::<T>::get(account)
222 .is_some_and(|stored_rev_ca| stored_rev_ca == rev_ca)
223 {
224 return Err(InvalidTransaction::Stale.into());
225 }
226
227 let provides = twox_64(&("setup", &rev_ca, &account).encode()[..]);
229 let valid_transaction =
230 ValidTransaction::with_tag_prefix("Ppl:Alias").and_provides(provides).into();
231
232 let local_origin = Origin::PersonalAlias(rev_ca);
234 let mut origin = origin;
235 origin.set_caller_from(local_origin);
236
237 Ok((valid_transaction, Val::UsingProof, origin))
238 },
239 Some(AsPersonInfo::AsPersonalIdentityWithProof(signature, index)) => {
240 ensure!(
241 matches!(origin.as_system_ref(), Some(frame_system::RawOrigin::None)),
242 InvalidTransaction::BadSigner
243 );
244
245 let Some(Call::<T>::set_personal_id_account { account, call_valid_at }) =
246 call.is_sub_type()
247 else {
248 return Err(InvalidTransaction::Call.into());
249 };
250
251 let now = frame_system::Pallet::<T>::block_number();
252 if now < *call_valid_at {
253 return Err(InvalidTransaction::Future.into());
254 }
255 let time_tolerance = Pallet::<T>::account_setup_time_tolerance();
256 if now > call_valid_at.saturating_add(time_tolerance) {
257 return Err(InvalidTransaction::Stale.into());
258 }
259
260 let key = People::<T>::get(index)
261 .map(|record| record.key)
262 .ok_or(InvalidTransaction::BadSigner)?;
263
264 let msg = inherited_implication.using_encoded(sp_io::hashing::blake2_256);
265
266 if !T::Crypto::verify_signature(signature, &msg[..], &key) {
267 return Err(InvalidTransaction::BadProof.into());
268 }
269
270 if People::<T>::get(index).is_some_and(|record| {
272 record.account.is_some_and(|stored_account| stored_account == *account)
273 }) {
274 return Err(InvalidTransaction::Stale.into());
275 }
276
277 let provides = twox_64(&("setup", index, &account).encode()[..]);
279 let valid_transaction =
280 ValidTransaction::with_tag_prefix("Ppl:Id").and_provides(provides).into();
281
282 let local_origin = Origin::PersonalIdentity(*index);
284 let mut origin = origin;
285 origin.set_caller_from(local_origin);
286
287 Ok((valid_transaction, Val::UsingProof, origin))
288 },
289 None => Ok((ValidTransaction::default(), Val::NotUsing, origin)),
290 }
291 }
292
293 fn prepare(
294 self,
295 val: Self::Val,
296 _origin: &<T as frame_system::Config>::RuntimeOrigin,
297 _call: &<T as frame_system::Config>::RuntimeCall,
298 _info: &DispatchInfoOf<<T as frame_system::Config>::RuntimeCall>,
299 _len: usize,
300 ) -> Result<Self::Pre, TransactionValidityError> {
301 match val {
302 Val::UsingAccount(who, nonce) =>
303 CheckNonce::<T>::prepare_nonce_for_account(&who, nonce)?,
304 Val::NotUsing | Val::UsingProof => (),
305 }
306
307 Ok(())
308 }
309}