frame_support/traits/reality.rs
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//! Traits concerned with modelling reality.
19
20use core::marker::PhantomData;
21
22use codec::{Decode, DecodeWithMemTracking, Encode, FullCodec, MaxEncodedLen};
23use frame_support::{CloneNoBound, EqNoBound, Parameter, PartialEqNoBound};
24use scale_info::TypeInfo;
25use sp_core::ConstU32;
26use sp_runtime::{traits::Member, BoundedVec, DispatchError, DispatchResult};
27
28/// Identity of personhood.
29///
30/// This is a persistent identifier for every individual. Regardless of what else the individual
31/// changes within the system (such as identity documents, cryptographic keys, etc...) this does not
32/// change. As such, it should never be used in application code.
33pub type PersonalId = u64;
34
35/// Identifier for a specific application in which we may wish to track individual people.
36///
37/// NOTE: This MUST remain equivalent to the type `Context` in the crate `verifiable`.
38pub type Context = [u8; 32];
39
40/// Identifier for a specific individual within an application context.
41///
42/// NOTE: This MUST remain equivalent to the type `Alias` in the crate `verifiable`.
43pub type Alias = [u8; 32];
44
45/// The type we use to identify different rings.
46pub type RingIndex = u32;
47
48/// Data type for arbitrary information handled by the statement oracle.
49pub type Data = BoundedVec<u8, ConstU32<32>>;
50
51/// The maximum length of custom statement data.
52pub const MAX_STATEMENT_DATA_SIZE: u32 = 256;
53
54/// Data type for custom statement information handled by the statement oracle.
55pub type CustomStatement = BoundedVec<u8, ConstU32<MAX_STATEMENT_DATA_SIZE>>;
56
57/// The type used to represent the hash of the evidence used in statements by the statement oracle.
58pub type EvidenceHash = [u8; 32];
59
60/// Maximum length of the context passed in the oracle's judgements.
61pub const CONTEXT_SIZE: u32 = 64;
62
63/// The type used to represent the context of an oracle's judgement.
64pub type JudgementContext = BoundedVec<u8, ConstU32<CONTEXT_SIZE>>;
65
66/// The [`Alias`] type enriched with the originating [`Context`].
67#[derive(
68 Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo, DecodeWithMemTracking,
69)]
70pub struct ContextualAlias {
71 /// The alias of the person.
72 pub alias: Alias,
73 /// The context in which this alias was created.
74 pub context: Context,
75}
76
77/// Trait to recognize people and handle personal id.
78///
79/// `PersonalId` goes through multiple state: free, reserved, used; a used personal id can belong
80/// to a recognized person or a suspended person.
81pub trait AddOnlyPeopleTrait {
82 type Member: Parameter + MaxEncodedLen;
83 /// Reserve a new id for a future person. This id is not recognized, not reserved, and has
84 /// never been reserved in the past.
85 fn reserve_new_id() -> PersonalId;
86 /// Renew a reservation for a personal id. The id is not recognized, but has been reserved in
87 /// the past.
88 ///
89 /// An error is returned if the id is used or wasn't reserved before.
90 fn renew_id_reservation(personal_id: PersonalId) -> Result<(), DispatchError>;
91 /// Cancel the reservation for a personal id
92 ///
93 /// An error is returned if the id wasn't reserved in the first place.
94 fn cancel_id_reservation(personal_id: PersonalId) -> Result<(), DispatchError>;
95 /// Recognized a person.
96 ///
97 /// The personal id must be reserved or the person must have already been recognized and
98 /// suspended in the past.
99 /// If recognizing a new person, a key must be provided. If resuming the personhood then no key
100 /// must be provided.
101 ///
102 /// An error is returned if:
103 /// * `maybe_key` is some and the personal id was not reserved or is used by a recognized or
104 /// suspended person.
105 /// * `maybe_key` is none and the personal id was not recognized before.
106 fn recognize_personhood(
107 who: PersonalId,
108 maybe_key: Option<Self::Member>,
109 ) -> Result<(), DispatchError>;
110 // All stuff for benchmarks.
111 #[cfg(feature = "runtime-benchmarks")]
112 type Secret;
113 #[cfg(feature = "runtime-benchmarks")]
114 fn mock_key(who: PersonalId) -> (Self::Member, Self::Secret);
115}
116
117/// Trait to recognize and suspend people.
118pub trait PeopleTrait: AddOnlyPeopleTrait {
119 /// Suspend a set of people. This operation must be called within a mutation session.
120 ///
121 /// An error is returned if:
122 /// * a suspended personal id was already suspended.
123 /// * a personal id doesn't belong to any person.
124 fn suspend_personhood(suspensions: &[PersonalId]) -> DispatchResult;
125 /// Start a mutation session for setting people.
126 ///
127 /// An error is returned if the mutation session can be started at the moment. It is expected
128 /// to become startable later.
129 fn start_people_set_mutation_session() -> DispatchResult;
130 /// End a mutation session for setting people.
131 ///
132 /// An error is returned if there is no mutation session ongoing.
133 fn end_people_set_mutation_session() -> DispatchResult;
134}
135
136impl AddOnlyPeopleTrait for () {
137 type Member = ();
138 fn reserve_new_id() -> PersonalId {
139 0
140 }
141 fn renew_id_reservation(_: PersonalId) -> Result<(), DispatchError> {
142 Ok(())
143 }
144 fn cancel_id_reservation(_: PersonalId) -> Result<(), DispatchError> {
145 Ok(())
146 }
147 fn recognize_personhood(_: PersonalId, _: Option<Self::Member>) -> Result<(), DispatchError> {
148 Ok(())
149 }
150
151 #[cfg(feature = "runtime-benchmarks")]
152 type Secret = PersonalId;
153 #[cfg(feature = "runtime-benchmarks")]
154 fn mock_key(who: PersonalId) -> (Self::Member, Self::Secret) {
155 ((), who)
156 }
157}
158
159impl PeopleTrait for () {
160 fn suspend_personhood(_: &[PersonalId]) -> DispatchResult {
161 Ok(())
162 }
163 fn start_people_set_mutation_session() -> DispatchResult {
164 Ok(())
165 }
166 fn end_people_set_mutation_session() -> DispatchResult {
167 Ok(())
168 }
169}
170
171/// Trait to get the total number of active members in a set.
172pub trait CountedMembers {
173 /// Returns the number of active members in the set.
174 fn active_count(&self) -> u32;
175}
176
177/// A legitimate verdict on a particular statement.
178#[derive(
179 Clone,
180 Copy,
181 PartialEq,
182 Eq,
183 Debug,
184 Encode,
185 Decode,
186 MaxEncodedLen,
187 TypeInfo,
188 DecodeWithMemTracking,
189)]
190pub enum Truth {
191 /// The evidence can be taken as a clear indication that the statement is true. Doubt may still
192 /// remain but it should be unlikely (no more than 1 chance in 20) that this doubt would be
193 /// substantial enough to contravene the evidence.
194 True,
195 /// The evidence contradicts the statement.
196 False,
197}
198
199/// Judgement passed on the truth and validity of a statement.
200#[derive(
201 Clone,
202 Copy,
203 PartialEq,
204 Eq,
205 Debug,
206 Encode,
207 Decode,
208 MaxEncodedLen,
209 TypeInfo,
210 DecodeWithMemTracking,
211)]
212pub enum Judgement {
213 /// A judgement on the truth of a statement.
214 Truth(Truth),
215 /// The evidence supplied probably (P > 50%) implies contempt for the system. Submitting
216 /// evidence which clearly appears to be manipulated or intentionally provides no indication of
217 /// truth for the statement would imply this outcome.
218 Contempt,
219}
220
221impl Judgement {
222 pub fn matches_intent(&self, j: Self) -> bool {
223 use self::Truth::*;
224 use Judgement::*;
225 matches!(
226 (self, j),
227 (Truth(True), Truth(True)) | (Truth(False), Truth(False)) | (Contempt, Contempt)
228 )
229 }
230}
231
232pub mod identity {
233 use super::*;
234
235 /// Social platforms that statement oracles support.
236 #[derive(
237 Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo, DecodeWithMemTracking,
238 )]
239 pub enum Social {
240 Twitter { username: Data },
241 Github { username: Data },
242 }
243
244 impl Social {
245 pub fn eq_platform(&self, other: &Social) -> bool {
246 matches!(
247 (&self, &other),
248 (Social::Twitter { .. }, Social::Twitter { .. }) |
249 (Social::Github { .. }, Social::Github { .. })
250 )
251 }
252 }
253}
254
255/// A statement upon which a [`StatementOracle`] can provide judgement.
256#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
257pub enum Statement {
258 /// Ask for whether evidence exists to confirm that a particular social credential on a
259 /// supported platform belongs to a person.
260 IdentityCredential { platform: identity::Social, evidence: Data },
261 /// Ask for whether a username meets certain standards.
262 ///
263 /// It is up to the oracle to decide upon username validity,
264 /// but it may be assumed that a username is considered acceptable if it:
265 /// - contains no offensive, discriminatory, or inappropriate content,
266 /// - is visually distinct and readable in user interfaces,
267 /// - complies with other oracle guidelines.
268 UsernameValid { username: Data },
269 /// Ask for a custom statement to be judged. The responsibility of correctly interpreting the
270 /// encoded bytes falls on the [`StatementOracle`] implementation handling this. If no custom
271 /// statements are allowed, the implementation should reject this variant altogether.
272 Custom { id: u8, data: CustomStatement },
273}
274
275/// Describes the location within the runtime of a callback, along with other type information such
276/// as parameters passed into the callback.
277#[derive(
278 CloneNoBound, PartialEqNoBound, EqNoBound, Debug, Encode, Decode, MaxEncodedLen, TypeInfo,
279)]
280#[scale_info(skip_type_params(Params, RuntimeCall))]
281#[codec(mel_bound())]
282pub struct Callback<Params, RuntimeCall> {
283 pallet_index: u8,
284 call_index: u8,
285 phantom: PhantomData<(Params, RuntimeCall)>,
286}
287
288impl<Params: Encode, RuntimeCall: Decode> Callback<Params, RuntimeCall> {
289 pub const fn from_parts(pallet_index: u8, call_index: u8) -> Self {
290 Self { pallet_index, call_index, phantom: PhantomData }
291 }
292 pub fn curry(&self, args: Params) -> Result<RuntimeCall, codec::Error> {
293 (self.pallet_index, self.call_index, args).using_encoded(|mut d| Decode::decode(&mut d))
294 }
295}
296
297/// A provider of wondrous magic: give it a `Statement` and it will tell you if it's true, with
298/// some degree of resilience.
299///
300/// It's asynchronous, so you give it a callback in the form of a `RuntimeCall` stub.
301pub trait StatementOracle<RuntimeCall> {
302 /// A small piece of data which may be used to identify different ongoing judgements.
303 type Ticket: Member + FullCodec + TypeInfo + MaxEncodedLen + Default;
304
305 /// Judge a `statement` and get a Judgement.
306 ///
307 /// We only care about the pallet/call index of `callback`; it must take exactly three
308 /// arguments:
309 ///
310 /// - `Self::Ticket`: The ticket which was returned here to identify the judgement.
311 /// - `JudgementContext`: The value of `context` which was passed in to this call.
312 /// - `Judgement`: The judgement given by the oracle.
313 ///
314 /// It is assumed that all costs associated with this oraclisation have already been paid for
315 /// or are absorbed by the system acting in its own interests.
316 fn judge_statement(
317 statement: Statement,
318 context: JudgementContext,
319 callback: Callback<(Self::Ticket, JudgementContext, Judgement), RuntimeCall>,
320 ) -> Result<Self::Ticket, DispatchError>;
321}
322
323impl<C> StatementOracle<C> for () {
324 type Ticket = ();
325 fn judge_statement(
326 _: Statement,
327 _: JudgementContext,
328 _: Callback<(Self::Ticket, JudgementContext, Judgement), C>,
329 ) -> Result<(), DispatchError> {
330 Err(DispatchError::Unavailable)
331 }
332}