mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 09:21:05 +00:00
Custom runtime module errors (#3433)
* srml-system checks * wip * more modules compiles * node-runtime checks * build.sh passes * include dispatch error in failed event * revert some unnecessary changes * refactor based on comments * more compile error fixes * avoid unnecessary into * reorder code * fixes some tests * manually implement encode & decode to avoid i8 workaround * more test fixes * more fixes * more error fixes * Apply suggestions from code review Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * address comments * test for DispatchError encoding * tyep alias for democracy * make error printable * line width * fix balances tests * fix executive test * fix system tests * bump version * ensure consistent method signature * Apply suggestions from code review Co-Authored-By: Gavin Wood <github@gavwood.com> * changes based on review * Add issue number for TODOs * fix * line width * fix test * Update core/sr-primitives/src/lib.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update core/sr-primitives/src/traits.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update srml/council/src/motions.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update srml/council/src/motions.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * update based on review * More concrete macro matching * fix test build issue * Update hex-literal dependency version. (#3141) * Update hex-literal dep version. * Update lock file. * Start to rework the new error handling * More work to get it back compiling * Start to fix after master merge * The great transaction error handling refactoring * Make `decl_error` errors convertible to `&'static str` * Make srml-executive build again * Fix `sr-primitives` tests * More fixes * Last round of fix ups * Fix build * Fix build * Apply suggestions from code review Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * Rename some stuff * Fixes after master merge * Adds `CheckBlockGasLimit` signed extension * Remove debug stuff * Fix srml-balances test * Rename `InvalidIndex` to `CannotLookup` * Remove weird generic parameters * Rename function again * Fix import * Document the signed extension * Change from `Into` to `From` * Update srml/contracts/src/lib.rs Co-Authored-By: Sergei Pepyakin <sergei@parity.io> * Fix compilation * Update srml/contracts/src/lib.rs Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * Update core/sr-primitives/src/transaction_validity.rs Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * Remove unused code * Fix compilation * Some cleanups * Fix compile errors * Make `TransactionValidity` a `Result` * Apply suggestions from code review Co-Authored-By: Gavin Wood <gavin@parity.io> * Beautify the code a little bit and fix test * Make `CannotLookup` an inherent error declared by `decl_error!` * Adds some documentation * Make `ApplyOutcome` a result * Up the spec_version * Apply suggestions from code review Co-Authored-By: Gavin Wood <gavin@parity.io> Co-Authored-By: DemiMarie-parity <48690212+DemiMarie-parity@users.noreply.github.com>
This commit is contained in:
@@ -59,13 +59,13 @@
|
||||
//! # pub type Balances = u64;
|
||||
//! # pub type AllModules = u64;
|
||||
//! # pub enum Runtime {};
|
||||
//! # use sr_primitives::transaction_validity::TransactionValidity;
|
||||
//! # use sr_primitives::transaction_validity::{TransactionValidity, UnknownTransaction};
|
||||
//! # use sr_primitives::traits::ValidateUnsigned;
|
||||
//! # impl ValidateUnsigned for Runtime {
|
||||
//! # type Call = ();
|
||||
//! #
|
||||
//! # fn validate_unsigned(_call: &Self::Call) -> TransactionValidity {
|
||||
//! # TransactionValidity::Invalid(0)
|
||||
//! # UnknownTransaction::NoUnsignedValidator.into()
|
||||
//! # }
|
||||
//! # }
|
||||
//! /// Executive: handles dispatch to the various modules.
|
||||
@@ -74,50 +74,17 @@
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::marker::PhantomData;
|
||||
use rstd::result;
|
||||
use sr_primitives::{generic::Digest, traits::{
|
||||
self, Header, Zero, One, Checkable, Applyable, CheckEqual, OnFinalize,
|
||||
OnInitialize, NumberFor, Block as BlockT, OffchainWorker, ValidateUnsigned
|
||||
}};
|
||||
use support::Dispatchable;
|
||||
use rstd::{prelude::*, marker::PhantomData};
|
||||
use sr_primitives::{
|
||||
generic::Digest, ApplyResult, weights::GetDispatchInfo,
|
||||
traits::{
|
||||
self, Header, Zero, One, Checkable, Applyable, CheckEqual, OnFinalize, OnInitialize,
|
||||
NumberFor, Block as BlockT, OffchainWorker, ValidateUnsigned, Dispatchable
|
||||
},
|
||||
transaction_validity::TransactionValidity,
|
||||
};
|
||||
use codec::{Codec, Encode};
|
||||
use system::{extrinsics_root, DigestOf};
|
||||
use sr_primitives::{ApplyOutcome, ApplyError};
|
||||
use sr_primitives::transaction_validity::TransactionValidity;
|
||||
use sr_primitives::weights::GetDispatchInfo;
|
||||
|
||||
mod internal {
|
||||
use sr_primitives::traits::DispatchError;
|
||||
|
||||
pub enum ApplyError {
|
||||
BadSignature(&'static str),
|
||||
Stale,
|
||||
Future,
|
||||
CantPay,
|
||||
FullBlock,
|
||||
}
|
||||
|
||||
pub enum ApplyOutcome {
|
||||
Success,
|
||||
Fail(&'static str),
|
||||
}
|
||||
|
||||
impl From<DispatchError> for ApplyError {
|
||||
fn from(d: DispatchError) -> Self {
|
||||
match d {
|
||||
DispatchError::Payment => ApplyError::CantPay,
|
||||
DispatchError::Exhausted => ApplyError::FullBlock,
|
||||
DispatchError::NoPermission => ApplyError::CantPay,
|
||||
DispatchError::BadState => ApplyError::CantPay,
|
||||
DispatchError::Stale => ApplyError::Stale,
|
||||
DispatchError::Future => ApplyError::Future,
|
||||
DispatchError::BadProof => ApplyError::BadSignature(""),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait that can be used to execute a block.
|
||||
pub trait ExecuteBlock<Block: BlockT> {
|
||||
@@ -239,30 +206,19 @@ 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<ApplyOutcome, ApplyError> {
|
||||
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(Ok(())) => (),
|
||||
Ok(Err(e)) => runtime_io::print(e),
|
||||
Err(e) => { let err: &'static str = e.into(); panic!(err) },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,9 +227,9 @@ where
|
||||
uxt: Block::Extrinsic,
|
||||
encoded_len: usize,
|
||||
to_note: Option<Vec<u8>>,
|
||||
) -> result::Result<internal::ApplyOutcome, internal::ApplyError> {
|
||||
) -> ApplyResult {
|
||||
// Verify that the signature is good.
|
||||
let xt = uxt.check(&Default::default()).map_err(internal::ApplyError::BadSignature)?;
|
||||
let xt = uxt.check(&Default::default())?;
|
||||
|
||||
// We don't need to make sure to `note_extrinsic` only after we know it's going to be
|
||||
// executed to prevent it from leaking in storage since at this point, it will either
|
||||
@@ -286,15 +242,11 @@ where
|
||||
|
||||
// Decode parameters and dispatch
|
||||
let dispatch_info = xt.get_dispatch_info();
|
||||
let r = Applyable::dispatch(xt, dispatch_info, encoded_len)
|
||||
.map_err(internal::ApplyError::from)?;
|
||||
let r = Applyable::apply(xt, dispatch_info, encoded_len)?;
|
||||
|
||||
<system::Module<System>>::note_applied_extrinsic(&r, encoded_len as u32);
|
||||
|
||||
r.map(|_| internal::ApplyOutcome::Success).or_else(|e| match e {
|
||||
sr_primitives::BLOCK_FULL => Err(internal::ApplyError::FullBlock),
|
||||
e => Ok(internal::ApplyOutcome::Fail(e))
|
||||
})
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
fn final_checks(header: &System::Header) {
|
||||
@@ -324,21 +276,8 @@ where
|
||||
///
|
||||
/// Changes made to storage should be discarded.
|
||||
pub fn validate_transaction(uxt: Block::Extrinsic) -> TransactionValidity {
|
||||
// Note errors > 0 are from ApplyError
|
||||
const UNKNOWN_ERROR: i8 = -127;
|
||||
const INVALID_INDEX: i8 = -10;
|
||||
|
||||
let encoded_len = uxt.using_encoded(|d| d.len());
|
||||
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(sr_primitives::BAD_SIGNATURE) => return TransactionValidity::Invalid(ApplyError::BadSignature as i8),
|
||||
Err(_) => return TransactionValidity::Invalid(UNKNOWN_ERROR),
|
||||
};
|
||||
let xt = uxt.check(&Default::default())?;
|
||||
|
||||
let dispatch_info = xt.get_dispatch_info();
|
||||
xt.validate::<UnsignedValidator>(dispatch_info, encoded_len)
|
||||
@@ -357,13 +296,15 @@ mod tests {
|
||||
use balances::Call;
|
||||
use runtime_io::with_externalities;
|
||||
use primitives::{H256, Blake2Hasher};
|
||||
use sr_primitives::generic::Era;
|
||||
use sr_primitives::Perbill;
|
||||
use sr_primitives::weights::Weight;
|
||||
use sr_primitives::traits::{Header as HeaderT, BlakeTwo256, IdentityLookup, ConvertInto};
|
||||
use sr_primitives::testing::{Digest, Header, Block};
|
||||
use support::{impl_outer_event, impl_outer_origin, parameter_types};
|
||||
use support::traits::{Currency, LockIdentifier, LockableCurrency, WithdrawReasons, WithdrawReason};
|
||||
use sr_primitives::{
|
||||
generic::Era, Perbill, DispatchError, weights::Weight, testing::{Digest, Header, Block},
|
||||
traits::{Header as HeaderT, BlakeTwo256, IdentityLookup, ConvertInto},
|
||||
transaction_validity::{InvalidTransaction, UnknownTransaction}, ApplyError,
|
||||
};
|
||||
use support::{
|
||||
impl_outer_event, impl_outer_origin, parameter_types,
|
||||
traits::{Currency, LockIdentifier, LockableCurrency, WithdrawReasons, WithdrawReason},
|
||||
};
|
||||
use system;
|
||||
use hex_literal::hex;
|
||||
|
||||
@@ -432,8 +373,8 @@ mod tests {
|
||||
|
||||
fn validate_unsigned(call: &Self::Call) -> TransactionValidity {
|
||||
match call {
|
||||
Call::set_balance(_, _, _) => TransactionValidity::Valid(Default::default()),
|
||||
_ => TransactionValidity::Invalid(0),
|
||||
Call::set_balance(_, _, _) => Ok(Default::default()),
|
||||
_ => UnknownTransaction::NoUnsignedValidator.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -479,7 +420,7 @@ mod tests {
|
||||
Digest::default(),
|
||||
));
|
||||
let r = Executive::apply_extrinsic(xt);
|
||||
assert_eq!(r, Ok(ApplyOutcome::Success));
|
||||
assert!(r.is_ok());
|
||||
assert_eq!(<balances::Module<Runtime>>::total_balance(&1), 142 - 10 - weight);
|
||||
assert_eq!(<balances::Module<Runtime>>::total_balance(&2), 69);
|
||||
});
|
||||
@@ -582,14 +523,19 @@ mod tests {
|
||||
assert_eq!(<system::Module<Runtime>>::all_extrinsics_weight(), 0);
|
||||
|
||||
for nonce in 0..=num_to_exhaust_block {
|
||||
let xt = sr_primitives::testing::TestXt(sign_extra(1, nonce.into(), 0), Call::transfer::<Runtime>(33, 0));
|
||||
let xt = sr_primitives::testing::TestXt(
|
||||
sign_extra(1, nonce.into(), 0), Call::transfer::<Runtime>(33, 0),
|
||||
);
|
||||
let res = Executive::apply_extrinsic(xt);
|
||||
if nonce != num_to_exhaust_block {
|
||||
assert_eq!(res.unwrap(), ApplyOutcome::Success);
|
||||
assert_eq!(<system::Module<Runtime>>::all_extrinsics_weight(), encoded_len * (nonce + 1));
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(
|
||||
<system::Module<Runtime>>::all_extrinsics_weight(),
|
||||
encoded_len * (nonce + 1),
|
||||
);
|
||||
assert_eq!(<system::Module<Runtime>>::extrinsic_index(), Some(nonce as u32 + 1));
|
||||
} else {
|
||||
assert_eq!(res, Err(ApplyError::FullBlock));
|
||||
assert_eq!(res, Err(InvalidTransaction::ExhaustsResources.into()));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -606,9 +552,9 @@ mod tests {
|
||||
assert_eq!(<system::Module<Runtime>>::all_extrinsics_weight(), 0);
|
||||
assert_eq!(<system::Module<Runtime>>::all_extrinsics_weight(), 0);
|
||||
|
||||
assert_eq!(Executive::apply_extrinsic(xt.clone()).unwrap(), ApplyOutcome::Success);
|
||||
assert_eq!(Executive::apply_extrinsic(x1.clone()).unwrap(), ApplyOutcome::Success);
|
||||
assert_eq!(Executive::apply_extrinsic(x2.clone()).unwrap(), ApplyOutcome::Success);
|
||||
assert!(Executive::apply_extrinsic(xt.clone()).unwrap().is_ok());
|
||||
assert!(Executive::apply_extrinsic(x1.clone()).unwrap().is_ok());
|
||||
assert!(Executive::apply_extrinsic(x2.clone()).unwrap().is_ok());
|
||||
|
||||
// default weight for `TestXt` == encoded length.
|
||||
assert_eq!(<system::Module<Runtime>>::all_extrinsics_weight(), (3 * len).into());
|
||||
@@ -624,12 +570,18 @@ mod tests {
|
||||
#[test]
|
||||
fn validate_unsigned() {
|
||||
let xt = sr_primitives::testing::TestXt(None, Call::set_balance(33, 69, 69));
|
||||
let valid = TransactionValidity::Valid(Default::default());
|
||||
let mut t = new_test_ext(1);
|
||||
|
||||
with_externalities(&mut t, || {
|
||||
assert_eq!(Executive::validate_transaction(xt.clone()), valid);
|
||||
assert_eq!(Executive::apply_extrinsic(xt), Ok(ApplyOutcome::Fail));
|
||||
assert_eq!(Executive::validate_transaction(xt.clone()), Ok(Default::default()));
|
||||
assert_eq!(
|
||||
Executive::apply_extrinsic(xt),
|
||||
Ok(
|
||||
Err(
|
||||
DispatchError { module: None, error: 0, message: Some("RequireRootOrigin") }
|
||||
)
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -657,11 +609,21 @@ mod tests {
|
||||
));
|
||||
|
||||
if lock == WithdrawReasons::except(WithdrawReason::TransactionPayment) {
|
||||
assert_eq!(Executive::apply_extrinsic(xt).unwrap(), ApplyOutcome::Fail);
|
||||
assert_eq!(
|
||||
Executive::apply_extrinsic(xt).unwrap(),
|
||||
Err(DispatchError {
|
||||
module: None,
|
||||
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!(<balances::Module<Runtime>>::total_balance(&1), 111 - 10 - weight);
|
||||
} else {
|
||||
assert_eq!(Executive::apply_extrinsic(xt), Err(ApplyError::CantPay));
|
||||
assert_eq!(
|
||||
Executive::apply_extrinsic(xt),
|
||||
Err(ApplyError::Validity(InvalidTransaction::Payment.into())),
|
||||
);
|
||||
assert_eq!(<balances::Module<Runtime>>::total_balance(&1), 111);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user