From 4bd19b2028cc07f17bab1136d326dcf55b82d72c Mon Sep 17 00:00:00 2001 From: Connor Davis <17688291+PoisonPhang@users.noreply.github.com> Date: Thu, 3 Feb 2022 12:06:20 -0600 Subject: [PATCH] Inverted Validate trait (#572) * failed to decode wrapped types * inverted validate * inverted existing impl of Validate * Added module doc and examples * cargo +nightly fmt * fixed module doc examples * removed commented out code * restored validation * cargo +nightly fmt * made validate param more generic * cleaned up validate calls --- frame/composable-support/src/validation.rs | 212 ++++++++++++++++---- frame/composable-traits/src/lending/math.rs | 6 +- frame/composable-traits/src/lending/mod.rs | 36 ++-- 3 files changed, 194 insertions(+), 60 deletions(-) diff --git a/frame/composable-support/src/validation.rs b/frame/composable-support/src/validation.rs index d375d85c895..6f8340dc800 100644 --- a/frame/composable-support/src/validation.rs +++ b/frame/composable-support/src/validation.rs @@ -1,3 +1,70 @@ +//! Module for validating extrinsic inputs +//! +//! This module is made of two main parts that are needed to validate an +//! extrinsic input, the `Validated` struct and the `Valitate` trait. +//! +//! # Example +//! ## Single Validation +//! ``` +//! use composable_support::validation::{self, Validate, Validated}; +//! use scale_info::TypeInfo; +//! +//! pub struct SomeInput; +//! +//! #[derive(Clone, Copy, Debug, PartialEq, TypeInfo)] +//! pub struct ValidateSomeInput; +//! +//! impl Validate +//! for ValidateSomeInput { +//! fn validate(input: SomeInput) -> Result { +//! // ... validation code +//! Ok(input) +//! } +//! } +//! +//! pub type CheckSomeCondition = (ValidateSomeInput, validation::Valid); +//! +//! pub fn someExtrinsic(input: Validated) { +//! // ... extrinsic code +//! } +//! ``` +//! +//! ## Multiple Validations (Up to 3) +//! ``` +//! use composable_support::validation::{self, Validate, Validated}; +//! use scale_info::TypeInfo; +//! +//! pub struct SomeInput; +//! +//! #[derive(Clone, Copy, Debug, PartialEq, TypeInfo)] +//! pub struct ValidateSomeInput; +//! +//! #[derive(Clone, Copy, Debug, PartialEq, TypeInfo)] +//! pub struct ValidateAnotherCondition; +//! +//! impl Validate +//! for ValidateSomeInput { +//! fn validate(input: SomeInput) -> Result { +//! // ... validation code +//! return Ok(input) +//! } +//! } +//! +//! impl Validate +//! for ValidateAnotherCondition { +//! fn validate(input: SomeInput) -> Result { +//! // ... validation code +//! return Ok(input) +//! } +//! } +//! +//! pub type CheckSomeConditions = (ValidateSomeInput, ValidateAnotherCondition, validation::Valid); +//! +//! pub fn someExtrinsic(input: Validated) { +//! // ... extrinsic code +//! } +//! ``` + use core::marker::PhantomData; use scale_info::TypeInfo; use sp_runtime::DispatchError; @@ -11,10 +78,14 @@ pub struct Validated { impl Validated where - Validated: Validate, + Validated: Validate, + U: Validate, { pub fn new(value: T, _validator_tag: U) -> Result { - Validate::::validate(Self { value, _marker: PhantomData }) + match >::validate(value) { + Ok(value) => Ok(Self { value, _marker: PhantomData }), + Err(e) => Err(e), + } } } @@ -22,9 +93,9 @@ pub trait ValidateDispatch: Sized { fn validate(self) -> Result; } -pub trait Validate: Sized { +pub trait Validate { // use string here because in serde layer there is not dispatch - fn validate(self) -> Result; + fn validate(input: T) -> Result; } #[derive(Debug, Eq, PartialEq, Default)] @@ -33,25 +104,29 @@ pub struct Valid; #[derive(Debug, Eq, PartialEq, Default)] pub struct Invalid; -impl Validate for T { +impl Validate for Invalid { #[inline(always)] - fn validate(self) -> Result { + fn validate(_input: T) -> Result { Err("not valid") } } -impl Validate for T { +impl Validate for Valid { #[inline(always)] - fn validate(self) -> Result { - Ok(self) + fn validate(input: T) -> Result { + Ok(input) } } -impl + Validate, U, V> Validate<(U, V)> for T { +impl Validate for (U, V) +where + U: Validate, + V: Validate, +{ #[inline(always)] - fn validate(self) -> Result { - let value = Validate::::validate(self)?; - let value = Validate::::validate(value)?; + fn validate(input: T) -> Result { + let value = U::validate(input)?; + let value = V::validate(value)?; Ok(value) } } @@ -59,38 +134,47 @@ impl + Validate, U, V> Validate<(U, V)> for T { // as per substrate pattern and existing macroses for similar purposes, they tend to make things // flat like `#[impl_trait_for_tuples::impl_for_tuples(30)]` // so if we will need more than 3, can consider it -impl + Validate + Validate, U, V, W> Validate<(U, V, W)> for T { +impl Validate for (U, V, W) +where + U: Validate, + V: Validate, + W: Validate, +{ #[inline(always)] - fn validate(self) -> Result { - let value = Validate::::validate(self)?; - let value = Validate::::validate(value)?; - let value = Validate::::validate(value)?; + fn validate(input: T) -> Result { + let value = U::validate(input)?; + let value = V::validate(value)?; + let value = W::validate(value)?; Ok(value) } } -impl + Validate + Validate + Validate, U, V, W, Z> Validate<(U, V, W, Z)> - for T +impl Validate for (U, V, W, Z) +where + U: Validate, + V: Validate, + W: Validate, + Z: Validate, { #[inline(always)] - fn validate(self) -> Result { - let value = Validate::::validate(self)?; - let value = Validate::::validate(value)?; - let value = Validate::::validate(value)?; - let value = Validate::::validate(value)?; + fn validate(input: T) -> Result { + let value = U::validate(input)?; + let value = V::validate(value)?; + let value = W::validate(value)?; + let value = Z::validate(value)?; Ok(value) } } -impl, U> Validated { +impl> Validated { pub fn value(self) -> T { self.value } } -impl, U> codec::Decode for Validated { +impl> codec::Decode for Validated { fn decode(input: &mut I) -> Result { - let value = Validate::::validate(T::decode(input)?)?; + let value = >::validate(T::decode(input)?)?; Ok(Validated { value, _marker: PhantomData }) } fn skip(input: &mut I) -> Result<(), codec::Error> { @@ -113,11 +197,17 @@ pub(crate) mod private { } } -impl, U> codec::WrapperTypeEncode +impl> codec::WrapperTypeEncode for Validated { } +impl> Validate for Validated { + fn validate(input: T) -> Result { + >::validate(input) + } +} + #[cfg(test)] mod test { use super::*; @@ -132,6 +222,10 @@ mod test { type CheckARangeTag = (ValidARange, Valid); type CheckBRangeTag = (ValidBRange, Valid); type CheckABRangeTag = (ValidARange, (ValidBRange, Valid)); + type ManyValidatorsTagsNestedInvalid = (ValidARange, (ValidBRange, (Invalid, Valid))); + type ManyValidatorsTagsNestedValid = (ValidARange, (ValidBRange, Valid)); + type ManyValidatorsTagsFlatInvalid = (ValidARange, ValidBRange, Invalid, Valid); + type ManyValidatorsTagsFlatValid = (ValidARange, ValidBRange, Valid); // note: next seems is not supported yet // type NestedValidated = (Validated, Validated); // #[derive(Debug, Eq, PartialEq, codec::Encode, codec::Decode, Default)] @@ -144,22 +238,22 @@ mod test { b: u32, } - impl Validate for X { - fn validate(self) -> Result { - if self.a > 10 { + impl Validate for ValidARange { + fn validate(input: X) -> Result { + if input.a > 10 { Err("Out of range") } else { - Ok(self) + Ok(input) } } } - impl Validate for X { - fn validate(self) -> Result { - if self.b > 10 { + impl Validate for ValidBRange { + fn validate(input: X) -> Result { + if input.b > 10 { Err("Out of range") } else { - Ok(self) + Ok(input) } } } @@ -167,20 +261,49 @@ mod test { #[test] fn nested_validator() { let valid = X { a: 10, b: 0xCAFEBABE }; + assert!(>::validate(valid) + .is_err()); - type ManyValidatorsTagsNested = (ValidARange, (ValidBRange, (Invalid, Valid))); - - assert!(Validate::::validate(valid).is_err()); + let valid = X { a: 10, b: 10 }; + assert_ok!( + >::validate( + valid + ) + ); } #[test] fn either_nested_or_flat() { let valid = X { a: 10, b: 0xCAFEBABE }; - type ManyValidatorsTagsNested = (ValidARange, (ValidBRange, (Invalid, Valid))); - type ManyValidatorsTagsFlat = (ValidARange, ValidBRange, Invalid, Valid); assert_eq!( - Validate::::validate(valid.clone()), - Validate::::validate(valid) + >::validate( + valid.clone() + ), + >::validate(valid) + ); + } + + #[test] + fn flat_validator_multiple_invalid() { + let value = X { a: 10, b: 0xCAFEBABE }; + + assert!( + >::validate(value).is_err() + ); + } + + #[test] + fn flat_validator_multiple_valid() { + let value = X { a: 10, b: 0xCAFEBABE }; + + assert!( + >::validate( + value + ) + .is_err() ); } @@ -196,6 +319,7 @@ mod test { fn test_valid_a() { let valid = X { a: 10, b: 0xCAFEBABE }; let bytes = valid.encode(); + assert_eq!( Ok(Validated { value: valid, _marker: PhantomData }), Validated::::decode(&mut &bytes[..]) diff --git a/frame/composable-traits/src/lending/math.rs b/frame/composable-traits/src/lending/math.rs index cc4f1c02b02..e943a72370c 100644 --- a/frame/composable-traits/src/lending/math.rs +++ b/frame/composable-traits/src/lending/math.rs @@ -113,10 +113,10 @@ impl InterestRateModel { } pub struct InteresteRateModelIsValid; -impl Validate for InterestRateModel { - fn validate(self) -> Result { +impl Validate for InteresteRateModelIsValid { + fn validate(interest_rate_model: InterestRateModel) -> Result { const ERROR: &str = "interest rate model is not valid"; - match self { + match interest_rate_model { InterestRateModel::Jump(x) => JumpModel::new(x.base_rate, x.jump_rate, x.full_rate, x.target_utilization) .ok_or(ERROR) diff --git a/frame/composable-traits/src/lending/mod.rs b/frame/composable-traits/src/lending/mod.rs index f97ecc3487d..976890be68a 100644 --- a/frame/composable-traits/src/lending/mod.rs +++ b/frame/composable-traits/src/lending/mod.rs @@ -47,29 +47,39 @@ pub struct MarketModelValid; #[derive(Clone, Copy, Debug, PartialEq, TypeInfo)] pub struct CurrencyPairIsNotSame; -impl Validate - for CreateInput +impl + Validate, MarketModelValid> for MarketModelValid { - fn validate(self) -> Result { - if self.updatable.collateral_factor < MoreThanOneFixedU128::one() { - return Err("collateral factor must be >=1") + fn validate( + create_input: CreateInput, + ) -> Result, &'static str> { + if create_input.updatable.collateral_factor < MoreThanOneFixedU128::one() { + return Err("collateral factor must be >= 1") } - let interest_rate_model = - Validate::::validate(self.updatable.interest_rate_model)?; + let interest_rate_model = >::validate(create_input.updatable.interest_rate_model)?; - Ok(Self { updatable: UpdateInput { interest_rate_model, ..self.updatable }, ..self }) + Ok(CreateInput { + updatable: UpdateInput { interest_rate_model, ..create_input.updatable }, + ..create_input + }) } } -impl Validate - for CreateInput +impl + Validate, CurrencyPairIsNotSame> + for CurrencyPairIsNotSame { - fn validate(self) -> Result { - if self.currency_pair.base == self.currency_pair.quote { + fn validate( + create_input: CreateInput, + ) -> Result, &'static str> { + if create_input.currency_pair.base == create_input.currency_pair.quote { Err("currency pair must be different assets") } else { - Ok(self) + Ok(create_input) } } }