Unverified Commit 1e312d1e authored by Hero Bird's avatar Hero Bird Committed by GitHub

Make ink! lang use ink_core::storage2 module (#422)

* [abi] create new layout trait and structures

* [abi] remove unused import

* [abi] add missing license header

* [abi] fix clippy warning

* [primitives] add KeyPtr to ink_primitives

* [core] use KeyPtr through ink_primitives and add ExtKeyPtr trait

* [abi] use KeyPtr of ink_primitives

* [abi] make EnumLayout use StructLayout

* [abi] implement custom serde::Serialize for LayoutKey

* [abi] implement IntoCompact for layout2 module types

* [abi] add doc comment

* [abi] apply rustfmt

* [abi] add unit test for LayoutKey serialization

* [abi] rename ArrayLayout::array -> new

* [abi] add dispatch_key field to enum layout

* [abi] derive From for all Layout variants

* [abi] add unit tests for the new layout traits and data structures

* [abi] add experimental CellLayout

* [abi] make tests work again for layout2

* [abi] use specialized serializer function for LayoutKey

* [abi] refactor UnboundedLayout

* [abi] add unit test for UnboundedLayout

* [abi] apply rustfmt

* [core/derive] slightly refactor code

* [abi] simplify enum test

* [primitives] derive Debug, Copy and Clone for KeyPtr

* [abi] simplify CellLayout::new

* [abi] fix calling incorrect constructor for LayoutKey

* [abi] move layout2 module file into directory

* [core] add StorageLayout trait to storage2 module

* [abi_derive, core_derive] remove unnecessary extern crate proc_macro

* [core] imply ink-generate-abi feature by std feature

* [core] derive Metadata for env types for std feature

* [core] add Key, Hash, AccountId and String StorageLayout impls

* [core/derive] add initial structure for StorageLayout derive

* [core] add StorageLayout impls for some more prelude types

* [core] fix Box<T> impl for StorageLayout trait

* [core] implement StorageLayout for storage2::{Memory, Pack, LazyArray}

* [core] fix StorageLayout impl for LazyArray

* [core] implement StorageLayout for LazyIndexMap

* [abi] add HashLayout

* [abi] rewrite UnboundedLayout test to HashLayout test

* [abi] remove unbounded layout

* [abi, core] apply rustfmt

* [core] add initial skeleton for StorageLayout tests

* [core] add StorageLayout impls for Lazy, LazyCell and LazyHashMap

* [core] add StorageLayout impls for Option and Result

* [core] add LayoutCryptoHasher trait

* [core] fix StorageLayout impl for LazyHashMap

* [core] apply rustfmt

* [core] add StorageLayout impl for storage2::Vec

* [core] add StorageLayout for storage2::SmallVec

* [core] implement StorageLayout for storage2::Stash

* [core] implement StorageLayout for storage BitStash, Bitvec and HashMap

* [core] improve StorageLayout impl for storage2::Vec

* [core] apply rustfmt

* [abi] fix clippy warning

* [core] add StorageLayout impl for storage2::alloc::Box

* [core] add StorageLayout impl for DynamicAllocator

* [core/derive] impl StorageLayout derive for struct items

* [core/derive] make StorageLayout derive work for enum items

* [core/derive] fix expansion for nested enums

* [core/derive] fix a bug with nested enums

* [core/derive] add unit test for MixedEnum

* [core/derive] apply rustfmt

* [lang] add revision 2 module

* [core] remove generic T: EnvTypes param from Dispatch trait

* [lang] remove unnecessary import

* [lang] remove unnecessary generic parameter

* [lang/macro] generate codegen for the new v2 traits

* [core] re-export SpreadLayout and PackedLayout macros from within ink_core

* [lang] further adjustments to make ink_lang_macro work with storage2

* [lang, core] apply rustfmt

* [lang] fix clippy warning

* [lang] fix some bugs with constructors without inputs

* [examples] adjust the Flipper example contract for the changes

* [lang] remove codegen for testing ink! contracts

No longer needed.

* [lang] remove test-env crate feature

* [lang] minor improvements to EnvAccess utility type

* [examples] remove unused import for Flipper

* [examples] remove test-env crate feature for Flipper

* [lang] remove unused IdentType::span method

* [examples] Flipper: adjust contract for ink_lang changes

* [examples] adjust Erc20 for the ink_lang changes

* [examples] fix some indentation in Cargo.toml

* [examples] adjust DNS contract for ink_lang changes

* [examples] adjust ERC721 for ink_lang changes

There is still a bug that we need to fix in ink_core.

* [example] adjust lib.rs of ERC721 to ink_lang changes

* [examples] adjust Delegator example contract for ink_lang changes

* [lang_macro] generate SpreadLayout and PackedLayout impls for cross-calling wrappers

* [core] release storage2::Stash::entries() iterator for non-testing API

* [core] add SpreadLayout and PackedLayout impls for () type

* [examples] convert multisig contract example to new ink_lang

* [examples] fix incrementer Cargo.toml indentations

* [examples] adjust runtime-storage example contract for ink_lang changes

* [core] apply rustfmt

* [lang] generate correct attributes for messages

* [examples] make use of derive(Default) for DNS example

* [lang_macro] improve some ink! error messages

* [lang_macro] update all compile tests

* [examples] multisig: ignore doc test

This needs serious fixing later! @athei

* [lang] remove old ink_lang abstractions

* [lang] remove no longer needed ink_lang modules

* [lang_macro] add compile test for derive on storage struct

* [examples] ignore failing doc test

* [examples] actually ignore the failing doc-test for multi-sig

* [lang] move v2 module back to origin

* [lang_macro] adjust codegen for new dispatch module structure of ink_lang

* [lang_macro] no longer impl ink_core::{Flush, AllocateUsing} for ink! dependencies

* [lang_macro] use root namespaces in codegen everywhere

* [lang] apply rustfmt

* [lang_macro] use only root namespaces in codegen (forgot some)

* [lang] remove nightly Rust feature usage

* [core] re-export StorageLayout from ink_core::storage2::traits

* [lang,abi] adjust ink! metadata generation codegen to new layout

* [lang] use root namespaces in codegen

* [examples] apply rustfmt

* [lang] update cross-calling codegen for new storage layout

* [examples] update Delegator example contract

* [examples] apply rustfmt

* [abi] remove StorageLayout trait from ink_abi crate

* [examples] apply rustfmt

* [examples] fix clippy warning

* [examples] fix multisig_plain contract

* [examples] apply rustfmt

* [examples] fix clippy warning

* [examples] remove commented out code from multisig_plain example

* [lang] refactor codegen for contract storage struct

* [lang] fix unused import warning

* [lang] eliminate CrossCallingConflictCfg

* [lang] do not generate normal storage struct as dependency

* [lang] re-introduce cross calling conflict cfg codegen

* [lang/macro] refactor codegen for cross-calling

* [lang] add new traits for Event connectors

* [lang/macro] use connectors API for event codegen

* [lang/macro] apply rustfmt

* [lang/macro] remove out-of-ink-module export

* [abi] fix selector encoding

* [lang/macro] fix unused warning

* [examples] adjust example smart contracts for recents ink_lang_macro changes

* [abi] fix unit test for selector

* [chores] update README ink! example

* [alloc] silence warnings of unused unsafe for core::intrinsics::abort

* [examples] apply rustfmt

* [lang/macro] fix codegen for `cargo test`

* [*] replace ink-generate-abi crate feature everywhere with std

* [examples] fix Cargo.toml for generating metadata

* [examples] move flipper/src/lib.rs one up

* [examples] move lib.rs to root folder

* [examples] fix Cargo.toml of ERC721

* [examples] apply rustfmt to ERC20

* [examples] apply rustfmt to ERC721

* [examples] apply rustfmt to all examples

* [abi] remove unused serialize_selector function

* [examples] fix bug that DNS example still used type-metadata crate

* [example] fix bug in multisig_plain example
parent 5a7b7e5e
Pipeline #96624 passed with stages
in 8 minutes and 16 seconds
......@@ -94,32 +94,34 @@ mod flipper {
#[ink(storage)]
struct Flipper {
/// The single `bool` value.
value: storage::Value<bool>,
value: bool,
}
impl Flipper {
/// Instantiates a new Flipper contract and initializes `value` to `init_value`.
#[ink(constructor)]
fn new(&mut self, init_value: bool) {
self.value.set(init_value);
fn new(init_value: bool) -> Self {
Self {
value: init_value,
}
}
/// Instantiates a new Flipper contract and initializes `value` to `false` by default.
#[ink(constructor)]
fn default(&mut self) {
self.new(false)
fn default() -> Self {
Self::new(false)
}
/// Flips `value` from `true` to `false` or vice versa.
#[ink(message)]
fn flip(&mut self) {
*self.value = !self.get();
self.value = !self.value;
}
/// Returns the current state of `value`.
#[ink(message)]
fn get(&self) -> bool {
*self.value
self.value
}
}
......
......@@ -16,7 +16,6 @@
#[cfg(not(feature = "std"))]
extern crate alloc;
extern crate proc_macro;
#[macro_use]
mod error;
......
// Copyright 2018-2020 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.
#[cfg(test)]
mod tests;
use crate::utils::serialize_as_byte_str;
use derive_more::From;
use ink_prelude::collections::btree_map::BTreeMap;
use ink_primitives::Key;
use scale_info::{
form::{
CompactForm,
Form,
MetaForm,
},
IntoCompact,
Metadata,
Registry,
};
/// 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")]
pub enum Layout<F: Form = MetaForm> {
/// An encoded cell.
///
/// This is the only leaf node within the layout graph.
/// All layout nodes have this node type as their leafs.
///
/// This represents the encoding of a single cell mapped to a single key.
Cell(CellLayout<F>),
/// A layout that hashes values into the entire storage key space.
///
/// This is commonly used by ink! hashmaps and similar data structures.
Hash(HashLayout<F>),
/// An array of associated storage cells encoded with a given type.
///
/// This can also represent only a single cell.
Array(ArrayLayout<F>),
/// A struct layout with fields of different types.
Struct(StructLayout<F>),
/// An enum layout with a discriminant telling which variant is layed out.
Enum(EnumLayout<F>),
}
/// A pointer into some storage region.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)]
#[serde(transparent)]
pub struct LayoutKey {
#[serde(serialize_with = "serialize_as_byte_str")]
key: [u8; 32],
}
impl From<Key> for LayoutKey {
fn from(key: Key) -> Self {
let mut arr = [0x00; 32];
arr.copy_from_slice(key.as_bytes());
Self { key: arr }
}
}
/// An encoded cell.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, From, serde::Serialize)]
#[serde(bound = "F::TypeId: serde::Serialize")]
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,
}
impl CellLayout {
/// Creates a new cell layout.
pub fn new<T>(key: LayoutKey) -> Self
where
T: Metadata,
{
Self {
key,
ty: <T as Metadata>::meta_type(),
}
}
}
impl IntoCompact for CellLayout {
type Output = CellLayout<CompactForm>;
fn into_compact(self, registry: &mut Registry) -> Self::Output {
CellLayout {
key: self.key,
ty: registry.register_type(&self.ty),
}
}
}
impl IntoCompact for Layout {
type Output = Layout<CompactForm>;
fn into_compact(self, registry: &mut Registry) -> Self::Output {
match self {
Layout::Cell(encoded_cell) => {
Layout::Cell(encoded_cell.into_compact(registry))
}
Layout::Hash(hash_layout) => Layout::Hash(hash_layout.into_compact(registry)),
Layout::Array(array_layout) => {
Layout::Array(array_layout.into_compact(registry))
}
Layout::Struct(struct_layout) => {
Layout::Struct(struct_layout.into_compact(registry))
}
Layout::Enum(enum_layout) => Layout::Enum(enum_layout.into_compact(registry)),
}
}
}
/// 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")]
pub struct HashLayout<F: Form = MetaForm> {
/// The key offset used by the strategy.
offset: LayoutKey,
/// The hashing strategy to layout the underlying elements.
strategy: HashingStrategy,
/// The storage layout of the unbounded layout elements.
layout: Box<Layout<F>>,
}
impl HashLayout {
/// Creates a new unbounded layout.
pub fn new<K, L>(offset: K, strategy: HashingStrategy, layout: L) -> Self
where
K: Into<LayoutKey>,
L: Into<Layout>,
{
Self {
offset: offset.into(),
strategy,
layout: Box::new(layout.into()),
}
}
}
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)),
}
}
}
/// The unbounded hashing strategy.
///
/// 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)]
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")]
prefix: Vec<u8>,
/// An optional postfix to the computed hash.
#[serde(serialize_with = "serialize_as_byte_str")]
postfix: Vec<u8>,
}
impl HashingStrategy {
/// Creates a new unbounded hashing strategy.
pub fn new(hasher: CryptoHasher, prefix: Vec<u8>, postfix: Vec<u8>) -> Self {
Self {
hasher,
prefix,
postfix,
}
}
}
/// One of the supported crypto hashers.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)]
pub enum CryptoHasher {
/// The BLAKE-2 crypto hasher with an output of 256 bits.
Blake2x256,
/// The SHA-2 crypto hasher with an output of 256 bits.
Sha2x256,
/// The KECCAK crypto hasher with an output of 256 bits.
Keccak256,
}
/// 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")]
pub struct ArrayLayout<F: Form = MetaForm> {
/// The offset key of the array layout.
///
/// This is the same key as the 0-th element of the array layout.
offset: LayoutKey,
/// The number of elements in the array layout.
len: u32,
/// The number of cells each element in the array layout consists of.
cells_per_elem: u64,
/// The layout of the elements stored in the array layout.
layout: Box<Layout<F>>,
}
impl ArrayLayout {
/// Creates an array layout with the given length.
pub fn new<K, L>(at: K, len: u32, cells_per_elem: u64, layout: L) -> Self
where
K: Into<LayoutKey>,
L: Into<Layout>,
{
Self {
offset: at.into(),
len,
cells_per_elem,
layout: Box::new(layout.into()),
}
}
}
impl IntoCompact for ArrayLayout {
type Output = ArrayLayout<CompactForm>;
fn into_compact(self, registry: &mut Registry) -> Self::Output {
ArrayLayout {
offset: self.offset,
len: self.len,
cells_per_elem: self.cells_per_elem,
layout: Box::new(self.layout.into_compact(registry)),
}
}
}
/// A struct layout with consecutive fields of different layout.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)]
#[serde(bound = "F::TypeId: serde::Serialize")]
pub struct StructLayout<F: Form = MetaForm> {
/// The fields of the struct layout.
fields: Vec<FieldLayout<F>>,
}
impl StructLayout {
/// Creates a new struct layout.
pub fn new<F>(fields: F) -> Self
where
F: IntoIterator<Item = FieldLayout>,
{
Self {
fields: fields.into_iter().collect(),
}
}
}
impl IntoCompact for StructLayout {
type Output = StructLayout<CompactForm>;
fn into_compact(self, registry: &mut Registry) -> Self::Output {
StructLayout {
fields: self
.fields
.into_iter()
.map(|field| field.into_compact(registry))
.collect::<Vec<_>>(),
}
}
}
/// The layout for a particular field of a struct layout.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)]
#[serde(bound = "F::TypeId: serde::Serialize")]
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<F::String>,
/// The kind of the field.
///
/// This is either a direct layout bound
/// or another recursive layout sub-struct.
layout: Layout<F>,
}
impl FieldLayout {
/// Creates a new field layout.
pub fn new<N, L>(name: N, layout: L) -> Self
where
N: Into<Option<<MetaForm as Form>::String>>,
L: Into<Layout>,
{
Self {
name: name.into(),
layout: layout.into(),
}
}
}
impl IntoCompact for FieldLayout {
type Output = FieldLayout<CompactForm>;
fn into_compact(self, registry: &mut Registry) -> Self::Output {
FieldLayout {
name: self.name.map(|name| registry.register_string(name)),
layout: self.layout.into_compact(registry),
}
}
}
/// The discriminant of an enum variant.
#[derive(
Debug,
Copy,
Clone,
PartialEq,
Eq,
PartialOrd,
Ord,
serde::Serialize,
serde::Deserialize,
)]
pub struct Discriminant(usize);
impl From<usize> for Discriminant {
fn from(value: usize) -> Self {
Self(value)
}
}
/// An enum storage layout.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)]
#[serde(bound = "F::TypeId: serde::Serialize")]
pub struct EnumLayout<F: Form = MetaForm> {
/// The key where the discriminant is stored to dispatch the variants.
dispatch_key: LayoutKey,
/// The variants of the enum.
variants: BTreeMap<Discriminant, StructLayout<F>>,
}
impl EnumLayout {
/// Creates a new enum layout.
pub fn new<K, V>(dispatch_key: K, variants: V) -> Self
where
K: Into<LayoutKey>,
V: IntoIterator<Item = (Discriminant, StructLayout)>,
{
Self {
dispatch_key: dispatch_key.into(),
variants: variants.into_iter().collect(),
}
}
}
impl IntoCompact for EnumLayout {
type Output = EnumLayout<CompactForm>;
fn into_compact(self, registry: &mut Registry) -> Self::Output {
EnumLayout {
dispatch_key: self.dispatch_key,
variants: self
.variants
.into_iter()
.map(|(discriminant, layout)| {
(discriminant, layout.into_compact(registry))
})
.collect(),
}
}
}
// Copyright 2018-2020 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 ink_primitives::KeyPtr;
#[test]
fn layout_key_works() {
let layout_key = LayoutKey::from(Key([0x01; 32]));
let json = serde_json::to_string(&layout_key).unwrap();
assert_eq!(
json,
"\"0x0101010101010101010101010101010101010101010101010101010101010101\"",
);
}
fn named_fields_struct_layout(key_ptr: &mut KeyPtr) -> Layout {
StructLayout::new(vec![
FieldLayout::new(
"a",
CellLayout::new::<i32>(LayoutKey::from(key_ptr.advance_by(1))),
),
FieldLayout::new(
"b",
CellLayout::new::<i64>(LayoutKey::from(key_ptr.advance_by(1))),
),
])
.into()
}
#[test]
fn named_fields_work() {
let layout = named_fields_struct_layout(&mut KeyPtr::from(Key([0x00; 32])));
let mut registry = Registry::new();
let compacted = layout.into_compact(&mut registry);
let json = serde_json::to_value(&compacted).unwrap();
let expected = serde_json::json! {
{
"Struct": {
"fields": [
{
"layout": {
"Cell": {
"key": "0x\
0000000000000000\
0000000000000000\
0000000000000000\
0000000000000000",
"ty": 1,
}
},
"name": 1,
},
{
"layout": {
"Cell": {
"key": "0x\
0000000000000000\
0000000000000000\
0000000000000000\
0000000000000001",
"ty": 2,
}
},
"name": 2,
}
]
}
}
};
assert_eq!(json, expected);
}
fn tuple_struct_layout(key_ptr: &mut KeyPtr) -> Layout {
StructLayout::new(vec![
FieldLayout::new(
None,
CellLayout::new::<i32>(LayoutKey::from(key_ptr.advance_by(1))),
),
FieldLayout::new(
None,
CellLayout::new::<i64>(LayoutKey::from(key_ptr.advance_by(1))),
),
])
.into()
}
#[test]
fn tuple_struct_work() {
let layout = tuple_struct_layout(&mut KeyPtr::from(Key([0x00; 32])));
let mut registry = Registry::new();
let compacted = layout.into_compact(&mut registry);
let json = serde_json::to_value(&compacted).unwrap();
let expected = serde_json::json! {
{
"Struct": {
"fields": [
{
"layout": {
"Cell": {
"key": "0x\
0000000000000000\
0000000000000000\
0000000000000000\
0000000000000000",
"ty": 1,
}
},
"name": null,
},
{
"layout": {
"Cell": {
"key": "0x\
0000000000000000\
0000000000000000\
0000000000000000\
0000000000000001",
"ty": 2,
}
},
"name": null,
}
]
}
}
};
assert_eq!(json, expected);
}
fn clike_enum_layout(key_ptr: &mut KeyPtr) -> Layout {
EnumLayout::new(
key_ptr.advance_by(1),
vec![
(Discriminant(0), StructLayout::new(vec![])),
(Discriminant(1), StructLayout::new(vec![])),
(Discriminant(2), StructLayout::new(vec![])),
],
)
.into()
}
#[test]
fn clike_enum_work() {
let layout = clike_enum_layout(&mut KeyPtr::from(Key([0x00; 32])));
let mut registry = Registry::new();
let compacted = layout.into_compact(&mut registry);
let json = serde_json::to_value(&compacted).unwrap();
let expected = serde_json::json! {
{
"Enum": {
"dispatch_key": "0x\
0000000000000000\