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 }
# Sadly couldn't be marked as dev-dependency.
# Never use this crate outside of the off-chain environment!
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]
default = ["std"]
......
......@@ -137,10 +137,10 @@ impl Metadata<'_> {
let constr = match trait_ident {
Some(trait_ident) => {
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 => {
quote_spanned!(span => name(#ident_lit))
quote_spanned!(span => from_name(#ident_lit))
}
};
quote_spanned!(span =>
......@@ -231,10 +231,10 @@ impl Metadata<'_> {
let constr = match trait_ident {
Some(trait_ident) => {
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 => {
quote_spanned!(span => name(#ident_lit))
quote_spanned!(span => from_name(#ident_lit))
}
};
quote_spanned!(span =>
......
......@@ -30,7 +30,7 @@ ink_storage = { version = "3.0.0", path = "../../storage/" }
ink_lang = { version = "3.0.0", path = ".." }
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]
name = "ink_lang_macro"
......
......@@ -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 }
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"] }
scale-info = { version = "0.3", default-features = false, features = ["derive"] }
scale-info = { version = "0.4", default-features = false, features = ["derive"] }
[dev-dependencies]
pretty_assertions = "0.6.1"
......
......@@ -15,7 +15,10 @@
#[cfg(test)]
mod tests;
use crate::utils::serialize_as_byte_str;
use crate::{
serde_hex,
utils::serialize_as_byte_str,
};
use derive_more::From;
use ink_prelude::collections::btree_map::BTreeMap;
use ink_primitives::Key;
......@@ -30,10 +33,18 @@ use scale_info::{
Registry,
TypeInfo,
};
use serde::{
de::DeserializeOwned,
Deserialize,
Serialize,
};
/// Represents the static storage layout of an ink! smart contract.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, From, serde::Serialize)]
#[serde(bound = "F::TypeId: serde::Serialize")]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, From, Serialize, Deserialize)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
#[serde(rename_all = "camelCase")]
pub enum Layout<F: Form = MetaForm> {
/// An encoded cell.
......@@ -58,13 +69,31 @@ pub enum Layout<F: Form = MetaForm> {
}
/// A pointer into some storage region.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)]
#[serde(transparent)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, From)]
pub struct LayoutKey {
#[serde(serialize_with = "serialize_as_byte_str")]
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 {
fn from(key: &'a Key) -> Self {
Self {
......@@ -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.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, From, serde::Serialize)]
#[serde(bound = "F::TypeId: serde::Serialize")]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, From, Serialize, Deserialize)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct CellLayout<F: Form = MetaForm> {
/// The offset key into the storage.
key: LayoutKey,
/// The type of the encoded entity.
ty: <F as Form>::TypeId,
ty: <F as Form>::Type,
}
impl CellLayout {
......@@ -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.
///
/// Every hashing layout has an offset and a strategy to compute its keys.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)]
#[serde(bound = "F::TypeId: serde::Serialize")]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct HashLayout<F: Form = MetaForm> {
/// The key offset used by the strategy.
offset: LayoutKey,
......@@ -149,6 +206,18 @@ pub struct HashLayout<F: Form = MetaForm> {
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 {
/// Creates a new unbounded layout.
pub fn new<K, L>(offset: K, strategy: HashingStrategy, layout: L) -> Self
......@@ -164,15 +233,23 @@ impl HashLayout {
}
}
impl IntoCompact for HashLayout {
type Output = HashLayout<CompactForm>;
impl<F> HashLayout<F>
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 {
HashLayout {
offset: self.offset,
strategy: self.strategy,
layout: Box::new(self.layout.into_compact(registry)),
}
/// Returns the hashing strategy to layout the underlying elements.
pub fn strategy(&self) -> &HashingStrategy {
&self.strategy
}
/// 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 {
/// The offset key is used as another postfix for the computation.
/// So the actual formula is: `hasher(prefix + encoded(key) + offset + postfix)`
/// 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 {
/// One of the supported crypto hashers.
hasher: CryptoHasher,
/// 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>,
/// 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>,
}
......@@ -202,10 +285,25 @@ impl HashingStrategy {
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.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum CryptoHasher {
/// The BLAKE-2 crypto hasher with an output of 256 bits.
Blake2x256,
......@@ -216,8 +314,11 @@ pub enum CryptoHasher {
}
/// A layout for an array of associated cells with the same encoding.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)]
#[serde(bound = "F::TypeId: serde::Serialize")]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct ArrayLayout<F: Form = MetaForm> {
/// The offset key of the array layout.
///
......@@ -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 {
type Output = ArrayLayout<CompactForm>;
......@@ -261,8 +390,11 @@ impl IntoCompact for ArrayLayout {
}
/// A struct layout with consecutive fields of different layout.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)]
#[serde(bound = "F::TypeId: serde::Serialize")]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct StructLayout<F: Form = MetaForm> {
/// The fields of the struct layout.
fields: Vec<FieldLayout<F>>,
......@@ -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 {
type Output = StructLayout<CompactForm>;
......@@ -295,13 +437,16 @@ impl IntoCompact for StructLayout {
}
/// The layout for a particular field of a struct layout.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)]
#[serde(bound = "F::TypeId: serde::Serialize")]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct FieldLayout<F: Form = MetaForm> {
/// The name of the field.
///
/// 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.
///
/// This is either a direct layout bound
......@@ -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 {
type Output = FieldLayout<CompactForm>;
fn into_compact(self, registry: &mut Registry) -> Self::Output {
FieldLayout {
name: self.name,
name: self.name.map(|name| name.into_compact(registry)),
layout: self.layout.into_compact(registry),
}
}
}
/// The discriminant of an enum variant.
#[derive(
Debug,
Copy,
Clone,
PartialEq,
Eq,
PartialOrd,
Ord,
serde::Serialize,
serde::Deserialize,
)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Discriminant(usize);
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.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)]
#[serde(bound = "F::TypeId: serde::Serialize")]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct EnumLayout<F: Form = MetaForm> {
/// The key where the discriminant is stored to dispatch the variants.
dispatch_key: LayoutKey,
......@@ -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 {
type Output = EnumLayout<CompactForm>;
......
......@@ -39,22 +39,29 @@ pub use self::specs::{
MessageSpec,
MessageSpecBuilder,
ReturnTypeSpec,
Selector,
TypeSpec,
};
use impl_serde::serialize as serde_hex;
#[cfg(feature = "derive")]
use scale_info::{
form::CompactForm,
IntoCompact as _,
Registry,
RegistryReadOnly,
};
use serde::{
Deserialize,
Serialize,
};
use serde::Serialize;
/// An entire ink! project for metadata file generation purposes.
#[derive(Debug, Serialize)]
#[derive(Debug, Serialize, Deserialize)]
pub struct InkProject {
#[serde(flatten)]
registry: Registry,
registry: RegistryReadOnly,
#[serde(rename = "storage")]
/// The layout of the storage data structure
layout: layout::Layout<CompactForm>,
......@@ -72,7 +79,24 @@ impl InkProject {
Self {
layout: layout.into().into_compact(&mut registry),
spec: spec.into().into_compact(&mut registry),
registry,
registry: registry.into(),
}
}
}
impl InkProject {
/// Returns a read-only registry of types in the contract.
pub fn registry(&self) -> &RegistryReadOnly {
&self.registry
}
/// Returns the storage layout of the contract.
pub fn layout(&self) -> &layout::Layout<CompactForm> {
&self.layout
}
/// Returns the specification of the contract.
pub fn spec(&self) -> &ContractSpec<CompactForm> {
&self.spec
}
}
......@@ -14,7 +14,7 @@
#![allow(clippy::new_ret_no_self)]
use crate::utils::serialize_as_byte_str;
use crate::serde_hex;
#[cfg(not(feature = "std"))]
use alloc::{
format,
......@@ -33,11 +33,18 @@ use scale_info::{
Registry,
TypeInfo,
};
use serde::Serialize;
use serde::{
de::DeserializeOwned,
Deserialize,
Serialize,
};
/// Describes a contract.
#[derive(Debug, PartialEq, Eq, Serialize)]
#[serde(bound = "F::TypeId: Serialize")]
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct ContractSpec<F: Form = MetaForm> {
/// The set of constructors of the contract.
constructors: Vec<ConstructorSpec<F>>,
......@@ -46,7 +53,7 @@ pub struct ContractSpec<F: Form = MetaForm> {
/// The events of the contract.
events: Vec<EventSpec<F>>,
/// The contract documentation.
docs: Vec<&'static str>,
docs: Vec<F::String>,
}
impl IntoCompact for ContractSpec {
......@@ -69,11 +76,36 @@ impl IntoCompact for ContractSpec {
.into_iter()
.map(|event| event.into_compact(registry))
.collect::<Vec<_>>(),
docs: self.docs,
docs: registry.map_into_compact(self.docs),
}
}
}
impl<F> ContractSpec<F>
where
F: Form,
{
/// Returns the set of constructors of the contract.
pub fn constructors(&self) -> &[ConstructorSpec<F>] {
&self.constructors
}
/// Returns the external messages of the contract.
pub fn messages(&self) -> &[MessageSpec<F>] {
&self.messages
}
/// Returns the events of the contract.
pub fn events(&self) -> &[EventSpec<F>] {
&self.events
}
/// Returns the contract documentation.
pub fn docs(&self) -> &[F::String] {
&self.docs
}
}
/// The message builder is ready to finalize construction.
pub enum Valid {}
/// The message builder is not ready to finalize construction.
......@@ -182,20 +214,22 @@ impl ContractSpec {
}
/// Describes a constructor of a contract.