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), +}