pallet-multisig: Improve opaque call handling (#10060)

* pallet-multisig: Improve opaque call handling

Before the opaque call was just a type redefinition of `Vec<u8>`. With metadata v14 that was
breaking external tools, as they stopped looking at the type name. To improve the situation the
`WrapperKeepOpaque` type is introduced that communicates to the outside the correct type info.

* Cleanup

* Fix benchmarks

* FMT
This commit is contained in:
Bastian Köcher
2021-10-25 13:29:38 +02:00
committed by GitHub
parent ad308386d8
commit 6cfb0c7eb1
5 changed files with 241 additions and 63 deletions
+2 -1
View File
@@ -52,7 +52,8 @@ mod misc;
pub use misc::{
Backing, ConstU32, EnsureInherentsAreFirst, EstimateCallFee, ExecuteBlock, ExtrinsicCall, Get,
GetBacking, GetDefault, HandleLifetime, IsSubType, IsType, Len, OffchainWorker,
OnKilledAccount, OnNewAccount, SameOrOther, Time, TryDrop, UnixTime, WrapperOpaque,
OnKilledAccount, OnNewAccount, SameOrOther, Time, TryDrop, UnixTime, WrapperKeepOpaque,
WrapperOpaque,
};
mod stored_map;
+102 -1
View File
@@ -18,7 +18,7 @@
//! Smaller traits used in FRAME which don't need their own file.
use crate::dispatch::Parameter;
use codec::{Decode, Encode, EncodeLike, Input, MaxEncodedLen};
use codec::{CompactLen, Decode, DecodeAll, 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::prelude::*;
@@ -390,6 +390,7 @@ impl<Call, Balance: From<u32>, const T: u32> EstimateCallFee<Call, Balance> for
pub struct WrapperOpaque<T>(pub T);
impl<T: Encode> EncodeLike for WrapperOpaque<T> {}
impl<T: Encode> EncodeLike<WrapperKeepOpaque<T>> for WrapperOpaque<T> {}
impl<T: Encode> Encode for WrapperOpaque<T> {
fn size_hint(&self) -> usize {
@@ -456,6 +457,93 @@ impl<T: TypeInfo + 'static> TypeInfo for WrapperOpaque<T> {
}
}
/// A wrapper for any type `T` which implement encode/decode in a way compatible with `Vec<u8>`.
///
/// This type is similar to [`WrapperOpaque`], but it differs in the way it stores the type `T`.
/// While [`WrapperOpaque`] stores the decoded type, the [`WrapperKeepOpaque`] stores the type only
/// in its opaque format, aka as a `Vec<u8>`. To access the real type `T` [`Self::try_decode`] needs
/// to be used.
#[derive(Debug, Eq, PartialEq, Default, Clone)]
pub struct WrapperKeepOpaque<T> {
data: Vec<u8>,
_phantom: sp_std::marker::PhantomData<T>,
}
impl<T: Decode> WrapperKeepOpaque<T> {
/// Try to decode the wrapped type from the inner `data`.
///
/// Returns `None` if the decoding failed.
pub fn try_decode(&self) -> Option<T> {
T::decode_all(&mut &self.data[..]).ok()
}
/// Returns the length of the encoded `T`.
pub fn encoded_len(&self) -> usize {
self.data.len()
}
/// Returns the encoded data.
pub fn encoded(&self) -> &[u8] {
&self.data
}
/// Create from the given encoded `data`.
pub fn from_encoded(data: Vec<u8>) -> Self {
Self { data, _phantom: sp_std::marker::PhantomData }
}
}
impl<T: Encode> EncodeLike for WrapperKeepOpaque<T> {}
impl<T: Encode> EncodeLike<WrapperOpaque<T>> for WrapperKeepOpaque<T> {}
impl<T: Encode> Encode for WrapperKeepOpaque<T> {
fn size_hint(&self) -> usize {
self.data.len() + codec::Compact::<u32>::compact_len(&(self.data.len() as u32))
}
fn encode_to<O: codec::Output + ?Sized>(&self, dest: &mut O) {
self.data.encode_to(dest);
}
fn encode(&self) -> Vec<u8> {
self.data.encode()
}
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
self.data.using_encoded(f)
}
}
impl<T: Decode> Decode for WrapperKeepOpaque<T> {
fn decode<I: Input>(input: &mut I) -> Result<Self, codec::Error> {
Ok(Self { data: Vec::<u8>::decode(input)?, _phantom: sp_std::marker::PhantomData })
}
fn skip<I: Input>(input: &mut I) -> Result<(), codec::Error> {
<Vec<u8>>::skip(input)
}
}
impl<T: MaxEncodedLen> MaxEncodedLen for WrapperKeepOpaque<T> {
fn max_encoded_len() -> usize {
WrapperOpaque::<T>::max_encoded_len()
}
}
impl<T: TypeInfo + 'static> TypeInfo for WrapperKeepOpaque<T> {
type Identity = Self;
fn type_info() -> Type {
Type::builder()
.path(Path::new("WrapperKeepOpaque", module_path!()))
.type_params(vec![TypeParameter::new("T", Some(meta_type::<T>()))])
.composite(
Fields::unnamed()
.field(|f| f.compact::<u32>())
.field(|f| f.ty::<T>().type_name("T")),
)
}
}
#[cfg(test)]
mod test {
use super::*;
@@ -488,4 +576,17 @@ mod test {
);
assert_eq!(<WrapperOpaque<[u8; 2usize.pow(14)]>>::max_encoded_len(), 2usize.pow(14) + 4);
}
#[test]
fn test_keep_opaque_wrapper() {
let data = 3u32.encode().encode();
let keep_opaque = WrapperKeepOpaque::<u32>::decode(&mut &data[..]).unwrap();
keep_opaque.try_decode().unwrap();
let data = WrapperOpaque(50u32).encode();
let decoded = WrapperKeepOpaque::<u32>::decode(&mut &data[..]).unwrap();
let data = decoded.encode();
WrapperOpaque::<u32>::decode(&mut &data[..]).unwrap();
}
}