mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-17 03:11:01 +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,
|
PhantomDataSendSync,
|
||||||
Phase,
|
Phase,
|
||||||
};
|
};
|
||||||
|
use bitvec::{
|
||||||
|
order::Lsb0,
|
||||||
|
vec::BitVec,
|
||||||
|
};
|
||||||
use codec::{
|
use codec::{
|
||||||
Codec,
|
Codec,
|
||||||
Compact,
|
Compact,
|
||||||
Decode,
|
Decode,
|
||||||
Encode,
|
|
||||||
Error as CodecError,
|
Error as CodecError,
|
||||||
Input,
|
Input,
|
||||||
};
|
};
|
||||||
use derivative::Derivative;
|
use derivative::Derivative;
|
||||||
use scale_info::{
|
use scale_info::{
|
||||||
|
PortableRegistry,
|
||||||
TypeDef,
|
TypeDef,
|
||||||
TypeDefPrimitive,
|
TypeDefPrimitive,
|
||||||
};
|
};
|
||||||
@@ -161,165 +165,189 @@ impl<T: Config> EventsDecoder<T> {
|
|||||||
input: &mut &[u8],
|
input: &mut &[u8],
|
||||||
output: &mut Vec<u8>,
|
output: &mut Vec<u8>,
|
||||||
) -> Result<(), BasicError> {
|
) -> Result<(), BasicError> {
|
||||||
let ty = self
|
let all_bytes = *input;
|
||||||
.metadata
|
// consume some bytes, moving the cursor forward:
|
||||||
.resolve_type(type_id)
|
decode_and_consume_type(type_id, &self.metadata.runtime_metadata().types, input)?;
|
||||||
.ok_or(MetadataError::TypeNotFound(type_id))?;
|
// 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>(
|
// Given a type Id and a type registry, attempt to consume the bytes
|
||||||
input: &mut &[u8],
|
// corresponding to that type from our input.
|
||||||
output: &mut Vec<u8>,
|
fn decode_and_consume_type(
|
||||||
) -> Result<(), BasicError> {
|
type_id: u32,
|
||||||
let decoded = T::decode(input)?;
|
types: &PortableRegistry,
|
||||||
decoded.encode_to(output);
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
TypeDef::Variant(variant) => {
|
||||||
match ty.type_def() {
|
let variant_index = u8::decode(input)?;
|
||||||
TypeDef::Composite(composite) => {
|
let variant = variant
|
||||||
for field in composite.fields() {
|
.variants()
|
||||||
self.decode_type(field.ty().id(), input, output)?
|
.iter()
|
||||||
}
|
.find(|v| v.index() == variant_index)
|
||||||
Ok(())
|
.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) => {
|
Ok(())
|
||||||
let variant_index = u8::decode(input)?;
|
}
|
||||||
variant_index.encode_to(output);
|
TypeDef::Sequence(seq) => {
|
||||||
let variant = variant
|
let len = <Compact<u32>>::decode(input)?;
|
||||||
.variants()
|
for _ in 0..len.0 {
|
||||||
.iter()
|
decode_and_consume_type(seq.type_param().id(), types, input)?;
|
||||||
.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(())
|
|
||||||
}
|
}
|
||||||
TypeDef::Sequence(seq) => {
|
Ok(())
|
||||||
let len = <Compact<u32>>::decode(input)?;
|
}
|
||||||
len.encode_to(output);
|
TypeDef::Array(arr) => {
|
||||||
for _ in 0..len.0 {
|
for _ in 0..arr.len() {
|
||||||
self.decode_type(seq.type_param().id(), input, output)?;
|
decode_and_consume_type(arr.type_param().id(), types, input)?;
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
TypeDef::Array(arr) => {
|
Ok(())
|
||||||
for _ in 0..arr.len() {
|
}
|
||||||
self.decode_type(arr.type_param().id(), input, output)?;
|
TypeDef::Tuple(tuple) => {
|
||||||
}
|
for field in tuple.fields() {
|
||||||
Ok(())
|
decode_and_consume_type(field.id(), types, input)?;
|
||||||
}
|
}
|
||||||
TypeDef::Tuple(tuple) => {
|
Ok(())
|
||||||
for field in tuple.fields() {
|
}
|
||||||
self.decode_type(field.id(), input, output)?;
|
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 {
|
match primitive {
|
||||||
TypeDefPrimitive::Bool => decode_raw::<bool>(input, output),
|
TypeDefPrimitive::U8 => consume_type::<Compact<u8>>(input),
|
||||||
TypeDefPrimitive::Char => {
|
TypeDefPrimitive::U16 => consume_type::<Compact<u16>>(input),
|
||||||
Err(EventsDecodingError::UnsupportedPrimitive(
|
TypeDefPrimitive::U32 => consume_type::<Compact<u32>>(input),
|
||||||
TypeDefPrimitive::Char,
|
TypeDefPrimitive::U64 => consume_type::<Compact<u64>>(input),
|
||||||
)
|
TypeDefPrimitive::U128 => consume_type::<Compact<u128>>(input),
|
||||||
.into())
|
prim => {
|
||||||
}
|
Err(EventsDecodingError::InvalidCompactPrimitive(prim.clone())
|
||||||
TypeDefPrimitive::Str => decode_raw::<String>(input, output),
|
.into())
|
||||||
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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
TypeDef::Compact(compact) => {
|
match inner.type_def() {
|
||||||
let inner = self
|
TypeDef::Primitive(primitive) => decode_compact_primitive(primitive),
|
||||||
.metadata
|
TypeDef::Composite(composite) => {
|
||||||
.resolve_type(compact.type_param().id())
|
match composite.fields() {
|
||||||
.ok_or(MetadataError::TypeNotFound(type_id))?;
|
[field] => {
|
||||||
let mut decode_compact_primitive = |primitive: &TypeDefPrimitive| {
|
let field_ty =
|
||||||
match primitive {
|
types.resolve(field.ty().id()).ok_or_else(|| {
|
||||||
TypeDefPrimitive::U8 => decode_raw::<Compact<u8>>(input, output),
|
MetadataError::TypeNotFound(field.ty().id())
|
||||||
TypeDefPrimitive::U16 => {
|
})?;
|
||||||
decode_raw::<Compact<u16>>(input, output)
|
if let TypeDef::Primitive(primitive) = field_ty.type_def() {
|
||||||
}
|
decode_compact_primitive(primitive)
|
||||||
TypeDefPrimitive::U32 => {
|
} else {
|
||||||
decode_raw::<Compact<u32>>(input, output)
|
Err(EventsDecodingError::InvalidCompactType(
|
||||||
}
|
|
||||||
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(
|
|
||||||
"Composite type must have a single primitive field"
|
"Composite type must have a single primitive field"
|
||||||
.into(),
|
.into(),
|
||||||
)
|
)
|
||||||
.into())
|
.into())
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
Err(EventsDecodingError::InvalidCompactType(
|
|
||||||
"Composite type must have a single field".into(),
|
|
||||||
)
|
|
||||||
.into())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
_ => {
|
||||||
_ => {
|
Err(EventsDecodingError::InvalidCompactType(
|
||||||
Err(EventsDecodingError::InvalidCompactType(
|
"Composite type must have a single field".into(),
|
||||||
"Compact type must be a primitive or a composite type".into(),
|
)
|
||||||
)
|
.into())
|
||||||
.into())
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => {
|
||||||
|
Err(EventsDecodingError::InvalidCompactType(
|
||||||
|
"Compact type must be a primitive or a composite type".into(),
|
||||||
|
)
|
||||||
|
.into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
TypeDef::BitSequence(_bitseq) => {
|
}
|
||||||
// decode_raw::<bitvec::BitVec>
|
TypeDef::BitSequence(bitseq) => {
|
||||||
unimplemented!("BitVec decoding for events not implemented yet")
|
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.
|
/// Invalid compact type, must be an unsigned int.
|
||||||
#[error("Invalid compact primitive {0:?}")]
|
#[error("Invalid compact primitive {0:?}")]
|
||||||
InvalidCompactPrimitive(TypeDefPrimitive),
|
InvalidCompactPrimitive(TypeDefPrimitive),
|
||||||
|
/// Invalid compact type; error details in string.
|
||||||
#[error("Invalid compact composite type {0}")]
|
#[error("Invalid compact composite type {0}")]
|
||||||
InvalidCompactType(String),
|
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)]
|
#[cfg(test)]
|
||||||
@@ -345,6 +377,7 @@ mod tests {
|
|||||||
DefaultConfig,
|
DefaultConfig,
|
||||||
Phase,
|
Phase,
|
||||||
};
|
};
|
||||||
|
use codec::Encode;
|
||||||
use frame_metadata::{
|
use frame_metadata::{
|
||||||
v14::{
|
v14::{
|
||||||
ExtrinsicMetadata,
|
ExtrinsicMetadata,
|
||||||
@@ -360,6 +393,8 @@ mod tests {
|
|||||||
};
|
};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
type TypeId = scale_info::interner::UntrackedSymbol<std::any::TypeId>;
|
||||||
|
|
||||||
#[derive(Encode)]
|
#[derive(Encode)]
|
||||||
pub struct EventRecord<E: Encode> {
|
pub struct EventRecord<E: Encode> {
|
||||||
phase: Phase,
|
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 {
|
fn pallet_metadata<E: TypeInfo + 'static>(pallet_index: u8) -> PalletMetadata {
|
||||||
let event = PalletEventMetadata {
|
let event = PalletEventMetadata {
|
||||||
ty: meta_type::<E>(),
|
ty: meta_type::<E>(),
|
||||||
@@ -404,6 +449,19 @@ mod tests {
|
|||||||
EventsDecoder::<DefaultConfig>::new(metadata)
|
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]
|
#[test]
|
||||||
fn decode_single_event() {
|
fn decode_single_event() {
|
||||||
#[derive(Clone, Encode, TypeInfo)]
|
#[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.variant_index, encoded_event[0]);
|
||||||
assert_eq!(events[0].1.data.0, encoded_event[1..]);
|
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