mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-27 03:27:58 +00:00
Introduce stacked filtering (#6273)
* Introduce stacked filtering. * Benchmarks * Remove unneeded crates * Fix proxy type's permissiveness checks. * Repot multisig to make utility stateless. * Repot filter stack impl into macro * Fix wasm build * Tests * Final test. * Tests for the macro * Fix test * Line width * Fix * Update frame/multisig/src/benchmarking.rs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * Update primitives/std/with_std.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Grumble * Update frame/support/src/traits.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Update frame/support/src/traits.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Update frame/support/src/traits.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Update frame/support/src/traits.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Update frame/support/src/traits.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Update frame/multisig/src/tests.rs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * Update frame/multisig/src/tests.rs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * Grumble * Migration * Grumble * Comments * Migration * Fix * Fix * Line width * Allow unused * Update frame/multisig/src/lib.rs Co-authored-by: Alexander Popiak <alexander.popiak@parity.io> * Fix up grumble. * Remove Utility constraint in NonTransfer Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>
This commit is contained in:
@@ -33,6 +33,10 @@ use crate::storage::StorageMap;
|
||||
use crate::weights::Weight;
|
||||
use impl_trait_for_tuples::impl_for_tuples;
|
||||
|
||||
/// Re-expected for the macro.
|
||||
#[doc(hidden)]
|
||||
pub use sp_std::{mem::{swap, take}, cell::RefCell, vec::Vec, boxed::Box};
|
||||
|
||||
/// Simple trait for providing a filter over a reference to some type.
|
||||
pub trait Filter<T> {
|
||||
/// Determine if a given value should be allowed through the filter (returns `true`) or not.
|
||||
@@ -43,29 +47,241 @@ impl<T> Filter<T> for () {
|
||||
fn filter(_: &T) -> bool { true }
|
||||
}
|
||||
|
||||
/// Trait to add a constraint onto the filter.
|
||||
pub trait FilterStack<T>: Filter<T> {
|
||||
/// The type used to archive the stack.
|
||||
type Stack;
|
||||
|
||||
/// Add a new `constraint` onto the filter.
|
||||
fn push(constraint: impl Fn(&T) -> bool + 'static);
|
||||
|
||||
/// Removes the most recently pushed, and not-yet-popped, constraint from the filter.
|
||||
fn pop();
|
||||
|
||||
/// Clear the filter, returning a value that may be used later to `restore` it.
|
||||
fn take() -> Self::Stack;
|
||||
|
||||
/// Restore the filter from a previous `take` operation.
|
||||
fn restore(taken: Self::Stack);
|
||||
}
|
||||
|
||||
/// Guard type for pushing a constraint to a `FilterStack` and popping when dropped.
|
||||
pub struct FilterStackGuard<F: FilterStack<T>, T>(PhantomData<(F, T)>);
|
||||
|
||||
/// Guard type for clearing all pushed constraints from a `FilterStack` and reinstating them when
|
||||
/// dropped.
|
||||
pub struct ClearFilterGuard<F: FilterStack<T>, T>(Option<F::Stack>, PhantomData<T>);
|
||||
|
||||
impl<F: FilterStack<T>, T> FilterStackGuard<F, T> {
|
||||
/// Create a new instance, adding a new `constraint` onto the filter `T`, and popping it when
|
||||
/// this instance is dropped.
|
||||
pub fn new(constraint: impl Fn(&T) -> bool + 'static) -> Self {
|
||||
F::push(constraint);
|
||||
Self(PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FilterStack<T>, T> Drop for FilterStackGuard<F, T> {
|
||||
fn drop(&mut self) {
|
||||
F::pop();
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FilterStack<T>, T> ClearFilterGuard<F, T> {
|
||||
/// Create a new instance, adding a new `constraint` onto the filter `T`, and popping it when
|
||||
/// this instance is dropped.
|
||||
pub fn new() -> Self {
|
||||
Self(Some(F::take()), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FilterStack<T>, T> Drop for ClearFilterGuard<F, T> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(taken) = self.0.take() {
|
||||
F::restore(taken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple trait for providing a filter over a reference to some type, given an instance of itself.
|
||||
pub trait InstanceFilter<T> {
|
||||
pub trait InstanceFilter<T>: Sized + Send + Sync {
|
||||
/// Determine if a given value should be allowed through the filter (returns `true`) or not.
|
||||
fn filter(&self, _: &T) -> bool;
|
||||
|
||||
/// Determines whether `self` matches at least all items that `o` does.
|
||||
fn is_no_less_permissive(&self, o: &Self) -> bool { !self.is_less_permissive(o) }
|
||||
|
||||
/// Determines whether `self` matches at most only the items that `o` does.
|
||||
fn is_no_more_permissive(&self, o: &Self) -> bool { !o.is_less_permissive(&self) }
|
||||
|
||||
/// Determines whether `self` matches all the items that `o` does and others.
|
||||
fn is_more_permissive(&self, o: &Self) -> bool { o.is_less_permissive(self) }
|
||||
|
||||
/// Determines whether `self` does not match all the items that `_o` does, nor any others.
|
||||
///
|
||||
/// NOTE: This is the only `*permissive` function that needs to be reimplemented.
|
||||
fn is_less_permissive(&self, _o: &Self) -> bool { true }
|
||||
/// Determines whether `self` matches at least everything that `_o` does.
|
||||
fn is_superset(&self, _o: &Self) -> bool { false }
|
||||
}
|
||||
|
||||
impl<T> InstanceFilter<T> for () {
|
||||
fn filter(&self, _: &T) -> bool { true }
|
||||
fn is_less_permissive(&self, _o: &Self) -> bool { false }
|
||||
fn is_superset(&self, _o: &Self) -> bool { true }
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_filter_stack {
|
||||
($target:ty, $base:ty, $call:ty, $module:ident) => {
|
||||
#[cfg(feature = "std")]
|
||||
mod $module {
|
||||
#[allow(unused_imports)]
|
||||
use super::*;
|
||||
use $crate::traits::{swap, take, RefCell, Vec, Box, Filter, FilterStack};
|
||||
|
||||
thread_local! {
|
||||
static FILTER: RefCell<Vec<Box<dyn Fn(&$call) -> bool + 'static>>> = RefCell::new(Vec::new());
|
||||
}
|
||||
|
||||
impl Filter<$call> for $target {
|
||||
fn filter(call: &$call) -> bool {
|
||||
<$base>::filter(call) &&
|
||||
FILTER.with(|filter| filter.borrow().iter().all(|f| f(call)))
|
||||
}
|
||||
}
|
||||
|
||||
impl FilterStack<$call> for $target {
|
||||
type Stack = Vec<Box<dyn Fn(&$call) -> bool + 'static>>;
|
||||
fn push(f: impl Fn(&$call) -> bool + 'static) {
|
||||
FILTER.with(|filter| filter.borrow_mut().push(Box::new(f)));
|
||||
}
|
||||
fn pop() {
|
||||
FILTER.with(|filter| filter.borrow_mut().pop());
|
||||
}
|
||||
fn take() -> Self::Stack {
|
||||
FILTER.with(|filter| take(filter.borrow_mut().as_mut()))
|
||||
}
|
||||
fn restore(mut s: Self::Stack) {
|
||||
FILTER.with(|filter| swap(filter.borrow_mut().as_mut(), &mut s));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
mod $module {
|
||||
#[allow(unused_imports)]
|
||||
use super::*;
|
||||
use $crate::traits::{swap, take, RefCell, Vec, Box, Filter, FilterStack};
|
||||
|
||||
struct ThisFilter(RefCell<Vec<Box<dyn Fn(&$call) -> bool + 'static>>>);
|
||||
// NOTE: Safe only in wasm (guarded above) because there's only one thread.
|
||||
unsafe impl Send for ThisFilter {}
|
||||
unsafe impl Sync for ThisFilter {}
|
||||
|
||||
static FILTER: ThisFilter = ThisFilter(RefCell::new(Vec::new()));
|
||||
|
||||
impl Filter<$call> for $target {
|
||||
fn filter(call: &$call) -> bool {
|
||||
<$base>::filter(call) && FILTER.0.borrow().iter().all(|f| f(call))
|
||||
}
|
||||
}
|
||||
|
||||
impl FilterStack<$call> for $target {
|
||||
type Stack = Vec<Box<dyn Fn(&$call) -> bool + 'static>>;
|
||||
fn push(f: impl Fn(&$call) -> bool + 'static) {
|
||||
FILTER.0.borrow_mut().push(Box::new(f));
|
||||
}
|
||||
fn pop() {
|
||||
FILTER.0.borrow_mut().pop();
|
||||
}
|
||||
fn take() -> Self::Stack {
|
||||
take(FILTER.0.borrow_mut().as_mut())
|
||||
}
|
||||
fn restore(mut s: Self::Stack) {
|
||||
swap(FILTER.0.borrow_mut().as_mut(), &mut s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_impl_filter_stack {
|
||||
use super::*;
|
||||
|
||||
pub struct IsCallable;
|
||||
pub struct BaseFilter;
|
||||
impl Filter<u32> for BaseFilter {
|
||||
fn filter(x: &u32) -> bool { x % 2 == 0 }
|
||||
}
|
||||
impl_filter_stack!(
|
||||
crate::traits::test_impl_filter_stack::IsCallable,
|
||||
crate::traits::test_impl_filter_stack::BaseFilter,
|
||||
u32,
|
||||
is_callable
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn impl_filter_stack_should_work() {
|
||||
assert!(IsCallable::filter(&36));
|
||||
assert!(IsCallable::filter(&40));
|
||||
assert!(IsCallable::filter(&42));
|
||||
assert!(!IsCallable::filter(&43));
|
||||
|
||||
IsCallable::push(|x| *x < 42);
|
||||
assert!(IsCallable::filter(&36));
|
||||
assert!(IsCallable::filter(&40));
|
||||
assert!(!IsCallable::filter(&42));
|
||||
|
||||
IsCallable::push(|x| *x % 3 == 0);
|
||||
assert!(IsCallable::filter(&36));
|
||||
assert!(!IsCallable::filter(&40));
|
||||
|
||||
IsCallable::pop();
|
||||
assert!(IsCallable::filter(&36));
|
||||
assert!(IsCallable::filter(&40));
|
||||
assert!(!IsCallable::filter(&42));
|
||||
|
||||
let saved = IsCallable::take();
|
||||
assert!(IsCallable::filter(&36));
|
||||
assert!(IsCallable::filter(&40));
|
||||
assert!(IsCallable::filter(&42));
|
||||
assert!(!IsCallable::filter(&43));
|
||||
|
||||
IsCallable::restore(saved);
|
||||
assert!(IsCallable::filter(&36));
|
||||
assert!(IsCallable::filter(&40));
|
||||
assert!(!IsCallable::filter(&42));
|
||||
|
||||
IsCallable::pop();
|
||||
assert!(IsCallable::filter(&36));
|
||||
assert!(IsCallable::filter(&40));
|
||||
assert!(IsCallable::filter(&42));
|
||||
assert!(!IsCallable::filter(&43));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn guards_should_work() {
|
||||
assert!(IsCallable::filter(&36));
|
||||
assert!(IsCallable::filter(&40));
|
||||
assert!(IsCallable::filter(&42));
|
||||
assert!(!IsCallable::filter(&43));
|
||||
{
|
||||
let _guard_1 = FilterStackGuard::<IsCallable, u32>::new(|x| *x < 42);
|
||||
assert!(IsCallable::filter(&36));
|
||||
assert!(IsCallable::filter(&40));
|
||||
assert!(!IsCallable::filter(&42));
|
||||
{
|
||||
let _guard_2 = FilterStackGuard::<IsCallable, u32>::new(|x| *x % 3 == 0);
|
||||
assert!(IsCallable::filter(&36));
|
||||
assert!(!IsCallable::filter(&40));
|
||||
}
|
||||
assert!(IsCallable::filter(&36));
|
||||
assert!(IsCallable::filter(&40));
|
||||
assert!(!IsCallable::filter(&42));
|
||||
{
|
||||
let _guard_2 = ClearFilterGuard::<IsCallable, u32>::new();
|
||||
assert!(IsCallable::filter(&36));
|
||||
assert!(IsCallable::filter(&40));
|
||||
assert!(IsCallable::filter(&42));
|
||||
assert!(!IsCallable::filter(&43));
|
||||
}
|
||||
assert!(IsCallable::filter(&36));
|
||||
assert!(IsCallable::filter(&40));
|
||||
assert!(!IsCallable::filter(&42));
|
||||
}
|
||||
assert!(IsCallable::filter(&36));
|
||||
assert!(IsCallable::filter(&40));
|
||||
assert!(IsCallable::filter(&42));
|
||||
assert!(!IsCallable::filter(&43));
|
||||
}
|
||||
}
|
||||
|
||||
/// An abstraction of a value stored within storage, but possibly as part of a larger composite
|
||||
|
||||
Reference in New Issue
Block a user