Unverified Commit 5a7b7e5e authored by Andrew Jones's avatar Andrew Jones Committed by GitHub

New metadata format (#296)

* Move spec tests to separate file

* Add contract spec serialization test

* Camel case field names

* Encode selector as hex string

* Move specs module back to top level

* Move layout test to tests.rs

* Layout externally tagged enums & camelCase

* Format code

* Rename TypeSpec::ty -> TypeSpec::id

* TEMPORARY: Use my type-metadata branch

* Derive TypeInfo for Key

* Switch from type_metadata to scale_info

* Convert layout to using scale-info

* Fix scale-info optional dependencies

* Fmt

* Fix up straggling type-metadata dependency

* scake-info master

* Use scale-info from crates.io

* oops

* Fix compilation errors

* Fmt

* Fix examples manifests

* Remove redundant into()
parent d0e29735
Pipeline #96617 passed with stages
in 8 minutes and 49 seconds
......@@ -253,7 +253,7 @@ publish-docs:
- git fetch origin gh-pages
# Generating Docs
- time cargo doc --no-deps --all-features
-p type-metadata -p ink_abi -p ink_abi_derive -p ink_core -p ink_core_derive
-p scale-info -p ink_abi -p ink_abi_derive -p ink_core -p ink_core_derive
-p ink_primitives -p ink_prelude -p ink_lang -p ink_lang_macro
# saving README and docs
- mv target/doc/ /tmp/
......
......@@ -21,15 +21,11 @@ ink_primitives = { version = "2.1.0", path = "../primitives/", default-features
serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] }
derive_more = { version = "0.99", default-features = false, features = ["from"] }
[dependencies.type-metadata]
git = "https://github.com/type-metadata/type-metadata.git"
rev = "02eae9f35c40c943b56af5b60616219f2b72b47d"
default-features = false
features = ["derive"]
scale-info = { version = "0.1", default-features = false, features = ["derive"] }
[dev-dependencies]
serde_json = "1.0"
assert-json-diff = "1.0.1"
[features]
default = [
......@@ -40,7 +36,7 @@ std = [
"ink_abi_derive/std",
"ink_prelude/std",
"serde/std",
"type-metadata/std",
"scale-info/std",
]
derive = [
"ink_abi_derive"
......
......@@ -84,7 +84,7 @@ fn generate_fields_layout<'a>(
fn generate_struct_fields_layout(fields: &Punctuated<Field, Token![,]>) -> TokenStream2 {
let fields_layout = generate_fields_layout(fields);
quote! {
use type_metadata::Metadata as _;
use scale_info::Metadata as _;
_ink_abi::LayoutStruct::new(Self::meta_type(), __core::vec![
#( #fields_layout, )*
])
......@@ -97,7 +97,7 @@ fn generate_struct_layout(data_struct: &DataStruct) -> TokenStream2 {
Fields::Unnamed(ref fs) => generate_struct_fields_layout(&fs.unnamed),
Fields::Unit => {
quote! {
_ink_abi::LayoutStruct::new(<Self as type_metadata::Metadata>::meta_type(), Vec::new())
_ink_abi::LayoutStruct::new(<Self as scale_info::Metadata>::meta_type(), Vec::new())
}
}
}
......
......@@ -22,7 +22,7 @@ pub fn wrap(impl_quote: TokenStream2) -> TokenStream2 {
#[allow(unknown_lints)]
#[cfg_attr(feature = "cargo-clippy", allow(useless_attribute))]
#[allow(rust_2018_idioms)]
use type_metadata as _type_metadata;
use scale_info as _scale_info;
use ink_abi as _ink_abi;
#[cfg(not(feature = "std"))]
......
......@@ -17,14 +17,9 @@ use alloc::{
string::String,
vec::Vec,
};
use core::fmt::Write;
use derive_more::From;
use serde::{
Serialize,
Serializer,
};
use type_metadata::{
use scale_info::{
form::{
CompactForm,
Form,
......@@ -33,6 +28,10 @@ use type_metadata::{
IntoCompact,
Registry,
};
use serde::{
Serialize,
Serializer,
};
/// Implemented by types that have a storage layout.
///
......@@ -49,15 +48,14 @@ impl From<ink_primitives::Key> for LayoutKey {
impl HasLayout for ink_primitives::Key {
fn layout(&self) -> StorageLayout {
LayoutRange::cell(*self, <[u8; 32] as type_metadata::Metadata>::meta_type())
.into()
LayoutRange::cell(*self, <[u8; 32] as scale_info::Metadata>::meta_type()).into()
}
}
/// Either a concrete layout bound or another layout sub-struct.
#[derive(Debug, PartialEq, Eq, Serialize, From)]
#[serde(bound = "F::TypeId: Serialize")]
#[serde(untagged)]
#[serde(rename_all = "lowercase")]
pub enum StorageLayout<F: Form = MetaForm> {
/// A concrete layout bound.
Range(LayoutRange<F>),
......@@ -94,10 +92,9 @@ pub struct LayoutKey(
#[derive(Debug, PartialEq, Eq, Serialize)]
#[serde(bound = "F::TypeId: Serialize")]
pub struct LayoutStruct<F: Form = MetaForm> {
#[serde(rename = "struct.type")]
#[serde(rename = "type")]
self_ty: F::TypeId,
/// The sub-fields of the struct.
#[serde(rename = "struct.fields")]
fields: Vec<LayoutField<F>>,
}
......@@ -176,13 +173,12 @@ impl IntoCompact for LayoutField {
#[serde(bound = "F::TypeId: Serialize")]
pub struct LayoutRange<F: Form = MetaForm> {
/// The single key for cells or the starting key address for chunks.
#[serde(rename = "range.offset", serialize_with = "serialize_key")]
#[serde(serialize_with = "serialize_key")]
offset: LayoutKey,
/// The amount of associated key addresses starting from the offset key.
#[serde(rename = "range.len")]
len: u32,
/// The element type stored under the associated keys.
#[serde(rename = "range.elem_type")]
#[serde(rename = "elemType")]
elem_ty: F::TypeId,
}
......@@ -228,47 +224,5 @@ fn serialize_key<S>(key: &LayoutKey, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let bytes = key.0;
let mut hex = String::with_capacity(bytes.len() * 2 + 2);
write!(hex, "0x").expect("failed writing to string");
for byte in &bytes {
write!(hex, "{:02x}", byte).expect("failed writing to string");
}
serializer.serialize_str(&hex)
}
#[cfg(test)]
mod tests {
use super::*;
use type_metadata::{
form::{
Form,
MetaForm,
},
IntoCompact,
Registry,
};
#[test]
fn key_must_serialize_to_hex() {
// given
let type_id = <MetaForm as Form>::TypeId::new::<u32>();
let offset = LayoutKey([1; 32]);
let cs: LayoutRange<MetaForm> = LayoutRange {
offset,
len: 1337,
elem_ty: type_id,
};
let mut registry = Registry::new();
// when
let json = serde_json::to_string(&cs.into_compact(&mut registry)).unwrap();
// then
assert_eq!(
json,
"{\"range.offset\":\"0x0101010101010101010101010101010101010101010101010101010101010101\",\"range.len\":1337,\"range.elem_type\":1}"
);
}
super::hex_encode(&key.0[..], serializer)
}
......@@ -17,6 +17,9 @@
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(test)]
mod tests;
mod layout;
mod specs;
......@@ -51,12 +54,16 @@ pub use self::{
},
};
use serde::Serialize;
use type_metadata::{
use core::fmt::Write as _;
use scale_info::{
form::CompactForm,
IntoCompact as _,
Registry,
};
use serde::{
Serialize,
Serializer,
};
/// An entire ink! project for ABI file generation purposes.
#[derive(Debug, Serialize)]
......@@ -83,3 +90,15 @@ impl InkProject {
}
}
}
fn hex_encode<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut hex = String::with_capacity(bytes.len() * 2 + 2);
write!(hex, "0x").expect("failed writing to string");
for byte in bytes {
write!(hex, "{:02x}", byte).expect("failed writing to string");
}
serializer.serialize_str(&hex)
}
......@@ -22,11 +22,7 @@ use alloc::{
};
use core::marker::PhantomData;
use serde::{
Serialize,
Serializer,
};
use type_metadata::{
use scale_info::{
form::{
CompactForm,
Form,
......@@ -36,6 +32,10 @@ use type_metadata::{
Metadata,
Registry,
};
use serde::{
Serialize,
Serializer,
};
/// Describes a contract.
#[derive(Debug, PartialEq, Eq, Serialize)]
......@@ -295,6 +295,7 @@ impl ConstructorSpecBuilder<state::Selector> {
/// Describes a contract message.
#[derive(Debug, PartialEq, Eq, Serialize)]
#[serde(bound = "F::TypeId: Serialize")]
#[serde(rename_all = "camelCase")]
pub struct MessageSpec<F: Form = MetaForm> {
/// The name of the message.
name: F::String,
......@@ -550,7 +551,7 @@ impl EventSpec {
/// default setup. Even though it would be useful for third party tools
/// such as the Polkadot UI to know that we are handling with `Balance`
/// types, we currently cannot communicate this without display names.
pub type DisplayName<F> = type_metadata::Namespace<F>;
pub type DisplayName<F> = scale_info::Path<F>;
/// A type specification.
///
......@@ -571,9 +572,10 @@ pub type DisplayName<F> = type_metadata::Namespace<F>;
/// simply be a type alias to `fn(i32, i32) -> Ordering`.
#[derive(Debug, PartialEq, Eq, Serialize)]
#[serde(bound = "F::TypeId: Serialize")]
#[serde(rename_all = "camelCase")]
pub struct TypeSpec<F: Form = MetaForm> {
/// The actual type.
ty: F::TypeId,
id: F::TypeId,
/// The compile-time known displayed representation of the type.
display_name: DisplayName<F>,
}
......@@ -583,7 +585,7 @@ impl IntoCompact for TypeSpec {
fn into_compact(self, registry: &mut Registry) -> Self::Output {
TypeSpec {
ty: registry.register_type(&self.ty),
id: registry.register_type(&self.id),
display_name: self.display_name.into_compact(registry),
}
}
......@@ -626,8 +628,9 @@ impl TypeSpec {
S: IntoIterator<Item = <MetaForm as Form>::String>,
{
Self {
ty: T::meta_type(),
display_name: DisplayName::new(segments).expect("display name is invalid"),
id: T::meta_type(),
display_name: DisplayName::from_segments(segments)
.expect("display name is invalid"),
}
}
......@@ -637,8 +640,8 @@ impl TypeSpec {
T: Metadata,
{
Self {
ty: T::meta_type(),
display_name: DisplayName::prelude(),
id: T::meta_type(),
display_name: DisplayName::default(),
}
}
}
......@@ -830,36 +833,5 @@ fn serialize_selector<S>(s: &[u8; 4], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let hex = format!(
r#"["0x{:02X}","0x{:02X}","0x{:02X}","0x{:02X}"]"#,
s[0], s[1], s[2], s[3]
);
serializer.serialize_str(&hex)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn construct_selector_must_serialize_to_hex() {
// given
let name = "foo";
let cs: ConstructorSpec<MetaForm> = ConstructorSpec {
name,
selector: 123_456_789u32.to_be_bytes(),
args: Vec::new(),
docs: Vec::new(),
};
let mut registry = Registry::new();
// when
let json = serde_json::to_string(&cs.into_compact(&mut registry)).unwrap();
// then
assert_eq!(
json,
r#"{"name":1,"selector":"[\"0x07\",\"0x5B\",\"0xCD\",\"0x15\"]","args":[],"docs":[]}"#
);
}
super::hex_encode(&s[..], serializer)
}
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::*;
use assert_json_diff::assert_json_eq;
use scale_info::{
form::{
Form,
MetaForm,
},
IntoCompact,
Registry,
};
use serde_json::json;
#[test]
fn spec_constructor_selector_must_serialize_to_hex() {
// given
let name = "foo";
let cs = ConstructorSpec::new(name)
.selector(123_456_789u32.to_be_bytes())
.done();
let mut registry = Registry::new();
// when
let json = serde_json::to_value(&cs.into_compact(&mut registry)).unwrap();
// then
assert_json_eq!(
json,
json!({
"name": 1,
"selector": "0x075bcd15",
"args": [],
"docs": []
})
);
}
#[test]
fn layout_range_json() {
// given
let type_id = <MetaForm as Form>::TypeId::new::<u32>();
let offset = LayoutKey([1; 32]);
let layout = StorageLayout::Range(LayoutRange::cell(offset, type_id));
let mut registry = Registry::new();
// when
let json = serde_json::to_value(&layout.into_compact(&mut registry)).unwrap();
// then
assert_json_eq!(
json,
json!({
"range": {
"offset": "0x0101010101010101010101010101010101010101010101010101010101010101",
"len": 1,
"elemType": 1
}
})
);
}
#[test]
fn layout_struct_json() {
// given
let type_id = <MetaForm as Form>::TypeId::new::<u32>();
let layout = StorageLayout::Struct(LayoutStruct::new(type_id, Vec::new()));
let mut registry = Registry::new();
// when
let json = serde_json::to_value(&layout.into_compact(&mut registry)).unwrap();
// then
assert_json_eq!(
json,
json!({
"struct": {
"type": 1,
"fields": [],
}
})
);
}
#[test]
fn spec_contract_json() {
// given
let contract: ContractSpec = ContractSpec::new("incrementer")
.constructors(vec![
ConstructorSpec::new("new")
.selector([94u8, 189u8, 136u8, 214u8])
.args(vec![MessageParamSpec::new("init_value")
.of_type(TypeSpec::with_name_segs::<i32, _>(
vec!["i32"].into_iter().map(AsRef::as_ref),
))
.done()])
.docs(Vec::new())
.done(),
ConstructorSpec::new("default")
.selector([2u8, 34u8, 255u8, 24u8])
.args(Vec::new())
.docs(Vec::new())
.done(),
])
.messages(vec![
MessageSpec::new("inc")
.selector([231u8, 208u8, 89u8, 15u8])
.mutates(true)
.args(vec![MessageParamSpec::new("by")
.of_type(TypeSpec::with_name_segs::<i32, _>(
vec!["i32"].into_iter().map(AsRef::as_ref),
))
.done()])
.docs(Vec::new())
.returns(ReturnTypeSpec::new(None))
.done(),
MessageSpec::new("get")
.selector([37u8, 68u8, 74u8, 254u8])
.mutates(false)
.args(Vec::new())
.docs(Vec::new())
.returns(ReturnTypeSpec::new(TypeSpec::with_name_segs::<i32, _>(
vec!["i32"].into_iter().map(AsRef::as_ref),
)))
.done(),
])
.events(Vec::new())
.docs(Vec::new())
.done();
let mut registry = Registry::new();
// when
let json = serde_json::to_value(&contract.into_compact(&mut registry)).unwrap();
// then
assert_json_eq!(
json,
json!({
"constructors": [
{
"args": [
{
"name": 3,
"type": {
"displayName": [
4
],
"id": 1
}
}
],
"docs": [],
"name": 2,
"selector": "0x5ebd88d6"
},
{
"args": [],
"docs": [],
"name": 5,
"selector": "0x0222ff18"
}
],
"docs": [],
"events": [],
"messages": [
{
"args": [
{
"name": 7,
"type": {
"displayName": [
4
],
"id": 1
}
}
],
"docs": [],
"mutates": true,
"name": 6,
"returnType": null,
"selector": "0xe7d0590f"
},
{
"args": [],
"docs": [],
"mutates": false,
"name": 8,
"returnType": {
"displayName": [
4
],
"id": 1
},
"selector": "0x25444afe"
}
],
"name": 1
})
)
}
......@@ -39,13 +39,7 @@ 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 }
[dependencies.type-metadata]
git = "https://github.com/type-metadata/type-metadata.git"
rev = "02eae9f35c40c943b56af5b60616219f2b72b47d"
default-features = false
features = ["derive"]
optional = true
scale-info = { version = "0.1", default-features = false, features = ["derive"], optional = true }
[dev-dependencies]
itertools = "0.9"
......@@ -58,7 +52,7 @@ std = [
"ink_prelude/std",
"ink_primitives/std",
"scale/std",
"type-metadata/std",
"scale-info/std",
"rand",
"rand/std",
"num-traits/std",
......@@ -69,6 +63,6 @@ std = [
]
ink-generate-abi = [
"ink_abi",
"type-metadata",
"scale-info",
"std",
]
......@@ -32,7 +32,7 @@ use scale::{
Encode,
};
#[cfg(feature = "ink-generate-abi")]
use type_metadata::Metadata;
use scale_info::Metadata;
use core::ops::{
Add,
......
......@@ -13,7 +13,7 @@
// limitations under the License.
#[cfg(feature = "ink-generate-abi")]
use type_metadata::Metadata;
use scale_info::Metadata;
use super::*;
use ink_primitives::Key;
......
......@@ -31,7 +31,7 @@ use ink_abi::{
};
use ink_primitives::Key;
#[cfg(feature = "ink-generate-abi")]
use type_metadata::Metadata;
use scale_info::Metadata;
/// Allocator for dynamic contract storage.
///
......
......@@ -31,13 +31,12 @@ use ink_abi::{
use ink_prelude::boxed::Box;
use ink_primitives::Key;
#[cfg(feature = "ink-generate-abi")]
use type_metadata::{
HasTypeDef,
use scale_info::{
build::Fields,
Metadata,
NamedField,
TypeDef,
TypeDefStruct,
TypeId,
Path,
Type,
TypeInfo,
};