Skip to content

Commit

Permalink
Add topics to EventDetails (#989)
Browse files Browse the repository at this point in the history
* Add topics to `EventDetails`

* Update comment

* Fmt

* Clippy
  • Loading branch information
ascjones authored May 31, 2023
1 parent 1ddae68 commit 011a4bd
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 22 deletions.
2 changes: 1 addition & 1 deletion subxt/src/blocks/extrinsic_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,7 @@ impl<T: Config> ExtrinsicEvents<T> {
///
/// 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<Item = Result<events::EventDetails, Error>> + '_ {
pub fn iter(&self) -> impl Iterator<Item = Result<events::EventDetails<T>, Error>> + '_ {
self.events.iter().filter(|ev| {
ev.as_ref()
.map(|ev| ev.phase() == events::Phase::ApplyExtrinsic(self.idx))
Expand Down
84 changes: 63 additions & 21 deletions subxt/src/events/events_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ impl<T: Config> Events<T> {
// use of it with our `FilterEvents` stuff.
pub fn iter(
&self,
) -> impl Iterator<Item = Result<EventDetails, Error>> + Send + Sync + 'static {
) -> impl Iterator<Item = Result<EventDetails<T>, 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();
Expand All @@ -133,12 +133,7 @@ impl<T: Config> Events<T> {
if event_bytes.len() <= pos || num_events == index {
None
} else {
match EventDetails::decode_from::<T>(
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();
Expand Down Expand Up @@ -189,7 +184,7 @@ impl<T: Config> Events<T> {

/// The event details.
#[derive(Debug, Clone)]
pub struct EventDetails {
pub struct EventDetails<T: Config> {
phase: Phase,
/// The index of the event in the list of events in a given block.
index: u32,
Expand All @@ -205,16 +200,17 @@ pub struct EventDetails {
// end of everything (fields + topics)
end_idx: usize,
metadata: Metadata,
topics: Vec<T::Hash>,
}

impl EventDetails {
impl<T: Config> EventDetails<T> {
// Attempt to dynamically decode a single event from our events input.
fn decode_from<T: Config>(
fn decode_from(
metadata: Metadata,
all_bytes: Arc<[u8]>,
start_idx: usize,
index: u32,
) -> Result<EventDetails, Error> {
) -> Result<EventDetails<T>, Error> {
let input = &mut &all_bytes[start_idx..];

let phase = Phase::decode(input)?;
Expand Down Expand Up @@ -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::<T::Hash>::decode(input)?;
// topics come after the event data in EventRecord.
let topics = Vec::<T::Hash>::decode(input)?;

// what bytes did we skip over in total, including topics.
let end_idx = all_bytes.len() - input.len();
Expand All @@ -269,6 +264,7 @@ impl EventDetails {
end_idx,
all_bytes,
metadata,
topics,
})
}

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -467,14 +468,21 @@ pub(crate) mod test_utils {
topics: Vec<<SubstrateConfig as Config>::Hash>,
}

impl<E: Encode> EventRecord<E> {
/// Create a new event record with the given phase, event, and topics.
pub fn new(phase: Phase, event: E, topics: Vec<<SubstrateConfig as Config>::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<E: Encode>(phase: Phase, event: E) -> EventRecord<E> {
EventRecord {
phase,
event: AllEvents::Test(event),
topics: vec![],
}
EventRecord::new(phase, event, vec![])
}

/// Build fake metadata consisting of a single pallet that knows
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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<SubstrateConfig>,
expected: TestRawEventDetails,
) {
let types = &metadata.types();
Expand Down Expand Up @@ -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<String>),
}

// Create fake metadata that knows about our single event, above:
let metadata = metadata::<Event>();

// 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::<Event>(
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());
}
}

0 comments on commit 011a4bd

Please # to comment.