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_crypto_hashing::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 },
124 Some(AsPersonInfo::AsPersonalAliasWithProof(_, _, _)) => {
126 T::WeightInfo::as_person_alias_with_proof()
127 },
128 Some(AsPersonInfo::AsPersonalIdentityWithProof(_, _)) => {
130 T::WeightInfo::as_person_identity_with_proof()
131 },
132 Some(AsPersonInfo::AsPersonalIdentityWithAccount(_)) => {
134 T::WeightInfo::as_person_identity_with_account()
135 },
136 }
137 }
138
139 fn validate(
140 &self,
141 origin: <T as frame_system::Config>::RuntimeOrigin,
142 call: &<T as frame_system::Config>::RuntimeCall,
143 _info: &DispatchInfoOf<<T as frame_system::Config>::RuntimeCall>,
144 _len: usize,
145 _self_implicit: Self::Implicit,
146 inherited_implication: &impl Encode,
147 _source: TransactionSource,
148 ) -> ValidateResult<Self::Val, <T as frame_system::Config>::RuntimeCall> {
149 match &self.0 {
150 Some(AsPersonInfo::AsPersonalAliasWithAccount(nonce)) => {
151 let Some(frame_system::Origin::<T>::Signed(who)) = origin.as_system_ref() else {
152 return Err(InvalidTransaction::BadSigner.into());
153 };
154 let who = who.clone();
155
156 let rev_ca = AccountToAlias::<T>::get(&who).ok_or(InvalidTransaction::BadSigner)?;
157 ensure!(
158 Root::<T>::get(rev_ca.ring)
159 .is_some_and(|ring| ring.revision == rev_ca.revision),
160 InvalidTransaction::BadSigner,
161 );
162
163 let local_origin = Origin::PersonalAlias(rev_ca);
164 let mut origin = origin;
165 origin.set_caller_from(local_origin);
166
167 let ValidNonceInfo { requires, provides } =
168 CheckNonce::<T>::validate_nonce_for_account(&who, *nonce)?;
169 let validity = ValidTransaction { requires, provides, ..Default::default() };
170
171 Ok((validity, Val::UsingAccount(who, *nonce), origin))
172 },
173 Some(AsPersonInfo::AsPersonalIdentityWithAccount(nonce)) => {
174 let Some(frame_system::Origin::<T>::Signed(who)) = origin.as_system_ref() else {
175 return Err(InvalidTransaction::BadSigner.into());
176 };
177 let who = who.clone();
178
179 let id =
180 AccountToPersonalId::<T>::get(&who).ok_or(InvalidTransaction::BadSigner)?;
181 let local_origin = Origin::PersonalIdentity(id);
182 let mut origin = origin;
183 origin.set_caller_from(local_origin);
184
185 let ValidNonceInfo { requires, provides } =
186 CheckNonce::<T>::validate_nonce_for_account(&who, *nonce)?;
187 let validity = ValidTransaction { requires, provides, ..Default::default() };
188
189 Ok((validity, Val::UsingAccount(who, *nonce), origin))
190 },
191 Some(AsPersonInfo::AsPersonalAliasWithProof(proof, ring_index, context)) => {
192 ensure!(
193 matches!(origin.as_system_ref(), Some(frame_system::RawOrigin::None)),
194 InvalidTransaction::BadSigner
195 );
196
197 let Some(Call::<T>::set_alias_account { account, call_valid_at }) =
198 call.is_sub_type()
199 else {
200 return Err(InvalidTransaction::Call.into());
201 };
202
203 let ring = Root::<T>::get(ring_index).ok_or(InvalidTransaction::Call)?;
204 let now = frame_system::Pallet::<T>::block_number();
205 if now < *call_valid_at {
206 return Err(InvalidTransaction::Future.into());
207 }
208 let time_tolerance = Pallet::<T>::account_setup_time_tolerance();
209 if now > call_valid_at.saturating_add(time_tolerance) {
210 return Err(InvalidTransaction::Stale.into());
211 }
212
213 let msg = inherited_implication.using_encoded(sp_io::hashing::blake2_256);
214
215 let alias = T::Crypto::validate(proof, &ring.root, &context[..], &msg[..])
216 .map_err(|_| InvalidTransaction::BadProof)?;
217
218 let rev_ca = RevisedContextualAlias {
219 revision: ring.revision,
220 ring: *ring_index,
221 ca: ContextualAlias { alias, context: *context },
222 };
223
224 if AccountToAlias::<T>::get(account)
226 .is_some_and(|stored_rev_ca| stored_rev_ca == rev_ca)
227 {
228 return Err(InvalidTransaction::Stale.into());
229 }
230
231 let provides = twox_64(&("setup", &rev_ca, &account).encode()[..]);
233 let valid_transaction =
234 ValidTransaction::with_tag_prefix("Ppl:Alias").and_provides(provides).into();
235
236 let local_origin = Origin::PersonalAlias(rev_ca);
238 let mut origin = origin;
239 origin.set_caller_from(local_origin);
240
241 Ok((valid_transaction, Val::UsingProof, origin))
242 },
243 Some(AsPersonInfo::AsPersonalIdentityWithProof(signature, index)) => {
244 ensure!(
245 matches!(origin.as_system_ref(), Some(frame_system::RawOrigin::None)),
246 InvalidTransaction::BadSigner
247 );
248
249 let Some(Call::<T>::set_personal_id_account { account, call_valid_at }) =
250 call.is_sub_type()
251 else {
252 return Err(InvalidTransaction::Call.into());
253 };
254
255 let now = frame_system::Pallet::<T>::block_number();
256 if now < *call_valid_at {
257 return Err(InvalidTransaction::Future.into());
258 }
259 let time_tolerance = Pallet::<T>::account_setup_time_tolerance();
260 if now > call_valid_at.saturating_add(time_tolerance) {
261 return Err(InvalidTransaction::Stale.into());
262 }
263
264 let key = People::<T>::get(index)
265 .map(|record| record.key)
266 .ok_or(InvalidTransaction::BadSigner)?;
267
268 let msg = inherited_implication.using_encoded(sp_io::hashing::blake2_256);
269
270 if !T::Crypto::verify_signature(signature, &msg[..], &key) {
271 return Err(InvalidTransaction::BadProof.into());
272 }
273
274 if People::<T>::get(index).is_some_and(|record| {
276 record.account.is_some_and(|stored_account| stored_account == *account)
277 }) {
278 return Err(InvalidTransaction::Stale.into());
279 }
280
281 let provides = twox_64(&("setup", index, &account).encode()[..]);
283 let valid_transaction =
284 ValidTransaction::with_tag_prefix("Ppl:Id").and_provides(provides).into();
285
286 let local_origin = Origin::PersonalIdentity(*index);
288 let mut origin = origin;
289 origin.set_caller_from(local_origin);
290
291 Ok((valid_transaction, Val::UsingProof, origin))
292 },
293 None => Ok((ValidTransaction::default(), Val::NotUsing, origin)),
294 }
295 }
296
297 fn prepare(
298 self,
299 val: Self::Val,
300 _origin: &<T as frame_system::Config>::RuntimeOrigin,
301 _call: &<T as frame_system::Config>::RuntimeCall,
302 _info: &DispatchInfoOf<<T as frame_system::Config>::RuntimeCall>,
303 _len: usize,
304 ) -> Result<Self::Pre, TransactionValidityError> {
305 match val {
306 Val::UsingAccount(who, nonce) => {
307 CheckNonce::<T>::prepare_nonce_for_account(&who, nonce)?
308 },
309 Val::NotUsing | Val::UsingProof => (),
310 }
311
312 Ok(())
313 }
314}