From b42a80c82b54b97411d2e3bdde72f6707ef757f4 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 10 Oct 2022 15:57:02 +0300 Subject: [PATCH] Remove without_storage_info from parachains pallet (#1596) * remove without_storage_info from pallet-bridge-parachains * fix benchmarks --- bridges/bin/millau/runtime/src/lib.rs | 4 + bridges/modules/grandpa/src/lib.rs | 32 +++- bridges/modules/grandpa/src/storage_types.rs | 46 +----- bridges/modules/parachains/src/lib.rs | 139 ++++++++++++++++-- bridges/modules/parachains/src/mock.rs | 2 + bridges/primitives/chain-rialto/src/lib.rs | 3 + bridges/primitives/chain-westend/src/lib.rs | 3 + bridges/primitives/parachains/src/lib.rs | 6 +- bridges/primitives/runtime/src/lib.rs | 2 + .../primitives/runtime/src/storage_types.rs | 90 ++++++++++++ 10 files changed, 258 insertions(+), 69 deletions(-) create mode 100644 bridges/primitives/runtime/src/storage_types.rs diff --git a/bridges/bin/millau/runtime/src/lib.rs b/bridges/bin/millau/runtime/src/lib.rs index 538417127e362..cb0862bc047f3 100644 --- a/bridges/bin/millau/runtime/src/lib.rs +++ b/bridges/bin/millau/runtime/src/lib.rs @@ -550,6 +550,8 @@ impl pallet_bridge_messages::Config for Run parameter_types! { pub const RialtoParasPalletName: &'static str = bp_rialto::PARAS_PALLET_NAME; pub const WestendParasPalletName: &'static str = bp_westend::PARAS_PALLET_NAME; + pub const MaxRialtoParaHeadSize: u32 = bp_rialto::MAX_NESTED_PARACHAIN_HEAD_SIZE; + pub const MaxWestendParaHeadSize: u32 = bp_westend::MAX_NESTED_PARACHAIN_HEAD_SIZE; } /// Instance of the with-Rialto parachains pallet. @@ -562,6 +564,7 @@ impl pallet_bridge_parachains::Config for Runtime type ParasPalletName = RialtoParasPalletName; type TrackedParachains = frame_support::traits::Everything; type HeadsToKeep = HeadersToKeep; + type MaxParaHeadSize = MaxRialtoParaHeadSize; } /// Instance of the with-Westend parachains pallet. @@ -574,6 +577,7 @@ impl pallet_bridge_parachains::Config for Runtime type ParasPalletName = WestendParasPalletName; type TrackedParachains = frame_support::traits::Everything; type HeadsToKeep = HeadersToKeep; + type MaxParaHeadSize = MaxWestendParaHeadSize; } construct_runtime!( diff --git a/bridges/modules/grandpa/src/lib.rs b/bridges/modules/grandpa/src/lib.rs index c4cbadcb388f6..37afbf2739ec7 100644 --- a/bridges/modules/grandpa/src/lib.rs +++ b/bridges/modules/grandpa/src/lib.rs @@ -36,10 +36,12 @@ // Runtime-generated enums #![allow(clippy::large_enum_variant)] -use storage_types::{StoredAuthoritySet, StoredBridgedHeader}; +use storage_types::StoredAuthoritySet; use bp_header_chain::{justification::GrandpaJustification, InitializationData}; -use bp_runtime::{BlockNumberOf, Chain, HashOf, HasherOf, HeaderOf, OwnedBridgeModule}; +use bp_runtime::{ + BlockNumberOf, BoundedStorageValue, Chain, HashOf, HasherOf, HeaderOf, OwnedBridgeModule, +}; use finality_grandpa::voter_set::VoterSet; use frame_support::{ensure, fail}; use frame_system::ensure_signed; @@ -73,6 +75,9 @@ pub type BridgedBlockHash = HashOf<>::BridgedChain>; pub type BridgedBlockHasher = HasherOf<>::BridgedChain>; /// Header of the bridged chain. pub type BridgedHeader = HeaderOf<>::BridgedChain>; +/// Stored header of the bridged chain. +pub type StoredBridgedHeader = + BoundedStorageValue<>::MaxBridgedHeaderSize, BridgedHeader>; #[frame_support::pallet] pub mod pallet { @@ -199,8 +204,18 @@ pub mod pallet { let is_authorities_change_enacted = try_enact_authority_change::(&finality_target, set_id)?; - let finality_target = - StoredBridgedHeader::::try_from_bridged_header(*finality_target)?; + let finality_target = StoredBridgedHeader::::try_from_inner(*finality_target) + .map_err(|e| { + log::error!( + target: LOG_TARGET, + "Size of header {:?} ({}) is larger that the configured value {}", + hash, + e.value_size, + e.maximal_size, + ); + + Error::::TooLargeHeader + })?; >::mutate(|count| *count += 1); insert_header::(finality_target, hash); log::info!( @@ -504,7 +519,8 @@ pub mod pallet { init_params; let authority_set = StoredAuthoritySet::::try_new(authority_list, set_id) .map_err(|_| Error::TooManyAuthoritiesInSet)?; - let header = StoredBridgedHeader::::try_from_bridged_header(*header)?; + let header = StoredBridgedHeader::::try_from_inner(*header) + .map_err(|_| Error::::TooLargeHeader)?; let initial_hash = header.hash(); >::put(initial_hash); @@ -538,7 +554,7 @@ pub mod pallet { ); let hash = header.hash(); insert_header::( - StoredBridgedHeader::try_from_bridged_header(header) + StoredBridgedHeader::::try_from_inner(header) .expect("only used from benchmarks; benchmarks are correct; qed"), hash, ); @@ -553,7 +569,7 @@ impl, I: 'static> Pallet { /// if the pallet has not been initialized yet. pub fn best_finalized() -> Option> { let (_, hash) = >::get()?; - >::get(hash).map(|h| h.0) + >::get(hash).map(|h| h.into_inner()) } /// Check if a particular header is known to the bridge pallet. @@ -1103,7 +1119,7 @@ mod tests { >::put((2, hash)); >::insert( hash, - StoredBridgedHeader::try_from_bridged_header(header).unwrap(), + StoredBridgedHeader::::try_from_inner(header).unwrap(), ); assert_ok!( diff --git a/bridges/modules/grandpa/src/storage_types.rs b/bridges/modules/grandpa/src/storage_types.rs index 5d2cb661d813a..ac4835e4f0d41 100644 --- a/bridges/modules/grandpa/src/storage_types.rs +++ b/bridges/modules/grandpa/src/storage_types.rs @@ -16,12 +16,12 @@ //! Wrappers for public types that are implementing `MaxEncodedLen` -use crate::{BridgedHeader, Config, Error}; +use crate::Config; use bp_header_chain::AuthoritySet; use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::{traits::Get, BoundedVec, RuntimeDebugNoBound}; -use scale_info::{Type, TypeInfo}; +use frame_support::{BoundedVec, RuntimeDebugNoBound}; +use scale_info::TypeInfo; use sp_finality_grandpa::{AuthorityId, AuthorityList, AuthorityWeight, SetId}; /// A bounded list of Grandpa authorities with associated weights. @@ -64,43 +64,3 @@ impl, I: 'static> From> for AuthoritySet { AuthoritySet { authorities: t.authorities.into(), set_id: t.set_id } } } - -/// A bounded chain header. -#[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebugNoBound)] -pub struct StoredBridgedHeader, I: 'static>(pub BridgedHeader); - -impl, I: 'static> StoredBridgedHeader { - /// Construct `StoredBridgedHeader` from the `BridgedHeader` with all required checks. - pub fn try_from_bridged_header(header: BridgedHeader) -> Result> { - // this conversion is heavy (since we do encoding here), so we may want to optimize it later - // (e.g. by introducing custom Encode implementation, and turning `StoredBridgedHeader` into - // `enum StoredBridgedHeader { Decoded(BridgedHeader), Encoded(Vec) }`) - if header.encoded_size() > T::MaxBridgedHeaderSize::get() as usize { - Err(Error::TooLargeHeader) - } else { - Ok(StoredBridgedHeader(header)) - } - } -} - -impl, I: 'static> sp_std::ops::Deref for StoredBridgedHeader { - type Target = BridgedHeader; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl, I: 'static> TypeInfo for StoredBridgedHeader { - type Identity = Self; - - fn type_info() -> Type { - BridgedHeader::::type_info() - } -} - -impl, I: 'static> MaxEncodedLen for StoredBridgedHeader { - fn max_encoded_len() -> usize { - T::MaxBridgedHeaderSize::get() as usize - } -} diff --git a/bridges/modules/parachains/src/lib.rs b/bridges/modules/parachains/src/lib.rs index 290bdadf04a73..ac21e997f3146 100644 --- a/bridges/modules/parachains/src/lib.rs +++ b/bridges/modules/parachains/src/lib.rs @@ -69,11 +69,15 @@ pub mod pallet { use super::*; use bp_parachains::{BestParaHeadHash, ImportedParaHeadsKeyProvider, ParasInfoKeyProvider}; use bp_runtime::{ - BasicOperatingMode, OwnedBridgeModule, StorageDoubleMapKeyProvider, StorageMapKeyProvider, + BasicOperatingMode, BoundedStorageValue, OwnedBridgeModule, StorageDoubleMapKeyProvider, + StorageMapKeyProvider, }; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; + /// Stored parachain head of given parachains pallet. + pub type StoredParaHeadOf = + BoundedStorageValue<>::MaxParaHeadSize, ParaHead>; /// Weight info of the given parachains pallet. pub type WeightInfoOf = >::WeightInfo; @@ -94,6 +98,12 @@ pub mod pallet { }, /// The caller has provided obsolete parachain head, which is already known to the pallet. RejectedObsoleteParachainHead { parachain: ParaId, parachain_head_hash: ParaHash }, + /// The caller has provided parachain head that exceeds the maximal configured head size. + RejectedLargeParachainHead { + parachain: ParaId, + parachain_head_hash: ParaHash, + parachain_head_size: u32, + }, /// Parachain head has been updated. UpdatedParachainHead { parachain: ParaId, parachain_head_hash: ParaHash }, } @@ -156,6 +166,17 @@ pub mod pallet { /// Incautious change of this constant may lead to orphan entries in the runtime storage. #[pallet::constant] type HeadsToKeep: Get; + + /// Maximal size (in bytes) of the SCALE-encoded parachain head. + /// + /// Keep in mind that the size of any tracked parachain header must not exceed this value. + /// So if you're going to track multiple parachains, one of which is storing large digests + /// in its headers, you shall choose this maximal value. + /// + /// There's no mandatory headers in this pallet, so it can't stall if there's some header + /// that exceeds this bound. + #[pallet::constant] + type MaxParaHeadSize: Get; } /// Optional pallet owner. @@ -196,7 +217,7 @@ pub mod pallet { ::Key1, ::Hasher2, ::Key2, - ::Value, + StoredParaHeadOf, >; /// A ring buffer of imported parachain head hashes. Ordered by the insertion time. @@ -206,7 +227,6 @@ pub mod pallet { #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] - #[pallet::without_storage_info] pub struct Pallet(PhantomData<(T, I)>); impl, I: 'static> OwnedBridgeModule for Pallet { @@ -381,12 +401,12 @@ pub mod pallet { /// Get best finalized header of the given parachain. pub fn best_parachain_head(parachain: ParaId) -> Option { let best_para_head_hash = ParasInfo::::get(parachain)?.best_head_hash.head_hash; - ImportedParaHeads::::get(parachain, best_para_head_hash) + ImportedParaHeads::::get(parachain, best_para_head_hash).map(|h| h.into_inner()) } /// Get parachain head with given hash. pub fn parachain_head(parachain: ParaId, hash: ParaHash) -> Option { - ImportedParaHeads::::get(parachain, hash) + ImportedParaHeads::::get(parachain, hash).map(|h| h.into_inner()) } /// Verify that the passed storage proof is valid, given it is crafted using @@ -478,12 +498,13 @@ pub mod pallet { ) -> Result { // check if head has been already updated at better relay chain block. Without this // check, we may import heads in random order + let err_log_prefix = "The parachain head can't be updated"; let is_valid = Self::validate_updated_parachain_head( parachain, &stored_best_head, updated_at_relay_block_number, updated_head_hash, - "The parachain head can't be updated", + err_log_prefix, ); if !is_valid { Self::deposit_event(Event::RejectedObsoleteParachainHead { @@ -492,6 +513,30 @@ pub mod pallet { }); return Err(()) } + + // verify that the parachain head size is <= `MaxParaHeadSize` + let updated_head = match StoredParaHeadOf::::try_from_inner(updated_head) { + Ok(updated_head) => updated_head, + Err(e) => { + log::trace!( + target: LOG_TARGET, + "{}. The parachain head size for {:?} is {}. It exceeds maximal configured size {}.", + err_log_prefix, + parachain, + e.value_size, + e.maximal_size, + ); + + Self::deposit_event(Event::RejectedLargeParachainHead { + parachain, + parachain_head_hash: updated_head_hash, + parachain_head_size: e.value_size as _, + }); + + return Err(()) + }, + }; + let next_imported_hash_position = stored_best_head .map_or(0, |stored_best_head| stored_best_head.next_imported_hash_position); @@ -575,8 +620,8 @@ pub mod pallet { mod tests { use super::*; use crate::mock::{ - run_test, test_relay_header, Event as TestEvent, Origin, TestRuntime, PARAS_PALLET_NAME, - UNTRACKED_PARACHAIN_ID, + run_test, test_relay_header, Event as TestEvent, Origin, TestRuntime, + MAXIMAL_PARACHAIN_HEAD_SIZE, PARAS_PALLET_NAME, UNTRACKED_PARACHAIN_ID, }; use codec::Encode; @@ -674,6 +719,12 @@ mod tests { ParaHead((parachain, head_number).encode()) } + fn large_head_data(parachain: u32, head_number: u32) -> ParaHead { + ParaHead( + (parachain, head_number, vec![42u8; MAXIMAL_PARACHAIN_HEAD_SIZE as usize]).encode(), + ) + } + fn head_hash(parachain: u32, head_number: u32) -> ParaHash { head_data(parachain, head_number).hash() } @@ -770,18 +821,21 @@ mod tests { ImportedParaHeads::::get( ParaId(1), initial_best_head(1).best_head_hash.head_hash - ), + ) + .map(|h| h.into_inner()), Some(head_data(1, 0)) ); assert_eq!( ImportedParaHeads::::get( ParaId(2), initial_best_head(2).best_head_hash.head_hash - ), + ) + .map(|h| h.into_inner()), None ); assert_eq!( - ImportedParaHeads::::get(ParaId(3), head_hash(3, 10)), + ImportedParaHeads::::get(ParaId(3), head_hash(3, 10)) + .map(|h| h.into_inner()), Some(head_data(3, 10)) ); @@ -830,11 +884,13 @@ mod tests { }) ); assert_eq!( - ImportedParaHeads::::get(ParaId(1), head_data(1, 5).hash()), + ImportedParaHeads::::get(ParaId(1), head_data(1, 5).hash()) + .map(|h| h.into_inner()), Some(head_data(1, 5)) ); assert_eq!( - ImportedParaHeads::::get(ParaId(1), head_data(1, 10).hash()), + ImportedParaHeads::::get(ParaId(1), head_data(1, 10).hash()) + .map(|h| h.into_inner()), None ); assert_eq!( @@ -863,11 +919,13 @@ mod tests { }) ); assert_eq!( - ImportedParaHeads::::get(ParaId(1), head_data(1, 5).hash()), + ImportedParaHeads::::get(ParaId(1), head_data(1, 5).hash()) + .map(|h| h.into_inner()), Some(head_data(1, 5)) ); assert_eq!( - ImportedParaHeads::::get(ParaId(1), head_data(1, 10).hash()), + ImportedParaHeads::::get(ParaId(1), head_data(1, 10).hash()) + .map(|h| h.into_inner()), Some(head_data(1, 10)) ); assert_eq!( @@ -1092,6 +1150,57 @@ mod tests { }); } + #[test] + fn does_nothing_when_parachain_head_is_too_large() { + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 5)), (2, large_head_data(1, 5))]); + run_test(|| { + // start with relay block #0 and try to import head#5 of parachain#1 and untracked + // parachain + initialize(state_root); + let result = Pallet::::submit_parachain_heads( + Origin::signed(1), + (0, test_relay_header(0, state_root).hash()), + parachains, + proof, + ); + assert_ok!(result); + assert_eq!( + ParasInfo::::get(ParaId(1)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 0, + head_hash: head_data(1, 5).hash() + }, + next_imported_hash_position: 1, + }) + ); + assert_eq!(ParasInfo::::get(ParaId(2)), None); + assert_eq!( + System::::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 5).hash(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::RejectedLargeParachainHead { + parachain: ParaId(2), + parachain_head_hash: large_head_data(1, 5).hash(), + parachain_head_size: large_head_data(1, 5).encoded_size() as u32, + }), + topics: vec![], + }, + ], + ); + }); + } + #[test] fn prunes_old_heads() { run_test(|| { diff --git a/bridges/modules/parachains/src/mock.rs b/bridges/modules/parachains/src/mock.rs index a31fb4c47f90e..f1d39592b20a4 100644 --- a/bridges/modules/parachains/src/mock.rs +++ b/bridges/modules/parachains/src/mock.rs @@ -36,6 +36,7 @@ type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type HeadsToKeep = HeadsToKeep; + type MaxParaHeadSize = frame_support::traits::ConstU32; } #[derive(Debug)] diff --git a/bridges/primitives/chain-rialto/src/lib.rs b/bridges/primitives/chain-rialto/src/lib.rs index 9698da8877946..257e94d0273c2 100644 --- a/bridges/primitives/chain-rialto/src/lib.rs +++ b/bridges/primitives/chain-rialto/src/lib.rs @@ -106,6 +106,9 @@ pub const MAX_AUTHORITIES_COUNT: u32 = 5; /// Maximal SCALE-encoded header size (in bytes) at Rialto. pub const MAX_HEADER_SIZE: u32 = 512; +/// Maximal SCALE-encoded size of parachains headers that are stored at Rialto `Paras` pallet. +pub const MAX_NESTED_PARACHAIN_HEAD_SIZE: u32 = MAX_HEADER_SIZE; + /// Re-export `time_units` to make usage easier. pub use time_units::*; diff --git a/bridges/primitives/chain-westend/src/lib.rs b/bridges/primitives/chain-westend/src/lib.rs index eeb30709accd7..2fd27094cb50f 100644 --- a/bridges/primitives/chain-westend/src/lib.rs +++ b/bridges/primitives/chain-westend/src/lib.rs @@ -70,6 +70,9 @@ pub const MAX_AUTHORITIES_COUNT: u32 = 100_000; /// some fixed reserve for other things (digest, block hash and number, ...) as well. pub const MAX_HEADER_SIZE: u32 = 4096 + MAX_AUTHORITIES_COUNT * 40; +/// Maximal SCALE-encoded size of parachains headers that are stored at Westend `Paras` pallet. +pub const MAX_NESTED_PARACHAIN_HEAD_SIZE: u32 = MAX_HEADER_SIZE; + /// Identifier of Westmint parachain at the Westend relay chain. pub const WESTMINT_PARACHAIN_ID: u32 = 2000; diff --git a/bridges/primitives/parachains/src/lib.rs b/bridges/primitives/parachains/src/lib.rs index 52b4954897308..f2edebf8a22a7 100644 --- a/bridges/primitives/parachains/src/lib.rs +++ b/bridges/primitives/parachains/src/lib.rs @@ -23,13 +23,13 @@ use bp_polkadot_core::{ BlockNumber as RelayBlockNumber, }; use bp_runtime::{StorageDoubleMapKeyProvider, StorageMapKeyProvider}; -use codec::{Decode, Encode}; +use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{Blake2_128Concat, RuntimeDebug, Twox64Concat}; use scale_info::TypeInfo; use sp_core::storage::StorageKey; /// Best known parachain head hash. -#[derive(Clone, Decode, Encode, PartialEq, RuntimeDebug, TypeInfo)] +#[derive(Clone, Decode, Encode, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] pub struct BestParaHeadHash { /// Number of relay block where this head has been read. /// @@ -45,7 +45,7 @@ pub struct BestParaHeadHash { } /// Best known parachain head as it is stored in the runtime storage. -#[derive(Decode, Encode, PartialEq, RuntimeDebug, TypeInfo)] +#[derive(Decode, Encode, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] pub struct ParaInfo { /// Best known parachain head hash. pub best_head_hash: BestParaHeadHash, diff --git a/bridges/primitives/runtime/src/lib.rs b/bridges/primitives/runtime/src/lib.rs index d775e09c47a00..94a231333ded7 100644 --- a/bridges/primitives/runtime/src/lib.rs +++ b/bridges/primitives/runtime/src/lib.rs @@ -40,6 +40,7 @@ pub use storage_proof::{ record_all_keys as record_all_trie_keys, Error as StorageProofError, ProofSize as StorageProofSize, StorageProofChecker, }; +pub use storage_types::BoundedStorageValue; #[cfg(feature = "std")] pub use storage_proof::craft_valid_storage_proof; @@ -48,6 +49,7 @@ pub mod messages; mod chain; mod storage_proof; +mod storage_types; // Re-export macro to aviod include paste dependency everywhere pub use sp_runtime::paste; diff --git a/bridges/primitives/runtime/src/storage_types.rs b/bridges/primitives/runtime/src/storage_types.rs new file mode 100644 index 0000000000000..b37f779d00b32 --- /dev/null +++ b/bridges/primitives/runtime/src/storage_types.rs @@ -0,0 +1,90 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Wrapper for a runtime storage value that checks if value exceeds given maximum +//! during conversion. + +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{traits::Get, RuntimeDebug}; +use scale_info::{Type, TypeInfo}; +use sp_std::{marker::PhantomData, ops::Deref}; + +/// Error that is returned when the value size exceeds maximal configured size. +#[derive(RuntimeDebug)] +pub struct MaximalSizeExceededError { + /// Size of the value. + pub value_size: usize, + /// Maximal configured size. + pub maximal_size: usize, +} + +/// A bounded runtime storage value. +#[derive(Clone, Decode, Encode, Eq, PartialEq)] +pub struct BoundedStorageValue { + value: V, + _phantom: PhantomData, +} + +impl sp_std::fmt::Debug for BoundedStorageValue { + fn fmt(&self, fmt: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + self.value.fmt(fmt) + } +} + +impl, V: Encode> BoundedStorageValue { + /// Construct `BoundedStorageValue` from the underlying `value` with all required checks. + /// + /// Returns error if value size exceeds given bounds. + pub fn try_from_inner(value: V) -> Result { + // this conversion is heavy (since we do encoding here), so we may want to optimize it later + // (e.g. by introducing custom Encode implementation, and turning `BoundedStorageValue` into + // `enum BoundedStorageValue { Decoded(V), Encoded(Vec) }`) + let value_size = value.encoded_size(); + let maximal_size = B::get() as usize; + if value_size > maximal_size { + Err(MaximalSizeExceededError { value_size, maximal_size }) + } else { + Ok(BoundedStorageValue { value, _phantom: Default::default() }) + } + } + + /// Convert into the inner type + pub fn into_inner(self) -> V { + self.value + } +} + +impl Deref for BoundedStorageValue { + type Target = V; + + fn deref(&self) -> &Self::Target { + &self.value + } +} + +impl TypeInfo for BoundedStorageValue { + type Identity = Self; + + fn type_info() -> Type { + V::type_info() + } +} + +impl, V: Encode> MaxEncodedLen for BoundedStorageValue { + fn max_encoded_len() -> usize { + B::get() as usize + } +}