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:
Bastian Köcher
2019-09-04 16:21:42 +02:00
committed by GitHub
parent 5e4bc7c9b6
commit c6f3798078
46 changed files with 1259 additions and 630 deletions
@@ -30,9 +30,10 @@ use inherents::InherentData;
use log::{error, info, debug, trace};
use primitives::{H256, Blake2Hasher, ExecutionContext};
use sr_primitives::{
traits::{Block as BlockT, Hash as HashT, Header as HeaderT, ProvideRuntimeApi, DigestFor, BlakeTwo256},
traits::{
Block as BlockT, Hash as HashT, Header as HeaderT, ProvideRuntimeApi, DigestFor, BlakeTwo256
},
generic::BlockId,
ApplyError,
};
use transaction_pool::txpool::{self, Pool as TransactionPool};
use substrate_telemetry::{telemetry, CONSENSUS_INFO};
@@ -170,7 +171,7 @@ impl<Block, B, E, RA, A> Proposer<Block, SubstrateClient<B, E, Block, RA>, A> wh
Ok(()) => {
debug!("[{:?}] Pushed to the block.", pending.hash);
}
Err(error::Error::ApplyExtrinsicFailed(ApplyError::FullBlock)) => {
Err(error::Error::ApplyExtrinsicFailed(e)) if e.exhausted_resources() => {
if is_first {
debug!("[{:?}] Invalid transaction: FullBlock on empty block", pending.hash);
unqueue_invalid.push(pending.hash.clone());
@@ -178,7 +179,7 @@ impl<Block, B, E, RA, A> Proposer<Block, SubstrateClient<B, E, Block, RA>, A> wh
skipped += 1;
debug!(
"Block seems full, but will try {} more transactions before quitting.",
MAX_SKIPPED_TRANSACTIONS - skipped
MAX_SKIPPED_TRANSACTIONS - skipped,
);
} else {
debug!("Block is full, proceed with proposing.");
@@ -17,7 +17,6 @@
use super::api::BlockBuilder as BlockBuilderApi;
use std::vec::Vec;
use codec::Encode;
use sr_primitives::ApplyOutcome;
use sr_primitives::generic::BlockId;
use sr_primitives::traits::{
Header as HeaderT, Hash, Block as BlockT, One, HashFor, ProvideRuntimeApi, ApiRef, DigestFor,
@@ -104,7 +103,7 @@ where
ExecutionContext::BlockConstruction,
xt.clone()
)? {
Ok(ApplyOutcome::Success) | Ok(ApplyOutcome::Fail) => {
Ok(_) => {
extrinsics.push(xt);
Ok(())
}
+5 -5
View File
@@ -101,15 +101,15 @@ impl From<Error> for rpc::Error {
message: format!("Verification Error: {}", e).into(),
data: Some(format!("{:?}", e).into()),
},
Error::Pool(PoolError::InvalidTransaction(code)) => rpc::Error {
Error::Pool(PoolError::InvalidTransaction(e)) => rpc::Error {
code: rpc::ErrorCode::ServerError(POOL_INVALID_TX),
message: "Invalid Transaction".into(),
data: Some(code.into()),
data: serde_json::to_value(e).ok(),
},
Error::Pool(PoolError::UnknownTransactionValidity(code)) => rpc::Error {
Error::Pool(PoolError::UnknownTransaction(e)) => rpc::Error {
code: rpc::ErrorCode::ServerError(POOL_UNKNOWN_VALIDITY),
message: "Unknown Transaction Validity".into(),
data: Some(code.into()),
data: serde_json::to_value(e).ok(),
},
Error::Pool(PoolError::TemporarilyBanned) => rpc::Error {
code: rpc::ErrorCode::ServerError(POOL_TEMPORARILY_BANNED),
@@ -133,7 +133,7 @@ impl From<Error> for rpc::Error {
},
Error::Pool(PoolError::ImmediatelyDropped) => rpc::Error {
code: rpc::ErrorCode::ServerError(POOL_IMMEDIATELY_DROPPED),
message: "Immediately Dropped" .into(),
message: "Immediately Dropped".into(),
data: Some("The transaction couldn't enter the pool because of the limit".into()),
},
Error::UnsupportedKeyType => rpc::Error {
+7 -1
View File
@@ -55,7 +55,13 @@ pub mod offchain;
/// Trait for things which can be printed.
pub trait Printable {
/// Print the object.
fn print(self);
fn print(&self);
}
impl Printable for u8 {
fn print(&self) {
u64::from(*self).print()
}
}
/// Converts a public trait definition into a private trait and set of public functions
+4 -4
View File
@@ -479,19 +479,19 @@ pub fn with_storage<R, F: FnOnce() -> R>(
}
impl<'a> Printable for &'a [u8] {
fn print(self) {
println!("Runtime: {}", HexDisplay::from(&self));
fn print(&self) {
println!("Runtime: {}", HexDisplay::from(self));
}
}
impl<'a> Printable for &'a str {
fn print(self) {
fn print(&self) {
println!("Runtime: {}", self);
}
}
impl Printable for u64 {
fn print(self) {
fn print(&self) {
println!("Runtime: {}", self);
}
}
+5 -4
View File
@@ -1220,7 +1220,7 @@ unsafe fn from_raw_parts(ptr: *mut u8, len: u32) -> Option<Vec<u8>> {
impl Api for () {}
impl<'a> Printable for &'a [u8] {
fn print(self) {
fn print(&self) {
unsafe {
ext_print_hex.get()(self.as_ptr(), self.len() as u32);
}
@@ -1228,7 +1228,7 @@ impl<'a> Printable for &'a [u8] {
}
impl<'a> Printable for &'a str {
fn print(self) {
fn print(&self) {
unsafe {
ext_print_utf8.get()(self.as_ptr() as *const u8, self.len() as u32);
}
@@ -1236,7 +1236,8 @@ impl<'a> Printable for &'a str {
}
impl Printable for u64 {
fn print(self) {
unsafe { ext_print_num.get()(self); }
fn print(&self) {
unsafe { ext_print_num.get()(*self); }
}
}
@@ -17,10 +17,8 @@
//! Generic implementation of an extrinsic that has passed the verification
//! stage.
use rstd::result::Result;
use crate::traits::{
self, Member, MaybeDisplay, SignedExtension, DispatchError, Dispatchable, DispatchResult,
ValidateUnsigned
self, Member, MaybeDisplay, SignedExtension, Dispatchable, ValidateUnsigned,
};
use crate::weights::{GetDispatchInfo, DispatchInfo};
use crate::transaction_validity::TransactionValidity;
@@ -55,28 +53,24 @@ where
self.signed.as_ref().map(|x| &x.0)
}
fn validate<U: ValidateUnsigned<Call=Self::Call>>(&self,
fn validate<U: ValidateUnsigned<Call = Self::Call>>(
&self,
info: DispatchInfo,
len: usize,
) -> TransactionValidity {
if let Some((ref id, ref extra)) = self.signed {
Extra::validate(extra, id, &self.function, info, len).into()
Extra::validate(extra, id, &self.function, info, len)
} else {
match Extra::validate_unsigned(&self.function, info, len) {
Ok(extra) => match U::validate_unsigned(&self.function) {
TransactionValidity::Valid(v) =>
TransactionValidity::Valid(v.combine_with(extra)),
x => x,
},
x => x.into(),
}
let valid = Extra::validate_unsigned(&self.function, info, len)?;
Ok(valid.combine_with(U::validate_unsigned(&self.function)?))
}
}
fn dispatch(self,
fn apply(
self,
info: DispatchInfo,
len: usize,
) -> Result<DispatchResult, DispatchError> {
) -> crate::ApplyResult {
let (maybe_who, pre) = if let Some((id, extra)) = self.signed {
let pre = Extra::pre_dispatch(extra, &id, &self.function, info, len)?;
(Some(id), pre)
@@ -86,7 +80,7 @@ where
};
let res = self.function.dispatch(Origin::from(maybe_who));
Extra::post_dispatch(pre, info, len);
Ok(res)
Ok(res.map_err(Into::into))
}
}
@@ -21,9 +21,11 @@ use std::fmt;
use rstd::prelude::*;
use runtime_io::blake2_256;
use crate::codec::{Decode, Encode, Input, Error};
use crate::traits::{self, Member, MaybeDisplay, SignedExtension, Checkable, Extrinsic};
use super::CheckedExtrinsic;
use codec::{Decode, Encode, Input, Error};
use crate::{
traits::{self, Member, MaybeDisplay, SignedExtension, Checkable, Extrinsic},
generic::CheckedExtrinsic, transaction_validity::{TransactionValidityError, InvalidTransaction},
};
const TRANSACTION_VERSION: u8 = 3;
@@ -101,11 +103,11 @@ where
Signature: Member + traits::Verify<Signer=AccountId>,
Extra: SignedExtension<AccountId=AccountId>,
AccountId: Member + MaybeDisplay,
Lookup: traits::Lookup<Source=Address, Target=AccountId>
Lookup: traits::Lookup<Source=Address, Target=AccountId>,
{
type Checked = CheckedExtrinsic<AccountId, Call, Extra>;
fn check(self, lookup: &Lookup) -> Result<Self::Checked, &'static str> {
fn check(self, lookup: &Lookup) -> Result<Self::Checked, TransactionValidityError> {
Ok(match self.signature {
Some((signed, signature, extra)) => {
let signed = lookup.lookup(signed)?;
@@ -113,7 +115,7 @@ where
if !raw_payload.using_encoded(|payload| {
signature.verify(payload, &signed)
}) {
return Err(crate::BAD_SIGNATURE)
return Err(InvalidTransaction::BadProof.into())
}
let (function, extra, _) = raw_payload.deconstruct();
@@ -136,9 +138,9 @@ where
/// is going to be different than the `SignaturePayload` - so the thing the extrinsic
/// actually contains.
pub struct SignedPayload<Call, Extra: SignedExtension>((
Call,
Extra,
Extra::AdditionalSigned,
Call,
Extra,
Extra::AdditionalSigned,
));
impl<Call, Extra> SignedPayload<Call, Extra> where
@@ -148,7 +150,7 @@ impl<Call, Extra> SignedPayload<Call, Extra> where
/// Create new `SignedPayload`.
///
/// This function may fail if `additional_signed` of `Extra` is not available.
pub fn new(call: Call, extra: Extra) -> Result<Self, &'static str> {
pub fn new(call: Call, extra: Extra) -> Result<Self, TransactionValidityError> {
let additional_signed = extra.additional_signed()?;
let raw_payload = (call, extra, additional_signed);
Ok(Self(raw_payload))
@@ -256,7 +258,12 @@ where
Extra: SignedExtension,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "UncheckedExtrinsic({:?}, {:?})", self.signature.as_ref().map(|x| (&x.0, &x.2)), self.function)
write!(
f,
"UncheckedExtrinsic({:?}, {:?})",
self.signature.as_ref().map(|x| (&x.0, &x.2)),
self.function,
)
}
}
@@ -265,15 +272,10 @@ mod tests {
use super::*;
use runtime_io::blake2_256;
use crate::codec::{Encode, Decode};
use crate::traits::{SignedExtension, Lookup};
use crate::traits::{SignedExtension, IdentityLookup};
use serde::{Serialize, Deserialize};
struct TestContext;
impl Lookup for TestContext {
type Source = u64;
type Target = u64;
fn lookup(&self, s: u64) -> Result<u64, &'static str> { Ok(s) }
}
type TestContext = IdentityLookup<u64>;
#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Encode, Decode)]
struct TestSig(u64, Vec<u8>);
@@ -298,7 +300,7 @@ mod tests {
type AdditionalSigned = ();
type Pre = ();
fn additional_signed(&self) -> rstd::result::Result<(), &'static str> { Ok(()) }
fn additional_signed(&self) -> rstd::result::Result<(), TransactionValidityError> { Ok(()) }
}
type Ex = UncheckedExtrinsic<TestAccountId, TestCall, TestSig, TestExtra>;
@@ -340,7 +342,7 @@ mod tests {
fn unsigned_check_should_work() {
let ux = Ex::new_unsigned(vec![0u8; 0]);
assert!(!ux.is_signed().unwrap_or(false));
assert!(<Ex as Checkable<TestContext>>::check(ux, &TestContext).is_ok());
assert!(<Ex as Checkable<TestContext>>::check(ux, &Default::default()).is_ok());
}
#[test]
@@ -349,10 +351,13 @@ mod tests {
vec![0u8; 0],
TEST_ACCOUNT,
TestSig(TEST_ACCOUNT, vec![0u8; 0]),
TestExtra
TestExtra,
);
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, &Default::default()),
Err(InvalidTransaction::BadProof.into()),
);
}
#[test]
@@ -361,12 +366,12 @@ mod tests {
vec![0u8; 0],
TEST_ACCOUNT,
TestSig(TEST_ACCOUNT, (vec![0u8; 0], TestExtra).encode()),
TestExtra
TestExtra,
);
assert!(ux.is_signed().unwrap_or(false));
assert_eq!(
<Ex as Checkable<TestContext>>::check(ux, &TestContext),
Ok(CEx { signed: Some((TEST_ACCOUNT, TestExtra)), function: vec![0u8; 0] })
<Ex as Checkable<TestContext>>::check(ux, &Default::default()),
Ok(CEx { signed: Some((TEST_ACCOUNT, TestExtra)), function: vec![0u8; 0] }),
);
}
+114 -48
View File
@@ -58,19 +58,6 @@ pub use generic::{DigestItem, Digest};
pub use primitives::crypto::{key_types, KeyTypeId, CryptoType};
pub use app_crypto::AppKey;
/// 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";
/// Justification type.
pub type Justification = Vec<u8>;
@@ -636,53 +623,111 @@ impl From<ed25519::Signature> for AnySignature {
}
}
#[derive(Eq, PartialEq, Clone, Copy, Decode)]
#[derive(Eq, PartialEq, Clone, Copy, Decode, Encode)]
#[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,
/// 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])
}
}
impl codec::EncodeLike for ApplyOutcome {}
#[derive(Eq, PartialEq, Clone, Copy, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Serialize))]
#[repr(u8)]
/// Reason why an extrinsic couldn't be applied (i.e. invalid extrinsic).
pub enum ApplyError {
/// Bad signature.
BadSignature = 0,
/// Nonce too low.
Stale = 1,
/// Nonce too high.
Future = 2,
/// Sending account had too low a balance.
CantPay = 3,
/// Block is full, no more extrinsics can be applied.
FullBlock = 255,
/// General error to do with the permissions of the sender.
NoPermission,
/// General error to do with the state of the system in general.
BadState,
/// Any error to do with the transaction validity.
Validity(transaction_validity::TransactionValidityError),
}
impl codec::Encode for ApplyError {
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
f(&[*self as u8])
impl ApplyError {
/// Returns if the reason for the error was block resource exhaustion.
pub fn exhausted_resources(&self) -> bool {
match self {
Self::Validity(e) => e.exhausted_resources(),
_ => false,
}
}
}
impl codec::EncodeLike for ApplyError {}
impl From<ApplyError> for &'static str {
fn from(err: ApplyError) -> &'static str {
match err {
ApplyError::NoPermission => "Transaction does not have required permissions",
ApplyError::BadState => "System state currently prevents this transaction",
ApplyError::Validity(v) => v.into(),
}
}
}
impl From<transaction_validity::TransactionValidityError> for ApplyError {
fn from(err: transaction_validity::TransactionValidityError) -> Self {
ApplyError::Validity(err)
}
}
/// The outcome of applying a transaction.
pub type ApplyOutcome = Result<(), DispatchError>;
impl From<DispatchError> for ApplyOutcome {
fn from(err: DispatchError) -> Self {
Err(err)
}
}
/// Result from attempt to apply an extrinsic.
pub type ApplyResult = Result<ApplyOutcome, ApplyError>;
#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode)]
#[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: Option<u8>,
/// Module specific error value
pub error: u8,
/// Optional error message.
#[codec(skip)]
pub message: Option<&'static str>,
}
impl DispatchError {
/// Create a new instance of `DispatchError`.
pub fn new(module: Option<u8>, error: u8, message: Option<&'static str>) -> Self {
Self {
module,
error,
message,
}
}
}
impl runtime_io::Printable for DispatchError {
fn print(&self) {
"DispatchError".print();
if let Some(module) = self.module {
module.print();
}
self.error.print();
if let Some(msg) = self.message {
msg.print();
}
}
}
impl traits::ModuleDispatchError for &'static str {
fn as_u8(&self) -> u8 {
0
}
fn as_str(&self) -> &'static str {
self
}
}
impl From<&'static str> for DispatchError {
fn from(err: &'static str) -> DispatchError {
DispatchError::new(None, 0, Some(err))
}
}
/// Verify a signature on an encoded value in a lazy manner. This can be
/// an optimization if the signature scheme has an "unsigned" escape hash.
pub fn verify_encoded_lazy<V: Verify, T: codec::Encode>(sig: &V, item: &T, signer: &V::Signer) -> bool {
@@ -852,6 +897,7 @@ impl traits::Extrinsic for OpaqueExtrinsic {
#[cfg(test)]
mod tests {
use super::DispatchError;
use crate::codec::{Encode, Decode};
use super::{Perbill, Permill};
@@ -967,6 +1013,26 @@ mod tests {
);
}
#[test]
fn dispatch_error_encoding() {
let error = DispatchError {
module: Some(1),
error: 2,
message: Some("error message"),
};
let encoded = error.encode();
let decoded = DispatchError::decode(&mut &encoded[..]).unwrap();
assert_eq!(encoded, vec![1, 1, 2]);
assert_eq!(
decoded,
DispatchError {
module: Some(1),
error: 2,
message: None,
},
);
}
#[test]
fn per_bill_square() {
const FIXTURES: &[(u32, u32)] = &[
+14 -11
View File
@@ -20,14 +20,14 @@ use serde::{Serialize, Serializer, Deserialize, de::Error as DeError, Deserializ
use std::{fmt::Debug, ops::Deref, fmt};
use crate::codec::{Codec, Encode, Decode};
use crate::traits::{
self, Checkable, Applyable, BlakeTwo256, OpaqueKeys, DispatchError, DispatchResult,
ValidateUnsigned, SignedExtension, Dispatchable,
self, Checkable, Applyable, BlakeTwo256, OpaqueKeys, ValidateUnsigned,
SignedExtension, Dispatchable,
};
use crate::{generic, KeyTypeId};
use crate::{generic, KeyTypeId, ApplyResult};
use crate::weights::{GetDispatchInfo, DispatchInfo};
pub use primitives::H256;
use primitives::{crypto::{CryptoType, Dummy, key_types, Public}, U256};
use crate::transaction_validity::TransactionValidity;
use crate::transaction_validity::{TransactionValidity, TransactionValidityError};
/// Authority Id
#[derive(Default, PartialEq, Eq, Clone, Encode, Decode, Debug, Hash, Serialize, Deserialize)]
@@ -271,7 +271,7 @@ impl<Call, Extra> Debug for TestXt<Call, Extra> {
impl<Call: Codec + Sync + Send, Context, Extra> Checkable<Context> for TestXt<Call, Extra> {
type Checked = Self;
fn check(self, _: &Context) -> Result<Self::Checked, &'static str> { Ok(self) }
fn check(self, _: &Context) -> Result<Self::Checked, TransactionValidityError> { Ok(self) }
}
impl<Call: Codec + Sync + Send, Extra> traits::Extrinsic for TestXt<Call, Extra> {
type Call = Call;
@@ -289,7 +289,7 @@ impl<Call: Codec + Sync + Send, Extra> traits::Extrinsic for TestXt<Call, Extra>
impl<Origin, Call, Extra> Applyable for TestXt<Call, Extra> where
Call: 'static + Sized + Send + Sync + Clone + Eq + Codec + Debug + Dispatchable<Origin=Origin>,
Extra: SignedExtension<AccountId=u64, Call=Call>,
Origin: From<Option<u64>>
Origin: From<Option<u64>>,
{
type AccountId = u64;
type Call = Call;
@@ -297,19 +297,21 @@ impl<Origin, Call, Extra> Applyable for TestXt<Call, Extra> where
fn sender(&self) -> Option<&Self::AccountId> { self.0.as_ref().map(|x| &x.0) }
/// Checks to see if this is a valid *transaction*. It returns information on it if so.
fn validate<U: ValidateUnsigned<Call=Self::Call>>(&self,
fn validate<U: ValidateUnsigned<Call=Self::Call>>(
&self,
_info: DispatchInfo,
_len: usize,
) -> TransactionValidity {
TransactionValidity::Valid(Default::default())
Ok(Default::default())
}
/// Executes all necessary logic needed prior to dispatch and deconstructs into function call,
/// index and sender.
fn dispatch(self,
fn apply(
self,
info: DispatchInfo,
len: usize,
) -> Result<DispatchResult, DispatchError> {
) -> ApplyResult {
let maybe_who = if let Some((who, extra)) = self.0 {
Extra::pre_dispatch(extra, &who, &self.1, info, len)?;
Some(who)
@@ -317,7 +319,8 @@ impl<Origin, Call, Extra> Applyable for TestXt<Call, Extra> where
Extra::pre_dispatch_unsigned(&self.1, info, len)?;
None
};
Ok(self.1.dispatch(maybe_who.into()))
Ok(self.1.dispatch(maybe_who.into()).map_err(Into::into))
}
}
+101 -84
View File
@@ -23,7 +23,9 @@ use runtime_io;
#[cfg(feature = "std")] use serde::{Serialize, Deserialize, de::DeserializeOwned};
use primitives::{self, Hasher, Blake2Hasher};
use crate::codec::{Codec, Encode, Decode, HasCompact};
use crate::transaction_validity::{ValidTransaction, TransactionValidity};
use crate::transaction_validity::{
ValidTransaction, TransactionValidity, TransactionValidityError, UnknownTransaction,
};
use crate::generic::{Digest, DigestItem};
use crate::weights::DispatchInfo;
pub use integer_sqrt::IntegerSquareRoot;
@@ -93,18 +95,44 @@ impl<
}
}
/// An error type that indicates that the origin is invalid.
#[derive(Encode, Decode)]
pub struct InvalidOrigin;
impl From<InvalidOrigin> for &'static str {
fn from(_: InvalidOrigin) -> &'static str {
"Invalid origin"
}
}
/// Some sort of check on the origin is performed by this object.
pub trait EnsureOrigin<OuterOrigin> {
/// A return type.
type Success;
/// Perform the origin check.
fn ensure_origin(o: OuterOrigin) -> result::Result<Self::Success, &'static str> {
Self::try_origin(o).map_err(|_| "Invalid origin")
fn ensure_origin(o: OuterOrigin) -> result::Result<Self::Success, InvalidOrigin> {
Self::try_origin(o).map_err(|_| InvalidOrigin)
}
/// Perform the origin check.
fn try_origin(o: OuterOrigin) -> result::Result<Self::Success, OuterOrigin>;
}
/// An error that indicates that a lookup failed.
#[derive(Encode, Decode)]
pub struct LookupError;
impl From<LookupError> for &'static str {
fn from(_: LookupError) -> &'static str {
"Can not lookup"
}
}
impl From<LookupError> for TransactionValidityError {
fn from(_: LookupError) -> Self {
UnknownTransaction::CannotLookup.into()
}
}
/// Means of changing one type into another in a manner dependent on the source type.
pub trait Lookup {
/// Type to lookup from.
@@ -112,7 +140,7 @@ pub trait Lookup {
/// Type to lookup into.
type Target;
/// Attempt a lookup.
fn lookup(&self, s: Self::Source) -> result::Result<Self::Target, &'static str>;
fn lookup(&self, s: Self::Source) -> Result<Self::Target, LookupError>;
}
/// Means of changing one type into another in a manner dependent on the source type.
@@ -124,7 +152,7 @@ pub trait StaticLookup {
/// Type to lookup into.
type Target;
/// Attempt a lookup.
fn lookup(s: Self::Source) -> result::Result<Self::Target, &'static str>;
fn lookup(s: Self::Source) -> Result<Self::Target, LookupError>;
/// Convert from Target back to Source.
fn unlookup(t: Self::Target) -> Self::Source;
}
@@ -135,13 +163,14 @@ pub struct IdentityLookup<T>(PhantomData<T>);
impl<T: Codec + Clone + PartialEq + MaybeDebug> StaticLookup for IdentityLookup<T> {
type Source = T;
type Target = T;
fn lookup(x: T) -> result::Result<T, &'static str> { Ok(x) }
fn lookup(x: T) -> Result<T, LookupError> { Ok(x) }
fn unlookup(x: T) -> T { x }
}
impl<T> Lookup for IdentityLookup<T> {
type Source = T;
type Target = T;
fn lookup(&self, x: T) -> result::Result<T, &'static str> { Ok(x) }
fn lookup(&self, x: T) -> Result<T, LookupError> { Ok(x) }
}
/// Extensible conversion trait. Generic over both source and destination types.
@@ -781,7 +810,7 @@ pub trait Checkable<Context>: Sized {
type Checked;
/// Check self, given an instance of Context.
fn check(self, c: &Context) -> Result<Self::Checked, &'static str>;
fn check(self, c: &Context) -> Result<Self::Checked, TransactionValidityError>;
}
/// A "checkable" piece of information, used by the standard Substrate Executive in order to
@@ -793,61 +822,21 @@ pub trait BlindCheckable: Sized {
type Checked;
/// Check self.
fn check(self) -> Result<Self::Checked, &'static str>;
fn check(self) -> Result<Self::Checked, TransactionValidityError>;
}
// Every `BlindCheckable` is also a `StaticCheckable` for arbitrary `Context`.
impl<T: BlindCheckable, Context> Checkable<Context> for T {
type Checked = <Self as BlindCheckable>::Checked;
fn check(self, _c: &Context) -> Result<Self::Checked, &'static str> {
fn check(self, _c: &Context) -> Result<Self::Checked, TransactionValidityError> {
BlindCheckable::check(self)
}
}
/// An abstract error concerning an attempt to verify, check or dispatch the transaction. This
/// cannot be more concrete because it's designed to work reasonably well over a broad range of
/// possible transaction types.
#[cfg_attr(feature = "std", derive(PartialEq, Debug))]
pub enum DispatchError {
/// General error to do with the inability to pay some fees (e.g. account balance too low).
Payment,
/// General error to do with the exhaustion of block resources.
Exhausted,
/// General error to do with the permissions of the sender.
NoPermission,
/// General error to do with the state of the system in general.
BadState,
/// General error to do with the transaction being outdated (e.g. nonce too low).
Stale,
/// General error to do with the transaction not yet being valid (e.g. nonce too high).
Future,
/// General error to do with the transaction's proofs (e.g. signature).
BadProof,
}
impl From<DispatchError> for i8 {
fn from(e: DispatchError) -> i8 {
match e {
DispatchError::Payment => -64,
DispatchError::Exhausted => -65,
DispatchError::NoPermission => -66,
DispatchError::BadState => -67,
DispatchError::Stale => -68,
DispatchError::Future => -69,
DispatchError::BadProof => -70,
}
}
}
/// Result of a module function call; either nothing (functions are only called for "side effects")
/// or an error message.
pub type DispatchResult = result::Result<(), &'static str>;
pub type DispatchResult<Error> = result::Result<(), Error>;
/// A lazy call (module function and argument values) that can be executed via its `dispatch`
/// method.
@@ -858,15 +847,15 @@ pub trait Dispatchable {
type Origin;
/// ...
type Trait;
/// The error type returned by this dispatchable.
type Error: Into<crate::DispatchError>;
/// Actually dispatch this call and result the result of it.
fn dispatch(self, origin: Self::Origin) -> DispatchResult;
fn dispatch(self, origin: Self::Origin) -> DispatchResult<Self::Error>;
}
/// Means by which a transaction may be extended. This type embodies both the data and the logic
/// that should be additionally associated with the transaction. It should be plain old data.
pub trait SignedExtension:
Codec + MaybeDebug + Sync + Send + Clone + Eq + PartialEq
{
pub trait SignedExtension: Codec + MaybeDebug + Sync + Send + Clone + Eq + PartialEq {
/// The type which encodes the sender identity.
type AccountId;
@@ -882,7 +871,7 @@ pub trait SignedExtension:
/// Construct any additional data that should be in the signed payload of the transaction. Can
/// also perform any pre-signature-verification checks and return an error if needed.
fn additional_signed(&self) -> Result<Self::AdditionalSigned, &'static str>;
fn additional_signed(&self) -> Result<Self::AdditionalSigned, TransactionValidityError>;
/// Validate a signed transaction for the transaction queue.
///
@@ -899,8 +888,8 @@ pub trait SignedExtension:
_call: &Self::Call,
_info: DispatchInfo,
_len: usize,
) -> Result<ValidTransaction, DispatchError> {
Ok(Default::default())
) -> TransactionValidity {
Ok(ValidTransaction::default())
}
/// Do any pre-flight stuff for a signed transaction.
@@ -917,8 +906,10 @@ pub trait SignedExtension:
call: &Self::Call,
info: DispatchInfo,
len: usize,
) -> Result<Self::Pre, DispatchError> {
self.validate(who, call, info, len).map(|_| Self::Pre::default())
) -> Result<Self::Pre, crate::ApplyError> {
self.validate(who, call, info, len)
.map(|_| Self::Pre::default())
.map_err(Into::into)
}
/// Validate an unsigned transaction for the transaction queue.
@@ -936,7 +927,9 @@ pub trait SignedExtension:
_call: &Self::Call,
_info: DispatchInfo,
_len: usize,
) -> Result<ValidTransaction, DispatchError> { Ok(Default::default()) }
) -> TransactionValidity {
Ok(ValidTransaction::default())
}
/// Do any pre-flight stuff for a unsigned transaction.
///
@@ -950,16 +943,25 @@ pub trait SignedExtension:
call: &Self::Call,
info: DispatchInfo,
len: usize,
) -> Result<Self::Pre, DispatchError> {
Self::validate_unsigned(call, info, len).map(|_| Self::Pre::default())
) -> Result<Self::Pre, crate::ApplyError> {
Self::validate_unsigned(call, info, len)
.map(|_| Self::Pre::default())
.map_err(Into::into)
}
/// Do any post-flight stuff for a transaction.
fn post_dispatch(
_pre: Self::Pre,
_info: DispatchInfo,
_len: usize,
) { }
fn post_dispatch(_pre: Self::Pre, _info: DispatchInfo, _len: usize) { }
}
/// An error that is returned by a dispatchable function of a module.
pub trait ModuleDispatchError {
/// Convert this error to an `u8`.
///
/// The `u8` corresponds to the index of the variant in the error enum.
fn as_u8(&self) -> u8;
/// Convert the error to a `&'static str`.
fn as_str(&self) -> &'static str;
}
macro_rules! tuple_impl_indexed {
@@ -974,10 +976,10 @@ macro_rules! tuple_impl_indexed {
> SignedExtension for ($($direct),+,) {
type AccountId = AccountId;
type Call = Call;
type AdditionalSigned = ($($direct::AdditionalSigned,)+);
type AdditionalSigned = ( $( $direct::AdditionalSigned, )+ );
type Pre = ($($direct::Pre,)+);
fn additional_signed(&self) -> Result<Self::AdditionalSigned, &'static str> {
Ok(( $(self.$index.additional_signed()?,)+ ))
fn additional_signed(&self) -> Result<Self::AdditionalSigned, TransactionValidityError> {
Ok(( $( self.$index.additional_signed()?, )+ ))
}
fn validate(
&self,
@@ -985,9 +987,16 @@ macro_rules! tuple_impl_indexed {
call: &Self::Call,
info: DispatchInfo,
len: usize,
) -> Result<ValidTransaction, DispatchError> {
let aggregator = vec![$(<$direct as SignedExtension>::validate(&self.$index, who, call, info, len)?),+];
Ok(aggregator.into_iter().fold(ValidTransaction::default(), |acc, a| acc.combine_with(a)))
) -> TransactionValidity {
let aggregator = vec![
$( <$direct as SignedExtension>::validate(&self.$index, who, call, info, len)? ),+
];
Ok(
aggregator.into_iter().fold(
ValidTransaction::default(),
|acc, a| acc.combine_with(a),
)
)
}
fn pre_dispatch(
self,
@@ -995,22 +1004,28 @@ macro_rules! tuple_impl_indexed {
call: &Self::Call,
info: DispatchInfo,
len: usize,
) -> Result<Self::Pre, DispatchError> {
) -> Result<Self::Pre, $crate::ApplyError> {
Ok(($(self.$index.pre_dispatch(who, call, info, len)?,)+))
}
fn validate_unsigned(
call: &Self::Call,
info: DispatchInfo,
len: usize,
) -> Result<ValidTransaction, DispatchError> {
let aggregator = vec![$($direct::validate_unsigned(call, info, len)?),+];
Ok(aggregator.into_iter().fold(ValidTransaction::default(), |acc, a| acc.combine_with(a)))
) -> TransactionValidity {
let aggregator = vec![ $( $direct::validate_unsigned(call, info, len)? ),+ ];
Ok(
aggregator.into_iter().fold(
ValidTransaction::default(),
|acc, a| acc.combine_with(a),
)
)
}
fn pre_dispatch_unsigned(
call: &Self::Call,
info: DispatchInfo,
len: usize,
) -> Result<Self::Pre, DispatchError> {
) -> Result<Self::Pre, $crate::ApplyError> {
Ok(($($direct::pre_dispatch_unsigned(call, info, len)?,)+))
}
fn post_dispatch(
@@ -1047,7 +1062,7 @@ impl SignedExtension for () {
type AdditionalSigned = ();
type Call = ();
type Pre = ();
fn additional_signed(&self) -> rstd::result::Result<(), &'static str> { Ok(()) }
fn additional_signed(&self) -> rstd::result::Result<(), TransactionValidityError> { Ok(()) }
}
/// An "executable" piece of information, used by the standard Substrate Executive in order to
@@ -1067,17 +1082,19 @@ pub trait Applyable: Sized + Send + Sync {
fn sender(&self) -> Option<&Self::AccountId>;
/// Checks to see if this is a valid *transaction*. It returns information on it if so.
fn validate<V: ValidateUnsigned<Call=Self::Call>>(&self,
fn validate<V: ValidateUnsigned<Call=Self::Call>>(
&self,
info: DispatchInfo,
len: usize,
) -> TransactionValidity;
/// Executes all necessary logic needed prior to dispatch and deconstructs into function call,
/// index and sender.
fn dispatch(self,
fn apply(
self,
info: DispatchInfo,
len: usize,
) -> Result<DispatchResult, DispatchError>;
) -> crate::ApplyResult;
}
/// Auxiliary wrapper that holds an api instance and binds it to the given lifetime.
@@ -17,8 +17,7 @@
//! Transaction validity interface.
use rstd::prelude::*;
use crate::codec::{Encode, Decode, Error};
use crate::traits::DispatchError;
use crate::codec::{Encode, Decode};
/// Priority for a transaction. Additive. Higher is better.
pub type TransactionPriority = u64;
@@ -30,29 +29,151 @@ pub type TransactionLongevity = u64;
/// Tag for a transaction. No two transactions with the same tag should be placed on-chain.
pub type TransactionTag = Vec<u8>;
/// Information on a transaction's validity and, if valid, on how it relates to other transactions.
#[derive(Clone, PartialEq, Eq, Encode)]
#[cfg_attr(feature = "std", derive(Debug))]
pub enum TransactionValidity {
/// Transaction is invalid. Details are described by the error code.
Invalid(i8),
/// Transaction is valid.
Valid(ValidTransaction),
/// Transaction validity can't be determined.
Unknown(i8),
/// An invalid transaction validity.
#[derive(Clone, PartialEq, Eq, Encode, Decode, Copy)]
#[cfg_attr(feature = "std", derive(Debug, serde::Serialize))]
pub enum InvalidTransaction {
/// The call of the transaction is not expected.
Call,
/// General error to do with the inability to pay some fees (e.g. account balance too low).
Payment,
/// General error to do with the transaction not yet being valid (e.g. nonce too high).
Future,
/// General error to do with the transaction being outdated (e.g. nonce too low).
Stale,
/// General error to do with the transaction's proofs (e.g. signature).
BadProof,
/// The transaction birth block is ancient.
AncientBirthBlock,
/// The transaction would exhaust the resources of current block.
///
/// The transaction might be valid, but there are not enough resources left in the current block.
ExhaustsResources,
/// Any other custom invalid validity that is not covered by this enum.
Custom(u8),
}
impl From<Result<ValidTransaction, DispatchError>> for TransactionValidity {
fn from(r: Result<ValidTransaction, DispatchError>) -> Self {
match r {
Ok(v) => TransactionValidity::Valid(v),
Err(e) => TransactionValidity::Invalid(e.into()),
impl InvalidTransaction {
/// Returns if the reason for the invalidity was block resource exhaustion.
pub fn exhausted_resources(&self) -> bool {
match self {
Self::ExhaustsResources => true,
_ => false,
}
}
}
impl From<InvalidTransaction> for &'static str {
fn from(invalid: InvalidTransaction) -> &'static str {
match invalid {
InvalidTransaction::Call => "Transaction call is not expected",
InvalidTransaction::Future => "Transaction will be valid in the future",
InvalidTransaction::Stale => "Transaction is outdated",
InvalidTransaction::BadProof => "Transaction has a bad signature",
InvalidTransaction::AncientBirthBlock => "Transaction has an ancient birth block",
InvalidTransaction::ExhaustsResources =>
"Transaction would exhausts the block limits",
InvalidTransaction::Payment =>
"Inability to pay some fees (e.g. account balance too low)",
InvalidTransaction::Custom(_) => "InvalidTransaction custom error",
}
}
}
/// An unknown transaction validity.
#[derive(Clone, PartialEq, Eq, Encode, Decode, Copy)]
#[cfg_attr(feature = "std", derive(Debug, serde::Serialize))]
pub enum UnknownTransaction {
/// Could not lookup some information that is required to validate the transaction.
CannotLookup,
/// No validator found for the given unsigned transaction.
NoUnsignedValidator,
/// Any other custom unknown validity that is not covered by this enum.
Custom(u8),
}
impl From<UnknownTransaction> for &'static str {
fn from(unknown: UnknownTransaction) -> &'static str {
match unknown {
UnknownTransaction::CannotLookup =>
"Could not lookup information required to validate the transaction",
UnknownTransaction::NoUnsignedValidator =>
"Could not find an unsigned validator for the unsigned transaction",
UnknownTransaction::Custom(_) => "UnknownTransaction custom error",
}
}
}
/// Errors that can occur while checking the validity of a transaction.
#[derive(Clone, PartialEq, Eq, Encode, Decode, Copy)]
#[cfg_attr(feature = "std", derive(Debug, serde::Serialize))]
pub enum TransactionValidityError {
/// The transaction is invalid.
Invalid(InvalidTransaction),
/// Transaction validity can't be determined.
Unknown(UnknownTransaction),
}
impl TransactionValidityError {
/// Returns `true` if the reason for the error was block resource exhaustion.
pub fn exhausted_resources(&self) -> bool {
match self {
Self::Invalid(e) => e.exhausted_resources(),
Self::Unknown(_) => false,
}
}
}
impl From<TransactionValidityError> for &'static str {
fn from(err: TransactionValidityError) -> &'static str {
match err {
TransactionValidityError::Invalid(invalid) => invalid.into(),
TransactionValidityError::Unknown(unknown) => unknown.into(),
}
}
}
impl From<InvalidTransaction> for TransactionValidityError {
fn from(err: InvalidTransaction) -> Self {
TransactionValidityError::Invalid(err)
}
}
impl From<UnknownTransaction> for TransactionValidityError {
fn from(err: UnknownTransaction) -> Self {
TransactionValidityError::Unknown(err)
}
}
impl From<InvalidTransaction> for crate::ApplyError {
fn from(invalid: InvalidTransaction) -> crate::ApplyError {
TransactionValidityError::from(invalid).into()
}
}
impl From<UnknownTransaction> for crate::ApplyError {
fn from(unknown: UnknownTransaction) -> crate::ApplyError {
TransactionValidityError::from(unknown).into()
}
}
/// Information on a transaction's validity and, if valid, on how it relates to other transactions.
pub type TransactionValidity = Result<ValidTransaction, TransactionValidityError>;
impl Into<TransactionValidity> for InvalidTransaction {
fn into(self) -> TransactionValidity {
Err(self.into())
}
}
impl Into<TransactionValidity> for UnknownTransaction {
fn into(self) -> TransactionValidity {
Err(self.into())
}
}
/// Information concerning a valid transaction.
#[derive(Clone, PartialEq, Eq, Encode)]
#[derive(Clone, PartialEq, Eq, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct ValidTransaction {
/// Priority of the transaction.
@@ -112,49 +233,13 @@ impl ValidTransaction {
}
}
impl Decode for TransactionValidity {
fn decode<I: crate::codec::Input>(value: &mut I) -> Result<Self, Error> {
match value.read_byte()? {
0 => Ok(TransactionValidity::Invalid(i8::decode(value)?)),
1 => {
let priority = TransactionPriority::decode(value)?;
let requires = Vec::decode(value)?;
let provides = Vec::decode(value)?;
let longevity = TransactionLongevity::decode(value)?;
let propagate = bool::decode(value).unwrap_or(true);
Ok(TransactionValidity::Valid(ValidTransaction {
priority, requires, provides, longevity, propagate,
}))
},
2 => Ok(TransactionValidity::Unknown(i8::decode(value)?)),
_ => Err("Invalid transaction validity variant".into()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_decode_with_backward_compat() {
let old_encoding = vec![
1, 5, 0, 0, 0, 0, 0, 0, 0, 4, 16, 1, 2, 3, 4, 4, 12, 4, 5, 6, 42, 0, 0, 0, 0, 0, 0, 0
];
assert_eq!(TransactionValidity::decode(&mut &*old_encoding), Ok(TransactionValidity::Valid(ValidTransaction {
priority: 5,
requires: vec![vec![1, 2, 3, 4]],
provides: vec![vec![4, 5, 6]],
longevity: 42,
propagate: true,
})));
}
#[test]
fn should_encode_and_decode() {
let v = TransactionValidity::Valid(ValidTransaction {
let v: TransactionValidity = Ok(ValidTransaction {
priority: 5,
requires: vec![vec![1, 2, 3, 4]],
provides: vec![vec![4, 5, 6]],
@@ -165,7 +250,7 @@ mod tests {
let encoded = v.encode();
assert_eq!(
encoded,
vec![1, 5, 0, 0, 0, 0, 0, 0, 0, 4, 16, 1, 2, 3, 4, 4, 12, 4, 5, 6, 42, 0, 0, 0, 0, 0, 0, 0, 0]
vec![0, 5, 0, 0, 0, 0, 0, 0, 0, 4, 16, 1, 2, 3, 4, 4, 12, 4, 5, 6, 42, 0, 0, 0, 0, 0, 0, 0, 0]
);
// decode back
+8 -6
View File
@@ -38,7 +38,9 @@ use substrate_client::{
};
use sr_primitives::{
ApplyResult, create_runtime_str, Perbill, impl_opaque_keys,
transaction_validity::{TransactionValidity, ValidTransaction},
transaction_validity::{
TransactionValidity, ValidTransaction, TransactionValidityError, InvalidTransaction,
},
traits::{
BlindCheckable, BlakeTwo256, Block as BlockT, Extrinsic as ExtrinsicT,
GetNodeBlockType, GetRuntimeBlockType, Verify, IdentityLookup,
@@ -123,17 +125,17 @@ impl serde::Serialize for Extrinsic {
impl BlindCheckable for Extrinsic {
type Checked = Self;
fn check(self) -> Result<Self, &'static str> {
fn check(self) -> Result<Self, TransactionValidityError> {
match self {
Extrinsic::AuthoritiesChange(new_auth) => Ok(Extrinsic::AuthoritiesChange(new_auth)),
Extrinsic::Transfer(transfer, signature) => {
if sr_primitives::verify_encoded_lazy(&signature, &transfer, &transfer.from) {
Ok(Extrinsic::Transfer(transfer, signature))
} else {
Err(sr_primitives::BAD_SIGNATURE)
Err(InvalidTransaction::BadProof.into())
}
},
Extrinsic::IncludeData(_) => Err(sr_primitives::BAD_SIGNATURE),
Extrinsic::IncludeData(_) => Err(InvalidTransaction::BadProof.into()),
Extrinsic::StorageChange(key, value) => Ok(Extrinsic::StorageChange(key, value)),
}
}
@@ -478,7 +480,7 @@ cfg_if! {
impl client_api::TaggedTransactionQueue<Block> for Runtime {
fn validate_transaction(utx: <Block as BlockT>::Extrinsic) -> TransactionValidity {
if let Extrinsic::IncludeData(data) = utx {
return TransactionValidity::Valid(ValidTransaction {
return Ok(ValidTransaction {
priority: data.len() as u64,
requires: vec![],
provides: vec![data],
@@ -662,7 +664,7 @@ cfg_if! {
impl client_api::TaggedTransactionQueue<Block> for Runtime {
fn validate_transaction(utx: <Block as BlockT>::Extrinsic) -> TransactionValidity {
if let Extrinsic::IncludeData(data) = utx {
return TransactionValidity::Valid(ValidTransaction{
return Ok(ValidTransaction{
priority: data.len() as u64,
requires: vec![],
provides: vec![data],
+21 -22
View File
@@ -21,12 +21,12 @@ use rstd::prelude::*;
use runtime_io::{storage_root, ordered_trie_root, storage_changes_root, twox_128, blake2_256};
use runtime_support::storage::{self, StorageValue, StorageMap};
use runtime_support::storage_items;
use sr_primitives::traits::{Hash as HashT, BlakeTwo256, Header as _};
use sr_primitives::generic;
use sr_primitives::{ApplyError, ApplyOutcome, ApplyResult};
use sr_primitives::transaction_validity::{TransactionValidity, ValidTransaction};
use sr_primitives::{
traits::{Hash as HashT, BlakeTwo256, Header as _}, generic, ApplyError, ApplyResult,
transaction_validity::{TransactionValidity, ValidTransaction, InvalidTransaction},
};
use codec::{KeyedVec, Encode};
use super::{
use crate::{
AccountId, BlockNumber, Extrinsic, Transfer, H256 as Hash, Block, Header, Digest, AuthorityId
};
use primitives::{Blake2Hasher, storage::well_known_keys};
@@ -119,7 +119,7 @@ fn execute_block_with_state_root_handler(
// execute transactions
block.extrinsics.iter().enumerate().for_each(|(i, e)| {
storage::unhashed::put(well_known_keys::EXTRINSIC_INDEX, &(i as u32));
execute_transaction_backend(e).unwrap_or_else(|_| panic!("Invalid transaction"));
let _ = execute_transaction_backend(e).unwrap_or_else(|_| panic!("Invalid transaction"));
storage::unhashed::kill(well_known_keys::EXTRINSIC_INDEX);
});
@@ -158,17 +158,17 @@ impl executive::ExecuteBlock<Block> for BlockExecutor {
/// This doesn't attempt to validate anything regarding the block.
pub fn validate_transaction(utx: Extrinsic) -> TransactionValidity {
if check_signature(&utx).is_err() {
return TransactionValidity::Invalid(ApplyError::BadSignature as i8);
return InvalidTransaction::BadProof.into();
}
let tx = utx.transfer();
let nonce_key = tx.from.to_keyed_vec(NONCE_OF);
let expected_nonce: u64 = storage::hashed::get_or(&blake2_256, &nonce_key, 0);
if tx.nonce < expected_nonce {
return TransactionValidity::Invalid(ApplyError::Stale as i8);
return InvalidTransaction::Stale.into();
}
if tx.nonce > expected_nonce + 64 {
return TransactionValidity::Unknown(ApplyError::Future as i8);
return InvalidTransaction::Future.into();
}
let hash = |from: &AccountId, nonce: u64| {
@@ -188,7 +188,7 @@ pub fn validate_transaction(utx: Extrinsic) -> TransactionValidity {
p
};
TransactionValidity::Valid(ValidTransaction {
Ok(ValidTransaction {
priority: tx.amount,
requires,
provides,
@@ -244,8 +244,7 @@ pub fn finalize_block() -> Header {
#[inline(always)]
fn check_signature(utx: &Extrinsic) -> Result<(), ApplyError> {
use sr_primitives::traits::BlindCheckable;
utx.clone().check().map_err(|_| ApplyError::BadSignature)?;
Ok(())
utx.clone().check().map_err(|_| InvalidTransaction::BadProof.into()).map(|_| ())
}
fn execute_transaction_backend(utx: &Extrinsic) -> ApplyResult {
@@ -253,7 +252,7 @@ fn execute_transaction_backend(utx: &Extrinsic) -> ApplyResult {
match utx {
Extrinsic::Transfer(ref transfer, _) => execute_transfer_backend(transfer),
Extrinsic::AuthoritiesChange(ref new_auth) => execute_new_authorities_backend(new_auth),
Extrinsic::IncludeData(_) => Ok(ApplyOutcome::Success),
Extrinsic::IncludeData(_) => Ok(Ok(())),
Extrinsic::StorageChange(key, value) => execute_storage_change(key, value.as_ref().map(|v| &**v)),
}
}
@@ -263,7 +262,7 @@ fn execute_transfer_backend(tx: &Transfer) -> ApplyResult {
let nonce_key = tx.from.to_keyed_vec(NONCE_OF);
let expected_nonce: u64 = storage::hashed::get_or(&blake2_256, &nonce_key, 0);
if !(tx.nonce == expected_nonce) {
return Err(ApplyError::Stale)
return Err(InvalidTransaction::Stale.into());
}
// increment nonce in storage
@@ -275,18 +274,18 @@ fn execute_transfer_backend(tx: &Transfer) -> ApplyResult {
// enact transfer
if !(tx.amount <= from_balance) {
return Err(ApplyError::CantPay)
return Err(InvalidTransaction::Payment.into());
}
let to_balance_key = tx.to.to_keyed_vec(BALANCE_OF);
let to_balance: u64 = storage::hashed::get_or(&blake2_256, &to_balance_key, 0);
storage::hashed::put(&blake2_256, &from_balance_key, &(from_balance - tx.amount));
storage::hashed::put(&blake2_256, &to_balance_key, &(to_balance + tx.amount));
Ok(ApplyOutcome::Success)
Ok(Ok(()))
}
fn execute_new_authorities_backend(new_authorities: &[AuthorityId]) -> ApplyResult {
NewAuthorities::put(new_authorities.to_vec());
Ok(ApplyOutcome::Success)
Ok(Ok(()))
}
fn execute_storage_change(key: &[u8], value: Option<&[u8]>) -> ApplyResult {
@@ -294,7 +293,7 @@ fn execute_storage_change(key: &[u8], value: Option<&[u8]>) -> ApplyResult {
Some(value) => storage::unhashed::put_raw(key, value),
None => storage::unhashed::kill(key),
}
Ok(ApplyOutcome::Success)
Ok(Ok(()))
}
#[cfg(feature = "std")]
@@ -304,7 +303,7 @@ fn info_expect_equal_hash(given: &Hash, expected: &Hash) {
println!(
"Hash: given={}, expected={}",
HexDisplay::from(given.as_fixed_bytes()),
HexDisplay::from(expected.as_fixed_bytes())
HexDisplay::from(expected.as_fixed_bytes()),
);
}
}
@@ -312,9 +311,9 @@ fn info_expect_equal_hash(given: &Hash, expected: &Hash) {
#[cfg(not(feature = "std"))]
fn info_expect_equal_hash(given: &Hash, expected: &Hash) {
if given != expected {
::runtime_io::print("Hash not equal");
::runtime_io::print(given.as_bytes());
::runtime_io::print(expected.as_bytes());
runtime_io::print("Hash not equal");
runtime_io::print(given.as_bytes());
runtime_io::print(expected.as_bytes());
}
}
@@ -16,7 +16,9 @@
//! Transaction pool errors.
use sr_primitives::transaction_validity::TransactionPriority as Priority;
use sr_primitives::transaction_validity::{
TransactionPriority as Priority, InvalidTransaction, UnknownTransaction,
};
/// Transaction pool result.
pub type Result<T> = std::result::Result<T, Error>;
@@ -25,11 +27,11 @@ pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, derive_more::Display, derive_more::From)]
pub enum Error {
/// Transaction is not verifiable yet, but might be in the future.
#[display(fmt="Unkown Transaction Validity. Error code: {}", _0)]
UnknownTransactionValidity(i8),
#[display(fmt="Unknown transaction validity: {:?}", _0)]
UnknownTransaction(UnknownTransaction),
/// Transaction is invalid.
#[display(fmt="Invalid Transaction. Error Code: {}", _0)]
InvalidTransaction(i8),
#[display(fmt="Invalid transaction validity: {:?}", _0)]
InvalidTransaction(InvalidTransaction),
/// The transaction validity returned no "provides" tag.
///
/// Such transactions are not accepted to the pool, since we use those tags
@@ -34,7 +34,7 @@ use parking_lot::{Mutex, RwLock};
use sr_primitives::{
generic::BlockId,
traits::{self, SaturatedConversion},
transaction_validity::{TransactionValidity, TransactionTag as Tag},
transaction_validity::{TransactionValidity, TransactionTag as Tag, TransactionValidityError},
};
pub use crate::base_pool::Limit;
@@ -129,7 +129,7 @@ impl<B: ChainApi> Pool<B> {
}
match self.api.validate_transaction(at, xt.clone())? {
TransactionValidity::Valid(validity) => if validity.provides.is_empty() {
Ok(validity) => if validity.provides.is_empty() {
Err(error::Error::NoTagsProvided.into())
} else {
Ok(base::Transaction {
@@ -145,12 +145,12 @@ impl<B: ChainApi> Pool<B> {
.saturating_add(validity.longevity),
})
},
TransactionValidity::Invalid(e) => {
Err(TransactionValidityError::Invalid(e)) => {
Err(error::Error::InvalidTransaction(e).into())
},
TransactionValidity::Unknown(e) => {
Err(TransactionValidityError::Unknown(e)) => {
self.listener.write().invalid(&hash);
Err(error::Error::UnknownTransactionValidity(e).into())
Err(error::Error::UnknownTransaction(e).into())
},
}
})
@@ -244,7 +244,7 @@ impl<B: ChainApi> Pool<B> {
None => {
let validity = self.api.validate_transaction(parent, extrinsic.clone());
match validity {
Ok(TransactionValidity::Valid(mut validity)) => {
Ok(Ok(mut validity)) => {
tags.append(&mut validity.provides);
},
// silently ignore invalid extrinsics,
@@ -453,7 +453,7 @@ fn fire_events<H, H2, Ex>(
#[cfg(test)]
mod tests {
use super::*;
use sr_primitives::transaction_validity::ValidTransaction;
use sr_primitives::transaction_validity::{ValidTransaction, InvalidTransaction};
use codec::Encode;
use test_runtime::{Block, Extrinsic, Transfer, H256, AccountId};
use assert_matches::assert_matches;
@@ -472,8 +472,11 @@ mod tests {
type Error = error::Error;
/// Verify extrinsic at given block.
fn validate_transaction(&self, at: &BlockId<Self::Block>, uxt: ExtrinsicFor<Self>) -> Result<TransactionValidity, Self::Error> {
fn validate_transaction(
&self,
at: &BlockId<Self::Block>,
uxt: ExtrinsicFor<Self>,
) -> Result<TransactionValidity, Self::Error> {
let block_number = self.block_id_to_number(at)?.unwrap();
let nonce = uxt.transfer().nonce;
@@ -488,9 +491,9 @@ mod tests {
}
if nonce < block_number {
Ok(TransactionValidity::Invalid(0))
Ok(InvalidTransaction::Stale.into())
} else {
Ok(TransactionValidity::Valid(ValidTransaction {
Ok(Ok(ValidTransaction {
priority: 4,
requires: if nonce > block_number { vec![vec![nonce as u8 - 1]] } else { vec![] },
provides: if nonce == INVALID_NONCE { vec![] } else { vec![vec![nonce as u8]] },
+14 -8
View File
@@ -39,7 +39,11 @@ impl txpool::ChainApi for TestApi {
type Hash = Hash;
type Error = error::Error;
fn validate_transaction(&self, at: &BlockId<Self::Block>, uxt: txpool::ExtrinsicFor<Self>) -> error::Result<TransactionValidity> {
fn validate_transaction(
&self,
at: &BlockId<Self::Block>,
uxt: txpool::ExtrinsicFor<Self>,
) -> error::Result<TransactionValidity> {
let expected = index(at);
let requires = if expected == uxt.transfer().nonce {
vec![]
@@ -48,13 +52,15 @@ impl txpool::ChainApi for TestApi {
};
let provides = vec![vec![uxt.transfer().nonce as u8]];
Ok(TransactionValidity::Valid(ValidTransaction {
priority: 1,
requires,
provides,
longevity: 64,
propagate: true,
}))
Ok(
Ok(ValidTransaction {
priority: 1,
requires,
provides,
longevity: 64,
propagate: true,
})
)
}
fn block_id_to_number(&self, at: &BlockId<Self::Block>) -> error::Result<Option<txpool::NumberFor<Self>>> {