diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs index 77163755ac..7539c3c938 100644 --- a/substrate/frame/support/src/lib.rs +++ b/substrate/frame/support/src/lib.rs @@ -1238,7 +1238,7 @@ pub mod pallet_prelude { dispatch::{DispatchResultWithPostInfo, Parameter, DispatchError, DispatchResult}, weights::{DispatchClass, Pays, Weight}, storage::types::{StorageValue, StorageMap, StorageDoubleMap, ValueQuery, OptionQuery}, - storage::bounded_vec::{BoundedVec, BoundedVecValue}, + storage::bounded_vec::BoundedVec, }; pub use codec::{Encode, Decode}; pub use crate::inherent::{InherentData, InherentIdentifier, ProvideInherent}; diff --git a/substrate/frame/support/src/storage/bounded_vec.rs b/substrate/frame/support/src/storage/bounded_vec.rs index 8aecf2dc10..6bb6ea541c 100644 --- a/substrate/frame/support/src/storage/bounded_vec.rs +++ b/substrate/frame/support/src/storage/bounded_vec.rs @@ -19,18 +19,17 @@ //! or a double map. use sp_std::prelude::*; -use sp_std::{convert::TryFrom, marker::PhantomData}; +use sp_std::{convert::TryFrom, fmt, marker::PhantomData}; use codec::{FullCodec, Encode, EncodeLike, Decode}; -use core::{ops::{Index, IndexMut}, slice::SliceIndex}; +use core::{ + ops::{Deref, Index, IndexMut}, + slice::SliceIndex, +}; use crate::{ traits::{Get, MaxEncodedLen}, storage::{generator, StorageDecodeLength, StorageValue, StorageMap, StorageDoubleMap}, }; -/// Marker trait for types `T` that can be stored in storage as `BoundedVec`. -pub trait BoundedVecValue: FullCodec + Clone + sp_std::fmt::Debug {} -impl BoundedVecValue for T {} - /// A bounded vector. /// /// It has implementations for efficient append and length decoding, as with a normal `Vec<_>`, once @@ -38,30 +37,55 @@ impl BoundedVecValue for T {} /// /// As the name suggests, the length of the queue is always bounded. All internal operations ensure /// this bound is respected. -#[derive(Encode, Decode, crate::DefaultNoBound, crate::CloneNoBound, crate::DebugNoBound)] -pub struct BoundedVec>(Vec, PhantomData); - -// NOTE: we could also implement this as: -// impl, S2: Get> PartialEq> for BoundedVec -// to allow comparison of bounded vectors with different bounds. -impl> PartialEq for BoundedVec { - fn eq(&self, rhs: &Self) -> bool { - self.0 == rhs.0 - } -} -impl> Eq for BoundedVec {} - -impl> BoundedVec { - /// Get the bound of the type in `usize`. - pub fn bound() -> usize { - S::get() as usize - } +#[derive(Encode, Decode)] +pub struct BoundedVec(Vec, PhantomData); +impl BoundedVec { /// Create `Self` from `t` without any checks. unsafe fn unchecked_from(t: Vec) -> Self { Self(t, Default::default()) } + /// Consume self, and return the inner `Vec`. Henceforth, the `Vec<_>` can be altered in an + /// arbitrary way. At some point, if the reverse conversion is required, `TryFrom>` can + /// be used. + /// + /// This is useful for cases if you need access to an internal API of the inner `Vec<_>` which + /// is not provided by the wrapper `BoundedVec`. + pub fn into_inner(self) -> Vec { + self.0 + } + + /// Exactly the same semantics as [`Vec::remove`]. + /// + /// # Panics + /// + /// Panics if `index` is out of bounds. + pub fn remove(&mut self, index: usize) { + self.0.remove(index); + } + + /// Exactly the same semantics as [`Vec::swap_remove`]. + /// + /// # Panics + /// + /// Panics if `index` is out of bounds. + pub fn swap_remove(&mut self, index: usize) { + self.0.swap_remove(index); + } + + /// Exactly the same semantics as [`Vec::retain`]. + pub fn retain bool>(&mut self, f: F) { + self.0.retain(f) + } +} + +impl> BoundedVec { + /// Get the bound of the type in `usize`. + pub fn bound() -> usize { + S::get() as usize + } + /// Create `Self` from `t` without any checks. Logs warnings if the bound is not being /// respected. The additional scope can be used to indicate where a potential overflow is /// happening. @@ -77,17 +101,6 @@ impl> BoundedVec { Self::unchecked_from(t) } - /// Consume self, and return the inner `Vec`. Henceforth, the `Vec<_>` can be altered in an - /// arbitrary way. At some point, if the reverse conversion is required, `TryFrom>` can - /// be used. - /// - /// This is useful for cases if you need access to an internal API of the inner `Vec<_>` which - /// is not provided by the wrapper `BoundedVec`. - pub fn into_inner(self) -> Vec { - debug_assert!(self.0.len() <= Self::bound()); - self.0 - } - /// Consumes self and mutates self via the given `mutate` function. /// /// If the outcome of mutation is within bounds, `Some(Self)` is returned. Else, `None` is @@ -129,37 +142,42 @@ impl> BoundedVec { Err(()) } } +} - /// Exactly the same semantics as [`Vec::remove`]. - /// - /// # Panics - /// - /// Panics if `index` is out of bounds. - pub fn remove(&mut self, index: usize) { - self.0.remove(index); - } - - /// Exactly the same semantics as [`Vec::swap_remove`]. - /// - /// # Panics - /// - /// Panics if `index` is out of bounds. - pub fn swap_remove(&mut self, index: usize) { - self.0.swap_remove(index); - } - - /// Exactly the same semantics as [`Vec::retain`]. - pub fn retain bool>(&mut self, f: F) { - self.0.retain(f) +impl Default for BoundedVec { + fn default() -> Self { + // the bound cannot be below 0, which is satisfied by an empty vector + unsafe { Self::unchecked_from(Vec::default()) } } } -impl> TryFrom> for BoundedVec { +#[cfg(feature = "std")] +impl fmt::Debug for BoundedVec +where + T: fmt::Debug, + S: Get, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("BoundedVec").field(&self.0).field(&Self::bound()).finish() + } +} + +impl Clone for BoundedVec +where + T: Clone, +{ + fn clone(&self) -> Self { + // bound is retained + unsafe { Self::unchecked_from(self.0.clone()) } + } +} + +impl> TryFrom> for BoundedVec { type Error = (); fn try_from(t: Vec) -> Result { if t.len() <= Self::bound() { // explicit check just above - Ok(unsafe {Self::unchecked_from(t)}) + Ok(unsafe { Self::unchecked_from(t) }) } else { Err(()) } @@ -167,26 +185,26 @@ impl> TryFrom> for BoundedVec { } // It is okay to give a non-mutable reference of the inner vec to anyone. -impl> AsRef> for BoundedVec { +impl AsRef> for BoundedVec { fn as_ref(&self) -> &Vec { &self.0 } } -impl> AsRef<[T]> for BoundedVec { +impl AsRef<[T]> for BoundedVec { fn as_ref(&self) -> &[T] { &self.0 } } -impl> AsMut<[T]> for BoundedVec { +impl AsMut<[T]> for BoundedVec { fn as_mut(&mut self) -> &mut [T] { &mut self.0 } } // will allow for immutable all operations of `Vec` on `BoundedVec`. -impl> sp_std::ops::Deref for BoundedVec { +impl Deref for BoundedVec { type Target = Vec; fn deref(&self) -> &Self::Target { @@ -195,7 +213,10 @@ impl> sp_std::ops::Deref for BoundedVec { } // Allows for indexing similar to a normal `Vec`. Can panic if out of bound. -impl, I: SliceIndex<[T]>> Index for BoundedVec { +impl Index for BoundedVec +where + I: SliceIndex<[T]>, +{ type Output = I::Output; #[inline] @@ -204,14 +225,17 @@ impl, I: SliceIndex<[T]>> Index for BoundedVe } } -impl, I: SliceIndex<[T]>> IndexMut for BoundedVec { +impl IndexMut for BoundedVec +where + I: SliceIndex<[T]>, +{ #[inline] fn index_mut(&mut self, index: I) -> &mut Self::Output { self.0.index_mut(index) } } -impl> sp_std::iter::IntoIterator for BoundedVec { +impl sp_std::iter::IntoIterator for BoundedVec { type Item = T; type IntoIter = sp_std::vec::IntoIter; fn into_iter(self) -> Self::IntoIter { @@ -219,7 +243,7 @@ impl> sp_std::iter::IntoIterator for BoundedVec< } } -impl> codec::DecodeLength for BoundedVec { +impl codec::DecodeLength for BoundedVec { fn len(self_encoded: &[u8]) -> Result { // `BoundedVec` stored just a `Vec`, thus the length is at the beginning in // `Compact` form, and same implementation as `Vec` can be used. @@ -227,16 +251,30 @@ impl> codec::DecodeLength for BoundedVec { } } -impl> PartialEq> for BoundedVec { +// NOTE: we could also implement this as: +// impl, S2: Get> PartialEq> for BoundedVec +// to allow comparison of bounded vectors with different bounds. +impl PartialEq for BoundedVec +where + T: PartialEq, +{ + fn eq(&self, rhs: &Self) -> bool { + self.0 == rhs.0 + } +} + +impl> PartialEq> for BoundedVec { fn eq(&self, other: &Vec) -> bool { &self.0 == other } } -impl> StorageDecodeLength for BoundedVec {} +impl Eq for BoundedVec where T: Eq {} + +impl StorageDecodeLength for BoundedVec {} /// Storage value that is *maybe* capable of [`StorageAppend`](crate::storage::StorageAppend). -pub trait TryAppendValue> { +pub trait TryAppendValue> { /// Try and append the `item` into the storage item. /// /// This might fail if bounds are not respected. @@ -244,7 +282,7 @@ pub trait TryAppendValue> { } /// Storage map that is *maybe* capable of [`StorageAppend`](crate::storage::StorageAppend). -pub trait TryAppendMap> { +pub trait TryAppendMap> { /// Try and append the `item` into the storage map at the given `key`. /// /// This might fail if bounds are not respected. @@ -255,7 +293,7 @@ pub trait TryAppendMap> { } /// Storage double map that is *maybe* capable of [`StorageAppend`](crate::storage::StorageAppend). -pub trait TryAppendDoubleMap> { +pub trait TryAppendDoubleMap> { /// Try and append the `item` into the storage double map at the given `key`. /// /// This might fail if bounds are not respected. @@ -270,8 +308,12 @@ pub trait TryAppendDoubleMap Result<(), ()>; } -impl, StorageValueT: generator::StorageValue>> - TryAppendValue for StorageValueT +impl TryAppendValue for StorageValueT +where + BoundedVec: FullCodec, + T: Encode, + S: Get, + StorageValueT: generator::StorageValue>, { fn try_append>(item: LikeT) -> Result<(), ()> { let bound = BoundedVec::::bound(); @@ -288,12 +330,13 @@ impl, StorageValueT: generator::StorageValue, - StorageMapT: generator::StorageMap>, - > TryAppendMap for StorageMapT +impl TryAppendMap for StorageMapT +where + K: FullCodec, + BoundedVec: FullCodec, + T: Encode, + S: Get, + StorageMapT: generator::StorageMap>, { fn try_append + Clone, LikeT: EncodeLike>( key: LikeK, @@ -311,13 +354,14 @@ impl< } } -impl< - K1: FullCodec, - K2: FullCodec, - T: BoundedVecValue, - S: Get, - StorageDoubleMapT: generator::StorageDoubleMap>, - > TryAppendDoubleMap for StorageDoubleMapT +impl TryAppendDoubleMap for StorageDoubleMapT +where + K1: FullCodec, + K2: FullCodec, + BoundedVec: FullCodec, + T: Encode, + S: Get, + StorageDoubleMapT: generator::StorageDoubleMap>, { fn try_append< LikeK1: EncodeLike + Clone, @@ -342,7 +386,7 @@ impl< impl MaxEncodedLen for BoundedVec where - T: BoundedVecValue + MaxEncodedLen, + T: MaxEncodedLen, S: Get, BoundedVec: Encode, { @@ -350,7 +394,8 @@ where // BoundedVec encodes like Vec which encodes like [T], which is a compact u32 // plus each item in the slice: // https://substrate.dev/rustdocs/v3.0.0/src/parity_scale_codec/codec.rs.html#798-808 - codec::Compact(S::get()).encoded_size() + codec::Compact(S::get()) + .encoded_size() .saturating_add(Self::bound().saturating_mul(T::max_encoded_len())) } } @@ -427,12 +472,13 @@ pub mod test { // append to a non-existing assert!(FooMap::get(2).is_none()); assert_ok!(FooMap::try_append(2, 4)); - assert_eq!(FooMap::get(2).unwrap(), unsafe {BoundedVec::::unchecked_from(vec![4])}); + assert_eq!(FooMap::get(2).unwrap(), unsafe { + BoundedVec::::unchecked_from(vec![4]) + }); assert_ok!(FooMap::try_append(2, 5)); - assert_eq!( - FooMap::get(2).unwrap(), - unsafe {BoundedVec::::unchecked_from(vec![4, 5])} - ); + assert_eq!(FooMap::get(2).unwrap(), unsafe { + BoundedVec::::unchecked_from(vec![4, 5]) + }); }); TestExternalities::default().execute_with(|| { @@ -449,15 +495,13 @@ pub mod test { // append to a non-existing assert!(FooDoubleMap::get(2, 1).is_none()); assert_ok!(FooDoubleMap::try_append(2, 1, 4)); - assert_eq!( - FooDoubleMap::get(2, 1).unwrap(), - unsafe {BoundedVec::::unchecked_from(vec![4])} - ); + assert_eq!(FooDoubleMap::get(2, 1).unwrap(), unsafe { + BoundedVec::::unchecked_from(vec![4]) + }); assert_ok!(FooDoubleMap::try_append(2, 1, 5)); - assert_eq!( - FooDoubleMap::get(2, 1).unwrap(), - unsafe {BoundedVec::::unchecked_from(vec![4, 5])} - ); + assert_eq!(FooDoubleMap::get(2, 1).unwrap(), unsafe { + BoundedVec::::unchecked_from(vec![4, 5]) + }); }); } diff --git a/substrate/frame/support/src/storage/mod.rs b/substrate/frame/support/src/storage/mod.rs index 1eed6f0c4a..8abe404861 100644 --- a/substrate/frame/support/src/storage/mod.rs +++ b/substrate/frame/support/src/storage/mod.rs @@ -20,10 +20,7 @@ use sp_core::storage::ChildInfo; use sp_std::prelude::*; use codec::{FullCodec, FullEncode, Encode, EncodeLike, Decode}; -use crate::{ - hash::{Twox128, StorageHasher, ReversibleStorageHasher}, - traits::Get, -}; +use crate::hash::{Twox128, StorageHasher, ReversibleStorageHasher}; use sp_runtime::generic::{Digest, DigestItem}; pub use sp_runtime::TransactionOutcome; @@ -811,13 +808,13 @@ pub trait StorageDecodeLength: private::Sealed + codec::DecodeLength { /// outside of this crate. mod private { use super::*; - use bounded_vec::{BoundedVecValue, BoundedVec}; + use bounded_vec::BoundedVec; pub trait Sealed {} impl Sealed for Vec {} impl Sealed for Digest {} - impl> Sealed for BoundedVec {} + impl Sealed for BoundedVec {} impl Sealed for bounded_btree_map::BoundedBTreeMap {} } diff --git a/substrate/frame/support/src/storage/types/double_map.rs b/substrate/frame/support/src/storage/types/double_map.rs index 184d96b3a5..70b0c19f76 100644 --- a/substrate/frame/support/src/storage/types/double_map.rs +++ b/substrate/frame/support/src/storage/types/double_map.rs @@ -18,11 +18,11 @@ //! Storage map type. Implements StorageDoubleMap, StorageIterableDoubleMap, //! StoragePrefixedDoubleMap traits and their methods directly. -use codec::{FullCodec, Decode, EncodeLike, Encode}; +use codec::{Decode, Encode, EncodeLike, FullCodec}; use crate::{ storage::{ StorageAppend, StorageDecodeLength, - bounded_vec::{BoundedVec, BoundedVecValue}, + bounded_vec::BoundedVec, types::{OptionQuery, QueryKindTrait, OnEmptyGetter}, }, traits::{GetDefault, StorageInstance, Get}, @@ -121,7 +121,7 @@ impl, OnEmpty>, OnEmpty: crate::traits::Get + 'static, - VecValue: BoundedVecValue, + VecValue: FullCodec, VecBound: Get, { /// Try and append the given item to the double map in the storage. diff --git a/substrate/frame/support/src/storage/types/map.rs b/substrate/frame/support/src/storage/types/map.rs index 187323b4ad..b9c3044f93 100644 --- a/substrate/frame/support/src/storage/types/map.rs +++ b/substrate/frame/support/src/storage/types/map.rs @@ -22,7 +22,7 @@ use codec::{FullCodec, Decode, EncodeLike, Encode}; use crate::{ storage::{ StorageAppend, StorageDecodeLength, - bounded_vec::{BoundedVec, BoundedVecValue}, + bounded_vec::BoundedVec, types::{OptionQuery, QueryKindTrait, OnEmptyGetter}, }, traits::{GetDefault, StorageInstance, Get}, @@ -100,7 +100,7 @@ where Key: FullCodec, QueryKind: QueryKindTrait, OnEmpty>, OnEmpty: crate::traits::Get + 'static, - VecValue: BoundedVecValue, + VecValue: FullCodec, VecBound: Get, { /// Try and append the given item to the map in the storage. diff --git a/substrate/frame/support/src/storage/types/value.rs b/substrate/frame/support/src/storage/types/value.rs index d536d76d76..6a92a2a632 100644 --- a/substrate/frame/support/src/storage/types/value.rs +++ b/substrate/frame/support/src/storage/types/value.rs @@ -21,7 +21,7 @@ use codec::{FullCodec, Decode, EncodeLike, Encode}; use crate::{ storage::{ StorageAppend, StorageDecodeLength, - bounded_vec::{BoundedVec, BoundedVecValue}, + bounded_vec::BoundedVec, types::{OptionQuery, QueryKindTrait, OnEmptyGetter}, }, traits::{GetDefault, StorageInstance, Get}, @@ -67,7 +67,7 @@ where Prefix: StorageInstance, QueryKind: QueryKindTrait, OnEmpty>, OnEmpty: crate::traits::Get + 'static, - VecValue: BoundedVecValue, + VecValue: FullCodec, VecBound: Get, { /// Try and append the given item to the value in the storage.