use external_memory_tools::{AddressableBuffer, ExternalMemory};
use scale_info::{form::PortableForm, Field, Path, Type, TypeDef, TypeDefPrimitive, Variant};
use crate::std::{borrow::ToOwned, string::String, vec::Vec};
use crate::cards::Info;
use crate::decoding_sci::{husk_type, pick_variant};
use crate::propagated::Checker;
use crate::traits::{AsMetadata, ResolveType, SignedExtensionMetadata};
pub const BALANCE_ID_SET: &[&str] = &[
"Balance",
"BalanceOf<T>",
"BalanceOf<T, I>",
"DepositBalance",
"ExtendedBalance",
"PalletBalanceOf<T>",
"T::Balance",
];
pub const SIGNATURE_ECDSA_ID_SET: &[&str] = &["ecdsa::Signature"];
pub const SIGNATURE_ED25519_ID_SET: &[&str] = &["ed25519::Signature"];
pub const SIGNATURE_SR25519_ID_SET: &[&str] = &["sr25519::Signature"];
pub const NONCE_ID_SET: &[&str] = &["nonce"];
pub const SPEC_VERSION_ID_SET: &[&str] = &["spec_version"];
pub const SPEC_NAME_ID_SET: &[&str] = &["spec_name"];
pub const TX_VERSION_ID_SET: &[&str] = &["tx_version", "transaction_version"];
pub const ACCOUNT_ID32: &str = "AccountId32";
pub const CALL: &[&str] = &["Call", "RuntimeCall"];
pub const ERA: &str = "Era";
pub const EVENT: &[&str] = &["Event", "RuntimeEvent"];
pub const H160: &[&str] = &["AccountId20", "H160"];
pub const H256: &str = "H256";
pub const H512: &str = "H512";
pub const MULTI_SIGNATURE: &str = "MultiSignature";
pub const PERBILL: &str = "Perbill";
pub const PERCENT: &str = "Percent";
pub const PERMILL: &str = "Permill";
pub const PERQUINTILL: &str = "Perquintill";
pub const PERU16: &str = "PerU16";
pub const PUBLIC: &str = "Public";
pub const SIGNATURE: &str = "Signature";
pub const SP_CORE_ED25519: &[&str] = &["sp_core", "ed25519"];
pub const SP_CORE_SR25519: &[&str] = &["sp_core", "sr25519"];
pub const SP_CORE_ECDSA: &[&str] = &["sp_core", "ecdsa"];
pub const UNCHECKED_EXTRINSIC_NAMESPACE: &[&str] =
&["sp_runtime", "generic", "unchecked_extrinsic"];
pub const UNCHECKED_EXTRINSIC_IDENT: &str = "UncheckedExtrinsic";
pub const CHECK_SPEC_VERSION: &str = "CheckSpecVersion";
pub const CHECK_TX_VERSION: &str = "CheckTxVersion";
pub const CHECK_GENESIS: &str = "CheckGenesis";
pub const CHECK_MORTALITY: &str = "CheckMortality";
pub const CHECK_NONCE: &str = "CheckNonce";
pub const CHARGE_TRANSACTION_PAYMENT: &str = "ChargeTransactionPayment";
pub const CHARGE_ASSET_TX_PAYMENT: &str = "ChargeAssetTxPayment";
pub const ENUM_INDEX_ENCODED_LEN: usize = 1;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SpecialtyUnsignedInteger {
None,
Balance,
Tip,
TipAsset,
Nonce,
SpecVersion,
TxVersion,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SpecialtyStr {
None,
SpecName,
}
#[derive(Clone, Copy, Debug)]
pub enum SpecialtyH256 {
None,
GenesisHash,
BlockHash,
}
#[derive(Clone, Copy, Debug)]
pub enum SignatureIndicator {
None,
Ecdsa,
Ed25519,
Sr25519,
}
impl SignatureIndicator {
pub fn from_field<E, M>(
field: &Field<PortableForm>,
ext_memory: &mut E,
registry: &M::TypeRegistry,
) -> Self
where
E: ExternalMemory,
M: AsMetadata<E>,
{
if let Ok(husked_field_ty) =
husk_type::<E, M>(&field.ty, registry, ext_memory, Checker::new())
{
let array_u8_length = match husked_field_ty.ty.type_def {
TypeDef::Array(signature_array_ty) => {
let element_ty_id = signature_array_ty.type_param.id;
if let Ok(element_ty) = registry.resolve_ty(element_ty_id, ext_memory) {
if let TypeDef::Primitive(TypeDefPrimitive::U8) = element_ty.type_def {
signature_array_ty.len
} else {
return Self::None;
}
} else {
return Self::None;
}
}
_ => return Self::None,
};
match &field.type_name {
Some(type_name) => match type_name.as_str() {
a if SIGNATURE_ECDSA_ID_SET.contains(&a) => {
if array_u8_length == substrate_crypto_light::ecdsa::SIGNATURE_LEN as u32 {
Self::Ecdsa
} else {
Self::None
}
}
a if SIGNATURE_ED25519_ID_SET.contains(&a) => {
if array_u8_length == substrate_crypto_light::ed25519::SIGNATURE_LEN as u32
{
Self::Ed25519
} else {
Self::None
}
}
a if SIGNATURE_SR25519_ID_SET.contains(&a) => {
if array_u8_length == substrate_crypto_light::sr25519::SIGNATURE_LEN as u32
{
Self::Sr25519
} else {
Self::None
}
}
_ => Self::None,
},
None => Self::None,
}
} else {
Self::None
}
}
}
#[derive(Clone, Copy, Debug)]
pub enum Hint {
None,
CheckSpecVersion,
CheckTxVersion,
CheckGenesis,
CheckMortality,
CheckNonce,
ChargeAssetTxPayment,
ChargeTransactionPayment,
FieldBalance,
FieldNonce,
FieldSpecName,
FieldSpecVersion,
FieldTxVersion,
}
impl Hint {
pub fn from_field(field: &Field<PortableForm>) -> Self {
let mut out = match &field.name {
Some(name) => match name.as_str() {
a if NONCE_ID_SET.contains(&a) => Self::FieldNonce,
a if SPEC_VERSION_ID_SET.contains(&a) => Self::FieldSpecVersion,
a if SPEC_NAME_ID_SET.contains(&a) => Self::FieldSpecName,
a if TX_VERSION_ID_SET.contains(&a) => Self::FieldTxVersion,
_ => Self::None,
},
None => Self::None,
};
if let Self::None = out {
if let Some(type_name) = &field.type_name {
out = match type_name.as_str() {
a if BALANCE_ID_SET.contains(&a) => Self::FieldBalance,
_ => Self::None,
};
}
}
out
}
pub fn from_ext_meta(signed_ext_meta: &SignedExtensionMetadata) -> Self {
match signed_ext_meta.identifier.as_str() {
CHECK_SPEC_VERSION => Self::CheckSpecVersion,
CHECK_TX_VERSION => Self::CheckTxVersion,
CHECK_GENESIS => Self::CheckGenesis,
CHECK_MORTALITY => Self::CheckMortality,
CHECK_NONCE => Self::CheckNonce,
CHARGE_ASSET_TX_PAYMENT => Self::ChargeAssetTxPayment,
CHARGE_TRANSACTION_PAYMENT => Self::ChargeTransactionPayment,
_ => Self::None,
}
}
pub fn from_path(path: &Path<PortableForm>) -> Self {
match path.ident() {
Some(a) => match a.as_str() {
CHECK_NONCE => Self::CheckNonce,
CHARGE_ASSET_TX_PAYMENT => Self::ChargeAssetTxPayment,
CHARGE_TRANSACTION_PAYMENT => Self::ChargeTransactionPayment,
_ => Self::None,
},
None => Self::None,
}
}
pub fn unsigned_integer(&self) -> SpecialtyUnsignedInteger {
match &self {
Hint::CheckSpecVersion | Hint::FieldSpecVersion => {
SpecialtyUnsignedInteger::SpecVersion
}
Hint::CheckTxVersion | Hint::FieldTxVersion => SpecialtyUnsignedInteger::TxVersion,
Hint::CheckNonce | Hint::FieldNonce => SpecialtyUnsignedInteger::Nonce,
Hint::ChargeTransactionPayment => SpecialtyUnsignedInteger::Tip,
Hint::ChargeAssetTxPayment => SpecialtyUnsignedInteger::TipAsset,
Hint::FieldBalance => SpecialtyUnsignedInteger::Balance,
_ => SpecialtyUnsignedInteger::None,
}
}
pub fn string(&self) -> SpecialtyStr {
match &self {
Hint::FieldSpecName => SpecialtyStr::SpecName,
_ => SpecialtyStr::None,
}
}
pub fn hash256(&self) -> SpecialtyH256 {
match &self {
Hint::CheckGenesis => SpecialtyH256::GenesisHash,
Hint::CheckMortality => SpecialtyH256::BlockHash,
_ => SpecialtyH256::None,
}
}
}
pub enum SpecialtyTypeHinted {
None,
AccountId32,
Era,
H160,
H256,
H512,
MultiSignature,
PalletSpecific(PalletSpecificItem),
Perbill,
Percent,
Permill,
Perquintill,
PerU16,
PublicEd25519,
PublicSr25519,
PublicEcdsa,
SignatureEd25519,
SignatureSr25519,
SignatureEcdsa,
UncheckedExtrinsic,
}
#[derive(Debug, Eq, PartialEq)]
pub enum PalletSpecificItem {
Call,
Event,
}
impl SpecialtyTypeHinted {
pub fn from_type(ty: &Type<PortableForm>) -> Self {
match ty.path.ident() {
Some(a) => match a.as_str() {
ACCOUNT_ID32 => Self::AccountId32,
a if CALL.contains(&a) => Self::PalletSpecific(PalletSpecificItem::Call),
ERA => Self::Era,
a if EVENT.contains(&a) => Self::PalletSpecific(PalletSpecificItem::Event),
a if H160.contains(&a) => Self::H160,
H256 => Self::H256,
H512 => Self::H512,
MULTI_SIGNATURE => Self::MultiSignature,
PERBILL => Self::Perbill,
PERCENT => Self::Percent,
PERMILL => Self::Permill,
PERQUINTILL => Self::Perquintill,
PERU16 => Self::PerU16,
PUBLIC => match ty
.path
.namespace()
.iter()
.map(|x| x.as_str())
.collect::<Vec<&str>>()
.as_ref()
{
SP_CORE_ED25519 => Self::PublicEd25519,
SP_CORE_SR25519 => Self::PublicSr25519,
SP_CORE_ECDSA => Self::PublicEcdsa,
_ => Self::None,
},
SIGNATURE => match ty
.path
.namespace()
.iter()
.map(|x| x.as_str())
.collect::<Vec<&str>>()
.as_ref()
{
SP_CORE_ED25519 => Self::SignatureEd25519,
SP_CORE_SR25519 => Self::SignatureSr25519,
SP_CORE_ECDSA => Self::SignatureEcdsa,
_ => Self::None,
},
UNCHECKED_EXTRINSIC_IDENT => match ty
.path
.namespace()
.iter()
.map(|x| x.as_str())
.collect::<Vec<&str>>()
.as_ref()
{
UNCHECKED_EXTRINSIC_NAMESPACE => Self::UncheckedExtrinsic,
_ => Self::None,
},
_ => Self::None,
},
None => Self::None,
}
}
}
pub enum SpecialtyTypeChecked {
None,
AccountId32,
Era,
H160,
H256,
H512,
PalletSpecific {
pallet_name: String,
pallet_info: Info,
pallet_variant: Variant<PortableForm>,
item_ty_id: u32,
variants: Vec<Variant<PortableForm>>,
item: PalletSpecificItem,
},
Perbill,
Percent,
Permill,
Perquintill,
PerU16,
PublicEd25519,
PublicSr25519,
PublicEcdsa,
SignatureEd25519,
SignatureSr25519,
SignatureEcdsa,
}
impl SpecialtyTypeChecked {
pub fn from_type<B, E, M>(
ty: &Type<PortableForm>,
data: &B,
ext_memory: &mut E,
position: &mut usize,
registry: &M::TypeRegistry,
) -> Self
where
B: AddressableBuffer<E>,
E: ExternalMemory,
M: AsMetadata<E>,
{
match SpecialtyTypeHinted::from_type(ty) {
SpecialtyTypeHinted::None => Self::None,
SpecialtyTypeHinted::AccountId32 => Self::AccountId32,
SpecialtyTypeHinted::Era => Self::Era,
SpecialtyTypeHinted::H160 => Self::H160,
SpecialtyTypeHinted::H256 => Self::H256,
SpecialtyTypeHinted::H512 => Self::H512,
SpecialtyTypeHinted::MultiSignature => {
if let TypeDef::Variant(x) = &ty.type_def {
match pick_variant::<B, E>(&x.variants, data, ext_memory, *position) {
Ok(signature_variant) => {
if signature_variant.fields.len() == 1 {
match SignatureIndicator::from_field::<E, M>(
&signature_variant.fields[0],
ext_memory,
registry,
) {
SignatureIndicator::None => Self::None,
SignatureIndicator::Ecdsa => {
*position += ENUM_INDEX_ENCODED_LEN;
Self::SignatureEcdsa
}
SignatureIndicator::Ed25519 => {
*position += ENUM_INDEX_ENCODED_LEN;
Self::SignatureEd25519
}
SignatureIndicator::Sr25519 => {
*position += ENUM_INDEX_ENCODED_LEN;
Self::SignatureSr25519
}
}
} else {
Self::None
}
}
Err(_) => Self::None,
}
} else {
Self::None
}
}
SpecialtyTypeHinted::PalletSpecific(item) => {
if let TypeDef::Variant(x) = &ty.type_def {
match pick_variant::<B, E>(&x.variants, data, ext_memory, *position) {
Ok(pallet_variant) => {
let pallet_name = pallet_variant.name.to_owned();
if pallet_variant.fields.len() == 1 {
let item_ty_id = pallet_variant.fields[0].ty.id;
match registry.resolve_ty(item_ty_id, ext_memory) {
Ok(variants_ty) => {
if let SpecialtyTypeHinted::PalletSpecific(item_repeated) =
SpecialtyTypeHinted::from_type(&variants_ty)
{
if item != item_repeated {
Self::None
} else if let TypeDef::Variant(ref var) =
variants_ty.type_def
{
let pallet_info = Info::from_ty(&variants_ty);
*position += ENUM_INDEX_ENCODED_LEN;
Self::PalletSpecific {
pallet_name,
pallet_info,
pallet_variant: pallet_variant.to_owned(),
item_ty_id,
variants: var.variants.to_vec(),
item,
}
} else {
Self::None
}
} else {
Self::None
}
}
Err(_) => Self::None,
}
} else {
Self::None
}
}
Err(_) => Self::None,
}
} else {
Self::None
}
}
SpecialtyTypeHinted::Perbill => Self::Perbill,
SpecialtyTypeHinted::Percent => Self::Percent,
SpecialtyTypeHinted::Permill => Self::Permill,
SpecialtyTypeHinted::Perquintill => Self::Perquintill,
SpecialtyTypeHinted::PerU16 => Self::PerU16,
SpecialtyTypeHinted::PublicEd25519 => Self::PublicEd25519,
SpecialtyTypeHinted::PublicSr25519 => Self::PublicSr25519,
SpecialtyTypeHinted::PublicEcdsa => Self::PublicEcdsa,
SpecialtyTypeHinted::SignatureEd25519 => Self::SignatureEd25519,
SpecialtyTypeHinted::SignatureSr25519 => Self::SignatureSr25519,
SpecialtyTypeHinted::SignatureEcdsa => Self::SignatureEcdsa,
SpecialtyTypeHinted::UncheckedExtrinsic => Self::None,
}
}
}