diff --git a/subxt/Cargo.toml b/subxt/Cargo.toml index 1b647e3351..10cded0179 100644 --- a/subxt/Cargo.toml +++ b/subxt/Cargo.toml @@ -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"] } diff --git a/subxt/src/error.rs b/subxt/src/error.rs index 50d1c1cbca..c392382135 100644 --- a/subxt/src/error.rs +++ b/subxt/src/error.rs @@ -14,15 +14,13 @@ // You should have received a copy of the GNU General Public License // along with subxt. If not, see . -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 { 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), diff --git a/subxt/src/events/decoding.rs b/subxt/src/events/decoding.rs deleted file mode 100644 index 6594f39b3e..0000000000 --- a/subxt/src/events/decoding.rs +++ /dev/null @@ -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 . - -//! 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(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 = >::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::(input), - TypeDefPrimitive::Char => { - Err( - EventsDecodingError::UnsupportedPrimitive(TypeDefPrimitive::Char) - .into(), - ) - } - TypeDefPrimitive::Str => consume_type::(input), - TypeDefPrimitive::U8 => consume_type::(input), - TypeDefPrimitive::U16 => consume_type::(input), - TypeDefPrimitive::U32 => consume_type::(input), - TypeDefPrimitive::U64 => consume_type::(input), - TypeDefPrimitive::U128 => consume_type::(input), - TypeDefPrimitive::U256 => { - Err( - EventsDecodingError::UnsupportedPrimitive(TypeDefPrimitive::U256) - .into(), - ) - } - TypeDefPrimitive::I8 => consume_type::(input), - TypeDefPrimitive::I16 => consume_type::(input), - TypeDefPrimitive::I32 => consume_type::(input), - TypeDefPrimitive::I64 => consume_type::(input), - TypeDefPrimitive::I128 => consume_type::(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::>(input), - TypeDefPrimitive::U16 => consume_type::>(input), - TypeDefPrimitive::U32 => consume_type::>(input), - TypeDefPrimitive::U64 => consume_type::>(input), - TypeDefPrimitive::U128 => consume_type::>(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 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::>(input) - } - TypeDef::Primitive(TypeDefPrimitive::U16) => { - consume_type::>(input) - } - TypeDef::Primitive(TypeDefPrimitive::U32) => { - consume_type::>(input) - } - TypeDef::Primitive(TypeDefPrimitive::U64) => { - consume_type::>(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; - - /// Build a type registry that knows about the single type provided. - fn singleton_type_registry( - ) -> (TypeId, PortableRegistry) { - let m = scale_info::MetaType::new::(); - 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::(); - 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_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::(); - let res = decode_and_consume_type(id.id(), ®, 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(), ®, 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(), ®, 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::(); - let res = decode_and_consume_type(id.id(), ®, 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(), ®, dummy_cursor); - assert_matches!(res, Err(Codec(_))); - - let res = decode_and_consume_type(INVALID_TYPE_ID, ®, 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 { - 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 { - #[codec(compact)] - val: T, - } - decode_and_consume_type_consumes_all_bytes(CompactV4 { val: 0u8 }); - decode_and_consume_type_consumes_all_bytes(CompactV4 { val: 1u16 }); - } -} diff --git a/subxt/src/events/event_subscription.rs b/subxt/src/events/event_subscription.rs index 733291d84a..fcb87395b1 100644 --- a/subxt/src/events/event_subscription.rs +++ b/subxt/src/events/event_subscription.rs @@ -46,7 +46,6 @@ pub use super::{ EventDetails, EventFilter, Events, - EventsDecodingError, FilterEvents, RawEventDetails, }; diff --git a/subxt/src/events/events_type.rs b/subxt/src/events/events_type.rs index b15c3179bf..7ea2b700d4 100644 --- a/subxt/src/events/events_type.rs +++ b/subxt/src/events/events_type.rs @@ -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 { pub event: Evs, } +/// A Value which has been decoded from some raw bytes. +pub type DecodedValue = scale_value::Value; + /// 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, + /// Generic values representing each field of the event. + pub fields: Vec, } impl RawEventDetails { /// Attempt to decode this [`RawEventDetails`] into a specific event. pub fn as_event(&self) -> Result, 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( // 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( 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() -> Arc> { Arc::new(RwLock::new(test_utils::metadata::())) } + /// [`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, + } + + /// 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>, + 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), } // 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::( - metadata, + metadata.clone(), vec![event_record(Phase::ApplyExtrinsic(123), event)], ); - let event_details: Vec = - events.iter_raw().collect::>().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::( - 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 = - events.iter_raw().collect::>().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::( - 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::( - metadata, + metadata.clone(), vec![event_record(Phase::Finalization, Event::A(1))], ); @@ -863,26 +920,21 @@ mod tests { ); // Dynamically decode: - let event_details: Vec = - events.iter_raw().collect::>().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::( - metadata, + metadata.clone(), vec![event_record( Phase::Finalization, Event::A(CompactWrapper(1)), @@ -921,26 +973,21 @@ mod tests { ); // Dynamically decode: - let event_details: Vec = - events.iter_raw().collect::>().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::( - metadata, + metadata.clone(), vec![event_record(Phase::Finalization, Event::A(MyType::B))], ); @@ -980,25 +1027,20 @@ mod tests { ); // Dynamically decode: - let event_details: Vec = - events.iter_raw().collect::>().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()); } } diff --git a/subxt/src/events/mod.rs b/subxt/src/events/mod.rs index 62680608d7..1ca40be9de 100644 --- a/subxt/src/events/mod.rs +++ b/subxt/src/events/mod.rs @@ -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, diff --git a/subxt/src/transaction.rs b/subxt/src/transaction.rs index cda1b3ba3e..5cb96d9328 100644 --- a/subxt/src/transaction.rs +++ b/subxt/src/transaction.rs @@ -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();