Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add topics to EventDetails #989

Merged
merged 4 commits into from
May 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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());
}
}