1
// This file is part of Substrate.
2

            
3
// Copyright (C) Parity Technologies (UK) Ltd.
4
// SPDX-License-Identifier: Apache-2.0
5

            
6
// Licensed under the Apache License, Version 2.0 (the "License");
7
// you may not use this file except in compliance with the License.
8
// You may obtain a copy of the License at
9
//
10
// 	http://www.apache.org/licenses/LICENSE-2.0
11
//
12
// Unless required by applicable law or agreed to in writing, software
13
// distributed under the License is distributed on an "AS IS" BASIS,
14
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
// See the License for the specific language governing permissions and
16
// limitations under the License.
17

            
18
#[cfg(feature = "runtime-benchmarks")]
19
use alloc::vec;
20
use codec::{Decode, Encode, MaxEncodedLen};
21
#[cfg(feature = "runtime-benchmarks")]
22
use enumflags2::BitFlag;
23
use enumflags2::{bitflags, BitFlags};
24
use frame_support::{traits::Get, CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound};
25
use scale_info::{build::Variants, Path, Type, TypeInfo};
26
use sp_runtime::{BoundedVec, RuntimeDebug};
27

            
28
use crate::types::{Data, IdentityInformationProvider};
29

            
30
/// The fields that we use to identify the owner of an account with. Each corresponds to a field
31
/// in the `IdentityInfo` struct.
32
#[bitflags]
33
#[repr(u64)]
34
#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug)]
35
pub enum IdentityField {
36
	Display,
37
	Legal,
38
	Web,
39
	Riot,
40
	Email,
41
	PgpFingerprint,
42
	Image,
43
	Twitter,
44
}
45

            
46
impl TypeInfo for IdentityField {
47
	type Identity = Self;
48

            
49
	fn type_info() -> scale_info::Type {
50
		Type::builder().path(Path::new("IdentityField", module_path!())).variant(
51
			Variants::new()
52
				.variant("Display", |v| v.index(0))
53
				.variant("Legal", |v| v.index(1))
54
				.variant("Web", |v| v.index(2))
55
				.variant("Riot", |v| v.index(3))
56
				.variant("Email", |v| v.index(4))
57
				.variant("PgpFingerprint", |v| v.index(5))
58
				.variant("Image", |v| v.index(6))
59
				.variant("Twitter", |v| v.index(7)),
60
		)
61
	}
62
}
63

            
64
/// Information concerning the identity of the controller of an account.
65
///
66
/// NOTE: This should be stored at the end of the storage item to facilitate the addition of extra
67
/// fields in a backwards compatible way through a specialized `Decode` impl.
68
#[derive(
69
	CloneNoBound,
70
	Encode,
71
	Decode,
72
	EqNoBound,
73
	MaxEncodedLen,
74
	PartialEqNoBound,
75
	RuntimeDebugNoBound,
76
	TypeInfo,
77
)]
78
#[codec(mel_bound())]
79
#[scale_info(skip_type_params(FieldLimit))]
80
pub struct IdentityInfo<FieldLimit: Get<u32>> {
81
	/// Additional fields of the identity that are not catered for with the struct's explicit
82
	/// fields.
83
	pub additional: BoundedVec<(Data, Data), FieldLimit>,
84

            
85
	/// A reasonable display name for the controller of the account. This should be whatever it is
86
	/// that it is typically known as and should not be confusable with other entities, given
87
	/// reasonable context.
88
	///
89
	/// Stored as UTF-8.
90
	pub display: Data,
91

            
92
	/// The full legal name in the local jurisdiction of the entity. This might be a bit
93
	/// long-winded.
94
	///
95
	/// Stored as UTF-8.
96
	pub legal: Data,
97

            
98
	/// A representative website held by the controller of the account.
99
	///
100
	/// NOTE: `https://` is automatically prepended.
101
	///
102
	/// Stored as UTF-8.
103
	pub web: Data,
104

            
105
	/// The Riot/Matrix handle held by the controller of the account.
106
	///
107
	/// Stored as UTF-8.
108
	pub riot: Data,
109

            
110
	/// The email address of the controller of the account.
111
	///
112
	/// Stored as UTF-8.
113
	pub email: Data,
114

            
115
	/// The PGP/GPG public key of the controller of the account.
116
	pub pgp_fingerprint: Option<[u8; 20]>,
117

            
118
	/// A graphic image representing the controller of the account. Should be a company,
119
	/// organization or project logo or a headshot in the case of a human.
120
	pub image: Data,
121

            
122
	/// The Twitter identity. The leading `@` character may be elided.
123
	pub twitter: Data,
124
}
125

            
126
impl<FieldLimit: Get<u32> + 'static> IdentityInformationProvider for IdentityInfo<FieldLimit> {
127
	type FieldsIdentifier = u64;
128

            
129
	fn has_identity(&self, fields: Self::FieldsIdentifier) -> bool {
130
		self.fields().bits() & fields == fields
131
	}
132

            
133
	#[cfg(feature = "runtime-benchmarks")]
134
	fn create_identity_info() -> Self {
135
		let data = Data::Raw(vec![0; 32].try_into().unwrap());
136

            
137
		IdentityInfo {
138
			additional: vec![(data.clone(), data.clone()); FieldLimit::get().try_into().unwrap()]
139
				.try_into()
140
				.unwrap(),
141
			display: data.clone(),
142
			legal: data.clone(),
143
			web: data.clone(),
144
			riot: data.clone(),
145
			email: data.clone(),
146
			pgp_fingerprint: Some([0; 20]),
147
			image: data.clone(),
148
			twitter: data,
149
		}
150
	}
151

            
152
	#[cfg(feature = "runtime-benchmarks")]
153
	fn all_fields() -> Self::FieldsIdentifier {
154
		IdentityField::all().bits()
155
	}
156
}
157

            
158
impl<FieldLimit: Get<u32>> Default for IdentityInfo<FieldLimit> {
159
	fn default() -> Self {
160
		IdentityInfo {
161
			additional: BoundedVec::default(),
162
			display: Data::None,
163
			legal: Data::None,
164
			web: Data::None,
165
			riot: Data::None,
166
			email: Data::None,
167
			pgp_fingerprint: None,
168
			image: Data::None,
169
			twitter: Data::None,
170
		}
171
	}
172
}
173

            
174
impl<FieldLimit: Get<u32>> IdentityInfo<FieldLimit> {
175
	pub(crate) fn fields(&self) -> BitFlags<IdentityField> {
176
		let mut res = <BitFlags<IdentityField>>::empty();
177
		if !self.display.is_none() {
178
			res.insert(IdentityField::Display);
179
		}
180
		if !self.legal.is_none() {
181
			res.insert(IdentityField::Legal);
182
		}
183
		if !self.web.is_none() {
184
			res.insert(IdentityField::Web);
185
		}
186
		if !self.riot.is_none() {
187
			res.insert(IdentityField::Riot);
188
		}
189
		if !self.email.is_none() {
190
			res.insert(IdentityField::Email);
191
		}
192
		if self.pgp_fingerprint.is_some() {
193
			res.insert(IdentityField::PgpFingerprint);
194
		}
195
		if !self.image.is_none() {
196
			res.insert(IdentityField::Image);
197
		}
198
		if !self.twitter.is_none() {
199
			res.insert(IdentityField::Twitter);
200
		}
201
		res
202
	}
203
}