1
// Copyright 2019-2023 PureStake Inc.
2
// This file is part of Moonbeam.
3

            
4
// Moonbeam is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Moonbeam is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
//! Precompile to receive GMP callbacks and forward to XCM
18

            
19
#![cfg_attr(not(feature = "std"), no_std)]
20

            
21
extern crate alloc;
22

            
23
use fp_evm::PrecompileHandle;
24
use frame_support::dispatch::{GetDispatchInfo, PostDispatchInfo};
25
use frame_support::sp_runtime::traits::StaticLookup;
26
use frame_support::traits::Currency;
27
use pallet_evm::AddressMapping;
28
use pallet_identity::legacy::IdentityField;
29
use parity_scale_codec::MaxEncodedLen;
30
use precompile_utils::prelude::*;
31
use sp_core::{ConstU32, Get, H160, H256, U256};
32
use sp_runtime::traits::Dispatchable;
33
use sp_std::boxed::Box;
34
use sp_std::marker::PhantomData;
35
use sp_std::vec::Vec;
36

            
37
#[cfg(test)]
38
mod mock;
39
#[cfg(test)]
40
mod tests;
41

            
42
type BalanceOf<T> = <<T as pallet_identity::Config>::Currency as Currency<
43
	<T as frame_system::Config>::AccountId,
44
>>::Balance;
45

            
46
type IdentityFieldOf<T> = <<T as pallet_identity::Config>::IdentityInformation
47
	as pallet_identity::IdentityInformationProvider>::FieldsIdentifier;
48

            
49
/// Solidity selector of the Vote log, which is the Keccak of the Log signature.
50
pub(crate) const SELECTOR_LOG_IDENTITY_SET: [u8; 32] = keccak256!("IdentitySet(address)");
51
pub(crate) const SELECTOR_LOG_IDENTITY_CLEARED: [u8; 32] = keccak256!("IdentityCleared(address)");
52
pub(crate) const SELECTOR_LOG_JUDGEMENT_REQUESTED: [u8; 32] =
53
	keccak256!("JudgementRequested(address,uint32)");
54
pub(crate) const SELECTOR_LOG_JUDGEMENT_UNREQUESTED: [u8; 32] =
55
	keccak256!("JudgementUnrequested(address,uint32)");
56
pub(crate) const SELECTOR_LOG_JUDGEMENT_GIVEN: [u8; 32] =
57
	keccak256!("JudgementGiven(address,uint32)");
58
pub(crate) const SELECTOR_LOG_SUB_IDENTITY_ADDED: [u8; 32] =
59
	keccak256!("SubIdentityAdded(address,address)");
60
pub(crate) const SELECTOR_LOG_SUB_IDENTITY_REMOVED: [u8; 32] =
61
	keccak256!("SubIdentityRemoved(address,address)");
62
pub(crate) const SELECTOR_LOG_SUB_IDENTITY_REVOKED: [u8; 32] =
63
	keccak256!("SubIdentityRevoked(address)");
64

            
65
/// A precompile to wrap the functionality from pallet-identity
66
pub struct IdentityPrecompile<Runtime, MaxAdditionalFields>(
67
	PhantomData<(Runtime, MaxAdditionalFields)>,
68
);
69

            
70
287
#[precompile_utils::precompile]
71
#[precompile::test_concrete_types(mock::Runtime, mock::MaxAdditionalFields)]
72
impl<Runtime, MaxAdditionalFields> IdentityPrecompile<Runtime, MaxAdditionalFields>
73
where
74
	MaxAdditionalFields: Get<u32> + 'static,
75
	Runtime: pallet_evm::Config
76
		+ pallet_identity::Config<
77
			IdentityInformation = pallet_identity::legacy::IdentityInfo<MaxAdditionalFields>,
78
		>,
79
	Runtime::AccountId: Into<H160>,
80
	Runtime::Hash: From<H256>,
81
	Runtime::RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo,
82
	<Runtime::RuntimeCall as Dispatchable>::RuntimeOrigin: From<Option<Runtime::AccountId>>,
83
	Runtime::RuntimeCall: From<pallet_identity::Call<Runtime>>,
84
	BalanceOf<Runtime>: TryFrom<U256> + Into<U256> + solidity::Codec,
85
{
86
	// Note: addRegistrar(address) & killIdentity(address) are not supported since they use a
87
	// force origin.
88

            
89
	// editorconfig-checker-disable
90
	#[precompile::public("setIdentity((((bool,bytes),(bool,bytes))[],(bool,bytes),(bool,bytes),(bool,bytes),(bool,bytes),(bool,bytes),bool,bytes,(bool,bytes),(bool,bytes)))")]
91
12
	fn set_identity(
92
12
		handle: &mut impl PrecompileHandle,
93
12
		info: IdentityInfo<MaxAdditionalFields>,
94
12
	) -> EvmResult {
95
12
		// editorconfig-checker-enable
96
12
		let caller = handle.context().caller;
97
12

            
98
12
		let event = log1(
99
12
			handle.context().address,
100
12
			SELECTOR_LOG_IDENTITY_SET,
101
12
			solidity::encode_event_data(Address(caller)),
102
12
		);
103
12
		handle.record_log_costs(&[&event])?;
104

            
105
12
		let info: Box<Runtime::IdentityInformation> = Self::identity_to_input(info)?;
106

            
107
12
		let call = pallet_identity::Call::<Runtime>::set_identity { info };
108
12

            
109
12
		let origin = Runtime::AddressMapping::into_account_id(caller);
110
12
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
111

            
112
12
		event.record(handle)?;
113

            
114
12
		Ok(())
115
12
	}
116

            
117
	#[precompile::public("setSubs((address,(bool,bytes))[])")]
118
2
	fn set_subs(
119
2
		handle: &mut impl PrecompileHandle,
120
2
		subs: BoundedVec<(Address, Data), Runtime::MaxSubAccounts>,
121
2
	) -> EvmResult {
122
2
		let subs: Vec<_> = subs.into();
123
2
		let mut call_subs = Vec::with_capacity(subs.len());
124
4
		for (i, (addr, data)) in subs.into_iter().enumerate() {
125
4
			let addr = Runtime::AddressMapping::into_account_id(addr.into());
126
4
			let data: pallet_identity::Data = data
127
4
				.try_into()
128
4
				.map_err(|e| RevertReason::custom(e).in_field(alloc::format!("index {i}")))?;
129
4
			call_subs.push((addr, data));
130
		}
131
2
		let call = pallet_identity::Call::<Runtime>::set_subs { subs: call_subs };
132
2

            
133
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
134
2
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
135

            
136
1
		Ok(())
137
2
	}
138

            
139
	#[precompile::public("clearIdentity()")]
140
2
	fn clear_identity(handle: &mut impl PrecompileHandle) -> EvmResult {
141
2
		let caller = handle.context().caller;
142
2

            
143
2
		let event = log1(
144
2
			handle.context().address,
145
2
			SELECTOR_LOG_IDENTITY_CLEARED,
146
2
			solidity::encode_event_data(Address(caller)),
147
2
		);
148
2
		handle.record_log_costs(&[&event])?;
149

            
150
2
		let call = pallet_identity::Call::<Runtime>::clear_identity {};
151
2

            
152
2
		let origin = Runtime::AddressMapping::into_account_id(caller);
153
2
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
154

            
155
1
		event.record(handle)?;
156

            
157
1
		Ok(())
158
2
	}
159

            
160
	#[precompile::public("requestJudgement(uint32,uint256)")]
161
3
	fn request_judgement(
162
3
		handle: &mut impl PrecompileHandle,
163
3
		reg_index: u32,
164
3
		max_fee: U256,
165
3
	) -> EvmResult {
166
3
		let caller = handle.context().caller;
167
3

            
168
3
		let event = log1(
169
3
			handle.context().address,
170
3
			SELECTOR_LOG_JUDGEMENT_REQUESTED,
171
3
			solidity::encode_event_data((Address(caller), reg_index)),
172
3
		);
173
3
		handle.record_log_costs(&[&event])?;
174

            
175
3
		let max_fee = max_fee
176
3
			.try_into()
177
3
			.map_err(|_| RevertReason::value_is_too_large("max_fee"))?;
178
3
		let call = pallet_identity::Call::<Runtime>::request_judgement { reg_index, max_fee };
179
3

            
180
3
		let origin = Runtime::AddressMapping::into_account_id(caller);
181
3
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
182

            
183
3
		event.record(handle)?;
184

            
185
3
		Ok(())
186
3
	}
187

            
188
	#[precompile::public("cancelRequest(uint32)")]
189
1
	fn cancel_request(handle: &mut impl PrecompileHandle, reg_index: u32) -> EvmResult {
190
1
		let caller = handle.context().caller;
191
1

            
192
1
		let event = log1(
193
1
			handle.context().address,
194
1
			SELECTOR_LOG_JUDGEMENT_UNREQUESTED,
195
1
			solidity::encode_event_data((Address(caller), reg_index)),
196
1
		);
197
1
		handle.record_log_costs(&[&event])?;
198

            
199
1
		let call = pallet_identity::Call::<Runtime>::cancel_request { reg_index };
200
1

            
201
1
		let origin = Runtime::AddressMapping::into_account_id(caller);
202
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
203

            
204
1
		event.record(handle)?;
205

            
206
1
		Ok(())
207
1
	}
208

            
209
	#[precompile::public("setFee(uint32,uint256)")]
210
5
	fn set_fee(handle: &mut impl PrecompileHandle, index: u32, fee: U256) -> EvmResult {
211
5
		let fee = fee
212
5
			.try_into()
213
5
			.map_err(|_| RevertReason::value_is_too_large("fee"))?;
214
5
		let call = pallet_identity::Call::<Runtime>::set_fee { index, fee };
215
5

            
216
5
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
217
5
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
218

            
219
4
		Ok(())
220
5
	}
221

            
222
	#[precompile::public("setAccountId(uint32,address)")]
223
2
	fn set_account_id(handle: &mut impl PrecompileHandle, index: u32, new: Address) -> EvmResult {
224
2
		let new = Runtime::Lookup::unlookup(Runtime::AddressMapping::into_account_id(new.0));
225
2
		let call = pallet_identity::Call::<Runtime>::set_account_id { index, new };
226
2

            
227
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
228
2
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
229

            
230
1
		Ok(())
231
2
	}
232

            
233
	#[precompile::public("setFields(uint32,(bool,bool,bool,bool,bool,bool,bool,bool))")]
234
2
	fn set_fields(
235
2
		handle: &mut impl PrecompileHandle,
236
2
		index: u32,
237
2
		fields: IdentityFields,
238
2
	) -> EvmResult {
239
2
		let fields = Self::identity_fields_to_input(fields);
240
2
		let call = pallet_identity::Call::<Runtime>::set_fields { index, fields };
241
2

            
242
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
243
2
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
244

            
245
1
		Ok(())
246
2
	}
247

            
248
	#[precompile::public(
249
		"provideJudgement(uint32,address,(bool,bool,uint256,bool,bool,bool,bool,bool),bytes32)"
250
	)]
251
1
	fn provide_judgement(
252
1
		handle: &mut impl PrecompileHandle,
253
1
		reg_index: u32,
254
1
		target: Address,
255
1
		judgement: Judgement,
256
1
		identity: H256,
257
1
	) -> EvmResult {
258
1
		let caller = handle.context().caller;
259
1

            
260
1
		let event = log1(
261
1
			handle.context().address,
262
1
			SELECTOR_LOG_JUDGEMENT_GIVEN,
263
1
			solidity::encode_event_data((target, reg_index)),
264
1
		);
265
1
		handle.record_log_costs(&[&event])?;
266

            
267
1
		let target = Runtime::Lookup::unlookup(Runtime::AddressMapping::into_account_id(target.0));
268
1
		let judgement = Self::judgment_to_input(judgement)?;
269
1
		let identity: Runtime::Hash = identity.into();
270
1
		let call = pallet_identity::Call::<Runtime>::provide_judgement {
271
1
			reg_index,
272
1
			target,
273
1
			judgement,
274
1
			identity,
275
1
		};
276
1

            
277
1
		let origin = Runtime::AddressMapping::into_account_id(caller);
278
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
279

            
280
1
		event.record(handle)?;
281

            
282
1
		Ok(())
283
1
	}
284

            
285
	#[precompile::public("addSub(address,(bool,bytes))")]
286
4
	fn add_sub(handle: &mut impl PrecompileHandle, sub: Address, data: Data) -> EvmResult {
287
4
		let caller = handle.context().caller;
288
4

            
289
4
		let event = log1(
290
4
			handle.context().address,
291
4
			SELECTOR_LOG_SUB_IDENTITY_ADDED,
292
4
			solidity::encode_event_data((sub, Address(caller))),
293
4
		);
294
4
		handle.record_log_costs(&[&event])?;
295

            
296
4
		let sub = Runtime::Lookup::unlookup(Runtime::AddressMapping::into_account_id(sub.0));
297
4
		let data: pallet_identity::Data = data
298
4
			.try_into()
299
4
			.map_err(|e| RevertReason::custom(e).in_field("data"))?;
300
4
		let call = pallet_identity::Call::<Runtime>::add_sub { sub, data };
301
4

            
302
4
		let origin = Runtime::AddressMapping::into_account_id(caller);
303
4
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
304

            
305
4
		event.record(handle)?;
306

            
307
4
		Ok(())
308
4
	}
309

            
310
	#[precompile::public("renameSub(address,(bool,bytes))")]
311
1
	fn rename_sub(handle: &mut impl PrecompileHandle, sub: Address, data: Data) -> EvmResult {
312
1
		let sub = Runtime::Lookup::unlookup(Runtime::AddressMapping::into_account_id(sub.0));
313
1
		let data: pallet_identity::Data = data
314
1
			.try_into()
315
1
			.map_err(|e| RevertReason::custom(e).in_field("data"))?;
316
1
		let call = pallet_identity::Call::<Runtime>::rename_sub { sub, data };
317
1

            
318
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
319
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
320

            
321
1
		Ok(())
322
1
	}
323

            
324
	#[precompile::public("removeSub(address)")]
325
1
	fn remove_sub(handle: &mut impl PrecompileHandle, sub: Address) -> EvmResult {
326
1
		let caller = handle.context().caller;
327
1

            
328
1
		let event = log1(
329
1
			handle.context().address,
330
1
			SELECTOR_LOG_SUB_IDENTITY_REMOVED,
331
1
			solidity::encode_event_data((sub, Address(caller))),
332
1
		);
333
1
		handle.record_log_costs(&[&event])?;
334

            
335
1
		let sub = Runtime::Lookup::unlookup(Runtime::AddressMapping::into_account_id(sub.0));
336
1
		let call = pallet_identity::Call::<Runtime>::remove_sub { sub };
337
1

            
338
1
		let origin = Runtime::AddressMapping::into_account_id(caller);
339
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
340

            
341
1
		event.record(handle)?;
342

            
343
1
		Ok(())
344
1
	}
345

            
346
	#[precompile::public("quitSub()")]
347
1
	fn quit_sub(handle: &mut impl PrecompileHandle) -> EvmResult {
348
1
		let caller = handle.context().caller;
349
1

            
350
1
		let event = log1(
351
1
			handle.context().address,
352
1
			SELECTOR_LOG_SUB_IDENTITY_REVOKED,
353
1
			solidity::encode_event_data(Address(caller)),
354
1
		);
355
1
		handle.record_log_costs(&[&event])?;
356

            
357
1
		let call = pallet_identity::Call::<Runtime>::quit_sub {};
358
1

            
359
1
		let origin = Runtime::AddressMapping::into_account_id(caller);
360
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
361

            
362
1
		event.record(handle)?;
363

            
364
1
		Ok(())
365
1
	}
366

            
367
	#[precompile::public("identity(address)")]
368
	#[precompile::view]
369
9
	fn identity(
370
9
		handle: &mut impl PrecompileHandle,
371
9
		who: Address,
372
9
	) -> EvmResult<Registration<MaxAdditionalFields>> {
373
9
		// Storage item: IdentityOf ->
374
9
		//		Registration<BalanceOf<T>, T::MaxRegistrars, T::MaxAdditionalFields>
375
9
		handle.record_db_read::<Runtime>(pallet_identity::Registration::<
376
9
			BalanceOf<Runtime>,
377
9
			Runtime::MaxRegistrars,
378
9
			Runtime::IdentityInformation,
379
9
		>::max_encoded_len())?;
380

            
381
9
		let who: H160 = who.into();
382
9
		let who = Runtime::AddressMapping::into_account_id(who);
383
9
		let identity = pallet_identity::Pallet::<Runtime>::identity(who);
384
9

            
385
9
		Ok(Self::identity_to_output(identity)?)
386
9
	}
387

            
388
	#[precompile::public("superOf(address)")]
389
	#[precompile::view]
390
2
	fn super_of(handle: &mut impl PrecompileHandle, who: Address) -> EvmResult<SuperOf> {
391
2
		// Storage item: SuperOf -> (T::AccountId, Data)
392
2
		handle.record_db_read::<Runtime>(
393
2
			Runtime::AccountId::max_encoded_len()
394
2
				.saturating_add(pallet_identity::Data::max_encoded_len()),
395
2
		)?;
396

            
397
2
		let who: H160 = who.into();
398
2
		let who = Runtime::AddressMapping::into_account_id(who);
399
2
		if let Some((account, data)) = pallet_identity::Pallet::<Runtime>::super_of(who) {
400
1
			Ok(SuperOf {
401
1
				is_valid: true,
402
1
				account: Address(account.into()),
403
1
				data: Self::data_to_output(data),
404
1
			})
405
		} else {
406
1
			Ok(SuperOf::default())
407
		}
408
2
	}
409

            
410
	#[precompile::public("subsOf(address)")]
411
	#[precompile::view]
412
2
	fn subs_of(handle: &mut impl PrecompileHandle, who: Address) -> EvmResult<SubsOf> {
413
2
		// Storage item: SubsOf -> (BalanceOf<T>, BoundedVec<T::AccountId, T::MaxSubAccounts>)
414
2
		handle.record_db_read::<Runtime>(
415
2
			BalanceOf::<Runtime>::max_encoded_len().saturating_add(
416
2
				Runtime::AccountId::max_encoded_len()
417
2
					.saturating_mul(Runtime::MaxSubAccounts::get() as usize),
418
2
			),
419
2
		)?;
420

            
421
2
		let who: H160 = who.into();
422
2
		let who = Runtime::AddressMapping::into_account_id(who);
423
2
		let (deposit, accounts) = pallet_identity::Pallet::<Runtime>::subs_of(who);
424
2

            
425
2
		let accounts = accounts
426
2
			.into_iter()
427
2
			.map(|account| Address(account.into()))
428
2
			.collect();
429
2

            
430
2
		Ok(SubsOf {
431
2
			deposit: deposit.into(),
432
2
			accounts,
433
2
		})
434
2
	}
435

            
436
	#[precompile::public("registrars()")]
437
	#[precompile::view]
438
2
	fn registrars(handle: &mut impl PrecompileHandle) -> EvmResult<Vec<Registrar>> {
439
2
		// Storage item: Registrars ->
440
2
		// 		BoundedVec<Option<RegistrarInfo<BalanceOf<T>, T::AccountId>>, T::MaxRegistrars>
441
2
		handle.record_db_read::<Runtime>(
442
2
			pallet_identity::RegistrarInfo::<
443
2
				BalanceOf<Runtime>,
444
2
				Runtime::AccountId,
445
2
				IdentityFieldOf<Runtime>,
446
2
			>::max_encoded_len()
447
2
			.saturating_mul(Runtime::MaxRegistrars::get() as usize),
448
2
		)?;
449

            
450
2
		let registrars = pallet_identity::Pallet::<Runtime>::registrars()
451
2
			.into_iter()
452
2
			.enumerate()
453
2
			.map(|(index, maybe_reg)| {
454
1
				if let Some(reg) = maybe_reg {
455
1
					let fields: u64 = reg.fields.into();
456
1
					Registrar {
457
1
						is_valid: true,
458
1
						index: index as u32,
459
1
						account: Address(reg.account.into()),
460
1
						fee: reg.fee.into(),
461
1
						fields: IdentityFields {
462
1
							display: fields & (IdentityField::Display as u64)
463
1
								== (IdentityField::Display as u64),
464
1
							legal: fields & (IdentityField::Legal as u64)
465
1
								== (IdentityField::Legal as u64),
466
1
							web: fields & (IdentityField::Web as u64)
467
1
								== (IdentityField::Web as u64),
468
1
							riot: fields & (IdentityField::Riot as u64)
469
1
								== (IdentityField::Riot as u64),
470
1
							email: fields & (IdentityField::Email as u64)
471
1
								== (IdentityField::Email as u64),
472
1
							pgp_fingerprint: fields & (IdentityField::PgpFingerprint as u64)
473
1
								== (IdentityField::PgpFingerprint as u64),
474
1
							image: fields & (IdentityField::Image as u64)
475
1
								== (IdentityField::Image as u64),
476
1
							twitter: fields & (IdentityField::Twitter as u64)
477
1
								== (IdentityField::Twitter as u64),
478
1
						},
479
1
					}
480
				} else {
481
					Registrar {
482
						is_valid: false,
483
						index: index as u32,
484
						..Default::default()
485
					}
486
				}
487
2
			})
488
2
			.collect();
489
2

            
490
2
		Ok(registrars)
491
2
	}
492

            
493
2
	fn identity_fields_to_input(fields: IdentityFields) -> IdentityFieldOf<Runtime> {
494
2
		let mut field_bits = 0u64;
495
2
		if fields.display {
496
2
			field_bits = field_bits | IdentityField::Display as u64;
497
2
		}
498
2
		if fields.legal {
499
			field_bits = field_bits | IdentityField::Legal as u64;
500
2
		}
501
2
		if fields.web {
502
2
			field_bits = field_bits | IdentityField::Web as u64;
503
2
		}
504
2
		if fields.riot {
505
			field_bits = field_bits | IdentityField::Riot as u64;
506
2
		}
507
2
		if fields.email {
508
			field_bits = field_bits | IdentityField::Email as u64;
509
2
		}
510
2
		if fields.pgp_fingerprint {
511
			field_bits = field_bits | IdentityField::PgpFingerprint as u64;
512
2
		}
513
2
		if fields.image {
514
			field_bits = field_bits | IdentityField::Image as u64;
515
2
		}
516
2
		if fields.twitter {
517
			field_bits = field_bits | IdentityField::Twitter as u64;
518
2
		}
519

            
520
2
		IdentityFieldOf::<Runtime>::from(field_bits)
521
2
	}
522

            
523
12
	fn identity_to_input(
524
12
		info: IdentityInfo<MaxAdditionalFields>,
525
12
	) -> MayRevert<Box<pallet_identity::legacy::IdentityInfo<MaxAdditionalFields>>> {
526
12
		// let additional: Vec<(pallet_identity::Data, pallet_identity::Data)> = info.additional.into();
527
12
		let mut additional: sp_runtime::BoundedVec<
528
12
			(pallet_identity::Data, pallet_identity::Data),
529
12
			MaxAdditionalFields,
530
12
		> = Default::default();
531
12
		let iter: Vec<_> = info.additional.into();
532
12
		for (i, (k, v)) in iter.into_iter().enumerate() {
533
2
			let k: pallet_identity::Data = k.try_into().map_err(|e| {
534
				RevertReason::custom(e).in_field(alloc::format!("additional.{i}.key"))
535
2
			})?;
536
2
			let v: pallet_identity::Data = v.try_into().map_err(|e| {
537
				RevertReason::custom(e).in_field(alloc::format!("additional.{i}.value"))
538
2
			})?;
539
2
			additional
540
2
				.try_push((k, v))
541
2
				.map_err(|_| RevertReason::custom("out of bounds").in_field("additional"))?;
542
		}
543

            
544
12
		let pgp_fingerprint: Option<[u8; 20]> = if info.has_pgp_fingerprint {
545
2
			let fingerprint: Vec<_> = info.pgp_fingerprint.into();
546
2
			let fingerprint = fingerprint
547
2
				.try_into()
548
2
				.map_err(|_| RevertReason::custom("pgp_fingerprint must be 20 bytes long"))?;
549
2
			Some(fingerprint)
550
		} else {
551
10
			None
552
		};
553
12
		let identity_info = pallet_identity::legacy::IdentityInfo::<MaxAdditionalFields> {
554
12
			additional,
555
12
			display: info
556
12
				.display
557
12
				.try_into()
558
12
				.map_err(|e| RevertReason::custom(e).in_field("display"))?,
559
12
			legal: info
560
12
				.legal
561
12
				.try_into()
562
12
				.map_err(|e| RevertReason::custom(e).in_field("legal"))?,
563
12
			web: info
564
12
				.web
565
12
				.try_into()
566
12
				.map_err(|e| RevertReason::custom(e).in_field("web"))?,
567
12
			riot: info
568
12
				.riot
569
12
				.try_into()
570
12
				.map_err(|e| RevertReason::custom(e).in_field("riot"))?,
571
12
			email: info
572
12
				.email
573
12
				.try_into()
574
12
				.map_err(|e| RevertReason::custom(e).in_field("email"))?,
575
12
			pgp_fingerprint,
576
12
			image: info
577
12
				.image
578
12
				.try_into()
579
12
				.map_err(|e| RevertReason::custom(e).in_field("image"))?,
580
12
			twitter: info
581
12
				.twitter
582
12
				.try_into()
583
12
				.map_err(|e| RevertReason::custom(e).in_field("twitter"))?,
584
		};
585

            
586
12
		Ok(Box::new(identity_info))
587
12
	}
588

            
589
9
	fn identity_to_output(
590
9
		registration: Option<(
591
9
			pallet_identity::Registration<
592
9
				BalanceOf<Runtime>,
593
9
				Runtime::MaxRegistrars,
594
9
				Runtime::IdentityInformation,
595
9
			>,
596
9
			Option<
597
9
				frame_support::BoundedVec<
598
9
					u8,
599
9
					<Runtime as pallet_identity::Config>::MaxUsernameLength,
600
9
				>,
601
9
			>,
602
9
		)>,
603
9
	) -> MayRevert<Registration<MaxAdditionalFields>> {
604
9
		if registration.is_none() {
605
1
			return Ok(Registration::<MaxAdditionalFields>::default());
606
8
		}
607
8

            
608
8
		let registration = registration.expect("none case checked above; qed").0;
609
8
		let mut identity_info = IdentityInfo::<MaxAdditionalFields> {
610
8
			additional: Default::default(),
611
8
			display: Self::data_to_output(registration.info.display),
612
8
			legal: Self::data_to_output(registration.info.legal),
613
8
			web: Self::data_to_output(registration.info.web),
614
8
			riot: Self::data_to_output(registration.info.riot),
615
8
			email: Self::data_to_output(registration.info.email),
616
8
			has_pgp_fingerprint: false,
617
8
			pgp_fingerprint: Default::default(),
618
8
			image: Self::data_to_output(registration.info.image),
619
8
			twitter: Self::data_to_output(registration.info.twitter),
620
8
		};
621
8

            
622
8
		let mut additional = Vec::new();
623
8
		for (k, v) in registration.info.additional.into_iter() {
624
2
			let k: Data = Self::data_to_output(k);
625
2
			let v: Data = Self::data_to_output(v);
626
2
			additional.push((k, v));
627
2
		}
628

            
629
8
		if let Some(pgp_fingerprint) = registration.info.pgp_fingerprint {
630
1
			identity_info.has_pgp_fingerprint = true;
631
1
			identity_info.pgp_fingerprint = pgp_fingerprint.into();
632
7
		}
633

            
634
8
		identity_info.additional = additional.into();
635
8

            
636
8
		let mut judgements = Vec::new();
637
8
		for (index, judgement) in registration.judgements.into_iter() {
638
7
			judgements.push((index, Self::judgement_to_output(judgement)));
639
7
		}
640

            
641
8
		let reg = Registration::<MaxAdditionalFields> {
642
8
			is_valid: true,
643
8
			judgements: judgements.into(),
644
8
			deposit: registration.deposit.into(),
645
8
			info: identity_info,
646
8
		};
647
8

            
648
8
		Ok(reg)
649
9
	}
650

            
651
7
	fn judgement_to_output(value: pallet_identity::Judgement<BalanceOf<Runtime>>) -> Judgement {
652
7
		let mut judgement = Judgement::default();
653
7

            
654
7
		match value {
655
1
			pallet_identity::Judgement::Unknown => {
656
1
				judgement.is_unknown = true;
657
1
			}
658
1
			pallet_identity::Judgement::FeePaid(balance) => {
659
1
				judgement.is_fee_paid = true;
660
1
				judgement.fee_paid_deposit = balance.into();
661
1
			}
662
1
			pallet_identity::Judgement::Reasonable => {
663
1
				judgement.is_reasonable = true;
664
1
			}
665
1
			pallet_identity::Judgement::KnownGood => {
666
1
				judgement.is_known_good = true;
667
1
			}
668
1
			pallet_identity::Judgement::OutOfDate => {
669
1
				judgement.is_out_of_date = true;
670
1
			}
671
1
			pallet_identity::Judgement::LowQuality => {
672
1
				judgement.is_low_quality = true;
673
1
			}
674
1
			pallet_identity::Judgement::Erroneous => {
675
1
				judgement.is_erroneous = true;
676
1
			}
677
		};
678

            
679
7
		judgement
680
7
	}
681

            
682
1
	fn judgment_to_input(
683
1
		value: Judgement,
684
1
	) -> Result<pallet_identity::Judgement<BalanceOf<Runtime>>, RevertReason> {
685
1
		if value.is_unknown {
686
			return Ok(pallet_identity::Judgement::Unknown);
687
1
		}
688
1

            
689
1
		if value.is_fee_paid {
690
			let amount: BalanceOf<Runtime> = value
691
				.fee_paid_deposit
692
				.try_into()
693
				.map_err(|_| RevertReason::value_is_too_large("fee_paid_deposit").into())?;
694

            
695
			return Ok(pallet_identity::Judgement::FeePaid(amount));
696
1
		}
697
1

            
698
1
		if value.is_reasonable {
699
1
			return Ok(pallet_identity::Judgement::Reasonable);
700
		}
701

            
702
		if value.is_known_good {
703
			return Ok(pallet_identity::Judgement::KnownGood);
704
		}
705

            
706
		if value.is_out_of_date {
707
			return Ok(pallet_identity::Judgement::OutOfDate);
708
		}
709

            
710
		if value.is_low_quality {
711
			return Ok(pallet_identity::Judgement::LowQuality);
712
		}
713

            
714
		if value.is_erroneous {
715
			return Ok(pallet_identity::Judgement::Erroneous);
716
		}
717

            
718
		return Err(RevertReason::custom("invalid"));
719
1
	}
720

            
721
61
	fn data_to_output(data: pallet_identity::Data) -> Data {
722
61
		let mut output = Data::default();
723
61
		match data {
724
42
			pallet_identity::Data::None => (),
725
19
			pallet_identity::Data::Raw(bytes) => {
726
19
				let bytes: Vec<_> = bytes.into();
727
19
				output.has_data = true;
728
19
				output.value = bytes.into();
729
19
			}
730
			pallet_identity::Data::BlakeTwo256(bytes) => {
731
				output.has_data = true;
732
				output.value = bytes.into();
733
			}
734
			pallet_identity::Data::Sha256(bytes) => {
735
				output.has_data = true;
736
				output.value = bytes.into();
737
			}
738
			pallet_identity::Data::Keccak256(bytes) => {
739
				output.has_data = true;
740
				output.value = bytes.into();
741
			}
742
			pallet_identity::Data::ShaThree256(bytes) => {
743
				output.has_data = true;
744
				output.value = bytes.into();
745
			}
746
		}
747

            
748
61
		output
749
61
	}
750
}
751

            
752
97
#[derive(Default, Debug, Eq, PartialEq, solidity::Codec)]
753
pub struct Data {
754
	has_data: bool,
755
	value: BoundedBytes<ConstU32<32>>,
756
}
757

            
758
impl TryFrom<Data> for pallet_identity::Data {
759
	type Error = &'static str;
760

            
761
97
	fn try_from(value: Data) -> Result<Self, Self::Error> {
762
97
		if !value.has_data {
763
60
			return Ok(pallet_identity::Data::None);
764
37
		}
765
37

            
766
37
		let value: Vec<_> = value.value.into();
767
37
		let value: sp_runtime::BoundedVec<_, ConstU32<32>> =
768
37
			value.try_into().map_err(|_| "exceeded bounds")?;
769
37
		Ok(pallet_identity::Data::Raw(value))
770
97
	}
771
}
772

            
773
#[derive(Eq, PartialEq, Debug, solidity::Codec)]
774
pub struct Additional {
775
	key: Data,
776
	value: Data,
777
}
778

            
779
12
#[derive(Eq, PartialEq, Debug, solidity::Codec)]
780
pub struct IdentityInfo<FieldLimit> {
781
	additional: BoundedVec<(Data, Data), FieldLimit>,
782
	display: Data,
783
	legal: Data,
784
	web: Data,
785
	riot: Data,
786
	email: Data,
787
	has_pgp_fingerprint: bool,
788
	pgp_fingerprint: BoundedBytes<ConstU32<20>>,
789
	image: Data,
790
	twitter: Data,
791
}
792

            
793
impl<T> Default for IdentityInfo<T> {
794
13
	fn default() -> Self {
795
13
		Self {
796
13
			additional: Default::default(),
797
13
			display: Default::default(),
798
13
			legal: Default::default(),
799
13
			web: Default::default(),
800
13
			riot: Default::default(),
801
13
			email: Default::default(),
802
13
			has_pgp_fingerprint: Default::default(),
803
13
			pgp_fingerprint: Default::default(),
804
13
			image: Default::default(),
805
13
			twitter: Default::default(),
806
13
		}
807
13
	}
808
}
809

            
810
1
#[derive(Eq, PartialEq, Default, Debug, solidity::Codec)]
811
pub struct Judgement {
812
	is_unknown: bool,
813
	is_fee_paid: bool,
814
	fee_paid_deposit: U256,
815
	is_reasonable: bool,
816
	is_known_good: bool,
817
	is_out_of_date: bool,
818
	is_low_quality: bool,
819
	is_erroneous: bool,
820
}
821

            
822
#[derive(Eq, PartialEq, Debug, solidity::Codec)]
823
pub struct Registration<FieldLimit> {
824
	is_valid: bool,
825
	judgements: Vec<(u32, Judgement)>,
826
	deposit: U256,
827
	info: IdentityInfo<FieldLimit>,
828
}
829

            
830
impl<T> Default for Registration<T> {
831
2
	fn default() -> Self {
832
2
		Self {
833
2
			is_valid: false,
834
2
			judgements: Vec::new(),
835
2
			deposit: Default::default(),
836
2
			info: Default::default(),
837
2
		}
838
2
	}
839
}
840

            
841
#[derive(Default, Debug, solidity::Codec)]
842
pub struct SuperOf {
843
	is_valid: bool,
844
	account: Address,
845
	data: Data,
846
}
847

            
848
#[derive(Default, Debug, solidity::Codec)]
849
pub struct SubsOf {
850
	deposit: U256,
851
	accounts: Vec<Address>,
852
}
853

            
854
2
#[derive(Default, Debug, solidity::Codec)]
855
pub struct IdentityFields {
856
	display: bool,
857
	legal: bool,
858
	web: bool,
859
	riot: bool,
860
	email: bool,
861
	pgp_fingerprint: bool,
862
	image: bool,
863
	twitter: bool,
864
}
865

            
866
#[derive(Default, Debug, solidity::Codec)]
867
pub struct Registrar {
868
	is_valid: bool,
869
	index: u32,
870
	account: Address,
871
	fee: U256,
872
	fields: IdentityFields,
873
}