Unverified Commit d7a47f4a authored by Andrew Jones's avatar Andrew Jones Committed by GitHub
Browse files

Deserialize metadata (#488)

* Make public fields for InkProject and specs

* Make layout fields public

* Use scale-info from branch

* Add Deserialize derives, use ReadOnlyRegistry

* Deserialize contract metadata specs

* Deserialize layout2 specs

* WIP decode hex

* Use impl_serde crate for hex byte string serde

* Test for selector deserialization

* Fix up custom deserialization

* Export selector and add to_vec impl

* Fmt

* Fix up spec segments IntoCompact

* Make name public

* Update to latest scale-info master

* Replace pub fields with getters

* Replace pub fields with getters in spec

* Fix search replace error with any::TypeId

* Use scale-info 0.4

* Fmt

* Remove commented out getter

* Fix up other scale-info deps

* Clippy

* Fix up message and constructor spec builder method names

* Serialize empty bytes vec without 0x prefix
parent 9f24b473
Pipeline #109829 passed with stages
in 6 minutes and 26 seconds
...@@ -38,7 +38,8 @@ blake2 = { version = "0.9", optional = true } ...@@ -38,7 +38,8 @@ blake2 = { version = "0.9", optional = true }
# Sadly couldn't be marked as dev-dependency. # Sadly couldn't be marked as dev-dependency.
# Never use this crate outside of the off-chain environment! # Never use this crate outside of the off-chain environment!
rand = { version = "0.7", default-features = false, features = ["alloc"], optional = true } rand = { version = "0.7", default-features = false, features = ["alloc"], optional = true }
scale-info = { version = "0.3", default-features = false, features = ["derive"], optional = true } scale-info = { version = "0.4", default-features = false, features = ["derive"], optional = true }
[features] [features]
default = ["std"] default = ["std"]
......
...@@ -137,10 +137,10 @@ impl Metadata<'_> { ...@@ -137,10 +137,10 @@ impl Metadata<'_> {
let constr = match trait_ident { let constr = match trait_ident {
Some(trait_ident) => { Some(trait_ident) => {
let trait_ident_lit = trait_ident.to_string(); let trait_ident_lit = trait_ident.to_string();
quote_spanned!(span => trait_and_name(#trait_ident_lit, #ident_lit)) quote_spanned!(span => from_trait_and_name(#trait_ident_lit, #ident_lit))
} }
None => { None => {
quote_spanned!(span => name(#ident_lit)) quote_spanned!(span => from_name(#ident_lit))
} }
}; };
quote_spanned!(span => quote_spanned!(span =>
...@@ -231,10 +231,10 @@ impl Metadata<'_> { ...@@ -231,10 +231,10 @@ impl Metadata<'_> {
let constr = match trait_ident { let constr = match trait_ident {
Some(trait_ident) => { Some(trait_ident) => {
let trait_ident_lit = trait_ident.to_string(); let trait_ident_lit = trait_ident.to_string();
quote_spanned!(span => trait_and_name(#trait_ident_lit, #ident_lit)) quote_spanned!(span => from_trait_and_name(#trait_ident_lit, #ident_lit))
} }
None => { None => {
quote_spanned!(span => name(#ident_lit)) quote_spanned!(span => from_name(#ident_lit))
} }
}; };
quote_spanned!(span => quote_spanned!(span =>
......
...@@ -30,7 +30,7 @@ ink_storage = { version = "3.0.0", path = "../../storage/" } ...@@ -30,7 +30,7 @@ ink_storage = { version = "3.0.0", path = "../../storage/" }
ink_lang = { version = "3.0.0", path = ".." } ink_lang = { version = "3.0.0", path = ".." }
trybuild = "1.0.24" trybuild = "1.0.24"
scale-info = { version = "0.3", default-features = false, features = ["derive"] } scale-info = { version = "0.4", default-features = false, features = ["derive"] }
[lib] [lib]
name = "ink_lang_macro" name = "ink_lang_macro"
......
...@@ -19,8 +19,9 @@ ink_prelude = { version = "3.0.0", path = "../prelude/", default-features = fals ...@@ -19,8 +19,9 @@ ink_prelude = { version = "3.0.0", path = "../prelude/", default-features = fals
ink_primitives = { version = "3.0.0", path = "../primitives/", default-features = false } ink_primitives = { version = "3.0.0", path = "../primitives/", default-features = false }
serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] } serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] }
impl-serde = "0.3.1"
derive_more = { version = "0.99", default-features = false, features = ["from"] } derive_more = { version = "0.99", default-features = false, features = ["from"] }
scale-info = { version = "0.3", default-features = false, features = ["derive"] } scale-info = { version = "0.4", default-features = false, features = ["derive"] }
[dev-dependencies] [dev-dependencies]
pretty_assertions = "0.6.1" pretty_assertions = "0.6.1"
......
...@@ -15,7 +15,10 @@ ...@@ -15,7 +15,10 @@
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
use crate::utils::serialize_as_byte_str; use crate::{
serde_hex,
utils::serialize_as_byte_str,
};
use derive_more::From; use derive_more::From;
use ink_prelude::collections::btree_map::BTreeMap; use ink_prelude::collections::btree_map::BTreeMap;
use ink_primitives::Key; use ink_primitives::Key;
...@@ -30,10 +33,18 @@ use scale_info::{ ...@@ -30,10 +33,18 @@ use scale_info::{
Registry, Registry,
TypeInfo, TypeInfo,
}; };
use serde::{
de::DeserializeOwned,
Deserialize,
Serialize,
};
/// Represents the static storage layout of an ink! smart contract. /// Represents the static storage layout of an ink! smart contract.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, From, serde::Serialize)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, From, Serialize, Deserialize)]
#[serde(bound = "F::TypeId: serde::Serialize")] #[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub enum Layout<F: Form = MetaForm> { pub enum Layout<F: Form = MetaForm> {
/// An encoded cell. /// An encoded cell.
...@@ -58,13 +69,31 @@ pub enum Layout<F: Form = MetaForm> { ...@@ -58,13 +69,31 @@ pub enum Layout<F: Form = MetaForm> {
} }
/// A pointer into some storage region. /// A pointer into some storage region.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, From)]
#[serde(transparent)]
pub struct LayoutKey { pub struct LayoutKey {
#[serde(serialize_with = "serialize_as_byte_str")]
key: [u8; 32], key: [u8; 32],
} }
impl serde::Serialize for LayoutKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serde_hex::serialize(&self.key, serializer)
}
}
impl<'de> serde::Deserialize<'de> for LayoutKey {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let mut arr = [0; 32];
serde_hex::deserialize_check_len(d, serde_hex::ExpectedLen::Exact(&mut arr[..]))?;
Ok(arr.into())
}
}
impl<'a> From<&'a Key> for LayoutKey { impl<'a> From<&'a Key> for LayoutKey {
fn from(key: &'a Key) -> Self { fn from(key: &'a Key) -> Self {
Self { Self {
...@@ -81,14 +110,24 @@ impl From<Key> for LayoutKey { ...@@ -81,14 +110,24 @@ impl From<Key> for LayoutKey {
} }
} }
impl LayoutKey {
/// Returns the underlying bytes of the layout key.
pub fn to_bytes(&self) -> &[u8] {
&self.key
}
}
/// An encoded cell. /// An encoded cell.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, From, serde::Serialize)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, From, Serialize, Deserialize)]
#[serde(bound = "F::TypeId: serde::Serialize")] #[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct CellLayout<F: Form = MetaForm> { pub struct CellLayout<F: Form = MetaForm> {
/// The offset key into the storage. /// The offset key into the storage.
key: LayoutKey, key: LayoutKey,
/// The type of the encoded entity. /// The type of the encoded entity.
ty: <F as Form>::TypeId, ty: <F as Form>::Type,
} }
impl CellLayout { impl CellLayout {
...@@ -135,11 +174,29 @@ impl IntoCompact for Layout { ...@@ -135,11 +174,29 @@ impl IntoCompact for Layout {
} }
} }
impl<F> CellLayout<F>
where
F: Form,
{
/// Returns the offset key into the storage.
pub fn key(&self) -> &LayoutKey {
&self.key
}
/// Returns the type of the encoded entity.
pub fn ty(&self) -> &F::Type {
&self.ty
}
}
/// A hashing layout potentially hitting all cells of the storage. /// A hashing layout potentially hitting all cells of the storage.
/// ///
/// Every hashing layout has an offset and a strategy to compute its keys. /// Every hashing layout has an offset and a strategy to compute its keys.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(bound = "F::TypeId: serde::Serialize")] #[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct HashLayout<F: Form = MetaForm> { pub struct HashLayout<F: Form = MetaForm> {
/// The key offset used by the strategy. /// The key offset used by the strategy.
offset: LayoutKey, offset: LayoutKey,
...@@ -149,6 +206,18 @@ pub struct HashLayout<F: Form = MetaForm> { ...@@ -149,6 +206,18 @@ pub struct HashLayout<F: Form = MetaForm> {
layout: Box<Layout<F>>, layout: Box<Layout<F>>,
} }
impl IntoCompact for HashLayout {
type Output = HashLayout<CompactForm>;
fn into_compact(self, registry: &mut Registry) -> Self::Output {
HashLayout {
offset: self.offset,
strategy: self.strategy,
layout: Box::new(self.layout.into_compact(registry)),
}
}
}
impl HashLayout { impl HashLayout {
/// Creates a new unbounded layout. /// Creates a new unbounded layout.
pub fn new<K, L>(offset: K, strategy: HashingStrategy, layout: L) -> Self pub fn new<K, L>(offset: K, strategy: HashingStrategy, layout: L) -> Self
...@@ -164,15 +233,23 @@ impl HashLayout { ...@@ -164,15 +233,23 @@ impl HashLayout {
} }
} }
impl IntoCompact for HashLayout { impl<F> HashLayout<F>
type Output = HashLayout<CompactForm>; where
F: Form,
{
/// Returns the key offset used by the strategy.
pub fn offset(&self) -> &LayoutKey {
&self.offset
}
fn into_compact(self, registry: &mut Registry) -> Self::Output { /// Returns the hashing strategy to layout the underlying elements.
HashLayout { pub fn strategy(&self) -> &HashingStrategy {
offset: self.offset, &self.strategy
strategy: self.strategy, }
layout: Box::new(self.layout.into_compact(registry)),
} /// Returns the storage layout of the unbounded layout elements.
pub fn layout(&self) -> &Layout<F> {
&self.layout
} }
} }
...@@ -181,15 +258,21 @@ impl IntoCompact for HashLayout { ...@@ -181,15 +258,21 @@ impl IntoCompact for HashLayout {
/// The offset key is used as another postfix for the computation. /// The offset key is used as another postfix for the computation.
/// So the actual formula is: `hasher(prefix + encoded(key) + offset + postfix)` /// So the actual formula is: `hasher(prefix + encoded(key) + offset + postfix)`
/// Where `+` in this contexts means append of the byte slices. /// Where `+` in this contexts means append of the byte slices.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct HashingStrategy { pub struct HashingStrategy {
/// One of the supported crypto hashers. /// One of the supported crypto hashers.
hasher: CryptoHasher, hasher: CryptoHasher,
/// An optional prefix to the computed hash. /// An optional prefix to the computed hash.
#[serde(serialize_with = "serialize_as_byte_str")] #[serde(
serialize_with = "serialize_as_byte_str",
deserialize_with = "serde_hex::deserialize"
)]
prefix: Vec<u8>, prefix: Vec<u8>,
/// An optional postfix to the computed hash. /// An optional postfix to the computed hash.
#[serde(serialize_with = "serialize_as_byte_str")] #[serde(
serialize_with = "serialize_as_byte_str",
deserialize_with = "serde_hex::deserialize"
)]
postfix: Vec<u8>, postfix: Vec<u8>,
} }
...@@ -202,10 +285,25 @@ impl HashingStrategy { ...@@ -202,10 +285,25 @@ impl HashingStrategy {
postfix, postfix,
} }
} }
/// Returns the supported crypto hasher.
pub fn hasher(&self) -> &CryptoHasher {
&self.hasher
}
/// Returns the optional prefix to the computed hash.
pub fn prefix(&self) -> &[u8] {
&self.prefix
}
/// Returns the optional postfix to the computed hash.
pub fn postfix(&self) -> &[u8] {
&self.postfix
}
} }
/// One of the supported crypto hashers. /// One of the supported crypto hashers.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum CryptoHasher { pub enum CryptoHasher {
/// The BLAKE-2 crypto hasher with an output of 256 bits. /// The BLAKE-2 crypto hasher with an output of 256 bits.
Blake2x256, Blake2x256,
...@@ -216,8 +314,11 @@ pub enum CryptoHasher { ...@@ -216,8 +314,11 @@ pub enum CryptoHasher {
} }
/// A layout for an array of associated cells with the same encoding. /// A layout for an array of associated cells with the same encoding.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(bound = "F::TypeId: serde::Serialize")] #[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct ArrayLayout<F: Form = MetaForm> { pub struct ArrayLayout<F: Form = MetaForm> {
/// The offset key of the array layout. /// The offset key of the array layout.
/// ///
...@@ -247,6 +348,34 @@ impl ArrayLayout { ...@@ -247,6 +348,34 @@ impl ArrayLayout {
} }
} }
#[allow(clippy::len_without_is_empty)]
impl<F> ArrayLayout<F>
where
F: Form,
{
/// Returns the offset key of the array layout.
///
/// This is the same key as the 0-th element of the array layout.
pub fn offset(&self) -> &LayoutKey {
&self.offset
}
/// Returns the number of elements in the array layout.
pub fn len(&self) -> u32 {
self.len
}
/// Returns he number of cells each element in the array layout consists of.
pub fn cells_per_elem(&self) -> u64 {
self.cells_per_elem
}
/// Returns the layout of the elements stored in the array layout.
pub fn layout(&self) -> &Layout<F> {
&self.layout
}
}
impl IntoCompact for ArrayLayout { impl IntoCompact for ArrayLayout {
type Output = ArrayLayout<CompactForm>; type Output = ArrayLayout<CompactForm>;
...@@ -261,8 +390,11 @@ impl IntoCompact for ArrayLayout { ...@@ -261,8 +390,11 @@ impl IntoCompact for ArrayLayout {
} }
/// A struct layout with consecutive fields of different layout. /// A struct layout with consecutive fields of different layout.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(bound = "F::TypeId: serde::Serialize")] #[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct StructLayout<F: Form = MetaForm> { pub struct StructLayout<F: Form = MetaForm> {
/// The fields of the struct layout. /// The fields of the struct layout.
fields: Vec<FieldLayout<F>>, fields: Vec<FieldLayout<F>>,
...@@ -280,6 +412,16 @@ impl StructLayout { ...@@ -280,6 +412,16 @@ impl StructLayout {
} }
} }
impl<F> StructLayout<F>
where
F: Form,
{
/// Returns the fields of the struct layout.
pub fn fields(&self) -> &[FieldLayout<F>] {
&self.fields
}
}
impl IntoCompact for StructLayout { impl IntoCompact for StructLayout {
type Output = StructLayout<CompactForm>; type Output = StructLayout<CompactForm>;
...@@ -295,13 +437,16 @@ impl IntoCompact for StructLayout { ...@@ -295,13 +437,16 @@ impl IntoCompact for StructLayout {
} }
/// The layout for a particular field of a struct layout. /// The layout for a particular field of a struct layout.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(bound = "F::TypeId: serde::Serialize")] #[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct FieldLayout<F: Form = MetaForm> { pub struct FieldLayout<F: Form = MetaForm> {
/// The name of the field. /// The name of the field.
/// ///
/// Can be missing, e.g. in case of an enum tuple struct variant. /// Can be missing, e.g. in case of an enum tuple struct variant.
name: Option<&'static str>, name: Option<F::String>,
/// The kind of the field. /// The kind of the field.
/// ///
/// This is either a direct layout bound /// This is either a direct layout bound
...@@ -323,29 +468,39 @@ impl FieldLayout { ...@@ -323,29 +468,39 @@ impl FieldLayout {
} }
} }
impl<F> FieldLayout<F>
where
F: Form,
{
/// Returns the name of the field.
///
/// Can be missing, e.g. in case of an enum tuple struct variant.
pub fn name(&self) -> Option<&F::String> {
self.name.as_ref()
}
/// Returns the kind of the field.
///
/// This is either a direct layout bound
/// or another recursive layout sub-struct.
pub fn layout(&self) -> &Layout<F> {
&self.layout
}
}
impl IntoCompact for FieldLayout { impl IntoCompact for FieldLayout {
type Output = FieldLayout<CompactForm>; type Output = FieldLayout<CompactForm>;
fn into_compact(self, registry: &mut Registry) -> Self::Output { fn into_compact(self, registry: &mut Registry) -> Self::Output {
FieldLayout { FieldLayout {
name: self.name, name: self.name.map(|name| name.into_compact(registry)),
layout: self.layout.into_compact(registry), layout: self.layout.into_compact(registry),
} }
} }
} }
/// The discriminant of an enum variant. /// The discriminant of an enum variant.
#[derive( #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
Debug,
Copy,
Clone,
PartialEq,
Eq,
PartialOrd,
Ord,
serde::Serialize,
serde::Deserialize,
)]
pub struct Discriminant(usize); pub struct Discriminant(usize);
impl From<usize> for Discriminant { impl From<usize> for Discriminant {
...@@ -354,9 +509,19 @@ impl From<usize> for Discriminant { ...@@ -354,9 +509,19 @@ impl From<usize> for Discriminant {
} }
} }
impl Discriminant {
/// Returns the value of the discriminant
pub fn value(&self) -> usize {
self.0
}
}
/// An enum storage layout. /// An enum storage layout.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(bound = "F::TypeId: serde::Serialize")] #[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct EnumLayout<F: Form = MetaForm> { pub struct EnumLayout<F: Form = MetaForm> {
/// The key where the discriminant is stored to dispatch the variants. /// The key where the discriminant is stored to dispatch the variants.
dispatch_key: LayoutKey, dispatch_key: LayoutKey,
...@@ -378,6 +543,21 @@ impl EnumLayout { ...@@ -378,6 +543,21 @@ impl EnumLayout {
} }
} }
impl<F> EnumLayout<F>
where
F: Form,
{
/// Returns the key where the discriminant is stored to dispatch the variants.
pub fn dispatch_key(&self) -> &LayoutKey {
&self.dispatch_key
}
/// Returns the variants of the enum.
pub fn variants(&self) -> &BTreeMap<Discriminant, StructLayout<F>> {
&self.variants
}
}
impl IntoCompact for EnumLayout { impl IntoCompact for EnumLayout {
type Output = EnumLayout<CompactForm>; type Output = EnumLayout<CompactForm>;
......