Runtime agnostic Events (#20)

* Introduce OpaqueEvent

* Look up event by module and variant

* Index events by module

* Get events by module

* Dynamically decode events

* Decode System events and EventRecord topics

* Use type sizes to decode raw events

* Remove unused imports

* rustfmt

* Unify error types, fix some compiler errors

* Make dynamic event decoding work

- fix compilation errors
- skip modules with no events when indexing
- preallocate vec for raw event data

* Remove printlns, replace where required with log

* Remove unused import

* Check missing type sizes

* Ignore unknown event arg type sizes

* Decode concrete System events, assumes every Runtime has the module

* Reorganise usings

* pub use some types

* Code docs

* Export Error

* Error Display impls

* Format code
This commit is contained in:
Andrew Jones
2019-09-26 17:48:25 +01:00
committed by GitHub
parent f9ae14bdb9
commit ee6db12917
8 changed files with 595 additions and 99 deletions
+1
View File
@@ -35,6 +35,7 @@ url = "1.7"
env_logger = "0.6"
node-runtime = { git = "https://github.com/paritytech/substrate/", package = "node-runtime" }
srml-balances = { git = "https://github.com/paritytech/substrate/", package = "srml-balances" }
srml-contracts = { git = "https://github.com/paritytech/substrate/", package = "srml-contracts" }
substrate-keyring = { git = "https://github.com/paritytech/substrate/", package = "substrate-keyring" }
tokio = "0.1"
wabt = "0.9.0"
+8 -2
View File
@@ -14,22 +14,28 @@
// You should have received a copy of the GNU General Public License
// along with substrate-subxt. If not, see <http://www.gnu.org/licenses/>.
use crate::metadata::MetadataError;
use crate::{
events::EventsError,
metadata::MetadataError,
};
use jsonrpc_core_client::RpcError;
use parity_scale_codec::Error as CodecError;
use std::io::Error as IoError;
use substrate_primitives::crypto::SecretStringError;
/// Error enum.
#[derive(Debug, derive_more::From)]
#[derive(Debug, derive_more::From, derive_more::Display)]
pub enum Error {
/// Codec error.
Codec(CodecError),
/// Events error.
Events(EventsError),
/// Io error.
Io(IoError),
/// Rpc error.
Rpc(RpcError),
/// Secret string error.
#[display(fmt = "Secret String Error")]
SecretString(SecretStringError),
/// Metadata error.
Metadata(MetadataError),
+236
View File
@@ -0,0 +1,236 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of substrate-subxt.
//
// subxt is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// subxt is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with substrate-subxt. If not, see <http://www.gnu.org/licenses/>.
use crate::{
metadata::{
EventArg,
Metadata,
MetadataError,
},
srml::balances::Balances,
System,
SystemEvent,
};
use log;
use parity_scale_codec::{
Codec,
Compact,
Decode,
Encode,
Error as CodecError,
Input,
Output,
};
use srml_system::Phase;
use std::{
collections::{
HashMap,
HashSet,
},
convert::TryFrom,
marker::{
PhantomData,
Send,
},
};
/// Top level Event that can be produced by a substrate runtime
#[derive(Debug)]
pub enum RuntimeEvent {
System(SystemEvent),
Raw(RawEvent),
}
/// Raw bytes for an Event
#[derive(Debug)]
pub struct RawEvent {
/// The name of the module from whence the Event originated
pub module: String,
/// The name of the Event
pub variant: String,
/// The raw Event data
pub data: Vec<u8>,
}
#[derive(Debug, derive_more::From, derive_more::Display)]
pub enum EventsError {
CodecError(CodecError),
Metadata(MetadataError),
#[display(fmt = "Type Sizes Missing: {:?}", _0)]
TypeSizesMissing(Vec<String>),
TypeSizeUnavailable(String),
}
pub struct EventsDecoder<T> {
metadata: Metadata, // todo: [AJ] borrow?
type_sizes: HashMap<String, usize>,
marker: PhantomData<fn() -> T>,
}
impl<T: System + Balances + 'static> TryFrom<Metadata> for EventsDecoder<T> {
type Error = EventsError;
fn try_from(metadata: Metadata) -> Result<Self, Self::Error> {
let mut decoder = Self {
metadata,
type_sizes: HashMap::new(),
marker: PhantomData,
};
// register default event arg type sizes for dynamic decoding of events
decoder.register_type_size::<bool>("bool")?;
decoder.register_type_size::<u32>("ReferendumIndex")?;
decoder.register_type_size::<[u8; 16]>("Kind")?;
decoder.register_type_size::<[u8; 32]>("AuthorityId")?;
decoder.register_type_size::<u8>("u8")?;
decoder.register_type_size::<u32>("u32")?;
decoder.register_type_size::<u32>("AccountIndex")?;
decoder.register_type_size::<u32>("SessionIndex")?;
decoder.register_type_size::<u32>("PropIndex")?;
decoder.register_type_size::<u32>("ProposalIndex")?;
decoder.register_type_size::<u32>("AuthorityIndex")?;
decoder.register_type_size::<u64>("AuthorityWeight")?;
decoder.register_type_size::<u32>("MemberCount")?;
decoder.register_type_size::<T::AccountId>("AccountId")?;
decoder.register_type_size::<T::BlockNumber>("BlockNumber")?;
decoder.register_type_size::<T::Hash>("Hash")?;
decoder.register_type_size::<<T as Balances>::Balance>("Balance")?;
// VoteThreshold enum index
decoder.register_type_size::<u8>("VoteThreshold")?;
// Ignore these unregistered types, which are not fixed size primitives
decoder.check_missing_type_sizes(vec![
"DispatchError",
"OpaqueTimeSlot",
"rstd::marker::PhantomData<(AccountId, Event)>",
])?;
Ok(decoder)
}
}
impl<T: System + Balances + 'static> EventsDecoder<T> {
pub fn register_type_size<U>(&mut self, name: &str) -> Result<usize, EventsError>
where
U: Default + Codec + Send + 'static,
{
let size = U::default().encode().len();
if size > 0 {
self.type_sizes.insert(name.to_string(), size);
Ok(size)
} else {
Err(EventsError::TypeSizeUnavailable(name.to_owned()))
}
}
fn check_missing_type_sizes<I: IntoIterator<Item = &'static str>>(
&self,
ignore: I,
) -> Result<(), Vec<String>> {
let mut missing = HashSet::new();
let mut ignore_set = HashSet::new();
ignore_set.extend(ignore);
for module in self.metadata.modules() {
for event in module.events() {
for arg in event.arguments() {
for primitive in arg.primitives() {
if !self.type_sizes.contains_key(&primitive)
&& !ignore_set.contains(primitive.as_str())
{
missing.insert(primitive);
}
}
}
}
}
if missing.is_empty() {
Ok(())
} else {
Err(missing.into_iter().collect())
}
}
fn decode_raw_bytes<I: Input, W: Output>(
&self,
args: &[EventArg],
input: &mut I,
output: &mut W,
) -> Result<(), EventsError> {
for arg in args {
match arg {
EventArg::Vec(arg) => {
let len = <Compact<u32>>::decode(input)?;
len.encode_to(output);
for _ in 0..len.0 {
self.decode_raw_bytes(&[*arg.clone()], input, output)?
}
}
EventArg::Tuple(args) => self.decode_raw_bytes(args, input, output)?,
EventArg::Primitive(name) => {
if let Some(size) = self.type_sizes.get(name) {
let mut buf = vec![0; *size];
input.read(&mut buf)?;
buf.encode_to(output);
} else {
return Err(EventsError::TypeSizeUnavailable(name.to_owned()))
}
}
}
}
Ok(())
}
pub fn decode_events(
&self,
input: &mut &[u8],
) -> Result<Vec<(Phase, RuntimeEvent)>, EventsError> {
let compact_len = <Compact<u32>>::decode(input)?;
let len = compact_len.0 as usize;
let mut r = Vec::new();
for _ in 0..len {
// decode EventRecord
let phase = Phase::decode(input)?;
let module_variant = input.read_byte()? as u8;
let module_name = self.metadata.module_name(module_variant)?;
let event = if module_name == "System" {
let system_event = SystemEvent::decode(input)?;
RuntimeEvent::System(system_event)
} else {
let event_variant = input.read_byte()? as u8;
let module = self.metadata.module(&module_name)?;
let event_metadata = module.event(event_variant)?;
log::debug!("decoding event '{}::{}'", module_name, event_metadata.name);
let mut event_data = Vec::<u8>::new();
self.decode_raw_bytes(
&event_metadata.arguments(),
input,
&mut event_data,
)?;
RuntimeEvent::Raw(RawEvent {
module: module_name.clone(),
variant: event_metadata.name.clone(),
data: event_data,
})
};
// topics come after the event data in EventRecord
let _topics = Vec::<T::Hash>::decode(input)?;
r.push((phase, event));
}
Ok(r)
}
}
+65 -32
View File
@@ -37,8 +37,11 @@ use runtime_primitives::{
generic::UncheckedExtrinsic,
traits::StaticLookup,
};
use std::marker::PhantomData;
use sr_version::RuntimeVersion;
use std::{
convert::TryFrom,
marker::PhantomData,
};
use substrate_primitives::{
blake2_256,
storage::{
@@ -51,43 +54,39 @@ use url::Url;
use crate::{
codec::Encoded,
events::EventsDecoder,
metadata::MetadataError,
rpc::{
BlockNumber,
ChainBlock,
MapStream,
Rpc,
ChainBlock,
BlockNumber
},
srml::{
balances::Balances,
system::{
System,
SystemEvent,
SystemStore,
},
ModuleCalls,
},
};
pub use error::Error;
mod codec;
mod error;
mod events;
mod metadata;
mod rpc;
pub mod srml;
mod srml;
/// Captures data for when an extrinsic is successfully included in a block
#[derive(Debug)]
pub struct ExtrinsicSuccess<T: System> {
/// Block hash.
pub block: T::Hash,
/// Extinsic hash.
pub extrinsic: T::Hash,
/// List of events.
pub events: Vec<T::Event>,
}
pub use error::Error;
pub use events::RawEvent;
pub use rpc::ExtrinsicSuccess;
pub use srml::*;
fn connect<T: System>(url: &Url) -> impl Future<Item = Rpc<T>, Error = Error> {
ws::connect(url)
.map_err(Into::into)
ws::connect(url).map_err(Into::into)
}
/// ClientBuilder for constructing a Client.
@@ -150,7 +149,7 @@ impl<T: System> Clone for Client<T> {
}
}
impl<T: System + 'static> Client<T> {
impl<T: System + Balances + 'static> Client<T> {
fn connect(&self) -> impl Future<Item = Rpc<T>, Error = Error> {
connect(&self.url)
}
@@ -186,15 +185,24 @@ impl<T: System + 'static> Client<T> {
}
/// Get a block hash. By default returns the latest block hash
pub fn block_hash(&self, hash: Option<BlockNumber<T>>) -> impl Future<Item = Option<T::Hash>, Error = Error> {
self.connect().and_then(|rpc| rpc.block_hash(hash.map(|h| h)))
pub fn block_hash(
&self,
hash: Option<BlockNumber<T>>,
) -> impl Future<Item = Option<T::Hash>, Error = Error> {
self.connect()
.and_then(|rpc| rpc.block_hash(hash.map(|h| h)))
}
/// Get a block
pub fn block<H>(&self, hash: Option<H>) -> impl Future<Item = Option<ChainBlock<T>>, Error = Error>
where H: Into<T::Hash> + 'static
pub fn block<H>(
&self,
hash: Option<H>,
) -> impl Future<Item = Option<ChainBlock<T>>, Error = Error>
where
H: Into<T::Hash> + 'static,
{
self.connect().and_then(|rpc| rpc.block(hash.map(|h| h.into())))
self.connect()
.and_then(|rpc| rpc.block(hash.map(|h| h.into())))
}
/// Subscribe to events.
@@ -267,7 +275,7 @@ pub struct XtBuilder<T: System, P, V = Invalid> {
marker: PhantomData<fn() -> V>,
}
impl<T: System + 'static, P, V> XtBuilder<T, P, V>
impl<T: System + Balances + 'static, P, V> XtBuilder<T, P, V>
where
P: Pair,
{
@@ -316,7 +324,7 @@ where
}
}
impl<T: System + 'static, P> XtBuilder<T, P, Valid>
impl<T: System + Balances + 'static, P> XtBuilder<T, P, Valid>
where
P: Pair,
P::Public: Into<<T::Lookup as StaticLookup>::Source>,
@@ -355,7 +363,12 @@ where
);
let extra = T::extra(account_nonce);
let raw_payload = (call.clone(), extra.clone(), version, (&genesis_hash, &genesis_hash));
let raw_payload = (
call.clone(),
extra.clone(),
version,
(&genesis_hash, &genesis_hash),
);
let signature = raw_payload.using_encoded(|payload| {
if payload.len() > 256 {
signer.sign(&blake2_256(payload)[..])
@@ -388,11 +401,19 @@ where
&self,
) -> impl Future<Item = ExtrinsicSuccess<T>, Error = Error> {
let cli = self.client.connect();
let metadata = self.client.metadata().clone();
let decoder = EventsDecoder::try_from(metadata)
.into_future()
.map_err(Into::into);
self.create_and_sign()
.into_future()
.map_err(Into::into)
.and_then(move |extrinsic| {
cli.and_then(move |rpc| rpc.submit_and_watch_extrinsic(extrinsic))
.join(decoder)
.and_then(move |(extrinsic, decoder)| {
cli.and_then(move |rpc| {
rpc.submit_and_watch_extrinsic(extrinsic, decoder)
})
})
}
}
@@ -514,8 +535,21 @@ mod tests {
.contracts(|call| call.put_code(500_000, wasm))
.submit_and_watch();
rt.block_on(put_code)
let success = rt
.block_on(put_code)
.expect("Extrinsic should be included in a block");
let code_hash =
success.find_event::<<Runtime as System>::Hash>("Contracts", "CodeStored");
assert!(
code_hash.is_some(),
"Contracts CodeStored event should be present"
);
assert!(
code_hash.unwrap().is_ok(),
"CodeStored Hash should decode successfully"
);
}
#[test]
@@ -529,9 +563,8 @@ mod tests {
#[ignore] // requires locally running substrate node
fn test_getting_block() {
let (mut rt, client) = test_setup();
rt.block_on(client.block_hash(None).and_then(move |h| {
client.block(h)
})).unwrap();
rt.block_on(client.block_hash(None).and_then(move |h| client.block(h)))
.unwrap();
}
#[test]
+150 -15
View File
@@ -13,16 +13,21 @@ use runtime_metadata::{
META_RESERVED,
};
use std::{
collections::HashMap,
collections::{
HashMap,
HashSet,
},
convert::TryFrom,
marker::PhantomData,
str::FromStr,
};
use substrate_primitives::storage::StorageKey;
#[derive(Debug, Clone)]
#[derive(Debug, Clone, derive_more::Display)]
pub enum MetadataError {
ModuleNotFound(&'static str),
ModuleNotFound(String),
CallNotFound(&'static str),
EventNotFound(u8),
StorageNotFound(&'static str),
StorageTypeError,
MapValueTypeError,
@@ -31,15 +36,31 @@ pub enum MetadataError {
#[derive(Clone, Debug)]
pub struct Metadata {
modules: HashMap<String, ModuleMetadata>,
modules_by_event_index: HashMap<u8, String>,
}
impl Metadata {
pub fn module(&self, name: &'static str) -> Result<&ModuleMetadata, MetadataError> {
pub fn modules(&self) -> impl Iterator<Item = &ModuleMetadata> {
self.modules.values()
}
pub fn module<S>(&self, name: S) -> Result<&ModuleMetadata, MetadataError>
where
S: ToString,
{
let name = name.to_string();
self.modules
.get(name)
.get(&name)
.ok_or(MetadataError::ModuleNotFound(name))
}
pub fn module_name(&self, module_index: u8) -> Result<String, MetadataError> {
self.modules_by_event_index
.get(&module_index)
.cloned()
.ok_or(MetadataError::EventNotFound(module_index))
}
pub fn pretty(&self) -> String {
let mut string = String::new();
for (name, module) in &self.modules {
@@ -55,9 +76,9 @@ impl Metadata {
string.push_str(call.as_str());
string.push('\n');
}
for (event, _) in &module.events {
for (_, event) in &module.events {
string.push_str(" e ");
string.push_str(event.as_str());
string.push_str(event.name.as_str());
string.push('\n');
}
}
@@ -67,14 +88,19 @@ impl Metadata {
#[derive(Clone, Debug)]
pub struct ModuleMetadata {
index: Vec<u8>,
index: u8,
name: String,
storage: HashMap<String, StorageMetadata>,
calls: HashMap<String, Vec<u8>>,
events: HashMap<String, Vec<u8>>,
events: HashMap<u8, ModuleEventMetadata>,
// constants
}
impl ModuleMetadata {
pub fn name(&self) -> &str {
&self.name
}
pub fn call<T: Encode>(
&self,
function: &'static str,
@@ -84,7 +110,7 @@ impl ModuleMetadata {
.calls
.get(function)
.ok_or(MetadataError::CallNotFound(function))?;
let mut bytes = self.index.clone();
let mut bytes = vec![self.index];
bytes.extend(fn_bytes);
bytes.extend(params.encode());
Ok(Encoded(bytes))
@@ -95,6 +121,16 @@ impl ModuleMetadata {
.get(key)
.ok_or(MetadataError::StorageNotFound(key))
}
pub fn events(&self) -> impl Iterator<Item = &ModuleEventMetadata> {
self.events.values()
}
pub fn event(&self, index: u8) -> Result<&ModuleEventMetadata, MetadataError> {
self.events
.get(&index)
.ok_or(MetadataError::EventNotFound(index))
}
}
#[derive(Clone, Debug)]
@@ -158,11 +194,86 @@ impl<K: Encode, V: Decode + Clone> StorageMap<K, V> {
}
}
#[derive(Clone, Debug)]
pub struct ModuleEventMetadata {
pub name: String,
arguments: HashSet<EventArg>,
}
impl ModuleEventMetadata {
pub fn arguments(&self) -> Vec<EventArg> {
self.arguments.iter().cloned().collect()
}
}
/// Naive representation of event argument types, supports current set of substrate EventArg types.
/// If and when Substrate uses `type-metadata`, this can be replaced.
///
/// Used to calculate the size of a instance of an event variant without having the concrete type,
/// so the raw bytes can be extracted from the encoded `Vec<EventRecord<E>>` (without `E` defined).
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum EventArg {
Primitive(String),
Vec(Box<EventArg>),
Tuple(Vec<EventArg>),
}
impl FromStr for EventArg {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.starts_with("Vec<") {
if s.ends_with('>') {
Ok(EventArg::Vec(Box::new(s[4..s.len() - 1].parse()?)))
} else {
Err(Error::InvalidEventArg(
s.to_string(),
"Expected closing `>` for `Vec`",
))
}
} else if s.starts_with("(") {
if s.ends_with(")") {
let mut args = Vec::new();
for arg in s[1..s.len() - 1].split(',') {
let arg = arg.trim().parse()?;
args.push(arg)
}
Ok(EventArg::Tuple(args))
} else {
Err(Error::InvalidEventArg(
s.to_string(),
"Expecting closing `)` for tuple",
))
}
} else {
Ok(EventArg::Primitive(s.to_string()))
}
}
}
impl EventArg {
/// Returns all primitive types for this EventArg
pub fn primitives(&self) -> Vec<String> {
match self {
EventArg::Primitive(p) => vec![p.clone()],
EventArg::Vec(arg) => arg.primitives(),
EventArg::Tuple(args) => {
let mut primitives = Vec::new();
for arg in args {
primitives.extend(arg.primitives())
}
primitives
}
}
}
}
#[derive(Debug)]
pub enum Error {
InvalidPrefix,
InvalidVersion,
ExpectedDecoded,
InvalidEventArg(String, &'static str),
}
impl TryFrom<RuntimeMetadataPrefixed> for Metadata {
@@ -177,10 +288,22 @@ impl TryFrom<RuntimeMetadataPrefixed> for Metadata {
_ => Err(Error::InvalidVersion)?,
};
let mut modules = HashMap::new();
let mut modules_by_event_index = HashMap::new();
let mut event_index = 0;
for (i, module) in convert(meta.modules)?.into_iter().enumerate() {
modules.insert(convert(module.name.clone())?, convert_module(i, module)?);
let module_name = convert(module.name.clone())?;
let module_metadata = convert_module(i, module)?;
// modules with no events have no corresponding definition in the top level enum
if !module_metadata.events.is_empty() {
modules_by_event_index.insert(event_index, module_name.clone());
event_index = event_index + 1;
}
modules.insert(module_name, module_metadata);
}
Ok(Metadata { modules })
Ok(Metadata {
modules,
modules_by_event_index,
})
}
}
@@ -216,18 +339,30 @@ fn convert_module(
let mut event_map = HashMap::new();
if let Some(events) = module.event {
for (index, event) in convert(events)?.into_iter().enumerate() {
let name = convert(event.name)?;
event_map.insert(name, vec![index as u8]);
event_map.insert(index as u8, convert_event(event)?);
}
}
Ok(ModuleMetadata {
index: vec![index as u8],
index: index as u8,
name: convert(module.name)?,
storage: storage_map,
calls: call_map,
events: event_map,
})
}
fn convert_event(
event: runtime_metadata::EventMetadata,
) -> Result<ModuleEventMetadata, Error> {
let name = convert(event.name)?;
let mut arguments = HashSet::new();
for arg in convert(event.arguments)? {
let arg = arg.parse::<EventArg>()?;
arguments.insert(arg);
}
Ok(ModuleEventMetadata { name, arguments })
}
fn convert_entry(
prefix: String,
entry: runtime_metadata::StorageEntryMetadata,
+113 -47
View File
@@ -16,8 +16,15 @@
use crate::{
error::Error,
events::{
EventsDecoder,
RuntimeEvent,
},
metadata::Metadata,
srml::system::System,
srml::{
balances::Balances,
system::System,
},
};
use futures::future::{
self,
@@ -29,6 +36,7 @@ use num_traits::bounds::Bounded;
use parity_scale_codec::{
Decode,
Encode,
Error as CodecError,
};
use runtime_metadata::RuntimeMetadataPrefixed;
@@ -107,22 +115,29 @@ impl<T: System> Rpc<T> {
}
/// Get a block hash, returns hash of latest block by default
pub fn block_hash(&self, hash: Option<BlockNumber<T>>) -> impl Future<Item = Option<T::Hash>, Error = Error> {
pub fn block_hash(
&self,
hash: Option<BlockNumber<T>>,
) -> impl Future<Item = Option<T::Hash>, Error = Error> {
self.chain.block_hash(hash).map_err(Into::into)
}
/// Get a Block
pub fn block(&self, hash: Option<T::Hash>) -> impl Future<Item = Option<ChainBlock<T>>, Error = Error> {
pub fn block(
&self,
hash: Option<T::Hash>,
) -> impl Future<Item = Option<ChainBlock<T>>, Error = Error> {
self.chain.block(hash).map_err(Into::into)
}
/// Fetch the runtime version
pub fn runtime_version(&self, at: Option<T::Hash>) -> impl Future<Item = RuntimeVersion, Error = Error> {
pub fn runtime_version(
&self,
at: Option<T::Hash>,
) -> impl Future<Item = RuntimeVersion, Error = Error> {
self.state.runtime_version(at).map_err(Into::into)
}
}
use crate::ExtrinsicSuccess;
use futures::{
future::IntoFuture,
stream::{
@@ -132,17 +147,22 @@ use futures::{
};
use jsonrpc_core_client::TypedSubscriptionStream;
use runtime_primitives::traits::Hash;
use srml_system::EventRecord;
use substrate_primitives::{
storage::StorageChangeSet,
twox_128,
};
use transaction_pool::txpool::watcher::Status;
use crate::{
events::RawEvent,
srml::system::SystemEvent,
};
use srml_system::Phase;
type MapClosure<T> = Box<dyn Fn(T) -> T + Send>;
pub type MapStream<T> = stream::Map<TypedSubscriptionStream<T>, MapClosure<T>>;
impl<T: System> Rpc<T> {
impl<T: System + Balances + 'static> Rpc<T> {
/// Subscribe to substrate System Events
pub fn subscribe_events(
&self,
@@ -205,9 +225,10 @@ impl<T: System> Rpc<T> {
}
/// Create and submit an extrinsic and return corresponding Event if successful
pub fn submit_and_watch_extrinsic<E>(
pub fn submit_and_watch_extrinsic<E: 'static>(
self,
extrinsic: E,
decoder: EventsDecoder<T>,
) -> impl Future<Item = ExtrinsicSuccess<T>, Error = Error>
where
E: Encode,
@@ -267,16 +288,71 @@ impl<T: System> Rpc<T> {
bh,
sb.block.extrinsics.len()
);
wait_for_block_events::<T>(ext_hash, &sb, bh, events)
wait_for_block_events(decoder, ext_hash, sb, bh, events)
})
})
}
}
/// Captures data for when an extrinsic is successfully included in a block
#[derive(Debug)]
pub struct ExtrinsicSuccess<T: System> {
/// Block hash.
pub block: T::Hash,
/// Extrinsic hash.
pub extrinsic: T::Hash,
/// Raw runtime events, can be decoded by the caller.
pub events: Vec<RuntimeEvent>,
}
impl<T: System> ExtrinsicSuccess<T> {
/// Find the Event for the given module/variant, with raw encoded event data.
/// Returns `None` if the Event is not found.
pub fn find_event_raw(&self, module: &str, variant: &str) -> Option<&RawEvent> {
self.events.iter().find_map(|evt| {
match evt {
RuntimeEvent::Raw(ref raw)
if raw.module == module && raw.variant == variant =>
{
Some(raw)
}
_ => None,
}
})
}
/// Returns all System Events
pub fn system_events(&self) -> Vec<&SystemEvent> {
self.events
.iter()
.filter_map(|evt| {
match evt {
RuntimeEvent::System(evt) => Some(evt),
_ => None,
}
})
.collect()
}
/// Find the Event for the given module/variant, attempting to decode the event data.
/// Returns `None` if the Event is not found.
/// Returns `Err` if the data fails to decode into the supplied type
pub fn find_event<E: Decode>(
&self,
module: &str,
variant: &str,
) -> Option<Result<E, CodecError>> {
self.find_event_raw(module, variant)
.map(|evt| E::decode(&mut &evt.data[..]))
}
}
/// Waits for events for the block triggered by the extrinsic
fn wait_for_block_events<T: System>(
pub fn wait_for_block_events<T: System + Balances + 'static>(
decoder: EventsDecoder<T>,
ext_hash: T::Hash,
signed_block: &ChainBlock<T>,
signed_block: ChainBlock<T>,
block_hash: T::Hash,
events_stream: MapStream<StorageChangeSet<T::Hash>>,
) -> impl Future<Item = ExtrinsicSuccess<T>, Error = Error> {
@@ -292,49 +368,39 @@ fn wait_for_block_events<T: System>(
.into_future();
let block_hash = block_hash.clone();
let block_events = events_stream
events_stream
.filter(move |event| event.block == block_hash)
.into_future()
.map_err(|(e, _)| e.into())
.and_then(|(change_set, _)| {
match change_set {
None => future::ok(Vec::new()),
Some(change_set) => {
let events = change_set
.changes
.iter()
.filter_map(|(_key, data)| {
data.as_ref().map(|data| Decode::decode(&mut &data.0[..]))
})
.collect::<Result<Vec<Vec<EventRecord<T::Event, T::Hash>>>, _>>()
.map(|events| events.into_iter().flat_map(|es| es).collect())
.map_err(Into::into);
future::result(events)
}
}
});
block_events
.join(ext_index)
.map(move |(events, ext_index)| {
let events: Vec<T::Event> = events
.iter()
.filter_map(|e| {
if let srml_system::Phase::ApplyExtrinsic(i) = e.phase {
if i as usize == ext_index {
Some(e.event.clone())
} else {
None
.and_then(move |((change_set, _), ext_index)| {
let events = match change_set {
None => Vec::new(),
Some(change_set) => {
let mut events = Vec::new();
for (_key, data) in change_set.changes {
if let Some(data) = data {
match decoder.decode_events(&mut &data.0[..]) {
Ok(raw_events) => {
for (phase, event) in raw_events {
if let Phase::ApplyExtrinsic(i) = phase {
if i as usize == ext_index {
events.push(event)
}
}
}
}
Err(err) => return future::err(err.into()),
}
}
} else {
None
}
})
.collect::<Vec<T::Event>>();
ExtrinsicSuccess {
events
}
};
future::ok(ExtrinsicSuccess {
block: block_hash,
extrinsic: ext_hash,
events,
}
})
})
}
+7
View File
@@ -115,3 +115,10 @@ where
.call("call", (dest, compact(value), compact(gas_limit), data))
}
}
/// Contracts Events
#[derive(parity_scale_codec::Decode)]
pub enum Event<T: System> {
/// Contract code stored
CodeStored(T::Hash),
}
+15 -3
View File
@@ -3,7 +3,10 @@ use crate::{
codec::Encoded,
error::Error,
metadata::MetadataError,
srml::ModuleCalls,
srml::{
balances::Balances,
ModuleCalls,
},
Client,
Valid,
XtBuilder,
@@ -114,7 +117,7 @@ pub trait SystemStore {
) -> Box<dyn Future<Item = <Self::System as System>::Index, Error = Error> + Send>;
}
impl<T: System + 'static> SystemStore for Client<T> {
impl<T: System + Balances + 'static> SystemStore for Client<T> {
type System = T;
fn account_nonce(
@@ -152,7 +155,7 @@ pub trait SystemXt {
) -> Result<Encoded, MetadataError>;
}
impl<T: System + 'static, P, V> SystemXt for XtBuilder<T, P, V>
impl<T: System + Balances + 'static, P, V> SystemXt for XtBuilder<T, P, V>
where
P: Pair,
{
@@ -178,3 +181,12 @@ where
self.module.call("set_code", code)
}
}
/// Event for the System module.
#[derive(Clone, Debug, parity_scale_codec::Decode)]
pub enum SystemEvent {
/// An extrinsic completed successfully.
ExtrinsicSuccess,
/// An extrinsic failed.
ExtrinsicFailed(runtime_primitives::DispatchError),
}