use super::*;
use codec::{Decode, Encode, MaxEncodedLen};
use enumflags2::{bitflags, BitFlags};
use frame_support::{
traits::{ConstU32, Get},
BoundedVec, CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound,
};
use scale_info::{
build::{Fields, Variants},
meta_type, Path, Type, TypeInfo, TypeParameter,
};
use sp_runtime::{traits::Zero, RuntimeDebug};
use sp_std::{fmt::Debug, iter::once, ops::Add, prelude::*};
#[derive(Clone, Eq, PartialEq, RuntimeDebug, MaxEncodedLen)]
pub enum Data {
None,
Raw(BoundedVec<u8, ConstU32<32>>),
BlakeTwo256([u8; 32]),
Sha256([u8; 32]),
Keccak256([u8; 32]),
ShaThree256([u8; 32]),
}
impl Data {
pub fn is_none(&self) -> bool {
self == &Data::None
}
}
impl Decode for Data {
fn decode<I: codec::Input>(input: &mut I) -> sp_std::result::Result<Self, codec::Error> {
let b = input.read_byte()?;
Ok(match b {
0 => Data::None,
n @ 1..=33 => {
let mut r: BoundedVec<_, _> = vec![0u8; n as usize - 1]
.try_into()
.expect("bound checked in match arm condition; qed");
input.read(&mut r[..])?;
Data::Raw(r)
},
34 => Data::BlakeTwo256(<[u8; 32]>::decode(input)?),
35 => Data::Sha256(<[u8; 32]>::decode(input)?),
36 => Data::Keccak256(<[u8; 32]>::decode(input)?),
37 => Data::ShaThree256(<[u8; 32]>::decode(input)?),
_ => return Err(codec::Error::from("invalid leading byte")),
})
}
}
impl Encode for Data {
fn encode(&self) -> Vec<u8> {
match self {
Data::None => vec![0u8; 1],
Data::Raw(ref x) => {
let l = x.len().min(32);
let mut r = vec![l as u8 + 1; l + 1];
r[1..].copy_from_slice(&x[..l as usize]);
r
},
Data::BlakeTwo256(ref h) => once(34u8).chain(h.iter().cloned()).collect(),
Data::Sha256(ref h) => once(35u8).chain(h.iter().cloned()).collect(),
Data::Keccak256(ref h) => once(36u8).chain(h.iter().cloned()).collect(),
Data::ShaThree256(ref h) => once(37u8).chain(h.iter().cloned()).collect(),
}
}
}
impl codec::EncodeLike for Data {}
macro_rules! data_raw_variants {
($variants:ident, $(($index:literal, $size:literal)),* ) => {
$variants
$(
.variant(concat!("Raw", stringify!($size)), |v| v
.index($index)
.fields(Fields::unnamed().field(|f| f.ty::<[u8; $size]>()))
)
)*
}
}
impl TypeInfo for Data {
type Identity = Self;
fn type_info() -> Type {
let variants = Variants::new().variant("None", |v| v.index(0));
let variants = data_raw_variants!(
variants,
(1, 0),
(2, 1),
(3, 2),
(4, 3),
(5, 4),
(6, 5),
(7, 6),
(8, 7),
(9, 8),
(10, 9),
(11, 10),
(12, 11),
(13, 12),
(14, 13),
(15, 14),
(16, 15),
(17, 16),
(18, 17),
(19, 18),
(20, 19),
(21, 20),
(22, 21),
(23, 22),
(24, 23),
(25, 24),
(26, 25),
(27, 26),
(28, 27),
(29, 28),
(30, 29),
(31, 30),
(32, 31),
(33, 32)
);
let variants = variants
.variant("BlakeTwo256", |v| {
v.index(34).fields(Fields::unnamed().field(|f| f.ty::<[u8; 32]>()))
})
.variant("Sha256", |v| {
v.index(35).fields(Fields::unnamed().field(|f| f.ty::<[u8; 32]>()))
})
.variant("Keccak256", |v| {
v.index(36).fields(Fields::unnamed().field(|f| f.ty::<[u8; 32]>()))
})
.variant("ShaThree256", |v| {
v.index(37).fields(Fields::unnamed().field(|f| f.ty::<[u8; 32]>()))
});
Type::builder().path(Path::new("Data", module_path!())).variant(variants)
}
}
impl Default for Data {
fn default() -> Self {
Self::None
}
}
pub type RegistrarIndex = u32;
#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
pub enum Judgement<Balance: Encode + Decode + MaxEncodedLen + Copy + Clone + Debug + Eq + PartialEq>
{
Unknown,
FeePaid(Balance),
Reasonable,
KnownGood,
OutOfDate,
LowQuality,
Erroneous,
}
impl<Balance: Encode + Decode + MaxEncodedLen + Copy + Clone + Debug + Eq + PartialEq>
Judgement<Balance>
{
pub(crate) fn has_deposit(&self) -> bool {
matches!(self, Judgement::FeePaid(_))
}
pub(crate) fn is_sticky(&self) -> bool {
matches!(self, Judgement::FeePaid(_) | Judgement::Erroneous)
}
}
#[bitflags]
#[repr(u64)]
#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub enum IdentityField {
Display = 0b0000000000000000000000000000000000000000000000000000000000000001,
Legal = 0b0000000000000000000000000000000000000000000000000000000000000010,
Web = 0b0000000000000000000000000000000000000000000000000000000000000100,
Riot = 0b0000000000000000000000000000000000000000000000000000000000001000,
Email = 0b0000000000000000000000000000000000000000000000000000000000010000,
PgpFingerprint = 0b0000000000000000000000000000000000000000000000000000000000100000,
Image = 0b0000000000000000000000000000000000000000000000000000000001000000,
Twitter = 0b0000000000000000000000000000000000000000000000000000000010000000,
}
#[derive(Clone, Copy, PartialEq, Default, RuntimeDebug)]
pub struct IdentityFields(pub BitFlags<IdentityField>);
impl MaxEncodedLen for IdentityFields {
fn max_encoded_len() -> usize {
u64::max_encoded_len()
}
}
impl Eq for IdentityFields {}
impl Encode for IdentityFields {
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
self.0.bits().using_encoded(f)
}
}
impl Decode for IdentityFields {
fn decode<I: codec::Input>(input: &mut I) -> sp_std::result::Result<Self, codec::Error> {
let field = u64::decode(input)?;
Ok(Self(<BitFlags<IdentityField>>::from_bits(field as u64).map_err(|_| "invalid value")?))
}
}
impl TypeInfo for IdentityFields {
type Identity = Self;
fn type_info() -> Type {
Type::builder()
.path(Path::new("BitFlags", module_path!()))
.type_params(vec![TypeParameter::new("T", Some(meta_type::<IdentityField>()))])
.composite(Fields::unnamed().field(|f| f.ty::<u64>().type_name("IdentityField")))
}
}
#[derive(
CloneNoBound, Encode, Decode, Eq, MaxEncodedLen, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo,
)]
#[codec(mel_bound())]
#[cfg_attr(test, derive(frame_support::DefaultNoBound))]
#[scale_info(skip_type_params(FieldLimit))]
pub struct IdentityInfo<FieldLimit: Get<u32>> {
pub additional: BoundedVec<(Data, Data), FieldLimit>,
pub display: Data,
pub legal: Data,
pub web: Data,
pub riot: Data,
pub email: Data,
pub pgp_fingerprint: Option<[u8; 20]>,
pub image: Data,
pub twitter: Data,
}
impl<FieldLimit: Get<u32>> IdentityInfo<FieldLimit> {
pub(crate) fn fields(&self) -> IdentityFields {
let mut res = <BitFlags<IdentityField>>::empty();
if !self.display.is_none() {
res.insert(IdentityField::Display);
}
if !self.legal.is_none() {
res.insert(IdentityField::Legal);
}
if !self.web.is_none() {
res.insert(IdentityField::Web);
}
if !self.riot.is_none() {
res.insert(IdentityField::Riot);
}
if !self.email.is_none() {
res.insert(IdentityField::Email);
}
if self.pgp_fingerprint.is_some() {
res.insert(IdentityField::PgpFingerprint);
}
if !self.image.is_none() {
res.insert(IdentityField::Image);
}
if !self.twitter.is_none() {
res.insert(IdentityField::Twitter);
}
IdentityFields(res)
}
}
#[derive(
CloneNoBound, Encode, Eq, MaxEncodedLen, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo,
)]
#[codec(mel_bound())]
#[scale_info(skip_type_params(MaxJudgements, MaxAdditionalFields))]
pub struct Registration<
Balance: Encode + Decode + MaxEncodedLen + Copy + Clone + Debug + Eq + PartialEq,
MaxJudgements: Get<u32>,
MaxAdditionalFields: Get<u32>,
> {
pub judgements: BoundedVec<(RegistrarIndex, Judgement<Balance>), MaxJudgements>,
pub deposit: Balance,
pub info: IdentityInfo<MaxAdditionalFields>,
}
impl<
Balance: Encode + Decode + MaxEncodedLen + Copy + Clone + Debug + Eq + PartialEq + Zero + Add,
MaxJudgements: Get<u32>,
MaxAdditionalFields: Get<u32>,
> Registration<Balance, MaxJudgements, MaxAdditionalFields>
{
pub(crate) fn total_deposit(&self) -> Balance {
self.deposit +
self.judgements
.iter()
.map(|(_, ref j)| if let Judgement::FeePaid(fee) = j { *fee } else { Zero::zero() })
.fold(Zero::zero(), |a, i| a + i)
}
}
impl<
Balance: Encode + Decode + MaxEncodedLen + Copy + Clone + Debug + Eq + PartialEq,
MaxJudgements: Get<u32>,
MaxAdditionalFields: Get<u32>,
> Decode for Registration<Balance, MaxJudgements, MaxAdditionalFields>
{
fn decode<I: codec::Input>(input: &mut I) -> sp_std::result::Result<Self, codec::Error> {
let (judgements, deposit, info) = Decode::decode(&mut AppendZerosInput::new(input))?;
Ok(Self { judgements, deposit, info })
}
}
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
pub struct RegistrarInfo<
Balance: Encode + Decode + Clone + Debug + Eq + PartialEq,
AccountId: Encode + Decode + Clone + Debug + Eq + PartialEq,
> {
pub account: AccountId,
pub fee: Balance,
pub fields: IdentityFields,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn manual_data_type_info() {
let mut registry = scale_info::Registry::new();
let type_id = registry.register_type(&scale_info::meta_type::<Data>());
let registry: scale_info::PortableRegistry = registry.into();
let type_info = registry.resolve(type_id.id).unwrap();
let check_type_info = |data: &Data| {
let variant_name = match data {
Data::None => "None".to_string(),
Data::BlakeTwo256(_) => "BlakeTwo256".to_string(),
Data::Sha256(_) => "Sha256".to_string(),
Data::Keccak256(_) => "Keccak256".to_string(),
Data::ShaThree256(_) => "ShaThree256".to_string(),
Data::Raw(bytes) => format!("Raw{}", bytes.len()),
};
if let scale_info::TypeDef::Variant(variant) = &type_info.type_def {
let variant = variant
.variants
.iter()
.find(|v| v.name == variant_name)
.expect(&format!("Expected to find variant {}", variant_name));
let field_arr_len = variant
.fields
.first()
.and_then(|f| registry.resolve(f.ty.id))
.map(|ty| {
if let scale_info::TypeDef::Array(arr) = &ty.type_def {
arr.len
} else {
panic!("Should be an array type")
}
})
.unwrap_or(0);
let encoded = data.encode();
assert_eq!(encoded[0], variant.index);
assert_eq!(encoded.len() as u32 - 1, field_arr_len);
} else {
panic!("Should be a variant type")
};
};
let mut data = vec![
Data::None,
Data::BlakeTwo256(Default::default()),
Data::Sha256(Default::default()),
Data::Keccak256(Default::default()),
Data::ShaThree256(Default::default()),
];
for n in 0..32 {
data.push(Data::Raw(vec![0u8; n as usize].try_into().unwrap()))
}
for d in data.iter() {
check_type_info(d);
}
}
}