#[cfg(feature = "std")]
use std::cmp::Ordering;
#[cfg(not(feature = "std"))]
use core::cmp::Ordering;
use external_memory_tools::{AddressableBuffer, BufferError, ExternalMemory};
use crate::cards::{Call, ExtendedData, ParsedData};
use crate::compacts::get_compact;
use crate::decode_as_type_at_position;
use crate::error::{ParserError, UncheckedExtrinsicError};
use crate::traits::AsCompleteMetadata;
const VERSION_LENGTH: usize = 1;
const VERSION_MASK: u8 = 0b0111_1111;
const VERSION_UNSIGNED: u8 = 0;
pub fn decode_as_unchecked_extrinsic<B, E, M>(
input: &B,
ext_memory: &mut E,
metadata: &M,
) -> Result<UncheckedExtrinsic, UncheckedExtrinsicError<E, M>>
where
B: AddressableBuffer<E>,
E: ExternalMemory,
M: AsCompleteMetadata<E>,
{
let extrinsic_type_params = metadata
.extrinsic_type_params()
.map_err(UncheckedExtrinsicError::MetaStructure)?;
let registry = metadata.types();
let mut extrinsic_start: usize = 0;
let extrinsic_length = get_compact::<u32, B, E>(input, ext_memory, &mut extrinsic_start)
.map_err(|_| UncheckedExtrinsicError::FormatNoCompact)? as usize;
let len = input.total_len();
match (extrinsic_start + extrinsic_length).cmp(&len) {
Ordering::Greater => {
return Err(UncheckedExtrinsicError::Parsing(ParserError::Buffer(
BufferError::DataTooShort {
position: len,
minimal_length: extrinsic_start + extrinsic_length - len,
},
)))
}
Ordering::Less => {
return Err(UncheckedExtrinsicError::Parsing(
ParserError::SomeDataNotUsedBlob {
from: extrinsic_start + extrinsic_length,
},
))
}
Ordering::Equal => {}
}
let mut position = extrinsic_start;
let version_byte = input
.read_byte(ext_memory, position)
.map_err(|e| UncheckedExtrinsicError::Parsing(ParserError::Buffer(e)))?;
position += VERSION_LENGTH;
let version = metadata
.extrinsic_version()
.map_err(UncheckedExtrinsicError::MetaStructure)?;
if version_byte & VERSION_MASK != version {
Err(UncheckedExtrinsicError::VersionMismatch {
version_byte,
version,
})
} else if version_byte & !VERSION_MASK == VERSION_UNSIGNED {
let call_extended_data = decode_as_type_at_position::<B, E, M>(
&extrinsic_type_params.call_ty,
input,
ext_memory,
®istry,
&mut position,
)?;
if let ParsedData::Call(call) = call_extended_data.data {
Ok(UncheckedExtrinsic::Unsigned { call })
} else {
Err(UncheckedExtrinsicError::UnexpectedCallTy {
call_ty_id: extrinsic_type_params.call_ty.id,
})
}
} else {
let address = decode_as_type_at_position::<B, E, M>(
&extrinsic_type_params.address_ty,
input,
ext_memory,
®istry,
&mut position,
)?;
let signature = decode_as_type_at_position::<B, E, M>(
&extrinsic_type_params.signature_ty,
input,
ext_memory,
®istry,
&mut position,
)?;
let extra = decode_as_type_at_position::<B, E, M>(
&extrinsic_type_params.extra_ty,
input,
ext_memory,
®istry,
&mut position,
)?;
let call_extended_data = decode_as_type_at_position::<B, E, M>(
&extrinsic_type_params.call_ty,
input,
ext_memory,
®istry,
&mut position,
)?;
if let ParsedData::Call(call) = call_extended_data.data {
Ok(UncheckedExtrinsic::Signed {
address,
signature,
extra,
call,
})
} else {
Err(UncheckedExtrinsicError::UnexpectedCallTy {
call_ty_id: extrinsic_type_params.call_ty.id,
})
}
}
}
#[derive(Debug, Eq, PartialEq)]
#[allow(clippy::large_enum_variant)]
pub enum UncheckedExtrinsic {
Signed {
address: ExtendedData,
signature: ExtendedData,
extra: ExtendedData,
call: Call,
},
Unsigned {
call: Call,
},
}