mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 22:11:06 +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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user