mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 11:41:04 +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:
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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] }),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)] = &[
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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]] },
|
||||
|
||||
@@ -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>>> {
|
||||
|
||||
Reference in New Issue
Block a user