mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 15:51:12 +00:00
401 lines
14 KiB
Rust
401 lines
14 KiB
Rust
mod decode_as_event;
|
|
|
|
use crate::config::{Config, HashFor};
|
|
use crate::error::EventsError;
|
|
use crate::{backend::BackendExt, client::OnlineClientAtBlockT};
|
|
use codec::{Compact, Decode, Encode};
|
|
use scale_decode::{DecodeAsFields, DecodeAsType};
|
|
use std::marker::PhantomData;
|
|
use std::sync::Arc;
|
|
use subxt_metadata::Metadata;
|
|
|
|
pub use decode_as_event::DecodeAsEvent;
|
|
|
|
/// The events at some block.
|
|
#[derive(Debug)]
|
|
pub struct Events<T> {
|
|
metadata: Arc<Metadata>,
|
|
// Note; raw event bytes are prefixed with a Compact<u32> containing
|
|
// the number of events to be decoded. The start_idx reflects that, so
|
|
// that we can skip over those bytes when decoding them
|
|
event_bytes: Vec<u8>,
|
|
start_idx: usize,
|
|
num_events: u32,
|
|
marker: core::marker::PhantomData<T>,
|
|
}
|
|
|
|
impl<T: Config> Events<T> {
|
|
pub(crate) async fn fetch(client: impl OnlineClientAtBlockT<T>) -> Result<Self, EventsError> {
|
|
// Fetch the bytes. Ensure things work if we get 0 bytes back.
|
|
let block_hash = client.block_hash();
|
|
let event_bytes = client
|
|
.backend()
|
|
.storage_fetch_value(system_events_key().to_vec(), block_hash)
|
|
.await
|
|
.map_err(EventsError::CannotFetchEventBytes)?
|
|
.unwrap_or_default();
|
|
|
|
// event_bytes is a SCALE encoded vector of events. So, pluck the
|
|
// compact encoded length from the front, leaving the remaining bytes
|
|
// for our iterating to decode.
|
|
//
|
|
// Note: if we get no bytes back, avoid an error reading vec length
|
|
// and default to 0 events.
|
|
let cursor = &mut &*event_bytes;
|
|
let num_events = <Compact<u32>>::decode(cursor).unwrap_or(Compact(0)).0;
|
|
|
|
// Start decoding after the compact encoded bytes.
|
|
let start_idx = event_bytes.len() - cursor.len();
|
|
|
|
Ok(Self {
|
|
metadata: client.metadata(),
|
|
event_bytes,
|
|
start_idx,
|
|
num_events,
|
|
marker: PhantomData,
|
|
})
|
|
}
|
|
|
|
/// The number of events.
|
|
pub fn len(&self) -> u32 {
|
|
self.num_events
|
|
}
|
|
|
|
/// Are there no events in this block?
|
|
// Note: mainly here to satisfy clippy.
|
|
pub fn is_empty(&self) -> bool {
|
|
self.num_events == 0
|
|
}
|
|
|
|
/// Return the bytes representing all of the events.
|
|
pub fn bytes(&self) -> &[u8] {
|
|
&self.event_bytes
|
|
}
|
|
|
|
/// Iterate over all of the events, using metadata to dynamically
|
|
/// decode them as we go, and returning the raw bytes and other associated
|
|
/// details. If an error occurs, all subsequent iterations return `None`.
|
|
// Dev note: The returned iterator is 'static + Send so that we can box it up and make
|
|
// use of it with our `FilterEvents` stuff.
|
|
pub fn iter(&'_ self) -> impl Iterator<Item = Result<Event<'_, T>, EventsError>> + Send + Sync {
|
|
// The event bytes ignoring the compact encoded length on the front:
|
|
let event_bytes = &*self.event_bytes;
|
|
let metadata = &*self.metadata;
|
|
let num_events = self.num_events;
|
|
|
|
let mut pos = self.start_idx;
|
|
let mut index = 0;
|
|
core::iter::from_fn(move || {
|
|
if event_bytes.len() <= pos || num_events == index {
|
|
None
|
|
} else {
|
|
match Event::decode_from(metadata, event_bytes, pos, index) {
|
|
Ok(event_details) => {
|
|
// Skip over decoded bytes in next iteration:
|
|
pos += event_details.bytes().len();
|
|
// Increment the index:
|
|
index += 1;
|
|
// Return the event details:
|
|
Some(Ok(event_details))
|
|
}
|
|
Err(e) => {
|
|
// By setting the position to the "end" of the event bytes,
|
|
// the cursor len will become 0 and the iterator will return `None`
|
|
// from now on:
|
|
pos = event_bytes.len();
|
|
Some(Err(e))
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
/// Iterate through the events, Decoding and returning any that match the given type.
|
|
///
|
|
/// This is a convenience function for calling [`Self::iter`] and then [`Event::decode_call_data_fields_as`]
|
|
/// on each event that we iterate over, filtering those that don't match.
|
|
pub fn find<E: DecodeAsEvent>(&self) -> impl Iterator<Item = Result<E, EventsError>> {
|
|
self.iter()
|
|
.filter_map(|e| e.ok())
|
|
.filter_map(|e| e.decode_fields_as::<E>())
|
|
}
|
|
|
|
/// Find an event matching the given type, returning true if it exists. This function does _not_
|
|
/// try to actually decode the event bytes into the given type.
|
|
pub fn has<E: DecodeAsEvent>(&self) -> bool {
|
|
self.iter().filter_map(|e| e.ok()).any(|e| e.is::<E>())
|
|
}
|
|
}
|
|
|
|
/// A phase of a block's execution.
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Decode, Encode)]
|
|
pub enum Phase {
|
|
/// Applying an extrinsic.
|
|
ApplyExtrinsic(u32),
|
|
/// Finalizing the block.
|
|
Finalization,
|
|
/// Initializing the block.
|
|
Initialization,
|
|
}
|
|
|
|
/// The event details.
|
|
#[derive(Debug, Clone)]
|
|
pub struct Event<'events, T: Config> {
|
|
pallet_name: &'events str,
|
|
event_name: &'events str,
|
|
metadata: &'events Metadata,
|
|
// all of the event bytes (not just this one).
|
|
all_bytes: &'events [u8],
|
|
// event phase.
|
|
phase: Phase,
|
|
/// The index of the event in the list of events in a given block.
|
|
index: u32,
|
|
// start of the bytes (phase, pallet/variant index and then fields and then topic to follow).
|
|
start_idx: usize,
|
|
// start of the event (ie pallet/variant index and then the fields and topic after).
|
|
event_start_idx: usize,
|
|
// start of the fields (ie after phase and pallet/variant index).
|
|
event_fields_start_idx: usize,
|
|
// end of the fields.
|
|
event_fields_end_idx: usize,
|
|
// end of everything (fields + topics)
|
|
end_idx: usize,
|
|
// event topics.
|
|
topics: Vec<HashFor<T>>,
|
|
}
|
|
|
|
impl<'events, T: Config> Event<'events, T> {
|
|
/// Attempt to dynamically decode a single event from our events input.
|
|
fn decode_from(
|
|
metadata: &'events Metadata,
|
|
all_bytes: &'events [u8],
|
|
start_idx: usize,
|
|
index: u32,
|
|
) -> Result<Event<'events, T>, EventsError> {
|
|
let input = &mut &all_bytes[start_idx..];
|
|
|
|
let phase = Phase::decode(input).map_err(EventsError::CannotDecodePhase)?;
|
|
|
|
let event_start_idx = all_bytes.len() - input.len();
|
|
|
|
let pallet_index = u8::decode(input).map_err(EventsError::CannotDecodePalletIndex)?;
|
|
let variant_index = u8::decode(input).map_err(EventsError::CannotDecodeVariantIndex)?;
|
|
|
|
let event_fields_start_idx = all_bytes.len() - input.len();
|
|
|
|
// Get metadata for the event:
|
|
let event_pallet = metadata
|
|
.pallet_by_event_index(pallet_index)
|
|
.ok_or_else(|| EventsError::CannotFindPalletWithIndex(pallet_index))?;
|
|
let event_variant = event_pallet
|
|
.event_variant_by_index(variant_index)
|
|
.ok_or_else(|| EventsError::CannotFindVariantWithIndex {
|
|
pallet_name: event_pallet.name().to_string(),
|
|
variant_index,
|
|
})?;
|
|
|
|
tracing::debug!(
|
|
"Decoding Event '{}::{}'",
|
|
event_pallet.name(),
|
|
&event_variant.name
|
|
);
|
|
|
|
// Skip over the bytes belonging to this event.
|
|
for field_metadata in &event_variant.fields {
|
|
// Skip over the bytes for this field:
|
|
scale_decode::visitor::decode_with_visitor(
|
|
input,
|
|
field_metadata.ty.id,
|
|
metadata.types(),
|
|
scale_decode::visitor::IgnoreVisitor::new(),
|
|
)
|
|
.map_err(|e| EventsError::CannotDecodeFieldInEvent {
|
|
pallet_name: event_pallet.name().to_string(),
|
|
event_name: event_variant.name.clone(),
|
|
field_name: field_metadata
|
|
.name
|
|
.clone()
|
|
.unwrap_or("<unknown>".to_string()),
|
|
reason: e,
|
|
})?;
|
|
}
|
|
|
|
// the end of the field bytes.
|
|
let event_fields_end_idx = all_bytes.len() - input.len();
|
|
|
|
// topics come after the event data in EventRecord.
|
|
let topics =
|
|
Vec::<HashFor<T>>::decode(input).map_err(EventsError::CannotDecodeEventTopics)?;
|
|
|
|
// what bytes did we skip over in total, including topics.
|
|
let end_idx = all_bytes.len() - input.len();
|
|
|
|
Ok(Event {
|
|
pallet_name: event_pallet.name(),
|
|
event_name: &event_variant.name,
|
|
phase,
|
|
index,
|
|
start_idx,
|
|
event_start_idx,
|
|
event_fields_start_idx,
|
|
event_fields_end_idx,
|
|
end_idx,
|
|
all_bytes,
|
|
metadata,
|
|
topics,
|
|
})
|
|
}
|
|
|
|
/// When was the event produced?
|
|
pub fn phase(&self) -> Phase {
|
|
self.phase
|
|
}
|
|
|
|
/// What index is this event in the stored events for this block.
|
|
pub fn index(&self) -> u32 {
|
|
self.index
|
|
}
|
|
|
|
/// The index of the pallet that the event originated from.
|
|
pub fn pallet_index(&self) -> u8 {
|
|
// Note: never panics; we expect these bytes to exist
|
|
// in order that the EventDetails could be created.
|
|
self.all_bytes[self.event_fields_start_idx - 2]
|
|
}
|
|
|
|
/// The index of the event variant that the event originated from.
|
|
pub fn event_index(&self) -> u8 {
|
|
// Note: never panics; we expect these bytes to exist
|
|
// in order that the EventDetails could be created.
|
|
self.all_bytes[self.event_fields_start_idx - 1]
|
|
}
|
|
|
|
/// The name of the pallet from whence the Event originated.
|
|
pub fn pallet_name(&self) -> &str {
|
|
self.pallet_name
|
|
}
|
|
|
|
/// The name of the event (ie the name of the variant that it corresponds to).
|
|
pub fn event_name(&self) -> &str {
|
|
self.event_name
|
|
}
|
|
|
|
/// Return _all_ of the bytes representing this event, which include, in order:
|
|
/// - The phase.
|
|
/// - Pallet and event index.
|
|
/// - Event fields.
|
|
/// - Event Topics.
|
|
pub fn bytes(&self) -> &[u8] {
|
|
&self.all_bytes[self.start_idx..self.end_idx]
|
|
}
|
|
|
|
/// Return the bytes representing the fields stored in this event.
|
|
pub fn field_bytes(&self) -> &[u8] {
|
|
&self.all_bytes[self.event_fields_start_idx..self.event_fields_end_idx]
|
|
}
|
|
|
|
/// Return the topics associated with this event.
|
|
pub fn topics(&self) -> &[HashFor<T>] {
|
|
&self.topics
|
|
}
|
|
|
|
/// Return true if this [`Event`] matches the provided type.
|
|
pub fn is<E: DecodeAsEvent>(&self) -> bool {
|
|
E::is_event(self.pallet_name(), self.event_name())
|
|
}
|
|
|
|
/// Attempt to decode this [`Event`] into an outer event enum type (which includes
|
|
/// the pallet and event enum variants as well as the event fields). One compatible
|
|
/// type for this is exposed via static codegen as a root level `Event` type.
|
|
pub fn decode_as<E: DecodeAsType>(&self) -> Result<E, EventsError> {
|
|
let bytes = &self.all_bytes[self.event_start_idx..self.event_fields_end_idx];
|
|
|
|
let decoded = E::decode_as_type(
|
|
&mut &bytes[..],
|
|
self.metadata.outer_enums().event_enum_ty(),
|
|
self.metadata.types(),
|
|
)
|
|
.map_err(|e| {
|
|
let md = self.event_metadata();
|
|
EventsError::CannotDecodeEventEnum {
|
|
pallet_name: md.pallet.name().to_string(),
|
|
event_name: md.variant.name.clone(),
|
|
reason: e,
|
|
}
|
|
})?;
|
|
|
|
Ok(decoded)
|
|
}
|
|
|
|
/// Decode the event call data fields into some type which implements [`DecodeAsEvent`].
|
|
///
|
|
/// Event types generated via the [`crate::subxt!`] macro implement this.
|
|
pub fn decode_fields_as<E: DecodeAsEvent>(&self) -> Option<Result<E, EventsError>> {
|
|
if self.is::<E>() {
|
|
Some(self.decode_fields_unchecked_as::<E>())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
/// Decode the event call data fields into some type which implements [`DecodeAsFields`].
|
|
///
|
|
/// This ignores the pallet and event name information, so you should check those via [`Self::pallet_name()`]
|
|
/// and [`Self::event_name()`] to confirm that this event is the one you are intending to decode.
|
|
///
|
|
/// Prefer to use [`Self::decode_call_data_fields_as`] where possible.
|
|
pub fn decode_fields_unchecked_as<E: DecodeAsFields>(&self) -> Result<E, EventsError> {
|
|
let bytes = &mut self.field_bytes();
|
|
let event_metadata = self.event_metadata();
|
|
|
|
let mut fields = event_metadata
|
|
.variant
|
|
.fields
|
|
.iter()
|
|
.map(|f| scale_decode::Field::new(f.ty.id, f.name.as_deref()));
|
|
|
|
let decoded =
|
|
E::decode_as_fields(bytes, &mut fields, self.metadata.types()).map_err(|e| {
|
|
EventsError::CannotDecodeEventFields {
|
|
pallet_name: event_metadata.pallet.name().to_string(),
|
|
event_name: event_metadata.variant.name.clone(),
|
|
reason: e,
|
|
}
|
|
})?;
|
|
|
|
Ok(decoded)
|
|
}
|
|
|
|
/// Fetch details from the metadata for this event. This is used for decoding but
|
|
/// we try to avoid using it elsewhere.
|
|
fn event_metadata(&self) -> EventMetadataDetails<'_> {
|
|
let pallet = self
|
|
.metadata
|
|
.pallet_by_event_index(self.pallet_index())
|
|
.expect("event pallet to be found; we did this already during decoding");
|
|
let variant = pallet
|
|
.event_variant_by_index(self.event_index())
|
|
.expect("event variant to be found; we did this already during decoding");
|
|
|
|
EventMetadataDetails { pallet, variant }
|
|
}
|
|
}
|
|
|
|
// The storage key needed to access events.
|
|
fn system_events_key() -> [u8; 32] {
|
|
let a = sp_crypto_hashing::twox_128(b"System");
|
|
let b = sp_crypto_hashing::twox_128(b"Events");
|
|
let mut res = [0; 32];
|
|
res[0..16].clone_from_slice(&a);
|
|
res[16..32].clone_from_slice(&b);
|
|
res
|
|
}
|
|
|
|
/// Details for the given event plucked from the metadata.
|
|
struct EventMetadataDetails<'a> {
|
|
/// Metadata for the pallet that the event belongs to.
|
|
pub pallet: subxt_metadata::PalletMetadata<'a>,
|
|
/// Metadata for the variant which describes the pallet events.
|
|
pub variant: &'a scale_info::Variant<scale_info::form::PortableForm>,
|
|
}
|