mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-14 13:21:10 +00:00
Subscribe to Runtime upgrades for proper extrinsic construction (#513)
* subxt: Add subscription to runtime upgrades Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt: Synchronize and expose inner `RuntimeVersion` of the `Client` Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * examples: Add runtime update example Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt: Expose `RuntimeVersion` as locked Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt: Expose `Metadata` as locked Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt/storage: Use locked metadata Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt: Use parking lot RwLock variant for locked metadata Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt: Utilize locked metadata variant Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt/transaction: Use locked metadata variant Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update subxt to use locked version of the Metadata Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt: Add runtime update client wrapper Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * examples: Modify runtime update example Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Fix clippy Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Fix cargo check Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * codegen: Keep consistency with cargo check fix Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt: Remove unnecessary Arc Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt: Remove MetadataInner and use parking_lot::RwLock Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update polkadot.rs Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update polkadot.rs generation comment Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt: Switch to async::Mutex Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt: Block executor while decoding dynamic events Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * codegen: Use async API to handle async locking Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Remove unused dependencies Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update examples and integration-tests Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Fix test deadlock Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Revert back to sync lock Revert "Fix test deadlock" This reverts commit 4a79933df23e81573611cb14be6c5b5b2b56d4df. Revert "Update examples and integration-tests" This reverts commit 5423f6eb4131582909d5a4ca70adff75e27cdd0e. Revert "Remove unused dependencies" This reverts commit e8ecbabb5b01a7ba4ae83b8bde36295a3f64daf7. Revert "codegen: Use async API to handle async locking" This reverts commit ced4646541c431adcb973369b1061b7b3cbfaae1. Revert "subxt: Block executor while decoding dynamic events" This reverts commit 8b3ba4a5eabb29f77ac1ca671450956fc479a33d. Revert "subxt: Switch to async::Mutex" This reverts commit f5bde9b79394a6bf61b6b9daefc36ceaa84b82be. * subxt: Perform RuntimeVersion update before fetching metadata Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Reintroduce MetadataInner * Use parking lot instead of std::RwLock Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt: Reduce lock metadata time when decoding events Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * codegen: Update `validate_metdata` locking pattern Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt/examples: Update polkadot download link Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * tests: Wrap metadata in a helper function Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update examples/examples/subscribe_runtime_updates.rs Co-authored-by: James Wilson <james@jsdw.me> * subxt/updates: Update runtime if version is different Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> Co-authored-by: James Wilson <james@jsdw.me>
This commit is contained in:
@@ -165,7 +165,7 @@ pub struct EventSubscription<'a, Sub, T: Config, Evs: 'static> {
|
||||
#[derivative(Debug = "ignore")]
|
||||
at: Option<
|
||||
std::pin::Pin<
|
||||
Box<dyn Future<Output = Result<Events<'a, T, Evs>, BasicError>> + Send + 'a>,
|
||||
Box<dyn Future<Output = Result<Events<T, Evs>, BasicError>> + Send + 'a>,
|
||||
>,
|
||||
>,
|
||||
_event_type: std::marker::PhantomData<Evs>,
|
||||
@@ -222,7 +222,7 @@ where
|
||||
Sub: Stream<Item = Result<T::Header, E>> + Unpin + 'a,
|
||||
E: Into<BasicError>,
|
||||
{
|
||||
type Item = Result<Events<'a, T, Evs>, BasicError>;
|
||||
type Item = Result<Events<T, Evs>, BasicError>;
|
||||
|
||||
fn poll_next(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
|
||||
@@ -32,11 +32,13 @@ use codec::{
|
||||
Input,
|
||||
};
|
||||
use derivative::Derivative;
|
||||
use parking_lot::RwLock;
|
||||
use sp_core::{
|
||||
storage::StorageKey,
|
||||
twox_128,
|
||||
Bytes,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Obtain events at some block hash. The generic parameter is what we
|
||||
/// will attempt to decode each event into if using [`Events::iter()`],
|
||||
@@ -50,7 +52,7 @@ use sp_core::{
|
||||
pub async fn at<T: Config, Evs: Decode>(
|
||||
client: &'_ Client<T>,
|
||||
block_hash: T::Hash,
|
||||
) -> Result<Events<'_, T, Evs>, BasicError> {
|
||||
) -> Result<Events<T, Evs>, BasicError> {
|
||||
let mut event_bytes = client
|
||||
.rpc()
|
||||
.storage(&system_events_key(), Some(block_hash))
|
||||
@@ -90,8 +92,8 @@ fn system_events_key() -> StorageKey {
|
||||
/// information needed to decode and iterate over them.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Debug(bound = ""))]
|
||||
pub struct Events<'a, T: Config, Evs> {
|
||||
metadata: &'a Metadata,
|
||||
pub struct Events<T: Config, Evs> {
|
||||
metadata: Arc<RwLock<Metadata>>,
|
||||
block_hash: T::Hash,
|
||||
// Note; raw event bytes are prefixed with a Compact<u32> containing
|
||||
// the number of events to be decoded. We should have stripped that off
|
||||
@@ -101,7 +103,7 @@ pub struct Events<'a, T: Config, Evs> {
|
||||
_event_type: std::marker::PhantomData<Evs>,
|
||||
}
|
||||
|
||||
impl<'a, T: Config, Evs: Decode> Events<'a, T, Evs> {
|
||||
impl<'a, T: Config, Evs: Decode> Events<T, Evs> {
|
||||
/// The number of events.
|
||||
pub fn len(&self) -> u32 {
|
||||
self.num_events
|
||||
@@ -183,6 +185,11 @@ impl<'a, T: Config, Evs: Decode> Events<'a, T, Evs> {
|
||||
) -> impl Iterator<Item = Result<RawEventDetails, BasicError>> + '_ {
|
||||
let event_bytes = &self.event_bytes;
|
||||
|
||||
let metadata = {
|
||||
let metadata = self.metadata.read();
|
||||
metadata.clone()
|
||||
};
|
||||
|
||||
let mut pos = 0;
|
||||
let mut index = 0;
|
||||
std::iter::from_fn(move || {
|
||||
@@ -192,7 +199,7 @@ impl<'a, T: Config, Evs: Decode> Events<'a, T, Evs> {
|
||||
if start_len == 0 || self.num_events == index {
|
||||
None
|
||||
} else {
|
||||
match decode_raw_event_details::<T>(self.metadata, index, cursor) {
|
||||
match decode_raw_event_details::<T>(&metadata, index, cursor) {
|
||||
Ok(raw_event) => {
|
||||
// Skip over decoded bytes in next iteration:
|
||||
pos += start_len - cursor.len();
|
||||
@@ -228,6 +235,11 @@ impl<'a, T: Config, Evs: Decode> Events<'a, T, Evs> {
|
||||
) -> impl Iterator<Item = Result<RawEventDetails, BasicError>> + 'a {
|
||||
let mut pos = 0;
|
||||
let mut index = 0;
|
||||
let metadata = {
|
||||
let metadata = self.metadata.read();
|
||||
metadata.clone()
|
||||
};
|
||||
|
||||
std::iter::from_fn(move || {
|
||||
let cursor = &mut &self.event_bytes[pos..];
|
||||
let start_len = cursor.len();
|
||||
@@ -235,7 +247,7 @@ impl<'a, T: Config, Evs: Decode> Events<'a, T, Evs> {
|
||||
if start_len == 0 || self.num_events == index {
|
||||
None
|
||||
} else {
|
||||
match decode_raw_event_details::<T>(self.metadata, index, cursor) {
|
||||
match decode_raw_event_details::<T>(&metadata, index, cursor) {
|
||||
Ok(raw_event) => {
|
||||
// Skip over decoded bytes in next iteration:
|
||||
pos += start_len - cursor.len();
|
||||
@@ -468,9 +480,9 @@ pub(crate) mod test_utils {
|
||||
/// Build an `Events` object for test purposes, based on the details provided,
|
||||
/// and with a default block hash.
|
||||
pub fn events<E: Decode + Encode>(
|
||||
metadata: &'_ Metadata,
|
||||
metadata: Arc<RwLock<Metadata>>,
|
||||
event_records: Vec<EventRecord<E>>,
|
||||
) -> Events<'_, DefaultConfig, AllEvents<E>> {
|
||||
) -> Events<DefaultConfig, AllEvents<E>> {
|
||||
let num_events = event_records.len() as u32;
|
||||
let mut event_bytes = Vec::new();
|
||||
for ev in event_records {
|
||||
@@ -482,10 +494,10 @@ pub(crate) mod test_utils {
|
||||
/// Much like [`events`], but takes pre-encoded events and event count, so that we can
|
||||
/// mess with the bytes in tests if we need to.
|
||||
pub fn events_raw<E: Decode + Encode>(
|
||||
metadata: &'_ Metadata,
|
||||
metadata: Arc<RwLock<Metadata>>,
|
||||
event_bytes: Vec<u8>,
|
||||
num_events: u32,
|
||||
) -> Events<'_, DefaultConfig, AllEvents<E>> {
|
||||
) -> Events<DefaultConfig, AllEvents<E>> {
|
||||
Events {
|
||||
block_hash: <DefaultConfig as Config>::Hash::default(),
|
||||
event_bytes,
|
||||
@@ -503,7 +515,6 @@ mod tests {
|
||||
event_record,
|
||||
events,
|
||||
events_raw,
|
||||
metadata,
|
||||
AllEvents,
|
||||
},
|
||||
*,
|
||||
@@ -512,6 +523,11 @@ mod tests {
|
||||
use codec::Encode;
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
/// Build a fake wrapped metadata.
|
||||
fn metadata<E: TypeInfo + 'static>() -> Arc<RwLock<Metadata>> {
|
||||
Arc::new(RwLock::new(test_utils::metadata::<E>()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn statically_decode_single_event() {
|
||||
#[derive(Clone, Debug, PartialEq, Decode, Encode, TypeInfo)]
|
||||
@@ -521,11 +537,10 @@ mod tests {
|
||||
|
||||
// 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
|
||||
// construst an Events object to iterate them:
|
||||
// construct an Events object to iterate them:
|
||||
let events = events::<Event>(
|
||||
&metadata,
|
||||
metadata,
|
||||
vec![event_record(Phase::Finalization, Event::A(1))],
|
||||
);
|
||||
|
||||
@@ -555,7 +570,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::<Event>(
|
||||
&metadata,
|
||||
metadata,
|
||||
vec![
|
||||
event_record(Phase::Initialization, Event::A(1)),
|
||||
event_record(Phase::ApplyExtrinsic(123), Event::B(true)),
|
||||
@@ -610,7 +625,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_raw::<Event>(
|
||||
&metadata,
|
||||
metadata,
|
||||
event_bytes,
|
||||
3, // 2 "good" events, and then it'll hit the naff bytes.
|
||||
);
|
||||
@@ -654,7 +669,7 @@ mod tests {
|
||||
// construst an Events object to iterate them:
|
||||
let event = Event::A(1);
|
||||
let events = events::<Event>(
|
||||
&metadata,
|
||||
metadata,
|
||||
vec![event_record(Phase::ApplyExtrinsic(123), event)],
|
||||
);
|
||||
|
||||
@@ -699,7 +714,7 @@ mod tests {
|
||||
let event3 = Event::A(234);
|
||||
|
||||
let events = events::<Event>(
|
||||
&metadata,
|
||||
metadata,
|
||||
vec![
|
||||
event_record(Phase::Initialization, event1),
|
||||
event_record(Phase::ApplyExtrinsic(123), event2),
|
||||
@@ -773,7 +788,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_raw::<Event>(
|
||||
&metadata,
|
||||
metadata,
|
||||
event_bytes,
|
||||
3, // 2 "good" events, and then it'll hit the naff bytes.
|
||||
);
|
||||
@@ -831,7 +846,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::<Event>(
|
||||
&metadata,
|
||||
metadata,
|
||||
vec![event_record(Phase::Finalization, Event::A(1))],
|
||||
);
|
||||
|
||||
@@ -886,7 +901,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::<Event>(
|
||||
&metadata,
|
||||
metadata,
|
||||
vec![event_record(
|
||||
Phase::Finalization,
|
||||
Event::A(CompactWrapper(1)),
|
||||
@@ -948,7 +963,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::<Event>(
|
||||
&metadata,
|
||||
metadata,
|
||||
vec![event_record(Phase::Finalization, Event::A(MyType::B))],
|
||||
);
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ impl<'a, Sub: 'a, T: Config, Filter: EventFilter> FilterEvents<'a, Sub, T, Filte
|
||||
|
||||
impl<'a, Sub, T, Evs, Filter> Stream for FilterEvents<'a, Sub, T, Filter>
|
||||
where
|
||||
Sub: Stream<Item = Result<Events<'a, T, Evs>, BasicError>> + Unpin + 'a,
|
||||
Sub: Stream<Item = Result<Events<T, Evs>, BasicError>> + Unpin + 'a,
|
||||
T: Config,
|
||||
Evs: Decode + 'static,
|
||||
Filter: EventFilter,
|
||||
@@ -125,7 +125,7 @@ pub trait EventFilter: private::Sealed {
|
||||
type ReturnType;
|
||||
/// Filter the events based on the type implementing this trait.
|
||||
fn filter<'a, T: Config, Evs: Decode + 'static>(
|
||||
events: Events<'a, T, Evs>,
|
||||
events: Events<T, Evs>,
|
||||
) -> Box<
|
||||
dyn Iterator<
|
||||
Item = Result<
|
||||
@@ -150,7 +150,7 @@ impl<Ev: Event> private::Sealed for (Ev,) {}
|
||||
impl<Ev: Event> EventFilter for (Ev,) {
|
||||
type ReturnType = Ev;
|
||||
fn filter<'a, T: Config, Evs: Decode + 'static>(
|
||||
events: Events<'a, T, Evs>,
|
||||
events: Events<T, Evs>,
|
||||
) -> Box<
|
||||
dyn Iterator<Item = Result<FilteredEventDetails<T::Hash, Ev>, BasicError>>
|
||||
+ Send
|
||||
@@ -192,7 +192,7 @@ macro_rules! impl_event_filter {
|
||||
impl <$($ty: Event),+> EventFilter for ( $($ty,)+ ) {
|
||||
type ReturnType = ( $(Option<$ty>,)+ );
|
||||
fn filter<'a, T: Config, Evs: Decode + 'static>(
|
||||
events: Events<'a, T, Evs>
|
||||
events: Events<T, Evs>
|
||||
) -> Box<dyn Iterator<Item=Result<FilteredEventDetails<T::Hash,Self::ReturnType>, BasicError>> + Send + 'a> {
|
||||
let block_hash = events.block_hash();
|
||||
let mut iter = events.into_iter_raw();
|
||||
@@ -259,7 +259,9 @@ mod test {
|
||||
Stream,
|
||||
StreamExt,
|
||||
};
|
||||
use parking_lot::RwLock;
|
||||
use scale_info::TypeInfo;
|
||||
use std::sync::Arc;
|
||||
|
||||
// Some pretend events in a pallet
|
||||
#[derive(Clone, Debug, PartialEq, Decode, Encode, TypeInfo)]
|
||||
@@ -295,13 +297,12 @@ mod test {
|
||||
|
||||
// A stream of fake events for us to try filtering on.
|
||||
fn events_stream(
|
||||
metadata: &'_ Metadata,
|
||||
) -> impl Stream<
|
||||
Item = Result<Events<'_, DefaultConfig, AllEvents<PalletEvents>>, BasicError>,
|
||||
> {
|
||||
metadata: Arc<RwLock<Metadata>>,
|
||||
) -> impl Stream<Item = Result<Events<DefaultConfig, AllEvents<PalletEvents>>, BasicError>>
|
||||
{
|
||||
stream::iter(vec![
|
||||
events::<PalletEvents>(
|
||||
metadata,
|
||||
metadata.clone(),
|
||||
vec![
|
||||
event_record(Phase::Initialization, PalletEvents::A(EventA(1))),
|
||||
event_record(Phase::ApplyExtrinsic(0), PalletEvents::B(EventB(true))),
|
||||
@@ -309,7 +310,7 @@ mod test {
|
||||
],
|
||||
),
|
||||
events::<PalletEvents>(
|
||||
metadata,
|
||||
metadata.clone(),
|
||||
vec![event_record(
|
||||
Phase::ApplyExtrinsic(1),
|
||||
PalletEvents::B(EventB(false)),
|
||||
@@ -328,11 +329,11 @@ mod test {
|
||||
|
||||
#[tokio::test]
|
||||
async fn filter_one_event_from_stream() {
|
||||
let metadata = metadata::<PalletEvents>();
|
||||
let metadata = Arc::new(RwLock::new(metadata::<PalletEvents>()));
|
||||
|
||||
// Filter out fake event stream to select events matching `EventA` only.
|
||||
let actual: Vec<_> =
|
||||
FilterEvents::<_, DefaultConfig, (EventA,)>::new(events_stream(&metadata))
|
||||
FilterEvents::<_, DefaultConfig, (EventA,)>::new(events_stream(metadata))
|
||||
.map(|e| e.unwrap())
|
||||
.collect()
|
||||
.await;
|
||||
@@ -360,11 +361,11 @@ mod test {
|
||||
|
||||
#[tokio::test]
|
||||
async fn filter_some_events_from_stream() {
|
||||
let metadata = metadata::<PalletEvents>();
|
||||
let metadata = Arc::new(RwLock::new(metadata::<PalletEvents>()));
|
||||
|
||||
// Filter out fake event stream to select events matching `EventA` or `EventB`.
|
||||
let actual: Vec<_> = FilterEvents::<_, DefaultConfig, (EventA, EventB)>::new(
|
||||
events_stream(&metadata),
|
||||
events_stream(metadata),
|
||||
)
|
||||
.map(|e| e.unwrap())
|
||||
.collect()
|
||||
@@ -408,11 +409,11 @@ mod test {
|
||||
|
||||
#[tokio::test]
|
||||
async fn filter_no_events_from_stream() {
|
||||
let metadata = metadata::<PalletEvents>();
|
||||
let metadata = Arc::new(RwLock::new(metadata::<PalletEvents>()));
|
||||
|
||||
// Filter out fake event stream to select events matching `EventC` (none exist).
|
||||
let actual: Vec<_> =
|
||||
FilterEvents::<_, DefaultConfig, (EventC,)>::new(events_stream(&metadata))
|
||||
FilterEvents::<_, DefaultConfig, (EventC,)>::new(events_stream(metadata))
|
||||
.map(|e| e.unwrap())
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
Reference in New Issue
Block a user