mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-06 17:18:03 +00:00
Allow decoding Events containing BitVecs (#408)
* Handle bitvec decoding * clippy * rework bitvec decoding; don't care about order and can handle other store primitives * cargo fmt
This commit is contained in:
+230
-139
@@ -26,16 +26,20 @@ use crate::{
|
||||
PhantomDataSendSync,
|
||||
Phase,
|
||||
};
|
||||
use bitvec::{
|
||||
order::Lsb0,
|
||||
vec::BitVec,
|
||||
};
|
||||
use codec::{
|
||||
Codec,
|
||||
Compact,
|
||||
Decode,
|
||||
Encode,
|
||||
Error as CodecError,
|
||||
Input,
|
||||
};
|
||||
use derivative::Derivative;
|
||||
use scale_info::{
|
||||
PortableRegistry,
|
||||
TypeDef,
|
||||
TypeDefPrimitive,
|
||||
};
|
||||
@@ -161,165 +165,189 @@ impl<T: Config> EventsDecoder<T> {
|
||||
input: &mut &[u8],
|
||||
output: &mut Vec<u8>,
|
||||
) -> Result<(), BasicError> {
|
||||
let ty = self
|
||||
.metadata
|
||||
.resolve_type(type_id)
|
||||
.ok_or(MetadataError::TypeNotFound(type_id))?;
|
||||
let all_bytes = *input;
|
||||
// consume some bytes, moving the cursor forward:
|
||||
decode_and_consume_type(type_id, &self.metadata.runtime_metadata().types, input)?;
|
||||
// count how many bytes were consumed based on remaining length:
|
||||
let consumed_len = all_bytes.len() - input.len();
|
||||
// move those consumed bytes to the output vec unaltered:
|
||||
output.extend(&all_bytes[0..consumed_len]);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_raw<T: Codec>(
|
||||
input: &mut &[u8],
|
||||
output: &mut Vec<u8>,
|
||||
) -> Result<(), BasicError> {
|
||||
let decoded = T::decode(input)?;
|
||||
decoded.encode_to(output);
|
||||
// Given a type Id and a type registry, attempt to consume the bytes
|
||||
// corresponding to that type from our input.
|
||||
fn decode_and_consume_type(
|
||||
type_id: u32,
|
||||
types: &PortableRegistry,
|
||||
input: &mut &[u8],
|
||||
) -> Result<(), BasicError> {
|
||||
let ty = types
|
||||
.resolve(type_id)
|
||||
.ok_or(MetadataError::TypeNotFound(type_id))?;
|
||||
|
||||
fn consume_type<T: Codec>(input: &mut &[u8]) -> Result<(), BasicError> {
|
||||
T::decode(input)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
match ty.type_def() {
|
||||
TypeDef::Composite(composite) => {
|
||||
for field in composite.fields() {
|
||||
decode_and_consume_type(field.ty().id(), types, input)?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
match ty.type_def() {
|
||||
TypeDef::Composite(composite) => {
|
||||
for field in composite.fields() {
|
||||
self.decode_type(field.ty().id(), input, output)?
|
||||
}
|
||||
Ok(())
|
||||
TypeDef::Variant(variant) => {
|
||||
let variant_index = u8::decode(input)?;
|
||||
let variant = variant
|
||||
.variants()
|
||||
.iter()
|
||||
.find(|v| v.index() == variant_index)
|
||||
.ok_or_else(|| {
|
||||
BasicError::Other(format!("Variant {} not found", variant_index))
|
||||
})?;
|
||||
for field in variant.fields() {
|
||||
decode_and_consume_type(field.ty().id(), types, input)?;
|
||||
}
|
||||
TypeDef::Variant(variant) => {
|
||||
let variant_index = u8::decode(input)?;
|
||||
variant_index.encode_to(output);
|
||||
let variant = variant
|
||||
.variants()
|
||||
.iter()
|
||||
.find(|v| v.index() == variant_index)
|
||||
.ok_or_else(|| {
|
||||
BasicError::Other(format!("Variant {} not found", variant_index))
|
||||
})?;
|
||||
for field in variant.fields() {
|
||||
self.decode_type(field.ty().id(), input, output)?;
|
||||
}
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
TypeDef::Sequence(seq) => {
|
||||
let len = <Compact<u32>>::decode(input)?;
|
||||
for _ in 0..len.0 {
|
||||
decode_and_consume_type(seq.type_param().id(), types, input)?;
|
||||
}
|
||||
TypeDef::Sequence(seq) => {
|
||||
let len = <Compact<u32>>::decode(input)?;
|
||||
len.encode_to(output);
|
||||
for _ in 0..len.0 {
|
||||
self.decode_type(seq.type_param().id(), input, output)?;
|
||||
}
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
TypeDef::Array(arr) => {
|
||||
for _ in 0..arr.len() {
|
||||
decode_and_consume_type(arr.type_param().id(), types, input)?;
|
||||
}
|
||||
TypeDef::Array(arr) => {
|
||||
for _ in 0..arr.len() {
|
||||
self.decode_type(arr.type_param().id(), input, output)?;
|
||||
}
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
TypeDef::Tuple(tuple) => {
|
||||
for field in tuple.fields() {
|
||||
decode_and_consume_type(field.id(), types, input)?;
|
||||
}
|
||||
TypeDef::Tuple(tuple) => {
|
||||
for field in tuple.fields() {
|
||||
self.decode_type(field.id(), input, output)?;
|
||||
Ok(())
|
||||
}
|
||||
TypeDef::Primitive(primitive) => {
|
||||
match primitive {
|
||||
TypeDefPrimitive::Bool => consume_type::<bool>(input),
|
||||
TypeDefPrimitive::Char => {
|
||||
Err(
|
||||
EventsDecodingError::UnsupportedPrimitive(TypeDefPrimitive::Char)
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
TypeDefPrimitive::Str => consume_type::<String>(input),
|
||||
TypeDefPrimitive::U8 => consume_type::<u8>(input),
|
||||
TypeDefPrimitive::U16 => consume_type::<u16>(input),
|
||||
TypeDefPrimitive::U32 => consume_type::<u32>(input),
|
||||
TypeDefPrimitive::U64 => consume_type::<u64>(input),
|
||||
TypeDefPrimitive::U128 => consume_type::<u128>(input),
|
||||
TypeDefPrimitive::U256 => {
|
||||
Err(
|
||||
EventsDecodingError::UnsupportedPrimitive(TypeDefPrimitive::U256)
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
TypeDefPrimitive::I8 => consume_type::<i8>(input),
|
||||
TypeDefPrimitive::I16 => consume_type::<i16>(input),
|
||||
TypeDefPrimitive::I32 => consume_type::<i32>(input),
|
||||
TypeDefPrimitive::I64 => consume_type::<i64>(input),
|
||||
TypeDefPrimitive::I128 => consume_type::<i128>(input),
|
||||
TypeDefPrimitive::I256 => {
|
||||
Err(
|
||||
EventsDecodingError::UnsupportedPrimitive(TypeDefPrimitive::I256)
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
TypeDef::Primitive(primitive) => {
|
||||
}
|
||||
TypeDef::Compact(compact) => {
|
||||
let inner = types
|
||||
.resolve(compact.type_param().id())
|
||||
.ok_or(MetadataError::TypeNotFound(type_id))?;
|
||||
let mut decode_compact_primitive = |primitive: &TypeDefPrimitive| {
|
||||
match primitive {
|
||||
TypeDefPrimitive::Bool => decode_raw::<bool>(input, output),
|
||||
TypeDefPrimitive::Char => {
|
||||
Err(EventsDecodingError::UnsupportedPrimitive(
|
||||
TypeDefPrimitive::Char,
|
||||
)
|
||||
.into())
|
||||
}
|
||||
TypeDefPrimitive::Str => decode_raw::<String>(input, output),
|
||||
TypeDefPrimitive::U8 => decode_raw::<u8>(input, output),
|
||||
TypeDefPrimitive::U16 => decode_raw::<u16>(input, output),
|
||||
TypeDefPrimitive::U32 => decode_raw::<u32>(input, output),
|
||||
TypeDefPrimitive::U64 => decode_raw::<u64>(input, output),
|
||||
TypeDefPrimitive::U128 => decode_raw::<u128>(input, output),
|
||||
TypeDefPrimitive::U256 => {
|
||||
Err(EventsDecodingError::UnsupportedPrimitive(
|
||||
TypeDefPrimitive::U256,
|
||||
)
|
||||
.into())
|
||||
}
|
||||
TypeDefPrimitive::I8 => decode_raw::<i8>(input, output),
|
||||
TypeDefPrimitive::I16 => decode_raw::<i16>(input, output),
|
||||
TypeDefPrimitive::I32 => decode_raw::<i32>(input, output),
|
||||
TypeDefPrimitive::I64 => decode_raw::<i64>(input, output),
|
||||
TypeDefPrimitive::I128 => decode_raw::<i128>(input, output),
|
||||
TypeDefPrimitive::I256 => {
|
||||
Err(EventsDecodingError::UnsupportedPrimitive(
|
||||
TypeDefPrimitive::I256,
|
||||
)
|
||||
.into())
|
||||
TypeDefPrimitive::U8 => consume_type::<Compact<u8>>(input),
|
||||
TypeDefPrimitive::U16 => consume_type::<Compact<u16>>(input),
|
||||
TypeDefPrimitive::U32 => consume_type::<Compact<u32>>(input),
|
||||
TypeDefPrimitive::U64 => consume_type::<Compact<u64>>(input),
|
||||
TypeDefPrimitive::U128 => consume_type::<Compact<u128>>(input),
|
||||
prim => {
|
||||
Err(EventsDecodingError::InvalidCompactPrimitive(prim.clone())
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
TypeDef::Compact(compact) => {
|
||||
let inner = self
|
||||
.metadata
|
||||
.resolve_type(compact.type_param().id())
|
||||
.ok_or(MetadataError::TypeNotFound(type_id))?;
|
||||
let mut decode_compact_primitive = |primitive: &TypeDefPrimitive| {
|
||||
match primitive {
|
||||
TypeDefPrimitive::U8 => decode_raw::<Compact<u8>>(input, output),
|
||||
TypeDefPrimitive::U16 => {
|
||||
decode_raw::<Compact<u16>>(input, output)
|
||||
}
|
||||
TypeDefPrimitive::U32 => {
|
||||
decode_raw::<Compact<u32>>(input, output)
|
||||
}
|
||||
TypeDefPrimitive::U64 => {
|
||||
decode_raw::<Compact<u64>>(input, output)
|
||||
}
|
||||
TypeDefPrimitive::U128 => {
|
||||
decode_raw::<Compact<u128>>(input, output)
|
||||
}
|
||||
prim => {
|
||||
Err(EventsDecodingError::InvalidCompactPrimitive(
|
||||
prim.clone(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
};
|
||||
match inner.type_def() {
|
||||
TypeDef::Primitive(primitive) => decode_compact_primitive(primitive),
|
||||
TypeDef::Composite(composite) => {
|
||||
match composite.fields() {
|
||||
[field] => {
|
||||
let field_ty = self
|
||||
.metadata
|
||||
.resolve_type(field.ty().id())
|
||||
.ok_or_else(|| {
|
||||
MetadataError::TypeNotFound(field.ty().id())
|
||||
})?;
|
||||
if let TypeDef::Primitive(primitive) = field_ty.type_def()
|
||||
{
|
||||
decode_compact_primitive(primitive)
|
||||
} else {
|
||||
Err(EventsDecodingError::InvalidCompactType(
|
||||
};
|
||||
match inner.type_def() {
|
||||
TypeDef::Primitive(primitive) => decode_compact_primitive(primitive),
|
||||
TypeDef::Composite(composite) => {
|
||||
match composite.fields() {
|
||||
[field] => {
|
||||
let field_ty =
|
||||
types.resolve(field.ty().id()).ok_or_else(|| {
|
||||
MetadataError::TypeNotFound(field.ty().id())
|
||||
})?;
|
||||
if let TypeDef::Primitive(primitive) = field_ty.type_def() {
|
||||
decode_compact_primitive(primitive)
|
||||
} else {
|
||||
Err(EventsDecodingError::InvalidCompactType(
|
||||
"Composite type must have a single primitive field"
|
||||
.into(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
Err(EventsDecodingError::InvalidCompactType(
|
||||
"Composite type must have a single field".into(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
Err(EventsDecodingError::InvalidCompactType(
|
||||
"Compact type must be a primitive or a composite type".into(),
|
||||
)
|
||||
.into())
|
||||
_ => {
|
||||
Err(EventsDecodingError::InvalidCompactType(
|
||||
"Composite type must have a single field".into(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
Err(EventsDecodingError::InvalidCompactType(
|
||||
"Compact type must be a primitive or a composite type".into(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
TypeDef::BitSequence(_bitseq) => {
|
||||
// decode_raw::<bitvec::BitVec>
|
||||
unimplemented!("BitVec decoding for events not implemented yet")
|
||||
}
|
||||
TypeDef::BitSequence(bitseq) => {
|
||||
let bit_store_def = types
|
||||
.resolve(bitseq.bit_store_type().id())
|
||||
.ok_or(MetadataError::TypeNotFound(type_id))?
|
||||
.type_def();
|
||||
|
||||
// We just need to consume the correct number of bytes. Roughly, we encode this
|
||||
// as a Compact<u32> length, and then a slice of T of that length, where T is the
|
||||
// bit store type. So, we ignore the bit order and only care that the bit store type
|
||||
// used lines up in terms of the number of bytes it will take to encode/decode it.
|
||||
match bit_store_def {
|
||||
TypeDef::Primitive(TypeDefPrimitive::U8) => {
|
||||
consume_type::<BitVec<Lsb0, u8>>(input)
|
||||
}
|
||||
TypeDef::Primitive(TypeDefPrimitive::U16) => {
|
||||
consume_type::<BitVec<Lsb0, u16>>(input)
|
||||
}
|
||||
TypeDef::Primitive(TypeDefPrimitive::U32) => {
|
||||
consume_type::<BitVec<Lsb0, u32>>(input)
|
||||
}
|
||||
TypeDef::Primitive(TypeDefPrimitive::U64) => {
|
||||
consume_type::<BitVec<Lsb0, u64>>(input)
|
||||
}
|
||||
store => {
|
||||
return Err(EventsDecodingError::InvalidBitSequenceType(format!(
|
||||
"{:?}",
|
||||
store
|
||||
))
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -333,8 +361,12 @@ pub enum EventsDecodingError {
|
||||
/// Invalid compact type, must be an unsigned int.
|
||||
#[error("Invalid compact primitive {0:?}")]
|
||||
InvalidCompactPrimitive(TypeDefPrimitive),
|
||||
/// Invalid compact type; error details in string.
|
||||
#[error("Invalid compact composite type {0}")]
|
||||
InvalidCompactType(String),
|
||||
/// Invalid bit sequence type; bit store type or bit order type used aren't supported.
|
||||
#[error("Invalid bit sequence type; bit store type {0} is not supported")]
|
||||
InvalidBitSequenceType(String),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -345,6 +377,7 @@ mod tests {
|
||||
DefaultConfig,
|
||||
Phase,
|
||||
};
|
||||
use codec::Encode;
|
||||
use frame_metadata::{
|
||||
v14::{
|
||||
ExtrinsicMetadata,
|
||||
@@ -360,6 +393,8 @@ mod tests {
|
||||
};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
type TypeId = scale_info::interner::UntrackedSymbol<std::any::TypeId>;
|
||||
|
||||
#[derive(Encode)]
|
||||
pub struct EventRecord<E: Encode> {
|
||||
phase: Phase,
|
||||
@@ -377,6 +412,16 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn singleton_type_registry<T: scale_info::TypeInfo + 'static>(
|
||||
) -> (TypeId, PortableRegistry) {
|
||||
let m = scale_info::MetaType::new::<T>();
|
||||
let mut types = scale_info::Registry::new();
|
||||
let id = types.register_type(&m);
|
||||
let portable_registry: PortableRegistry = types.into();
|
||||
|
||||
(id, portable_registry)
|
||||
}
|
||||
|
||||
fn pallet_metadata<E: TypeInfo + 'static>(pallet_index: u8) -> PalletMetadata {
|
||||
let event = PalletEventMetadata {
|
||||
ty: meta_type::<E>(),
|
||||
@@ -404,6 +449,19 @@ mod tests {
|
||||
EventsDecoder::<DefaultConfig>::new(metadata)
|
||||
}
|
||||
|
||||
fn decode_and_consume_type_consumes_all_bytes<
|
||||
T: codec::Encode + scale_info::TypeInfo + 'static,
|
||||
>(
|
||||
val: T,
|
||||
) {
|
||||
let (type_id, registry) = singleton_type_registry::<T>();
|
||||
let bytes = val.encode();
|
||||
let cursor = &mut &*bytes;
|
||||
|
||||
decode_and_consume_type(type_id.id(), ®istry, cursor).unwrap();
|
||||
assert_eq!(cursor.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_single_event() {
|
||||
#[derive(Clone, Encode, TypeInfo)]
|
||||
@@ -552,4 +610,37 @@ mod tests {
|
||||
assert_eq!(events[0].1.variant_index, encoded_event[0]);
|
||||
assert_eq!(events[0].1.data.0, encoded_event[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_bitvec() {
|
||||
use bitvec::order::Msb0;
|
||||
|
||||
decode_and_consume_type_consumes_all_bytes(
|
||||
bitvec::bitvec![Lsb0, u8; 0, 1, 1, 0, 1],
|
||||
);
|
||||
decode_and_consume_type_consumes_all_bytes(
|
||||
bitvec::bitvec![Msb0, u8; 0, 1, 1, 0, 1, 0, 1, 0, 0],
|
||||
);
|
||||
|
||||
decode_and_consume_type_consumes_all_bytes(
|
||||
bitvec::bitvec![Lsb0, u16; 0, 1, 1, 0, 1],
|
||||
);
|
||||
decode_and_consume_type_consumes_all_bytes(
|
||||
bitvec::bitvec![Msb0, u16; 0, 1, 1, 0, 1, 0, 1, 0, 0],
|
||||
);
|
||||
|
||||
decode_and_consume_type_consumes_all_bytes(
|
||||
bitvec::bitvec![Lsb0, u32; 0, 1, 1, 0, 1],
|
||||
);
|
||||
decode_and_consume_type_consumes_all_bytes(
|
||||
bitvec::bitvec![Msb0, u32; 0, 1, 1, 0, 1, 0, 1, 0, 0],
|
||||
);
|
||||
|
||||
decode_and_consume_type_consumes_all_bytes(
|
||||
bitvec::bitvec![Lsb0, u64; 0, 1, 1, 0, 1],
|
||||
);
|
||||
decode_and_consume_type_consumes_all_bytes(
|
||||
bitvec::bitvec![Msb0, u64; 0, 1, 1, 0, 1, 0, 1, 0, 0],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user