Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Fix WrapperOpaque max encded len and type info #9881

Merged
6 commits merged into from
Sep 29, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 54 additions & 5 deletions frame/support/src/traits/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@

//! Smaller traits used in FRAME which don't need their own file.

use crate::{dispatch::Parameter, TypeInfo};
use crate::dispatch::Parameter;
use codec::{Decode, Encode, EncodeLike, Input, MaxEncodedLen};
use scale_info::{build::Fields, meta_type, Path, Type, TypeInfo, TypeParameter};
use sp_runtime::{traits::Block as BlockT, DispatchError};
use sp_std::vec::Vec;
use sp_std::prelude::*;

/// Anything that can have a `::len()` method.
pub trait Len {
Expand Down Expand Up @@ -384,16 +385,15 @@ impl<Call, Balance: From<u32>, const T: u32> EstimateCallFee<Call, Balance> for
///
/// The encoding is the encoding of `T` prepended with the compact encoding of its size in bytes.
/// Thus the encoded value can be decoded as a `Vec<u8>`.
#[derive(Debug, Eq, PartialEq, Default, Clone, MaxEncodedLen, TypeInfo)]
#[derive(Debug, Eq, PartialEq, Default, Clone)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub struct WrapperOpaque<T>(pub T);

impl<T: Encode> EncodeLike for WrapperOpaque<T> {}

impl<T: Encode> Encode for WrapperOpaque<T> {
fn size_hint(&self) -> usize {
// Compact<u32> usually takes at most 4 bytes
self.0.size_hint().saturating_add(4)
self.0.size_hint().saturating_add(<codec::Compact<u32>>::max_encoded_len())
}

fn encode_to<O: codec::Output + ?Sized>(&self, dest: &mut O) {
Expand Down Expand Up @@ -425,6 +425,37 @@ impl<T> From<T> for WrapperOpaque<T> {
}
}

impl<T: MaxEncodedLen> MaxEncodedLen for WrapperOpaque<T> {
fn max_encoded_len() -> usize {
let t_max_len = T::max_encoded_len();

// See scale encoding https://substrate.dev/docs/en/knowledgebase/advanced/codec
if t_max_len < 64 {
t_max_len + 1
} else if t_max_len < 2usize.pow(14) {
t_max_len + 2
} else if t_max_len < 2usize.pow(30) {
t_max_len + 4
} else {
<codec::Compact<u32>>::max_encoded_len().saturating_add(T::max_encoded_len())
}
}
}

impl<T: TypeInfo + 'static> TypeInfo for WrapperOpaque<T> {
type Identity = Self;
fn type_info() -> Type {
Type::builder()
.path(Path::new("WrapperOpaque", module_path!()))
.type_params(vec![TypeParameter::new("T", Some(meta_type::<T>()))])
.composite(
Fields::unnamed()
.field(|f| f.compact::<u32>().type_name("EncodedLengthOfT"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need type_name here. It is optional and is used for the name of the type of the field as it appears in the code, in order to identify alias usage. E.g. where the concrete type is u128 the type_name would be Balance for example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, I'll do another PR with this removed

.field(|f| f.ty::<T>().type_name("T")),
)
}
}

#[cfg(test)]
mod test {
use super::*;
Expand All @@ -438,5 +469,23 @@ mod test {
assert_eq!(decoded_from_vec_u8, 3u32);
let decoded = <WrapperOpaque<u32>>::decode(&mut &encoded[..]).unwrap();
assert_eq!(decoded.0, 3u32);

assert_eq!(<WrapperOpaque<[u8; 63]>>::max_encoded_len(), 63 + 1);
assert_eq!(
<WrapperOpaque<[u8; 63]>>::max_encoded_len(),
WrapperOpaque([0u8; 63]).encode().len()
);

assert_eq!(<WrapperOpaque<[u8; 64]>>::max_encoded_len(), 64 + 2);
assert_eq!(
<WrapperOpaque<[u8; 64]>>::max_encoded_len(),
WrapperOpaque([0u8; 64]).encode().len()
);

assert_eq!(
<WrapperOpaque<[u8; 2usize.pow(14) - 1]>>::max_encoded_len(),
2usize.pow(14) - 1 + 2
);
assert_eq!(<WrapperOpaque<[u8; 2usize.pow(14)]>>::max_encoded_len(), 2usize.pow(14) + 4);
}
}