use crate::dispatch::{DispatchResult, Parameter};
use codec::{CompactLen, Decode, DecodeLimit, Encode, EncodeLike, Input, MaxEncodedLen};
use impl_trait_for_tuples::impl_for_tuples;
use scale_info::{build::Fields, meta_type, Path, Type, TypeInfo, TypeParameter};
use sp_arithmetic::traits::{CheckedAdd, CheckedMul, CheckedSub, One, Saturating};
use sp_core::bounded::bounded_vec::TruncateFrom;
#[doc(hidden)]
pub use sp_runtime::traits::{
ConstBool, ConstI128, ConstI16, ConstI32, ConstI64, ConstI8, ConstU128, ConstU16, ConstU32,
ConstU64, ConstU8, Get, GetDefault, TryCollect, TypedGet,
};
use sp_runtime::{traits::Block as BlockT, DispatchError};
use sp_std::{cmp::Ordering, prelude::*};
#[doc(hidden)]
pub const DEFENSIVE_OP_PUBLIC_ERROR: &str = "a defensive failure has been triggered; please report the block number at https://github.com/paritytech/substrate/issues";
#[doc(hidden)]
pub const DEFENSIVE_OP_INTERNAL_ERROR: &str = "Defensive failure has been triggered!";
#[macro_export]
macro_rules! defensive {
() => {
frame_support::__private::log::error!(
target: "runtime",
"{}",
$crate::traits::DEFENSIVE_OP_PUBLIC_ERROR
);
debug_assert!(false, "{}", $crate::traits::DEFENSIVE_OP_INTERNAL_ERROR);
};
($error:expr $(,)?) => {
frame_support::__private::log::error!(
target: "runtime",
"{}: {:?}",
$crate::traits::DEFENSIVE_OP_PUBLIC_ERROR,
$error
);
debug_assert!(false, "{}: {:?}", $crate::traits::DEFENSIVE_OP_INTERNAL_ERROR, $error);
};
($error:expr, $proof:expr $(,)?) => {
frame_support::__private::log::error!(
target: "runtime",
"{}: {:?}: {:?}",
$crate::traits::DEFENSIVE_OP_PUBLIC_ERROR,
$error,
$proof,
);
debug_assert!(false, "{}: {:?}: {:?}", $crate::traits::DEFENSIVE_OP_INTERNAL_ERROR, $error, $proof);
}
}
#[macro_export]
macro_rules! defensive_assert {
($cond:expr $(, $proof:expr )? $(,)?) => {
if !($cond) {
$crate::defensive!(::core::stringify!($cond) $(, $proof )?);
}
};
}
pub mod defensive_prelude {
pub use super::{Defensive, DefensiveOption, DefensiveResult};
}
pub trait Defensive<T> {
fn defensive_unwrap_or(self, other: T) -> T;
fn defensive_unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T;
fn defensive_unwrap_or_default(self) -> T
where
T: Default;
fn defensive(self) -> Self;
fn defensive_proof(self, proof: &'static str) -> Self;
}
pub trait DefensiveResult<T, E> {
fn defensive_map_err<F, O: FnOnce(E) -> F>(self, o: O) -> Result<T, F>;
fn defensive_map_or_else<U, D: FnOnce(E) -> U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U;
fn defensive_ok(self) -> Option<T>;
fn defensive_map<U, F: FnOnce(T) -> U>(self, f: F) -> Result<U, E>;
}
pub trait DefensiveOption<T> {
fn defensive_map_or_else<U, D: FnOnce() -> U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U;
fn defensive_ok_or_else<E: sp_std::fmt::Debug, F: FnOnce() -> E>(self, err: F) -> Result<T, E>;
fn defensive_ok_or<E: sp_std::fmt::Debug>(self, err: E) -> Result<T, E>;
fn defensive_map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U>;
}
impl<T> Defensive<T> for Option<T> {
fn defensive_unwrap_or(self, or: T) -> T {
match self {
Some(inner) => inner,
None => {
defensive!();
or
},
}
}
fn defensive_unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T {
match self {
Some(inner) => inner,
None => {
defensive!();
f()
},
}
}
fn defensive_unwrap_or_default(self) -> T
where
T: Default,
{
match self {
Some(inner) => inner,
None => {
defensive!();
Default::default()
},
}
}
fn defensive(self) -> Self {
match self {
Some(inner) => Some(inner),
None => {
defensive!();
None
},
}
}
fn defensive_proof(self, proof: &'static str) -> Self {
if self.is_none() {
defensive!(proof);
}
self
}
}
impl<T, E: sp_std::fmt::Debug> Defensive<T> for Result<T, E> {
fn defensive_unwrap_or(self, or: T) -> T {
match self {
Ok(inner) => inner,
Err(e) => {
defensive!(e);
or
},
}
}
fn defensive_unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T {
match self {
Ok(inner) => inner,
Err(e) => {
defensive!(e);
f()
},
}
}
fn defensive_unwrap_or_default(self) -> T
where
T: Default,
{
match self {
Ok(inner) => inner,
Err(e) => {
defensive!(e);
Default::default()
},
}
}
fn defensive(self) -> Self {
match self {
Ok(inner) => Ok(inner),
Err(e) => {
defensive!(e);
Err(e)
},
}
}
fn defensive_proof(self, proof: &'static str) -> Self {
match self {
Ok(inner) => Ok(inner),
Err(e) => {
defensive!(e, proof);
Err(e)
},
}
}
}
impl<T, E: sp_std::fmt::Debug> DefensiveResult<T, E> for Result<T, E> {
fn defensive_map_err<F, O: FnOnce(E) -> F>(self, o: O) -> Result<T, F> {
self.map_err(|e| {
defensive!(e);
o(e)
})
}
fn defensive_map_or_else<U, D: FnOnce(E) -> U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U {
self.map_or_else(
|e| {
defensive!(e);
default(e)
},
f,
)
}
fn defensive_ok(self) -> Option<T> {
match self {
Ok(inner) => Some(inner),
Err(e) => {
defensive!(e);
None
},
}
}
fn defensive_map<U, F: FnOnce(T) -> U>(self, f: F) -> Result<U, E> {
match self {
Ok(inner) => Ok(f(inner)),
Err(e) => {
defensive!(e);
Err(e)
},
}
}
}
impl<T> DefensiveOption<T> for Option<T> {
fn defensive_map_or_else<U, D: FnOnce() -> U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U {
self.map_or_else(
|| {
defensive!();
default()
},
f,
)
}
fn defensive_ok_or_else<E: sp_std::fmt::Debug, F: FnOnce() -> E>(self, err: F) -> Result<T, E> {
self.ok_or_else(|| {
let err_value = err();
defensive!(err_value);
err_value
})
}
fn defensive_ok_or<E: sp_std::fmt::Debug>(self, err: E) -> Result<T, E> {
self.ok_or_else(|| {
defensive!(err);
err
})
}
fn defensive_map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> {
match self {
Some(inner) => Some(f(inner)),
None => {
defensive!();
None
},
}
}
}
pub trait DefensiveSaturating {
fn defensive_saturating_add(self, other: Self) -> Self;
fn defensive_saturating_sub(self, other: Self) -> Self;
fn defensive_saturating_mul(self, other: Self) -> Self;
fn defensive_saturating_accrue(&mut self, other: Self);
fn defensive_saturating_reduce(&mut self, other: Self);
fn defensive_saturating_inc(&mut self);
fn defensive_saturating_dec(&mut self);
}
impl<T: Saturating + CheckedAdd + CheckedMul + CheckedSub + One> DefensiveSaturating for T {
fn defensive_saturating_add(self, other: Self) -> Self {
self.checked_add(&other).defensive_unwrap_or_else(|| self.saturating_add(other))
}
fn defensive_saturating_sub(self, other: Self) -> Self {
self.checked_sub(&other).defensive_unwrap_or_else(|| self.saturating_sub(other))
}
fn defensive_saturating_mul(self, other: Self) -> Self {
self.checked_mul(&other).defensive_unwrap_or_else(|| self.saturating_mul(other))
}
fn defensive_saturating_accrue(&mut self, other: Self) {
*self = sp_std::mem::replace(self, One::one()).defensive_saturating_add(other);
}
fn defensive_saturating_reduce(&mut self, other: Self) {
*self = sp_std::mem::replace(self, One::one()).defensive_saturating_sub(other);
}
fn defensive_saturating_inc(&mut self) {
self.defensive_saturating_accrue(One::one());
}
fn defensive_saturating_dec(&mut self) {
self.defensive_saturating_reduce(One::one());
}
}
pub trait DefensiveTruncateFrom<T> {
fn defensive_truncate_from(unbound: T) -> Self;
}
impl<T, U> DefensiveTruncateFrom<U> for T
where
T: TruncateFrom<U> + TryFrom<U, Error = U>,
{
fn defensive_truncate_from(unbound: U) -> Self {
unbound.try_into().map_or_else(
|err| {
defensive!("DefensiveTruncateFrom truncating");
T::truncate_from(err)
},
|bound| bound,
)
}
}
pub trait DefensiveMin<T> {
fn defensive_min(self, other: T) -> Self;
fn defensive_strict_min(self, other: T) -> Self;
}
impl<T> DefensiveMin<T> for T
where
T: sp_std::cmp::PartialOrd<T>,
{
fn defensive_min(self, other: T) -> Self {
if self <= other {
self
} else {
defensive!("DefensiveMin");
other
}
}
fn defensive_strict_min(self, other: T) -> Self {
if self < other {
self
} else {
defensive!("DefensiveMin strict");
other
}
}
}
pub trait DefensiveMax<T> {
fn defensive_max(self, other: T) -> Self;
fn defensive_strict_max(self, other: T) -> Self;
}
impl<T> DefensiveMax<T> for T
where
T: sp_std::cmp::PartialOrd<T>,
{
fn defensive_max(self, other: T) -> Self {
if self >= other {
self
} else {
defensive!("DefensiveMax");
other
}
}
fn defensive_strict_max(self, other: T) -> Self {
if self > other {
self
} else {
defensive!("DefensiveMax strict");
other
}
}
}
pub trait Len {
fn len(&self) -> usize;
}
impl<T: IntoIterator + Clone> Len for T
where
<T as IntoIterator>::IntoIter: ExactSizeIterator,
{
fn len(&self) -> usize {
self.clone().into_iter().len()
}
}
pub trait TryDrop: Sized {
fn try_drop(self) -> Result<(), Self>;
}
impl TryDrop for () {
fn try_drop(self) -> Result<(), Self> {
Ok(())
}
}
pub enum SameOrOther<A, B> {
None,
Same(A),
Other(B),
}
impl<A, B> TryDrop for SameOrOther<A, B> {
fn try_drop(self) -> Result<(), Self> {
if let SameOrOther::None = self {
Ok(())
} else {
Err(self)
}
}
}
impl<A, B> SameOrOther<A, B> {
pub fn try_same(self) -> Result<A, Self> {
match self {
SameOrOther::Same(a) => Ok(a),
x => Err(x),
}
}
pub fn try_other(self) -> Result<B, Self> {
match self {
SameOrOther::Other(b) => Ok(b),
x => Err(x),
}
}
pub fn try_none(self) -> Result<(), Self> {
match self {
SameOrOther::None => Ok(()),
x => Err(x),
}
}
pub fn same(self) -> Result<A, B>
where
A: Default,
{
match self {
SameOrOther::Same(a) => Ok(a),
SameOrOther::None => Ok(A::default()),
SameOrOther::Other(b) => Err(b),
}
}
pub fn other(self) -> Result<B, A>
where
B: Default,
{
match self {
SameOrOther::Same(a) => Err(a),
SameOrOther::None => Ok(B::default()),
SameOrOther::Other(b) => Ok(b),
}
}
}
#[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))]
#[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))]
#[cfg_attr(feature = "tuples-128", impl_for_tuples(128))]
pub trait OnNewAccount<AccountId> {
fn on_new_account(who: &AccountId);
}
#[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))]
#[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))]
#[cfg_attr(feature = "tuples-128", impl_for_tuples(128))]
pub trait OnKilledAccount<AccountId> {
fn on_killed_account(who: &AccountId);
}
pub trait HandleLifetime<T> {
fn created(_t: &T) -> Result<(), DispatchError> {
Ok(())
}
fn killed(_t: &T) -> Result<(), DispatchError> {
Ok(())
}
}
impl<T> HandleLifetime<T> for () {}
pub trait Time {
type Moment: sp_arithmetic::traits::AtLeast32Bit + Parameter + Default + Copy + MaxEncodedLen;
fn now() -> Self::Moment;
}
pub trait UnixTime {
fn now() -> core::time::Duration;
}
pub trait IsType<T>: Into<T> + From<T> {
fn from_ref(t: &T) -> &Self;
fn into_ref(&self) -> &T;
fn from_mut(t: &mut T) -> &mut Self;
fn into_mut(&mut self) -> &mut T;
}
impl<T> IsType<T> for T {
fn from_ref(t: &T) -> &Self {
t
}
fn into_ref(&self) -> &T {
self
}
fn from_mut(t: &mut T) -> &mut Self {
t
}
fn into_mut(&mut self) -> &mut T {
self
}
}
pub trait IsSubType<T> {
fn is_sub_type(&self) -> Option<&T>;
}
pub trait ExecuteBlock<Block: BlockT> {
fn execute_block(block: Block);
}
pub trait PrivilegeCmp<Origin> {
fn cmp_privilege(left: &Origin, right: &Origin) -> Option<Ordering>;
}
pub struct EqualPrivilegeOnly;
impl<Origin: PartialEq> PrivilegeCmp<Origin> for EqualPrivilegeOnly {
fn cmp_privilege(left: &Origin, right: &Origin) -> Option<Ordering> {
(left == right).then(|| Ordering::Equal)
}
}
#[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))]
#[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))]
#[cfg_attr(feature = "tuples-128", impl_for_tuples(128))]
pub trait OffchainWorker<BlockNumber> {
fn offchain_worker(_n: BlockNumber) {}
}
pub struct Backing {
pub approvals: u32,
pub eligible: u32,
}
pub trait GetBacking {
fn get_backing(&self) -> Option<Backing>;
}
pub trait EnsureInherentsAreFirst<Block> {
fn ensure_inherents_are_first(block: &Block) -> Result<(), u32>;
}
pub trait ExtrinsicCall: sp_runtime::traits::Extrinsic {
fn call(&self) -> &Self::Call;
}
#[cfg(feature = "std")]
impl<Call, Extra> ExtrinsicCall for sp_runtime::testing::TestXt<Call, Extra>
where
Call: codec::Codec + Sync + Send + TypeInfo,
Extra: TypeInfo,
{
fn call(&self) -> &Self::Call {
&self.call
}
}
impl<Address, Call, Signature, Extra> ExtrinsicCall
for sp_runtime::generic::UncheckedExtrinsic<Address, Call, Signature, Extra>
where
Address: TypeInfo,
Call: TypeInfo,
Signature: TypeInfo,
Extra: sp_runtime::traits::SignedExtension + TypeInfo,
{
fn call(&self) -> &Self::Call {
&self.function
}
}
pub trait EstimateCallFee<Call, Balance> {
fn estimate_call_fee(call: &Call, post_info: crate::dispatch::PostDispatchInfo) -> Balance;
}
#[cfg(feature = "std")]
impl<Call, Balance: From<u32>, const T: u32> EstimateCallFee<Call, Balance> for ConstU32<T> {
fn estimate_call_fee(_: &Call, _: crate::dispatch::PostDispatchInfo) -> Balance {
T.into()
}
}
#[derive(Debug, Eq, PartialEq, Default, Clone)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub struct WrapperOpaque<T>(pub T);
impl<T: Encode> EncodeLike for WrapperOpaque<T> {}
impl<T: Encode> EncodeLike<WrapperKeepOpaque<T>> for WrapperOpaque<T> {}
impl<T: Encode> Encode for WrapperOpaque<T> {
fn size_hint(&self) -> usize {
self.0.size_hint().saturating_add(<codec::Compact<u32>>::max_encoded_len())
}
fn encode_to<O: codec::Output + ?Sized>(&self, dest: &mut O) {
self.0.encode().encode_to(dest);
}
fn encode(&self) -> Vec<u8> {
self.0.encode().encode()
}
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
self.0.encode().using_encoded(f)
}
}
impl<T: Decode> Decode for WrapperOpaque<T> {
fn decode<I: Input>(input: &mut I) -> Result<Self, codec::Error> {
Ok(Self(T::decode_all_with_depth_limit(
sp_api::MAX_EXTRINSIC_DEPTH,
&mut &<Vec<u8>>::decode(input)?[..],
)?))
}
fn skip<I: Input>(input: &mut I) -> Result<(), codec::Error> {
<Vec<u8>>::skip(input)
}
}
impl<T> From<T> for WrapperOpaque<T> {
fn from(t: T) -> Self {
Self(t)
}
}
impl<T: MaxEncodedLen> MaxEncodedLen for WrapperOpaque<T> {
fn max_encoded_len() -> usize {
let t_max_len = T::max_encoded_len();
if t_max_len < 64 {
t_max_len + 1
} else if t_max_len < 2usize.pow(14) {
t_max_len + 2
} else if t_max_len < 2usize.pow(30) {
t_max_len + 4
} else {
<codec::Compact<u32>>::max_encoded_len().saturating_add(T::max_encoded_len())
}
}
}
impl<T: TypeInfo + 'static> TypeInfo for WrapperOpaque<T> {
type Identity = Self;
fn type_info() -> Type {
Type::builder()
.path(Path::new("WrapperOpaque", module_path!()))
.type_params(vec![TypeParameter::new("T", Some(meta_type::<T>()))])
.composite(
Fields::unnamed()
.field(|f| f.compact::<u32>())
.field(|f| f.ty::<T>().type_name("T")),
)
}
}
#[derive(Debug, Eq, PartialEq, Default, Clone)]
pub struct WrapperKeepOpaque<T> {
data: Vec<u8>,
_phantom: sp_std::marker::PhantomData<T>,
}
impl<T: Decode> WrapperKeepOpaque<T> {
pub fn try_decode(&self) -> Option<T> {
T::decode_all_with_depth_limit(sp_api::MAX_EXTRINSIC_DEPTH, &mut &self.data[..]).ok()
}
pub fn encoded_len(&self) -> usize {
self.data.len()
}
pub fn encoded(&self) -> &[u8] {
&self.data
}
pub fn from_encoded(data: Vec<u8>) -> Self {
Self { data, _phantom: sp_std::marker::PhantomData }
}
}
impl<T: Encode> EncodeLike for WrapperKeepOpaque<T> {}
impl<T: Encode> EncodeLike<WrapperOpaque<T>> for WrapperKeepOpaque<T> {}
impl<T: Encode> Encode for WrapperKeepOpaque<T> {
fn size_hint(&self) -> usize {
self.data.len() + codec::Compact::<u32>::compact_len(&(self.data.len() as u32))
}
fn encode_to<O: codec::Output + ?Sized>(&self, dest: &mut O) {
self.data.encode_to(dest);
}
fn encode(&self) -> Vec<u8> {
self.data.encode()
}
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
self.data.using_encoded(f)
}
}
impl<T: Decode> Decode for WrapperKeepOpaque<T> {
fn decode<I: Input>(input: &mut I) -> Result<Self, codec::Error> {
Ok(Self { data: Vec::<u8>::decode(input)?, _phantom: sp_std::marker::PhantomData })
}
fn skip<I: Input>(input: &mut I) -> Result<(), codec::Error> {
<Vec<u8>>::skip(input)
}
}
impl<T: MaxEncodedLen> MaxEncodedLen for WrapperKeepOpaque<T> {
fn max_encoded_len() -> usize {
WrapperOpaque::<T>::max_encoded_len()
}
}
impl<T: TypeInfo + 'static> TypeInfo for WrapperKeepOpaque<T> {
type Identity = Self;
fn type_info() -> Type {
Type::builder()
.path(Path::new("WrapperKeepOpaque", module_path!()))
.type_params(vec![TypeParameter::new("T", Some(meta_type::<T>()))])
.composite(
Fields::unnamed()
.field(|f| f.compact::<u32>())
.field(|f| f.ty::<T>().type_name("T")),
)
}
}
pub trait PreimageProvider<Hash> {
fn have_preimage(hash: &Hash) -> bool;
fn get_preimage(hash: &Hash) -> Option<Vec<u8>>;
fn preimage_requested(hash: &Hash) -> bool;
fn request_preimage(hash: &Hash);
fn unrequest_preimage(hash: &Hash);
}
impl<Hash> PreimageProvider<Hash> for () {
fn have_preimage(_: &Hash) -> bool {
false
}
fn get_preimage(_: &Hash) -> Option<Vec<u8>> {
None
}
fn preimage_requested(_: &Hash) -> bool {
false
}
fn request_preimage(_: &Hash) {}
fn unrequest_preimage(_: &Hash) {}
}
pub trait PreimageRecipient<Hash>: PreimageProvider<Hash> {
type MaxSize: Get<u32>;
fn note_preimage(bytes: crate::BoundedVec<u8, Self::MaxSize>);
fn unnote_preimage(hash: &Hash);
}
impl<Hash> PreimageRecipient<Hash> for () {
type MaxSize = ();
fn note_preimage(_: crate::BoundedVec<u8, Self::MaxSize>) {}
fn unnote_preimage(_: &Hash) {}
}
pub trait AccountTouch<AssetId, AccountId> {
type Balance;
fn deposit_required(asset: AssetId) -> Self::Balance;
fn touch(asset: AssetId, who: AccountId, depositor: AccountId) -> DispatchResult;
}
#[cfg(test)]
mod test {
use super::*;
use sp_core::bounded::{BoundedSlice, BoundedVec};
use sp_std::marker::PhantomData;
#[test]
fn defensive_assert_works() {
defensive_assert!(true);
defensive_assert!(true,);
defensive_assert!(true, "must work");
defensive_assert!(true, "must work",);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "Defensive failure has been triggered!: \"1 == 0\": \"Must fail\"")]
fn defensive_assert_panics() {
defensive_assert!(1 == 0, "Must fail");
}
#[test]
#[cfg(not(debug_assertions))]
fn defensive_assert_does_not_panic() {
defensive_assert!(1 == 0, "Must fail");
}
#[test]
#[cfg(not(debug_assertions))]
fn defensive_saturating_accrue_works() {
let mut v = 1_u32;
v.defensive_saturating_accrue(2);
assert_eq!(v, 3);
v.defensive_saturating_accrue(u32::MAX);
assert_eq!(v, u32::MAX);
v.defensive_saturating_accrue(1);
assert_eq!(v, u32::MAX);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "Defensive")]
fn defensive_saturating_accrue_panics() {
let mut v = u32::MAX;
v.defensive_saturating_accrue(1); }
#[test]
#[cfg(not(debug_assertions))]
fn defensive_saturating_reduce_works() {
let mut v = u32::MAX;
v.defensive_saturating_reduce(3);
assert_eq!(v, u32::MAX - 3);
v.defensive_saturating_reduce(u32::MAX);
assert_eq!(v, 0);
v.defensive_saturating_reduce(1);
assert_eq!(v, 0);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "Defensive")]
fn defensive_saturating_reduce_panics() {
let mut v = 0_u32;
v.defensive_saturating_reduce(1); }
#[test]
#[cfg(not(debug_assertions))]
fn defensive_saturating_inc_works() {
let mut v = 0_u32;
for i in 1..10 {
v.defensive_saturating_inc();
assert_eq!(v, i);
}
v += u32::MAX - 10;
v.defensive_saturating_inc();
assert_eq!(v, u32::MAX);
v.defensive_saturating_inc();
assert_eq!(v, u32::MAX);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "Defensive")]
fn defensive_saturating_inc_panics() {
let mut v = u32::MAX;
v.defensive_saturating_inc(); }
#[test]
#[cfg(not(debug_assertions))]
fn defensive_saturating_dec_works() {
let mut v = u32::MAX;
for i in 1..10 {
v.defensive_saturating_dec();
assert_eq!(v, u32::MAX - i);
}
v -= u32::MAX - 10;
v.defensive_saturating_dec();
assert_eq!(v, 0);
v.defensive_saturating_dec();
assert_eq!(v, 0);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "Defensive")]
fn defensive_saturating_dec_panics() {
let mut v = 0_u32;
v.defensive_saturating_dec(); }
#[test]
#[cfg(not(debug_assertions))]
fn defensive_truncating_from_vec_defensive_works() {
let unbound = vec![1u32, 2];
let bound = BoundedVec::<u32, ConstU32<1>>::defensive_truncate_from(unbound);
assert_eq!(bound, vec![1u32]);
}
#[test]
#[cfg(not(debug_assertions))]
fn defensive_truncating_from_slice_defensive_works() {
let unbound = &[1u32, 2];
let bound = BoundedSlice::<u32, ConstU32<1>>::defensive_truncate_from(unbound);
assert_eq!(bound, &[1u32][..]);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(
expected = "Defensive failure has been triggered!: \"DefensiveTruncateFrom truncating\""
)]
fn defensive_truncating_from_vec_defensive_panics() {
let unbound = vec![1u32, 2];
let _ = BoundedVec::<u32, ConstU32<1>>::defensive_truncate_from(unbound);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(
expected = "Defensive failure has been triggered!: \"DefensiveTruncateFrom truncating\""
)]
fn defensive_truncating_from_slice_defensive_panics() {
let unbound = &[1u32, 2];
let _ = BoundedSlice::<u32, ConstU32<1>>::defensive_truncate_from(unbound);
}
#[test]
fn defensive_truncate_from_vec_works() {
let unbound = vec![1u32, 2, 3];
let bound = BoundedVec::<u32, ConstU32<3>>::defensive_truncate_from(unbound.clone());
assert_eq!(bound, unbound);
}
#[test]
fn defensive_truncate_from_slice_works() {
let unbound = [1u32, 2, 3];
let bound = BoundedSlice::<u32, ConstU32<3>>::defensive_truncate_from(&unbound);
assert_eq!(bound, &unbound[..]);
}
#[derive(Encode, Decode)]
enum NestedType {
Nested(Box<Self>),
Done,
}
#[test]
fn test_opaque_wrapper_decode_limit() {
let limit = sp_api::MAX_EXTRINSIC_DEPTH as usize;
let mut ok_bytes = vec![0u8; limit];
ok_bytes.push(1u8);
let mut err_bytes = vec![0u8; limit + 1];
err_bytes.push(1u8);
assert!(<WrapperOpaque<NestedType>>::decode(&mut &ok_bytes.encode()[..]).is_ok());
assert!(<WrapperOpaque<NestedType>>::decode(&mut &err_bytes.encode()[..]).is_err());
let ok_keep_opaque = WrapperKeepOpaque { data: ok_bytes, _phantom: PhantomData };
let err_keep_opaque = WrapperKeepOpaque { data: err_bytes, _phantom: PhantomData };
assert!(<WrapperKeepOpaque<NestedType>>::try_decode(&ok_keep_opaque).is_some());
assert!(<WrapperKeepOpaque<NestedType>>::try_decode(&err_keep_opaque).is_none());
}
#[test]
fn test_opaque_wrapper() {
let encoded = WrapperOpaque(3u32).encode();
assert_eq!(encoded, [codec::Compact(4u32).encode(), 3u32.to_le_bytes().to_vec()].concat());
let vec_u8 = <Vec<u8>>::decode(&mut &encoded[..]).unwrap();
let decoded_from_vec_u8 = u32::decode(&mut &vec_u8[..]).unwrap();
assert_eq!(decoded_from_vec_u8, 3u32);
let decoded = <WrapperOpaque<u32>>::decode(&mut &encoded[..]).unwrap();
assert_eq!(decoded.0, 3u32);
assert_eq!(<WrapperOpaque<[u8; 63]>>::max_encoded_len(), 63 + 1);
assert_eq!(
<WrapperOpaque<[u8; 63]>>::max_encoded_len(),
WrapperOpaque([0u8; 63]).encode().len()
);
assert_eq!(<WrapperOpaque<[u8; 64]>>::max_encoded_len(), 64 + 2);
assert_eq!(
<WrapperOpaque<[u8; 64]>>::max_encoded_len(),
WrapperOpaque([0u8; 64]).encode().len()
);
assert_eq!(
<WrapperOpaque<[u8; 2usize.pow(14) - 1]>>::max_encoded_len(),
2usize.pow(14) - 1 + 2
);
assert_eq!(<WrapperOpaque<[u8; 2usize.pow(14)]>>::max_encoded_len(), 2usize.pow(14) + 4);
let data = 4u64;
assert!(WrapperOpaque::<u32>::decode(&mut &data.encode().encode()[..]).is_err());
}
#[test]
fn test_keep_opaque_wrapper() {
let data = 3u32.encode().encode();
let keep_opaque = WrapperKeepOpaque::<u32>::decode(&mut &data[..]).unwrap();
keep_opaque.try_decode().unwrap();
let data = WrapperOpaque(50u32).encode();
let decoded = WrapperKeepOpaque::<u32>::decode(&mut &data[..]).unwrap();
let data = decoded.encode();
WrapperOpaque::<u32>::decode(&mut &data[..]).unwrap();
}
#[test]
fn defensive_min_works() {
assert_eq!(10, 10_u32.defensive_min(11_u32));
assert_eq!(10, 10_u32.defensive_min(10_u32));
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "Defensive failure has been triggered!: \"DefensiveMin\"")]
fn defensive_min_panics() {
10_u32.defensive_min(9_u32);
}
#[test]
fn defensive_strict_min_works() {
assert_eq!(10, 10_u32.defensive_strict_min(11_u32));
assert_eq!(9, 9_u32.defensive_strict_min(10_u32));
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "Defensive failure has been triggered!: \"DefensiveMin strict\"")]
fn defensive_strict_min_panics() {
9_u32.defensive_strict_min(9_u32);
}
#[test]
fn defensive_max_works() {
assert_eq!(11, 11_u32.defensive_max(10_u32));
assert_eq!(10, 10_u32.defensive_max(10_u32));
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "Defensive failure has been triggered!: \"DefensiveMax\"")]
fn defensive_max_panics() {
9_u32.defensive_max(10_u32);
}
#[test]
fn defensive_strict_max_works() {
assert_eq!(11, 11_u32.defensive_strict_max(10_u32));
assert_eq!(10, 10_u32.defensive_strict_max(9_u32));
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "Defensive failure has been triggered!: \"DefensiveMax strict\"")]
fn defensive_strict_max_panics() {
9_u32.defensive_strict_max(9_u32);
}
}