diff --git a/core/client/src/block_builder/block_builder.rs b/core/client/src/block_builder/block_builder.rs index 4564c29aae419..882791f95a79a 100644 --- a/core/client/src/block_builder/block_builder.rs +++ b/core/client/src/block_builder/block_builder.rs @@ -104,7 +104,7 @@ where ExecutionContext::BlockConstruction, xt.clone() )? { - Ok(ApplyOutcome::Success) | Ok(ApplyOutcome::Fail) => { + Ok(ApplyOutcome::Success) | Ok(ApplyOutcome::Fail(_)) => { extrinsics.push(xt); Ok(()) } diff --git a/core/sr-primitives/src/generic/unchecked_extrinsic.rs b/core/sr-primitives/src/generic/unchecked_extrinsic.rs index d6e0d60e2c218..d11561a415836 100644 --- a/core/sr-primitives/src/generic/unchecked_extrinsic.rs +++ b/core/sr-primitives/src/generic/unchecked_extrinsic.rs @@ -22,6 +22,7 @@ use std::fmt; use rstd::prelude::*; use crate::codec::{Decode, Encode, Codec, Input, HasCompact}; use crate::traits::{self, Member, SimpleArithmetic, MaybeDisplay, Lookup, Extrinsic}; +use crate::PrimitiveError; use super::CheckedExtrinsic; #[derive(PartialEq, Eq, Clone, Encode, Decode)] @@ -83,17 +84,18 @@ where Call: Encode + Member, Signature: Member + traits::Verify + Codec, AccountId: Member + MaybeDisplay, - Context: Lookup, + Context: Lookup { type Checked = CheckedExtrinsic; + type Error = PrimitiveError; - fn check(self, context: &Context) -> Result { + fn check(self, context: &Context) -> Result { Ok(match self.signature { Some(SignatureContent{signed, signature, index}) => { let payload = (index, self.function); let signed = context.lookup(signed)?; if !crate::verify_encoded_lazy(&signature, &payload, &signed) { - return Err(crate::BAD_SIGNATURE) + return Err(PrimitiveError::BadSignature) } CheckedExtrinsic { signed: Some((signed, payload.0)), diff --git a/core/sr-primitives/src/generic/unchecked_mortal_compact_extrinsic.rs b/core/sr-primitives/src/generic/unchecked_mortal_compact_extrinsic.rs index 36e17fc277cde..2cd52b386662a 100644 --- a/core/sr-primitives/src/generic/unchecked_mortal_compact_extrinsic.rs +++ b/core/sr-primitives/src/generic/unchecked_mortal_compact_extrinsic.rs @@ -24,6 +24,7 @@ use runtime_io::blake2_256; use crate::codec::{Decode, Encode, Input, Compact}; use crate::traits::{self, Member, SimpleArithmetic, MaybeDisplay, CurrentHeight, BlockNumberToHash, Lookup, Checkable, Extrinsic, SaturatedConversion}; +use crate::PrimitiveError; use super::{CheckedExtrinsic, Era}; const TRANSACTION_VERSION: u8 = 1; @@ -75,13 +76,14 @@ where AccountId: Member + MaybeDisplay, BlockNumber: SimpleArithmetic, Hash: Encode, - Context: Lookup + Context: Lookup + CurrentHeight + BlockNumberToHash, { type Checked = CheckedExtrinsic; + type Error = PrimitiveError; - fn check(self, context: &Context) -> Result { + fn check(self, context: &Context) -> Result { Ok(match self.signature { Some((signed, signature, index, era)) => { let current_u64 = context.current_height().saturated_into::(); @@ -96,7 +98,7 @@ where signature.verify(payload, &signed) } }) { - return Err(crate::BAD_SIGNATURE) + return Err(PrimitiveError::BadSignature) } CheckedExtrinsic { signed: Some((signed, (raw_payload.0).0)), @@ -198,6 +200,7 @@ mod tests { impl Lookup for TestContext { type Source = u64; type Target = u64; + type Error = &'static str; fn lookup(&self, s: u64) -> Result { Ok(s) } } impl CurrentHeight for TestContext { @@ -256,7 +259,7 @@ mod tests { fn badly_signed_check_should_fail() { let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, vec![0u8]), Era::immortal()); assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); + assert_eq!(>::check(ux, &TestContext), Err(PrimitiveError::BadSignature)); } #[test] @@ -284,14 +287,14 @@ mod tests { fn too_late_mortal_signed_check_should_fail() { let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::mortal(32, 10), 10u64).encode()), Era::mortal(32, 10)); assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); + assert_eq!(>::check(ux, &TestContext), Err(PrimitiveError::BadSignature)); } #[test] fn too_early_mortal_signed_check_should_fail() { let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::mortal(32, 43), 43u64).encode()), Era::mortal(32, 43)); assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); + assert_eq!(>::check(ux, &TestContext), Err(PrimitiveError::BadSignature)); } #[test] diff --git a/core/sr-primitives/src/generic/unchecked_mortal_extrinsic.rs b/core/sr-primitives/src/generic/unchecked_mortal_extrinsic.rs index 7f92b20edd0c3..38f88b65a1cfe 100644 --- a/core/sr-primitives/src/generic/unchecked_mortal_extrinsic.rs +++ b/core/sr-primitives/src/generic/unchecked_mortal_extrinsic.rs @@ -26,6 +26,7 @@ use crate::traits::{ self, Member, SimpleArithmetic, MaybeDisplay, CurrentHeight, BlockNumberToHash, Lookup, Checkable, Extrinsic, SaturatedConversion }; +use crate::PrimitiveError; use super::{CheckedExtrinsic, Era}; const TRANSACTION_VERSION: u8 = 1; @@ -76,13 +77,14 @@ where AccountId: Member + MaybeDisplay, BlockNumber: SimpleArithmetic, Hash: Encode, - Context: Lookup + Context: Lookup + CurrentHeight + BlockNumberToHash, { type Checked = CheckedExtrinsic; + type Error = PrimitiveError; - fn check(self, context: &Context) -> Result { + fn check(self, context: &Context) -> Result { Ok(match self.signature { Some((signed, signature, index, era)) => { let current_u64 = context.current_height().saturated_into::(); @@ -98,7 +100,7 @@ where signature.verify(payload, &signed) } }) { - return Err(crate::BAD_SIGNATURE) + return Err(PrimitiveError::BadSignature) } CheckedExtrinsic { signed: Some((signed, raw_payload.0)), @@ -199,7 +201,8 @@ mod tests { impl Lookup for TestContext { type Source = u64; type Target = u64; - fn lookup(&self, s: u64) -> Result { Ok(s) } + type Error = &'static str; + fn lookup(&self, s: u64) -> Result { Ok(s) } } impl CurrentHeight for TestContext { type BlockNumber = u64; @@ -257,7 +260,7 @@ mod tests { fn badly_signed_check_should_fail() { let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, vec![0u8]), Era::immortal()); assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); + assert_eq!(>::check(ux, &TestContext), Err(PrimitiveError::BadSignature)); } #[test] @@ -285,14 +288,14 @@ mod tests { fn too_late_mortal_signed_check_should_fail() { let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::mortal(32, 10), 10u64).encode()), Era::mortal(32, 10)); assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); + assert_eq!(>::check(ux, &TestContext), Err(PrimitiveError::BadSignature)); } #[test] fn too_early_mortal_signed_check_should_fail() { let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::mortal(32, 43), 43u64).encode()), Era::mortal(32, 43)); assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); + assert_eq!(>::check(ux, &TestContext), Err(PrimitiveError::BadSignature)); } #[test] diff --git a/core/sr-primitives/src/lib.rs b/core/sr-primitives/src/lib.rs index 607fdff376d1b..8bca8eb7bbd74 100644 --- a/core/sr-primitives/src/lib.rs +++ b/core/sr-primitives/src/lib.rs @@ -54,18 +54,41 @@ pub use generic::{DigestItem, Digest}; /// Re-export this since it's part of the API of this crate. pub use substrate_primitives::crypto::{key_types, KeyTypeId}; -/// A message indicating an invalid signature in extrinsic. -pub const BAD_SIGNATURE: &str = "bad signature in extrinsic"; - -/// Full block error message. -/// -/// This allows modules to indicate that given transaction is potentially valid -/// in the future, but can't be executed in the current state. -/// Note this error should be returned early in the execution to prevent DoS, -/// cause the fees are not being paid if this error is returned. -/// -/// Example: block gas limit is reached (the transaction can be retried in the next block though). -pub const BLOCK_FULL: &str = "block size limit is reached"; +#[cfg_attr(test, derive(PartialEq, Debug))] +/// Primitive Error type +pub enum PrimitiveError { + /// Unknown error + /// This exists only to make implementation easier. Should be avoid as much as possible. + Other(&'static str), + /// Indicating an invalid signature in extrinsic. + BadSignature, + /// Full block error message. + /// + /// This allows modules to indicate that given transaction is potentially valid + /// in the future, but can't be executed in the current state. + /// Note this error should be returned early in the execution to prevent DoS, + /// cause the fees are not being paid if this error is returned. + /// + /// Example: block gas limit is reached (the transaction can be retried in the next block though). + BlockFull, +} + +// Exists for for backward compatibility purpose. +impl Into<&'static str> for PrimitiveError { + fn into(self) -> &'static str { + match self { + PrimitiveError::Other(val) => val, + PrimitiveError::BadSignature => "bad signature in extrinsic", + PrimitiveError::BlockFull => "block size limit is reached", + } + } +} + +impl From<&'static str> for PrimitiveError { + fn from(val: &'static str) -> PrimitiveError { + PrimitiveError::Other(val) + } +} /// Justification type. pub type Justification = Vec; @@ -544,21 +567,14 @@ impl From for AnySignature { } } -#[derive(Eq, PartialEq, Clone, Copy, Decode)] +#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode)] #[cfg_attr(feature = "std", derive(Debug, Serialize))] -#[repr(u8)] /// Outcome of a valid extrinsic application. Capable of being sliced. pub enum ApplyOutcome { /// Successful application (extrinsic reported no issue). - Success = 0, + Success, /// Failed application (extrinsic was probably a no-op other than fees). - Fail = 1, -} - -impl codec::Encode for ApplyOutcome { - fn using_encoded R>(&self, f: F) -> R { - f(&[*self as u8]) - } + Fail(DispatchError), } #[derive(Eq, PartialEq, Clone, Copy, Decode)] @@ -578,12 +594,40 @@ pub enum ApplyError { FullBlock = 255, } -impl codec::Encode for ApplyError { +impl Encode for ApplyError { fn using_encoded R>(&self, f: F) -> R { f(&[*self as u8]) } } +#[derive(Eq, PartialEq, Clone, Copy)] +#[cfg_attr(feature = "std", derive(Debug, Serialize))] +/// Reason why a dispatch call failed +pub struct DispatchError { + /// Module index, matching the metadata module index + pub module: u8, + /// Module specific error value + pub error: u8, + /// Optional error message. + pub message: Option<&'static str>, +} + +impl Encode for DispatchError { + fn using_encoded R>(&self, f: F) -> R { + f(&[self.module, self.error]) + } +} + +impl Decode for DispatchError { + fn decode(input: &mut R) -> Option { + Some(DispatchError { + module: input.read_byte()?, + error: input.read_byte()?, + message: None, + }) + } +} + /// Result from attempt to apply an extrinsic. pub type ApplyResult = Result; @@ -754,6 +798,7 @@ impl traits::Extrinsic for OpaqueExtrinsic { #[cfg(test)] mod tests { + use super::DispatchError; use crate::codec::{Encode, Decode}; macro_rules! per_thing_upper_test { @@ -867,4 +912,21 @@ mod tests { ((Into::::into(std::u128::MAX) * 999_999u32) / 1_000_000u32).as_u128() ); } + + #[test] + fn dispatch_error_encoding() { + let error = DispatchError { + module: 1, + error: 2, + message: Some("error message"), + }; + let encoded = error.encode(); + let decoded = DispatchError::decode(&mut &*encoded).unwrap(); + assert_eq!(encoded, vec![1, 2]); + assert_eq!(decoded, DispatchError { + module: 1, + error: 2, + message: None, + }); + } } diff --git a/core/sr-primitives/src/testing.rs b/core/sr-primitives/src/testing.rs index f8df25ec596b0..f146161219dc0 100644 --- a/core/sr-primitives/src/testing.rs +++ b/core/sr-primitives/src/testing.rs @@ -22,6 +22,7 @@ use crate::codec::{Codec, Encode, Decode}; use crate::traits::{self, Checkable, Applyable, BlakeTwo256, OpaqueKeys, TypedKey}; use crate::{generic, KeyTypeId}; use crate::weights::{Weighable, Weight}; +use crate::PrimitiveError; pub use substrate_primitives::H256; use substrate_primitives::U256; use substrate_primitives::ed25519::{Public as AuthorityId}; @@ -216,7 +217,8 @@ impl Debug for TestXt { impl Checkable for TestXt { type Checked = Self; - fn check(self, _: &Context) -> Result { Ok(self) } + type Error = PrimitiveError; + fn check(self, _: &Context) -> Result { Ok(self) } } impl traits::Extrinsic for TestXt { fn is_signed(&self) -> Option { diff --git a/core/sr-primitives/src/traits.rs b/core/sr-primitives/src/traits.rs index a6d94babbe577..42421c63070ed 100644 --- a/core/sr-primitives/src/traits.rs +++ b/core/sr-primitives/src/traits.rs @@ -70,26 +70,44 @@ impl Verify for substrate_primitives::sr25519::Signature { } } +/// EnsureOrigin Error type +pub trait EnsureOriginError { + /// Indicates invalid origin + fn invalid_origin() -> Self; +} + /// Some sort of check on the origin is performed by this object. pub trait EnsureOrigin { /// A return type. type Success; + /// Error type + type Error: EnsureOriginError; /// Perform the origin check. - fn ensure_origin(o: OuterOrigin) -> result::Result { - Self::try_origin(o).map_err(|_| "Invalid origin") + fn ensure_origin(o: OuterOrigin) -> result::Result { + Self::try_origin(o).map_err(|_| Self::Error::invalid_origin()) } /// Perform the origin check. fn try_origin(o: OuterOrigin) -> result::Result; } +impl EnsureOriginError for () { + fn invalid_origin() -> () {} +} + +impl EnsureOriginError for &'static str { + fn invalid_origin() -> &'static str { "Invalid origin" } +} + /// Means of changing one type into another in a manner dependent on the source type. pub trait Lookup { /// Type to lookup from. type Source; /// Type to lookup into. type Target; + /// Error type + type Error; /// Attempt a lookup. - fn lookup(&self, s: Self::Source) -> result::Result; + fn lookup(&self, s: Self::Source) -> result::Result; } /// Means of changing one type into another in a manner dependent on the source type. @@ -100,8 +118,10 @@ pub trait StaticLookup { type Source: Codec + Clone + PartialEq + MaybeDebug; /// Type to lookup into. type Target; + /// Error type. + type Error; /// Attempt a lookup. - fn lookup(s: Self::Source) -> result::Result; + fn lookup(s: Self::Source) -> result::Result; /// Convert from Target back to Source. fn unlookup(t: Self::Target) -> Self::Source; } @@ -112,13 +132,15 @@ pub struct IdentityLookup(PhantomData); impl StaticLookup for IdentityLookup { type Source = T; type Target = T; - fn lookup(x: T) -> result::Result { Ok(x) } + type Error = &'static str; + fn lookup(x: T) -> result::Result { Ok(x) } fn unlookup(x: T) -> T { x } } impl Lookup for IdentityLookup { type Source = T; type Target = T; - fn lookup(&self, x: T) -> result::Result { Ok(x) } + type Error = &'static str; + fn lookup(&self, x: T) -> result::Result { Ok(x) } } /// Get the "current" block number. @@ -724,9 +746,11 @@ pub type DigestItemFor = DigestItem<<::Header as Header>::Hash>; pub trait Checkable: Sized { /// Returned if `check` succeeds. type Checked; + /// Indicates why `check` failed + type Error; /// Check self, given an instance of Context. - fn check(self, c: &Context) -> Result; + fn check(self, c: &Context) -> Result; } /// A "checkable" piece of information, used by the standard Substrate Executive in order to @@ -736,15 +760,19 @@ pub trait Checkable: Sized { pub trait BlindCheckable: Sized { /// Returned if `check` succeeds. type Checked; + /// Returned if `check` failed. + type Error; /// Check self. - fn check(self) -> Result; + fn check(self) -> Result; } // Every `BlindCheckable` is also a `StaticCheckable` for arbitrary `Context`. impl Checkable for T { type Checked = ::Checked; - fn check(self, _c: &Context) -> Result { + type Error = ::Error; + + fn check(self, _c: &Context) -> Result { BlindCheckable::check(self) } } diff --git a/core/test-runtime/src/lib.rs b/core/test-runtime/src/lib.rs index b72d2af62a9ac..edd192407eb5f 100644 --- a/core/test-runtime/src/lib.rs +++ b/core/test-runtime/src/lib.rs @@ -37,6 +37,7 @@ use runtime_primitives::{ ApplyResult, create_runtime_str, transaction_validity::TransactionValidity, + PrimitiveError, traits::{ BlindCheckable, BlakeTwo256, Block as BlockT, Extrinsic as ExtrinsicT, GetNodeBlockType, GetRuntimeBlockType, Verify @@ -122,18 +123,19 @@ impl serde::Serialize for Extrinsic { impl BlindCheckable for Extrinsic { type Checked = Self; + type Error = PrimitiveError; - fn check(self) -> Result { + fn check(self) -> Result { match self { Extrinsic::AuthoritiesChange(new_auth) => Ok(Extrinsic::AuthoritiesChange(new_auth)), Extrinsic::Transfer(transfer, signature) => { if runtime_primitives::verify_encoded_lazy(&signature, &transfer, &transfer.from) { Ok(Extrinsic::Transfer(transfer, signature)) } else { - Err(runtime_primitives::BAD_SIGNATURE) + Err(PrimitiveError::BadSignature) } }, - Extrinsic::IncludeData(_) => Err(runtime_primitives::BAD_SIGNATURE), + Extrinsic::IncludeData(_) => Err(PrimitiveError::BadSignature), Extrinsic::StorageChange(key, value) => Ok(Extrinsic::StorageChange(key, value)), } } diff --git a/node-template/runtime/src/lib.rs b/node-template/runtime/src/lib.rs index 865299285af52..5fae7efe490ba 100644 --- a/node-template/runtime/src/lib.rs +++ b/node-template/runtime/src/lib.rs @@ -133,6 +133,8 @@ impl system::Trait for Runtime { type Event = Event; /// The ubiquitous origin type. type Origin = Origin; + /// The ubiquitous error type. + type Error = Error; /// Maximum number of block number to block hash mappings to keep (oldest pruned first). type BlockHashCount = BlockHashCount; } @@ -181,6 +183,8 @@ impl balances::Trait for Runtime { type OnNewAccount = Indices; /// The ubiquitous event type. type Event = Event; + /// The ubiquitous event type. + type Error = Error; type TransactionPayment = (); type DustRemoval = (); @@ -213,7 +217,7 @@ construct_runtime!( Timestamp: timestamp::{Module, Call, Storage, Inherent}, Aura: aura::{Module, Config, Inherent(Timestamp)}, Indices: indices::{default, Config}, - Balances: balances, + Balances: balances::{default, Error}, Sudo: sudo, // Used for the module template in `./template.rs` TemplateModule: template::{Module, Call, Storage, Event}, diff --git a/node-template/runtime/src/template.rs b/node-template/runtime/src/template.rs index 05257d2a53341..a3ce00e58be13 100644 --- a/node-template/runtime/src/template.rs +++ b/node-template/runtime/src/template.rs @@ -71,13 +71,17 @@ mod tests { use runtime_io::with_externalities; use primitives::{H256, Blake2Hasher}; - use support::{impl_outer_origin, assert_ok, parameter_types}; + use support::{impl_outer_origin, impl_outer_error, assert_ok, parameter_types}; use runtime_primitives::{traits::{BlakeTwo256, IdentityLookup}, testing::Header}; impl_outer_origin! { pub enum Origin for Test {} } + impl_outer_error! { + pub enum Error for Test {} + } + // For testing the module, we construct most of a mock runtime. This means // first constructing a configuration type (`Test`) which `impl`s each of the // configuration traits of modules we want to use. @@ -96,6 +100,7 @@ mod tests { type Lookup = IdentityLookup; type Header = Header; type Event = (); + type Error = Error; type BlockHashCount = BlockHashCount; } impl Trait for Test { diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index 3bfb86c8cf4e7..0667d98f150d4 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -69,7 +69,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to equal spec_version. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 113, + spec_version: 114, impl_version: 114, apis: RUNTIME_API_VERSIONS, }; @@ -123,6 +123,7 @@ impl system::Trait for Runtime { type Lookup = Indices; type Header = generic::Header; type Event = Event; + type Error = Error; type BlockHashCount = BlockHashCount; } @@ -159,6 +160,7 @@ impl balances::Trait for Runtime { type CreationFee = CreationFee; type TransactionBaseFee = TransactionBaseFee; type TransactionByteFee = TransactionByteFee; + type Error = Error; } parameter_types! { @@ -405,7 +407,7 @@ construct_runtime!( Timestamp: timestamp::{Module, Call, Storage, Inherent}, Authorship: authorship::{Module, Call, Storage}, Indices: indices, - Balances: balances, + Balances: balances::{default, Error}, Staking: staking::{default, OfflineWorker}, Session: session::{Module, Call, Storage, Event, Config}, Democracy: democracy::{Module, Call, Storage, Config, Event}, diff --git a/srml/assets/src/lib.rs b/srml/assets/src/lib.rs index 19159bf60fba3..318ba3e519074 100644 --- a/srml/assets/src/lib.rs +++ b/srml/assets/src/lib.rs @@ -240,7 +240,7 @@ mod tests { use super::*; use runtime_io::with_externalities; - use srml_support::{impl_outer_origin, assert_ok, assert_noop, parameter_types}; + use srml_support::{impl_outer_origin, impl_outer_error, assert_ok, assert_noop, parameter_types}; use substrate_primitives::{H256, Blake2Hasher}; // The testing primitives are very useful for avoiding having to work with signatures // or public keys. `u64` is used as the `AccountId` and no `Signature`s are required. @@ -250,6 +250,10 @@ mod tests { pub enum Origin for Test {} } + impl_outer_error! { + pub enum Error for Test {} + } + // For testing the module, we construct most of a mock runtime. This means // first constructing a configuration type (`Test`) which `impl`s each of the // configuration traits of modules we want to use. @@ -268,6 +272,7 @@ mod tests { type Lookup = IdentityLookup; type Header = Header; type Event = (); + type Error = Error; type BlockHashCount = BlockHashCount; } impl Trait for Test { diff --git a/srml/aura/src/mock.rs b/srml/aura/src/mock.rs index 0cce522c76032..da3017fd414a6 100644 --- a/srml/aura/src/mock.rs +++ b/srml/aura/src/mock.rs @@ -22,7 +22,7 @@ use primitives::{ traits::IdentityLookup, testing::{Header, UintAuthorityId}, }; -use srml_support::{impl_outer_origin, parameter_types}; +use srml_support::{impl_outer_origin, impl_outer_error, parameter_types}; use runtime_io; use substrate_primitives::{H256, Blake2Hasher}; use crate::{Trait, Module, GenesisConfig}; @@ -31,6 +31,10 @@ impl_outer_origin!{ pub enum Origin for Test {} } +impl_outer_error! { + pub enum Error for Runtime {} +} + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. #[derive(Clone, PartialEq, Eq, Debug)] pub struct Test; @@ -50,6 +54,7 @@ impl system::Trait for Test { type Lookup = IdentityLookup; type Header = Header; type Event = (); + type Error = Error; type BlockHashCount = BlockHashCount; } diff --git a/srml/authorship/src/lib.rs b/srml/authorship/src/lib.rs index 758eeb285e197..7e38bfdc81210 100644 --- a/srml/authorship/src/lib.rs +++ b/srml/authorship/src/lib.rs @@ -326,12 +326,16 @@ mod tests { use primitives::traits::{BlakeTwo256, IdentityLookup}; use primitives::testing::Header; use primitives::generic::DigestItem; - use srml_support::{parameter_types, impl_outer_origin, ConsensusEngineId}; + use srml_support::{parameter_types, impl_outer_origin, impl_outer_error, ConsensusEngineId}; impl_outer_origin!{ pub enum Origin for Test {} } + impl_outer_error!{ + pub enum Error for Test {} + } + #[derive(Clone, Eq, PartialEq)] pub struct Test; @@ -349,6 +353,7 @@ mod tests { type Lookup = IdentityLookup; type Header = Header; type Event = (); + type Error = Error; type BlockHashCount = BlockHashCount; } diff --git a/srml/balances/src/lib.rs b/srml/balances/src/lib.rs index 30fb2a48e935c..bb1ccf1d7d8d0 100644 --- a/srml/balances/src/lib.rs +++ b/srml/balances/src/lib.rs @@ -152,7 +152,7 @@ use rstd::prelude::*; use rstd::{cmp, result, mem}; use parity_codec::{Codec, Encode, Decode}; -use srml_support::{StorageValue, StorageMap, Parameter, decl_event, decl_storage, decl_module}; +use srml_support::{StorageValue, StorageMap, Parameter, decl_event, decl_storage, decl_module, decl_error}; use srml_support::traits::{ UpdateBalanceOutcome, Currency, OnFreeBalanceZero, MakePayment, OnUnbalanced, WithdrawReason, WithdrawReasons, LockIdentifier, LockableCurrency, ExistenceRequirement, @@ -170,6 +170,8 @@ mod tests; pub use self::imbalances::{PositiveImbalance, NegativeImbalance}; +pub type DispatchResult = srml_support::dispatch::DispatchResult<>::Error>; + pub const DEFAULT_EXISTENTIAL_DEPOSIT: u32 = 0; pub const DEFAULT_TRANSFER_FEE: u32 = 0; pub const DEFAULT_CREATION_FEE: u32 = 0; @@ -233,6 +235,9 @@ pub trait Trait: system::Trait { /// The overarching event type. type Event: From> + Into<::Event>; + /// The overarching error type. + type Error: From + From + From<&'static str>; + /// The minimum amount required to keep an account open. type ExistentialDeposit: Get; @@ -274,6 +279,19 @@ decl_event!( } ); +decl_error! { + pub enum Error { + } +} + +impl From for &'static str { + fn from(err: Error) -> &'static str { + match err { + Error::Other(msg) => msg, + } + } +} + /// Struct to encode the vesting schedule of an individual account. #[derive(Encode, Decode, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "std", derive(Debug))] @@ -386,6 +404,8 @@ decl_storage! { decl_module! { pub struct Module, I: Instance = DefaultInstance> for enum Call where origin: T::Origin { + type Error = >::Error; + /// The minimum amount required to keep an account open. const ExistentialDeposit: T::Balance = T::ExistentialDeposit::get(); @@ -760,6 +780,7 @@ impl, I: Instance> system::Trait for ElevatedTrait { type Lookup = T::Lookup; type Header = T::Header; type Event = (); + type Error = &'static str; type BlockHashCount = T::BlockHashCount; } impl, I: Instance> Trait for ElevatedTrait { @@ -770,6 +791,7 @@ impl, I: Instance> Trait for ElevatedTrait { type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); + type Error = &'static str; type ExistentialDeposit = T::ExistentialDeposit; type TransferFee = T::TransferFee; type CreationFee = T::CreationFee; diff --git a/srml/balances/src/mock.rs b/srml/balances/src/mock.rs index 38dae9f25f6f5..77eec99d9ce6b 100644 --- a/srml/balances/src/mock.rs +++ b/srml/balances/src/mock.rs @@ -21,7 +21,7 @@ use primitives::{traits::{IdentityLookup}, testing::Header}; use substrate_primitives::{H256, Blake2Hasher}; use runtime_io; -use srml_support::{impl_outer_origin, parameter_types, traits::Get}; +use srml_support::{impl_outer_origin, impl_outer_error, parameter_types, traits::Get}; use std::cell::RefCell; use crate::{GenesisConfig, Module, Trait}; @@ -29,6 +29,16 @@ impl_outer_origin!{ pub enum Origin for Runtime {} } +mod balances { + pub use crate::Error; +} + +impl_outer_error! { + pub enum Error for Runtime { + balances, + } +} + thread_local! { static EXISTENTIAL_DEPOSIT: RefCell = RefCell::new(0); static TRANSFER_FEE: RefCell = RefCell::new(0); @@ -78,6 +88,7 @@ impl system::Trait for Runtime { type Lookup = IdentityLookup; type Header = Header; type Event = (); + type Error = Error; type BlockHashCount = BlockHashCount; } impl Trait for Runtime { @@ -88,6 +99,7 @@ impl Trait for Runtime { type TransactionPayment = (); type DustRemoval = (); type TransferPayment = (); + type Error = Error; type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; diff --git a/srml/balances/src/tests.rs b/srml/balances/src/tests.rs index 2d53ae510cc9a..cf2b5831055ab 100644 --- a/srml/balances/src/tests.rs +++ b/srml/balances/src/tests.rs @@ -215,7 +215,7 @@ fn default_indexing_on_new_accounts_should_not_work2() { // ext_deposit is 10, value is 9, not satisfies for ext_deposit assert_noop!( Balances::transfer(Some(1).into(), 5, 9), - "value too low to create account" + mock::Error::system(system::Error::Other("value too low to create account")) ); assert_eq!(Balances::is_dead_account(&5), true); // account 5 should not exist assert_eq!(Balances::free_balance(&1), 100); @@ -358,7 +358,10 @@ fn balance_transfer_when_reserved_should_not_work() { with_externalities(&mut ExtBuilder::default().build(), || { let _ = Balances::deposit_creating(&1, 111); assert_ok!(Balances::reserve(&1, 69)); - assert_noop!(Balances::transfer(Some(1).into(), 2, 69), "balance too low to send value"); + assert_noop!( + Balances::transfer(Some(1).into(), 2, 69), + mock::Error::system(system::Error::Other("balance too low to send value")) + ); }); } @@ -486,7 +489,7 @@ fn transferring_too_high_value_should_not_panic() { assert_err!( Balances::transfer(Some(1).into(), 2, u64::max_value()), - "destination balance too high to receive value" + mock::Error::system(system::Error::Other("destination balance too high to receive value")) ); assert_eq!(Balances::free_balance(&1), u64::max_value()); @@ -564,7 +567,7 @@ fn transfer_overflow_isnt_exploitable() { assert_err!( Balances::transfer(Some(1).into(), 5, evil_value), - "got overflow after adding a fee to value" + mock::Error::system(system::Error::Other("got overflow after adding a fee to value")) ); } ); @@ -616,7 +619,7 @@ fn check_vesting_status() { assert_eq!(System::block_number(), 10); // Account 1 has fully vested by block 10 - assert_eq!(Balances::vesting_balance(&1), 0); + assert_eq!(Balances::vesting_balance(&1), 0); // Account 2 has started vesting by block 10 assert_eq!(Balances::vesting_balance(&2), user2_free_balance); // Account 12 has started vesting by block 10 @@ -649,7 +652,7 @@ fn unvested_balance_should_not_transfer() { assert_eq!(Balances::vesting_balance(&1), 45); assert_noop!( Balances::transfer(Some(1).into(), 2, 56), - "vesting balance too high to send value" + mock::Error::system(system::Error::Other("vesting balance too high to send value")) ); // Account 1 cannot send more than vested amount } ); diff --git a/srml/collective/src/lib.rs b/srml/collective/src/lib.rs index 5f2a0ef2815da..914bdbb253062 100644 --- a/srml/collective/src/lib.rs +++ b/srml/collective/src/lib.rs @@ -322,6 +322,7 @@ impl< I, > EnsureOrigin for EnsureMember { type Success = AccountId; + type Error = &'static str; fn try_origin(o: O) -> Result { o.into().and_then(|o| match o { RawOrigin::Member(id) => Ok(id), @@ -338,6 +339,7 @@ impl< I, > EnsureOrigin for EnsureMembers { type Success = (MemberCount, MemberCount); + type Error = &'static str; fn try_origin(o: O) -> Result { o.into().and_then(|o| match o { RawOrigin::Members(n, m) if n >= N::VALUE => Ok((n, m)), @@ -357,6 +359,7 @@ impl< I, > EnsureOrigin for EnsureProportionMoreThan { type Success = (); + type Error = &'static str; fn try_origin(o: O) -> Result { o.into().and_then(|o| match o { RawOrigin::Members(n, m) if n * D::VALUE > N::VALUE * m => Ok(()), @@ -376,6 +379,7 @@ impl< I, > EnsureOrigin for EnsureProportionAtLeast { type Success = (); + type Error = &'static str; fn try_origin(o: O) -> Result { o.into().and_then(|o| match o { RawOrigin::Members(n, m) if n * D::VALUE >= N::VALUE * m => Ok(()), @@ -410,6 +414,7 @@ mod tests { type Lookup = IdentityLookup; type Header = Header; type Event = Event; + type Error = Error; type BlockHashCount = BlockHashCount; } impl Trait for Test { diff --git a/srml/contracts/src/gas.rs b/srml/contracts/src/gas.rs index 44d5b32fd938b..7c350f73818ea 100644 --- a/srml/contracts/src/gas.rs +++ b/srml/contracts/src/gas.rs @@ -16,7 +16,7 @@ use crate::{GasSpent, Module, Trait, BalanceOf, NegativeImbalanceOf}; use rstd::convert::TryFrom; -use runtime_primitives::BLOCK_FULL; +use runtime_primitives::PrimitiveError; use runtime_primitives::traits::{CheckedMul, Zero, SaturatedConversion, SimpleArithmetic, UniqueSaturatedInto}; use srml_support::StorageValue; use srml_support::traits::{Currency, ExistenceRequirement, Get, Imbalance, OnUnbalanced, WithdrawReason}; @@ -205,7 +205,7 @@ pub fn buy_gas( let gas_available = T::BlockGasLimit::get() - >::gas_spent(); if gas_limit > gas_available { // gas limit reached, revert the transaction and retry again in the future - return Err(BLOCK_FULL); + return Err(PrimitiveError::BlockFull.into()); } // Buy the specified amount of gas. diff --git a/srml/contracts/src/tests.rs b/srml/contracts/src/tests.rs index 3e63bc0defc89..db005197f194e 100644 --- a/srml/contracts/src/tests.rs +++ b/srml/contracts/src/tests.rs @@ -34,7 +34,7 @@ use runtime_primitives::traits::{BlakeTwo256, Hash, IdentityLookup}; use runtime_primitives::BuildStorage; use srml_support::{ assert_ok, assert_err, impl_outer_dispatch, impl_outer_event, impl_outer_origin, parameter_types, - storage::child, StorageMap, StorageValue, traits::{Currency, Get}, + impl_outer_error, storage::child, StorageMap, StorageValue, traits::{Currency, Get}, }; use std::cell::RefCell; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -60,11 +60,19 @@ impl_outer_origin! { } impl_outer_dispatch! { pub enum Call for Test where origin: Origin { + type Error = Error; + balances::Balances, contract::Contract, } } +impl_outer_error! { + pub enum Error for Test { + balances + } +} + thread_local! { static EXISTENTIAL_DEPOSIT: RefCell = RefCell::new(0); static TRANSFER_FEE: RefCell = RefCell::new(0); @@ -107,6 +115,7 @@ impl system::Trait for Test { type Lookup = IdentityLookup; type Header = Header; type Event = MetaEvent; + type Error = Error; type BlockHashCount = BlockHashCount; } parameter_types! { @@ -121,6 +130,7 @@ impl balances::Trait for Test { type TransactionPayment = (); type DustRemoval = (); type TransferPayment = (); + type Error = Error; type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; diff --git a/srml/council/src/lib.rs b/srml/council/src/lib.rs index 8a52f9ec004a9..2fcdb22d81c02 100644 --- a/srml/council/src/lib.rs +++ b/srml/council/src/lib.rs @@ -39,7 +39,7 @@ mod tests { // These re-exports are here for a reason, edit with care pub use super::*; pub use runtime_io::with_externalities; - use srml_support::{impl_outer_origin, impl_outer_event, impl_outer_dispatch, parameter_types}; + use srml_support::{impl_outer_origin, impl_outer_event, impl_outer_dispatch, impl_outer_error, parameter_types}; use srml_support::traits::Get; pub use substrate_primitives::{H256, Blake2Hasher, u32_trait::{_1, _2, _3, _4}}; pub use primitives::traits::{BlakeTwo256, IdentityLookup}; @@ -61,11 +61,19 @@ mod tests { impl_outer_dispatch! { pub enum Call for Test where origin: Origin { + type Error = Error; + balances::Balances, democracy::Democracy, } } + impl_outer_error! { + pub enum Error for Test { + balances + } + } + thread_local! { static VOTER_BOND: RefCell = RefCell::new(0); static VOTING_FEE: RefCell = RefCell::new(0); @@ -109,6 +117,7 @@ mod tests { type Lookup = IdentityLookup; type Header = Header; type Event = Event; + type Error = Error; type BlockHashCount = BlockHashCount; } parameter_types! { @@ -126,6 +135,7 @@ mod tests { type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); + type Error = Error; type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; @@ -148,11 +158,11 @@ mod tests { type EmergencyVotingPeriod = VotingPeriod; type VotingPeriod = VotingPeriod; type MinimumDeposit = MinimumDeposit; - type ExternalOrigin = motions::EnsureProportionAtLeast<_1, _2, u64>; - type ExternalMajorityOrigin = motions::EnsureProportionAtLeast<_2, _3, u64>; - type EmergencyOrigin = motions::EnsureProportionAtLeast<_1, _1, u64>; - type CancellationOrigin = motions::EnsureProportionAtLeast<_2, _3, u64>; - type VetoOrigin = motions::EnsureMember; + type ExternalOrigin = motions::EnsureProportionAtLeast<_1, _2, u64, &'static str>; + type ExternalMajorityOrigin = motions::EnsureProportionAtLeast<_2, _3, u64, &'static str>; + type EmergencyOrigin = motions::EnsureProportionAtLeast<_1, _1, u64, &'static str>; + type CancellationOrigin = motions::EnsureProportionAtLeast<_2, _3, u64, &'static str>; + type VetoOrigin = motions::EnsureMember; type CooloffPeriod = CooloffPeriod; } parameter_types! { diff --git a/srml/democracy/src/lib.rs b/srml/democracy/src/lib.rs index d13feb4db531d..c856f34ff2f6f 100644 --- a/srml/democracy/src/lib.rs +++ b/srml/democracy/src/lib.rs @@ -164,6 +164,7 @@ impl Decode for Vote { } type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; +type Error = &'static str; pub const DEFAULT_ENACTMENT_PERIOD: u32 = 0; pub const DEFAULT_LAUNCH_PERIOD: u32 = 0; @@ -198,28 +199,28 @@ pub trait Trait: system::Trait + Sized { /// Origin from which the next tabled referendum may be forced. This is a normal /// "super-majority-required" referendum. - type ExternalOrigin: EnsureOrigin; + type ExternalOrigin: EnsureOrigin; /// Origin from which the next tabled referendum may be forced; this allows for the tabling of /// a majority-carries referendum. - type ExternalMajorityOrigin: EnsureOrigin; + type ExternalMajorityOrigin: EnsureOrigin; /// Origin from which the next referendum proposed by the external majority may be immediately /// tabled to vote asynchronously in a similar manner to the emergency origin. It remains a /// majority-carries vote. - type ExternalPushOrigin: EnsureOrigin; + type ExternalPushOrigin: EnsureOrigin; /// Origin from which emergency referenda may be scheduled. - type EmergencyOrigin: EnsureOrigin; + type EmergencyOrigin: EnsureOrigin; /// Minimum voting period allowed for an emergency referendum. type EmergencyVotingPeriod: Get; /// Origin from which any referenda may be cancelled in an emergency. - type CancellationOrigin: EnsureOrigin; + type CancellationOrigin: EnsureOrigin; /// Origin for anyone able to veto proposals. - type VetoOrigin: EnsureOrigin; + type VetoOrigin: EnsureOrigin; /// Period in blocks where an external proposal may not be re-submitted after being vetoed. type CooloffPeriod: Get; @@ -969,7 +970,7 @@ mod tests { use super::*; use runtime_io::with_externalities; use srml_support::{ - impl_outer_origin, impl_outer_dispatch, assert_noop, assert_ok, parameter_types, + impl_outer_origin, impl_outer_error, impl_outer_dispatch, assert_noop, assert_ok, parameter_types, traits::Contains }; use substrate_primitives::{H256, Blake2Hasher}; @@ -988,11 +989,19 @@ mod tests { impl_outer_dispatch! { pub enum Call for Test where origin: Origin { + type Error = Error; + balances::Balances, democracy::Democracy, } } + impl_outer_error! { + pub enum Error for Test { + balances, + } + } + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. #[derive(Clone, Eq, PartialEq, Debug)] pub struct Test; @@ -1009,6 +1018,7 @@ mod tests { type Lookup = IdentityLookup; type Header = Header; type Event = (); + type Error = Error; type BlockHashCount = BlockHashCount; } parameter_types! { @@ -1026,6 +1036,7 @@ mod tests { type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); + type Error = Error; type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; diff --git a/srml/elections/src/lib.rs b/srml/elections/src/lib.rs index 80324ecefbdaf..92ccc3c3374b8 100644 --- a/srml/elections/src/lib.rs +++ b/srml/elections/src/lib.rs @@ -1119,6 +1119,7 @@ mod tests { type Lookup = IdentityLookup; type Header = Header; type Event = Event; + type Error = Error; type BlockHashCount = BlockHashCount; } parameter_types! { @@ -1133,6 +1134,7 @@ mod tests { type OnNewAccount = (); type OnFreeBalanceZero = (); type Event = Event; + type Error = Error; type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); @@ -1220,7 +1222,7 @@ mod tests { UncheckedExtrinsic = UncheckedExtrinsic { System: system::{Module, Call, Event}, - Balances: balances::{Module, Call, Event, Config}, + Balances: balances::{Module, Call, Event, Config, Error}, Elections: elections::{Module, Call, Event, Config}, } ); diff --git a/srml/example/src/lib.rs b/srml/example/src/lib.rs index 20ee1c6ba114f..2f840657c3397 100644 --- a/srml/example/src/lib.rs +++ b/srml/example/src/lib.rs @@ -505,7 +505,7 @@ impl Module { mod tests { use super::*; - use srml_support::{assert_ok, impl_outer_origin, parameter_types}; + use srml_support::{assert_ok, impl_outer_origin, impl_outer_error, parameter_types}; use sr_io::with_externalities; use substrate_primitives::{H256, Blake2Hasher}; // The testing primitives are very useful for avoiding having to work with signatures @@ -518,6 +518,12 @@ mod tests { pub enum Origin for Test {} } + impl_outer_error! { + pub enum Error for Test { + balances + } + } + // For testing the module, we construct most of a mock runtime. This means // first constructing a configuration type (`Test`) which `impl`s each of the // configuration traits of modules we want to use. @@ -536,6 +542,7 @@ mod tests { type Lookup = IdentityLookup; type Header = Header; type Event = (); + type Error = Error; type BlockHashCount = BlockHashCount; } parameter_types! { @@ -553,6 +560,7 @@ mod tests { type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); + type Error = Error; type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; diff --git a/srml/executive/src/lib.rs b/srml/executive/src/lib.rs index 49d4addb3bc2b..970d65b6dfdd5 100644 --- a/srml/executive/src/lib.rs +++ b/srml/executive/src/lib.rs @@ -76,34 +76,23 @@ use rstd::prelude::*; use rstd::marker::PhantomData; -use rstd::result; -use primitives::{generic::Digest, traits::{ - self, Header, Zero, One, Checkable, Applyable, CheckEqual, OnFinalize, - OnInitialize, NumberFor, Block as BlockT, OffchainWorker, - ValidateUnsigned, -}}; +use rstd::convert::TryInto; +use primitives::{ + generic::Digest, ApplyResult, ApplyOutcome, ApplyError, DispatchError, PrimitiveError, + traits::{ + self, Header, Zero, One, Checkable, Applyable, CheckEqual, OnFinalize, + OnInitialize, NumberFor, Block as BlockT, OffchainWorker, + ValidateUnsigned, + } +}; use srml_support::{Dispatchable, traits::MakePayment}; use parity_codec::{Codec, Encode}; use system::{extrinsics_root, DigestOf}; -use primitives::{ApplyOutcome, ApplyError}; use primitives::transaction_validity::{TransactionValidity, TransactionPriority, TransactionLongevity}; use primitives::weights::Weighable; mod internal { pub const MAX_TRANSACTIONS_WEIGHT: u32 = 4 * 1024 * 1024; - - pub enum ApplyError { - BadSignature(&'static str), - Stale, - Future, - CantPay, - FullBlock, - } - - pub enum ApplyOutcome { - Success, - Fail(&'static str), - } } /// Trait that can be used to execute a block. @@ -130,8 +119,10 @@ impl< > ExecuteBlock for Executive where Block::Extrinsic: Checkable + Codec, + >::Error: Into, CheckedOf: Applyable + Weighable, CallOf: Dispatchable, + as Dispatchable>::Error: Into + TryInto, OriginOf: From>, UnsignedValidator: ValidateUnsigned>, { @@ -150,8 +141,10 @@ impl< > Executive where Block::Extrinsic: Checkable + Codec, + >::Error: Into, CheckedOf: Applyable + Weighable, CallOf: Dispatchable, + as Dispatchable>::Error: Into + TryInto, OriginOf: From>, UnsignedValidator: ValidateUnsigned>, { @@ -228,30 +221,29 @@ where /// Apply extrinsic outside of the block execution function. /// This doesn't attempt to validate anything regarding the block, but it builds a list of uxt /// hashes. - pub fn apply_extrinsic(uxt: Block::Extrinsic) -> result::Result { + pub fn apply_extrinsic(uxt: Block::Extrinsic) -> ApplyResult { let encoded = uxt.encode(); let encoded_len = encoded.len(); - match Self::apply_extrinsic_with_len(uxt, encoded_len, Some(encoded)) { - Ok(internal::ApplyOutcome::Success) => Ok(ApplyOutcome::Success), - Ok(internal::ApplyOutcome::Fail(_)) => Ok(ApplyOutcome::Fail), - Err(internal::ApplyError::CantPay) => Err(ApplyError::CantPay), - Err(internal::ApplyError::BadSignature(_)) => Err(ApplyError::BadSignature), - Err(internal::ApplyError::Stale) => Err(ApplyError::Stale), - Err(internal::ApplyError::Future) => Err(ApplyError::Future), - Err(internal::ApplyError::FullBlock) => Err(ApplyError::FullBlock), - } + Self::apply_extrinsic_with_len(uxt, encoded_len, Some(encoded)) } /// Apply an extrinsic inside the block execution function. fn apply_extrinsic_no_note(uxt: Block::Extrinsic) { let l = uxt.encode().len(); match Self::apply_extrinsic_with_len(uxt, l, None) { - Ok(internal::ApplyOutcome::Success) => (), - Ok(internal::ApplyOutcome::Fail(e)) => runtime_io::print(e), - Err(internal::ApplyError::CantPay) => panic!("All extrinsics should have sender able to pay their fees"), - Err(internal::ApplyError::BadSignature(_)) => panic!("All extrinsics should be properly signed"), - Err(internal::ApplyError::Stale) | Err(internal::ApplyError::Future) => panic!("All extrinsics should have the correct nonce"), - Err(internal::ApplyError::FullBlock) => panic!("Extrinsics should not exceed block limit"), + Ok(ApplyOutcome::Success) => (), + Ok(ApplyOutcome::Fail(e)) => { + runtime_io::print("Error:"); + runtime_io::print(e.module as u64); + runtime_io::print(e.error as u64); + if let Some(msg) = e.message { + runtime_io::print(msg); + } + }, + Err(ApplyError::CantPay) => panic!("All extrinsics should have sender able to pay their fees"), + Err(ApplyError::BadSignature) => panic!("All extrinsics should be properly signed"), + Err(ApplyError::Stale) | Err(ApplyError::Future) => panic!("All extrinsics should have the correct nonce"), + Err(ApplyError::FullBlock) => panic!("Extrinsics should not exceed block limit"), } } @@ -260,24 +252,26 @@ where uxt: Block::Extrinsic, encoded_len: usize, to_note: Option>, - ) -> result::Result { + ) -> ApplyResult { // Verify that the signature is good. - let xt = uxt.check(&Default::default()).map_err(internal::ApplyError::BadSignature)?; - + let xt = uxt.check(&Default::default()).map_err(|_| ApplyError::BadSignature)?; // Check the weight of the block if that extrinsic is applied. let weight = xt.weight(encoded_len); if >::all_extrinsics_weight() + weight > internal::MAX_TRANSACTIONS_WEIGHT { - return Err(internal::ApplyError::FullBlock); + return Err(ApplyError::FullBlock); } if let (Some(sender), Some(index)) = (xt.sender(), xt.index()) { // check index let expected_index = >::account_nonce(sender); - if index != &expected_index { return Err( - if index < &expected_index { internal::ApplyError::Stale } else { internal::ApplyError::Future } - ) } + if index < &expected_index { + return Err(ApplyError::Stale) + } else if index > &expected_index { + return Err(ApplyError::Future) + } // pay any fees - Payment::make_payment(sender, encoded_len).map_err(|_| internal::ApplyError::CantPay)?; + // TODO: propagate why can't pay #2952 + Payment::make_payment(sender, encoded_len).map_err(|_| ApplyError::CantPay)?; // AUDIT: Under no circumstances may this function panic from here onwards. // FIXME: ensure this at compile-time (such as by not defining a panic function, forcing @@ -294,12 +288,12 @@ where // Decode parameters and dispatch let (f, s) = xt.deconstruct(); - let r = f.dispatch(s.into()); + let r = f.dispatch(s.into()).map_err(Into::::into); >::note_applied_extrinsic(&r, encoded_len as u32); - r.map(|_| internal::ApplyOutcome::Success).or_else(|e| match e { - primitives::BLOCK_FULL => Err(internal::ApplyError::FullBlock), - e => Ok(internal::ApplyOutcome::Fail(e)) + Ok(match r { + Ok(_) => ApplyOutcome::Success, + Err(e) => ApplyOutcome::Fail(e), }) } @@ -340,12 +334,17 @@ where let xt = match uxt.check(&Default::default()) { // Checks out. Carry on. Ok(xt) => xt, - // An unknown account index implies that the transaction may yet become valid. - Err("invalid account index") => return TransactionValidity::Unknown(INVALID_INDEX), - // Technically a bad signature could also imply an out-of-date account index, but - // that's more of an edge case. - Err(primitives::BAD_SIGNATURE) => return TransactionValidity::Invalid(ApplyError::BadSignature as i8), - Err(_) => return TransactionValidity::Invalid(UNKNOWN_ERROR), + Err(err) => return match err.into() { + // An unknown account index implies that the transaction may yet become valid. + // TODO: avoid hardcoded error string here #2953 + PrimitiveError::Other("invalid account index") => + TransactionValidity::Unknown(INVALID_INDEX), + // Technically a bad signature could also imply an out-of-date account index, but + // that's more of an edge case. + PrimitiveError::BadSignature => + TransactionValidity::Invalid(ApplyError::BadSignature as i8), + _ => TransactionValidity::Invalid(UNKNOWN_ERROR), + } }; match (xt.sender(), xt.index()) { @@ -397,7 +396,7 @@ mod tests { use substrate_primitives::{H256, Blake2Hasher}; use primitives::traits::{Header as HeaderT, BlakeTwo256, IdentityLookup}; use primitives::testing::{Digest, Header, Block}; - use srml_support::{impl_outer_event, impl_outer_origin, parameter_types}; + use srml_support::{impl_outer_event, impl_outer_origin, impl_outer_error, parameter_types}; use srml_support::traits::{Currency, LockIdentifier, LockableCurrency, WithdrawReasons, WithdrawReason}; use system; use hex_literal::hex; @@ -413,6 +412,12 @@ mod tests { } } + impl_outer_error! { + pub enum Error for Runtime { + balances + } + } + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. #[derive(Clone, Eq, PartialEq)] pub struct Runtime; @@ -429,6 +434,7 @@ mod tests { type Lookup = IdentityLookup; type Header = Header; type Event = MetaEvent; + type Error = Error; type BlockHashCount = BlockHashCount; } parameter_types! { @@ -446,6 +452,7 @@ mod tests { type TransactionPayment = (); type DustRemoval = (); type TransferPayment = (); + type Error = Error; type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; @@ -645,7 +652,10 @@ mod tests { with_externalities(&mut t, || { assert_eq!(Executive::validate_transaction(xt.clone()), valid); - assert_eq!(Executive::apply_extrinsic(xt), Ok(ApplyOutcome::Fail)); + assert_eq!( + Executive::apply_extrinsic(xt), + Ok(ApplyOutcome::Fail(DispatchError { module: 0, error: 4 /*RequireNoOrigin*/ , message: None })) + ); }); } @@ -672,7 +682,11 @@ mod tests { )); if lock == WithdrawReasons::except(WithdrawReason::TransactionPayment) { - assert_eq!(Executive::apply_extrinsic(xt).unwrap(), ApplyOutcome::Fail); + assert_eq!(Executive::apply_extrinsic(xt).unwrap(), ApplyOutcome::Fail(DispatchError { + module: 0, + error: 0, + message: Some("account liquidity restrictions prevent withdrawal") + })); // but tx fee has been deducted. the transaction failed on transfer, not on fee. assert_eq!(>::total_balance(&1), 111 - 10); } else { diff --git a/srml/finality-tracker/src/lib.rs b/srml/finality-tracker/src/lib.rs index f9ccc36346222..465734f9c0d9d 100644 --- a/srml/finality-tracker/src/lib.rs +++ b/srml/finality-tracker/src/lib.rs @@ -268,7 +268,7 @@ mod tests { use substrate_primitives::H256; use primitives::traits::{BlakeTwo256, IdentityLookup, OnFinalize, Header as HeaderT}; use primitives::testing::Header; - use srml_support::{assert_ok, impl_outer_origin, parameter_types}; + use srml_support::{assert_ok, impl_outer_origin, impl_outer_error, parameter_types}; use srml_system as system; use std::cell::RefCell; @@ -285,6 +285,10 @@ mod tests { pub enum Origin for Test {} } + impl_outer_error! { + pub enum Error for Test {} + } + thread_local! { static NOTIFICATIONS: RefCell> = Default::default(); } @@ -310,6 +314,7 @@ mod tests { type Lookup = IdentityLookup; type Header = Header; type Event = (); + type Error = Error; type BlockHashCount = BlockHashCount; } parameter_types! { diff --git a/srml/generic-asset/src/lib.rs b/srml/generic-asset/src/lib.rs index 60370600a69a6..6cab7c4935033 100644 --- a/srml/generic-asset/src/lib.rs +++ b/srml/generic-asset/src/lib.rs @@ -1056,6 +1056,7 @@ impl system::Trait for ElevatedTrait { type Lookup = T::Lookup; type Header = T::Header; type Event = (); + type Error = &'static str; type BlockHashCount = T::BlockHashCount; } impl Trait for ElevatedTrait { diff --git a/srml/generic-asset/src/mock.rs b/srml/generic-asset/src/mock.rs index 02e18fc335839..70d6273fddfa8 100644 --- a/srml/generic-asset/src/mock.rs +++ b/srml/generic-asset/src/mock.rs @@ -25,7 +25,7 @@ use primitives::{ traits::{BlakeTwo256, IdentityLookup}, }; use substrate_primitives::{Blake2Hasher, H256}; -use support::{parameter_types, impl_outer_event, impl_outer_origin}; +use support::{parameter_types, impl_outer_event, impl_outer_origin, impl_outer_error}; use super::*; @@ -33,6 +33,10 @@ impl_outer_origin! { pub enum Origin for Test {} } +impl_outer_error! { + pub enum Error for Test {} +} + // For testing the module, we construct most of a mock runtime. This means // first constructing a configuration type (`Test`) which `impl`s each of the // configuration traits of modules we want to use. @@ -51,6 +55,7 @@ impl system::Trait for Test { type Lookup = IdentityLookup; type Header = Header; type Event = TestEvent; + type Error = Error; type BlockHashCount = BlockHashCount; } diff --git a/srml/grandpa/src/mock.rs b/srml/grandpa/src/mock.rs index 733b2deaf1493..30b34da75451c 100644 --- a/srml/grandpa/src/mock.rs +++ b/srml/grandpa/src/mock.rs @@ -20,7 +20,7 @@ use primitives::{DigestItem, traits::IdentityLookup, testing::{Header, UintAuthorityId}}; use runtime_io; -use srml_support::{impl_outer_origin, impl_outer_event, parameter_types}; +use srml_support::{impl_outer_origin, impl_outer_event, impl_outer_error, parameter_types}; use substrate_primitives::{H256, Blake2Hasher}; use parity_codec::{Encode, Decode}; use crate::{AuthorityId, GenesisConfig, Trait, Module, ConsensusLog}; @@ -30,6 +30,10 @@ impl_outer_origin!{ pub enum Origin for Test {} } +impl_outer_error! { + pub enum Error for Test {} +} + pub fn grandpa_log(log: ConsensusLog) -> DigestItem { DigestItem::Consensus(GRANDPA_ENGINE_ID, log.encode()) } @@ -37,9 +41,9 @@ pub fn grandpa_log(log: ConsensusLog) -> DigestItem { // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. #[derive(Clone, PartialEq, Eq, Debug, Decode, Encode)] pub struct Test; + impl Trait for Test { type Event = TestEvent; - } parameter_types! { pub const BlockHashCount: u64 = 250; @@ -54,6 +58,7 @@ impl system::Trait for Test { type Lookup = IdentityLookup; type Header = Header; type Event = TestEvent; + type Error = Error; type BlockHashCount = BlockHashCount; } diff --git a/srml/indices/src/lib.rs b/srml/indices/src/lib.rs index 45487e3b51cd1..cbb2079fc824f 100644 --- a/srml/indices/src/lib.rs +++ b/srml/indices/src/lib.rs @@ -224,6 +224,8 @@ impl OnNewAccount for Module { impl StaticLookup for Module { type Source = address::Address; type Target = T::AccountId; + type Error = &'static str; + fn lookup(a: Self::Source) -> result::Result { Self::lookup_address(a).ok_or("invalid account index") } diff --git a/srml/indices/src/mock.rs b/srml/indices/src/mock.rs index 53e8f314c94bb..5f61cec5481fc 100644 --- a/srml/indices/src/mock.rs +++ b/srml/indices/src/mock.rs @@ -22,7 +22,7 @@ use std::collections::HashSet; use ref_thread_local::{ref_thread_local, RefThreadLocal}; use primitives::testing::Header; use substrate_primitives::{H256, Blake2Hasher}; -use srml_support::{impl_outer_origin, parameter_types}; +use srml_support::{impl_outer_origin, impl_outer_error, parameter_types}; use {runtime_io, system}; use crate::{GenesisConfig, Module, Trait, IsDeadAccount, OnNewAccount, ResolveHint}; @@ -30,6 +30,10 @@ impl_outer_origin!{ pub enum Origin for Runtime {} } +impl_outer_error! { + pub enum Error for Runtime {} +} + ref_thread_local! { static managed ALIVE: HashSet = HashSet::new(); } @@ -77,6 +81,7 @@ impl system::Trait for Runtime { type Lookup = Indices; type Header = Header; type Event = (); + type Error = Error; type BlockHashCount = BlockHashCount; } impl Trait for Runtime { diff --git a/srml/session/src/mock.rs b/srml/session/src/mock.rs index 13d824c807f01..39f44346cd68d 100644 --- a/srml/session/src/mock.rs +++ b/srml/session/src/mock.rs @@ -18,7 +18,7 @@ use super::*; use std::cell::RefCell; -use srml_support::{impl_outer_origin, parameter_types}; +use srml_support::{impl_outer_origin, impl_outer_error, parameter_types}; use substrate_primitives::H256; use primitives::{ traits::{BlakeTwo256, IdentityLookup, ConvertInto}, @@ -30,6 +30,10 @@ impl_outer_origin! { pub enum Origin for Test {} } +impl_outer_error! { + pub enum Error for Test {} +} + thread_local! { pub static NEXT_VALIDATORS: RefCell> = RefCell::new(vec![1, 2, 3]); pub static AUTHORITIES: RefCell> = @@ -121,6 +125,7 @@ impl system::Trait for Test { type Lookup = IdentityLookup; type Header = Header; type Event = (); + type Error = Error; type BlockHashCount = BlockHashCount; } impl timestamp::Trait for Test { diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index 7dfe7b60e9f03..c279619e8ca47 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -854,7 +854,8 @@ decl_module! { let targets = targets.into_iter() .take(MAX_NOMINATIONS) .map(T::Lookup::lookup) - .collect::, &'static str>>()?; + .collect::, _>>() + ?; >::remove(stash); >::insert(stash, targets); diff --git a/srml/staking/src/mock.rs b/srml/staking/src/mock.rs index 246b6f96be7f3..7d84cf4b4286a 100644 --- a/srml/staking/src/mock.rs +++ b/srml/staking/src/mock.rs @@ -22,7 +22,7 @@ use primitives::traits::{IdentityLookup, Convert, OpaqueKeys, OnInitialize}; use primitives::testing::{Header, UintAuthorityId}; use substrate_primitives::{H256, Blake2Hasher}; use runtime_io; -use srml_support::{assert_ok, impl_outer_origin, parameter_types, EnumerableStorageMap}; +use srml_support::{assert_ok, impl_outer_origin, impl_outer_error, parameter_types, EnumerableStorageMap}; use srml_support::traits::{Currency, Get}; use crate::{EraIndex, GenesisConfig, Module, Trait, StakerStatus, ValidatorPrefs, RewardDestination, Nominators @@ -82,6 +82,12 @@ impl_outer_origin!{ pub enum Origin for Test {} } +impl_outer_error! { + pub enum Error for Test { + balances + } +} + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. #[derive(Clone, PartialEq, Eq, Debug)] pub struct Test; @@ -98,6 +104,7 @@ impl system::Trait for Test { type Lookup = IdentityLookup; type Header = Header; type Event = (); + type Error = Error; type BlockHashCount = BlockHashCount; } parameter_types! { @@ -114,6 +121,7 @@ impl balances::Trait for Test { type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); + type Error = Error; type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; diff --git a/srml/staking/src/tests.rs b/srml/staking/src/tests.rs index 5987d9800d963..0f39b560e51ba 100644 --- a/srml/staking/src/tests.rs +++ b/srml/staking/src/tests.rs @@ -889,7 +889,10 @@ fn cannot_transfer_staked_balance() { // Confirm account 11 (via controller 10) is totally staked assert_eq!(Staking::stakers(&11).total, 1000); // Confirm account 11 cannot transfer as a result - assert_noop!(Balances::transfer(Origin::signed(11), 20, 1), "account liquidity restrictions prevent withdrawal"); + assert_noop!( + Balances::transfer(Origin::signed(11), 20, 1), + mock::Error::system(system::Error::Other("account liquidity restrictions prevent withdrawal")) + ); // Give account 11 extra free balance let _ = Balances::make_free_balance_be(&11, 10000); @@ -915,7 +918,10 @@ fn cannot_transfer_staked_balance_2() { // Confirm account 21 (via controller 20) is totally staked assert_eq!(Staking::stakers(&21).total, 1000); // Confirm account 21 can transfer at most 1000 - assert_noop!(Balances::transfer(Origin::signed(21), 20, 1001), "account liquidity restrictions prevent withdrawal"); + assert_noop!( + Balances::transfer(Origin::signed(21), 20, 1001), + mock::Error::system(system::Error::Other("account liquidity restrictions prevent withdrawal")) + ); assert_ok!(Balances::transfer(Origin::signed(21), 20, 1000)); }); } diff --git a/srml/sudo/src/lib.rs b/srml/sudo/src/lib.rs index a421bdae68a52..90fedc2fe6f89 100644 --- a/srml/sudo/src/lib.rs +++ b/srml/sudo/src/lib.rs @@ -99,7 +99,7 @@ pub trait Trait: system::Trait { type Event: From> + Into<::Event>; /// A sudo-able call. - type Proposal: Parameter + Dispatchable; + type Proposal: Parameter + Dispatchable::Error>; } decl_module! { diff --git a/srml/support/src/dispatch.rs b/srml/support/src/dispatch.rs index f990cbd8d5a1e..ac07ab51d49ef 100644 --- a/srml/support/src/dispatch.rs +++ b/srml/support/src/dispatch.rs @@ -31,8 +31,11 @@ pub use sr_primitives::weights::{TransactionWeight, Weighable, Weight}; pub enum Never {} /// Result of a module function call; either nothing (functions are only called for "side effects") -/// or an error message. -pub type Result = result::Result<(), &'static str>; +/// or an error. +pub type DispatchResult = result::Result<(), Error>; + +/// Result with string error message. This exists for backward compatibility purpose. +pub type Result = DispatchResult<&'static str>; /// A lazy call (module function and argument values) that can be executed via its `dispatch` /// method. @@ -42,7 +45,8 @@ pub trait Dispatchable { /// identifier for the caller. The origin can be empty in the case of an inherent extrinsic. type Origin; type Trait; - fn dispatch(self, origin: Self::Origin) -> Result; + type Error; + fn dispatch(self, origin: Self::Origin) -> DispatchResult; } /// Serializable version of Dispatchable. @@ -243,6 +247,7 @@ macro_rules! decl_module { {} {} {} + {} [] $($t)* ); @@ -274,6 +279,7 @@ macro_rules! decl_module { {} {} {} + {} [] $($t)* ); @@ -290,6 +296,7 @@ macro_rules! decl_module { { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } + { $( $error_type:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $vis:vis fn deposit_event $(<$dpeg:ident $(, $dpeg_instance:ident)?>)* () = default; @@ -305,6 +312,7 @@ macro_rules! decl_module { { $( $on_finalize )* } { $( $offchain )* } { $( $constants )* } + { $( $error_type )* } [ $( $dispatchables )* ] $($rest)* ); @@ -319,6 +327,7 @@ macro_rules! decl_module { { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } + { $( $error_type:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $vis:vis fn deposit_event $(<$dpeg:ident $(, $dpeg_instance:ident)?>)* ( @@ -336,6 +345,7 @@ macro_rules! decl_module { { $( $on_finalize )* } { $( $offchain )* } { $( $constants )* } + { $( $error_type )* } [ $( $dispatchables )* ] $($rest)* ); @@ -350,6 +360,7 @@ macro_rules! decl_module { {} { $( $offchain:tt )* } { $( $constants:tt )* } + { $( $error_type:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn on_finalize($($param_name:ident : $param:ty),* ) { $( $impl:tt )* } @@ -365,6 +376,7 @@ macro_rules! decl_module { { fn on_finalize( $( $param_name : $param ),* ) { $( $impl )* } } { $( $offchain )* } { $( $constants )* } + { $( $error_type )* } [ $( $dispatchables )* ] $($rest)* ); @@ -379,6 +391,7 @@ macro_rules! decl_module { {} { $( $offchain:tt )* } { $( $constants:tt )* } + { $( $error_type:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn on_finalise($($param_name:ident : $param:ty),* ) { $( $impl:tt )* } @@ -398,6 +411,7 @@ macro_rules! decl_module { { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } + { $( $error_type:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn on_initialize($($param_name:ident : $param:ty),* ) { $( $impl:tt )* } @@ -413,6 +427,7 @@ macro_rules! decl_module { { $( $on_finalize )* } { $( $offchain )* } { $( $constants )* } + { $( $error_type )* } [ $( $dispatchables )* ] $($rest)* ); @@ -427,6 +442,7 @@ macro_rules! decl_module { { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } + { $( $error_type:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn on_initialise($($param_name:ident : $param:ty),* ) { $( $impl:tt )* } @@ -449,6 +465,7 @@ macro_rules! decl_module { { $( $on_finalize:tt )* } { } { $( $constants:tt )* } + { $( $error_type:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn offchain_worker($($param_name:ident : $param:ty),* ) { $( $impl:tt )* } @@ -466,6 +483,7 @@ macro_rules! decl_module { { $( $on_finalize )* } { fn offchain_worker( $( $param_name : $param ),* ) { $( $impl )* } } { $( $constants )* } + { $( $error_type )* } [ $( $dispatchables )* ] $($rest)* ); @@ -485,6 +503,7 @@ macro_rules! decl_module { { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } + { $( $error_type:tt )* } [ $( $dispatchables:tt )* ] $( #[doc = $doc_attr:tt] )* const $name:ident: $ty:ty = $value:expr; @@ -507,11 +526,85 @@ macro_rules! decl_module { $( #[doc = $doc_attr ] )* $name: $ty = $value; } + { $( $error_type )* } [ $( $dispatchables )* ] $($rest)* ); }; + // Parse error type + (@normalize + $(#[$attr:meta])* + pub struct $mod_type:ident< + $trait_instance:ident: + $trait_name:ident$(, $instance:ident: $instantiable:path $(= $module_default_instance:path)?)? + > + for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } + { $( $deposit_event:tt )* } + { $( $on_initialize:tt )* } + { $( $on_finalize:tt )* } + { $( $offchain:tt )* } + { $( $constants:tt )* } + { } + [ $( $dispatchables:tt )* ] + $(#[doc = $doc_attr:tt])* + type Error = $error_type:ty; + $($rest:tt)* + ) => { + $crate::decl_module!(@normalize + $(#[$attr])* + pub struct $mod_type< + $trait_instance: $trait_name$(, $instance: $instantiable $(= $module_default_instance)?)? + > + for enum $call_type where origin: $origin_type, system = $system + { $( $other_where_bounds )* } + { $( $deposit_event )* } + { $( $on_initialize )* } + { $( $on_finalize )* } + { $( $offchain )* } + { $( $constants )* } + { $error_type } + [ $( $dispatchables )* ] + $($rest)* + ); + }; + // Add default Error if none supplied + (@normalize + $(#[$attr:meta])* + pub struct $mod_type:ident< + $trait_instance:ident: + $trait_name:ident$(, $instance:ident: $instantiable:path $(= $module_default_instance:path)?)? + > + for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } + { $( $deposit_event:tt )* } + { $( $on_initialize:tt )* } + { $( $on_finalize:tt )* } + { $( $offchain:tt )* } + { $( $constants:tt )* } + { } + [ $($t:tt)* ] + $($rest:tt)* + ) => { + $crate::decl_module!(@normalize + $(#[$attr])* + pub struct $mod_type< + $trait_instance: $trait_name$(, $instance: $instantiable $(= $module_default_instance)?)? + > + for enum $call_type where origin: $origin_type, system = $system + { $( $other_where_bounds )* } + { $( $deposit_event )* } + { $( $on_initialize )* } + { $( $on_finalize )* } + { $( $offchain )* } + { $( $constants )* } + { &'static str } + [ $($t)* ] + $($rest)* + ); + }; + // This puts the function statement into the [], decreasing `$rest` and moving toward finishing the parse. (@normalize $(#[$attr:meta])* @@ -526,6 +619,7 @@ macro_rules! decl_module { { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } + { $error_type:ty } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* #[weight = $weight:expr] @@ -546,6 +640,7 @@ macro_rules! decl_module { { $( $on_finalize )* } { $( $offchain )* } { $( $constants )* } + { $error_type } [ $( $dispatchables )* $(#[doc = $doc_attr])* @@ -572,6 +667,7 @@ macro_rules! decl_module { { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } + { $( $error_type:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $fn_vis:vis fn $fn_name:ident( @@ -591,6 +687,7 @@ macro_rules! decl_module { { $( $on_finalize )* } { $( $offchain )* } { $( $constants )* } + { $( $error_type )* } [ $( $dispatchables )* ] $(#[doc = $doc_attr])* #[weight = $crate::dispatch::TransactionWeight::default()] @@ -611,6 +708,7 @@ macro_rules! decl_module { { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } + { $( $error_type:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $(#[weight = $weight:expr])? @@ -635,6 +733,7 @@ macro_rules! decl_module { { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } + { $( $error_type:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $(#[weight = $weight:expr])? @@ -659,6 +758,7 @@ macro_rules! decl_module { { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } + { $( $error_type:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $(#[weight = $weight:expr])? @@ -684,6 +784,7 @@ macro_rules! decl_module { { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } + { $( $error_type:tt )* } [ $( $dispatchables:tt )* ] ) => { $crate::decl_module!(@imp @@ -698,6 +799,7 @@ macro_rules! decl_module { { $( $on_finalize )* } { $( $offchain )* } { $( $constants )* } + { $( $error_type )* } ); }; @@ -863,6 +965,7 @@ macro_rules! decl_module { (@impl_function $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; $origin_ty:ty; + $error_type:ty; $ignore:ident; $(#[doc = $doc_attr:tt])* $vis:vis fn $name:ident ( @@ -873,7 +976,7 @@ macro_rules! decl_module { #[allow(unreachable_code)] $vis fn $name( $origin: $origin_ty $(, $param: $param_ty )* - ) -> $crate::dispatch::Result { + ) -> $crate::dispatch::DispatchResult<$error_type> { { $( $impl )* } // May be unreachable. Ok(()) @@ -884,6 +987,7 @@ macro_rules! decl_module { (@impl_function $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; $origin_ty:ty; + $error_type:ty; $ignore:ident; $(#[doc = $doc_attr:tt])* $vis:vis fn $name:ident ( @@ -1038,6 +1142,7 @@ macro_rules! decl_module { { $( $on_finalize:tt )* } { $( $offchain:tt )* } { $( $constants:tt )* } + { $error_type:ty } ) => { $crate::__check_reserved_fn_name! { $( $fn_name )* } @@ -1089,6 +1194,7 @@ macro_rules! decl_module { @impl_function $mod_type<$trait_instance: $trait_name $(, $fn_instance: $fn_instantiable)?>; $origin_type; + $error_type; $from; $(#[doc = $doc_attr])* $fn_vis fn $fn_name ( @@ -1195,7 +1301,8 @@ macro_rules! decl_module { { type Trait = $trait_instance; type Origin = $origin_type; - fn dispatch(self, _origin: Self::Origin) -> $crate::dispatch::Result { + type Error = $error_type; + fn dispatch(self, _origin: Self::Origin) -> $crate::dispatch::DispatchResult { match self { $( $call_type::$fn_name( $( $param_name ),* ) => { @@ -1222,8 +1329,8 @@ macro_rules! decl_module { #[doc(hidden)] pub fn dispatch>( d: D, - origin: D::Origin, - ) -> $crate::dispatch::Result { + origin: D::Origin + ) -> $crate::dispatch::DispatchResult { d.dispatch(origin) } } @@ -1260,6 +1367,25 @@ macro_rules! impl_outer_dispatch { $module:ident::$camelcase:ident, )* } + ) => { + $crate::impl_outer_dispatch! { + $(#[$attr])* + pub enum $call_type for $runtime where origin: $origin { + type Error = &'static str; + $( + $module::$camelcase, + )* + } + } + }; + ( + $(#[$attr:meta])* + pub enum $call_type:ident for $runtime:ident where origin: $origin:ty { + type Error = $error_type:ty; + $( + $module:ident::$camelcase:ident, + )* + } ) => { $(#[$attr])* #[derive(Clone, PartialEq, Eq, $crate::codec::Encode, $crate::codec::Decode)] @@ -1279,9 +1405,10 @@ macro_rules! impl_outer_dispatch { impl $crate::dispatch::Dispatchable for $call_type { type Origin = $origin; type Trait = $call_type; - fn dispatch(self, origin: $origin) -> $crate::dispatch::Result { + type Error = $error_type; + fn dispatch(self, origin: $origin) -> $crate::dispatch::DispatchResult { match self { - $( $call_type::$camelcase(call) => call.dispatch(origin), )* + $( $call_type::$camelcase(call) => call.dispatch(origin).map_err(Into::into), )* } } } diff --git a/srml/support/src/error.rs b/srml/support/src/error.rs new file mode 100644 index 0000000000000..e0041dd41f2ef --- /dev/null +++ b/srml/support/src/error.rs @@ -0,0 +1,205 @@ +#[macro_export] +macro_rules! impl_outer_error { + ( + $(#[$attr:meta])* + pub enum $name:ident for $runtime:ident { + $( $module:ident $( <$generic:ident $(, $instance:path )? > )? ),* $(,)? + } + ) => { + $crate::impl_outer_error! { + $(#[$attr])* + pub enum $name for $runtime where system = system { + $( $module $( <$generic $(, $instance )? > )?, )* + } + } + }; + ( + $(#[$attr:meta])* + pub enum $name:ident for $runtime:ident where system = $system:ident { + $( $module:ident $( <$generic:ident $(, $instance:path )?> )? ),* $(,)? + } + ) => { + $crate::impl_outer_error!( + $( #[$attr] )*; + $name; + $runtime; + $system; + Modules { $( $module $( <$generic $(, $instance )? > )*, )* }; + ); + }; + ( + $(#[$attr:meta])*; + $name:ident; + $runtime:ident; + $system:ident; + Modules { + $module:ident $( )?, + $( $rest_module:tt )* + }; + $( $parsed:tt )* + ) => { + $crate::impl_outer_error!( + $( #[$attr] )*; + $name; + $runtime; + $system; + Modules { $( $rest_module )* }; + $( $parsed )* $module $( <$runtime $(, $instance )? > )?, + ); + }; + + // The main macro expansion that actually renders the Error enum. + + ( + $(#[$attr:meta])*; + $name:ident; + $runtime:ident; + $system:ident; + Modules { }; + $( $module:ident $( <$generic_param:ident $(, $generic_instance:path )? > )* ,)* + ) => { + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. + #[derive(Clone, PartialEq, Eq, $crate::codec::Encode)] + #[cfg_attr(feature = "std", derive(Debug))] + $(#[$attr])* + #[allow(non_camel_case_types)] + pub enum $name { + system($system::Error), + $( + $module($module::Error), + )* + } + + impl From<$system::Error> for $name { + fn from(err: $system::Error) -> Self { + $name::system(err) + } + } + + impl From<&'static str> for $name { + fn from(err: &'static str) -> Self { + $name::system($system::Error::Other(err)) + } + } + + impl $crate::Printable for $name { + fn print(self) { + $crate::print("Error:"); + let err = Into::<$crate::runtime_primitives::DispatchError>::into(self); + $crate::print(err.module as u64); + $crate::print(err.error as u64); + if let Some(msg) = err.message { + $crate::print(msg); + } + } + } + + impl $crate::rstd::convert::TryInto<$system::Error> for $name { + type Error = Self; + fn try_into(self) -> $crate::dispatch::result::Result<$system::Error, Self::Error> { + if let $name::system(err) = self { + Ok(err) + } else { + Err(self) + } + } + } + + impl Into<$crate::runtime_primitives::DispatchError> for $name { + fn into(self) -> $crate::runtime_primitives::DispatchError { + match self { + $name::system(ref err) => match err { + $system::Error::Other(msg) => + $crate::runtime_primitives::DispatchError { + module: 0, + error: 0, + message: Some(msg), + }, + _ => $crate::runtime_primitives::DispatchError { + module: 0, + error: Into::::into(err), + message: None, + }, + }, + $( + $name::$module(ref err) => match err { + $module::Error::Other(msg) => + $crate::runtime_primitives::DispatchError { + module: $crate::codec::Encode::using_encoded(&self, |s| s[0]), + error: 0, + message: Some(msg), + }, + _ => $crate::runtime_primitives::DispatchError { + module: $crate::codec::Encode::using_encoded(&self, |s| s[0]), + error: Into::::into(err), + message: None, + }, + }, + )* + } + } + } + + $( + impl From<$module::Error> for $name { + fn from(err: $module::Error) -> Self { + $name::$module(err) + } + } + + impl $crate::rstd::convert::TryInto<$module::Error> for $name { + type Error = Self; + fn try_into(self) -> $crate::dispatch::result::Result<$module::Error, Self::Error> { + if let $name::$module(err) = self { + Ok(err) + } else { + Err(self) + } + } + } + )* + } +} + + +#[macro_export] +macro_rules! decl_error { + ( + $(#[$attr:meta])* + pub enum Error { + $( + $(#[$variant_attr:meta])* + $name:ident + ),* + $(,)? + } + ) => { + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. + #[derive(Clone, PartialEq, Eq, $crate::codec::Encode)] + #[cfg_attr(feature = "std", derive(Debug))] + $(#[$attr])* + #[allow(non_camel_case_types)] + pub enum Error { + Other(&'static str), + $( + $(#[$variant_attr])* + $name + ),* + } + + impl From<&Error> for u8 { + fn from(err: &Error) -> u8 { + match err { + Error::Other(_) => 0, + _ => $crate::codec::Encode::using_encoded(err, |s| s[0]), + } + } + } + + impl From<&'static str> for Error { + fn from(val: &'static str) -> Error { + Error::Other(val) + } + } + } +} diff --git a/srml/support/src/lib.rs b/srml/support/src/lib.rs index 361fef969b3a1..a2747c2802314 100644 --- a/srml/support/src/lib.rs +++ b/srml/support/src/lib.rs @@ -56,6 +56,8 @@ mod runtime; pub mod inherent; #[macro_use] pub mod unsigned; +#[macro_use] +pub mod error; mod double_map; pub mod traits; @@ -65,7 +67,7 @@ pub use self::storage::{ pub use self::hashable::Hashable; pub use self::dispatch::{Parameter, Dispatchable, Callable, IsSubType}; pub use self::double_map::StorageDoubleMapWithHasher; -pub use runtime_io::{print, storage_root}; +pub use runtime_io::{print, storage_root, Printable}; pub use runtime_primitives::ConsensusEngineId; /// Macro for easily creating a new implementation of the `Get` trait. Use similarly to diff --git a/srml/support/src/runtime.rs b/srml/support/src/runtime.rs index 4461e37518698..b5006714ca577 100644 --- a/srml/support/src/runtime.rs +++ b/srml/support/src/runtime.rs @@ -216,6 +216,14 @@ macro_rules! construct_runtime { } ),* ); + $crate::__decl_outer_error!( + $runtime; + $( + $name: $module:: $( < $module_instance >:: )? { + $( $modules $( <$modules_generic> )* ),* + } + ),* + ); $crate::__decl_all_modules!( $runtime; ; @@ -403,6 +411,7 @@ macro_rules! __create_decl_macro { __create_decl_macro!(__decl_outer_event, impl_outer_event, Event, $); __create_decl_macro!(__decl_outer_origin, impl_outer_origin, Origin, $); +__create_decl_macro!(__decl_outer_error, impl_outer_error, Error, $); /// A macro that defines all modules as an associated types of the Runtime type. #[macro_export] @@ -552,6 +561,7 @@ macro_rules! __decl_outer_dispatch { ) => { $crate::impl_outer_dispatch!( pub enum Call for $runtime where origin: Origin { + type Error = Error; $( $parsed_modules::$parsed_name, )* } ); diff --git a/srml/support/test/tests/instance.rs b/srml/support/test/tests/instance.rs index 62e7263b511be..ffa082f69a09b 100644 --- a/srml/support/test/tests/instance.rs +++ b/srml/support/test/tests/instance.rs @@ -238,6 +238,7 @@ impl system::Trait for Runtime { type BlockNumber = BlockNumber; type AccountId = AccountId; type Event = Event; + type Error = Error; } srml_support::construct_runtime!( @@ -407,4 +408,4 @@ fn storage_with_instance_basic_operation() { DoubleMap::remove(key1, key2); assert_eq!(DoubleMap::get(key1, key2), 0); }); -} \ No newline at end of file +} diff --git a/srml/support/test/tests/issue2219.rs b/srml/support/test/tests/issue2219.rs index 185b5e24807a9..0fba65bd750a7 100644 --- a/srml/support/test/tests/issue2219.rs +++ b/srml/support/test/tests/issue2219.rs @@ -160,6 +160,7 @@ impl system::Trait for Runtime { type BlockNumber = BlockNumber; type AccountId = AccountId; type Event = Event; + type Error = Error; } impl module::Trait for Runtime {} @@ -183,4 +184,4 @@ fn create_genesis_config() { enable_storage_role: true, }) }; -} \ No newline at end of file +} diff --git a/srml/support/test/tests/system.rs b/srml/support/test/tests/system.rs index 6483161211afc..9ae9c4a4bf060 100644 --- a/srml/support/test/tests/system.rs +++ b/srml/support/test/tests/system.rs @@ -8,6 +8,7 @@ pub trait Trait: 'static + Eq + Clone { type Hash; type AccountId: Encode + Decode; type Event: From; + type Error: From + From<&'static str>; } srml_support::decl_module! { @@ -24,6 +25,13 @@ srml_support::decl_event!( } ); +srml_support::decl_error! { + pub enum Error { + TestError, + AnotherError + } +} + /// Origin for the system module. #[derive(PartialEq, Eq, Clone)] #[cfg_attr(feature = "std", derive(Debug))] @@ -49,4 +57,4 @@ pub fn ensure_root(o: OuterOrigin) -> Result<(), &'stati where OuterOrigin: Into, OuterOrigin>> { o.into().map(|_| ()).map_err(|_| "bad origin: expected to be a root origin") -} \ No newline at end of file +} diff --git a/srml/system/benches/bench.rs b/srml/system/benches/bench.rs index ee4ebf711ab31..a3068463e7fde 100644 --- a/srml/system/benches/bench.rs +++ b/srml/system/benches/bench.rs @@ -54,6 +54,11 @@ impl_outer_event! { } } +#[allow(non_camel_case_types)] +pub enum Error { + system(system::Error) +} + #[derive(Clone, Eq, PartialEq)] pub struct Runtime; impl system::Trait for Runtime { @@ -66,6 +71,7 @@ impl system::Trait for Runtime { type Lookup = IdentityLookup; type Header = Header; type Event = Event; + type Error = Error; } impl module::Trait for Runtime { diff --git a/srml/system/src/lib.rs b/srml/system/src/lib.rs index a55cb9be86ea4..773900b874176 100644 --- a/srml/system/src/lib.rs +++ b/srml/system/src/lib.rs @@ -76,14 +76,18 @@ use serde::Serialize; use rstd::prelude::*; #[cfg(any(feature = "std", test))] use rstd::map; -use primitives::{generic, traits::{self, CheckEqual, SimpleArithmetic, - SimpleBitOps, Hash, Member, MaybeDisplay, EnsureOrigin, CurrentHeight, BlockNumberToHash, - MaybeSerializeDebugButNotDeserialize, MaybeSerializeDebug, StaticLookup, One, Bounded, Lookup, - Zero, -}}; +use primitives::{ + generic, PrimitiveError, DispatchError, + traits::{ + self, CheckEqual, SimpleArithmetic, + SimpleBitOps, Hash, Member, MaybeDisplay, EnsureOrigin, CurrentHeight, BlockNumberToHash, + MaybeSerializeDebugButNotDeserialize, MaybeSerializeDebug, StaticLookup, One, Bounded, Lookup, + Zero + } +}; use substrate_primitives::storage::well_known_keys; use srml_support::{ - storage, decl_module, decl_event, decl_storage, StorageDoubleMap, StorageValue, + storage, decl_module, decl_event, decl_storage, decl_error, StorageDoubleMap, StorageValue, StorageMap, Parameter, for_each_tuple, traits::{Contains, Get}, }; use safe_mix::TripletMix; @@ -172,7 +176,8 @@ pub trait Trait: 'static + Eq + Clone { /// Used to define the type and conversion mechanism for referencing accounts in transactions. It's perfectly /// reasonable for this to be an identity conversion (with the source type being `AccountId`), but other modules /// (e.g. Indices module) may provide more functional/efficient alternatives. - type Lookup: StaticLookup; + // TODO: avoid &'static str error type #2953 + type Lookup: StaticLookup; /// The block header. type Header: Parameter + traits::Header< @@ -183,6 +188,9 @@ pub trait Trait: 'static + Eq + Clone { /// The aggregated event type of the runtime. type Event: Parameter + Member + From; + /// The aggregated error type of the runtime. + type Error: Member + From + From<&'static str> + runtime_io::Printable; + /// Maximum number of block number to block hash mappings to keep (oldest pruned first). type BlockHashCount: Get; } @@ -195,6 +203,8 @@ pub type KeyValue = (Vec, Vec); decl_module! { pub struct Module for enum Call where origin: T::Origin { + type Error = T::Error; + /// Deposits an event into this block's event record. pub fn deposit_event(event: T::Event) { Self::deposit_event_indexed(&[], event); @@ -263,10 +273,45 @@ decl_event!( /// An extrinsic completed successfully. ExtrinsicSuccess, /// An extrinsic failed. - ExtrinsicFailed, + ExtrinsicFailed(DispatchError), } ); +decl_error! { + /// Error for the System module + pub enum Error { + BadSignature, + BlockFull, + RequireSignedOrigin, + RequireRootOrigin, + RequireNoOrigin, + } +} + +impl From for Error { + fn from(err: PrimitiveError) -> Error { + match err { + PrimitiveError::Other(err) => Error::Other(err), + PrimitiveError::BadSignature => Error::BadSignature, + PrimitiveError::BlockFull => Error::BlockFull, + } + } +} + +// Exists for for backward compatibility purpose. +impl From for &'static str { + fn from(err: Error) -> &'static str { + match err { + Error::Other(val) => val, + Error::BadSignature => "bad signature in extrinsic", + Error::BlockFull => "block size limit is reached", + Error::RequireSignedOrigin => "bad origin: expected to be a signed origin", + Error::RequireRootOrigin => "bad origin: expected to be a root origin", + Error::RequireNoOrigin => "bad origin: expected to be no origin", + } + } +} + /// Origin for the System module. #[derive(PartialEq, Eq, Clone)] #[cfg_attr(feature = "std", derive(Debug))] @@ -383,6 +428,7 @@ impl< AccountId, > EnsureOrigin for EnsureRoot { type Success = (); + type Error = &'static str; fn try_origin(o: O) -> Result { o.into().and_then(|o| match o { RawOrigin::Root => Ok(()), @@ -397,6 +443,7 @@ impl< AccountId, > EnsureOrigin for EnsureSigned { type Success = AccountId; + type Error = &'static str; fn try_origin(o: O) -> Result { o.into().and_then(|o| match o { RawOrigin::Signed(who) => Ok(who), @@ -412,6 +459,7 @@ impl< AccountId: PartialEq + Clone, > EnsureOrigin for EnsureSignedBy { type Success = AccountId; + type Error = &'static str; fn try_origin(o: O) -> Result { o.into().and_then(|o| match o { RawOrigin::Signed(ref who) if Who::contains(who) => Ok(who.clone()), @@ -426,6 +474,7 @@ impl< AccountId, > EnsureOrigin for EnsureNone { type Success = (); + type Error = &'static str; fn try_origin(o: O) -> Result { o.into().and_then(|o| match o { RawOrigin::None => Ok(()), @@ -437,6 +486,7 @@ impl< pub struct EnsureNever(::rstd::marker::PhantomData); impl EnsureOrigin for EnsureNever { type Success = T; + type Error = &'static str; fn try_origin(o: O) -> Result { Err(o) } @@ -444,32 +494,32 @@ impl EnsureOrigin for EnsureNever { /// Ensure that the origin `o` represents a signed extrinsic (i.e. transaction). /// Returns `Ok` with the account that signed the extrinsic or an `Err` otherwise. -pub fn ensure_signed(o: OuterOrigin) -> Result +pub fn ensure_signed(o: OuterOrigin) -> Result where OuterOrigin: Into, OuterOrigin>> { match o.into() { Ok(RawOrigin::Signed(t)) => Ok(t), - _ => Err("bad origin: expected to be a signed origin"), + _ => Err(Error::RequireSignedOrigin), } } /// Ensure that the origin `o` represents the root. Returns `Ok` or an `Err` otherwise. -pub fn ensure_root(o: OuterOrigin) -> Result<(), &'static str> +pub fn ensure_root(o: OuterOrigin) -> Result<(), Error> where OuterOrigin: Into, OuterOrigin>> { match o.into() { Ok(RawOrigin::Root) => Ok(()), - _ => Err("bad origin: expected to be a root origin"), + _ => Err(Error::RequireRootOrigin), } } /// Ensure that the origin `o` represents an unsigned extrinsic. Returns `Ok` or an `Err` otherwise. -pub fn ensure_none(o: OuterOrigin) -> Result<(), &'static str> +pub fn ensure_none(o: OuterOrigin) -> Result<(), Error> where OuterOrigin: Into, OuterOrigin>> { match o.into() { Ok(RawOrigin::None) => Ok(()), - _ => Err("bad origin: expected to be no origin"), + _ => Err(Error::RequireNoOrigin), } } @@ -720,10 +770,10 @@ impl Module { } /// To be called immediately after an extrinsic has been applied. - pub fn note_applied_extrinsic(r: &Result<(), &'static str>, encoded_len: u32) { + pub fn note_applied_extrinsic(r: &Result<(), DispatchError>, encoded_len: u32) { Self::deposit_event(match r { Ok(_) => Event::ExtrinsicSuccess, - Err(_) => Event::ExtrinsicFailed, + Err(err) => Event::ExtrinsicFailed(err.clone()), }.into()); let next_extrinsic_index = Self::extrinsic_index().unwrap_or_default() + 1u32; @@ -758,7 +808,8 @@ impl Default for ChainContext { impl Lookup for ChainContext { type Source = ::Source; type Target = ::Target; - fn lookup(&self, s: Self::Source) -> rstd::result::Result { + type Error = ::Error; + fn lookup(&self, s: Self::Source) -> rstd::result::Result { ::lookup(s) } } @@ -784,12 +835,16 @@ mod tests { use runtime_io::with_externalities; use substrate_primitives::H256; use primitives::{traits::{BlakeTwo256, IdentityLookup}, testing::Header}; - use srml_support::{impl_outer_origin, parameter_types}; + use srml_support::{impl_outer_origin, impl_outer_error, parameter_types}; - impl_outer_origin!{ + impl_outer_origin! { pub enum Origin for Test where system = super {} } + impl_outer_error! { + pub enum Error for Test where system = super {} + } + #[derive(Clone, Eq, PartialEq)] pub struct Test; @@ -807,6 +862,7 @@ mod tests { type Lookup = IdentityLookup; type Header = Header; type Event = u16; + type Error = Error; type BlockHashCount = BlockHashCount; } @@ -814,7 +870,7 @@ mod tests { fn from(e: Event) -> u16 { match e { Event::ExtrinsicSuccess => 100, - Event::ExtrinsicFailed => 101, + Event::ExtrinsicFailed(err) => Encode::using_encoded(&err, |s| (s[0] as u16) | ((s[1] as u16) << 8)), } } } @@ -853,14 +909,14 @@ mod tests { System::initialize(&2, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); System::deposit_event(42u16); System::note_applied_extrinsic(&Ok(()), 0); - System::note_applied_extrinsic(&Err(""), 0); + System::note_applied_extrinsic(&Err(DispatchError { module: 1, error: 2, message: None }), 0); System::note_finished_extrinsics(); System::deposit_event(3u16); System::finalize(); assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), event: 42u16, topics: vec![] }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: 100u16, topics: vec![] }, - EventRecord { phase: Phase::ApplyExtrinsic(1), event: 101u16, topics: vec![] }, + EventRecord { phase: Phase::ApplyExtrinsic(1), event: 0x0201u16, topics: vec![] }, EventRecord { phase: Phase::Finalization, event: 3u16, topics: vec![] } ]); }); diff --git a/srml/timestamp/src/lib.rs b/srml/timestamp/src/lib.rs index 48a0d04c32198..edd4ce264e55c 100644 --- a/srml/timestamp/src/lib.rs +++ b/srml/timestamp/src/lib.rs @@ -325,7 +325,7 @@ impl ProvideInherent for Module { mod tests { use super::*; - use srml_support::{impl_outer_origin, assert_ok, parameter_types}; + use srml_support::{impl_outer_origin, impl_outer_error, assert_ok, parameter_types}; use runtime_io::{with_externalities, TestExternalities}; use substrate_primitives::H256; use runtime_primitives::{traits::{BlakeTwo256, IdentityLookup}, testing::Header}; @@ -334,6 +334,10 @@ mod tests { pub enum Origin for Test {} } + impl_outer_error! { + pub enum Error for Runtime {} + } + #[derive(Clone, Eq, PartialEq)] pub struct Test; parameter_types! { @@ -349,6 +353,7 @@ mod tests { type Lookup = IdentityLookup; type Header = Header; type Event = (); + type Error = Error; type BlockHashCount = BlockHashCount; } parameter_types! { diff --git a/srml/treasury/src/lib.rs b/srml/treasury/src/lib.rs index 7c4d7b10f21cf..39bbf35cc403b 100644 --- a/srml/treasury/src/lib.rs +++ b/srml/treasury/src/lib.rs @@ -98,10 +98,10 @@ pub trait Trait: system::Trait { type Currency: Currency + ReservableCurrency; /// Origin from which approvals must come. - type ApproveOrigin: EnsureOrigin; + type ApproveOrigin: EnsureOrigin; /// Origin from which rejections must come. - type RejectOrigin: EnsureOrigin; + type RejectOrigin: EnsureOrigin; /// The overarching event type. type Event: From> + Into<::Event>; @@ -358,7 +358,7 @@ mod tests { use super::*; use runtime_io::with_externalities; - use srml_support::{assert_noop, assert_ok, impl_outer_origin, parameter_types}; + use srml_support::{assert_noop, assert_ok, impl_outer_origin, impl_outer_error, parameter_types}; use substrate_primitives::{H256, Blake2Hasher}; use runtime_primitives::{traits::{BlakeTwo256, OnFinalize, IdentityLookup}, testing::Header}; @@ -366,6 +366,12 @@ mod tests { pub enum Origin for Test {} } + impl_outer_error! { + pub enum Error for Test { + balances + } + } + #[derive(Clone, Eq, PartialEq)] pub struct Test; parameter_types! { @@ -381,6 +387,7 @@ mod tests { type Lookup = IdentityLookup; type Header = Header; type Event = (); + type Error = Error; type BlockHashCount = BlockHashCount; } parameter_types! { @@ -398,6 +405,7 @@ mod tests { type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); + type Error = Error; type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee;