Merge branch 'master' into staking

* master:
  decode option event arg (#158)
  Release v0.12.0 (#157)
  Only return an error if the extrinsic failed. (#156)
  Update to rc6. (#155)
  Different assert. (#153)
  Add a method to fetch an unhashed key, close #100 (#152)
  Fix port number. (#151)
This commit is contained in:
David Palm
2020-09-11 12:35:46 +02:00
19 changed files with 410 additions and 318 deletions
+29 -15
View File
@@ -62,13 +62,7 @@ pub enum Error {
TypeSizeUnavailable(String),
/// Runtime error.
#[error("Runtime error: {0}")]
Runtime(RuntimeError),
/// Bad origin.
#[error("Bad origin: throw by ensure_signed, ensure_root or ensure_none.")]
BadOrigin,
/// Cannot lookup.
#[error("Cannot lookup some information required to validate the transaction.")]
CannotLookup,
Runtime(#[from] RuntimeError),
/// Other error.
#[error("Other error: {0}")]
Other(String),
@@ -98,9 +92,29 @@ impl From<String> for Error {
}
}
impl Error {
/// Runtime error.
#[derive(Clone, Debug, Eq, Error, PartialEq)]
pub enum RuntimeError {
/// Module error.
#[error("Runtime module error: {0}")]
Module(ModuleError),
/// Bad origin.
#[error("Bad origin: throw by ensure_signed, ensure_root or ensure_none.")]
BadOrigin,
/// Cannot lookup.
#[error("Cannot lookup some information required to validate the transaction.")]
CannotLookup,
/// Other error.
#[error("Other error: {0}")]
Other(String),
}
impl RuntimeError {
/// Converts a `DispatchError` into a subxt error.
pub fn from_dispatch(metadata: &Metadata, error: DispatchError) -> Result<(), Self> {
pub fn from_dispatch(
metadata: &Metadata,
error: DispatchError,
) -> Result<Self, Error> {
match error {
DispatchError::Module {
index,
@@ -109,22 +123,22 @@ impl Error {
} => {
let module = metadata.module_with_errors(index)?;
let error = module.error(error)?;
Err(Error::Runtime(RuntimeError {
Ok(Self::Module(ModuleError {
module: module.name().to_string(),
error: error.to_string(),
}))
}
DispatchError::BadOrigin => Err(Error::BadOrigin),
DispatchError::CannotLookup => Err(Error::CannotLookup),
DispatchError::Other(msg) => Err(Error::Other(msg.into())),
DispatchError::BadOrigin => Ok(Self::BadOrigin),
DispatchError::CannotLookup => Ok(Self::CannotLookup),
DispatchError::Other(msg) => Ok(Self::Other(msg.into())),
}
}
}
/// Runtime errors.
/// Module error.
#[derive(Clone, Debug, Eq, Error, PartialEq)]
#[error("{error} from {module}")]
pub struct RuntimeError {
pub struct ModuleError {
pub module: String,
pub error: String,
}
+44 -15
View File
@@ -39,7 +39,10 @@ use std::{
};
use crate::{
error::Error,
error::{
Error,
RuntimeError,
},
metadata::{
EventArg,
Metadata,
@@ -151,6 +154,17 @@ impl<T: System> EventsDecoder<T> {
self.decode_raw_bytes(&[*arg.clone()], input, output)?
}
}
EventArg::Option(arg) => {
match input.read_byte()? {
0 => (),
1 => self.decode_raw_bytes(&[*arg.clone()], input, output)?,
_ => {
return Err(Error::Other(
"unexpected first byte decoding Option".into(),
))
}
}
}
EventArg::Tuple(args) => self.decode_raw_bytes(args, input, output)?,
EventArg::Primitive(name) => {
let result = match name.as_str() {
@@ -168,7 +182,9 @@ impl<T: System> EventsDecoder<T> {
}
};
if let Err(error) = result {
Error::from_dispatch(&self.metadata, error)?;
return Err(
RuntimeError::from_dispatch(&self.metadata, error)?.into()
)
}
}
}
@@ -177,10 +193,7 @@ impl<T: System> EventsDecoder<T> {
}
/// Decode events.
pub fn decode_events(
&self,
input: &mut &[u8],
) -> Result<Vec<(Phase, RawEvent)>, Error> {
pub fn decode_events(&self, input: &mut &[u8]) -> Result<Vec<(Phase, Raw)>, Error> {
let compact_len = <Compact<u32>>::decode(input)?;
let len = compact_len.0 as usize;
@@ -201,20 +214,36 @@ impl<T: System> EventsDecoder<T> {
);
let mut event_data = Vec::<u8>::new();
self.decode_raw_bytes(&event_metadata.arguments(), input, &mut event_data)?;
let result = self.decode_raw_bytes(
&event_metadata.arguments(),
input,
&mut event_data,
);
let raw = match result {
Ok(()) => {
log::debug!("raw bytes: {}", hex::encode(&event_data),);
log::debug!("raw bytes: {}", hex::encode(&event_data),);
let event = RawEvent {
module: module.name().to_string(),
variant: event_metadata.name.clone(),
data: event_data,
};
let event = RawEvent {
module: module.name().to_string(),
variant: event_metadata.name.clone(),
data: event_data,
// topics come after the event data in EventRecord
let _topics = Vec::<T::Hash>::decode(input)?;
Raw::Event(event)
}
Err(Error::Runtime(err)) => Raw::Error(err),
Err(err) => return Err(err),
};
// topics come after the event data in EventRecord
let _topics = Vec::<T::Hash>::decode(input)?;
r.push((phase, event));
r.push((phase, raw));
}
Ok(r)
}
}
pub enum Raw {
Event(RawEvent),
Error(RuntimeError),
}
+3 -2
View File
@@ -112,6 +112,7 @@ mod tests {
use crate::{
error::{
Error,
ModuleError,
RuntimeError,
},
events::EventsDecoder,
@@ -193,8 +194,8 @@ mod tests {
let res = client
.transfer_and_watch(&hans, alice.account_id(), 100_000_000_000)
.await;
if let Err(Error::Runtime(error)) = res {
let error2 = RuntimeError {
if let Err(Error::Runtime(RuntimeError::Module(error))) = res {
let error2 = ModuleError {
module: "Balances".into(),
error: "InsufficientBalance".into(),
};
+5 -2
View File
@@ -43,7 +43,10 @@ pub struct SudoCall<'a, T: Sudo> {
mod tests {
use super::*;
use crate::{
error::Error,
error::{
Error,
RuntimeError,
},
extrinsic::PairSigner,
frame::balances::TransferCall,
tests::{
@@ -68,7 +71,7 @@ mod tests {
let res = client.sudo_and_watch(&alice, &call).await;
assert!(
if let Err(Error::BadOrigin) = res {
if let Err(Error::Runtime(RuntimeError::BadOrigin)) = res {
true
} else {
false
+17 -8
View File
@@ -247,7 +247,7 @@ impl<T: Runtime, F: Store<T>> KeyIter<T, F> {
}
}
}
debug_assert_eq!(self.buffer.len(), self.count as usize);
debug_assert_eq!(self.buffer.len(), keys.len());
}
}
}
@@ -269,6 +269,19 @@ impl<T: Runtime> Client<T> {
&self.properties
}
/// Fetch the value under an unhashed storage key
pub async fn fetch_unhashed<V: Decode>(
&self,
key: StorageKey,
hash: Option<T::Hash>,
) -> Result<Option<V>, Error> {
if let Some(data) = self.rpc.storage(&key, hash).await? {
Ok(Some(Decode::decode(&mut &data.0[..])?))
} else {
Ok(None)
}
}
/// Fetch a StorageKey with an optional block hash.
pub async fn fetch<F: Store<T>>(
&self,
@@ -276,11 +289,7 @@ impl<T: Runtime> Client<T> {
hash: Option<T::Hash>,
) -> Result<Option<F::Returns>, Error> {
let key = store.key(&self.metadata)?;
if let Some(data) = self.rpc.storage(&key, hash).await? {
Ok(Some(Decode::decode(&mut &data.0[..])?))
} else {
Ok(None)
}
self.fetch_unhashed::<F::Returns>(key, hash).await
}
/// Fetch a StorageKey that has a default value with an optional block hash.
@@ -599,14 +608,14 @@ mod tests {
},
chain_spec: test_node::chain_spec::development_config().unwrap(),
role: Role::Authority(key),
enable_telemetry: false,
telemetry: None,
};
let client = ClientBuilder::new()
.set_client(
SubxtClient::from_config(config, test_node::service::new_full)
.expect("Error creating subxt client"),
)
.set_page_size(2)
.set_page_size(3)
.build()
.await
.expect("Error creating client");
+11
View File
@@ -398,6 +398,7 @@ pub enum EventArg {
Primitive(String),
Vec(Box<EventArg>),
Tuple(Vec<EventArg>),
Option(Box<EventArg>),
}
impl FromStr for EventArg {
@@ -413,6 +414,15 @@ impl FromStr for EventArg {
"Expected closing `>` for `Vec`",
))
}
} else if s.starts_with("Option<") {
if s.ends_with('>') {
Ok(EventArg::Option(Box::new(s[7..s.len() - 1].parse()?)))
} else {
Err(ConversionError::InvalidEventArg(
s.to_string(),
"Expected closing `>` for `Option`",
))
}
} else if s.starts_with('(') {
if s.ends_with(')') {
let mut args = Vec::new();
@@ -439,6 +449,7 @@ impl EventArg {
match self {
EventArg::Primitive(p) => vec![p.clone()],
EventArg::Vec(arg) => arg.primitives(),
EventArg::Option(arg) => arg.primitives(),
EventArg::Tuple(args) => {
let mut primitives = Vec::new();
for arg in args {
+6 -1
View File
@@ -22,6 +22,7 @@ use crate::{
error::Error,
events::{
EventsDecoder,
Raw,
RawEvent,
},
frame::{
@@ -99,13 +100,17 @@ impl<T: Runtime> EventSubscription<T> {
Ok(events) => events,
Err(error) => return Some(Err(error)),
};
for (phase, event) in raw_events {
for (phase, raw) in raw_events {
if let Phase::ApplyExtrinsic(i) = phase {
if let Some(ext_index) = self.extrinsic {
if i as usize != ext_index {
continue
}
}
let event = match raw {
Raw::Event(event) => event,
Raw::Error(err) => return Some(Err(err.into())),
};
if let Some((module, variant)) = self.event {
if event.module != module || event.variant != variant {
continue