-
Notifications
You must be signed in to change notification settings - Fork 2.7k
MaxEncodedLen tracking issue #8719
Description
Rationale
For PoV benchmarking, we need some way to estimate what the maximum encoded size of an arbitrary storage item can be. We've settled on a new trait, MaxEncodedLen.
While Encode::size_hint already exists, we can't rely on it. One fatal flaw: it's an optional, "if possible" method which for many structs simply returns the default size of 0. Another: Encode is implemented for certain types such as VecDeque<T>, which we'd want to intentionally exclude from storage, as they aren't bounded. We need a trait which we can use to exclude types.
Design
pub trait MaxEncodedLen: Encode {
fn max_encoded_len() -> usize;
}Roadmap
- implement BoundedEncodedLen #8720 Introduce the
BoundedEncodedLentrait - implement BoundedEncodedLen #8720 Implement for primitive types, fixed size arrays, tuples, etc. Non-containers for which
Encodeis implemented. Mostly defers tosp_std::mem::size_of::<T>(). - impl BoundedEncodedLen for BoundedVec #8727 Implement for
BoundedVec<T> - implement BoundedEncodedLen #8720 Implement for certain containers:
-
Option<T> -
Result<T, E> -
PhantomData<T> -
Compact<T>
-
- Optional: Implement
Boundedvariants for other container types for whichEncodeis implemented. ImplementBoundedEncodedLenfor these bounded variants.- Add
BoundedBTreeMaptoframe_support::storage#8745BTreeMap<K, V> - Add
BoundedBTreeSet#8750BTreeSet<T> -
BinaryHeap<T> -
LinkedList<T> -
VecDeque<T>
- Add
-
#[derive(MaxEncodedLen)]#8737 Derive macro:#[derive(Encode, BoundedEncodedLen)]for non-container structs, enums. Requires all fields to implementEncodeandBoundedEncodedLen. - Extend FRAME storage macros such that all
StorageMapand friends are actually implemented as newtypes which implement the full API of the parent type, plusBoundedEncodedLen- Backwards-compatibility: existing macro declarations should use a fixed bound of 300,000
- Allow to specify some max number of values for storages in pallet macro. #8735 A new optional attribute (pending bikeshedding) allows per-map customization of bound.
#[storage::map_bound(300_000)]. The value specified in this attribute is used to determine the bounded length of the map.
- New requirement on
frame::storage: all storage items must implementBoundedEncodedLen. - Patch all the holes to make everything work.
Notes
This always explicitly computes an upper bound. While a u128 can potentially be compact-encoded in only one byte, Compact<u128>::max_encoded_len() will always return 17.
While implementations of BoundedVec<T> and friends impose a hard cap on their length, we do not use BoundedEncodedLen to impose a hard cap on the size of StorageMap and friends. While potentially a parachain author could misrepresent the bounds on its maps to attempt to pack a parachain block with more transactions, that behavior is self-defeating. Eventually they will try to pack too many transactions into a block, causing that block to become invalid because its actual size is too large. User documentation for the storage::map_bound attribute should emphasize that it is in the chain author's best interest to use a reasonable value here.
We'd like to offer logging warnings that could be noted by Prometheus etc when the size of StorageMap exceeds certain thresholds (eg. info at 80%, warn at 100%), but that feature is out of scope of this tracking issue. It requires additional design work because currently the only way to determine a map's size is to iterate over its members, which is not performant.
max_encoded_len is a function instead of an associated constant for pragmatic rather than theoretical reasons. Currently, BoundedVec's limit is not a constant, so we can't use its value in a constant context.