Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Refactor module & runtime error handing #2880

Closed
wants to merge 49 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
ba48a0a
srml-system checks
xlc Jun 17, 2019
52c6b90
wip
xlc Jun 18, 2019
bd7228f
more modules compiles
xlc Jun 18, 2019
c0cece1
node-runtime checks
xlc Jun 18, 2019
a93fedd
build.sh passes
xlc Jun 19, 2019
3039e7c
include dispatch error in failed event
xlc Jun 19, 2019
c5c2e5a
revert some unnecessary changes
xlc Jun 19, 2019
1198fb7
refactor based on comments
xlc Jun 19, 2019
ed20244
Merge remote-tracking branch 'upstream/master' into improve-error-res…
xlc Jun 19, 2019
1f964d1
more compile error fixes
xlc Jun 20, 2019
83975c0
avoid unnecessary into
xlc Jun 20, 2019
e03db0c
reorder code
xlc Jun 20, 2019
7e01497
fixes some tests
xlc Jun 21, 2019
a6c989b
manually implement encode & decode to avoid i8 workaround
xlc Jun 21, 2019
9d2dbbb
more test fixes
xlc Jun 21, 2019
31c9c84
more fixes
xlc Jun 21, 2019
6cad33a
more error fixes
xlc Jun 22, 2019
ba42e6a
Apply suggestions from code review
xlc Jun 22, 2019
0f01cc4
address comments
xlc Jun 22, 2019
cce7c08
Merge remote-tracking branch 'upstream/master' into improve-error-res…
xlc Jun 22, 2019
c7ae1b1
test for DispatchError encoding
xlc Jun 22, 2019
0e424c1
tyep alias for democracy
xlc Jun 22, 2019
a145f27
make error printable
xlc Jun 22, 2019
ef4eecd
line width
xlc Jun 22, 2019
33668d8
fix balances tests
xlc Jun 22, 2019
d128a1a
fix executive test
xlc Jun 22, 2019
a4a8cdc
fix system tests
xlc Jun 22, 2019
f4387fb
bump version
xlc Jun 22, 2019
ef2eaa6
ensure consistent method signature
xlc Jun 22, 2019
31c998f
Apply suggestions from code review
xlc Jun 26, 2019
d6b94b0
Merge remote-tracking branch 'upstream/master' into improve-error-res…
xlc Jun 26, 2019
51ec628
changes based on review
xlc Jun 26, 2019
88887ee
Add issue number for TODOs
xlc Jun 26, 2019
550c1e0
Merge remote-tracking branch 'upstream/master' into improve-error-res…
xlc Jun 26, 2019
e8324c2
Merge remote-tracking branch 'upstream/master' into improve-error-res…
xlc Jun 29, 2019
c147a8e
fix
xlc Jun 29, 2019
633a482
line width
xlc Jun 29, 2019
b61e137
fix test
xlc Jun 29, 2019
1ca74ed
Merge remote-tracking branch 'upstream/master' into improve-error-res…
xlc Jul 8, 2019
3d2c850
Update core/sr-primitives/src/lib.rs
xlc Jul 10, 2019
8330acf
Update core/sr-primitives/src/traits.rs
xlc Jul 10, 2019
d41955a
Update srml/council/src/motions.rs
xlc Jul 10, 2019
66187b3
Update srml/council/src/motions.rs
xlc Jul 10, 2019
7489d3a
update based on review
xlc Jul 10, 2019
57f7958
Merge remote-tracking branch 'upstream/master' into improve-error-res…
xlc Jul 10, 2019
6e5c3ce
More concrete macro matching
xlc Jul 10, 2019
2d9794a
Merge remote-tracking branch 'upstream/master' into improve-error-res…
xlc Jul 11, 2019
1f5faac
Merge remote-tracking branch 'upstream/master' into improve-error-res…
xlc Jul 18, 2019
efe953a
fix test build issue
xlc Jul 18, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/client/src/block_builder/block_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
Expand Down
8 changes: 5 additions & 3 deletions core/sr-primitives/src/generic/unchecked_extrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -83,17 +84,18 @@ where
Call: Encode + Member,
Signature: Member + traits::Verify<Signer=AccountId> + Codec,
AccountId: Member + MaybeDisplay,
Context: Lookup<Source=Address, Target=AccountId>,
Context: Lookup<Source=Address, Target=AccountId, Error=&'static str>
{
type Checked = CheckedExtrinsic<AccountId, Index, Call>;
type Error = PrimitiveError;

fn check(self, context: &Context) -> Result<Self::Checked, &'static str> {
fn check(self, context: &Context) -> Result<Self::Checked, Self::Error> {
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)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -75,13 +76,14 @@ where
AccountId: Member + MaybeDisplay,
BlockNumber: SimpleArithmetic,
Hash: Encode,
Context: Lookup<Source=Address, Target=AccountId>
Context: Lookup<Source=Address, Target=AccountId, Error=&'static str>
+ CurrentHeight<BlockNumber=BlockNumber>
+ BlockNumberToHash<BlockNumber=BlockNumber, Hash=Hash>,
{
type Checked = CheckedExtrinsic<AccountId, Index, Call>;
type Error = PrimitiveError;

fn check(self, context: &Context) -> Result<Self::Checked, &'static str> {
fn check(self, context: &Context) -> Result<Self::Checked, Self::Error> {
Ok(match self.signature {
Some((signed, signature, index, era)) => {
let current_u64 = context.current_height().saturated_into::<u64>();
Expand All @@ -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)),
Expand Down Expand Up @@ -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<u64, &'static str> { Ok(s) }
}
impl CurrentHeight for TestContext {
Expand Down Expand Up @@ -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!(<Ex as Checkable<TestContext>>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE));
assert_eq!(<Ex as Checkable<TestContext>>::check(ux, &TestContext), Err(PrimitiveError::BadSignature));
}

#[test]
Expand Down Expand Up @@ -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!(<Ex as Checkable<TestContext>>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE));
assert_eq!(<Ex as Checkable<TestContext>>::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!(<Ex as Checkable<TestContext>>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE));
assert_eq!(<Ex as Checkable<TestContext>>::check(ux, &TestContext), Err(PrimitiveError::BadSignature));
}

#[test]
Expand Down
17 changes: 10 additions & 7 deletions core/sr-primitives/src/generic/unchecked_mortal_extrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -76,13 +77,14 @@ where
AccountId: Member + MaybeDisplay,
BlockNumber: SimpleArithmetic,
Hash: Encode,
Context: Lookup<Source=Address, Target=AccountId>
Context: Lookup<Source=Address, Target=AccountId, Error=&'static str>
+ CurrentHeight<BlockNumber=BlockNumber>
+ BlockNumberToHash<BlockNumber=BlockNumber, Hash=Hash>,
{
type Checked = CheckedExtrinsic<AccountId, Index, Call>;
type Error = PrimitiveError;

fn check(self, context: &Context) -> Result<Self::Checked, &'static str> {
fn check(self, context: &Context) -> Result<Self::Checked, Self::Error> {
Ok(match self.signature {
Some((signed, signature, index, era)) => {
let current_u64 = context.current_height().saturated_into::<u64>();
Expand All @@ -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)),
Expand Down Expand Up @@ -199,7 +201,8 @@ mod tests {
impl Lookup for TestContext {
type Source = u64;
type Target = u64;
fn lookup(&self, s: u64) -> Result<u64, &'static str> { Ok(s) }
type Error = &'static str;
fn lookup(&self, s: u64) -> Result<u64, Self::Error> { Ok(s) }
}
impl CurrentHeight for TestContext {
type BlockNumber = u64;
Expand Down Expand Up @@ -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!(<Ex as Checkable<TestContext>>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE));
assert_eq!(<Ex as Checkable<TestContext>>::check(ux, &TestContext), Err(PrimitiveError::BadSignature));
}

#[test]
Expand Down Expand Up @@ -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!(<Ex as Checkable<TestContext>>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE));
assert_eq!(<Ex as Checkable<TestContext>>::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!(<Ex as Checkable<TestContext>>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE));
assert_eq!(<Ex as Checkable<TestContext>>::check(ux, &TestContext), Err(PrimitiveError::BadSignature));
}

#[test]
Expand Down
108 changes: 85 additions & 23 deletions core/sr-primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8>;
Expand Down Expand Up @@ -544,21 +567,14 @@ impl From<ed25519::Signature> 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, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
f(&[*self as u8])
}
Fail(DispatchError),
}

#[derive(Eq, PartialEq, Clone, Copy, Decode)]
Expand All @@ -578,12 +594,40 @@ pub enum ApplyError {
FullBlock = 255,
}

impl codec::Encode for ApplyError {
impl Encode for ApplyError {
fn using_encoded<R, F: FnOnce(&[u8]) -> 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>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use #[codec(skip)] here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parity-codec can't handle u8 so I need to implement encode/decode manually anyway

}

impl Encode for DispatchError {
xlc marked this conversation as resolved.
Show resolved Hide resolved
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
f(&[self.module, self.error])
}
}

impl Decode for DispatchError {
fn decode<R: codec::Input>(input: &mut R) -> Option<Self> {
Some(DispatchError {
module: input.read_byte()?,
error: input.read_byte()?,
message: None,
})
}
}

/// Result from attempt to apply an extrinsic.
pub type ApplyResult = Result<ApplyOutcome, ApplyError>;

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -867,4 +912,21 @@ mod tests {
((Into::<U256>::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,
});
}
}
4 changes: 3 additions & 1 deletion core/sr-primitives/src/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -216,7 +217,8 @@ impl<Call> Debug for TestXt<Call> {

impl<Call: Codec + Sync + Send, Context> Checkable<Context> for TestXt<Call> {
type Checked = Self;
fn check(self, _: &Context) -> Result<Self::Checked, &'static str> { Ok(self) }
type Error = PrimitiveError;
fn check(self, _: &Context) -> Result<Self::Checked, Self::Error> { Ok(self) }
}
impl<Call: Codec + Sync + Send> traits::Extrinsic for TestXt<Call> {
fn is_signed(&self) -> Option<bool> {
Expand Down
Loading