From 011a4bd42f2b2a9d79dcd114e2ebbda1473a51f2 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 31 May 2023 13:10:06 +0100 Subject: [PATCH] Add topics to `EventDetails` (#989) * Add topics to `EventDetails` * Update comment * Fmt * Clippy --- subxt/src/blocks/extrinsic_types.rs | 2 +- subxt/src/events/events_type.rs | 84 +++++++++++++++++++++-------- 2 files changed, 64 insertions(+), 22 deletions(-) diff --git a/subxt/src/blocks/extrinsic_types.rs b/subxt/src/blocks/extrinsic_types.rs index 953272b4fb..4b3b5b8c10 100644 --- a/subxt/src/blocks/extrinsic_types.rs +++ b/subxt/src/blocks/extrinsic_types.rs @@ -560,7 +560,7 @@ impl ExtrinsicEvents { /// /// This works in the same way that [`events::Events::iter()`] does, with the /// exception that it filters out events not related to the submitted extrinsic. - pub fn iter(&self) -> impl Iterator> + '_ { + pub fn iter(&self) -> impl Iterator, Error>> + '_ { self.events.iter().filter(|ev| { ev.as_ref() .map(|ev| ev.phase() == events::Phase::ApplyExtrinsic(self.idx)) diff --git a/subxt/src/events/events_type.rs b/subxt/src/events/events_type.rs index af196f9984..75d38d0eb5 100644 --- a/subxt/src/events/events_type.rs +++ b/subxt/src/events/events_type.rs @@ -121,7 +121,7 @@ impl Events { // use of it with our `FilterEvents` stuff. pub fn iter( &self, - ) -> impl Iterator> + Send + Sync + 'static { + ) -> impl Iterator, Error>> + Send + Sync + 'static { // The event bytes ignoring the compact encoded length on the front: let event_bytes = self.event_bytes.clone(); let metadata = self.metadata.clone(); @@ -133,12 +133,7 @@ impl Events { if event_bytes.len() <= pos || num_events == index { None } else { - match EventDetails::decode_from::( - metadata.clone(), - event_bytes.clone(), - pos, - index, - ) { + match EventDetails::decode_from(metadata.clone(), event_bytes.clone(), pos, index) { Ok(event_details) => { // Skip over decoded bytes in next iteration: pos += event_details.bytes().len(); @@ -189,7 +184,7 @@ impl Events { /// The event details. #[derive(Debug, Clone)] -pub struct EventDetails { +pub struct EventDetails { phase: Phase, /// The index of the event in the list of events in a given block. index: u32, @@ -205,16 +200,17 @@ pub struct EventDetails { // end of everything (fields + topics) end_idx: usize, metadata: Metadata, + topics: Vec, } -impl EventDetails { +impl EventDetails { // Attempt to dynamically decode a single event from our events input. - fn decode_from( + fn decode_from( metadata: Metadata, all_bytes: Arc<[u8]>, start_idx: usize, index: u32, - ) -> Result { + ) -> Result, Error> { let input = &mut &all_bytes[start_idx..]; let phase = Phase::decode(input)?; @@ -252,9 +248,8 @@ impl EventDetails { // the end of the field bytes. let event_fields_end_idx = all_bytes.len() - input.len(); - // topics come after the event data in EventRecord. They aren't used for - // anything at the moment, so just decode and throw them away. - let _topics = Vec::::decode(input)?; + // topics come after the event data in EventRecord. + let topics = Vec::::decode(input)?; // what bytes did we skip over in total, including topics. let end_idx = all_bytes.len() - input.len(); @@ -269,6 +264,7 @@ impl EventDetails { end_idx, all_bytes, metadata, + topics, }) } @@ -385,6 +381,11 @@ impl EventDetails { &self.metadata, ) } + + /// Return the topics associated with this event. + pub fn topics(&self) -> &[T::Hash] { + &self.topics + } } /// Details for the given event plucked from the metadata. @@ -467,14 +468,21 @@ pub(crate) mod test_utils { topics: Vec<::Hash>, } + impl EventRecord { + /// Create a new event record with the given phase, event, and topics. + pub fn new(phase: Phase, event: E, topics: Vec<::Hash>) -> Self { + Self { + phase, + event: AllEvents::Test(event), + topics, + } + } + } + /// Build an EventRecord, which encoded events in the format expected /// to be handed back from storage queries to System.Events. pub fn event_record(phase: Phase, event: E) -> EventRecord { - EventRecord { - phase, - event: AllEvents::Test(event), - topics: vec![], - } + EventRecord::new(phase, event, vec![]) } /// Build fake metadata consisting of a single pallet that knows @@ -564,10 +572,12 @@ pub(crate) mod test_utils { #[cfg(test)] mod tests { use super::{ - test_utils::{event_record, events, events_raw, AllEvents}, + test_utils::{event_record, events, events_raw, AllEvents, EventRecord}, *, }; + use crate::SubstrateConfig; use codec::Encode; + use primitive_types::H256; use scale_info::TypeInfo; use scale_value::Value; @@ -596,7 +606,7 @@ mod tests { // Just for convenience, pass in the metadata type constructed // by the `metadata` function above to simplify caller code. metadata: &Metadata, - actual: EventDetails, + actual: EventDetails, expected: TestRawEventDetails, ) { let types = &metadata.types(); @@ -950,4 +960,36 @@ mod tests { ); assert!(event_details.next().is_none()); } + + #[test] + fn topics() { + #[derive(Clone, Debug, PartialEq, Decode, Encode, TypeInfo, scale_decode::DecodeAsType)] + enum Event { + A(u8, bool, Vec), + } + + // Create fake metadata that knows about our single event, above: + let metadata = metadata::(); + + // Encode our events in the format we expect back from a node, and + // construct an Events object to iterate them: + let event = Event::A(1, true, vec!["Hi".into()]); + let topics = vec![H256::from_low_u64_le(123), H256::from_low_u64_le(456)]; + let events = events::( + metadata, + vec![EventRecord::new( + Phase::ApplyExtrinsic(123), + event, + topics.clone(), + )], + ); + + let ev = events + .iter() + .next() + .expect("one event expected") + .expect("event should be extracted OK"); + + assert_eq!(topics, ev.topics()); + } }