diff --git a/Cargo.toml b/Cargo.toml
index 97c2817de4..9c0490023d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"
diff --git a/src/error.rs b/src/error.rs
index fce89cc311..9557bcb6a2 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -14,22 +14,28 @@
// You should have received a copy of the GNU General Public License
// along with substrate-subxt. If not, see .
-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),
diff --git a/src/events.rs b/src/events.rs
new file mode 100644
index 0000000000..fbfe863d36
--- /dev/null
+++ b/src/events.rs
@@ -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 .
+
+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,
+}
+
+#[derive(Debug, derive_more::From, derive_more::Display)]
+pub enum EventsError {
+ CodecError(CodecError),
+ Metadata(MetadataError),
+ #[display(fmt = "Type Sizes Missing: {:?}", _0)]
+ TypeSizesMissing(Vec),
+ TypeSizeUnavailable(String),
+}
+
+pub struct EventsDecoder {
+ metadata: Metadata, // todo: [AJ] borrow?
+ type_sizes: HashMap,
+ marker: PhantomData T>,
+}
+
+impl TryFrom for EventsDecoder {
+ type Error = EventsError;
+
+ fn try_from(metadata: Metadata) -> Result {
+ 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")?;
+ decoder.register_type_size::("ReferendumIndex")?;
+ decoder.register_type_size::<[u8; 16]>("Kind")?;
+ decoder.register_type_size::<[u8; 32]>("AuthorityId")?;
+ decoder.register_type_size::("u8")?;
+ decoder.register_type_size::("u32")?;
+ decoder.register_type_size::("AccountIndex")?;
+ decoder.register_type_size::("SessionIndex")?;
+ decoder.register_type_size::("PropIndex")?;
+ decoder.register_type_size::("ProposalIndex")?;
+ decoder.register_type_size::("AuthorityIndex")?;
+ decoder.register_type_size::("AuthorityWeight")?;
+ decoder.register_type_size::("MemberCount")?;
+ decoder.register_type_size::("AccountId")?;
+ decoder.register_type_size::("BlockNumber")?;
+ decoder.register_type_size::("Hash")?;
+ decoder.register_type_size::<::Balance>("Balance")?;
+ // VoteThreshold enum index
+ decoder.register_type_size::("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 EventsDecoder {
+ pub fn register_type_size(&mut self, name: &str) -> Result
+ 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>(
+ &self,
+ ignore: I,
+ ) -> Result<(), Vec> {
+ 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(
+ &self,
+ args: &[EventArg],
+ input: &mut I,
+ output: &mut W,
+ ) -> Result<(), EventsError> {
+ for arg in args {
+ match arg {
+ EventArg::Vec(arg) => {
+ let len = >::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, EventsError> {
+ let compact_len = >::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::::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::::decode(input)?;
+ r.push((phase, event));
+ }
+ Ok(r)
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 42e7fbe6f5..166e8a2fab 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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 {
- /// Block hash.
- pub block: T::Hash,
- /// Extinsic hash.
- pub extrinsic: T::Hash,
- /// List of events.
- pub events: Vec,
-}
+pub use error::Error;
+pub use events::RawEvent;
+pub use rpc::ExtrinsicSuccess;
+pub use srml::*;
fn connect(url: &Url) -> impl Future- , 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 Clone for Client {
}
}
-impl Client {
+impl Client {
fn connect(&self) -> impl Future
- , Error = Error> {
connect(&self.url)
}
@@ -186,15 +185,24 @@ impl Client {
}
/// Get a block hash. By default returns the latest block hash
- pub fn block_hash(&self, hash: Option>) -> impl Future
- , Error = Error> {
- self.connect().and_then(|rpc| rpc.block_hash(hash.map(|h| h)))
+ pub fn block_hash(
+ &self,
+ hash: Option>,
+ ) -> impl Future
- , Error = Error> {
+ self.connect()
+ .and_then(|rpc| rpc.block_hash(hash.map(|h| h)))
}
/// Get a block
- pub fn block(&self, hash: Option) -> impl Future
- >, Error = Error>
- where H: Into + 'static
+ pub fn block(
+ &self,
+ hash: Option,
+ ) -> impl Future
- >, Error = Error>
+ where
+ H: Into + '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 {
marker: PhantomData V>,
}
-impl XtBuilder
+impl XtBuilder
where
P: Pair,
{
@@ -316,7 +324,7 @@ where
}
}
-impl XtBuilder
+impl XtBuilder
where
P: Pair,
P::Public: Into<::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
- , 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::<::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]
diff --git a/src/metadata.rs b/src/metadata.rs
index 4ec1cb9316..6553f725d5 100644
--- a/src/metadata.rs
+++ b/src/metadata.rs
@@ -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,
+ modules_by_event_index: HashMap,
}
impl Metadata {
- pub fn module(&self, name: &'static str) -> Result<&ModuleMetadata, MetadataError> {
+ pub fn modules(&self) -> impl Iterator
- {
+ self.modules.values()
+ }
+
+ pub fn module
(&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 {
+ 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,
+ index: u8,
+ name: String,
storage: HashMap,
calls: HashMap>,
- events: HashMap>,
+ events: HashMap,
// constants
}
impl ModuleMetadata {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
pub fn call(
&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- {
+ 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 StorageMap {
}
}
+#[derive(Clone, Debug)]
+pub struct ModuleEventMetadata {
+ pub name: String,
+ arguments: HashSet,
+}
+
+impl ModuleEventMetadata {
+ pub fn arguments(&self) -> Vec {
+ 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>` (without `E` defined).
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum EventArg {
+ Primitive(String),
+ Vec(Box),
+ Tuple(Vec),
+}
+
+impl FromStr for EventArg {
+ type Err = Error;
+
+ fn from_str(s: &str) -> Result {
+ 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 {
+ 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 for Metadata {
@@ -177,10 +288,22 @@ impl TryFrom 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 {
+ let name = convert(event.name)?;
+ let mut arguments = HashSet::new();
+ for arg in convert(event.arguments)? {
+ let arg = arg.parse::()?;
+ arguments.insert(arg);
+ }
+ Ok(ModuleEventMetadata { name, arguments })
+}
+
fn convert_entry(
prefix: String,
entry: runtime_metadata::StorageEntryMetadata,
diff --git a/src/rpc.rs b/src/rpc.rs
index 4d731f7622..b3eec928f3 100644
--- a/src/rpc.rs
+++ b/src/rpc.rs
@@ -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 Rpc {
}
/// Get a block hash, returns hash of latest block by default
- pub fn block_hash(&self, hash: Option>) -> impl Future
- , Error = Error> {
+ pub fn block_hash(
+ &self,
+ hash: Option>,
+ ) -> impl Future
- , Error = Error> {
self.chain.block_hash(hash).map_err(Into::into)
}
/// Get a Block
- pub fn block(&self, hash: Option) -> impl Future
- >, Error = Error> {
+ pub fn block(
+ &self,
+ hash: Option,
+ ) -> impl Future
- >, Error = Error> {
self.chain.block(hash).map_err(Into::into)
}
/// Fetch the runtime version
- pub fn runtime_version(&self, at: Option) -> impl Future
- {
+ pub fn runtime_version(
+ &self,
+ at: Option,
+ ) -> impl Future
- {
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 = Box T + Send>;
pub type MapStream = stream::Map, MapClosure>;
-impl Rpc {
+impl Rpc {
/// Subscribe to substrate System Events
pub fn subscribe_events(
&self,
@@ -205,9 +225,10 @@ impl Rpc {
}
/// Create and submit an extrinsic and return corresponding Event if successful
- pub fn submit_and_watch_extrinsic(
+ pub fn submit_and_watch_extrinsic(
self,
extrinsic: E,
+ decoder: EventsDecoder,
) -> impl Future
- , Error = Error>
where
E: Encode,
@@ -267,16 +288,71 @@ impl Rpc {
bh,
sb.block.extrinsics.len()
);
- wait_for_block_events::(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 {
+ /// Block hash.
+ pub block: T::Hash,
+ /// Extrinsic hash.
+ pub extrinsic: T::Hash,
+ /// Raw runtime events, can be decoded by the caller.
+ pub events: Vec,
+}
+
+impl ExtrinsicSuccess {
+ /// 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(
+ &self,
+ module: &str,
+ variant: &str,
+ ) -> Option> {
+ 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(
+pub fn wait_for_block_events(
+ decoder: EventsDecoder,
ext_hash: T::Hash,
- signed_block: &ChainBlock,
+ signed_block: ChainBlock,
block_hash: T::Hash,
events_stream: MapStream>,
) -> impl Future
- , Error = Error> {
@@ -292,49 +368,39 @@ fn wait_for_block_events(
.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::>>, _>>()
- .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 = 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::>();
- ExtrinsicSuccess {
+ events
+ }
+ };
+ future::ok(ExtrinsicSuccess {
block: block_hash,
extrinsic: ext_hash,
events,
- }
+ })
})
}
diff --git a/src/srml/contracts.rs b/src/srml/contracts.rs
index a9d7b843d9..12873156d2 100644
--- a/src/srml/contracts.rs
+++ b/src/srml/contracts.rs
@@ -115,3 +115,10 @@ where
.call("call", (dest, compact(value), compact(gas_limit), data))
}
}
+
+/// Contracts Events
+#[derive(parity_scale_codec::Decode)]
+pub enum Event {
+ /// Contract code stored
+ CodeStored(T::Hash),
+}
diff --git a/src/srml/system.rs b/src/srml/system.rs
index 5226af1724..f377682486 100644
--- a/src/srml/system.rs
+++ b/src/srml/system.rs
@@ -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::Index, Error = Error> + Send>;
}
-impl SystemStore for Client {
+impl SystemStore for Client {
type System = T;
fn account_nonce(
@@ -152,7 +155,7 @@ pub trait SystemXt {
) -> Result;
}
-impl SystemXt for XtBuilder
+impl SystemXt for XtBuilder
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),
+}