diff --git a/substrate/frame/support/src/storage/bounded_vec.rs b/substrate/frame/support/src/storage/bounded_vec.rs index 5b253f76333be4306a0d4de267cf09e269d5ca27..f441ba39b8843cd8b2dde9ce95d119490ef9e3c3 100644 --- a/substrate/frame/support/src/storage/bounded_vec.rs +++ b/substrate/frame/support/src/storage/bounded_vec.rs @@ -23,7 +23,7 @@ use sp_std::{convert::TryFrom, marker::PhantomData}; use codec::{FullCodec, Encode, EncodeLike, Decode}; use core::{ops::{Index, IndexMut}, slice::SliceIndex}; use crate::{ - traits::Get, + traits::{Get, MaxEncodedLen}, storage::{generator, StorageDecodeLength, StorageValue, StorageMap, StorageDoubleMap}, }; @@ -347,6 +347,21 @@ impl< } } +impl<T, S> MaxEncodedLen for BoundedVec<T, S> +where + T: BoundedVecValue + MaxEncodedLen, + S: Get<u32>, + BoundedVec<T, S>: Encode, +{ + fn max_encoded_len() -> usize { + // BoundedVec<T, S> encodes like Vec<T> 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::<u32>::max_encoded_len() + .saturating_add(Self::bound().saturating_mul(T::max_encoded_len())) + } +} + #[cfg(test)] pub mod test { use super::*; diff --git a/substrate/frame/support/src/traits.rs b/substrate/frame/support/src/traits.rs index 7ee2b0a56094b5a42b6fb75040e8ecec9f1b54ef..d15356c1e1b09796146f3ba197fb79a303951c9f 100644 --- a/substrate/frame/support/src/traits.rs +++ b/substrate/frame/support/src/traits.rs @@ -80,3 +80,6 @@ pub use dispatch::{EnsureOrigin, OriginTrait, UnfilteredDispatchable}; mod voting; pub use voting::{CurrencyToVote, SaturatingCurrencyToVote, U128CurrencyToVote}; + +mod max_encoded_len; +pub use max_encoded_len::MaxEncodedLen; diff --git a/substrate/frame/support/src/traits/max_encoded_len.rs b/substrate/frame/support/src/traits/max_encoded_len.rs new file mode 100644 index 0000000000000000000000000000000000000000..2cf9007d4d62140f9446781e9ebb84c6adcd5846 --- /dev/null +++ b/substrate/frame/support/src/traits/max_encoded_len.rs @@ -0,0 +1,132 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use codec::{Compact, Encode}; +use impl_trait_for_tuples::impl_for_tuples; +use sp_std::{mem, marker::PhantomData}; + +/// Items implementing `MaxEncodedLen` have a statically known maximum encoded size. +/// +/// Some containers, such as `BoundedVec`, have enforced size limits and this trait +/// can be implemented accurately. Other containers, such as `StorageMap`, do not have enforced size +/// limits. For those containers, it is necessary to make a documented assumption about the maximum +/// usage, and compute the max encoded length based on that assumption. +pub trait MaxEncodedLen: Encode { + /// Upper bound, in bytes, of the maximum encoded size of this item. + fn max_encoded_len() -> usize; +} + +macro_rules! impl_primitives { + ( $($t:ty),+ ) => { + $( + impl MaxEncodedLen for $t { + fn max_encoded_len() -> usize { + mem::size_of::<$t>() + } + } + )+ + }; +} + +impl_primitives!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, bool); + +macro_rules! impl_compact { + ($( $t:ty => $e:expr; )*) => { + $( + impl MaxEncodedLen for Compact<$t> { + fn max_encoded_len() -> usize { + $e + } + } + )* + }; +} + +impl_compact!( + // https://github.com/paritytech/parity-scale-codec/blob/f0341dabb01aa9ff0548558abb6dcc5c31c669a1/src/compact.rs#L261 + u8 => 2; + // https://github.com/paritytech/parity-scale-codec/blob/f0341dabb01aa9ff0548558abb6dcc5c31c669a1/src/compact.rs#L291 + u16 => 4; + // https://github.com/paritytech/parity-scale-codec/blob/f0341dabb01aa9ff0548558abb6dcc5c31c669a1/src/compact.rs#L326 + u32 => 5; + // https://github.com/paritytech/parity-scale-codec/blob/f0341dabb01aa9ff0548558abb6dcc5c31c669a1/src/compact.rs#L369 + u64 => 9; + // https://github.com/paritytech/parity-scale-codec/blob/f0341dabb01aa9ff0548558abb6dcc5c31c669a1/src/compact.rs#L413 + u128 => 17; +); + +// impl_for_tuples for values 19 and higher fails because that's where the WrapperTypeEncode impl stops. +#[impl_for_tuples(18)] +impl MaxEncodedLen for Tuple { + fn max_encoded_len() -> usize { + let mut len: usize = 0; + for_tuples!( #( len = len.saturating_add(Tuple::max_encoded_len()); )* ); + len + } +} + +impl<T: MaxEncodedLen, const N: usize> MaxEncodedLen for [T; N] { + fn max_encoded_len() -> usize { + T::max_encoded_len().saturating_mul(N) + } +} + +impl<T: MaxEncodedLen> MaxEncodedLen for Option<T> { + fn max_encoded_len() -> usize { + T::max_encoded_len().saturating_add(1) + } +} + +impl<T, E> MaxEncodedLen for Result<T, E> +where + T: MaxEncodedLen, + E: MaxEncodedLen, +{ + fn max_encoded_len() -> usize { + T::max_encoded_len().max(E::max_encoded_len()).saturating_add(1) + } +} + +impl<T> MaxEncodedLen for PhantomData<T> { + fn max_encoded_len() -> usize { + 0 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + macro_rules! test_compact_length { + ($(fn $name:ident($t:ty);)*) => { + $( + #[test] + fn $name() { + assert_eq!(Compact(<$t>::MAX).encode().len(), Compact::<$t>::max_encoded_len()); + } + )* + }; + } + + test_compact_length!( + fn compact_u8(u8); + fn compact_u16(u16); + fn compact_u32(u32); + fn compact_u64(u64); + fn compact_u128(u128); + ); +}