Add DefensiveTruncateFrom (#12515)

* Add DefensiveTruncateFrom

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add tests

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix tests

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Map_err in preimage

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Map_err in beefy

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Make test dependant in debug-assertions

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: parity-processbot <>
This commit is contained in:
Oliver Tale-Yazdi
2022-10-20 23:06:12 +02:00
committed by GitHub
parent 92d2977292
commit 48a02bb056
5 changed files with 168 additions and 7 deletions
@@ -22,6 +22,7 @@ use codec::{CompactLen, Decode, DecodeLimit, Encode, EncodeLike, Input, MaxEncod
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, Saturating};
use sp_core::bounded::bounded_vec::TruncateFrom;
#[doc(hidden)]
pub use sp_runtime::traits::{
ConstBool, ConstI128, ConstI16, ConstI32, ConstI64, ConstI8, ConstU128, ConstU16, ConstU32,
@@ -369,6 +370,43 @@ impl<T: Saturating + CheckedAdd + CheckedMul + CheckedSub> DefensiveSaturating f
}
}
/// Construct an object by defensively truncating an input if the `TryFrom` conversion fails.
pub trait DefensiveTruncateFrom<T> {
/// Use `TryFrom` first and defensively fall back to truncating otherwise.
///
/// # Example
///
/// ```
/// use frame_support::{BoundedVec, traits::DefensiveTruncateFrom};
/// use sp_runtime::traits::ConstU32;
///
/// let unbound = vec![1, 2];
/// let bound = BoundedVec::<u8, ConstU32<2>>::defensive_truncate_from(unbound);
///
/// assert_eq!(bound, vec![1, 2]);
/// ```
fn defensive_truncate_from(unbound: T) -> Self;
}
impl<T, U> DefensiveTruncateFrom<U> for T
where
// NOTE: We use the fact that `BoundedVec` and
// `BoundedSlice` use `Self` as error type. We could also
// require a `Clone` bound and use `unbound.clone()` in the
// error case.
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,
)
}
}
/// Anything that can have a `::len()` method.
pub trait Len {
/// Return the length of data type.
@@ -950,8 +988,59 @@ impl<Hash> PreimageRecipient<Hash> for () {
#[cfg(test)]
mod test {
use super::*;
use sp_core::bounded::{BoundedSlice, BoundedVec};
use sp_std::marker::PhantomData;
#[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>),