Decode raw events using scale_value and return the decoded Values, too (#576)

* Decode raw events using scale_value and return the decoded Values, too

* cargo fmt

* cargo fmt and slight rearranging of test code
This commit is contained in:
James Wilson
2022-06-22 10:43:21 +01:00
committed by GitHub
parent a9129863bd
commit 1e8d0956cc
7 changed files with 176 additions and 625 deletions
+1 -1
View File
@@ -22,6 +22,7 @@ integration-tests = []
bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] }
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "full", "bit-vec"] }
scale-info = { version = "2.0.0", features = ["bit-vec"] }
scale-value = "0.2.0"
futures = "0.3.13"
hex = "0.4.3"
jsonrpsee = { version = "0.14.0", features = ["async-client", "client-ws-transport"] }
@@ -41,5 +42,4 @@ frame-metadata = "15.0.0"
derivative = "2.2.0"
[dev-dependencies]
assert_matches = "1.5.0"
tokio = { version = "1.8", features = ["macros", "time"] }
+5 -7
View File
@@ -14,15 +14,13 @@
// You should have received a copy of the GNU General Public License
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
use crate::{
events::EventsDecodingError,
metadata::{
InvalidMetadataError,
MetadataError,
},
use crate::metadata::{
InvalidMetadataError,
MetadataError,
};
use core::fmt::Debug;
use jsonrpsee::core::error::Error as RequestError;
use scale_value::scale::DecodeError;
use sp_core::crypto::SecretStringError;
use sp_runtime::transaction_validity::TransactionValidityError;
@@ -66,7 +64,7 @@ pub enum GenericError<E> {
Runtime(E),
/// Events decoding error.
#[error("Events decoding error: {0}")]
EventsDecoding(#[from] EventsDecodingError),
EventsDecoding(#[from] DecodeError),
/// Transaction progress error.
#[error("Transaction error: {0}")]
Transaction(#[from] TransactionError),
-487
View File
@@ -1,487 +0,0 @@
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
// This file is part of subxt.
//
// subxt is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// subxt is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
//! Dynamically decoding events.
use crate::{
error::BasicError,
metadata::MetadataError,
};
use bitvec::{
order::Lsb0,
vec::BitVec,
};
use codec::{
Codec,
Compact,
Decode,
};
use scale_info::{
PortableRegistry,
TypeDef,
TypeDefPrimitive,
};
/// Given a type Id and a type registry, attempt to consume the bytes
/// corresponding to that type from our input.
pub 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(())
}
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)?;
}
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)?;
}
Ok(())
}
TypeDef::Array(arr) => {
for _ in 0..arr.len() {
decode_and_consume_type(arr.type_param().id(), types, input)?;
}
Ok(())
}
TypeDef::Tuple(tuple) => {
for field in tuple.fields() {
decode_and_consume_type(field.id(), types, input)?;
}
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(),
)
}
}
}
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::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())
}
}
};
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())
}
}
}
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<u8, Lsb0>>(input)
}
TypeDef::Primitive(TypeDefPrimitive::U16) => {
consume_type::<BitVec<u16, Lsb0>>(input)
}
TypeDef::Primitive(TypeDefPrimitive::U32) => {
consume_type::<BitVec<u32, Lsb0>>(input)
}
TypeDef::Primitive(TypeDefPrimitive::U64) => {
consume_type::<BitVec<u64, Lsb0>>(input)
}
store => {
return Err(EventsDecodingError::InvalidBitSequenceType(format!(
"{:?}",
store
))
.into())
}
}
}
}
}
/// The possible errors that we can run into attempting to decode events.
#[derive(Debug, thiserror::Error)]
pub enum EventsDecodingError {
/// Unsupported primitive type
#[error("Unsupported primitive type {0:?}")]
UnsupportedPrimitive(TypeDefPrimitive),
/// 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)]
mod tests {
use super::*;
use crate::error::GenericError::{
Codec,
EventsDecoding,
Other,
};
use assert_matches::assert_matches;
use codec::Encode;
use scale_info::TypeInfo;
type TypeId = scale_info::interner::UntrackedSymbol<std::any::TypeId>;
/// Build a type registry that knows about the single type provided.
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 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(), &registry, cursor).unwrap();
assert_eq!(cursor.len(), 0);
}
#[test]
fn decode_bitvec() {
use bitvec::order::Msb0;
decode_and_consume_type_consumes_all_bytes(
bitvec::bitvec![u8, Lsb0; 0, 1, 1, 0, 1],
);
decode_and_consume_type_consumes_all_bytes(
bitvec::bitvec![u8, Msb0; 0, 1, 1, 0, 1, 0, 1, 0, 0],
);
decode_and_consume_type_consumes_all_bytes(
bitvec::bitvec![u16, Lsb0; 0, 1, 1, 0, 1],
);
decode_and_consume_type_consumes_all_bytes(
bitvec::bitvec![u16, Msb0; 0, 1, 1, 0, 1, 0, 1, 0, 0],
);
decode_and_consume_type_consumes_all_bytes(
bitvec::bitvec![u32, Lsb0; 0, 1, 1, 0, 1],
);
decode_and_consume_type_consumes_all_bytes(
bitvec::bitvec![u32, Msb0; 0, 1, 1, 0, 1, 0, 1, 0, 0],
);
decode_and_consume_type_consumes_all_bytes(
bitvec::bitvec![u64, Lsb0; 0, 1, 1, 0, 1],
);
decode_and_consume_type_consumes_all_bytes(
bitvec::bitvec![u64, Msb0; 0, 1, 1, 0, 1, 0, 1, 0, 0],
);
}
#[test]
fn decode_primitive() {
decode_and_consume_type_consumes_all_bytes(false);
decode_and_consume_type_consumes_all_bytes(true);
let dummy_data = vec![0u8];
let dummy_cursor = &mut &*dummy_data;
let (id, reg) = singleton_type_registry::<char>();
let res = decode_and_consume_type(id.id(), &reg, dummy_cursor);
assert_matches!(
res,
Err(EventsDecoding(EventsDecodingError::UnsupportedPrimitive(
TypeDefPrimitive::Char
)))
);
decode_and_consume_type_consumes_all_bytes("str".to_string());
decode_and_consume_type_consumes_all_bytes(1u8);
decode_and_consume_type_consumes_all_bytes(1i8);
decode_and_consume_type_consumes_all_bytes(1u16);
decode_and_consume_type_consumes_all_bytes(1i16);
decode_and_consume_type_consumes_all_bytes(1u32);
decode_and_consume_type_consumes_all_bytes(1i32);
decode_and_consume_type_consumes_all_bytes(1u64);
decode_and_consume_type_consumes_all_bytes(1i64);
decode_and_consume_type_consumes_all_bytes(1u128);
decode_and_consume_type_consumes_all_bytes(1i128);
}
#[test]
fn decode_tuple() {
decode_and_consume_type_consumes_all_bytes(());
decode_and_consume_type_consumes_all_bytes((true,));
decode_and_consume_type_consumes_all_bytes((true, "str"));
// Incomplete bytes for decoding
let dummy_data = false.encode();
let dummy_cursor = &mut &*dummy_data;
let (id, reg) = singleton_type_registry::<(bool, &'static str)>();
let res = decode_and_consume_type(id.id(), &reg, dummy_cursor);
assert_matches!(res, Err(Codec(_)));
// Incomplete bytes for decoding, with invalid char type
let dummy_data = (false, "str", 0u8).encode();
let dummy_cursor = &mut &*dummy_data;
let (id, reg) = singleton_type_registry::<(bool, &'static str, char)>();
let res = decode_and_consume_type(id.id(), &reg, dummy_cursor);
assert_matches!(
res,
Err(EventsDecoding(EventsDecodingError::UnsupportedPrimitive(
TypeDefPrimitive::Char
)))
);
// The last byte (0x0 u8) should not be consumed
assert_eq!(dummy_cursor.len(), 1);
}
#[test]
fn decode_array_and_seq() {
decode_and_consume_type_consumes_all_bytes([0]);
decode_and_consume_type_consumes_all_bytes([1, 2, 3, 4, 5]);
decode_and_consume_type_consumes_all_bytes([0; 500]);
decode_and_consume_type_consumes_all_bytes(["str", "abc", "cde"]);
decode_and_consume_type_consumes_all_bytes(vec![0]);
decode_and_consume_type_consumes_all_bytes(vec![1, 2, 3, 4, 5]);
decode_and_consume_type_consumes_all_bytes(vec!["str", "abc", "cde"]);
}
#[test]
fn decode_variant() {
#[derive(Clone, Encode, TypeInfo)]
enum EnumVar {
A,
B((&'static str, u8)),
C { named: i16 },
}
const INVALID_TYPE_ID: u32 = 1024;
decode_and_consume_type_consumes_all_bytes(EnumVar::A);
decode_and_consume_type_consumes_all_bytes(EnumVar::B(("str", 1)));
decode_and_consume_type_consumes_all_bytes(EnumVar::C { named: 1 });
// Invalid variant index
let dummy_data = 3u8.encode();
let dummy_cursor = &mut &*dummy_data;
let (id, reg) = singleton_type_registry::<EnumVar>();
let res = decode_and_consume_type(id.id(), &reg, dummy_cursor);
assert_matches!(res, Err(Other(_)));
// Valid index, incomplete data
let dummy_data = 2u8.encode();
let dummy_cursor = &mut &*dummy_data;
let res = decode_and_consume_type(id.id(), &reg, dummy_cursor);
assert_matches!(res, Err(Codec(_)));
let res = decode_and_consume_type(INVALID_TYPE_ID, &reg, dummy_cursor);
assert_matches!(res, Err(crate::error::GenericError::Metadata(_)));
}
#[test]
fn decode_composite() {
#[derive(Clone, Encode, TypeInfo)]
struct Composite {}
decode_and_consume_type_consumes_all_bytes(Composite {});
#[derive(Clone, Encode, TypeInfo)]
struct CompositeV2 {
id: u32,
name: String,
}
decode_and_consume_type_consumes_all_bytes(CompositeV2 {
id: 10,
name: "str".to_string(),
});
#[derive(Clone, Encode, TypeInfo)]
struct CompositeV3<T> {
id: u32,
extra: T,
}
decode_and_consume_type_consumes_all_bytes(CompositeV3 {
id: 10,
extra: vec![0, 1, 2],
});
decode_and_consume_type_consumes_all_bytes(CompositeV3 {
id: 10,
extra: bitvec::bitvec![u8, Lsb0; 0, 1, 1, 0, 1],
});
decode_and_consume_type_consumes_all_bytes(CompositeV3 {
id: 10,
extra: ("str", 1),
});
decode_and_consume_type_consumes_all_bytes(CompositeV3 {
id: 10,
extra: CompositeV2 {
id: 2,
name: "str".to_string(),
},
});
#[derive(Clone, Encode, TypeInfo)]
struct CompositeV4(u32, bool);
decode_and_consume_type_consumes_all_bytes(CompositeV4(1, true));
#[derive(Clone, Encode, TypeInfo)]
struct CompositeV5(u32);
decode_and_consume_type_consumes_all_bytes(CompositeV5(1));
}
#[test]
fn decode_compact() {
#[derive(Clone, Encode, TypeInfo)]
enum Compact {
A(#[codec(compact)] u32),
}
decode_and_consume_type_consumes_all_bytes(Compact::A(1));
#[derive(Clone, Encode, TypeInfo)]
struct CompactV2(#[codec(compact)] u32);
decode_and_consume_type_consumes_all_bytes(CompactV2(1));
#[derive(Clone, Encode, TypeInfo)]
struct CompactV3 {
#[codec(compact)]
val: u32,
}
decode_and_consume_type_consumes_all_bytes(CompactV3 { val: 1 });
#[derive(Clone, Encode, TypeInfo)]
struct CompactV4<T> {
#[codec(compact)]
val: T,
}
decode_and_consume_type_consumes_all_bytes(CompactV4 { val: 0u8 });
decode_and_consume_type_consumes_all_bytes(CompactV4 { val: 1u16 });
}
}
-1
View File
@@ -46,7 +46,6 @@ pub use super::{
EventDetails,
EventFilter,
Events,
EventsDecodingError,
FilterEvents,
RawEventDetails,
};
+168 -126
View File
@@ -16,7 +16,6 @@
//! A representation of a block of events.
use super::decoding;
use crate::{
error::BasicError,
Client,
@@ -36,7 +35,6 @@ use parking_lot::RwLock;
use sp_core::{
storage::StorageKey,
twox_128,
Bytes,
};
use std::sync::Arc;
@@ -310,6 +308,9 @@ pub struct EventDetails<Evs> {
pub event: Evs,
}
/// A Value which has been decoded from some raw bytes.
pub type DecodedValue = scale_value::Value<scale_value::scale::TypeId>;
/// The raw bytes for an event with associated details about
/// where and when it was emitted.
#[derive(Debug, Clone, PartialEq)]
@@ -326,15 +327,17 @@ pub struct RawEventDetails {
pub variant: String,
/// The index of the pallet Event variant.
pub variant_index: u8,
/// The raw Event data
pub data: Bytes,
/// The bytes representing the fields contained within the event.
pub bytes: Vec<u8>,
/// Generic values representing each field of the event.
pub fields: Vec<DecodedValue>,
}
impl RawEventDetails {
/// Attempt to decode this [`RawEventDetails`] into a specific event.
pub fn as_event<E: Event>(&self) -> Result<Option<E>, CodecError> {
if self.pallet == E::PALLET && self.variant == E::EVENT {
Ok(Some(E::decode(&mut &self.data[..])?))
Ok(Some(E::decode(&mut &self.bytes[..])?))
} else {
Ok(None)
}
@@ -369,15 +372,17 @@ fn decode_raw_event_details<T: Config>(
// Use metadata to figure out which bytes belong to this event:
let mut event_bytes = Vec::new();
let mut event_fields = Vec::new();
for arg in event_metadata.variant().fields() {
let type_id = arg.ty().id();
let all_bytes = *input;
// consume some bytes, moving the cursor forward:
decoding::decode_and_consume_type(
// consume some bytes for each event field, moving the cursor forward:
let value = scale_value::scale::decode_as_type(
input,
type_id,
&metadata.runtime_metadata().types,
input,
)?;
event_fields.push(value);
// 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:
@@ -396,7 +401,8 @@ fn decode_raw_event_details<T: Config>(
pallet: event_metadata.pallet().to_string(),
variant_index,
variant: event_metadata.event().to_string(),
data: event_bytes.into(),
bytes: event_bytes,
fields: event_fields,
})
}
@@ -522,12 +528,69 @@ mod tests {
use crate::Phase;
use codec::Encode;
use scale_info::TypeInfo;
use scale_value::Value;
/// Build a fake wrapped metadata.
fn metadata<E: TypeInfo + 'static>() -> Arc<RwLock<Metadata>> {
Arc::new(RwLock::new(test_utils::metadata::<E>()))
}
/// [`RawEventDetails`] can be annoying to test, because it contains
/// type info in the decoded field Values. Strip that here so that
/// we can compare fields more easily.
#[derive(Debug, PartialEq, Clone)]
pub struct TestRawEventDetails {
pub phase: Phase,
pub index: u32,
pub pallet: String,
pub pallet_index: u8,
pub variant: String,
pub variant_index: u8,
pub fields: Vec<scale_value::Value>,
}
/// Compare some actual [`RawEventDetails`] with a hand-constructed
/// (probably) [`TestRawEventDetails`].
pub fn assert_raw_events_match(
// Just for convenience, pass in the metadata type constructed
// by the `metadata` function above to simplify caller code.
metadata: &Arc<RwLock<Metadata>>,
actual: RawEventDetails,
expected: TestRawEventDetails,
) {
let metadata = metadata.read();
let types = &metadata.runtime_metadata().types;
// Make sure that the bytes handed back line up with the fields handed back;
// encode the fields back into bytes and they should be equal.
let mut actual_bytes = vec![];
for field in &actual.fields {
scale_value::scale::encode_as_type(
field.clone(),
field.context,
types,
&mut actual_bytes,
)
.expect("should be able to encode properly");
}
assert_eq!(actual_bytes, actual.bytes);
let actual_fields_no_context: Vec<_> = actual
.fields
.into_iter()
.map(|f| f.remove_context())
.collect();
// Check each of the other fields:
assert_eq!(actual.phase, expected.phase);
assert_eq!(actual.index, expected.index);
assert_eq!(actual.pallet, expected.pallet);
assert_eq!(actual.pallet_index, expected.pallet_index);
assert_eq!(actual.variant, expected.variant);
assert_eq!(actual.variant_index, expected.variant_index);
assert_eq!(actual_fields_no_context, expected.fields);
}
#[test]
fn statically_decode_single_event() {
#[derive(Clone, Debug, PartialEq, Decode, Encode, TypeInfo)]
@@ -657,9 +720,9 @@ mod tests {
#[test]
fn dynamically_decode_single_event() {
#[derive(Clone, Copy, Debug, PartialEq, Decode, Encode, TypeInfo)]
#[derive(Clone, Debug, PartialEq, Decode, Encode, TypeInfo)]
enum Event {
A(u8),
A(u8, bool, Vec<String>),
}
// Create fake metadata that knows about our single event, above:
@@ -667,33 +730,31 @@ mod tests {
// Encode our events in the format we expect back from a node, and
// construst an Events object to iterate them:
let event = Event::A(1);
let event = Event::A(1, true, vec!["Hi".into()]);
let events = events::<Event>(
metadata,
metadata.clone(),
vec![event_record(Phase::ApplyExtrinsic(123), event)],
);
let event_details: Vec<RawEventDetails> =
events.iter_raw().collect::<Result<_, _>>().unwrap();
let expected_event_data = {
let mut bytes = event.encode();
// Strip variant tag off event bytes:
bytes.drain(0..1);
bytes
};
assert_eq!(
event_details,
vec![RawEventDetails {
index: 0,
let mut event_details = events.iter_raw();
assert_raw_events_match(
&metadata,
event_details.next().unwrap().unwrap(),
TestRawEventDetails {
phase: Phase::ApplyExtrinsic(123),
index: 0,
pallet: "Test".to_string(),
pallet_index: 0,
variant: "A".to_string(),
variant_index: 0,
data: expected_event_data.into()
}]
fields: vec![
Value::uint(1u8),
Value::bool(true),
Value::unnamed_composite(vec![Value::string("Hi")]),
],
},
);
assert!(event_details.next().is_none());
}
#[test]
@@ -714,7 +775,7 @@ mod tests {
let event3 = Event::A(234);
let events = events::<Event>(
metadata,
metadata.clone(),
vec![
event_record(Phase::Initialization, event1),
event_record(Phase::ApplyExtrinsic(123), event2),
@@ -722,47 +783,48 @@ mod tests {
],
);
let event_details: Vec<RawEventDetails> =
events.iter_raw().collect::<Result<_, _>>().unwrap();
let event_bytes = |ev: Event| {
let mut bytes = ev.encode();
// Strip variant tag off event bytes:
bytes.drain(0..1);
bytes.into()
};
let mut event_details = events.iter_raw();
assert_eq!(
event_details,
vec![
RawEventDetails {
index: 0,
phase: Phase::Initialization,
pallet: "Test".to_string(),
pallet_index: 0,
variant: "A".to_string(),
variant_index: 0,
data: event_bytes(event1)
},
RawEventDetails {
index: 1,
phase: Phase::ApplyExtrinsic(123),
pallet: "Test".to_string(),
pallet_index: 0,
variant: "B".to_string(),
variant_index: 1,
data: event_bytes(event2)
},
RawEventDetails {
index: 2,
phase: Phase::Finalization,
pallet: "Test".to_string(),
pallet_index: 0,
variant: "A".to_string(),
variant_index: 0,
data: event_bytes(event3)
},
]
assert_raw_events_match(
&metadata,
event_details.next().unwrap().unwrap(),
TestRawEventDetails {
index: 0,
phase: Phase::Initialization,
pallet: "Test".to_string(),
pallet_index: 0,
variant: "A".to_string(),
variant_index: 0,
fields: vec![Value::uint(1u8)],
},
);
assert_raw_events_match(
&metadata,
event_details.next().unwrap().unwrap(),
TestRawEventDetails {
index: 1,
phase: Phase::ApplyExtrinsic(123),
pallet: "Test".to_string(),
pallet_index: 0,
variant: "B".to_string(),
variant_index: 1,
fields: vec![Value::bool(true)],
},
);
assert_raw_events_match(
&metadata,
event_details.next().unwrap().unwrap(),
TestRawEventDetails {
index: 2,
phase: Phase::Finalization,
pallet: "Test".to_string(),
pallet_index: 0,
variant: "A".to_string(),
variant_index: 0,
fields: vec![Value::uint(234u8)],
},
);
assert!(event_details.next().is_none());
}
#[test]
@@ -788,42 +850,37 @@ mod tests {
// Encode our events in the format we expect back from a node, and
// construst an Events object to iterate them:
let events = events_raw::<Event>(
metadata,
metadata.clone(),
event_bytes,
3, // 2 "good" events, and then it'll hit the naff bytes.
);
let event_bytes = |ev: Event| {
let mut bytes = ev.encode();
// Strip variant tag off event bytes:
bytes.drain(0..1);
bytes.into()
};
let mut events_iter = events.iter_raw();
assert_eq!(
assert_raw_events_match(
&metadata,
events_iter.next().unwrap().unwrap(),
RawEventDetails {
TestRawEventDetails {
index: 0,
phase: Phase::Initialization,
pallet: "Test".to_string(),
pallet_index: 0,
variant: "A".to_string(),
variant_index: 0,
data: event_bytes(Event::A(1))
}
fields: vec![Value::uint(1u8)],
},
);
assert_eq!(
assert_raw_events_match(
&metadata,
events_iter.next().unwrap().unwrap(),
RawEventDetails {
TestRawEventDetails {
index: 1,
phase: Phase::ApplyExtrinsic(123),
pallet: "Test".to_string(),
pallet_index: 0,
variant: "B".to_string(),
variant_index: 1,
data: event_bytes(Event::B(true))
}
fields: vec![Value::bool(true)],
},
);
// We'll hit an error trying to decode the third event:
@@ -846,7 +903,7 @@ mod tests {
// Encode our events in the format we expect back from a node, and
// construst an Events object to iterate them:
let events = events::<Event>(
metadata,
metadata.clone(),
vec![event_record(Phase::Finalization, Event::A(1))],
);
@@ -863,26 +920,21 @@ mod tests {
);
// Dynamically decode:
let event_details: Vec<RawEventDetails> =
events.iter_raw().collect::<Result<_, _>>().unwrap();
let expected_event_data = {
let mut bytes = Event::A(1).encode();
// Strip variant tag off event bytes:
bytes.drain(0..1);
bytes
};
assert_eq!(
event_details,
vec![RawEventDetails {
let mut event_details = events.iter_raw();
assert_raw_events_match(
&metadata,
event_details.next().unwrap().unwrap(),
TestRawEventDetails {
index: 0,
phase: Phase::Finalization,
pallet: "Test".to_string(),
pallet_index: 0,
variant: "A".to_string(),
variant_index: 0,
data: expected_event_data.into()
}]
fields: vec![Value::uint(1u8)],
},
);
assert!(event_details.next().is_none());
}
#[test]
@@ -901,7 +953,7 @@ mod tests {
// Encode our events in the format we expect back from a node, and
// construct an Events object to iterate them:
let events = events::<Event>(
metadata,
metadata.clone(),
vec![event_record(
Phase::Finalization,
Event::A(CompactWrapper(1)),
@@ -921,26 +973,21 @@ mod tests {
);
// Dynamically decode:
let event_details: Vec<RawEventDetails> =
events.iter_raw().collect::<Result<_, _>>().unwrap();
let expected_event_data = {
let mut bytes = Event::A(CompactWrapper(1)).encode();
// Strip variant tag off event bytes:
bytes.drain(0..1);
bytes
};
assert_eq!(
event_details,
vec![RawEventDetails {
let mut event_details = events.iter_raw();
assert_raw_events_match(
&metadata,
event_details.next().unwrap().unwrap(),
TestRawEventDetails {
index: 0,
phase: Phase::Finalization,
pallet: "Test".to_string(),
pallet_index: 0,
variant: "A".to_string(),
variant_index: 0,
data: expected_event_data.into()
}]
fields: vec![Value::unnamed_composite(vec![Value::uint(1u8)])],
},
);
assert!(event_details.next().is_none());
}
#[test]
@@ -963,7 +1010,7 @@ mod tests {
// Encode our events in the format we expect back from a node, and
// construct an Events object to iterate them:
let events = events::<Event>(
metadata,
metadata.clone(),
vec![event_record(Phase::Finalization, Event::A(MyType::B))],
);
@@ -980,25 +1027,20 @@ mod tests {
);
// Dynamically decode:
let event_details: Vec<RawEventDetails> =
events.iter_raw().collect::<Result<_, _>>().unwrap();
let expected_event_data = {
let mut bytes = Event::A(MyType::B).encode();
// Strip variant tag off event bytes:
bytes.drain(0..1);
bytes
};
assert_eq!(
event_details,
vec![RawEventDetails {
let mut event_details = events.iter_raw();
assert_raw_events_match(
&metadata,
event_details.next().unwrap().unwrap(),
TestRawEventDetails {
index: 0,
phase: Phase::Finalization,
pallet: "Test".to_string(),
pallet_index: 0,
variant: "A".to_string(),
variant_index: 0,
data: expected_event_data.into()
}]
fields: vec![Value::unnamed_variant("B", vec![])],
},
);
assert!(event_details.next().is_none());
}
}
+1 -2
View File
@@ -95,12 +95,10 @@
//! # }
//! ```
mod decoding;
mod event_subscription;
mod events_type;
mod filter_events;
pub use decoding::EventsDecodingError;
pub use event_subscription::{
subscribe,
subscribe_finalized,
@@ -111,6 +109,7 @@ pub use event_subscription::{
};
pub use events_type::{
at,
DecodedValue,
EventDetails,
Events,
RawEventDetails,
+1 -1
View File
@@ -386,7 +386,7 @@ impl<'client, T: Config, E: Decode + HasModuleError, Evs: Decode>
for ev in events.iter_raw() {
let ev = ev?;
if &ev.pallet == "System" && &ev.variant == "ExtrinsicFailed" {
let dispatch_error = E::decode(&mut &*ev.data)?;
let dispatch_error = E::decode(&mut &*ev.bytes)?;
if let Some(error_data) = dispatch_error.module_error_data() {
// Error index is utilized as the first byte from the error array.
let locked_metadata = self.client.metadata();