use tetcore_std::prelude::*;
use crate::codec::{Encode, Decode};
use crate::RuntimeDebug;
pub type TransactionPriority = u64;
pub type TransactionLongevity = u64;
pub type TransactionTag = Vec<u8>;
#[derive(Clone, PartialEq, Eq, Encode, Decode, Copy, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(serde::Serialize))]
pub enum InvalidTransaction {
Call,
Payment,
Future,
Stale,
BadProof,
AncientBirthBlock,
ExhaustsResources,
Custom(u8),
BadMandatory,
MandatoryDispatch,
}
impl InvalidTransaction {
pub fn exhausted_resources(&self) -> bool {
match self {
Self::ExhaustsResources => true,
_ => false,
}
}
pub fn was_mandatory(&self) -> bool {
match self {
Self::BadMandatory => 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 exhaust the block limits",
InvalidTransaction::Payment =>
"Inability to pay some fees (e.g. account balance too low)",
InvalidTransaction::BadMandatory =>
"A call was labelled as mandatory, but resulted in an Error.",
InvalidTransaction::MandatoryDispatch =>
"Transaction dispatch is mandatory; transactions may not have mandatory dispatches.",
InvalidTransaction::Custom(_) => "InvalidTransaction custom error",
}
}
}
#[derive(Clone, PartialEq, Eq, Encode, Decode, Copy, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(serde::Serialize))]
pub enum UnknownTransaction {
CannotLookup,
NoUnsignedValidator,
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",
}
}
}
#[derive(Clone, PartialEq, Eq, Encode, Decode, Copy, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(serde::Serialize))]
pub enum TransactionValidityError {
Invalid(InvalidTransaction),
Unknown(UnknownTransaction),
}
impl TransactionValidityError {
pub fn exhausted_resources(&self) -> bool {
match self {
Self::Invalid(e) => e.exhausted_resources(),
Self::Unknown(_) => false,
}
}
pub fn was_mandatory(&self) -> bool {
match self {
Self::Invalid(e) => e.was_mandatory(),
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)
}
}
#[cfg(feature = "std")]
impl std::error::Error for TransactionValidityError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
#[cfg(feature = "std")]
impl std::fmt::Display for TransactionValidityError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s: &'static str = (*self).into();
write!(f, "{}", s)
}
}
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())
}
}
#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, tetsy_util_mem::MallocSizeOf)]
pub enum TransactionSource {
InBlock,
Local,
External,
}
#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)]
pub struct ValidTransaction {
pub priority: TransactionPriority,
pub requires: Vec<TransactionTag>,
pub provides: Vec<TransactionTag>,
pub longevity: TransactionLongevity,
pub propagate: bool,
}
impl Default for ValidTransaction {
fn default() -> Self {
ValidTransaction {
priority: 0,
requires: vec![],
provides: vec![],
longevity: TransactionLongevity::max_value(),
propagate: true,
}
}
}
impl ValidTransaction {
pub fn with_tag_prefix(prefix: &'static str) -> ValidTransactionBuilder {
ValidTransactionBuilder {
prefix: Some(prefix),
validity: Default::default(),
}
}
pub fn combine_with(mut self, mut other: ValidTransaction) -> Self {
ValidTransaction {
priority: self.priority.saturating_add(other.priority),
requires: { self.requires.append(&mut other.requires); self.requires },
provides: { self.provides.append(&mut other.provides); self.provides },
longevity: self.longevity.min(other.longevity),
propagate: self.propagate && other.propagate,
}
}
}
#[derive(Default, Clone, RuntimeDebug)]
pub struct ValidTransactionBuilder {
prefix: Option<&'static str>,
validity: ValidTransaction,
}
impl ValidTransactionBuilder {
pub fn priority(mut self, priority: TransactionPriority) -> Self {
self.validity.priority = priority;
self
}
pub fn longevity(mut self, longevity: TransactionLongevity) -> Self {
self.validity.longevity = longevity;
self
}
pub fn propagate(mut self, propagate: bool) -> Self {
self.validity.propagate = propagate;
self
}
pub fn and_requires(mut self, tag: impl Encode) -> Self {
self.validity.requires.push(match self.prefix.as_ref() {
Some(prefix) => (prefix, tag).encode(),
None => tag.encode(),
});
self
}
pub fn and_provides(mut self, tag: impl Encode) -> Self {
self.validity.provides.push(match self.prefix.as_ref() {
Some(prefix) => (prefix, tag).encode(),
None => tag.encode(),
});
self
}
pub fn combine_with(mut self, validity: ValidTransaction) -> Self {
self.validity = core::mem::take(&mut self.validity).combine_with(validity);
self
}
pub fn build(self) -> TransactionValidity {
self.into()
}
}
impl From<ValidTransactionBuilder> for TransactionValidity {
fn from(builder: ValidTransactionBuilder) -> Self {
Ok(builder.into())
}
}
impl From<ValidTransactionBuilder> for ValidTransaction {
fn from(builder: ValidTransactionBuilder) -> Self {
builder.validity
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_encode_and_decode() {
let v: TransactionValidity = Ok(ValidTransaction {
priority: 5,
requires: vec![vec![1, 2, 3, 4]],
provides: vec![vec![4, 5, 6]],
longevity: 42,
propagate: false,
});
let encoded = v.encode();
assert_eq!(
encoded,
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]
);
assert_eq!(TransactionValidity::decode(&mut &*encoded), Ok(v));
}
#[test]
fn builder_should_prefix_the_tags() {
const PREFIX: &str = "test";
let a: ValidTransaction = ValidTransaction::with_tag_prefix(PREFIX)
.and_requires(1)
.and_requires(2)
.and_provides(3)
.and_provides(4)
.propagate(false)
.longevity(5)
.priority(3)
.priority(6)
.into();
assert_eq!(a, ValidTransaction {
propagate: false,
longevity: 5,
priority: 6,
requires: vec![(PREFIX, 1).encode(), (PREFIX, 2).encode()],
provides: vec![(PREFIX, 3).encode(), (PREFIX, 4).encode()],
});
}
}