mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-17 14:51:07 +00:00
Rename Palette to FRAME (#4182)
* palette -> frame * PALETTE, Palette -> FRAME * Move folder pallete -> frame * Update docs/Structure.adoc Co-Authored-By: Benjamin Kampmann <ben.kampmann@googlemail.com> * Update docs/README.adoc Co-Authored-By: Benjamin Kampmann <ben.kampmann@googlemail.com> * Update README.adoc
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
[package]
|
||||
name = "frame-support"
|
||||
version = "2.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
serde = { version = "1.0.101", optional = true, features = ["derive"] }
|
||||
codec = { package = "parity-scale-codec", version = "1.0.6", default-features = false, features = ["derive"] }
|
||||
frame-metadata = { path = "../metadata", default-features = false }
|
||||
rstd = { package = "sr-std", path = "../../primitives/sr-std", default-features = false }
|
||||
runtime-io ={ package = "sr-io", path = "../../primitives/sr-io", default-features = false }
|
||||
sr-primitives = { path = "../../primitives/sr-primitives", default-features = false }
|
||||
primitives = { package = "substrate-primitives", path = "../../primitives/core", default-features = false }
|
||||
sr-arithmetic = { path = "../../primitives/sr-arithmetic", default-features = false }
|
||||
inherents = { package = "substrate-inherents", path = "../../primitives/inherents", default-features = false }
|
||||
frame-support-procedural = { package = "frame-support-procedural", path = "./procedural" }
|
||||
paste = "0.1.6"
|
||||
once_cell = { version = "0.2.4", default-features = false, optional = true }
|
||||
state-machine = { package = "substrate-state-machine", path = "../../primitives/state-machine", optional = true }
|
||||
bitmask = { version = "0.5.0", default-features = false }
|
||||
impl-trait-for-tuples = "0.1.3"
|
||||
tracing = { version = "0.1.10", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "0.6.1"
|
||||
frame-system = { path = "../system" }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"tracing",
|
||||
"once_cell",
|
||||
"bitmask/std",
|
||||
"serde",
|
||||
"runtime-io/std",
|
||||
"codec/std",
|
||||
"rstd/std",
|
||||
"sr-primitives/std",
|
||||
"sr-arithmetic/std",
|
||||
"frame-metadata/std",
|
||||
"inherents/std",
|
||||
"state-machine",
|
||||
]
|
||||
nightly = []
|
||||
strict = []
|
||||
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "frame-support-procedural"
|
||||
version = "2.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
frame-support-procedural-tools = { package = "frame-support-procedural-tools", path = "./tools" }
|
||||
|
||||
proc-macro2 = "1.0.6"
|
||||
quote = "1.0.2"
|
||||
syn = { version = "1.0.7", features = ["full"] }
|
||||
@@ -0,0 +1,221 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// tag::description[]
|
||||
//! Proc macro of Support code for the runtime.
|
||||
// end::description[]
|
||||
|
||||
#![recursion_limit="512"]
|
||||
|
||||
extern crate proc_macro;
|
||||
|
||||
mod storage;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
/// Declares strongly-typed wrappers around codec-compatible types in storage.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```nocompile
|
||||
/// decl_storage! {
|
||||
/// trait Store for Module<T: Trait> as Example {
|
||||
/// Foo get(fn foo) config(): u32=12;
|
||||
/// Bar: map u32 => u32;
|
||||
/// pub Zed build(|config| vec![(0, 0)]): linked_map u32 => u32;
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Declaration is set with the header `(pub) trait Store for Module<T: Trait> as Example`,
|
||||
/// with `Store` a (pub) trait generated associating each storage item to the `Module` and
|
||||
/// `as Example` setting the prefix used for storage items of this module. `Example` must be unique:
|
||||
/// another module with the same name and the same inner storage item name will conflict.
|
||||
/// `Example` is called the module prefix.
|
||||
///
|
||||
/// note: For instantiable modules the module prefix is prepended with instance
|
||||
/// prefix. Instance prefix is "" for default instance and "Instance$n" for instance number $n.
|
||||
/// Thus, instance 3 of module Example has a module prefix of `Instance3Example`
|
||||
///
|
||||
/// Basic storage consists of a name and a type; supported types are:
|
||||
///
|
||||
/// * Value: `Foo: type`: Implements the
|
||||
/// [`StorageValue`](../frame_support/storage/trait.StorageValue.html) trait using the
|
||||
/// [`StorageValue generator`](../frame_support/storage/generator/trait.StorageValue.html).
|
||||
/// The generator `unhashed_key` is `$module_prefix ++ " " ++ $storage_name`
|
||||
///
|
||||
/// * Map: `Foo: map hasher($hash) type => type`: Implements the
|
||||
/// [`StorageMap`](../frame_support/storage/trait.StorageMap.html) trait using the
|
||||
/// [`StorageMap generator`](../frame_support/storage/generator/trait.StorageMap.html).
|
||||
///
|
||||
/// `$hash` representing a choice of hashing algorithms available in the
|
||||
/// [`Hashable`](../frame_support/trait.Hashable.html) trait.
|
||||
///
|
||||
/// `hasher($hash)` is optional and its default is `blake2_256`. One should use another hasher
|
||||
/// with care, see generator documentation.
|
||||
///
|
||||
/// The generator is implemented with:
|
||||
/// * `prefix`: `$module_prefix ++ " " ++ $storage_name`
|
||||
/// * `Hasher`: $hash
|
||||
///
|
||||
/// * Linked map: `Foo: linked_map hasher($hash) type => type`: Implements the
|
||||
/// [`StorageLinkedMap`](../frame_support/storage/trait.StorageLinkedMap.html) trait using the
|
||||
/// [`StorageLinkedMap generator`](../frame_support/storage/generator/trait.StorageLinkedMap.html).
|
||||
///
|
||||
/// `$hash` representing a choice of hashing algorithms available in the
|
||||
/// [`Hashable`](../frame_support/trait.Hashable.html) trait.
|
||||
///
|
||||
/// `hasher($hash)` is optional and its default is `blake2_256`. One should use another hasher
|
||||
/// with care, see generator documentation.
|
||||
///
|
||||
/// The generator is implemented with:
|
||||
/// * `prefix`: `$module_prefix ++ " " ++ $storage_name`
|
||||
/// * `head_key`: `"head of " ++ $module_prefix ++ " " ++ $storage_name`
|
||||
/// * `Hasher`: $hash
|
||||
///
|
||||
/// All key formatting logic can be accessed in a type-agnostic format via the
|
||||
/// [`KeyFormat`](../srml_support/storage/generator/trait.KeyFormat.html) trait, which
|
||||
/// is implemented for the storage linked map type as well.
|
||||
///
|
||||
/// * Double map: `Foo: double_map hasher($hash1) u32, $hash2(u32) => u32`: Implements the
|
||||
/// [`StorageDoubleMap`](../frame_support/storage/trait.StorageDoubleMap.html) trait using the
|
||||
/// [`StorageDoubleMap generator`](../frame_support/storage/generator/trait.StorageDoubleMap.html).
|
||||
///
|
||||
/// `$hash1` and `$hash2` representing choices of hashing algorithms available in the
|
||||
/// [`Hashable`](../frame_support/trait.Hashable.html) trait. They must be choosen with care, see
|
||||
/// generator documentation.
|
||||
///
|
||||
/// `hasher($hash)` is optional and its default is `blake2_256`.
|
||||
///
|
||||
/// `hasher($hash)` is optional and its default is `blake2_256`. One should use another hasher
|
||||
/// with care, see generator documentation.
|
||||
///
|
||||
/// If the first key is untrusted, a cryptographic `hasher` such as `blake2_256` must be used.
|
||||
/// Otherwise, other values of all storage items can be compromised.
|
||||
///
|
||||
/// If the second key is untrusted, a cryptographic `hasher` such as `blake2_256` must be used.
|
||||
/// Otherwise, other items in storage with the same first key can be compromised.
|
||||
///
|
||||
/// The generator is implemented with:
|
||||
/// * `key1_prefix`: `$module_prefix ++ " " ++ $storage_name`
|
||||
/// * `Hasher1`: $hash1
|
||||
/// * `Hasher2`: $hash2
|
||||
///
|
||||
/// Supported hashers (ordered from least to best security):
|
||||
///
|
||||
/// * `twox_64_concat` - TwoX with 64bit + key concatenated.
|
||||
/// * `twox_128` - TwoX with 128bit.
|
||||
/// * `twox_256` - TwoX with with 256bit.
|
||||
/// * `blake2_128` - Blake2 with 128bit.
|
||||
/// * `blake2_256` - Blake2 with 256bit.
|
||||
///
|
||||
/// Basic storage can be extended as such:
|
||||
///
|
||||
/// `#vis #name get(fn #getter) config(#field_name) build(#closure): #type = #default;`
|
||||
///
|
||||
/// * `#vis`: Set the visibility of the structure. `pub` or nothing.
|
||||
/// * `#name`: Name of the storage item, used as a prefix in storage.
|
||||
/// * [optional] `get(fn #getter)`: Implements the function #getter to `Module`.
|
||||
/// * [optional] `config(#field_name)`: `field_name` is optional if get is set.
|
||||
/// Will include the item in `GenesisConfig`.
|
||||
/// * [optional] `build(#closure)`: Closure called with storage overlays.
|
||||
/// * `#type`: Storage type.
|
||||
/// * [optional] `#default`: Value returned when none.
|
||||
///
|
||||
/// Storage items are accessible in multiple ways:
|
||||
///
|
||||
/// * The structure: `Foo` or `Foo::<T>` depending if the value type is generic or not.
|
||||
/// * The `Store` trait structure: `<Module<T> as Store>::Foo`
|
||||
/// * The getter on the module that calls get on the structure: `Module::<T>::foo()`
|
||||
///
|
||||
/// ## GenesisConfig
|
||||
///
|
||||
/// An optional `GenesisConfig` struct for storage initialization can be defined, either
|
||||
/// when at least one storage field requires default initialization
|
||||
/// (both `get` and `config` or `build`), or specifically as in:
|
||||
///
|
||||
/// ```nocompile
|
||||
/// decl_storage! {
|
||||
/// trait Store for Module<T: Trait> as Example {
|
||||
///
|
||||
/// // Your storage items
|
||||
/// }
|
||||
/// add_extra_genesis {
|
||||
/// config(genesis_field): GenesisFieldType;
|
||||
/// config(genesis_field2): GenesisFieldType;
|
||||
/// ...
|
||||
/// build(|_: &Self| {
|
||||
/// // Modification of storage
|
||||
/// })
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This struct can be exposed as `ExampleConfig` by the `construct_runtime!` macro like follows:
|
||||
///
|
||||
/// ```nocompile
|
||||
/// construct_runtime!(
|
||||
/// pub enum Runtume with ... {
|
||||
/// ...,
|
||||
/// Example: example::{Module, Storage, ..., Config<T>},
|
||||
/// ...,
|
||||
/// }
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// ### Module with Instances
|
||||
///
|
||||
/// The `decl_storage!` macro supports building modules with instances with the following syntax
|
||||
/// (`DefaultInstance` type is optional):
|
||||
///
|
||||
/// ```nocompile
|
||||
/// trait Store for Module<T: Trait<I>, I: Instance=DefaultInstance> as Example {}
|
||||
/// ```
|
||||
///
|
||||
/// Accessing the structure no requires the instance as generic parameter:
|
||||
/// * `Foo::<I>` if the value type is not generic
|
||||
/// * `Foo::<T, I>` if the value type is generic
|
||||
///
|
||||
/// ## Where clause
|
||||
///
|
||||
/// This macro supports a where clause which will be replicated to all generated types.
|
||||
///
|
||||
/// ```nocompile
|
||||
/// trait Store for Module<T: Trait> as Example where T::AccountId: std::fmt::Display {}
|
||||
/// ```
|
||||
///
|
||||
/// ## Limitations
|
||||
///
|
||||
/// # Instancing and generic `GenesisConfig`
|
||||
///
|
||||
/// If your module supports instancing and you see an error like `parameter `I` is never used` for
|
||||
/// your `decl_storage!`, you are hitting a limitation of the current implementation. You probably
|
||||
/// try to use an associated type of a non-instantiable trait. To solve this, add the following to
|
||||
/// your macro call:
|
||||
///
|
||||
/// ```nocompile
|
||||
/// add_extra_genesis {
|
||||
/// config(phantom): std::marker::PhantomData<I>,
|
||||
/// }
|
||||
/// ...
|
||||
///
|
||||
/// This adds a field to your `GenesisConfig` with the name `phantom` that you can initialize with
|
||||
/// `Default::default()`.
|
||||
///
|
||||
#[proc_macro]
|
||||
pub fn decl_storage(input: TokenStream) -> TokenStream {
|
||||
storage::decl_storage_impl(input)
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Builder logic definition used to build genesis storage.
|
||||
|
||||
use frame_support_procedural_tools::syn_ext as ext;
|
||||
use proc_macro2::TokenStream;
|
||||
use syn::spanned::Spanned;
|
||||
use quote::{quote, quote_spanned};
|
||||
use super::super::{DeclStorageDefExt, StorageLineTypeDef};
|
||||
|
||||
/// Definition of builder blocks, each block insert some value in the storage.
|
||||
/// They must be called inside externalities, and with `self` being the genesis config.
|
||||
pub struct BuilderDef {
|
||||
/// Contains:
|
||||
/// * build block for storage with build attribute.
|
||||
/// * build block for storage with config attribute and no build attribute.
|
||||
/// * build block for extra genesis build expression.
|
||||
pub blocks: Vec<TokenStream>,
|
||||
/// The build blocks requires generic traits.
|
||||
pub is_generic: bool,
|
||||
}
|
||||
|
||||
impl BuilderDef {
|
||||
pub fn from_def(scrate: &TokenStream, def: &DeclStorageDefExt) -> Self {
|
||||
let mut blocks = Vec::new();
|
||||
let mut is_generic = false;
|
||||
|
||||
for line in def.storage_lines.iter() {
|
||||
let storage_struct = &line.storage_struct;
|
||||
let storage_trait = &line.storage_trait;
|
||||
let value_type = &line.value_type;
|
||||
|
||||
// Contains the data to inset at genesis either from build or config.
|
||||
let mut data = None;
|
||||
|
||||
if let Some(builder) = &line.build {
|
||||
is_generic |= ext::expr_contains_ident(&builder, &def.module_runtime_generic);
|
||||
is_generic |= line.is_generic;
|
||||
|
||||
data = Some(quote_spanned!(builder.span() => &(#builder)(self)));
|
||||
} else if let Some(config) = &line.config {
|
||||
is_generic |= line.is_generic;
|
||||
|
||||
data = Some(quote!(&self.#config;));
|
||||
};
|
||||
|
||||
if let Some(data) = data {
|
||||
blocks.push(match &line.storage_type {
|
||||
StorageLineTypeDef::Simple(_) => {
|
||||
quote!{{
|
||||
let v: &#value_type = #data;
|
||||
<#storage_struct as #scrate::#storage_trait>::put::<&#value_type>(v);
|
||||
}}
|
||||
},
|
||||
StorageLineTypeDef::Map(map) | StorageLineTypeDef::LinkedMap(map) => {
|
||||
let key = &map.key;
|
||||
quote!{{
|
||||
let data: &#scrate::rstd::vec::Vec<(#key, #value_type)> = #data;
|
||||
data.iter().for_each(|(k, v)| {
|
||||
<#storage_struct as #scrate::#storage_trait>::insert::<
|
||||
&#key, &#value_type
|
||||
>(k, v);
|
||||
});
|
||||
}}
|
||||
},
|
||||
StorageLineTypeDef::DoubleMap(map) => {
|
||||
let key1 = &map.key1;
|
||||
let key2 = &map.key2;
|
||||
quote!{{
|
||||
let data: &#scrate::rstd::vec::Vec<(#key1, #key2, #value_type)> = #data;
|
||||
data.iter().for_each(|(k1, k2, v)| {
|
||||
<#storage_struct as #scrate::#storage_trait>::insert::<
|
||||
&#key1, &#key2, &#value_type
|
||||
>(k1, k2, v);
|
||||
});
|
||||
}}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(builder) = def.extra_genesis_build.as_ref() {
|
||||
is_generic |= ext::expr_contains_ident(&builder, &def.module_runtime_generic);
|
||||
|
||||
blocks.push(quote_spanned! { builder.span() =>
|
||||
let extra_genesis_builder: fn(&Self) = #builder;
|
||||
extra_genesis_builder(self);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Self {
|
||||
blocks,
|
||||
is_generic,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Genesis config defintion.
|
||||
|
||||
use frame_support_procedural_tools::syn_ext as ext;
|
||||
use proc_macro2::TokenStream;
|
||||
use syn::{spanned::Spanned, parse_quote};
|
||||
use quote::quote;
|
||||
use super::super::{DeclStorageDefExt, StorageLineTypeDef};
|
||||
|
||||
pub struct GenesisConfigFieldDef {
|
||||
pub name: syn::Ident,
|
||||
pub typ: syn::Type,
|
||||
pub attrs: Vec<syn::Meta>,
|
||||
pub default: TokenStream,
|
||||
}
|
||||
|
||||
pub struct GenesisConfigDef {
|
||||
pub is_generic: bool,
|
||||
pub fields: Vec<GenesisConfigFieldDef>,
|
||||
/// For example: `<T: Trait<I>, I: Instance=DefaultInstance>`.
|
||||
pub genesis_struct_decl: TokenStream,
|
||||
/// For example: `<T, I>`.
|
||||
pub genesis_struct: TokenStream,
|
||||
/// For example: `<T: Trait<I>, I: Instance>`.
|
||||
pub genesis_impl: TokenStream,
|
||||
/// The where clause to use to constrain generics if genesis config is generic.
|
||||
pub genesis_where_clause: Option<syn::WhereClause>,
|
||||
}
|
||||
|
||||
impl GenesisConfigDef {
|
||||
pub fn from_def(def: &DeclStorageDefExt) -> syn::Result<Self> {
|
||||
let fields = Self::get_genesis_config_field_defs(def)?;
|
||||
|
||||
let is_generic = fields.iter()
|
||||
.any(|field| ext::type_contains_ident(&field.typ, &def.module_runtime_generic));
|
||||
|
||||
let (
|
||||
genesis_struct_decl,
|
||||
genesis_impl,
|
||||
genesis_struct,
|
||||
genesis_where_clause
|
||||
) = if is_generic {
|
||||
let runtime_generic = &def.module_runtime_generic;
|
||||
let runtime_trait = &def.module_runtime_trait;
|
||||
let optional_instance = &def.optional_instance;
|
||||
let optional_instance_bound = &def.optional_instance_bound;
|
||||
let optional_instance_bound_optional_default = &def.optional_instance_bound_optional_default;
|
||||
let where_clause = &def.where_clause;
|
||||
(
|
||||
quote!(<#runtime_generic: #runtime_trait, #optional_instance_bound_optional_default>),
|
||||
quote!(<#runtime_generic: #runtime_trait, #optional_instance_bound>),
|
||||
quote!(<#runtime_generic, #optional_instance>),
|
||||
where_clause.clone(),
|
||||
)
|
||||
} else {
|
||||
(quote!(), quote!(), quote!(), None)
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
is_generic,
|
||||
fields,
|
||||
genesis_struct_decl,
|
||||
genesis_struct,
|
||||
genesis_impl,
|
||||
genesis_where_clause,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_genesis_config_field_defs(def: &DeclStorageDefExt)
|
||||
-> syn::Result<Vec<GenesisConfigFieldDef>>
|
||||
{
|
||||
let mut config_field_defs = Vec::new();
|
||||
|
||||
for (config_field, line) in def.storage_lines.iter()
|
||||
.filter_map(|line| line.config.as_ref().map(|config_field| (config_field.clone(), line)))
|
||||
{
|
||||
let value_type = &line.value_type;
|
||||
|
||||
let typ = match &line.storage_type {
|
||||
StorageLineTypeDef::Simple(_) => (*value_type).clone(),
|
||||
StorageLineTypeDef::Map(map) | StorageLineTypeDef::LinkedMap(map) => {
|
||||
let key = &map.key;
|
||||
parse_quote!( Vec<(#key, #value_type)> )
|
||||
},
|
||||
StorageLineTypeDef::DoubleMap(map) => {
|
||||
let key1 = &map.key1;
|
||||
let key2 = &map.key2;
|
||||
|
||||
parse_quote!( Vec<(#key1, #key2, #value_type)> )
|
||||
},
|
||||
};
|
||||
|
||||
let default = line.default_value.as_ref()
|
||||
.map(|d| {
|
||||
if line.is_option {
|
||||
quote!( #d.unwrap_or_default() )
|
||||
} else {
|
||||
quote!( #d )
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| quote!( Default::default() ));
|
||||
|
||||
config_field_defs.push(GenesisConfigFieldDef {
|
||||
name: config_field,
|
||||
typ,
|
||||
attrs: line.doc_attrs.clone(),
|
||||
default,
|
||||
});
|
||||
}
|
||||
|
||||
for line in &def.extra_genesis_config_lines {
|
||||
let attrs = line.attrs.iter()
|
||||
.map(|attr| {
|
||||
let meta = attr.parse_meta()?;
|
||||
if meta.path().is_ident("cfg") {
|
||||
return Err(syn::Error::new(
|
||||
meta.span(),
|
||||
"extra genesis config items do not support `cfg` attribute"
|
||||
));
|
||||
}
|
||||
Ok(meta)
|
||||
})
|
||||
.collect::<syn::Result<_>>()?;
|
||||
|
||||
let default = line.default.as_ref().map(|e| quote!( #e ))
|
||||
.unwrap_or_else(|| quote!( Default::default() ));
|
||||
|
||||
|
||||
config_field_defs.push(GenesisConfigFieldDef {
|
||||
name: line.name.clone(),
|
||||
typ: line.typ.clone(),
|
||||
attrs,
|
||||
default,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(config_field_defs)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Declaration of genesis config structure and implementation of build storage trait and
|
||||
//! functions.
|
||||
|
||||
use proc_macro2::{TokenStream, Span};
|
||||
use quote::quote;
|
||||
use super::{DeclStorageDefExt, instance_trait::DEFAULT_INSTANTIABLE_TRAIT_NAME};
|
||||
use genesis_config_def::GenesisConfigDef;
|
||||
use builder_def::BuilderDef;
|
||||
|
||||
mod genesis_config_def;
|
||||
mod builder_def;
|
||||
|
||||
const DEFAULT_INSTANCE_NAME: &str = "__GeneratedInstance";
|
||||
|
||||
fn decl_genesis_config_and_impl_default(
|
||||
scrate: &TokenStream,
|
||||
genesis_config: &GenesisConfigDef,
|
||||
) -> TokenStream {
|
||||
let config_fields = genesis_config.fields.iter().map(|field| {
|
||||
let (name, typ, attrs) = (&field.name, &field.typ, &field.attrs);
|
||||
quote!( #( #[ #attrs] )* pub #name: #typ, )
|
||||
});
|
||||
|
||||
let config_field_defaults = genesis_config.fields.iter().map(|field| {
|
||||
let (name, default) = (&field.name, &field.default);
|
||||
quote!( #name: #default, )
|
||||
});
|
||||
|
||||
let serde_bug_bound = if !genesis_config.fields.is_empty() {
|
||||
let mut b_ser = String::new();
|
||||
let mut b_dser = String::new();
|
||||
|
||||
for typ in genesis_config.fields.iter().map(|c| &c.typ) {
|
||||
let typ = quote!( #typ );
|
||||
b_ser.push_str(&format!("{} : {}::serde::Serialize, ", typ, scrate));
|
||||
b_dser.push_str(&format!("{} : {}::serde::de::DeserializeOwned, ", typ, scrate));
|
||||
}
|
||||
|
||||
quote! {
|
||||
#[serde(bound(serialize = #b_ser))]
|
||||
#[serde(bound(deserialize = #b_dser))]
|
||||
}
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
|
||||
let genesis_struct_decl = &genesis_config.genesis_struct_decl;
|
||||
let genesis_struct = &genesis_config.genesis_struct;
|
||||
let genesis_impl = &genesis_config.genesis_impl;
|
||||
let genesis_where_clause = &genesis_config.genesis_where_clause;
|
||||
|
||||
quote!(
|
||||
#[derive(#scrate::Serialize, #scrate::Deserialize)]
|
||||
#[cfg(feature = "std")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#serde_bug_bound
|
||||
pub struct GenesisConfig#genesis_struct_decl #genesis_where_clause {
|
||||
#( #config_fields )*
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl#genesis_impl Default for GenesisConfig#genesis_struct #genesis_where_clause {
|
||||
fn default() -> Self {
|
||||
GenesisConfig {
|
||||
#( #config_field_defaults )*
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn impl_build_storage(
|
||||
scrate: &TokenStream,
|
||||
def: &DeclStorageDefExt,
|
||||
genesis_config: &GenesisConfigDef,
|
||||
builders: &BuilderDef,
|
||||
) -> TokenStream {
|
||||
let runtime_generic = &def.module_runtime_generic;
|
||||
let runtime_trait = &def.module_runtime_trait;
|
||||
let optional_instance = &def.optional_instance;
|
||||
let optional_instance_bound = &def.optional_instance_bound;
|
||||
let where_clause = &def.where_clause;
|
||||
|
||||
let inherent_instance = def.optional_instance.clone().unwrap_or_else(|| {
|
||||
let name = syn::Ident::new(DEFAULT_INSTANCE_NAME, Span::call_site());
|
||||
quote!( #name )
|
||||
});
|
||||
let inherent_instance_bound = def.optional_instance_bound.clone().unwrap_or_else(|| {
|
||||
let bound = syn::Ident::new(DEFAULT_INSTANTIABLE_TRAIT_NAME, Span::call_site());
|
||||
quote!( #inherent_instance: #bound )
|
||||
});
|
||||
|
||||
let build_storage_impl = quote!(
|
||||
<#runtime_generic: #runtime_trait, #inherent_instance_bound>
|
||||
);
|
||||
|
||||
let genesis_struct = &genesis_config.genesis_struct;
|
||||
let genesis_impl = &genesis_config.genesis_impl;
|
||||
let genesis_where_clause = &genesis_config.genesis_where_clause;
|
||||
|
||||
let (
|
||||
fn_generic,
|
||||
fn_traitinstance,
|
||||
fn_where_clause
|
||||
) = if !genesis_config.is_generic && builders.is_generic {
|
||||
(
|
||||
quote!( <#runtime_generic: #runtime_trait, #optional_instance_bound> ),
|
||||
quote!( #runtime_generic, #optional_instance ),
|
||||
Some(&def.where_clause),
|
||||
)
|
||||
} else {
|
||||
(quote!(), quote!(), None)
|
||||
};
|
||||
|
||||
let builder_blocks = &builders.blocks;
|
||||
|
||||
let build_storage_impl_trait = quote!(
|
||||
#scrate::sr_primitives::BuildModuleGenesisStorage<#runtime_generic, #inherent_instance>
|
||||
);
|
||||
|
||||
quote!{
|
||||
#[cfg(feature = "std")]
|
||||
impl#genesis_impl GenesisConfig#genesis_struct #genesis_where_clause {
|
||||
pub fn build_storage #fn_generic (&self) -> std::result::Result<
|
||||
(
|
||||
#scrate::sr_primitives::StorageOverlay,
|
||||
#scrate::sr_primitives::ChildrenStorageOverlay,
|
||||
),
|
||||
String
|
||||
> #fn_where_clause {
|
||||
let mut storage = (Default::default(), Default::default());
|
||||
self.assimilate_storage::<#fn_traitinstance>(&mut storage)?;
|
||||
Ok(storage)
|
||||
}
|
||||
|
||||
/// Assimilate the storage for this module into pre-existing overlays.
|
||||
pub fn assimilate_storage #fn_generic (
|
||||
&self,
|
||||
tuple_storage: &mut (
|
||||
#scrate::sr_primitives::StorageOverlay,
|
||||
#scrate::sr_primitives::ChildrenStorageOverlay,
|
||||
),
|
||||
) -> std::result::Result<(), String> #fn_where_clause {
|
||||
#scrate::BasicExternalities::execute_with_storage(tuple_storage, || {
|
||||
#( #builder_blocks )*
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl#build_storage_impl #build_storage_impl_trait for GenesisConfig#genesis_struct
|
||||
#where_clause
|
||||
{
|
||||
fn build_module_genesis_storage(
|
||||
&self,
|
||||
storage: &mut (
|
||||
#scrate::sr_primitives::StorageOverlay,
|
||||
#scrate::sr_primitives::ChildrenStorageOverlay,
|
||||
),
|
||||
) -> std::result::Result<(), String> {
|
||||
self.assimilate_storage::<#fn_traitinstance> (storage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn genesis_config_and_build_storage(
|
||||
scrate: &TokenStream,
|
||||
def: &DeclStorageDefExt,
|
||||
) -> TokenStream {
|
||||
let builders = BuilderDef::from_def(scrate, def);
|
||||
if !builders.blocks.is_empty() {
|
||||
let genesis_config = match GenesisConfigDef::from_def(def) {
|
||||
Ok(genesis_config) => genesis_config,
|
||||
Err(err) => return err.to_compile_error(),
|
||||
};
|
||||
let decl_genesis_config_and_impl_default =
|
||||
decl_genesis_config_and_impl_default(scrate, &genesis_config);
|
||||
let impl_build_storage = impl_build_storage(scrate, def, &genesis_config, &builders);
|
||||
|
||||
quote!{
|
||||
#decl_genesis_config_and_impl_default
|
||||
#impl_build_storage
|
||||
}
|
||||
} else {
|
||||
quote!()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Implementation of getters on module structure.
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use super::{DeclStorageDefExt, StorageLineTypeDef};
|
||||
|
||||
pub fn impl_getters(scrate: &TokenStream, def: &DeclStorageDefExt) -> TokenStream {
|
||||
let mut getters = TokenStream::new();
|
||||
|
||||
for (get_fn, line) in def.storage_lines.iter()
|
||||
.filter_map(|line| line.getter.as_ref().map(|get_fn| (get_fn, line)))
|
||||
{
|
||||
let attrs = &line.doc_attrs;
|
||||
|
||||
let storage_struct = &line.storage_struct;
|
||||
let storage_trait = &line.storage_trait;
|
||||
|
||||
let getter = match &line.storage_type {
|
||||
StorageLineTypeDef::Simple(value) => {
|
||||
quote!{
|
||||
#( #[ #attrs ] )*
|
||||
pub fn #get_fn() -> #value {
|
||||
<#storage_struct as #scrate::#storage_trait>::get()
|
||||
}
|
||||
}
|
||||
},
|
||||
StorageLineTypeDef::Map(map) | StorageLineTypeDef::LinkedMap(map) => {
|
||||
let key = &map.key;
|
||||
let value = &map.value;
|
||||
quote!{
|
||||
#( #[ #attrs ] )*
|
||||
pub fn #get_fn<K: #scrate::codec::EncodeLike<#key>>(key: K) -> #value {
|
||||
<#storage_struct as #scrate::#storage_trait>::get(key)
|
||||
}
|
||||
}
|
||||
},
|
||||
StorageLineTypeDef::DoubleMap(map) => {
|
||||
let key1 = &map.key1;
|
||||
let key2 = &map.key2;
|
||||
let value = &map.value;
|
||||
quote!{
|
||||
pub fn #get_fn<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> #value
|
||||
where
|
||||
KArg1: #scrate::codec::EncodeLike<#key1>,
|
||||
KArg2: #scrate::codec::EncodeLike<#key2>,
|
||||
{
|
||||
<#storage_struct as #scrate::#storage_trait>::get(k1, k2)
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
getters.extend(getter);
|
||||
}
|
||||
|
||||
let module_struct = &def.module_struct;
|
||||
let module_impl = &def.module_impl;
|
||||
let where_clause = &def.where_clause;
|
||||
|
||||
quote!(
|
||||
impl#module_impl #module_struct #where_clause {
|
||||
#getters
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Implementation of the trait instance and the instance structures implementing it.
|
||||
//! (For not instantiable traits there is still the inherent instance implemented).
|
||||
|
||||
use proc_macro2::{TokenStream, Span};
|
||||
use quote::quote;
|
||||
use super::{DeclStorageDefExt, StorageLineTypeDef};
|
||||
|
||||
const NUMBER_OF_INSTANCE: usize = 16;
|
||||
const INHERENT_INSTANCE_NAME: &str = "__InherentHiddenInstance";
|
||||
pub(crate) const DEFAULT_INSTANTIABLE_TRAIT_NAME: &str = "__GeneratedInstantiable";
|
||||
|
||||
// prefix for consts in trait Instance
|
||||
pub(crate) const PREFIX_FOR: &str = "PREFIX_FOR_";
|
||||
pub(crate) const HEAD_KEY_FOR: &str = "HEAD_KEY_FOR_";
|
||||
|
||||
// Used to generate the const:
|
||||
// `const $name: &'static str = $value_prefix ++ instance_prefix ++ $value_suffix`
|
||||
struct InstanceConstDef {
|
||||
name: syn::Ident,
|
||||
value_prefix: String,
|
||||
value_suffix: String,
|
||||
}
|
||||
|
||||
// Used to generate an instance implementation.
|
||||
struct InstanceDef {
|
||||
prefix: String,
|
||||
instance_struct: syn::Ident,
|
||||
doc: TokenStream,
|
||||
}
|
||||
|
||||
pub fn decl_and_impl(scrate: &TokenStream, def: &DeclStorageDefExt) -> TokenStream {
|
||||
let mut impls = TokenStream::new();
|
||||
|
||||
let mut const_defs = vec![];
|
||||
|
||||
for line in def.storage_lines.iter() {
|
||||
let storage_prefix = format!("{} {}", def.crate_name, line.name);
|
||||
|
||||
let const_name = syn::Ident::new(
|
||||
&format!("{}{}", PREFIX_FOR, line.name.to_string()), proc_macro2::Span::call_site()
|
||||
);
|
||||
const_defs.push(InstanceConstDef {
|
||||
name: const_name,
|
||||
value_prefix: String::new(),
|
||||
value_suffix: storage_prefix.clone(),
|
||||
});
|
||||
|
||||
if let StorageLineTypeDef::LinkedMap(_) = line.storage_type {
|
||||
let const_name = syn::Ident::new(
|
||||
&format!("{}{}", HEAD_KEY_FOR, line.name.to_string()), proc_macro2::Span::call_site()
|
||||
);
|
||||
const_defs.push(InstanceConstDef {
|
||||
name: const_name,
|
||||
value_prefix: "head of ".into(),
|
||||
value_suffix: storage_prefix,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impls.extend(create_instance_trait(&const_defs, def));
|
||||
|
||||
// Implementation of instances.
|
||||
if let Some(module_instance) = &def.module_instance {
|
||||
let instance_defs = (0..NUMBER_OF_INSTANCE)
|
||||
.map(|i| {
|
||||
let name = format!("Instance{}", i);
|
||||
InstanceDef {
|
||||
instance_struct: syn::Ident::new(&name, proc_macro2::Span::call_site()),
|
||||
prefix: name,
|
||||
doc: quote!(#[doc=r"Module instance"]),
|
||||
}
|
||||
})
|
||||
.chain(
|
||||
module_instance.instance_default.as_ref().map(|ident| InstanceDef {
|
||||
prefix: String::new(),
|
||||
instance_struct: ident.clone(),
|
||||
doc: quote!(#[doc=r"Default module instance"]),
|
||||
})
|
||||
);
|
||||
|
||||
for instance_def in instance_defs {
|
||||
impls.extend(create_and_impl_instance_struct(scrate, &instance_def, &const_defs, def));
|
||||
}
|
||||
}
|
||||
|
||||
// The name of the inherently available instance.
|
||||
let inherent_instance = syn::Ident::new(INHERENT_INSTANCE_NAME, Span::call_site());
|
||||
|
||||
// Implementation of inherent instance.
|
||||
if let Some(default_instance) = def.module_instance.as_ref()
|
||||
.and_then(|i| i.instance_default.as_ref())
|
||||
{
|
||||
impls.extend(quote! {
|
||||
#[doc(hidden)]
|
||||
pub type #inherent_instance = #default_instance;
|
||||
});
|
||||
} else {
|
||||
let instance_def = InstanceDef {
|
||||
prefix: String::new(),
|
||||
instance_struct: inherent_instance,
|
||||
doc: quote!(#[doc(hidden)]),
|
||||
};
|
||||
impls.extend(create_and_impl_instance_struct(scrate, &instance_def, &const_defs, def));
|
||||
}
|
||||
|
||||
impls
|
||||
}
|
||||
|
||||
fn create_instance_trait(
|
||||
const_defs: &[InstanceConstDef],
|
||||
def: &DeclStorageDefExt,
|
||||
) -> TokenStream {
|
||||
let instance_trait = def.module_instance.as_ref().map(|i| i.instance_trait.clone())
|
||||
.unwrap_or_else(|| syn::Ident::new(DEFAULT_INSTANTIABLE_TRAIT_NAME, Span::call_site()));
|
||||
|
||||
let mut const_impls = TokenStream::new();
|
||||
for const_def in const_defs {
|
||||
let const_name = &const_def.name;
|
||||
const_impls.extend(quote! {
|
||||
const #const_name: &'static str;
|
||||
});
|
||||
}
|
||||
|
||||
let optional_hide = if def.module_instance.is_some() {
|
||||
quote!()
|
||||
} else {
|
||||
quote!(#[doc(hidden)])
|
||||
};
|
||||
|
||||
quote! {
|
||||
/// Tag a type as an instance of a module.
|
||||
///
|
||||
/// Defines storage prefixes, they must be unique.
|
||||
#optional_hide
|
||||
pub trait #instance_trait: 'static {
|
||||
/// The prefix used by any storage entry of an instance.
|
||||
const PREFIX: &'static str;
|
||||
#const_impls
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_and_impl_instance_struct(
|
||||
scrate: &TokenStream,
|
||||
instance_def: &InstanceDef,
|
||||
const_defs: &[InstanceConstDef],
|
||||
def: &DeclStorageDefExt,
|
||||
) -> TokenStream {
|
||||
let mut const_impls = TokenStream::new();
|
||||
|
||||
for const_def in const_defs {
|
||||
let const_value = format!(
|
||||
"{}{}{}", const_def.value_prefix, instance_def.prefix, const_def.value_suffix
|
||||
);
|
||||
let const_name = &const_def.name;
|
||||
|
||||
const_impls.extend(quote! {
|
||||
const #const_name: &'static str = #const_value;
|
||||
});
|
||||
}
|
||||
|
||||
let instance_trait = def.module_instance.as_ref().map(|i| i.instance_trait.clone())
|
||||
.unwrap_or_else(|| syn::Ident::new(DEFAULT_INSTANTIABLE_TRAIT_NAME, Span::call_site()));
|
||||
|
||||
let instance_struct = &instance_def.instance_struct;
|
||||
let prefix = format!("{}{}", instance_def.prefix, def.crate_name.to_string());
|
||||
let doc = &instance_def.doc;
|
||||
|
||||
quote! {
|
||||
// Those trait are derived because of wrong bounds for generics
|
||||
#[derive(
|
||||
Clone, Eq, PartialEq,
|
||||
#scrate::codec::Encode,
|
||||
#scrate::codec::Decode,
|
||||
#scrate::RuntimeDebug,
|
||||
)]
|
||||
#doc
|
||||
pub struct #instance_struct;
|
||||
impl #instance_trait for #instance_struct {
|
||||
const PREFIX: &'static str = #prefix;
|
||||
#const_impls
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Implementation of `storage_metadata` on module structure, used by construct_runtime.
|
||||
|
||||
use frame_support_procedural_tools::clean_type_string;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use super::{DeclStorageDefExt, StorageLineDefExt, StorageLineTypeDef};
|
||||
|
||||
fn storage_line_metadata_type(scrate: &TokenStream, line: &StorageLineDefExt) -> TokenStream {
|
||||
let value_type = &line.value_type;
|
||||
let value_type = clean_type_string("e!( #value_type ).to_string());
|
||||
match &line.storage_type {
|
||||
StorageLineTypeDef::Simple(_) => {
|
||||
quote!{
|
||||
#scrate::metadata::StorageEntryType::Plain(
|
||||
#scrate::metadata::DecodeDifferent::Encode(#value_type),
|
||||
)
|
||||
}
|
||||
},
|
||||
StorageLineTypeDef::Map(map) => {
|
||||
let hasher = map.hasher.into_metadata();
|
||||
let key = &map.key;
|
||||
let key = clean_type_string("e!(#key).to_string());
|
||||
quote!{
|
||||
#scrate::metadata::StorageEntryType::Map {
|
||||
hasher: #scrate::metadata::#hasher,
|
||||
key: #scrate::metadata::DecodeDifferent::Encode(#key),
|
||||
value: #scrate::metadata::DecodeDifferent::Encode(#value_type),
|
||||
is_linked: false,
|
||||
}
|
||||
}
|
||||
},
|
||||
StorageLineTypeDef::LinkedMap(map) => {
|
||||
let hasher = map.hasher.into_metadata();
|
||||
let key = &map.key;
|
||||
let key = clean_type_string("e!(#key).to_string());
|
||||
quote!{
|
||||
#scrate::metadata::StorageEntryType::Map {
|
||||
hasher: #scrate::metadata::#hasher,
|
||||
key: #scrate::metadata::DecodeDifferent::Encode(#key),
|
||||
value: #scrate::metadata::DecodeDifferent::Encode(#value_type),
|
||||
is_linked: true,
|
||||
}
|
||||
}
|
||||
},
|
||||
StorageLineTypeDef::DoubleMap(map) => {
|
||||
let hasher1 = map.hasher1.into_metadata();
|
||||
let hasher2 = map.hasher2.into_metadata();
|
||||
let key1 = &map.key1;
|
||||
let key1 = clean_type_string("e!(#key1).to_string());
|
||||
let key2 = &map.key2;
|
||||
let key2 = clean_type_string("e!(#key2).to_string());
|
||||
quote!{
|
||||
#scrate::metadata::StorageEntryType::DoubleMap {
|
||||
hasher: #scrate::metadata::#hasher1,
|
||||
key1: #scrate::metadata::DecodeDifferent::Encode(#key1),
|
||||
key2: #scrate::metadata::DecodeDifferent::Encode(#key2),
|
||||
value: #scrate::metadata::DecodeDifferent::Encode(#value_type),
|
||||
key2_hasher: #scrate::metadata::#hasher2,
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn default_byte_getter(
|
||||
scrate: &TokenStream,
|
||||
line: &StorageLineDefExt,
|
||||
def: &DeclStorageDefExt,
|
||||
) -> (TokenStream, TokenStream) {
|
||||
let default = line.default_value.as_ref().map(|d| quote!( #d ))
|
||||
.unwrap_or_else(|| quote!( Default::default() ));
|
||||
|
||||
let str_name = line.name.to_string();
|
||||
let struct_name = syn::Ident::new(&("__GetByteStruct".to_string() + &str_name), line.name.span());
|
||||
let cache_name = syn::Ident::new(&("__CACHE_GET_BYTE_STRUCT_".to_string() + &str_name), line.name.span());
|
||||
|
||||
let runtime_generic = &def.module_runtime_generic;
|
||||
let runtime_trait = &def.module_runtime_trait;
|
||||
let optional_instance_bound_optional_default = &def.optional_instance_bound_optional_default;
|
||||
let optional_instance_bound = &def.optional_instance_bound;
|
||||
let optional_instance = &def.optional_instance;
|
||||
let optional_comma_instance = optional_instance.as_ref().map(|i| quote!(, #i));
|
||||
let where_clause = &def.where_clause;
|
||||
|
||||
let query_type = &line.query_type;
|
||||
|
||||
let struct_def = quote! {
|
||||
#[doc(hidden)]
|
||||
pub struct #struct_name<
|
||||
#runtime_generic, #optional_instance_bound_optional_default
|
||||
>(pub #scrate::rstd::marker::PhantomData<(#runtime_generic #optional_comma_instance)>);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[allow(non_upper_case_globals)]
|
||||
static #cache_name: #scrate::once_cell::sync::OnceCell<#scrate::rstd::vec::Vec<u8>> =
|
||||
#scrate::once_cell::sync::OnceCell::new();
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<#runtime_generic: #runtime_trait, #optional_instance_bound>
|
||||
#scrate::metadata::DefaultByte
|
||||
for #struct_name<#runtime_generic, #optional_instance>
|
||||
#where_clause
|
||||
{
|
||||
fn default_byte(&self) -> #scrate::rstd::vec::Vec<u8> {
|
||||
use #scrate::codec::Encode;
|
||||
#cache_name.get_or_init(|| {
|
||||
let def_val: #query_type = #default;
|
||||
<#query_type as Encode>::encode(&def_val)
|
||||
}).clone()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<#runtime_generic: #runtime_trait, #optional_instance_bound> Send
|
||||
for #struct_name<#runtime_generic, #optional_instance> #where_clause {}
|
||||
|
||||
unsafe impl<#runtime_generic: #runtime_trait, #optional_instance_bound> Sync
|
||||
for #struct_name<#runtime_generic, #optional_instance> #where_clause {}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<#runtime_generic: #runtime_trait, #optional_instance_bound>
|
||||
#scrate::metadata::DefaultByte
|
||||
for #struct_name<#runtime_generic, #optional_instance>
|
||||
#where_clause
|
||||
{
|
||||
fn default_byte(&self) -> #scrate::rstd::vec::Vec<u8> {
|
||||
use #scrate::codec::Encode;
|
||||
let def_val: #query_type = #default;
|
||||
<#query_type as Encode>::encode(&def_val)
|
||||
}
|
||||
}
|
||||
};
|
||||
let struct_instance = quote!(
|
||||
#struct_name::<#runtime_generic, #optional_instance>(#scrate::rstd::marker::PhantomData)
|
||||
);
|
||||
|
||||
(struct_def, struct_instance)
|
||||
}
|
||||
|
||||
pub fn impl_metadata(scrate: &TokenStream, def: &DeclStorageDefExt) -> TokenStream {
|
||||
let mut entries = TokenStream::new();
|
||||
let mut default_byte_getter_struct_defs = TokenStream::new();
|
||||
|
||||
for line in def.storage_lines.iter() {
|
||||
let str_name = line.name.to_string();
|
||||
|
||||
let modifier = if line.is_option {
|
||||
quote!(#scrate::metadata::StorageEntryModifier::Optional)
|
||||
} else {
|
||||
quote!(#scrate::metadata::StorageEntryModifier::Default)
|
||||
};
|
||||
|
||||
let ty = storage_line_metadata_type(scrate, line);
|
||||
|
||||
let (
|
||||
default_byte_getter_struct_def,
|
||||
default_byte_getter_struct_instance,
|
||||
) = default_byte_getter(scrate, line, def);
|
||||
|
||||
let mut docs = TokenStream::new();
|
||||
for attr in line.attrs.iter().filter_map(|v| v.parse_meta().ok()) {
|
||||
if let syn::Meta::NameValue(meta) = attr {
|
||||
if meta.path.is_ident("doc") {
|
||||
let lit = meta.lit;
|
||||
docs.extend(quote!(#lit,));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let entry = quote! {
|
||||
#scrate::metadata::StorageEntryMetadata {
|
||||
name: #scrate::metadata::DecodeDifferent::Encode(#str_name),
|
||||
modifier: #modifier,
|
||||
ty: #ty,
|
||||
default: #scrate::metadata::DecodeDifferent::Encode(
|
||||
#scrate::metadata::DefaultByteGetter(&#default_byte_getter_struct_instance)
|
||||
),
|
||||
documentation: #scrate::metadata::DecodeDifferent::Encode(&[ #docs ]),
|
||||
},
|
||||
};
|
||||
|
||||
default_byte_getter_struct_defs.extend(default_byte_getter_struct_def);
|
||||
entries.extend(entry);
|
||||
}
|
||||
|
||||
let prefix = if let Some(instance) = &def.module_instance {
|
||||
let instance_generic = &instance.instance_generic;
|
||||
quote!(#instance_generic::PREFIX)
|
||||
} else {
|
||||
let prefix = def.crate_name.to_string();
|
||||
quote!(#prefix)
|
||||
};
|
||||
|
||||
let store_metadata = quote!(
|
||||
#scrate::metadata::StorageMetadata {
|
||||
prefix: #scrate::metadata::DecodeDifferent::Encode(#prefix),
|
||||
entries: #scrate::metadata::DecodeDifferent::Encode(&[ #entries ][..]),
|
||||
}
|
||||
);
|
||||
|
||||
let module_struct = &def.module_struct;
|
||||
let module_impl = &def.module_impl;
|
||||
let where_clause = &def.where_clause;
|
||||
|
||||
quote!(
|
||||
#default_byte_getter_struct_defs
|
||||
|
||||
impl#module_impl #module_struct #where_clause {
|
||||
#[doc(hidden)]
|
||||
pub fn storage_metadata() -> #scrate::metadata::StorageMetadata {
|
||||
#store_metadata
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,432 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! `decl_storage` input definition and expansion.
|
||||
|
||||
mod storage_struct;
|
||||
mod parse;
|
||||
mod store_trait;
|
||||
mod getters;
|
||||
mod metadata;
|
||||
mod instance_trait;
|
||||
mod genesis_config;
|
||||
|
||||
use quote::quote;
|
||||
use frame_support_procedural_tools::{
|
||||
generate_crate_access, generate_hidden_includes, syn_ext as ext
|
||||
};
|
||||
|
||||
/// All informations contained in input of decl_storage
|
||||
pub struct DeclStorageDef {
|
||||
/// Name of the module used to import hidden imports.
|
||||
hidden_crate: Option<syn::Ident>,
|
||||
/// Visibility of store trait.
|
||||
visibility: syn::Visibility,
|
||||
/// Name of store trait: usually `Store`.
|
||||
store_trait: syn::Ident,
|
||||
/// Module name used by construct_runtime: usually `Module`.
|
||||
module_name: syn::Ident,
|
||||
/// Usually `T`.
|
||||
module_runtime_generic: syn::Ident,
|
||||
/// Usually `Trait`
|
||||
module_runtime_trait: syn::Path,
|
||||
/// For instantiable module: usually `I: Instance=DefaultInstance`.
|
||||
module_instance: Option<ModuleInstanceDef>,
|
||||
/// Where claused used to constrain T and I even more.
|
||||
where_clause: Option<syn::WhereClause>,
|
||||
/// The extra build function used to build storage at genesis.
|
||||
extra_genesis_build: Option<syn::Expr>,
|
||||
/// The extra genesis config fields.
|
||||
extra_genesis_config_lines: Vec<ExtraGenesisLineDef>,
|
||||
/// Definition of storages.
|
||||
storage_lines: Vec<StorageLineDef>,
|
||||
/// Name of the crate, used for storage prefixes.
|
||||
crate_name: syn::Ident,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for DeclStorageDef {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
parse::parse(input)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extended version of `DeclStorageDef` with useful precomputed value.
|
||||
pub struct DeclStorageDefExt {
|
||||
/// Name of the module used to import hidden imports.
|
||||
hidden_crate: Option<syn::Ident>,
|
||||
/// Visibility of store trait.
|
||||
visibility: syn::Visibility,
|
||||
/// Name of store trait: usually `Store`.
|
||||
store_trait: syn::Ident,
|
||||
/// Module name used by construct_runtime: usually `Module`.
|
||||
#[allow(unused)]
|
||||
module_name: syn::Ident,
|
||||
/// Usually `T`.
|
||||
module_runtime_generic: syn::Ident,
|
||||
/// Usually `Trait`.
|
||||
module_runtime_trait: syn::Path,
|
||||
/// For instantiable module: usually `I: Instance=DefaultInstance`.
|
||||
module_instance: Option<ModuleInstanceDef>,
|
||||
/// Where claused used to constrain T and I even more.
|
||||
where_clause: Option<syn::WhereClause>,
|
||||
/// The extra build function used to build storage at genesis.
|
||||
extra_genesis_build: Option<syn::Expr>,
|
||||
/// The extra genesis config fields.
|
||||
extra_genesis_config_lines: Vec<ExtraGenesisLineDef>,
|
||||
/// Definition of storages.
|
||||
storage_lines: Vec<StorageLineDefExt>,
|
||||
/// Name of the crate, used for storage prefixes.
|
||||
crate_name: syn::Ident,
|
||||
/// Full struct expansion: `Module<T, I>`.
|
||||
module_struct: proc_macro2::TokenStream,
|
||||
/// Impl block for module: `<T: Trait, I: Instance>`.
|
||||
module_impl: proc_macro2::TokenStream,
|
||||
/// For instantiable: `I`.
|
||||
optional_instance: Option<proc_macro2::TokenStream>,
|
||||
/// For instantiable: `I: Instance`.
|
||||
optional_instance_bound: Option<proc_macro2::TokenStream>,
|
||||
/// For instantiable: `I: Instance = DefaultInstance`.
|
||||
optional_instance_bound_optional_default: Option<proc_macro2::TokenStream>,
|
||||
}
|
||||
|
||||
impl From<DeclStorageDef> for DeclStorageDefExt {
|
||||
fn from(mut def: DeclStorageDef) -> Self {
|
||||
let storage_lines = def.storage_lines.drain(..).collect::<Vec<_>>();
|
||||
let storage_lines = storage_lines.into_iter()
|
||||
.map(|line| StorageLineDefExt::from_def(line, &def))
|
||||
.collect();
|
||||
|
||||
let (
|
||||
optional_instance,
|
||||
optional_instance_bound,
|
||||
optional_instance_bound_optional_default,
|
||||
) = if let Some(instance) = def.module_instance.as_ref() {
|
||||
let instance_generic = &instance.instance_generic;
|
||||
let instance_trait= &instance.instance_trait;
|
||||
let optional_equal_instance_default = instance.instance_default.as_ref()
|
||||
.map(|d| quote!( = #d ));
|
||||
(
|
||||
Some(quote!(#instance_generic)),
|
||||
Some(quote!(#instance_generic: #instance_trait)),
|
||||
Some(quote!(#instance_generic: #instance_trait #optional_equal_instance_default)),
|
||||
)
|
||||
} else {
|
||||
(None, None, None)
|
||||
};
|
||||
|
||||
let module_runtime_generic = &def.module_runtime_generic;
|
||||
let module_runtime_trait = &def.module_runtime_trait;
|
||||
let module_name = &def.module_name;
|
||||
|
||||
let module_struct = quote!(
|
||||
#module_name<#module_runtime_generic, #optional_instance>
|
||||
);
|
||||
|
||||
let module_impl = quote!(
|
||||
<#module_runtime_generic: #module_runtime_trait + 'static, #optional_instance_bound>
|
||||
);
|
||||
|
||||
Self {
|
||||
hidden_crate: def.hidden_crate,
|
||||
visibility: def.visibility,
|
||||
store_trait: def.store_trait,
|
||||
module_name: def.module_name,
|
||||
module_runtime_generic: def.module_runtime_generic,
|
||||
module_runtime_trait: def.module_runtime_trait,
|
||||
module_instance: def.module_instance,
|
||||
where_clause: def.where_clause,
|
||||
extra_genesis_build: def.extra_genesis_build,
|
||||
extra_genesis_config_lines: def.extra_genesis_config_lines,
|
||||
crate_name: def.crate_name,
|
||||
storage_lines,
|
||||
module_struct,
|
||||
module_impl,
|
||||
optional_instance,
|
||||
optional_instance_bound,
|
||||
optional_instance_bound_optional_default,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Usually `I: Instance=DefaultInstance`.
|
||||
pub struct ModuleInstanceDef {
|
||||
/// Usually: `I`.
|
||||
instance_generic: syn::Ident,
|
||||
/// Usually: `Instance`.
|
||||
instance_trait: syn::Ident,
|
||||
/// Usually: `DefaultInstance`.
|
||||
instance_default: Option<syn::Ident>,
|
||||
}
|
||||
|
||||
pub struct StorageLineDef {
|
||||
attrs: Vec<syn::Attribute>,
|
||||
/// Visibility of the storage struct.
|
||||
visibility: syn::Visibility,
|
||||
name: syn::Ident,
|
||||
/// The name of getter function to be implemented on Module struct.
|
||||
getter: Option<syn::Ident>,
|
||||
/// The name of the field to be used in genesis config if any.
|
||||
config: Option<syn::Ident>,
|
||||
/// The build function of the storage if any.
|
||||
build: Option<syn::Expr>,
|
||||
/// Default value of genesis config field and also for storage when no value available.
|
||||
default_value: Option<syn::Expr>,
|
||||
storage_type: StorageLineTypeDef,
|
||||
}
|
||||
|
||||
pub struct StorageLineDefExt {
|
||||
#[allow(unused)]
|
||||
attrs: Vec<syn::Attribute>,
|
||||
/// Visibility of the storage struct.
|
||||
visibility: syn::Visibility,
|
||||
name: syn::Ident,
|
||||
/// The name of getter function to be implemented on Module struct.
|
||||
getter: Option<syn::Ident>,
|
||||
/// The name of the field to be used in genesis config if any.
|
||||
config: Option<syn::Ident>,
|
||||
/// The build function of the storage if any.
|
||||
build: Option<syn::Expr>,
|
||||
/// Default value of genesis config field and also for storage when no value available.
|
||||
default_value: Option<syn::Expr>,
|
||||
storage_type: StorageLineTypeDef,
|
||||
doc_attrs: Vec<syn::Meta>,
|
||||
/// Either the type stored in storage or wrapped in an Option.
|
||||
query_type: syn::Type,
|
||||
/// The type stored in storage.
|
||||
value_type: syn::Type,
|
||||
/// Full struct, for example: `StorageName<T, I>`.
|
||||
storage_struct: proc_macro2::TokenStream,
|
||||
/// If storage is generic over runtime then `T`.
|
||||
optional_storage_runtime_comma: Option<proc_macro2::TokenStream>,
|
||||
/// If storage is generic over runtime then `T: Trait`.
|
||||
optional_storage_runtime_bound_comma: Option<proc_macro2::TokenStream>,
|
||||
/// The where clause to use to constrain generics if storage is generic over runtime.
|
||||
optional_storage_where_clause: Option<proc_macro2::TokenStream>,
|
||||
/// Full trait, for example: `storage::StorageMap<u32, u32>`.
|
||||
storage_trait: proc_macro2::TokenStream,
|
||||
/// Full trait, for example: `storage::generator::StorageMap<u32, u32>`.
|
||||
storage_generator_trait: proc_macro2::TokenStream,
|
||||
/// Weither the storage is generic.
|
||||
is_generic: bool,
|
||||
/// Weither the storage value is an option.
|
||||
is_option: bool,
|
||||
}
|
||||
|
||||
impl StorageLineDefExt {
|
||||
fn from_def(storage_def: StorageLineDef, def: &DeclStorageDef) -> Self {
|
||||
let is_generic = match &storage_def.storage_type {
|
||||
StorageLineTypeDef::Simple(value) => {
|
||||
ext::type_contains_ident(&value, &def.module_runtime_generic)
|
||||
},
|
||||
StorageLineTypeDef::Map(map) => {
|
||||
ext::type_contains_ident(&map.key, &def.module_runtime_generic)
|
||||
|| ext::type_contains_ident(&map.value, &def.module_runtime_generic)
|
||||
}
|
||||
StorageLineTypeDef::LinkedMap(map) => {
|
||||
ext::type_contains_ident(&map.key, &def.module_runtime_generic)
|
||||
|| ext::type_contains_ident(&map.value, &def.module_runtime_generic)
|
||||
}
|
||||
StorageLineTypeDef::DoubleMap(map) => {
|
||||
ext::type_contains_ident(&map.key1, &def.module_runtime_generic)
|
||||
|| ext::type_contains_ident(&map.key2, &def.module_runtime_generic)
|
||||
|| ext::type_contains_ident(&map.value, &def.module_runtime_generic)
|
||||
}
|
||||
};
|
||||
|
||||
let query_type = match &storage_def.storage_type {
|
||||
StorageLineTypeDef::Simple(value) => value.clone(),
|
||||
StorageLineTypeDef::Map(map) => map.value.clone(),
|
||||
StorageLineTypeDef::LinkedMap(map) => map.value.clone(),
|
||||
StorageLineTypeDef::DoubleMap(map) => map.value.clone(),
|
||||
};
|
||||
let is_option = ext::extract_type_option(&query_type).is_some();
|
||||
let value_type = ext::extract_type_option(&query_type).unwrap_or(query_type.clone());
|
||||
|
||||
let module_runtime_generic = &def.module_runtime_generic;
|
||||
let module_runtime_trait = &def.module_runtime_trait;
|
||||
let optional_storage_runtime_comma = if is_generic {
|
||||
Some(quote!( #module_runtime_generic, ))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let optional_storage_runtime_bound_comma = if is_generic {
|
||||
Some(quote!( #module_runtime_generic: #module_runtime_trait, ))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let storage_name = &storage_def.name;
|
||||
let optional_instance_generic = def.module_instance.as_ref().map(|i| {
|
||||
let instance_generic = &i.instance_generic;
|
||||
quote!( #instance_generic )
|
||||
});
|
||||
let storage_struct = quote!(
|
||||
#storage_name<#optional_storage_runtime_comma #optional_instance_generic>
|
||||
);
|
||||
|
||||
let optional_storage_where_clause = if is_generic {
|
||||
def.where_clause.as_ref().map(|w| quote!( #w ))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let storage_trait_trunkated = match &storage_def.storage_type {
|
||||
StorageLineTypeDef::Simple(_) => {
|
||||
quote!( StorageValue<#value_type> )
|
||||
},
|
||||
StorageLineTypeDef::Map(map) => {
|
||||
let key = &map.key;
|
||||
quote!( StorageMap<#key, #value_type> )
|
||||
},
|
||||
StorageLineTypeDef::LinkedMap(map) => {
|
||||
let key = &map.key;
|
||||
quote!( StorageLinkedMap<#key, #value_type> )
|
||||
},
|
||||
StorageLineTypeDef::DoubleMap(map) => {
|
||||
let key1 = &map.key1;
|
||||
let key2 = &map.key2;
|
||||
quote!( StorageDoubleMap<#key1, #key2, #value_type> )
|
||||
},
|
||||
};
|
||||
|
||||
let storage_trait = quote!( storage::#storage_trait_trunkated );
|
||||
let storage_generator_trait = quote!( storage::generator::#storage_trait_trunkated );
|
||||
|
||||
let doc_attrs = storage_def.attrs.iter()
|
||||
.filter_map(|a| a.parse_meta().ok())
|
||||
.filter(|m| m.path().is_ident("doc"))
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
attrs: storage_def.attrs,
|
||||
visibility: storage_def.visibility,
|
||||
name: storage_def.name,
|
||||
getter: storage_def.getter,
|
||||
config: storage_def.config,
|
||||
build: storage_def.build,
|
||||
default_value: storage_def.default_value,
|
||||
storage_type: storage_def.storage_type,
|
||||
doc_attrs,
|
||||
query_type,
|
||||
value_type,
|
||||
storage_struct,
|
||||
optional_storage_runtime_comma,
|
||||
optional_storage_runtime_bound_comma,
|
||||
optional_storage_where_clause,
|
||||
storage_trait,
|
||||
storage_generator_trait,
|
||||
is_generic,
|
||||
is_option,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum StorageLineTypeDef {
|
||||
Map(MapDef),
|
||||
LinkedMap(MapDef),
|
||||
DoubleMap(DoubleMapDef),
|
||||
Simple(syn::Type),
|
||||
}
|
||||
|
||||
pub struct MapDef {
|
||||
pub hasher: HasherKind,
|
||||
pub key: syn::Type,
|
||||
/// This is the query value not the inner value used in storage trait implementation.
|
||||
pub value: syn::Type,
|
||||
}
|
||||
|
||||
pub struct DoubleMapDef {
|
||||
pub hasher1: HasherKind,
|
||||
pub hasher2: HasherKind,
|
||||
pub key1: syn::Type,
|
||||
pub key2: syn::Type,
|
||||
/// This is the query value not the inner value used in storage trait implementation.
|
||||
pub value: syn::Type,
|
||||
}
|
||||
|
||||
pub struct ExtraGenesisLineDef {
|
||||
attrs: Vec<syn::Attribute>,
|
||||
name: syn::Ident,
|
||||
typ: syn::Type,
|
||||
default: Option<syn::Expr>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum HasherKind {
|
||||
Blake2_256,
|
||||
Blake2_128,
|
||||
Twox256,
|
||||
Twox128,
|
||||
Twox64Concat,
|
||||
}
|
||||
|
||||
impl HasherKind {
|
||||
fn to_storage_hasher_struct(&self) -> proc_macro2::TokenStream {
|
||||
match self {
|
||||
HasherKind::Blake2_256 => quote!( Blake2_256 ),
|
||||
HasherKind::Blake2_128 => quote!( Blake2_128 ),
|
||||
HasherKind::Twox256 => quote!( Twox256 ),
|
||||
HasherKind::Twox128 => quote!( Twox128 ),
|
||||
HasherKind::Twox64Concat => quote!( Twox64Concat ),
|
||||
}
|
||||
}
|
||||
|
||||
fn into_metadata(&self) -> proc_macro2::TokenStream {
|
||||
match self {
|
||||
HasherKind::Blake2_256 => quote!( StorageHasher::Blake2_256 ),
|
||||
HasherKind::Blake2_128 => quote!( StorageHasher::Blake2_128 ),
|
||||
HasherKind::Twox256 => quote!( StorageHasher::Twox256 ),
|
||||
HasherKind::Twox128 => quote!( StorageHasher::Twox128 ),
|
||||
HasherKind::Twox64Concat => quote!( StorageHasher::Twox64Concat ),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Full implementation of decl_storage.
|
||||
pub fn decl_storage_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let def = syn::parse_macro_input!(input as DeclStorageDef);
|
||||
let def_ext = DeclStorageDefExt::from(def);
|
||||
|
||||
let hidden_crate_name = def_ext.hidden_crate.as_ref().map(|i| i.to_string())
|
||||
.unwrap_or_else(|| "decl_storage".to_string());
|
||||
|
||||
let scrate = generate_crate_access(&hidden_crate_name, "frame-support");
|
||||
let scrate_decl = generate_hidden_includes(&hidden_crate_name, "frame-support");
|
||||
|
||||
let store_trait = store_trait::decl_and_impl(&def_ext);
|
||||
let getters = getters::impl_getters(&scrate, &def_ext);
|
||||
let metadata = metadata::impl_metadata(&scrate, &def_ext);
|
||||
let instance_trait = instance_trait::decl_and_impl(&scrate, &def_ext);
|
||||
let genesis_config = genesis_config::genesis_config_and_build_storage(&scrate, &def_ext);
|
||||
let storage_struct = storage_struct::decl_and_impl(&scrate, &def_ext);
|
||||
|
||||
quote!(
|
||||
use #scrate::{
|
||||
StorageValue as _,
|
||||
StorageMap as _,
|
||||
StorageLinkedMap as _,
|
||||
StorageDoubleMap as _
|
||||
};
|
||||
|
||||
#scrate_decl
|
||||
#store_trait
|
||||
#getters
|
||||
#metadata
|
||||
#instance_trait
|
||||
#genesis_config
|
||||
#storage_struct
|
||||
).into()
|
||||
}
|
||||
@@ -0,0 +1,404 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Parsing of decl_storage input.
|
||||
|
||||
use frame_support_procedural_tools::{ToTokens, Parse, syn_ext as ext};
|
||||
use syn::{Ident, Token, spanned::Spanned};
|
||||
|
||||
mod keyword {
|
||||
syn::custom_keyword!(hiddencrate);
|
||||
syn::custom_keyword!(add_extra_genesis);
|
||||
syn::custom_keyword!(extra_genesis_skip_phantom_data_field);
|
||||
syn::custom_keyword!(config);
|
||||
syn::custom_keyword!(build);
|
||||
syn::custom_keyword!(get);
|
||||
syn::custom_keyword!(map);
|
||||
syn::custom_keyword!(linked_map);
|
||||
syn::custom_keyword!(double_map);
|
||||
syn::custom_keyword!(blake2_256);
|
||||
syn::custom_keyword!(blake2_128);
|
||||
syn::custom_keyword!(twox_256);
|
||||
syn::custom_keyword!(twox_128);
|
||||
syn::custom_keyword!(twox_64_concat);
|
||||
syn::custom_keyword!(hasher);
|
||||
}
|
||||
|
||||
/// Parsing usage only
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct StorageDefinition {
|
||||
pub hidden_crate: ext::Opt<SpecificHiddenCrate>,
|
||||
pub visibility: syn::Visibility,
|
||||
pub trait_token: Token![trait],
|
||||
pub ident: Ident,
|
||||
pub for_token: Token![for],
|
||||
pub module_ident: Ident,
|
||||
pub mod_lt_token: Token![<],
|
||||
pub mod_param_generic: syn::Ident,
|
||||
pub mod_param_bound_token: Option<Token![:]>,
|
||||
pub mod_param_bound: syn::Path,
|
||||
pub mod_instance_param_token: Option<Token![,]>,
|
||||
pub mod_instance: Option<syn::Ident>,
|
||||
pub mod_instantiable_token: Option<Token![:]>,
|
||||
pub mod_instantiable: Option<syn::Ident>,
|
||||
pub mod_default_instance_token: Option<Token![=]>,
|
||||
pub mod_default_instance: Option<syn::Ident>,
|
||||
pub mod_gt_token: Token![>],
|
||||
pub as_token: Token![as],
|
||||
pub crate_ident: Ident,
|
||||
pub where_clause: Option<syn::WhereClause>,
|
||||
pub content: ext::Braces<ext::Punctuated<DeclStorageLine, Token![;]>>,
|
||||
pub extra_genesis: ext::Opt<AddExtraGenesis>,
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct SpecificHiddenCrate {
|
||||
pub keyword: keyword::hiddencrate,
|
||||
pub ident: ext::Parens<Ident>,
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct AddExtraGenesis {
|
||||
pub extragenesis_keyword: keyword::add_extra_genesis,
|
||||
pub content: ext::Braces<AddExtraGenesisContent>,
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct AddExtraGenesisContent {
|
||||
pub lines: ext::Punctuated<AddExtraGenesisLineEnum, Token![;]>,
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
enum AddExtraGenesisLineEnum {
|
||||
AddExtraGenesisLine(AddExtraGenesisLine),
|
||||
AddExtraGenesisBuild(DeclStorageBuild),
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct AddExtraGenesisLine {
|
||||
pub attrs: ext::OuterAttributes,
|
||||
pub config_keyword: keyword::config,
|
||||
pub extra_field: ext::Parens<Ident>,
|
||||
pub coldot_token: Token![:],
|
||||
pub extra_type: syn::Type,
|
||||
pub default_value: ext::Opt<DeclStorageDefault>,
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct DeclStorageLine {
|
||||
// attrs (main use case is doc)
|
||||
pub attrs: ext::OuterAttributes,
|
||||
// visibility (no need to make optional
|
||||
pub visibility: syn::Visibility,
|
||||
// name
|
||||
pub name: Ident,
|
||||
pub getter: ext::Opt<DeclStorageGetter>,
|
||||
pub config: ext::Opt<DeclStorageConfig>,
|
||||
pub build: ext::Opt<DeclStorageBuild>,
|
||||
pub coldot_token: Token![:],
|
||||
pub storage_type: DeclStorageType,
|
||||
pub default_value: ext::Opt<DeclStorageDefault>,
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct DeclStorageGetterBody {
|
||||
fn_keyword: Option<Token![fn]>,
|
||||
ident: Ident,
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct DeclStorageGetter {
|
||||
pub getter_keyword: keyword::get,
|
||||
pub getfn: ext::Parens<DeclStorageGetterBody>,
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct DeclStorageConfig {
|
||||
pub config_keyword: keyword::config,
|
||||
pub expr: ext::Parens<Option<syn::Ident>>,
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct DeclStorageBuild {
|
||||
pub build_keyword: keyword::build,
|
||||
pub expr: ext::Parens<syn::Expr>,
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
enum DeclStorageType {
|
||||
Map(DeclStorageMap),
|
||||
LinkedMap(DeclStorageLinkedMap),
|
||||
DoubleMap(DeclStorageDoubleMap),
|
||||
Simple(syn::Type),
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct DeclStorageMap {
|
||||
pub map_keyword: keyword::map,
|
||||
pub hasher: ext::Opt<SetHasher>,
|
||||
pub key: syn::Type,
|
||||
pub ass_keyword: Token![=>],
|
||||
pub value: syn::Type,
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct DeclStorageLinkedMap {
|
||||
pub map_keyword: keyword::linked_map,
|
||||
pub hasher: ext::Opt<SetHasher>,
|
||||
pub key: syn::Type,
|
||||
pub ass_keyword: Token![=>],
|
||||
pub value: syn::Type,
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct DeclStorageDoubleMap {
|
||||
pub map_keyword: keyword::double_map,
|
||||
pub hasher: ext::Opt<SetHasher>,
|
||||
pub key1: syn::Type,
|
||||
pub comma_keyword: Token![,],
|
||||
pub key2_hasher: Hasher,
|
||||
pub key2: ext::Parens<syn::Type>,
|
||||
pub ass_keyword: Token![=>],
|
||||
pub value: syn::Type,
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
enum Hasher {
|
||||
Blake2_256(keyword::blake2_256),
|
||||
Blake2_128(keyword::blake2_128),
|
||||
Twox256(keyword::twox_256),
|
||||
Twox128(keyword::twox_128),
|
||||
Twox64Concat(keyword::twox_64_concat),
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct DeclStorageDefault {
|
||||
pub equal_token: Token![=],
|
||||
pub expr: syn::Expr,
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct SetHasher {
|
||||
pub hasher_keyword: keyword::hasher,
|
||||
pub inner: ext::Parens<Hasher>,
|
||||
}
|
||||
|
||||
impl From<SetHasher> for super::HasherKind {
|
||||
fn from(set_hasher: SetHasher) -> Self {
|
||||
set_hasher.inner.content.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Hasher> for super::HasherKind {
|
||||
fn from(hasher: Hasher) -> Self {
|
||||
match hasher {
|
||||
Hasher::Blake2_256(_) => super::HasherKind::Blake2_256,
|
||||
Hasher::Blake2_128(_) => super::HasherKind::Blake2_128,
|
||||
Hasher::Twox256(_) => super::HasherKind::Twox256,
|
||||
Hasher::Twox128(_) => super::HasherKind::Twox128,
|
||||
Hasher::Twox64Concat(_) => super::HasherKind::Twox64Concat,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_module_instance(
|
||||
instance: Option<syn::Ident>,
|
||||
instantiable: Option<syn::Ident>,
|
||||
default_instance: Option<syn::Ident>,
|
||||
) -> syn::Result<Option<super::ModuleInstanceDef>> {
|
||||
let right_syntax = "Should be $Instance: $Instantiable = $DefaultInstance";
|
||||
|
||||
match (instance, instantiable, default_instance) {
|
||||
(Some(instance), Some(instantiable), default_instance) => {
|
||||
Ok(Some(super::ModuleInstanceDef {
|
||||
instance_generic: instance,
|
||||
instance_trait: instantiable,
|
||||
instance_default: default_instance,
|
||||
}))
|
||||
},
|
||||
(None, None, None) => Ok(None),
|
||||
(Some(instance), None, _) => Err(
|
||||
syn::Error::new(
|
||||
instance.span(),
|
||||
format!(
|
||||
"Expect instantiable trait bound for instance: {}. {}",
|
||||
instance,
|
||||
right_syntax,
|
||||
)
|
||||
)
|
||||
),
|
||||
(None, Some(instantiable), _) => Err(
|
||||
syn::Error::new(
|
||||
instantiable.span(),
|
||||
format!(
|
||||
"Expect instance generic for bound instantiable: {}. {}",
|
||||
instantiable,
|
||||
right_syntax,
|
||||
)
|
||||
)
|
||||
),
|
||||
(None, _, Some(default_instance)) => Err(
|
||||
syn::Error::new(
|
||||
default_instance.span(),
|
||||
format!(
|
||||
"Expect instance generic for default instance: {}. {}",
|
||||
default_instance,
|
||||
right_syntax,
|
||||
)
|
||||
)
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(input: syn::parse::ParseStream) -> syn::Result<super::DeclStorageDef> {
|
||||
use syn::parse::Parse;
|
||||
|
||||
let def = StorageDefinition::parse(input)?;
|
||||
|
||||
let module_instance = get_module_instance(
|
||||
def.mod_instance,
|
||||
def.mod_instantiable,
|
||||
def.mod_default_instance,
|
||||
)?;
|
||||
|
||||
let mut extra_genesis_config_lines = vec![];
|
||||
let mut extra_genesis_build = None;
|
||||
|
||||
for line in def.extra_genesis.inner.into_iter()
|
||||
.flat_map(|o| o.content.content.lines.inner.into_iter())
|
||||
{
|
||||
match line {
|
||||
AddExtraGenesisLineEnum::AddExtraGenesisLine(def) => {
|
||||
extra_genesis_config_lines.push(super::ExtraGenesisLineDef{
|
||||
attrs: def.attrs.inner,
|
||||
name: def.extra_field.content,
|
||||
typ: def.extra_type,
|
||||
default: def.default_value.inner.map(|o| o.expr),
|
||||
});
|
||||
}
|
||||
AddExtraGenesisLineEnum::AddExtraGenesisBuild(def) => {
|
||||
if extra_genesis_build.is_some() {
|
||||
return Err(syn::Error::new(
|
||||
def.span(),
|
||||
"Only one build expression allowed for extra genesis"
|
||||
))
|
||||
}
|
||||
|
||||
extra_genesis_build = Some(def.expr.content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let storage_lines = parse_storage_line_defs(def.content.content.inner.into_iter())?;
|
||||
|
||||
Ok(super::DeclStorageDef {
|
||||
hidden_crate: def.hidden_crate.inner.map(|i| i.ident.content),
|
||||
visibility: def.visibility,
|
||||
module_name: def.module_ident,
|
||||
store_trait: def.ident,
|
||||
module_runtime_generic: def.mod_param_generic,
|
||||
module_runtime_trait: def.mod_param_bound,
|
||||
where_clause: def.where_clause,
|
||||
crate_name: def.crate_ident,
|
||||
module_instance,
|
||||
extra_genesis_build,
|
||||
extra_genesis_config_lines,
|
||||
storage_lines,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse the `DeclStorageLine` into `StorageLineDef`.
|
||||
fn parse_storage_line_defs(
|
||||
defs: impl Iterator<Item = DeclStorageLine>,
|
||||
) -> syn::Result<Vec<super::StorageLineDef>> {
|
||||
let mut storage_lines = Vec::<super::StorageLineDef>::new();
|
||||
|
||||
for line in defs {
|
||||
let getter = line.getter.inner.map(|o| o.getfn.content.ident);
|
||||
let config = if let Some(config) = line.config.inner {
|
||||
if let Some(ident) = config.expr.content {
|
||||
Some(ident)
|
||||
} else if let Some(ref ident) = getter {
|
||||
Some(ident.clone())
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
config.span(),
|
||||
"Invalid storage definition, couldn't find config identifier: storage must \
|
||||
either have a get identifier `get(fn ident)` or a defined config identifier \
|
||||
`config(ident)`",
|
||||
))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(ref config) = config {
|
||||
storage_lines.iter().filter_map(|sl| sl.config.as_ref()).try_for_each(|other_config| {
|
||||
if other_config == config {
|
||||
Err(syn::Error::new(
|
||||
config.span(),
|
||||
"`config()`/`get()` with the same name already defined.",
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
})?;
|
||||
}
|
||||
|
||||
let storage_type = match line.storage_type {
|
||||
DeclStorageType::Map(map) => super::StorageLineTypeDef::Map(
|
||||
super::MapDef {
|
||||
hasher: map.hasher.inner.map(Into::into)
|
||||
.unwrap_or(super::HasherKind::Blake2_256),
|
||||
key: map.key,
|
||||
value: map.value,
|
||||
}
|
||||
),
|
||||
DeclStorageType::LinkedMap(map) => super::StorageLineTypeDef::LinkedMap(
|
||||
super::MapDef {
|
||||
hasher: map.hasher.inner.map(Into::into)
|
||||
.unwrap_or(super::HasherKind::Blake2_256),
|
||||
key: map.key,
|
||||
value: map.value,
|
||||
}
|
||||
),
|
||||
DeclStorageType::DoubleMap(map) => super::StorageLineTypeDef::DoubleMap(
|
||||
super::DoubleMapDef {
|
||||
hasher1: map.hasher.inner.map(Into::into)
|
||||
.unwrap_or(super::HasherKind::Blake2_256),
|
||||
hasher2: map.key2_hasher.into(),
|
||||
key1: map.key1,
|
||||
key2: map.key2.content,
|
||||
value: map.value,
|
||||
}
|
||||
),
|
||||
DeclStorageType::Simple(expr) => super::StorageLineTypeDef::Simple(expr),
|
||||
};
|
||||
|
||||
storage_lines.push(super::StorageLineDef {
|
||||
attrs: line.attrs.inner,
|
||||
visibility: line.visibility,
|
||||
name: line.name,
|
||||
getter,
|
||||
config,
|
||||
build: line.build.inner.map(|o| o.expr.content),
|
||||
default_value: line.default_value.inner.map(|o| o.expr),
|
||||
storage_type,
|
||||
})
|
||||
}
|
||||
|
||||
Ok(storage_lines)
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Implementation of storage structures and implementation of storage traits on them.
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use super::{
|
||||
DeclStorageDefExt, StorageLineTypeDef,
|
||||
instance_trait::{PREFIX_FOR, HEAD_KEY_FOR},
|
||||
};
|
||||
|
||||
fn from_optional_value_to_query(is_option: bool, default: &Option<syn::Expr>) -> TokenStream {
|
||||
let default = default.as_ref().map(|d| quote!( #d ))
|
||||
.unwrap_or_else(|| quote!( Default::default() ));
|
||||
|
||||
if !is_option {
|
||||
// raw type case
|
||||
quote!( v.unwrap_or_else(|| #default ) )
|
||||
} else {
|
||||
// Option<> type case
|
||||
quote!( v.or_else(|| #default ) )
|
||||
}
|
||||
}
|
||||
|
||||
fn from_query_to_optional_value(is_option: bool) -> TokenStream {
|
||||
if !is_option {
|
||||
// raw type case
|
||||
quote!( Some(v) )
|
||||
} else {
|
||||
// Option<> type case
|
||||
quote!( v )
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decl_and_impl(scrate: &TokenStream, def: &DeclStorageDefExt) -> TokenStream {
|
||||
let mut impls = TokenStream::new();
|
||||
|
||||
for line in &def.storage_lines {
|
||||
|
||||
// Propagate doc attributes.
|
||||
let attrs = &line.doc_attrs;
|
||||
|
||||
let visibility = &line.visibility;
|
||||
let optional_storage_runtime_comma = &line.optional_storage_runtime_comma;
|
||||
let optional_storage_runtime_bound_comma = &line.optional_storage_runtime_bound_comma;
|
||||
let optional_storage_where_clause = &line.optional_storage_where_clause;
|
||||
let optional_instance_bound_optional_default = &def.optional_instance_bound_optional_default;
|
||||
let optional_instance_bound = &def.optional_instance_bound;
|
||||
let optional_instance = &def.optional_instance;
|
||||
let name = &line.name;
|
||||
|
||||
let struct_decl = quote!(
|
||||
#( #[ #attrs ] )*
|
||||
#visibility struct #name<
|
||||
#optional_storage_runtime_bound_comma #optional_instance_bound_optional_default
|
||||
>(
|
||||
#scrate::rstd::marker::PhantomData<
|
||||
(#optional_storage_runtime_comma #optional_instance)
|
||||
>
|
||||
) #optional_storage_where_clause;
|
||||
);
|
||||
|
||||
let from_query_to_optional_value = from_query_to_optional_value(line.is_option);
|
||||
let from_optional_value_to_query =
|
||||
from_optional_value_to_query(line.is_option, &line.default_value);
|
||||
|
||||
let final_prefix = if let Some(instance) = def.module_instance.as_ref() {
|
||||
let instance = &instance.instance_generic;
|
||||
let const_name = syn::Ident::new(
|
||||
&format!("{}{}", PREFIX_FOR, line.name.to_string()), proc_macro2::Span::call_site()
|
||||
);
|
||||
quote!( #instance::#const_name.as_bytes() )
|
||||
} else {
|
||||
let prefix = format!("{} {}", def.crate_name, line.name);
|
||||
quote!( #prefix.as_bytes() )
|
||||
};
|
||||
|
||||
|
||||
let storage_generator_trait = &line.storage_generator_trait;
|
||||
let storage_struct = &line.storage_struct;
|
||||
let impl_trait = quote!( #optional_storage_runtime_bound_comma #optional_instance_bound );
|
||||
let value_type = &line.value_type;
|
||||
let query_type = &line.query_type;
|
||||
|
||||
let struct_impl = match &line.storage_type {
|
||||
StorageLineTypeDef::Simple(_) => {
|
||||
quote!(
|
||||
impl<#impl_trait> #scrate::#storage_generator_trait for #storage_struct
|
||||
#optional_storage_where_clause
|
||||
{
|
||||
type Query = #query_type;
|
||||
|
||||
fn unhashed_key() -> &'static [u8] {
|
||||
#final_prefix
|
||||
}
|
||||
|
||||
fn from_optional_value_to_query(v: Option<#value_type>) -> Self::Query {
|
||||
#from_optional_value_to_query
|
||||
}
|
||||
|
||||
fn from_query_to_optional_value(v: Self::Query) -> Option<#value_type> {
|
||||
#from_query_to_optional_value
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
StorageLineTypeDef::Map(map) => {
|
||||
let hasher = map.hasher.to_storage_hasher_struct();
|
||||
quote!(
|
||||
impl<#impl_trait> #scrate::#storage_generator_trait for #storage_struct
|
||||
#optional_storage_where_clause
|
||||
{
|
||||
type Query = #query_type;
|
||||
type Hasher = #scrate::#hasher;
|
||||
|
||||
fn prefix() -> &'static [u8] {
|
||||
#final_prefix
|
||||
}
|
||||
|
||||
fn from_optional_value_to_query(v: Option<#value_type>) -> Self::Query {
|
||||
#from_optional_value_to_query
|
||||
}
|
||||
|
||||
fn from_query_to_optional_value(v: Self::Query) -> Option<#value_type> {
|
||||
#from_query_to_optional_value
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
StorageLineTypeDef::LinkedMap(map) => {
|
||||
let hasher = map.hasher.to_storage_hasher_struct();
|
||||
|
||||
// make sure to use different prefix for head and elements.
|
||||
let head_key = if let Some(instance) = def.module_instance.as_ref() {
|
||||
let instance = &instance.instance_generic;
|
||||
let const_name = syn::Ident::new(
|
||||
&format!("{}{}", HEAD_KEY_FOR, line.name.to_string()), proc_macro2::Span::call_site()
|
||||
);
|
||||
quote!( #instance::#const_name.as_bytes() )
|
||||
} else {
|
||||
let prefix = format!("head of {} {}", def.crate_name, line.name);
|
||||
quote!( #prefix.as_bytes() )
|
||||
};
|
||||
|
||||
quote!(
|
||||
impl<#impl_trait> #scrate::#storage_generator_trait for #storage_struct
|
||||
#optional_storage_where_clause
|
||||
{
|
||||
type Query = #query_type;
|
||||
type Hasher = #scrate::#hasher;
|
||||
type KeyFormat = Self;
|
||||
|
||||
fn prefix() -> &'static [u8] {
|
||||
#final_prefix
|
||||
}
|
||||
|
||||
fn from_optional_value_to_query(v: Option<#value_type>) -> Self::Query {
|
||||
#from_optional_value_to_query
|
||||
}
|
||||
|
||||
fn from_query_to_optional_value(v: Self::Query) -> Option<#value_type> {
|
||||
#from_query_to_optional_value
|
||||
}
|
||||
}
|
||||
|
||||
impl<#impl_trait> #scrate::storage::generator::LinkedMapKeyFormat for #storage_struct {
|
||||
type Hasher = #scrate::#hasher;
|
||||
|
||||
fn head_key() -> &'static [u8] {
|
||||
#head_key
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
StorageLineTypeDef::DoubleMap(map) => {
|
||||
let hasher1 = map.hasher1.to_storage_hasher_struct();
|
||||
let hasher2 = map.hasher2.to_storage_hasher_struct();
|
||||
quote!(
|
||||
impl<#impl_trait> #scrate::#storage_generator_trait for #storage_struct
|
||||
#optional_storage_where_clause
|
||||
{
|
||||
type Query = #query_type;
|
||||
|
||||
type Hasher1 = #scrate::#hasher1;
|
||||
|
||||
type Hasher2 = #scrate::#hasher2;
|
||||
|
||||
fn key1_prefix() -> &'static [u8] {
|
||||
#final_prefix
|
||||
}
|
||||
|
||||
fn from_optional_value_to_query(v: Option<#value_type>) -> Self::Query {
|
||||
#from_optional_value_to_query
|
||||
}
|
||||
|
||||
fn from_query_to_optional_value(v: Self::Query) -> Option<#value_type> {
|
||||
#from_query_to_optional_value
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
impls.extend(quote!(
|
||||
#struct_decl
|
||||
#struct_impl
|
||||
))
|
||||
}
|
||||
|
||||
impls
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Declaration of store trait and implementation on module structure.
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use super::DeclStorageDefExt;
|
||||
|
||||
pub fn decl_and_impl(def: &DeclStorageDefExt) -> TokenStream {
|
||||
let decl_store_items = def.storage_lines.iter()
|
||||
.map(|sline| &sline.name)
|
||||
.fold(TokenStream::new(), |mut items, name| {
|
||||
items.extend(quote!(type #name;));
|
||||
items
|
||||
});
|
||||
|
||||
let impl_store_items = def.storage_lines.iter()
|
||||
.fold(TokenStream::new(), |mut items, line| {
|
||||
let name = &line.name;
|
||||
let storage_struct = &line.storage_struct;
|
||||
|
||||
items.extend(quote!(type #name = #storage_struct;));
|
||||
items
|
||||
});
|
||||
|
||||
let visibility = &def.visibility;
|
||||
let store_trait = &def.store_trait;
|
||||
let module_struct = &def.module_struct;
|
||||
let module_impl = &def.module_impl;
|
||||
let where_clause = &def.where_clause;
|
||||
|
||||
quote!(
|
||||
#visibility trait #store_trait {
|
||||
#decl_store_items
|
||||
}
|
||||
impl#module_impl #store_trait for #module_struct #where_clause {
|
||||
#impl_store_items
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "frame-support-procedural-tools"
|
||||
version = "2.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
frame-support-procedural-tools-derive = { package = "frame-support-procedural-tools-derive", path = "./derive" }
|
||||
proc-macro2 = "1.0.6"
|
||||
quote = "1.0.2"
|
||||
syn = { version = "1.0.7", features = ["full"] }
|
||||
proc-macro-crate = "0.1.4"
|
||||
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "frame-support-procedural-tools-derive"
|
||||
version = "2.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0.6"
|
||||
quote = { version = "1.0.2", features = ["proc-macro"] }
|
||||
syn = { version = "1.0.7", features = ["proc-macro" ,"full", "extra-traits", "parsing"] }
|
||||
@@ -0,0 +1,243 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// tag::description[]
|
||||
//! Use to derive parsing for parsing struct.
|
||||
// end::description[]
|
||||
|
||||
#![recursion_limit = "128"]
|
||||
|
||||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Span;
|
||||
use syn::parse_macro_input;
|
||||
use quote::quote;
|
||||
|
||||
pub(crate) fn fields_idents(
|
||||
fields: impl Iterator<Item = syn::Field>,
|
||||
) -> impl Iterator<Item = proc_macro2::TokenStream> {
|
||||
fields.enumerate().map(|(ix, field)| {
|
||||
field.ident.clone().map(|i| quote!{#i}).unwrap_or_else(|| {
|
||||
let f_ix: syn::Ident = syn::Ident::new(&format!("f_{}", ix), Span::call_site());
|
||||
quote!( #f_ix )
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn fields_access(
|
||||
fields: impl Iterator<Item = syn::Field>,
|
||||
) -> impl Iterator<Item = proc_macro2::TokenStream> {
|
||||
fields.enumerate().map(|(ix, field)| {
|
||||
field.ident.clone().map(|i| quote!( #i )).unwrap_or_else(|| {
|
||||
let f_ix: syn::Index = syn::Index {
|
||||
index: ix as u32,
|
||||
span: Span::call_site(),
|
||||
};
|
||||
quote!( #f_ix )
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// self defined parsing struct or enum.
|
||||
/// not meant for any struct/enum, just for fast
|
||||
/// parse implementation.
|
||||
/// For enums:
|
||||
/// variant are tested in order of definition.
|
||||
/// Empty variant is always true.
|
||||
/// Please use carefully, this will fully parse successful variant twice.
|
||||
#[proc_macro_derive(Parse)]
|
||||
pub fn derive_parse(input: TokenStream) -> TokenStream {
|
||||
let item = parse_macro_input!(input as syn::Item);
|
||||
match item {
|
||||
syn::Item::Enum(input) => derive_parse_enum(input),
|
||||
syn::Item::Struct(input) => derive_parse_struct(input),
|
||||
_ => TokenStream::new(), // ignore
|
||||
}
|
||||
}
|
||||
|
||||
fn derive_parse_struct(input: syn::ItemStruct) -> TokenStream {
|
||||
let syn::ItemStruct {
|
||||
ident,
|
||||
generics,
|
||||
fields,
|
||||
..
|
||||
} = input;
|
||||
let field_names = {
|
||||
let name = fields_idents(fields.iter().map(Clone::clone));
|
||||
quote!{
|
||||
#(
|
||||
#name,
|
||||
)*
|
||||
}
|
||||
};
|
||||
let field = fields_idents(fields.iter().map(Clone::clone));
|
||||
let tokens = quote! {
|
||||
impl #generics syn::parse::Parse for #ident #generics {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
|
||||
#(
|
||||
let #field = input.parse()?;
|
||||
)*
|
||||
Ok(Self {
|
||||
#field_names
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
tokens.into()
|
||||
}
|
||||
|
||||
fn derive_parse_enum(input: syn::ItemEnum) -> TokenStream {
|
||||
let syn::ItemEnum {
|
||||
ident,
|
||||
generics,
|
||||
variants,
|
||||
..
|
||||
} = input;
|
||||
let variants = variants.iter().map(|v| {
|
||||
let variant_ident = v.ident.clone();
|
||||
let fields_build = if v.fields.iter().count() > 0 {
|
||||
let fields_id = fields_idents(v.fields.iter().map(Clone::clone));
|
||||
quote!( (#(#fields_id), *) )
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
|
||||
let fields_procs = fields_idents(v.fields.iter().map(Clone::clone))
|
||||
.map(|fident| {
|
||||
quote!{
|
||||
let mut #fident = match fork.parse() {
|
||||
Ok(r) => r,
|
||||
Err(_e) => break,
|
||||
};
|
||||
}
|
||||
});
|
||||
let fields_procs_again = fields_idents(v.fields.iter().map(Clone::clone))
|
||||
.map(|fident| {
|
||||
quote!{
|
||||
#fident = input.parse().expect("was parsed just before");
|
||||
}
|
||||
});
|
||||
|
||||
// double parse to update input cursor position
|
||||
// next syn crate version should be checked for a way
|
||||
// to copy position/state from a fork
|
||||
quote!{
|
||||
let mut fork = input.fork();
|
||||
loop {
|
||||
#(#fields_procs)*
|
||||
#(#fields_procs_again)*
|
||||
return Ok(#ident::#variant_ident#fields_build);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let tokens = quote! {
|
||||
impl #generics syn::parse::Parse for #ident #generics {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
|
||||
#(
|
||||
#variants
|
||||
)*
|
||||
// no early return from any variants
|
||||
Err(
|
||||
syn::parse::Error::new(
|
||||
proc_macro2::Span::call_site(),
|
||||
"derived enum no matching variants"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
tokens.into()
|
||||
}
|
||||
|
||||
/// self defined parsing struct or enum.
|
||||
/// not meant for any struct/enum, just for fast
|
||||
/// parse implementation.
|
||||
/// For enum:
|
||||
/// it only output fields (empty field act as a None).
|
||||
#[proc_macro_derive(ToTokens)]
|
||||
pub fn derive_totokens(input: TokenStream) -> TokenStream {
|
||||
let item = parse_macro_input!(input as syn::Item);
|
||||
match item {
|
||||
syn::Item::Enum(input) => derive_totokens_enum(input),
|
||||
syn::Item::Struct(input) => derive_totokens_struct(input),
|
||||
_ => TokenStream::new(), // ignore
|
||||
}
|
||||
}
|
||||
|
||||
fn derive_totokens_struct(input: syn::ItemStruct) -> TokenStream {
|
||||
let syn::ItemStruct {
|
||||
ident,
|
||||
generics,
|
||||
fields,
|
||||
..
|
||||
} = input;
|
||||
|
||||
let fields = fields_access(fields.iter().map(Clone::clone));
|
||||
let tokens = quote! {
|
||||
|
||||
impl #generics quote::ToTokens for #ident #generics {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
#(
|
||||
self.#fields.to_tokens(tokens);
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
tokens.into()
|
||||
}
|
||||
|
||||
fn derive_totokens_enum(input: syn::ItemEnum) -> TokenStream {
|
||||
let syn::ItemEnum {
|
||||
ident,
|
||||
generics,
|
||||
variants,
|
||||
..
|
||||
} = input;
|
||||
let variants = variants.iter().map(|v| {
|
||||
let v_ident = v.ident.clone();
|
||||
let fields_build = if v.fields.iter().count() > 0 {
|
||||
let fields_id = fields_idents(v.fields.iter().map(Clone::clone));
|
||||
quote!( (#(#fields_id), *) )
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
let field = fields_idents(v.fields.iter().map(Clone::clone));
|
||||
quote! {
|
||||
#ident::#v_ident#fields_build => {
|
||||
#(
|
||||
#field.to_tokens(tokens);
|
||||
)*
|
||||
},
|
||||
}
|
||||
});
|
||||
let tokens = quote! {
|
||||
impl #generics quote::ToTokens for #ident #generics {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
match self {
|
||||
#(
|
||||
#variants
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
tokens.into()
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// tag::description[]
|
||||
//! Proc macro helpers for procedural macros
|
||||
// end::description[]
|
||||
|
||||
// reexport proc macros
|
||||
pub use frame_support_procedural_tools_derive::*;
|
||||
|
||||
use proc_macro_crate::crate_name;
|
||||
use syn::parse::Error;
|
||||
use quote::quote;
|
||||
|
||||
pub mod syn_ext;
|
||||
|
||||
// FIXME #1569, remove the following functions, which are copied from sr-api-macros
|
||||
use proc_macro2::{TokenStream, Span};
|
||||
use syn::Ident;
|
||||
|
||||
fn generate_hidden_includes_mod_name(unique_id: &str) -> Ident {
|
||||
Ident::new(&format!("sr_api_hidden_includes_{}", unique_id), Span::call_site())
|
||||
}
|
||||
|
||||
/// Generates the access to the `frame-support` crate.
|
||||
pub fn generate_crate_access(unique_id: &str, def_crate: &str) -> TokenStream {
|
||||
if std::env::var("CARGO_PKG_NAME").unwrap() == def_crate {
|
||||
quote::quote!( frame_support )
|
||||
} else {
|
||||
let mod_name = generate_hidden_includes_mod_name(unique_id);
|
||||
quote::quote!( self::#mod_name::hidden_include )
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the hidden includes that are required to make the macro independent from its scope.
|
||||
pub fn generate_hidden_includes(unique_id: &str, def_crate: &str) -> TokenStream {
|
||||
if ::std::env::var("CARGO_PKG_NAME").unwrap() == def_crate {
|
||||
TokenStream::new()
|
||||
} else {
|
||||
let mod_name = generate_hidden_includes_mod_name(unique_id);
|
||||
|
||||
match crate_name(def_crate) {
|
||||
Ok(name) => {
|
||||
let name = Ident::new(&name, Span::call_site());
|
||||
quote::quote!(
|
||||
#[doc(hidden)]
|
||||
mod #mod_name {
|
||||
pub extern crate #name as hidden_include;
|
||||
}
|
||||
)
|
||||
},
|
||||
Err(e) => {
|
||||
let err = Error::new(Span::call_site(), &e).to_compile_error();
|
||||
quote!( #err )
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// fn to remove white spaces around string types
|
||||
// (basically whitespaces around tokens)
|
||||
pub fn clean_type_string(input: &str) -> String {
|
||||
input
|
||||
.replace(" ::", "::")
|
||||
.replace(":: ", "::")
|
||||
.replace(" ,", ",")
|
||||
.replace(" ;", ";")
|
||||
.replace(" [", "[")
|
||||
.replace("[ ", "[")
|
||||
.replace(" ]", "]")
|
||||
.replace(" (", "(")
|
||||
.replace("( ", "(")
|
||||
.replace(" )", ")")
|
||||
.replace(" <", "<")
|
||||
.replace("< ", "<")
|
||||
.replace(" >", ">")
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// tag::description[]
|
||||
//! Extension to syn types, mainly for parsing
|
||||
// end::description[]
|
||||
|
||||
use syn::{visit::{Visit, self}, parse::{Parse, ParseStream, Result}, Ident};
|
||||
use proc_macro2::{TokenStream, TokenTree};
|
||||
use quote::ToTokens;
|
||||
use std::iter::once;
|
||||
use frame_support_procedural_tools_derive::{ToTokens, Parse};
|
||||
|
||||
/// stop parsing here getting remaining token as content
|
||||
/// Warn duplicate stream (part of)
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
pub struct StopParse {
|
||||
pub inner: TokenStream,
|
||||
}
|
||||
|
||||
// inner macro really dependant on syn naming convention, do not export
|
||||
macro_rules! groups_impl {
|
||||
($name:ident, $tok:ident, $deli:ident, $parse:ident) => {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct $name<P> {
|
||||
pub token: syn::token::$tok,
|
||||
pub content: P,
|
||||
}
|
||||
|
||||
impl<P: Parse> Parse for $name<P> {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let syn::group::$name { token, content } = syn::group::$parse(input)?;
|
||||
let content = content.parse()?;
|
||||
Ok($name { token, content, })
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: ToTokens> ToTokens for $name<P> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let mut inner_stream = TokenStream::new();
|
||||
self.content.to_tokens(&mut inner_stream);
|
||||
let token_tree: proc_macro2::TokenTree =
|
||||
proc_macro2::Group::new(proc_macro2::Delimiter::$deli, inner_stream).into();
|
||||
tokens.extend(once(token_tree));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
groups_impl!(Braces, Brace, Brace, parse_braces);
|
||||
groups_impl!(Brackets, Bracket, Bracket, parse_brackets);
|
||||
groups_impl!(Parens, Paren, Parenthesis, parse_parens);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PunctuatedInner<P,T,V> {
|
||||
pub inner: syn::punctuated::Punctuated<P,T>,
|
||||
pub variant: V,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NoTrailing;
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Trailing;
|
||||
|
||||
pub type Punctuated<P,T> = PunctuatedInner<P,T,NoTrailing>;
|
||||
|
||||
pub type PunctuatedTrailing<P,T> = PunctuatedInner<P,T,Trailing>;
|
||||
|
||||
impl<P: Parse, T: Parse + syn::token::Token> Parse for PunctuatedInner<P,T,Trailing> {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
Ok(PunctuatedInner {
|
||||
inner: syn::punctuated::Punctuated::parse_separated_nonempty(input)?,
|
||||
variant: Trailing,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Parse, T: Parse> Parse for PunctuatedInner<P,T,NoTrailing> {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
Ok(PunctuatedInner {
|
||||
inner: syn::punctuated::Punctuated::parse_terminated(input)?,
|
||||
variant: NoTrailing,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: ToTokens, T: ToTokens, V> ToTokens for PunctuatedInner<P,T,V> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
self.inner.to_tokens(tokens)
|
||||
}
|
||||
}
|
||||
|
||||
/// Note that syn Meta is almost fine for use case (lacks only `ToToken`)
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Meta {
|
||||
pub inner: syn::Meta,
|
||||
}
|
||||
|
||||
impl Parse for Meta {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
Ok(Meta {
|
||||
inner: syn::Meta::parse(input)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Meta {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self.inner {
|
||||
syn::Meta::Path(ref path) => path.to_tokens(tokens),
|
||||
syn::Meta::List(ref l) => l.to_tokens(tokens),
|
||||
syn::Meta::NameValue(ref n) => n.to_tokens(tokens),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OuterAttributes {
|
||||
pub inner: Vec<syn::Attribute>,
|
||||
}
|
||||
|
||||
impl Parse for OuterAttributes {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let inner = syn::Attribute::parse_outer(input)?;
|
||||
Ok(OuterAttributes {
|
||||
inner,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for OuterAttributes {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
for att in self.inner.iter() {
|
||||
att.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Opt<P> {
|
||||
pub inner: Option<P>,
|
||||
}
|
||||
|
||||
impl<P: Parse> Parse for Opt<P> {
|
||||
// Note that it cost a double parsing (same as enum derive)
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let inner = match input.fork().parse::<P>() {
|
||||
Ok(_item) => Some(input.parse().expect("Same parsing ran before")),
|
||||
Err(_e) => None,
|
||||
};
|
||||
|
||||
Ok(Opt { inner })
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: ToTokens> ToTokens for Opt<P> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
if let Some(ref p) = self.inner {
|
||||
p.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_type_option(typ: &syn::Type) -> Option<syn::Type> {
|
||||
if let syn::Type::Path(ref path) = typ {
|
||||
let v = path.path.segments.last()?;
|
||||
if v.ident == "Option" {
|
||||
// Option has only one type argument in angle bracket.
|
||||
if let syn::PathArguments::AngleBracketed(a) = &v.arguments {
|
||||
if let syn::GenericArgument::Type(typ) = a.args.last()? {
|
||||
return Some(typ.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Auxialary structure to check if a given `Ident` is contained in an ast.
|
||||
struct ContainsIdent<'a> {
|
||||
ident: &'a Ident,
|
||||
result: bool,
|
||||
}
|
||||
|
||||
impl<'ast> ContainsIdent<'ast> {
|
||||
fn visit_tokenstream(&mut self, stream: TokenStream) {
|
||||
stream.into_iter().for_each(|tt|
|
||||
match tt {
|
||||
TokenTree::Ident(id) => self.visit_ident(&id),
|
||||
TokenTree::Group(ref group) => self.visit_tokenstream(group.stream()),
|
||||
_ => {}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn visit_ident(&mut self, ident: &Ident) {
|
||||
if ident == self.ident {
|
||||
self.result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> Visit<'ast> for ContainsIdent<'ast> {
|
||||
fn visit_ident(&mut self, input: &'ast Ident) {
|
||||
self.visit_ident(input);
|
||||
}
|
||||
|
||||
fn visit_macro(&mut self, input: &'ast syn::Macro) {
|
||||
self.visit_tokenstream(input.tokens.clone());
|
||||
visit::visit_macro(self, input);
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if a `Type` contains the given `Ident`.
|
||||
pub fn type_contains_ident(typ: &syn::Type, ident: &Ident) -> bool {
|
||||
let mut visit = ContainsIdent {
|
||||
result: false,
|
||||
ident,
|
||||
};
|
||||
|
||||
visit::visit_type(&mut visit, typ);
|
||||
visit.result
|
||||
}
|
||||
|
||||
/// Check if a `Expr` contains the given `Ident`.
|
||||
pub fn expr_contains_ident(expr: &syn::Expr, ident: &Ident) -> bool {
|
||||
let mut visit = ContainsIdent {
|
||||
result: false,
|
||||
ident,
|
||||
};
|
||||
|
||||
visit::visit_expr(&mut visit, expr);
|
||||
visit.result
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "frame-support-rpc"
|
||||
version = "2.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>", "Andrew Dirksen <andrew@dirksen.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
futures = { version = "0.3.0", features = ["compat"] }
|
||||
jsonrpc-client-transports = "14"
|
||||
jsonrpc-core = "14"
|
||||
parity-scale-codec = "1"
|
||||
serde = "1"
|
||||
frame-support = { path = "../" }
|
||||
substrate-primitives-storage = { path = "../../../primitives/core/storage" }
|
||||
substrate-rpc-api = { path = "../../../client/rpc/api" }
|
||||
|
||||
[dev-dependencies]
|
||||
frame-system = { path = "../../system" }
|
||||
tokio = "0.1"
|
||||
@@ -0,0 +1,155 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Combines [substrate_rpc_api::state::StateClient] with [frame_support::storage::generator] traits
|
||||
//! to provide strongly typed chain state queries over rpc.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use futures::compat::Future01CompatExt;
|
||||
use jsonrpc_client_transports::RpcError;
|
||||
use parity_scale_codec::{DecodeAll, FullCodec, FullEncode};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use frame_support::storage::generator::{
|
||||
StorageDoubleMap, StorageLinkedMap, StorageMap, StorageValue
|
||||
};
|
||||
use substrate_primitives_storage::{StorageData, StorageKey};
|
||||
use substrate_rpc_api::state::StateClient;
|
||||
|
||||
/// A typed query on chain state usable from an RPC client.
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use futures::compat::Compat;
|
||||
/// # use futures::compat::Future01CompatExt;
|
||||
/// # use futures::future::FutureExt;
|
||||
/// # use jsonrpc_client_transports::RpcError;
|
||||
/// # use jsonrpc_client_transports::transports::http;
|
||||
/// # use parity_scale_codec::Encode;
|
||||
/// # use frame_support::{decl_storage, decl_module};
|
||||
/// # use frame_support_rpc::StorageQuery;
|
||||
/// # use frame_system::Trait;
|
||||
/// # use substrate_rpc_api::state::StateClient;
|
||||
/// #
|
||||
/// # // Hash would normally be <TestRuntime as frame_system::Trait>::Hash, but we don't have
|
||||
/// # // frame_system::Trait implemented for TestRuntime. Here we just pretend.
|
||||
/// # type Hash = ();
|
||||
/// #
|
||||
/// # fn main() -> Result<(), RpcError> {
|
||||
/// # tokio::runtime::Runtime::new().unwrap().block_on(Compat::new(test().boxed()))
|
||||
/// # }
|
||||
/// #
|
||||
/// # struct TestRuntime;
|
||||
/// #
|
||||
/// # decl_module! {
|
||||
/// # pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
/// # }
|
||||
/// #
|
||||
/// pub type Loc = (i64, i64, i64);
|
||||
/// pub type Block = u8;
|
||||
///
|
||||
/// // Note that all fields are marked pub.
|
||||
/// decl_storage! {
|
||||
/// trait Store for Module<T: Trait> as TestRuntime {
|
||||
/// pub LastActionId: u64;
|
||||
/// pub Voxels: map Loc => Block;
|
||||
/// pub Actions: linked_map u64 => Loc;
|
||||
/// pub Prefab: double_map u128, blake2_256((i8, i8, i8)) => Block;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// # async fn test() -> Result<(), RpcError> {
|
||||
/// let conn = http::connect("http://[::1]:9933").compat().await?;
|
||||
/// let cl = StateClient::<Hash>::new(conn);
|
||||
///
|
||||
/// let q = StorageQuery::value::<LastActionId>();
|
||||
/// let _: Option<u64> = q.get(&cl, None).await?;
|
||||
///
|
||||
/// let q = StorageQuery::map::<Voxels, _>((0, 0, 0));
|
||||
/// let _: Option<Block> = q.get(&cl, None).await?;
|
||||
///
|
||||
/// let q = StorageQuery::linked_map::<Actions, _>(12);
|
||||
/// let _: Option<Loc> = q.get(&cl, None).await?;
|
||||
///
|
||||
/// let q = StorageQuery::double_map::<Prefab, _, _>(3, (0, 0, 0));
|
||||
/// let _: Option<Block> = q.get(&cl, None).await?;
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
||||
pub struct StorageQuery<V> {
|
||||
key: StorageKey,
|
||||
_spook: PhantomData<V>,
|
||||
}
|
||||
|
||||
impl<V: FullCodec> StorageQuery<V> {
|
||||
/// Create a storage query for a StorageValue.
|
||||
pub fn value<St: StorageValue<V>>() -> Self {
|
||||
Self {
|
||||
key: StorageKey(St::storage_value_final_key().to_vec()),
|
||||
_spook: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a storage query for a value in a StorageMap.
|
||||
pub fn map<St: StorageMap<K, V>, K: FullEncode>(key: K) -> Self {
|
||||
Self {
|
||||
key: StorageKey(St::storage_map_final_key(key).as_ref().to_vec()),
|
||||
_spook: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a storage query for a value in a StorageLinkedMap.
|
||||
pub fn linked_map<St: StorageLinkedMap<K, V>, K: FullCodec>(key: K) -> Self {
|
||||
Self {
|
||||
key: StorageKey(St::storage_linked_map_final_key(key).as_ref().to_vec()),
|
||||
_spook: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a storage query for a value in a StorageDoubleMap.
|
||||
pub fn double_map<St: StorageDoubleMap<K1, K2, V>, K1: FullEncode, K2: FullEncode>(
|
||||
key1: K1,
|
||||
key2: K2,
|
||||
) -> Self {
|
||||
Self {
|
||||
key: StorageKey(St::storage_double_map_final_key(key1, key2)),
|
||||
_spook: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Send this query over RPC, await the typed result.
|
||||
///
|
||||
/// Hash should be <YourRuntime as frame::Trait>::Hash.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// state_client represents a connection to the RPC server.
|
||||
///
|
||||
/// block_index indicates the block for which state will be queried. A value of None indicates
|
||||
/// the latest block.
|
||||
pub async fn get<Hash: Send + Sync + 'static + DeserializeOwned + Serialize>(
|
||||
self,
|
||||
state_client: &StateClient<Hash>,
|
||||
block_index: Option<Hash>,
|
||||
) -> Result<Option<V>, RpcError> {
|
||||
let opt: Option<StorageData> = state_client.storage(self.key, block_index).compat().await?;
|
||||
opt.map(|encoded| V::decode_all(&encoded.0))
|
||||
.transpose()
|
||||
.map_err(|decode_err| RpcError::Other(decode_err.into()))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Runtime debugging and logging utilities.
|
||||
//!
|
||||
//! This module contains macros and functions that will allow
|
||||
//! you to print logs out of the runtime code.
|
||||
//!
|
||||
//! First and foremost be aware that adding regular logging code to
|
||||
//! your runtime will have a negative effect on the performance
|
||||
//! and size of the blob. Luckily there are some ways to mitigate
|
||||
//! this that are described below.
|
||||
//!
|
||||
//! First component to utilize debug-printing and loggin is actually
|
||||
//! located in `primitives` crate: `primitives::RuntimeDebug`.
|
||||
//! This custom-derive generates `core::fmt::Debug` implementation,
|
||||
//! just like regular `derive(Debug)`, however it does not generate
|
||||
//! any code when the code is compiled to WASM. This means that
|
||||
//! you can safely sprinkle `RuntimeDebug` in your runtime codebase,
|
||||
//! without affecting the size. This also allows you to print/log
|
||||
//! both when the code is running natively or in WASM, but note
|
||||
//! that WASM debug formatting of structs will be empty.
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use frame_support::debug;
|
||||
//!
|
||||
//! #[derive(primitives::RuntimeDebug)]
|
||||
//! struct MyStruct {
|
||||
//! a: u64,
|
||||
//! }
|
||||
//!
|
||||
//! // First initialize the logger.
|
||||
//! //
|
||||
//! // This is only required when you want the logs to be printed
|
||||
//! // also during non-native run.
|
||||
//! // Note that enabling the logger has performance impact on
|
||||
//! // WASM runtime execution and should be used sparingly.
|
||||
//! debug::RuntimeLogger::init();
|
||||
//!
|
||||
//! let x = MyStruct { a: 5 };
|
||||
//! // will log an info line `"My struct: MyStruct{a:5}"` when running
|
||||
//! // natively, but will only print `"My struct: "` when running WASM.
|
||||
//! debug::info!("My struct: {:?}", x);
|
||||
//!
|
||||
//! // same output here, although this will print to stdout
|
||||
//! // (and without log format)
|
||||
//! debug::print!("My struct: {:?}", x);
|
||||
//! ```
|
||||
//!
|
||||
//! If you want to avoid extra overhead in WASM, but still be able
|
||||
//! to print / log when the code is executed natively you can use
|
||||
//! macros coming from `native` sub-module. This module enables
|
||||
//! logs conditionally and strips out logs in WASM.
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use frame_support::debug::native;
|
||||
//!
|
||||
//! #[derive(primitives::RuntimeDebug)]
|
||||
//! struct MyStruct {
|
||||
//! a: u64,
|
||||
//! }
|
||||
//!
|
||||
//! // We don't initialize the logger, since
|
||||
//! // we are not printing anything out in WASM.
|
||||
//! // debug::RuntimeLogger::init();
|
||||
//!
|
||||
//! let x = MyStruct { a: 5 };
|
||||
//!
|
||||
//! // Displays an info log when running natively, nothing when WASM.
|
||||
//! native::info!("My struct: {:?}", x);
|
||||
//!
|
||||
//! // same output to stdout, no overhead on WASM.
|
||||
//! native::print!("My struct: {:?}", x);
|
||||
//! ```
|
||||
|
||||
use rstd::vec::Vec;
|
||||
use rstd::fmt::{self, Debug};
|
||||
|
||||
pub use log::{info, debug, error, trace, warn};
|
||||
pub use crate::runtime_print as print;
|
||||
|
||||
/// Native-only logging.
|
||||
///
|
||||
/// Using any functions from this module will have any effect
|
||||
/// only if the runtime is running natively (i.e. not via WASM)
|
||||
#[cfg(feature = "std")]
|
||||
pub mod native {
|
||||
pub use super::{info, debug, error, trace, warn, print};
|
||||
}
|
||||
|
||||
/// Native-only logging.
|
||||
///
|
||||
/// Using any functions from this module will have any effect
|
||||
/// only if the runtime is running natively (i.e. not via WASM)
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub mod native {
|
||||
#[macro_export]
|
||||
macro_rules! noop {
|
||||
($($arg:tt)+) => {}
|
||||
}
|
||||
pub use noop as info;
|
||||
pub use noop as debug;
|
||||
pub use noop as error;
|
||||
pub use noop as trace;
|
||||
pub use noop as warn;
|
||||
pub use noop as print;
|
||||
}
|
||||
|
||||
/// Print out a formatted message.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// frame_support::runtime_print!("my value is {}", 3);
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! runtime_print {
|
||||
($($arg:tt)+) => {
|
||||
use core::fmt::Write;
|
||||
let mut w = $crate::debug::Writer::default();
|
||||
let _ = core::write!(&mut w, $($arg)+);
|
||||
w.print();
|
||||
}
|
||||
}
|
||||
|
||||
/// Print out the debuggable type.
|
||||
pub fn debug(data: &impl Debug) {
|
||||
runtime_print!("{:?}", data);
|
||||
}
|
||||
|
||||
/// A target for `core::write!` macro - constructs a string in memory.
|
||||
#[derive(Default)]
|
||||
pub struct Writer(Vec<u8>);
|
||||
|
||||
impl fmt::Write for Writer {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
self.0.extend(s.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Writer {
|
||||
/// Print the content of this `Writer` out.
|
||||
pub fn print(&self) {
|
||||
runtime_io::misc::print_utf8(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Runtime logger implementation - `log` crate backend.
|
||||
///
|
||||
/// The logger should be initialized if you want to display
|
||||
/// logs inside the runtime that is not necessarily running natively.
|
||||
///
|
||||
/// When runtime is executed natively any log statements are displayed
|
||||
/// even if this logger is NOT initialized.
|
||||
///
|
||||
/// Note that even though the logs are not displayed in WASM, they
|
||||
/// may still affect the size and performance of the generated runtime.
|
||||
/// To lower the footprint make sure to only use macros from `native`
|
||||
/// sub-module.
|
||||
pub struct RuntimeLogger;
|
||||
|
||||
impl RuntimeLogger {
|
||||
/// Initialize the logger.
|
||||
///
|
||||
/// This is a no-op when running natively (`std`).
|
||||
#[cfg(feature = "std")]
|
||||
pub fn init() {}
|
||||
|
||||
/// Initialize the logger.
|
||||
///
|
||||
/// This is a no-op when running natively (`std`).
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub fn init() {
|
||||
static LOGGER: RuntimeLogger = RuntimeLogger;;
|
||||
let _ = log::set_logger(&LOGGER);
|
||||
}
|
||||
}
|
||||
|
||||
impl log::Log for RuntimeLogger {
|
||||
fn enabled(&self, _metadata: &log::Metadata) -> bool {
|
||||
// to avoid calling to host twice, we pass everything
|
||||
// and let the host decide what to print.
|
||||
// If someone is initializing the logger they should
|
||||
// know what they are doing.
|
||||
true
|
||||
}
|
||||
|
||||
fn log(&self, record: &log::Record) {
|
||||
use fmt::Write;
|
||||
let mut w = Writer::default();
|
||||
let _ = core::write!(&mut w, "{}", record.args());
|
||||
|
||||
runtime_io::logging::log(
|
||||
record.level().into(),
|
||||
record.target(),
|
||||
&w.0,
|
||||
);
|
||||
}
|
||||
|
||||
fn flush(&self) {}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,166 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Macro for declaring a module error.
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use sr_primitives::traits::LookupError;
|
||||
pub use frame_metadata::{ModuleErrorMetadata, ErrorMetadata, DecodeDifferent};
|
||||
|
||||
/// Declare an error type for a runtime module.
|
||||
///
|
||||
/// The generated error type inherently has the variants `Other` and `CannotLookup`. `Other` can
|
||||
/// hold any `&'static str` error message and is present for convenience/backward compatibility.
|
||||
/// The `CannotLookup` variant indicates that some lookup could not be done. For both variants the
|
||||
/// error type implements `From<&'static str>` and `From<LookupError>` to make them usable with the
|
||||
/// try operator.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// ```
|
||||
/// # use frame_support::decl_error;
|
||||
/// decl_error! {
|
||||
/// /// Errors that can occur in my module.
|
||||
/// pub enum MyError {
|
||||
/// /// Hey this is an error message that indicates bla.
|
||||
/// MyCoolErrorMessage,
|
||||
/// /// You are just not cool enough for my module!
|
||||
/// YouAreNotCoolEnough,
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// `decl_error!` supports only variants that do not hold any data.
|
||||
#[macro_export]
|
||||
macro_rules! decl_error {
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub enum $error:ident {
|
||||
$(
|
||||
$( #[doc = $doc_attr:tt] )*
|
||||
$name:ident
|
||||
),*
|
||||
$(,)?
|
||||
}
|
||||
) => {
|
||||
#[derive(Clone, PartialEq, Eq, $crate::RuntimeDebug)]
|
||||
$(#[$attr])*
|
||||
pub enum $error {
|
||||
Other(&'static str),
|
||||
CannotLookup,
|
||||
$(
|
||||
$( #[doc = $doc_attr] )*
|
||||
$name
|
||||
),*
|
||||
}
|
||||
|
||||
impl $crate::dispatch::ModuleDispatchError for $error {
|
||||
fn as_u8(&self) -> u8 {
|
||||
$crate::decl_error! {
|
||||
@GENERATE_AS_U8
|
||||
self
|
||||
$error
|
||||
{}
|
||||
2,
|
||||
$( $name ),*
|
||||
}
|
||||
}
|
||||
|
||||
fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
$error::Other(err) => err,
|
||||
$error::CannotLookup => "Can not lookup",
|
||||
$(
|
||||
$error::$name => stringify!($name),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for $error {
|
||||
fn from(val: &'static str) -> $error {
|
||||
$error::Other(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$crate::error::LookupError> for $error {
|
||||
fn from(_: $crate::error::LookupError) -> $error {
|
||||
$error::CannotLookup
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$error> for &'static str {
|
||||
fn from(err: $error) -> &'static str {
|
||||
use $crate::dispatch::ModuleDispatchError;
|
||||
err.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<$crate::dispatch::DispatchError> for $error {
|
||||
fn into(self) -> $crate::dispatch::DispatchError {
|
||||
use $crate::dispatch::ModuleDispatchError;
|
||||
$crate::dispatch::DispatchError::new(None, self.as_u8(), Some(self.as_str()))
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::error::ModuleErrorMetadata for $error {
|
||||
fn metadata() -> &'static [$crate::error::ErrorMetadata] {
|
||||
&[
|
||||
$(
|
||||
$crate::error::ErrorMetadata {
|
||||
name: $crate::error::DecodeDifferent::Encode(stringify!($name)),
|
||||
documentation: $crate::error::DecodeDifferent::Encode(&[
|
||||
$( $doc_attr ),*
|
||||
]),
|
||||
}
|
||||
),*
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
(@GENERATE_AS_U8
|
||||
$self:ident
|
||||
$error:ident
|
||||
{ $( $generated:tt )* }
|
||||
$index:expr,
|
||||
$name:ident
|
||||
$( , $rest:ident )*
|
||||
) => {
|
||||
$crate::decl_error! {
|
||||
@GENERATE_AS_U8
|
||||
$self
|
||||
$error
|
||||
{
|
||||
$( $generated )*
|
||||
$error::$name => $index,
|
||||
}
|
||||
$index + 1,
|
||||
$( $rest ),*
|
||||
}
|
||||
};
|
||||
(@GENERATE_AS_U8
|
||||
$self:ident
|
||||
$error:ident
|
||||
{ $( $generated:tt )* }
|
||||
$index:expr,
|
||||
) => {
|
||||
match $self {
|
||||
$error::Other(_) => 0,
|
||||
$error::CannotLookup => 1,
|
||||
$( $generated )*
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,819 @@
|
||||
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
//! Macros that define an Event types. Events can be used to easily report changes or conditions
|
||||
//! in your runtime to external entities like users, chain explorers, or dApps.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
pub use frame_metadata::{EventMetadata, DecodeDifferent, OuterEventMetadata, FnEncode};
|
||||
|
||||
/// Implement the `Event` for a module.
|
||||
///
|
||||
/// # Simple Event Example:
|
||||
///
|
||||
/// ```rust
|
||||
/// frame_support::decl_event!(
|
||||
/// pub enum Event {
|
||||
/// Success,
|
||||
/// Failure(String),
|
||||
/// }
|
||||
/// );
|
||||
///
|
||||
///# fn main() {}
|
||||
/// ```
|
||||
///
|
||||
/// # Generic Event Example:
|
||||
///
|
||||
/// ```rust
|
||||
/// trait Trait {
|
||||
/// type Balance;
|
||||
/// type Token;
|
||||
/// }
|
||||
///
|
||||
/// mod event1 {
|
||||
/// // Event that specifies the generic parameter explicitly (`Balance`).
|
||||
/// frame_support::decl_event!(
|
||||
/// pub enum Event<T> where Balance = <T as super::Trait>::Balance {
|
||||
/// Message(Balance),
|
||||
/// }
|
||||
/// );
|
||||
/// }
|
||||
///
|
||||
/// mod event2 {
|
||||
/// // Event that uses the generic parameter `Balance`.
|
||||
/// // If no name for the generic parameter is specified explicitly,
|
||||
/// // the name will be taken from the type name of the trait.
|
||||
/// frame_support::decl_event!(
|
||||
/// pub enum Event<T> where <T as super::Trait>::Balance {
|
||||
/// Message(Balance),
|
||||
/// }
|
||||
/// );
|
||||
/// }
|
||||
///
|
||||
/// mod event3 {
|
||||
/// // And we even support declaring multiple generic parameters!
|
||||
/// frame_support::decl_event!(
|
||||
/// pub enum Event<T> where <T as super::Trait>::Balance, <T as super::Trait>::Token {
|
||||
/// Message(Balance, Token),
|
||||
/// }
|
||||
/// );
|
||||
/// }
|
||||
///
|
||||
///# fn main() {}
|
||||
/// ```
|
||||
///
|
||||
/// The syntax for generic events requires the `where`.
|
||||
///
|
||||
/// # Generic Event with Instance Example:
|
||||
///
|
||||
/// ```rust
|
||||
///# struct DefaultInstance;
|
||||
///# trait Instance {}
|
||||
///# impl Instance for DefaultInstance {}
|
||||
/// trait Trait<I: Instance=DefaultInstance> {
|
||||
/// type Balance;
|
||||
/// type Token;
|
||||
/// }
|
||||
///
|
||||
/// // For module with instances, DefaultInstance is optional
|
||||
/// frame_support::decl_event!(
|
||||
/// pub enum Event<T, I: Instance = DefaultInstance> where
|
||||
/// <T as Trait>::Balance,
|
||||
/// <T as Trait>::Token
|
||||
/// {
|
||||
/// Message(Balance, Token),
|
||||
/// }
|
||||
/// );
|
||||
///# fn main() {}
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! decl_event {
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub enum Event<$evt_generic_param:ident $(, $instance:ident $(: $instantiable:ident)? $( = $event_default_instance:path)? )?> where
|
||||
$( $tt:tt )*
|
||||
) => {
|
||||
$crate::__decl_generic_event!(
|
||||
$( #[ $attr ] )*;
|
||||
$evt_generic_param;
|
||||
$($instance $( = $event_default_instance)? )?;
|
||||
{ $( $tt )* };
|
||||
);
|
||||
};
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub enum Event {
|
||||
$(
|
||||
$events:tt
|
||||
)*
|
||||
}
|
||||
) => {
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(
|
||||
Clone, PartialEq, Eq,
|
||||
$crate::codec::Encode,
|
||||
$crate::codec::Decode,
|
||||
$crate::RuntimeDebug,
|
||||
)]
|
||||
/// Events for this module.
|
||||
///
|
||||
$(#[$attr])*
|
||||
pub enum Event {
|
||||
$(
|
||||
$events
|
||||
)*
|
||||
}
|
||||
impl From<Event> for () {
|
||||
fn from(_: Event) -> () { () }
|
||||
}
|
||||
impl Event {
|
||||
#[allow(dead_code)]
|
||||
#[doc(hidden)]
|
||||
pub fn metadata() -> &'static [ $crate::event::EventMetadata ] {
|
||||
$crate::__events_to_metadata!(; $( $events )* )
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
// This parsing to retrieve last ident on unnamed generic could be improved.
|
||||
// but user can still name it if the parsing fails. And improving parsing seems difficult.
|
||||
macro_rules! __decl_generic_event {
|
||||
(
|
||||
$(#[$attr:meta])*;
|
||||
$event_generic_param:ident;
|
||||
$($instance:ident $( = $event_default_instance:path)? )?;
|
||||
{ $( $tt:tt )* };
|
||||
) => {
|
||||
$crate::__decl_generic_event!(@format_generic
|
||||
$( #[ $attr ] )*;
|
||||
$event_generic_param;
|
||||
$($instance $( = $event_default_instance)? )?;
|
||||
{ $( $tt )* };
|
||||
{};
|
||||
);
|
||||
};
|
||||
// Finish formatting on an unnamed one
|
||||
(@format_generic
|
||||
$(#[$attr:meta])*;
|
||||
$event_generic_param:ident;
|
||||
$($instance:ident $( = $event_default_instance:path)? )?;
|
||||
{ <$generic:ident as $trait:path>::$trait_type:ident $(,)? { $( $events:tt )* } };
|
||||
{$( $parsed:tt)*};
|
||||
) => {
|
||||
$crate::__decl_generic_event!(@generate
|
||||
$( #[ $attr ] )*;
|
||||
$event_generic_param;
|
||||
$($instance $( = $event_default_instance)? )?;
|
||||
{ $($events)* };
|
||||
{ $($parsed)*, $trait_type = <$generic as $trait>::$trait_type };
|
||||
);
|
||||
};
|
||||
// Finish formatting on a named one
|
||||
(@format_generic
|
||||
$(#[$attr:meta])*;
|
||||
$event_generic_param:ident;
|
||||
$($instance:ident $( = $event_default_instance:path)? )?;
|
||||
{ $generic_rename:ident = $generic_type:ty $(,)? { $( $events:tt )* } };
|
||||
{ $($parsed:tt)* };
|
||||
) => {
|
||||
$crate::__decl_generic_event!(@generate
|
||||
$(#[$attr])*;
|
||||
$event_generic_param;
|
||||
$($instance $( = $event_default_instance)? )?;
|
||||
{ $($events)* };
|
||||
{ $($parsed)*, $generic_rename = $generic_type };
|
||||
);
|
||||
};
|
||||
// Parse named
|
||||
(@format_generic
|
||||
$(#[$attr:meta])*;
|
||||
$event_generic_param:ident;
|
||||
$($instance:ident $( = $event_default_instance:path)? )?;
|
||||
{ $generic_rename:ident = $generic_type:ty, $($rest:tt)* };
|
||||
{$( $parsed:tt)*};
|
||||
) => {
|
||||
$crate::__decl_generic_event!(@format_generic
|
||||
$( #[ $attr ] )*;
|
||||
$event_generic_param;
|
||||
$( $instance $( = $event_default_instance)? )?;
|
||||
{ $($rest)* };
|
||||
{ $($parsed)*, $generic_rename = $generic_type };
|
||||
);
|
||||
};
|
||||
// Parse unnamed
|
||||
(@format_generic
|
||||
$(#[$attr:meta])*;
|
||||
$event_generic_param:ident;
|
||||
$($instance:ident $( = $event_default_instance:path)? )?;
|
||||
{ <$generic:ident as $trait:path>::$trait_type:ident, $($rest:tt)* };
|
||||
{$($parsed:tt)*};
|
||||
) => {
|
||||
$crate::__decl_generic_event!(@format_generic
|
||||
$( #[ $attr ] )*;
|
||||
$event_generic_param;
|
||||
$($instance $( = $event_default_instance)? )?;
|
||||
{ $($rest)* };
|
||||
{ $($parsed)*, $trait_type = <$generic as $trait>::$trait_type };
|
||||
);
|
||||
};
|
||||
// Unnamed type can't be parsed
|
||||
(@format_generic
|
||||
$(#[$attr:meta])*;
|
||||
$event_generic_param:ident;
|
||||
$($instance:ident $( = $event_default_instance:path)? )?;
|
||||
{ $generic_type:ty, $($rest:tt)* };
|
||||
{ $($parsed:tt)* };
|
||||
) => {
|
||||
$crate::__decl_generic_event!(@cannot_parse $generic_type);
|
||||
};
|
||||
// Final unnamed type can't be parsed
|
||||
(@format_generic
|
||||
$(#[$attr:meta])*;
|
||||
$event_generic_param:ident;
|
||||
$($instance:ident $( = $event_default_instance:path)? )?;
|
||||
{ $generic_type:ty { $( $events:tt )* } };
|
||||
{$( $parsed:tt)*};
|
||||
) => {
|
||||
$crate::__decl_generic_event!(@cannot_parse $generic_type);
|
||||
};
|
||||
(@generate
|
||||
$(#[$attr:meta])*;
|
||||
$event_generic_param:ident;
|
||||
$($instance:ident $( = $event_default_instance:path)? )?;
|
||||
{ $( $events:tt )* };
|
||||
{ ,$( $generic_param:ident = $generic_type:ty ),* };
|
||||
) => {
|
||||
/// [`RawEvent`] specialized for the configuration [`Trait`]
|
||||
///
|
||||
/// [`RawEvent`]: enum.RawEvent.html
|
||||
/// [`Trait`]: trait.Trait.html
|
||||
pub type Event<$event_generic_param $(, $instance $( = $event_default_instance)? )?> = RawEvent<$( $generic_type ),* $(, $instance)? >;
|
||||
|
||||
#[derive(
|
||||
Clone, PartialEq, Eq,
|
||||
$crate::codec::Encode,
|
||||
$crate::codec::Decode,
|
||||
$crate::RuntimeDebug,
|
||||
)]
|
||||
/// Events for this module.
|
||||
///
|
||||
$(#[$attr])*
|
||||
pub enum RawEvent<$( $generic_param ),* $(, $instance)? > {
|
||||
$(
|
||||
$events
|
||||
)*
|
||||
$(
|
||||
#[doc(hidden)]
|
||||
#[codec(skip)]
|
||||
PhantomData($crate::rstd::marker::PhantomData<$instance>),
|
||||
)?
|
||||
}
|
||||
impl<$( $generic_param ),* $(, $instance)? > From<RawEvent<$( $generic_param ),* $(, $instance)?>> for () {
|
||||
fn from(_: RawEvent<$( $generic_param ),* $(, $instance)?>) -> () { () }
|
||||
}
|
||||
impl<$( $generic_param ),* $(, $instance)?> RawEvent<$( $generic_param ),* $(, $instance)?> {
|
||||
#[allow(dead_code)]
|
||||
pub fn metadata() -> &'static [$crate::event::EventMetadata] {
|
||||
$crate::__events_to_metadata!(; $( $events )* )
|
||||
}
|
||||
}
|
||||
};
|
||||
(@cannot_parse $ty:ty) => {
|
||||
compile_error!(concat!("The type `", stringify!($ty), "` can't be parsed as an unnamed one, please name it `Name = ", stringify!($ty), "`"));
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __events_to_metadata {
|
||||
(
|
||||
$( $metadata:expr ),*;
|
||||
$( #[doc = $doc_attr:tt] )*
|
||||
$event:ident $( ( $( $param:path ),* ) )*,
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
$crate::__events_to_metadata!(
|
||||
$( $metadata, )*
|
||||
$crate::event::EventMetadata {
|
||||
name: $crate::event::DecodeDifferent::Encode(stringify!($event)),
|
||||
arguments: $crate::event::DecodeDifferent::Encode(&[
|
||||
$( $( stringify!($param) ),* )*
|
||||
]),
|
||||
documentation: $crate::event::DecodeDifferent::Encode(&[
|
||||
$( $doc_attr ),*
|
||||
]),
|
||||
};
|
||||
$( $rest )*
|
||||
)
|
||||
};
|
||||
(
|
||||
$( $metadata:expr ),*;
|
||||
) => {
|
||||
&[ $( $metadata ),* ]
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs an Event type for a runtime. This is usually called automatically by the
|
||||
/// construct_runtime macro.
|
||||
#[macro_export]
|
||||
macro_rules! impl_outer_event {
|
||||
// Macro transformations (to convert invocations with incomplete parameters to the canonical
|
||||
// form)
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub enum $name:ident for $runtime:ident {
|
||||
$( $rest_event_without_system:tt )*
|
||||
}
|
||||
) => {
|
||||
$crate::impl_outer_event!(
|
||||
$( #[$attr] )*;
|
||||
$name;
|
||||
$runtime;
|
||||
system;
|
||||
Modules { $( $rest_event_without_system )* };
|
||||
;
|
||||
);
|
||||
};
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub enum $name:ident for $runtime:ident where system = $system:ident {
|
||||
$( $rest_event_with_system:tt )*
|
||||
}
|
||||
) => {
|
||||
$crate::impl_outer_event!(
|
||||
$( #[$attr] )*;
|
||||
$name;
|
||||
$runtime;
|
||||
$system;
|
||||
Modules { $( $rest_event_with_system )* };
|
||||
;
|
||||
);
|
||||
};
|
||||
// Generic + Instance
|
||||
(
|
||||
$(#[$attr:meta])*;
|
||||
$name:ident;
|
||||
$runtime:ident;
|
||||
$system:ident;
|
||||
Modules {
|
||||
$module:ident $instance:ident<T>,
|
||||
$( $rest_event_generic_instance:tt )*
|
||||
};
|
||||
$( $module_name:ident::Event $( <$generic_param:ident> )? $( { $generic_instance:ident } )?, )*;
|
||||
) => {
|
||||
$crate::impl_outer_event!(
|
||||
$( #[$attr] )*;
|
||||
$name;
|
||||
$runtime;
|
||||
$system;
|
||||
Modules { $( $rest_event_generic_instance )* };
|
||||
$( $module_name::Event $( <$generic_param> )? $( { $generic_instance } )?, )* $module::Event<$runtime>{ $instance },;
|
||||
);
|
||||
};
|
||||
// Instance
|
||||
(
|
||||
$(#[$attr:meta])*;
|
||||
$name:ident;
|
||||
$runtime:ident;
|
||||
$system:ident;
|
||||
Modules {
|
||||
$module:ident $instance:ident,
|
||||
$( $rest_event_instance:tt )*
|
||||
};
|
||||
$( $module_name:ident::Event $( <$generic_param:ident> )? $( { $generic_instance:ident } )?, )*;
|
||||
) => {
|
||||
$crate::impl_outer_event!(
|
||||
$( #[$attr] )*;
|
||||
$name;
|
||||
$runtime;
|
||||
$system;
|
||||
Modules { $( $rest_event_instance )* };
|
||||
$( $module_name::Event $( <$generic_param> )* $( { $generic_instance } )?, )* $module::Event { $instance },;
|
||||
);
|
||||
};
|
||||
// Generic
|
||||
(
|
||||
$(#[$attr:meta])*;
|
||||
$name:ident;
|
||||
$runtime:ident;
|
||||
$system:ident;
|
||||
Modules {
|
||||
$module:ident<T>,
|
||||
$( $rest_event_generic:tt )*
|
||||
};
|
||||
$( $module_name:ident::Event $( <$generic_param:ident> )? $( { $generic_instance:ident } )?, )*;
|
||||
) => {
|
||||
$crate::impl_outer_event!(
|
||||
$( #[$attr] )*;
|
||||
$name;
|
||||
$runtime;
|
||||
$system;
|
||||
Modules { $( $rest_event_generic )* };
|
||||
$( $module_name::Event $( <$generic_param> )? $( { $generic_instance } )?, )* $module::Event<$runtime>,;
|
||||
);
|
||||
};
|
||||
// No Generic and no Instance
|
||||
(
|
||||
$(#[$attr:meta])*;
|
||||
$name:ident;
|
||||
$runtime:ident;
|
||||
$system:ident;
|
||||
Modules {
|
||||
$module:ident,
|
||||
$( $rest_event_no_generic_no_instance:tt )*
|
||||
};
|
||||
$( $module_name:ident::Event $( <$generic_param:ident> )? $( { $generic_instance:ident } )?, )*;
|
||||
) => {
|
||||
$crate::impl_outer_event!(
|
||||
$( #[$attr] )*;
|
||||
$name;
|
||||
$runtime;
|
||||
$system;
|
||||
Modules { $( $rest_event_no_generic_no_instance )* };
|
||||
$( $module_name::Event $( <$generic_param> )? $( { $generic_instance } )?, )* $module::Event,;
|
||||
);
|
||||
};
|
||||
|
||||
// The main macro expansion that actually renders the Event enum code.
|
||||
(
|
||||
$(#[$attr:meta])*;
|
||||
$name:ident;
|
||||
$runtime:ident;
|
||||
$system:ident;
|
||||
Modules {};
|
||||
$( $module_name:ident::Event $( <$generic_param:ident> )? $( { $generic_instance:ident } )?, )*;
|
||||
) => {
|
||||
$crate::paste::item! {
|
||||
#[derive(
|
||||
Clone, PartialEq, Eq,
|
||||
$crate::codec::Encode,
|
||||
$crate::codec::Decode,
|
||||
$crate::RuntimeDebug,
|
||||
)]
|
||||
$(#[$attr])*
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum $name {
|
||||
system($system::Event),
|
||||
$(
|
||||
[< $module_name $(_ $generic_instance )? >](
|
||||
$module_name::Event < $( $generic_param )? $(, $module_name::$generic_instance )? >
|
||||
),
|
||||
)*
|
||||
}
|
||||
impl From<$system::Event> for $name {
|
||||
fn from(x: $system::Event) -> Self {
|
||||
$name::system(x)
|
||||
}
|
||||
}
|
||||
$(
|
||||
impl From<$module_name::Event < $( $generic_param, )? $( $module_name::$generic_instance )? >> for $name {
|
||||
fn from(x: $module_name::Event < $( $generic_param, )? $( $module_name::$generic_instance )? >) -> Self {
|
||||
$name::[< $module_name $(_ $generic_instance )? >](x)
|
||||
}
|
||||
}
|
||||
impl $crate::rstd::convert::TryInto<
|
||||
$module_name::Event < $( $generic_param, )? $( $module_name::$generic_instance )? >
|
||||
> for $name {
|
||||
type Error = ();
|
||||
|
||||
fn try_into(self) -> $crate::rstd::result::Result<
|
||||
$module_name::Event < $( $generic_param, )? $( $module_name::$generic_instance )? >, Self::Error
|
||||
> {
|
||||
match self {
|
||||
Self::[< $module_name $(_ $generic_instance )? >](evt) => Ok(evt),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
$crate::__impl_outer_event_json_metadata!(
|
||||
$runtime;
|
||||
$name;
|
||||
$system;
|
||||
$(
|
||||
$module_name::Event
|
||||
< $( $generic_param )? $(, $module_name::$generic_instance )? >
|
||||
$( $generic_instance )?,
|
||||
)*;
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __impl_outer_event_json_metadata {
|
||||
(
|
||||
$runtime:ident;
|
||||
$event_name:ident;
|
||||
$system:ident;
|
||||
$( $module_name:ident::Event < $( $generic_params:path ),* > $( $instance:ident )?, )*;
|
||||
) => {
|
||||
impl $runtime {
|
||||
#[allow(dead_code)]
|
||||
pub fn outer_event_metadata() -> $crate::event::OuterEventMetadata {
|
||||
$crate::event::OuterEventMetadata {
|
||||
name: $crate::event::DecodeDifferent::Encode(stringify!($event_name)),
|
||||
events: $crate::event::DecodeDifferent::Encode(&[
|
||||
("system", $crate::event::FnEncode(system::Event::metadata))
|
||||
$(
|
||||
, (
|
||||
stringify!($module_name),
|
||||
$crate::event::FnEncode(
|
||||
$module_name::Event ::< $( $generic_params ),* > ::metadata
|
||||
)
|
||||
)
|
||||
)*
|
||||
])
|
||||
}
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn __module_events_system() -> &'static [$crate::event::EventMetadata] {
|
||||
system::Event::metadata()
|
||||
}
|
||||
|
||||
$crate::paste::item! {
|
||||
$(
|
||||
#[allow(dead_code)]
|
||||
pub fn [< __module_events_ $module_name $( _ $instance )? >] () ->
|
||||
&'static [$crate::event::EventMetadata]
|
||||
{
|
||||
$module_name::Event ::< $( $generic_params ),* > ::metadata()
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(dead_code)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde::Serialize;
|
||||
use codec::{Encode, Decode};
|
||||
|
||||
mod system {
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
type BlockNumber;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
decl_event!(
|
||||
pub enum Event {
|
||||
SystemEvent,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
mod system_renamed {
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
type BlockNumber;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
decl_event!(
|
||||
pub enum Event {
|
||||
SystemEvent,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
mod event_module {
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
type Balance;
|
||||
type BlockNumber;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
decl_event!(
|
||||
/// Event without renaming the generic parameter `Balance` and `Origin`.
|
||||
pub enum Event<T> where <T as Trait>::Balance, <T as Trait>::Origin
|
||||
{
|
||||
/// Hi, I am a comment.
|
||||
TestEvent(Balance, Origin),
|
||||
/// Dog
|
||||
EventWithoutParams,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
mod event_module2 {
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
type Balance;
|
||||
type BlockNumber;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
decl_event!(
|
||||
/// Event with renamed generic parameter
|
||||
pub enum Event<T> where
|
||||
BalanceRenamed = <T as Trait>::Balance,
|
||||
OriginRenamed = <T as Trait>::Origin
|
||||
{
|
||||
TestEvent(BalanceRenamed),
|
||||
TestOrigin(OriginRenamed),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
mod event_module3 {
|
||||
decl_event!(
|
||||
pub enum Event {
|
||||
HiEvent,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
mod event_module4 {
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
type Balance;
|
||||
type BlockNumber;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
decl_event!(
|
||||
/// Event finish formatting on an unnamed one with trailling comma
|
||||
pub enum Event<T> where
|
||||
<T as Trait>::Balance,
|
||||
<T as Trait>::Origin,
|
||||
{
|
||||
TestEvent(Balance, Origin),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
mod event_module5 {
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
type Balance;
|
||||
type BlockNumber;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
decl_event!(
|
||||
/// Event finish formatting on an named one with trailing comma
|
||||
pub enum Event<T> where
|
||||
BalanceRenamed = <T as Trait>::Balance,
|
||||
OriginRenamed = <T as Trait>::Origin,
|
||||
{
|
||||
TestEvent(BalanceRenamed, OriginRenamed),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Serialize)]
|
||||
pub struct TestRuntime;
|
||||
|
||||
impl_outer_event! {
|
||||
pub enum TestEvent for TestRuntime {
|
||||
event_module<T>,
|
||||
event_module2<T>,
|
||||
event_module3,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Serialize)]
|
||||
pub struct TestRuntime2;
|
||||
|
||||
impl_outer_event! {
|
||||
pub enum TestEventSystemRenamed for TestRuntime2 where system = system_renamed {
|
||||
event_module<T>,
|
||||
event_module2<T>,
|
||||
event_module3,
|
||||
}
|
||||
}
|
||||
|
||||
impl event_module::Trait for TestRuntime {
|
||||
type Origin = u32;
|
||||
type Balance = u32;
|
||||
type BlockNumber = u32;
|
||||
}
|
||||
|
||||
impl event_module2::Trait for TestRuntime {
|
||||
type Origin = u32;
|
||||
type Balance = u32;
|
||||
type BlockNumber = u32;
|
||||
}
|
||||
|
||||
impl system::Trait for TestRuntime {
|
||||
type Origin = u32;
|
||||
type BlockNumber = u32;
|
||||
}
|
||||
|
||||
impl event_module::Trait for TestRuntime2 {
|
||||
type Origin = u32;
|
||||
type Balance = u32;
|
||||
type BlockNumber = u32;
|
||||
}
|
||||
|
||||
impl event_module2::Trait for TestRuntime2 {
|
||||
type Origin = u32;
|
||||
type Balance = u32;
|
||||
type BlockNumber = u32;
|
||||
}
|
||||
|
||||
impl system_renamed::Trait for TestRuntime2 {
|
||||
type Origin = u32;
|
||||
type BlockNumber = u32;
|
||||
}
|
||||
|
||||
const EXPECTED_METADATA: OuterEventMetadata = OuterEventMetadata {
|
||||
name: DecodeDifferent::Encode("TestEvent"),
|
||||
events: DecodeDifferent::Encode(&[
|
||||
(
|
||||
"system",
|
||||
FnEncode(|| &[
|
||||
EventMetadata {
|
||||
name: DecodeDifferent::Encode("SystemEvent"),
|
||||
arguments: DecodeDifferent::Encode(&[]),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
}
|
||||
])
|
||||
),
|
||||
(
|
||||
"event_module",
|
||||
FnEncode(|| &[
|
||||
EventMetadata {
|
||||
name: DecodeDifferent::Encode("TestEvent"),
|
||||
arguments: DecodeDifferent::Encode(&[ "Balance", "Origin" ]),
|
||||
documentation: DecodeDifferent::Encode(&[ " Hi, I am a comment." ])
|
||||
},
|
||||
EventMetadata {
|
||||
name: DecodeDifferent::Encode("EventWithoutParams"),
|
||||
arguments: DecodeDifferent::Encode(&[]),
|
||||
documentation: DecodeDifferent::Encode(&[ " Dog" ]),
|
||||
},
|
||||
])
|
||||
),
|
||||
(
|
||||
"event_module2",
|
||||
FnEncode(|| &[
|
||||
EventMetadata {
|
||||
name: DecodeDifferent::Encode("TestEvent"),
|
||||
arguments: DecodeDifferent::Encode(&[ "BalanceRenamed" ]),
|
||||
documentation: DecodeDifferent::Encode(&[])
|
||||
},
|
||||
EventMetadata {
|
||||
name: DecodeDifferent::Encode("TestOrigin"),
|
||||
arguments: DecodeDifferent::Encode(&[ "OriginRenamed" ]),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
])
|
||||
),
|
||||
(
|
||||
"event_module3",
|
||||
FnEncode(|| &[
|
||||
EventMetadata {
|
||||
name: DecodeDifferent::Encode("HiEvent"),
|
||||
arguments: DecodeDifferent::Encode(&[]),
|
||||
documentation: DecodeDifferent::Encode(&[])
|
||||
}
|
||||
])
|
||||
)
|
||||
])
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn outer_event_metadata() {
|
||||
assert_eq!(EXPECTED_METADATA, TestRuntime::outer_event_metadata());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Hash utilities.
|
||||
|
||||
use codec::Codec;
|
||||
use rstd::prelude::Vec;
|
||||
use runtime_io::hashing::{blake2_128, blake2_256, twox_64, twox_128, twox_256};
|
||||
|
||||
// This trait must be kept coherent with frame-support-procedural HasherKind usage
|
||||
pub trait Hashable: Sized {
|
||||
fn blake2_128(&self) -> [u8; 16];
|
||||
fn blake2_256(&self) -> [u8; 32];
|
||||
fn twox_128(&self) -> [u8; 16];
|
||||
fn twox_256(&self) -> [u8; 32];
|
||||
fn twox_64_concat(&self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
impl<T: Codec> Hashable for T {
|
||||
fn blake2_128(&self) -> [u8; 16] {
|
||||
self.using_encoded(blake2_128)
|
||||
}
|
||||
fn blake2_256(&self) -> [u8; 32] {
|
||||
self.using_encoded(blake2_256)
|
||||
}
|
||||
fn twox_128(&self) -> [u8; 16] {
|
||||
self.using_encoded(twox_128)
|
||||
}
|
||||
fn twox_256(&self) -> [u8; 32] {
|
||||
self.using_encoded(twox_256)
|
||||
}
|
||||
fn twox_64_concat(&self) -> Vec<u8> {
|
||||
self.using_encoded(Twox64Concat::hash)
|
||||
}
|
||||
}
|
||||
|
||||
/// Hasher to use to hash keys to insert to storage.
|
||||
pub trait StorageHasher: 'static {
|
||||
type Output: AsRef<[u8]>;
|
||||
fn hash(x: &[u8]) -> Self::Output;
|
||||
}
|
||||
|
||||
/// Hash storage keys with `concat(twox64(key), key)`
|
||||
pub struct Twox64Concat;
|
||||
impl StorageHasher for Twox64Concat {
|
||||
type Output = Vec<u8>;
|
||||
fn hash(x: &[u8]) -> Vec<u8> {
|
||||
twox_64(x)
|
||||
.iter()
|
||||
.chain(x.into_iter())
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
|
||||
/// Hash storage keys with blake2 128
|
||||
pub struct Blake2_128;
|
||||
impl StorageHasher for Blake2_128 {
|
||||
type Output = [u8; 16];
|
||||
fn hash(x: &[u8]) -> [u8; 16] {
|
||||
blake2_128(x)
|
||||
}
|
||||
}
|
||||
|
||||
/// Hash storage keys with blake2 256
|
||||
pub struct Blake2_256;
|
||||
impl StorageHasher for Blake2_256 {
|
||||
type Output = [u8; 32];
|
||||
fn hash(x: &[u8]) -> [u8; 32] {
|
||||
blake2_256(x)
|
||||
}
|
||||
}
|
||||
|
||||
/// Hash storage keys with twox 128
|
||||
pub struct Twox128;
|
||||
impl StorageHasher for Twox128 {
|
||||
type Output = [u8; 16];
|
||||
fn hash(x: &[u8]) -> [u8; 16] {
|
||||
twox_128(x)
|
||||
}
|
||||
}
|
||||
|
||||
/// Hash storage keys with twox 256
|
||||
pub struct Twox256;
|
||||
impl StorageHasher for Twox256 {
|
||||
type Output = [u8; 32];
|
||||
fn hash(x: &[u8]) -> [u8; 32] {
|
||||
twox_256(x)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_twox_64_concat() {
|
||||
let r = Twox64Concat::hash(b"foo");
|
||||
assert_eq!(r.split_at(8), (&twox_128(b"foo")[..8], &b"foo"[..]))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use crate::rstd::vec::Vec;
|
||||
#[doc(hidden)]
|
||||
pub use crate::sr_primitives::traits::{Block as BlockT, Extrinsic};
|
||||
#[doc(hidden)]
|
||||
pub use inherents::{InherentData, ProvideInherent, CheckInherentsResult, IsFatalError};
|
||||
|
||||
|
||||
/// Implement the outer inherent.
|
||||
/// All given modules need to implement `ProvideInherent`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```nocompile
|
||||
/// impl_outer_inherent! {
|
||||
/// impl Inherents where Block = Block, UncheckedExtrinsic = UncheckedExtrinsic {
|
||||
/// timestamp: Timestamp,
|
||||
/// consensus: Consensus,
|
||||
/// /// Aura module using the `Timestamp` call.
|
||||
/// aura: Timestamp,
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! impl_outer_inherent {
|
||||
(
|
||||
impl Inherents where Block = $block:ident, UncheckedExtrinsic = $uncheckedextrinsic:ident
|
||||
{
|
||||
$( $module:ident: $call:ident, )*
|
||||
}
|
||||
) => {
|
||||
trait InherentDataExt {
|
||||
fn create_extrinsics(&self) ->
|
||||
$crate::inherent::Vec<<$block as $crate::inherent::BlockT>::Extrinsic>;
|
||||
fn check_extrinsics(&self, block: &$block) -> $crate::inherent::CheckInherentsResult;
|
||||
}
|
||||
|
||||
impl InherentDataExt for $crate::inherent::InherentData {
|
||||
fn create_extrinsics(&self) ->
|
||||
$crate::inherent::Vec<<$block as $crate::inherent::BlockT>::Extrinsic> {
|
||||
use $crate::inherent::ProvideInherent;
|
||||
use $crate::inherent::Extrinsic;
|
||||
|
||||
let mut inherents = Vec::new();
|
||||
|
||||
$(
|
||||
if let Some(inherent) = $module::create_inherent(self) {
|
||||
inherents.push($uncheckedextrinsic::new(
|
||||
Call::$call(inherent),
|
||||
None,
|
||||
).expect("Runtime UncheckedExtrinsic is not Opaque, so it has to return `Some`; qed"));
|
||||
}
|
||||
)*
|
||||
|
||||
inherents
|
||||
}
|
||||
|
||||
fn check_extrinsics(&self, block: &$block) -> $crate::inherent::CheckInherentsResult {
|
||||
use $crate::inherent::{ProvideInherent, IsFatalError};
|
||||
|
||||
let mut result = $crate::inherent::CheckInherentsResult::new();
|
||||
for xt in block.extrinsics() {
|
||||
if $crate::inherent::Extrinsic::is_signed(xt).unwrap_or(false) {
|
||||
break;
|
||||
}
|
||||
|
||||
$(
|
||||
match xt.function {
|
||||
Call::$call(ref call) => {
|
||||
if let Err(e) = $module::check_inherent(call, self) {
|
||||
result.put_error(
|
||||
$module::INHERENT_IDENTIFIER, &e
|
||||
).expect("There is only one fatal error; qed");
|
||||
if e.is_fatal_error() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {},
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,608 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Support code for the runtime.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
/// Export ourself as `frame_support` to make tests happy.
|
||||
extern crate self as frame_support;
|
||||
|
||||
#[macro_use]
|
||||
extern crate bitmask;
|
||||
#[cfg(feature = "std")]
|
||||
pub extern crate tracing;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub use serde;
|
||||
#[doc(hidden)]
|
||||
pub use rstd;
|
||||
#[doc(hidden)]
|
||||
pub use codec;
|
||||
#[cfg(feature = "std")]
|
||||
#[doc(hidden)]
|
||||
pub use once_cell;
|
||||
#[doc(hidden)]
|
||||
pub use paste;
|
||||
#[cfg(feature = "std")]
|
||||
#[doc(hidden)]
|
||||
pub use state_machine::BasicExternalities;
|
||||
#[doc(hidden)]
|
||||
pub use runtime_io::storage::root as storage_root;
|
||||
#[doc(hidden)]
|
||||
pub use sr_primitives::RuntimeDebug;
|
||||
|
||||
#[macro_use]
|
||||
pub mod debug;
|
||||
#[macro_use]
|
||||
pub mod dispatch;
|
||||
pub mod storage;
|
||||
mod hash;
|
||||
#[macro_use]
|
||||
pub mod event;
|
||||
#[macro_use]
|
||||
mod origin;
|
||||
#[macro_use]
|
||||
pub mod metadata;
|
||||
#[macro_use]
|
||||
mod runtime;
|
||||
#[macro_use]
|
||||
pub mod inherent;
|
||||
#[macro_use]
|
||||
pub mod unsigned;
|
||||
#[macro_use]
|
||||
pub mod error;
|
||||
pub mod traits;
|
||||
pub mod weights;
|
||||
|
||||
pub use self::hash::{Twox256, Twox128, Blake2_256, Blake2_128, Twox64Concat, Hashable};
|
||||
pub use self::storage::{StorageValue, StorageMap, StorageLinkedMap, StorageDoubleMap};
|
||||
pub use self::dispatch::{Parameter, Callable, IsSubType};
|
||||
pub use sr_primitives::{self, ConsensusEngineId, print, traits::Printable};
|
||||
|
||||
/// Macro for easily creating a new implementation of the `Get` trait. Use similarly to
|
||||
/// how you would declare a `const`:
|
||||
///
|
||||
/// ```no_compile
|
||||
/// parameter_types! {
|
||||
/// pub const Argument: u64 = 42;
|
||||
/// }
|
||||
/// trait Config {
|
||||
/// type Parameter: Get<u64>;
|
||||
/// }
|
||||
/// struct Runtime;
|
||||
/// impl Config for Runtime {
|
||||
/// type Parameter = Argument;
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! parameter_types {
|
||||
(
|
||||
$( #[ $attr:meta ] )*
|
||||
$vis:vis const $name:ident: $type:ty = $value:expr;
|
||||
$( $rest:tt )*
|
||||
) => (
|
||||
$( #[ $attr ] )*
|
||||
$vis struct $name;
|
||||
$crate::parameter_types!{IMPL $name , $type , $value}
|
||||
$crate::parameter_types!{ $( $rest )* }
|
||||
);
|
||||
() => ();
|
||||
(IMPL $name:ident , $type:ty , $value:expr) => {
|
||||
impl $name {
|
||||
pub fn get() -> $type {
|
||||
$value
|
||||
}
|
||||
}
|
||||
impl<I: From<$type>> $crate::traits::Get<I> for $name {
|
||||
fn get() -> I {
|
||||
I::from($value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(inline)]
|
||||
pub use frame_support_procedural::decl_storage;
|
||||
|
||||
/// Return Err of the expression: `return Err($expression);`.
|
||||
///
|
||||
/// Used as `fail!(expression)`.
|
||||
#[macro_export]
|
||||
macro_rules! fail {
|
||||
( $y:expr ) => {{
|
||||
return Err($y);
|
||||
}}
|
||||
}
|
||||
|
||||
/// Evaluate `$x:expr` and if not true return `Err($y:expr)`.
|
||||
///
|
||||
/// Used as `ensure!(expression_to_ensure, expression_to_return_on_false)`.
|
||||
#[macro_export]
|
||||
macro_rules! ensure {
|
||||
( $x:expr, $y:expr $(,)? ) => {{
|
||||
if !$x {
|
||||
$crate::fail!($y);
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
/// Evaluate an expression, assert it returns an expected `Err` value and that
|
||||
/// runtime storage has not been mutated (i.e. expression is a no-operation).
|
||||
///
|
||||
/// Used as `assert_noop(expression_to_assert, expected_error_expression)`.
|
||||
#[macro_export]
|
||||
#[cfg(feature = "std")]
|
||||
macro_rules! assert_noop {
|
||||
(
|
||||
$x:expr,
|
||||
$y:expr $(,)?
|
||||
) => {
|
||||
let h = $crate::storage_root();
|
||||
$crate::assert_err!($x, $y);
|
||||
assert_eq!(h, $crate::storage_root());
|
||||
}
|
||||
}
|
||||
|
||||
/// Panic if an expression doesn't evaluate to an `Err`.
|
||||
///
|
||||
/// Used as `assert_err!(expression_to_assert, expected_err_expression)`.
|
||||
|
||||
/// Assert an expression returns an error specified.
|
||||
///
|
||||
/// Used as `assert_err!(expression_to_assert, expected_error_expression)`
|
||||
#[macro_export]
|
||||
#[cfg(feature = "std")]
|
||||
macro_rules! assert_err {
|
||||
( $x:expr , $y:expr $(,)? ) => {
|
||||
assert_eq!($x, Err($y));
|
||||
}
|
||||
}
|
||||
|
||||
/// Panic if an expression doesn't evaluate to `Ok`.
|
||||
///
|
||||
/// Used as `assert_ok!(expression_to_assert, expected_ok_expression)`,
|
||||
/// or `assert_ok!(expression_to_assert)` which would assert against `Ok(())`.
|
||||
#[macro_export]
|
||||
#[cfg(feature = "std")]
|
||||
macro_rules! assert_ok {
|
||||
( $x:expr $(,)? ) => {
|
||||
assert_eq!($x, Ok(()));
|
||||
};
|
||||
( $x:expr, $y:expr $(,)? ) => {
|
||||
assert_eq!($x, Ok($y));
|
||||
}
|
||||
}
|
||||
|
||||
/// Panic when the vectors are different, without taking the order into account.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// #[macro_use]
|
||||
/// # extern crate frame_support;
|
||||
/// # use frame_support::{assert_eq_uvec};
|
||||
/// # fn main() {
|
||||
/// assert_eq_uvec!(vec![1,2], vec![2,1]);
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// ```rust,should_panic
|
||||
/// #[macro_use]
|
||||
/// # extern crate frame_support;
|
||||
/// # use frame_support::{assert_eq_uvec};
|
||||
/// # fn main() {
|
||||
/// assert_eq_uvec!(vec![1,2,3], vec![2,1]);
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
#[cfg(feature = "std")]
|
||||
macro_rules! assert_eq_uvec {
|
||||
( $x:expr, $y:expr ) => {
|
||||
$crate::__assert_eq_uvec!($x, $y);
|
||||
$crate::__assert_eq_uvec!($y, $x);
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "std")]
|
||||
macro_rules! __assert_eq_uvec {
|
||||
( $x:expr, $y:expr ) => {
|
||||
$x.iter().for_each(|e| {
|
||||
if !$y.contains(e) { panic!(format!("vectors not equal: {:?} != {:?}", $x, $y)); }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// The void type - it cannot exist.
|
||||
// Oh rust, you crack me up...
|
||||
#[derive(Clone, Eq, PartialEq, RuntimeDebug)]
|
||||
pub enum Void {}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[doc(hidden)]
|
||||
pub use serde::{Serialize, Deserialize};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use codec::{Codec, EncodeLike};
|
||||
use frame_metadata::{
|
||||
DecodeDifferent, StorageEntryMetadata, StorageMetadata, StorageEntryType,
|
||||
StorageEntryModifier, DefaultByteGetter, StorageHasher,
|
||||
};
|
||||
use rstd::marker::PhantomData;
|
||||
|
||||
pub trait Trait {
|
||||
type BlockNumber: Codec + EncodeLike + Default;
|
||||
type Origin;
|
||||
}
|
||||
|
||||
mod module {
|
||||
#![allow(dead_code)]
|
||||
|
||||
use super::Trait;
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
}
|
||||
use self::module::Module;
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as Example {
|
||||
pub Data get(fn data) build(|_| vec![(15u32, 42u64)]):
|
||||
linked_map hasher(twox_64_concat) u32 => u64;
|
||||
pub OptionLinkedMap: linked_map u32 => Option<u32>;
|
||||
pub GenericData get(fn generic_data):
|
||||
linked_map hasher(twox_128) T::BlockNumber => T::BlockNumber;
|
||||
pub GenericData2 get(fn generic_data2):
|
||||
linked_map T::BlockNumber => Option<T::BlockNumber>;
|
||||
pub GetterNoFnKeyword get(no_fn): Option<u32>;
|
||||
|
||||
pub DataDM config(test_config) build(|_| vec![(15u32, 16u32, 42u64)]):
|
||||
double_map hasher(twox_64_concat) u32, blake2_256(u32) => u64;
|
||||
pub GenericDataDM:
|
||||
double_map T::BlockNumber, twox_128(T::BlockNumber) => T::BlockNumber;
|
||||
pub GenericData2DM:
|
||||
double_map T::BlockNumber, twox_256(T::BlockNumber) => Option<T::BlockNumber>;
|
||||
pub AppendableDM: double_map u32, blake2_256(T::BlockNumber) => Vec<u32>;
|
||||
}
|
||||
}
|
||||
|
||||
struct Test;
|
||||
impl Trait for Test {
|
||||
type BlockNumber = u32;
|
||||
type Origin = u32;
|
||||
}
|
||||
|
||||
fn new_test_ext() -> runtime_io::TestExternalities {
|
||||
GenesisConfig::default().build_storage().unwrap().into()
|
||||
}
|
||||
|
||||
type Map = Data;
|
||||
|
||||
#[test]
|
||||
fn linked_map_issue_3318() {
|
||||
new_test_ext().execute_with(|| {
|
||||
OptionLinkedMap::insert(1, 1);
|
||||
assert_eq!(OptionLinkedMap::get(1), Some(1));
|
||||
OptionLinkedMap::insert(1, 2);
|
||||
assert_eq!(OptionLinkedMap::get(1), Some(2));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_map_swap_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
OptionLinkedMap::insert(0, 0);
|
||||
OptionLinkedMap::insert(1, 1);
|
||||
OptionLinkedMap::insert(2, 2);
|
||||
OptionLinkedMap::insert(3, 3);
|
||||
|
||||
let collect = || OptionLinkedMap::enumerate().collect::<Vec<_>>();
|
||||
assert_eq!(collect(), vec![(3, 3), (2, 2), (1, 1), (0, 0)]);
|
||||
|
||||
// Two existing
|
||||
OptionLinkedMap::swap(1, 2);
|
||||
assert_eq!(collect(), vec![(3, 3), (2, 1), (1, 2), (0, 0)]);
|
||||
|
||||
// Back to normal
|
||||
OptionLinkedMap::swap(2, 1);
|
||||
assert_eq!(collect(), vec![(3, 3), (2, 2), (1, 1), (0, 0)]);
|
||||
|
||||
// Left existing
|
||||
OptionLinkedMap::swap(2, 5);
|
||||
assert_eq!(collect(), vec![(5, 2), (3, 3), (1, 1), (0, 0)]);
|
||||
|
||||
// Right existing
|
||||
OptionLinkedMap::swap(5, 2);
|
||||
assert_eq!(collect(), vec![(2, 2), (3, 3), (1, 1), (0, 0)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_map_basic_insert_remove_should_work() {
|
||||
new_test_ext().execute_with(|| {
|
||||
// initialized during genesis
|
||||
assert_eq!(Map::get(&15u32), 42u64);
|
||||
|
||||
// get / insert / take
|
||||
let key = 17u32;
|
||||
assert_eq!(Map::get(&key), 0u64);
|
||||
Map::insert(key, 4u64);
|
||||
assert_eq!(Map::get(&key), 4u64);
|
||||
assert_eq!(Map::take(&key), 4u64);
|
||||
assert_eq!(Map::get(&key), 0u64);
|
||||
|
||||
// mutate
|
||||
Map::mutate(&key, |val| {
|
||||
*val = 15;
|
||||
});
|
||||
assert_eq!(Map::get(&key), 15u64);
|
||||
|
||||
// remove
|
||||
Map::remove(&key);
|
||||
assert_eq!(Map::get(&key), 0u64);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_map_enumeration_and_head_should_work() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_eq!(Map::head(), Some(15));
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(15, 42)]);
|
||||
// insert / remove
|
||||
let key = 17u32;
|
||||
Map::insert(key, 4u64);
|
||||
assert_eq!(Map::head(), Some(key));
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(key, 4), (15, 42)]);
|
||||
assert_eq!(Map::take(&15), 42u64);
|
||||
assert_eq!(Map::take(&key), 4u64);
|
||||
assert_eq!(Map::head(), None);
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![]);
|
||||
|
||||
// Add couple of more elements
|
||||
Map::insert(key, 42u64);
|
||||
assert_eq!(Map::head(), Some(key));
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(key, 42)]);
|
||||
Map::insert(key + 1, 43u64);
|
||||
assert_eq!(Map::head(), Some(key + 1));
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(key + 1, 43), (key, 42)]);
|
||||
|
||||
// mutate
|
||||
let key = key + 2;
|
||||
Map::mutate(&key, |val| {
|
||||
*val = 15;
|
||||
});
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(key, 15), (key - 1, 43), (key - 2, 42)]);
|
||||
assert_eq!(Map::head(), Some(key));
|
||||
Map::mutate(&key, |val| {
|
||||
*val = 17;
|
||||
});
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(key, 17), (key - 1, 43), (key - 2, 42)]);
|
||||
|
||||
// remove first
|
||||
Map::remove(&key);
|
||||
assert_eq!(Map::head(), Some(key - 1));
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(key - 1, 43), (key - 2, 42)]);
|
||||
|
||||
// remove last from the list
|
||||
Map::remove(&(key - 2));
|
||||
assert_eq!(Map::head(), Some(key - 1));
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(key - 1, 43)]);
|
||||
|
||||
// remove the last element
|
||||
Map::remove(&(key - 1));
|
||||
assert_eq!(Map::head(), None);
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_map_basic_insert_remove_remove_prefix_should_work() {
|
||||
new_test_ext().execute_with(|| {
|
||||
type DoubleMap = DataDM;
|
||||
// initialized during genesis
|
||||
assert_eq!(DoubleMap::get(&15u32, &16u32), 42u64);
|
||||
|
||||
// get / insert / take
|
||||
let key1 = 17u32;
|
||||
let key2 = 18u32;
|
||||
assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
|
||||
DoubleMap::insert(&key1, &key2, &4u64);
|
||||
assert_eq!(DoubleMap::get(&key1, &key2), 4u64);
|
||||
assert_eq!(DoubleMap::take(&key1, &key2), 4u64);
|
||||
assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
|
||||
|
||||
// mutate
|
||||
DoubleMap::mutate(&key1, &key2, |val| {
|
||||
*val = 15;
|
||||
});
|
||||
assert_eq!(DoubleMap::get(&key1, &key2), 15u64);
|
||||
|
||||
// remove
|
||||
DoubleMap::remove(&key1, &key2);
|
||||
assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
|
||||
|
||||
// remove prefix
|
||||
DoubleMap::insert(&key1, &key2, &4u64);
|
||||
DoubleMap::insert(&key1, &(key2 + 1), &4u64);
|
||||
DoubleMap::insert(&(key1 + 1), &key2, &4u64);
|
||||
DoubleMap::insert(&(key1 + 1), &(key2 + 1), &4u64);
|
||||
DoubleMap::remove_prefix(&key1);
|
||||
assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
|
||||
assert_eq!(DoubleMap::get(&key1, &(key2 + 1)), 0u64);
|
||||
assert_eq!(DoubleMap::get(&(key1 + 1), &key2), 4u64);
|
||||
assert_eq!(DoubleMap::get(&(key1 + 1), &(key2 + 1)), 4u64);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_map_append_should_work() {
|
||||
new_test_ext().execute_with(|| {
|
||||
type DoubleMap = AppendableDM<Test>;
|
||||
|
||||
let key1 = 17u32;
|
||||
let key2 = 18u32;
|
||||
|
||||
DoubleMap::insert(&key1, &key2, &vec![1]);
|
||||
DoubleMap::append(&key1, &key2, &[2, 3]).unwrap();
|
||||
assert_eq!(DoubleMap::get(&key1, &key2), &[1, 2, 3]);
|
||||
});
|
||||
}
|
||||
|
||||
const EXPECTED_METADATA: StorageMetadata = StorageMetadata {
|
||||
prefix: DecodeDifferent::Encode("Example"),
|
||||
entries: DecodeDifferent::Encode(
|
||||
&[
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("Data"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map{
|
||||
hasher: StorageHasher::Twox64Concat,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("u64"),
|
||||
is_linked: true,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructData(PhantomData::<Test>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("OptionLinkedMap"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("u32"),
|
||||
is_linked: true,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructOptionLinkedMap(PhantomData::<Test>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("GenericData"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map{
|
||||
hasher: StorageHasher::Twox128,
|
||||
key: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
value: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
is_linked: true
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGenericData(PhantomData::<Test>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("GenericData2"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map{
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
value: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
is_linked: true
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGenericData2(PhantomData::<Test>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("GetterNoFnKeyword"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")),
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGetterNoFnKeyword(PhantomData::<Test>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("DataDM"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::DoubleMap{
|
||||
hasher: StorageHasher::Twox64Concat,
|
||||
key1: DecodeDifferent::Encode("u32"),
|
||||
key2: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("u64"),
|
||||
key2_hasher: StorageHasher::Blake2_256,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructDataDM(PhantomData::<Test>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("GenericDataDM"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::DoubleMap{
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key1: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
key2: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
value: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
key2_hasher: StorageHasher::Twox128,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGenericDataDM(PhantomData::<Test>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("GenericData2DM"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::DoubleMap{
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key1: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
key2: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
value: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
key2_hasher: StorageHasher::Twox256,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGenericData2DM(PhantomData::<Test>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("AppendableDM"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::DoubleMap{
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key1: DecodeDifferent::Encode("u32"),
|
||||
key2: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
value: DecodeDifferent::Encode("Vec<u32>"),
|
||||
key2_hasher: StorageHasher::Blake2_256,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGenericData2DM(PhantomData::<Test>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
]
|
||||
),
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn store_metadata() {
|
||||
let metadata = Module::<Test>::storage_metadata();
|
||||
pretty_assertions::assert_eq!(EXPECTED_METADATA, metadata);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,553 @@
|
||||
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
pub use frame_metadata::{
|
||||
DecodeDifferent, FnEncode, RuntimeMetadata, ModuleMetadata, RuntimeMetadataLastVersion,
|
||||
DefaultByteGetter, RuntimeMetadataPrefixed, StorageEntryMetadata, StorageMetadata,
|
||||
StorageEntryType, StorageEntryModifier, DefaultByte, StorageHasher, ModuleErrorMetadata
|
||||
};
|
||||
|
||||
/// Implements the metadata support for the given runtime and all its modules.
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
///# mod module0 {
|
||||
///# pub trait Trait {
|
||||
///# type Origin;
|
||||
///# type BlockNumber;
|
||||
///# }
|
||||
///# frame_support::decl_module! {
|
||||
///# pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
///# }
|
||||
///#
|
||||
///# frame_support::decl_storage! {
|
||||
///# trait Store for Module<T: Trait> as TestStorage {}
|
||||
///# }
|
||||
///# }
|
||||
///# use module0 as module1;
|
||||
///# use module0 as module2;
|
||||
///# impl module0::Trait for Runtime {
|
||||
///# type Origin = u32;
|
||||
///# type BlockNumber = u32;
|
||||
///# }
|
||||
///
|
||||
/// struct Runtime;
|
||||
/// frame_support::impl_runtime_metadata! {
|
||||
/// for Runtime with modules
|
||||
/// module0::Module as Module0 with,
|
||||
/// module1::Module as Module1 with,
|
||||
/// module2::Module as Module2 with Storage,
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// In this example, just `MODULE3` implements the `Storage` trait.
|
||||
#[macro_export]
|
||||
macro_rules! impl_runtime_metadata {
|
||||
(
|
||||
for $runtime:ident with modules
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
impl $runtime {
|
||||
pub fn metadata() -> $crate::metadata::RuntimeMetadataPrefixed {
|
||||
$crate::metadata::RuntimeMetadataLastVersion {
|
||||
modules: $crate::__runtime_modules_to_metadata!($runtime;; $( $rest )*),
|
||||
}.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __runtime_modules_to_metadata {
|
||||
(
|
||||
$runtime: ident;
|
||||
$( $metadata:expr ),*;
|
||||
$mod:ident::$module:ident $( < $instance:ident > )? as $name:ident $(with)+ $($kw:ident)*,
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
$crate::__runtime_modules_to_metadata!(
|
||||
$runtime;
|
||||
$( $metadata, )* $crate::metadata::ModuleMetadata {
|
||||
name: $crate::metadata::DecodeDifferent::Encode(stringify!($name)),
|
||||
storage: $crate::__runtime_modules_to_metadata_calls_storage!(
|
||||
$mod, $module $( <$instance> )?, $runtime, $(with $kw)*
|
||||
),
|
||||
calls: $crate::__runtime_modules_to_metadata_calls_call!(
|
||||
$mod, $module $( <$instance> )?, $runtime, $(with $kw)*
|
||||
),
|
||||
event: $crate::__runtime_modules_to_metadata_calls_event!(
|
||||
$mod, $module $( <$instance> )?, $runtime, $(with $kw)*
|
||||
),
|
||||
constants: $crate::metadata::DecodeDifferent::Encode(
|
||||
$crate::metadata::FnEncode(
|
||||
$mod::$module::<$runtime $(, $mod::$instance )?>::module_constants_metadata
|
||||
)
|
||||
),
|
||||
errors: $crate::metadata::DecodeDifferent::Encode(
|
||||
$crate::metadata::FnEncode(
|
||||
<$mod::$module::<$runtime $(, $mod::$instance )?> as $crate::metadata::ModuleErrorMetadata>::metadata
|
||||
)
|
||||
)
|
||||
};
|
||||
$( $rest )*
|
||||
)
|
||||
};
|
||||
(
|
||||
$runtime:ident;
|
||||
$( $metadata:expr ),*;
|
||||
) => {
|
||||
$crate::metadata::DecodeDifferent::Encode(&[ $( $metadata ),* ])
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __runtime_modules_to_metadata_calls_call {
|
||||
(
|
||||
$mod: ident,
|
||||
$module: ident $( <$instance:ident> )?,
|
||||
$runtime: ident,
|
||||
with Call
|
||||
$(with $kws:ident)*
|
||||
) => {
|
||||
Some($crate::metadata::DecodeDifferent::Encode(
|
||||
$crate::metadata::FnEncode(
|
||||
$mod::$module::<$runtime $(, $mod::$instance )?>::call_functions
|
||||
)
|
||||
))
|
||||
};
|
||||
(
|
||||
$mod: ident,
|
||||
$module: ident $( <$instance:ident> )?,
|
||||
$runtime: ident,
|
||||
with $_:ident
|
||||
$(with $kws:ident)*
|
||||
) => {
|
||||
$crate::__runtime_modules_to_metadata_calls_call! {
|
||||
$mod, $module $( <$instance> )?, $runtime, $(with $kws)*
|
||||
};
|
||||
};
|
||||
(
|
||||
$mod: ident,
|
||||
$module: ident $( <$instance:ident> )?,
|
||||
$runtime: ident,
|
||||
) => {
|
||||
None
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __runtime_modules_to_metadata_calls_event {
|
||||
(
|
||||
$mod: ident,
|
||||
$module: ident $( <$instance:ident> )?,
|
||||
$runtime: ident,
|
||||
with Event
|
||||
$(with $kws:ident)*
|
||||
) => {
|
||||
Some($crate::metadata::DecodeDifferent::Encode(
|
||||
$crate::metadata::FnEncode(
|
||||
$crate::paste::expr!{
|
||||
$runtime:: [< __module_events_ $mod $(_ $instance)?>]
|
||||
}
|
||||
)
|
||||
))
|
||||
};
|
||||
(
|
||||
$mod: ident,
|
||||
$module: ident $( <$instance:ident> )?,
|
||||
$runtime: ident,
|
||||
with $_:ident
|
||||
$(with $kws:ident)*
|
||||
) => {
|
||||
$crate::__runtime_modules_to_metadata_calls_event!( $mod, $module $( <$instance> )?, $runtime, $(with $kws)* );
|
||||
};
|
||||
(
|
||||
$mod: ident,
|
||||
$module: ident $( <$instance:ident> )?,
|
||||
$runtime: ident,
|
||||
) => {
|
||||
None
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __runtime_modules_to_metadata_calls_storage {
|
||||
(
|
||||
$mod: ident,
|
||||
$module: ident $( <$instance:ident> )?,
|
||||
$runtime: ident,
|
||||
with Storage
|
||||
$(with $kws:ident)*
|
||||
) => {
|
||||
Some($crate::metadata::DecodeDifferent::Encode(
|
||||
$crate::metadata::FnEncode(
|
||||
$mod::$module::<$runtime $(, $mod::$instance )?>::storage_metadata
|
||||
)
|
||||
))
|
||||
};
|
||||
(
|
||||
$mod: ident,
|
||||
$module: ident $( <$instance:ident> )?,
|
||||
$runtime: ident,
|
||||
with $_:ident
|
||||
$(with $kws:ident)*
|
||||
) => {
|
||||
$crate::__runtime_modules_to_metadata_calls_storage! {
|
||||
$mod, $module $( <$instance> )?, $runtime, $(with $kws)*
|
||||
};
|
||||
};
|
||||
(
|
||||
$mod: ident,
|
||||
$module: ident $( <$instance:ident> )?,
|
||||
$runtime: ident,
|
||||
) => {
|
||||
None
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
// Do not complain about unused `dispatch` and `dispatch_aux`.
|
||||
#[allow(dead_code)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use frame_metadata::{
|
||||
EventMetadata, StorageEntryModifier, StorageEntryType, FunctionMetadata, StorageEntryMetadata,
|
||||
ModuleMetadata, RuntimeMetadataPrefixed, DefaultByte, ModuleConstantMetadata, DefaultByteGetter,
|
||||
ErrorMetadata,
|
||||
};
|
||||
use codec::{Encode, Decode};
|
||||
use crate::traits::Get;
|
||||
|
||||
mod system {
|
||||
use super::*;
|
||||
|
||||
pub trait Trait {
|
||||
const ASSOCIATED_CONST: u64 = 500;
|
||||
type Origin: Into<Result<RawOrigin<Self::AccountId>, Self::Origin>>
|
||||
+ From<RawOrigin<Self::AccountId>>;
|
||||
type AccountId: From<u32> + Encode;
|
||||
type BlockNumber: From<u32> + Encode;
|
||||
type SomeValue: Get<u32>;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
/// Hi, I am a comment.
|
||||
const BlockNumber: T::BlockNumber = 100.into();
|
||||
const GetType: T::AccountId = T::SomeValue::get().into();
|
||||
const ASSOCIATED_CONST: u64 = T::ASSOCIATED_CONST.into();
|
||||
}
|
||||
}
|
||||
|
||||
decl_event!(
|
||||
pub enum Event {
|
||||
SystemEvent,
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum RawOrigin<AccountId> {
|
||||
Root,
|
||||
Signed(AccountId),
|
||||
None,
|
||||
}
|
||||
|
||||
impl<AccountId> From<Option<AccountId>> for RawOrigin<AccountId> {
|
||||
fn from(s: Option<AccountId>) -> RawOrigin<AccountId> {
|
||||
match s {
|
||||
Some(who) => RawOrigin::Signed(who),
|
||||
None => RawOrigin::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type Origin<T> = RawOrigin<<T as Trait>::AccountId>;
|
||||
}
|
||||
|
||||
mod event_module {
|
||||
use crate::dispatch::DispatchResult;
|
||||
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
type Balance;
|
||||
type BlockNumber;
|
||||
}
|
||||
|
||||
decl_event!(
|
||||
pub enum Event<T> where <T as Trait>::Balance
|
||||
{
|
||||
/// Hi, I am a comment.
|
||||
TestEvent(Balance),
|
||||
}
|
||||
);
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
type Error = Error;
|
||||
|
||||
fn aux_0(_origin) -> DispatchResult<Error> { unreachable!() }
|
||||
}
|
||||
}
|
||||
|
||||
crate::decl_error! {
|
||||
pub enum Error {
|
||||
/// Some user input error
|
||||
UserInputError,
|
||||
/// Something bad happened
|
||||
/// this could be due to many reasons
|
||||
BadThingHappened,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod event_module2 {
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
type Balance;
|
||||
type BlockNumber;
|
||||
}
|
||||
|
||||
decl_event!(
|
||||
pub enum Event<T> where <T as Trait>::Balance
|
||||
{
|
||||
TestEvent(Balance),
|
||||
}
|
||||
);
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
crate::decl_storage! {
|
||||
trait Store for Module<T: Trait> as TestStorage {
|
||||
StorageMethod : Option<u32>;
|
||||
}
|
||||
add_extra_genesis {
|
||||
build(|_| {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type EventModule = event_module::Module<TestRuntime>;
|
||||
type EventModule2 = event_module2::Module<TestRuntime>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)]
|
||||
pub struct TestRuntime;
|
||||
|
||||
impl_outer_event! {
|
||||
pub enum TestEvent for TestRuntime {
|
||||
event_module<T>,
|
||||
event_module2<T>,
|
||||
}
|
||||
}
|
||||
|
||||
impl_outer_origin! {
|
||||
pub enum Origin for TestRuntime {}
|
||||
}
|
||||
|
||||
impl_outer_dispatch! {
|
||||
pub enum Call for TestRuntime where origin: Origin {
|
||||
event_module::EventModule,
|
||||
event_module2::EventModule2,
|
||||
}
|
||||
}
|
||||
|
||||
impl event_module::Trait for TestRuntime {
|
||||
type Origin = Origin;
|
||||
type Balance = u32;
|
||||
type BlockNumber = u32;
|
||||
}
|
||||
|
||||
impl event_module2::Trait for TestRuntime {
|
||||
type Origin = Origin;
|
||||
type Balance = u32;
|
||||
type BlockNumber = u32;
|
||||
}
|
||||
|
||||
crate::parameter_types! {
|
||||
pub const SystemValue: u32 = 600;
|
||||
}
|
||||
|
||||
impl system::Trait for TestRuntime {
|
||||
type Origin = Origin;
|
||||
type AccountId = u32;
|
||||
type BlockNumber = u32;
|
||||
type SomeValue = SystemValue;
|
||||
}
|
||||
|
||||
impl_runtime_metadata!(
|
||||
for TestRuntime with modules
|
||||
system::Module as System with Event,
|
||||
event_module::Module as Module with Event Call,
|
||||
event_module2::Module as Module2 with Event Storage Call,
|
||||
);
|
||||
|
||||
struct ConstantBlockNumberByteGetter;
|
||||
impl DefaultByte for ConstantBlockNumberByteGetter {
|
||||
fn default_byte(&self) -> Vec<u8> {
|
||||
100u32.encode()
|
||||
}
|
||||
}
|
||||
|
||||
struct ConstantGetTypeByteGetter;
|
||||
impl DefaultByte for ConstantGetTypeByteGetter {
|
||||
fn default_byte(&self) -> Vec<u8> {
|
||||
SystemValue::get().encode()
|
||||
}
|
||||
}
|
||||
|
||||
struct ConstantAssociatedConstByteGetter;
|
||||
impl DefaultByte for ConstantAssociatedConstByteGetter {
|
||||
fn default_byte(&self) -> Vec<u8> {
|
||||
<TestRuntime as system::Trait>::ASSOCIATED_CONST.encode()
|
||||
}
|
||||
}
|
||||
|
||||
const EXPECTED_METADATA: RuntimeMetadataLastVersion = RuntimeMetadataLastVersion {
|
||||
modules: DecodeDifferent::Encode(&[
|
||||
ModuleMetadata {
|
||||
name: DecodeDifferent::Encode("System"),
|
||||
storage: None,
|
||||
calls: None,
|
||||
event: Some(DecodeDifferent::Encode(
|
||||
FnEncode(||&[
|
||||
EventMetadata {
|
||||
name: DecodeDifferent::Encode("SystemEvent"),
|
||||
arguments: DecodeDifferent::Encode(&[]),
|
||||
documentation: DecodeDifferent::Encode(&[])
|
||||
}
|
||||
])
|
||||
)),
|
||||
constants: DecodeDifferent::Encode(
|
||||
FnEncode(|| &[
|
||||
ModuleConstantMetadata {
|
||||
name: DecodeDifferent::Encode("BlockNumber"),
|
||||
ty: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
value: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&ConstantBlockNumberByteGetter)
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[" Hi, I am a comment."]),
|
||||
},
|
||||
ModuleConstantMetadata {
|
||||
name: DecodeDifferent::Encode("GetType"),
|
||||
ty: DecodeDifferent::Encode("T::AccountId"),
|
||||
value: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&ConstantGetTypeByteGetter)
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
ModuleConstantMetadata {
|
||||
name: DecodeDifferent::Encode("ASSOCIATED_CONST"),
|
||||
ty: DecodeDifferent::Encode("u64"),
|
||||
value: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&ConstantAssociatedConstByteGetter)
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
}
|
||||
])
|
||||
),
|
||||
errors: DecodeDifferent::Encode(FnEncode(|| &[])),
|
||||
},
|
||||
ModuleMetadata {
|
||||
name: DecodeDifferent::Encode("Module"),
|
||||
storage: None,
|
||||
calls: Some(
|
||||
DecodeDifferent::Encode(FnEncode(|| &[
|
||||
FunctionMetadata {
|
||||
name: DecodeDifferent::Encode("aux_0"),
|
||||
arguments: DecodeDifferent::Encode(&[]),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
}
|
||||
]))),
|
||||
event: Some(DecodeDifferent::Encode(
|
||||
FnEncode(||&[
|
||||
EventMetadata {
|
||||
name: DecodeDifferent::Encode("TestEvent"),
|
||||
arguments: DecodeDifferent::Encode(&["Balance"]),
|
||||
documentation: DecodeDifferent::Encode(&[" Hi, I am a comment."])
|
||||
}
|
||||
])
|
||||
)),
|
||||
constants: DecodeDifferent::Encode(FnEncode(|| &[])),
|
||||
errors: DecodeDifferent::Encode(FnEncode(|| &[
|
||||
ErrorMetadata {
|
||||
name: DecodeDifferent::Encode("UserInputError"),
|
||||
documentation: DecodeDifferent::Encode(&[" Some user input error"]),
|
||||
},
|
||||
ErrorMetadata {
|
||||
name: DecodeDifferent::Encode("BadThingHappened"),
|
||||
documentation: DecodeDifferent::Encode(&[
|
||||
" Something bad happened",
|
||||
" this could be due to many reasons",
|
||||
]),
|
||||
},
|
||||
])),
|
||||
},
|
||||
ModuleMetadata {
|
||||
name: DecodeDifferent::Encode("Module2"),
|
||||
storage: Some(DecodeDifferent::Encode(
|
||||
FnEncode(|| StorageMetadata {
|
||||
prefix: DecodeDifferent::Encode("TestStorage"),
|
||||
entries: DecodeDifferent::Encode(
|
||||
&[
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("StorageMethod"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")),
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(
|
||||
&event_module2::__GetByteStructStorageMethod(
|
||||
std::marker::PhantomData::<TestRuntime>
|
||||
)
|
||||
)
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
}
|
||||
]
|
||||
)
|
||||
}),
|
||||
)),
|
||||
calls: Some(DecodeDifferent::Encode(FnEncode(|| &[]))),
|
||||
event: Some(DecodeDifferent::Encode(
|
||||
FnEncode(||&[
|
||||
EventMetadata {
|
||||
name: DecodeDifferent::Encode("TestEvent"),
|
||||
arguments: DecodeDifferent::Encode(&["Balance"]),
|
||||
documentation: DecodeDifferent::Encode(&[])
|
||||
}
|
||||
])
|
||||
)),
|
||||
constants: DecodeDifferent::Encode(FnEncode(|| &[])),
|
||||
errors: DecodeDifferent::Encode(FnEncode(|| &[])),
|
||||
},
|
||||
])
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn runtime_metadata() {
|
||||
let metadata_encoded = TestRuntime::metadata().encode();
|
||||
let metadata_decoded = RuntimeMetadataPrefixed::decode(&mut &metadata_encoded[..]);
|
||||
let expected_metadata: RuntimeMetadataPrefixed = EXPECTED_METADATA.into();
|
||||
|
||||
pretty_assertions::assert_eq!(expected_metadata, metadata_decoded.unwrap());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Macros that define an Origin type. Every function call to your runtime has an origin which
|
||||
//! specifies where the extrinsic was generated from.
|
||||
|
||||
/// Constructs an Origin type for a runtime. This is usually called automatically by the
|
||||
/// construct_runtime macro. See also __create_decl_macro.
|
||||
#[macro_export]
|
||||
macro_rules! impl_outer_origin {
|
||||
|
||||
// Macro transformations (to convert invocations with incomplete parameters to the canonical
|
||||
// form)
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub enum $name:ident for $runtime:ident {
|
||||
$( $rest_without_system:tt )*
|
||||
}
|
||||
) => {
|
||||
$crate::impl_outer_origin! {
|
||||
$(#[$attr])*
|
||||
pub enum $name for $runtime where system = system {
|
||||
$( $rest_without_system )*
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub enum $name:ident for $runtime:ident where system = $system:ident {
|
||||
$( $rest_with_system:tt )*
|
||||
}
|
||||
) => {
|
||||
$crate::impl_outer_origin!(
|
||||
$( #[$attr] )*;
|
||||
$name;
|
||||
$runtime;
|
||||
$system;
|
||||
Modules { $( $rest_with_system )* };
|
||||
);
|
||||
};
|
||||
|
||||
// Generic + Instance
|
||||
(
|
||||
$(#[$attr:meta])*;
|
||||
$name:ident;
|
||||
$runtime:ident;
|
||||
$system:ident;
|
||||
Modules {
|
||||
$module:ident $instance:ident <T>
|
||||
$(, $( $rest_module:tt )* )?
|
||||
};
|
||||
$( $parsed:tt )*
|
||||
) => {
|
||||
$crate::impl_outer_origin!(
|
||||
$( #[$attr] )*;
|
||||
$name;
|
||||
$runtime;
|
||||
$system;
|
||||
Modules { $( $( $rest_module )* )? };
|
||||
$( $parsed )* $module <$runtime> { $instance },
|
||||
);
|
||||
};
|
||||
|
||||
// Instance
|
||||
(
|
||||
$(#[$attr:meta])*;
|
||||
$name:ident;
|
||||
$runtime:ident;
|
||||
$system:ident;
|
||||
Modules {
|
||||
$module:ident $instance:ident
|
||||
$(, $rest_module:tt )*
|
||||
};
|
||||
$( $parsed:tt )*
|
||||
) => {
|
||||
$crate::impl_outer_origin!(
|
||||
$( #[$attr] )*;
|
||||
$name;
|
||||
$runtime;
|
||||
$system;
|
||||
Modules { $( $rest_module )* };
|
||||
$( $parsed )* $module { $instance },
|
||||
);
|
||||
};
|
||||
|
||||
// Generic
|
||||
(
|
||||
$(#[$attr:meta])*;
|
||||
$name:ident;
|
||||
$runtime:ident;
|
||||
$system:ident;
|
||||
Modules {
|
||||
$module:ident <T>
|
||||
$(, $( $rest_module:tt )* )?
|
||||
};
|
||||
$( $parsed:tt )*
|
||||
) => {
|
||||
$crate::impl_outer_origin!(
|
||||
$( #[$attr] )*;
|
||||
$name;
|
||||
$runtime;
|
||||
$system;
|
||||
Modules { $( $( $rest_module )* )? };
|
||||
$( $parsed )* $module <$runtime>,
|
||||
);
|
||||
};
|
||||
|
||||
// No Generic and no Instance
|
||||
(
|
||||
$(#[$attr:meta])*;
|
||||
$name:ident;
|
||||
$runtime:ident;
|
||||
$system:ident;
|
||||
Modules {
|
||||
$module:ident
|
||||
$(, $( $rest_module:tt )* )?
|
||||
};
|
||||
$( $parsed:tt )*
|
||||
) => {
|
||||
$crate::impl_outer_origin!(
|
||||
$( #[$attr] )*;
|
||||
$name;
|
||||
$runtime;
|
||||
$system;
|
||||
Modules { $( $( $rest_module )* )? };
|
||||
$( $parsed )* $module,
|
||||
);
|
||||
};
|
||||
|
||||
// The main macro expansion that actually renders the Origin enum code.
|
||||
(
|
||||
$(#[$attr:meta])*;
|
||||
$name:ident;
|
||||
$runtime:ident;
|
||||
$system:ident;
|
||||
Modules { };
|
||||
$( $module:ident $( < $generic:ident > )? $( { $generic_instance:ident } )? ,)*
|
||||
) => {
|
||||
$crate::paste::item! {
|
||||
#[derive(Clone, PartialEq, Eq, $crate::RuntimeDebug)]
|
||||
$(#[$attr])*
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum $name {
|
||||
system($system::Origin<$runtime>),
|
||||
$(
|
||||
[< $module $( _ $generic_instance )? >]
|
||||
($module::Origin < $( $generic, )? $( $module::$generic_instance )? > ),
|
||||
)*
|
||||
#[allow(dead_code)]
|
||||
Void($crate::Void)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl $name {
|
||||
pub const NONE: Self = $name::system($system::RawOrigin::None);
|
||||
pub const ROOT: Self = $name::system($system::RawOrigin::Root);
|
||||
pub fn signed(by: <$runtime as $system::Trait>::AccountId) -> Self {
|
||||
$name::system($system::RawOrigin::Signed(by))
|
||||
}
|
||||
}
|
||||
impl From<$system::Origin<$runtime>> for $name {
|
||||
fn from(x: $system::Origin<$runtime>) -> Self {
|
||||
$name::system(x)
|
||||
}
|
||||
}
|
||||
impl Into<$crate::rstd::result::Result<$system::Origin<$runtime>, $name>> for $name {
|
||||
fn into(self) -> $crate::rstd::result::Result<$system::Origin<$runtime>, Self> {
|
||||
if let $name::system(l) = self {
|
||||
Ok(l)
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<Option<<$runtime as $system::Trait>::AccountId>> for $name {
|
||||
fn from(x: Option<<$runtime as $system::Trait>::AccountId>) -> Self {
|
||||
<$system::Origin<$runtime>>::from(x).into()
|
||||
}
|
||||
}
|
||||
$(
|
||||
$crate::paste::item! {
|
||||
impl From<$module::Origin < $( $generic )? $(, $module::$generic_instance )? > > for $name {
|
||||
fn from(x: $module::Origin < $( $generic )? $(, $module::$generic_instance )? >) -> Self {
|
||||
$name::[< $module $( _ $generic_instance )? >](x)
|
||||
}
|
||||
}
|
||||
impl Into<
|
||||
$crate::rstd::result::Result<
|
||||
$module::Origin < $( $generic )? $(, $module::$generic_instance )? >,
|
||||
$name,
|
||||
>>
|
||||
for $name {
|
||||
fn into(self) -> $crate::rstd::result::Result<
|
||||
$module::Origin < $( $generic )? $(, $module::$generic_instance )? >,
|
||||
Self,
|
||||
> {
|
||||
if let $name::[< $module $( _ $generic_instance )? >](l) = self {
|
||||
Ok(l)
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod system {
|
||||
pub trait Trait {
|
||||
type AccountId;
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum RawOrigin<AccountId> {
|
||||
Root,
|
||||
Signed(AccountId),
|
||||
None,
|
||||
}
|
||||
|
||||
impl<AccountId> From<Option<AccountId>> for RawOrigin<AccountId> {
|
||||
fn from(s: Option<AccountId>) -> RawOrigin<AccountId> {
|
||||
match s {
|
||||
Some(who) => RawOrigin::Signed(who),
|
||||
None => RawOrigin::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type Origin<T> = RawOrigin<<T as Trait>::AccountId>;
|
||||
}
|
||||
|
||||
mod origin_without_generic {
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Origin;
|
||||
}
|
||||
|
||||
mod origin_with_generic {
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Origin<T> {
|
||||
t: T
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct TestRuntime;
|
||||
|
||||
impl system::Trait for TestRuntime {
|
||||
type AccountId = u32;
|
||||
}
|
||||
|
||||
impl_outer_origin!(
|
||||
pub enum OriginWithoutSystem for TestRuntime {
|
||||
origin_without_generic,
|
||||
origin_with_generic<T>,
|
||||
}
|
||||
);
|
||||
|
||||
impl_outer_origin!(
|
||||
pub enum OriginWithoutSystem2 for TestRuntime {
|
||||
origin_with_generic<T>,
|
||||
origin_without_generic
|
||||
}
|
||||
);
|
||||
|
||||
impl_outer_origin!(
|
||||
pub enum OriginWithSystem for TestRuntime where system = system {
|
||||
origin_without_generic,
|
||||
origin_with_generic<T>
|
||||
}
|
||||
);
|
||||
|
||||
impl_outer_origin!(
|
||||
pub enum OriginWithSystem2 for TestRuntime where system = system {
|
||||
origin_with_generic<T>,
|
||||
origin_without_generic,
|
||||
}
|
||||
);
|
||||
|
||||
impl_outer_origin!(
|
||||
pub enum OriginEmpty for TestRuntime where system = system {}
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,948 @@
|
||||
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Macros to define a runtime. A runtime is basically all your logic running in Substrate,
|
||||
//! consisting of selected SRML modules and maybe some of your own modules.
|
||||
//! A lot of supporting logic is automatically generated for a runtime,
|
||||
//! mostly to combine data types and metadata of the included modules.
|
||||
|
||||
/// Construct a runtime, with the given name and the given modules.
|
||||
///
|
||||
/// The parameters here are specific types for `Block`, `NodeBlock`, and `InherentData`
|
||||
/// and the modules that are used by the runtime.
|
||||
/// `Block` is the block type that is used in the runtime and `NodeBlock` is the block type
|
||||
/// that is used in the node. For instance they can differ in the extrinsics type.
|
||||
///
|
||||
/// # Example:
|
||||
///
|
||||
/// ```nocompile
|
||||
/// construct_runtime!(
|
||||
/// pub enum Runtime where
|
||||
/// Block = Block,
|
||||
/// NodeBlock = runtime::Block,
|
||||
/// UncheckedExtrinsic = UncheckedExtrinsic
|
||||
/// {
|
||||
/// System: system,
|
||||
/// Test: test::{default},
|
||||
/// Test2: test_with_long_module::{Module},
|
||||
///
|
||||
/// // Module with instances
|
||||
/// Test3_Instance1: test3::<Instance1>::{Module, Call, Storage, Event<T, I>, Config<T, I>, Origin<T, I>},
|
||||
/// Test3_DefaultInstance: test3::{Module, Call, Storage, Event<T>, Config<T>, Origin<T>},
|
||||
/// }
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// The module `System: system` will expand to `System: system::{Module, Call, Storage, Event<T>, Config<T>}`.
|
||||
/// The identifier `System` is the name of the module and the lower case identifier `system` is the
|
||||
/// name of the Rust module/crate for this Substrate module.
|
||||
///
|
||||
/// The module `Test: test::{default}` will expand to
|
||||
/// `Test: test::{Module, Call, Storage, Event<T>, Config<T>}`.
|
||||
///
|
||||
/// The module `Test2: test_with_long_module::{Module}` will expand to
|
||||
/// `Test2: test_with_long_module::{Module}`.
|
||||
///
|
||||
/// We provide support for the following types in a module:
|
||||
///
|
||||
/// - `Module`
|
||||
/// - `Call`
|
||||
/// - `Storage`
|
||||
/// - `Event` or `Event<T>` (if the event is generic)
|
||||
/// - `Origin` or `Origin<T>` (if the origin is generic)
|
||||
/// - `Config` or `Config<T>` (if the config is generic)
|
||||
/// - `Inherent $( (CALL) )*` - If the module provides/can check inherents. The optional parameter
|
||||
/// is for modules that use a `Call` from a different module as
|
||||
/// inherent.
|
||||
/// - `ValidateUnsigned` - If the module validates unsigned extrinsics.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The population of the genesis storage depends on the order of modules. So, if one of your
|
||||
/// modules depends on another module, the module that is depended upon needs to come before
|
||||
/// the module depending on it.
|
||||
#[macro_export]
|
||||
macro_rules! construct_runtime {
|
||||
|
||||
// Macro transformations (to convert invocations with incomplete parameters to the canonical
|
||||
// form)
|
||||
|
||||
(
|
||||
pub enum $runtime:ident
|
||||
where
|
||||
Block = $block:ident,
|
||||
NodeBlock = $node_block:ty,
|
||||
UncheckedExtrinsic = $uncheckedextrinsic:ident
|
||||
{
|
||||
$( $rest:tt )*
|
||||
}
|
||||
) => {
|
||||
$crate::construct_runtime!(
|
||||
{
|
||||
$runtime;
|
||||
$block;
|
||||
$node_block;
|
||||
$uncheckedextrinsic;
|
||||
};
|
||||
{};
|
||||
$( $rest )*
|
||||
);
|
||||
};
|
||||
// No modules given, expand to the default module set.
|
||||
(
|
||||
{ $( $preset:tt )* };
|
||||
{ $( $expanded:tt )* };
|
||||
$name:ident: $module:ident,
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
$crate::construct_runtime!(
|
||||
{ $( $preset )* };
|
||||
{ $( $expanded )* };
|
||||
$name: $module::{default},
|
||||
$( $rest )*
|
||||
);
|
||||
};
|
||||
// `default` identifier given, expand to default + given extra modules
|
||||
(
|
||||
{ $( $preset:tt )* };
|
||||
{ $( $expanded:tt )* };
|
||||
$name:ident: $module:ident::{
|
||||
default
|
||||
$(,
|
||||
$modules:ident
|
||||
$( <$modules_generic:ident> )*
|
||||
$( ( $( $modules_args:ident ),* ) )*
|
||||
)*
|
||||
},
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
$crate::construct_runtime!(
|
||||
{ $( $preset )* };
|
||||
{
|
||||
$( $expanded )*
|
||||
$name: $module::{
|
||||
Module, Call, Storage, Event<T>, Config<T>
|
||||
$(,
|
||||
$modules $( <$modules_generic> )*
|
||||
$( ( $( $modules_args ),* ) )*
|
||||
)*
|
||||
},
|
||||
};
|
||||
$( $rest )*
|
||||
);
|
||||
};
|
||||
// Take all modules as given by the user.
|
||||
(
|
||||
{ $( $preset:tt )* };
|
||||
{ $( $expanded:tt )* };
|
||||
$name:ident: $module:ident :: $( < $module_instance:ident >:: )? {
|
||||
$(
|
||||
$modules:ident
|
||||
$( <$modules_generic:ident> )*
|
||||
$( ( $( $modules_args:ident ),* ) )*
|
||||
),*
|
||||
},
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
$crate::construct_runtime!(
|
||||
{ $( $preset )* };
|
||||
{
|
||||
$( $expanded )*
|
||||
$name: $module:: $( < $module_instance >:: )? {
|
||||
$(
|
||||
$modules $( <$modules_generic> )*
|
||||
$( ( $( $modules_args ),* ) )*
|
||||
),*
|
||||
},
|
||||
};
|
||||
$( $rest )*
|
||||
);
|
||||
};
|
||||
// The main macro expansion that actually renders the Runtime code.
|
||||
(
|
||||
{
|
||||
$runtime:ident;
|
||||
$block:ident;
|
||||
$node_block:ty;
|
||||
$uncheckedextrinsic:ident;
|
||||
};
|
||||
{
|
||||
$(
|
||||
$name:ident: $module:ident :: $( < $module_instance:ident >:: )? {
|
||||
$(
|
||||
$modules:ident
|
||||
$( <$modules_generic:ident> )*
|
||||
$( ( $( $modules_args:ident ),* ) )*
|
||||
),*
|
||||
},
|
||||
)*
|
||||
};
|
||||
) => {
|
||||
#[derive(Clone, Copy, PartialEq, Eq, $crate::RuntimeDebug)]
|
||||
pub struct $runtime;
|
||||
impl $crate::sr_primitives::traits::GetNodeBlockType for $runtime {
|
||||
type NodeBlock = $node_block;
|
||||
}
|
||||
impl $crate::sr_primitives::traits::GetRuntimeBlockType for $runtime {
|
||||
type RuntimeBlock = $block;
|
||||
}
|
||||
$crate::__decl_outer_event!(
|
||||
$runtime;
|
||||
$(
|
||||
$name: $module:: $( < $module_instance >:: )? {
|
||||
$( $modules $( <$modules_generic> )* ),*
|
||||
}
|
||||
),*
|
||||
);
|
||||
$crate::__decl_outer_origin!(
|
||||
$runtime;
|
||||
$(
|
||||
$name: $module:: $( < $module_instance >:: )? {
|
||||
$( $modules $( <$modules_generic> )* ),*
|
||||
}
|
||||
),*
|
||||
);
|
||||
$crate::__decl_all_modules!(
|
||||
$runtime;
|
||||
;
|
||||
{};
|
||||
{};
|
||||
$(
|
||||
$name: $module:: $( < $module_instance >:: )? { $( $modules ),* },
|
||||
)*
|
||||
);
|
||||
$crate::__decl_outer_dispatch!(
|
||||
$runtime;
|
||||
;
|
||||
$(
|
||||
$name: $module::{ $( $modules ),* }
|
||||
),*;
|
||||
);
|
||||
$crate::__decl_runtime_metadata!(
|
||||
$runtime;
|
||||
{};
|
||||
$(
|
||||
$name: $module:: $( < $module_instance >:: )? { $( $modules )* }
|
||||
)*
|
||||
);
|
||||
$crate::__decl_outer_config!(
|
||||
$runtime;
|
||||
{};
|
||||
$(
|
||||
$name: $module:: $( < $module_instance >:: )? {
|
||||
$( $modules $( <$modules_generic> )* ),*
|
||||
},
|
||||
)*
|
||||
);
|
||||
$crate::__decl_outer_inherent!(
|
||||
$runtime;
|
||||
$block;
|
||||
$uncheckedextrinsic;
|
||||
;
|
||||
$(
|
||||
$name: $module::{ $( $modules $( ( $( $modules_args )* ) )* ),* }
|
||||
),*;
|
||||
);
|
||||
$crate::__impl_outer_validate_unsigned!(
|
||||
$runtime;
|
||||
{};
|
||||
$(
|
||||
$name: $module::{ $( $modules $( ( $( $modules_args )* ) )* )* }
|
||||
)*
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A macro that generates a "__decl" private macro that transforms parts of the runtime definition
|
||||
/// to feed them into a public "impl" macro which accepts the format
|
||||
/// "pub enum $name for $runtime where system = $system".
|
||||
///
|
||||
/// Used to define Event and Origin associated types.
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __create_decl_macro {
|
||||
(
|
||||
// Parameter $d is a hack for the following issue:
|
||||
// https://github.com/rust-lang/rust/issues/35853
|
||||
$macro_name:ident, $macro_outer_name:ident, $macro_enum_name:ident, $d:tt
|
||||
) => {
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! $macro_name {
|
||||
(
|
||||
$runtime:ident;
|
||||
$d( $name:ident : $module:ident:: $d( < $module_instance:ident >:: )? {
|
||||
$d( $modules:ident $d( <$modules_generic:ident> ),* ),*
|
||||
}),*
|
||||
) => {
|
||||
$d crate::$macro_name!(@inner
|
||||
$runtime;
|
||||
;
|
||||
{};
|
||||
$d(
|
||||
$name: $module:: $d( < $module_instance >:: )? {
|
||||
$d( $modules $d( <$modules_generic> )* ),*
|
||||
},
|
||||
)*
|
||||
);
|
||||
};
|
||||
// Parse system module
|
||||
(@inner
|
||||
$runtime:ident;
|
||||
; // there can not be multiple `System`s
|
||||
{ $d( $parsed:tt )* };
|
||||
System: $module:ident::{
|
||||
$d( $modules:ident $d( <$modules_generic:ident> )* ),*
|
||||
},
|
||||
$d( $rest:tt )*
|
||||
) => {
|
||||
$d crate::$macro_name!(@inner
|
||||
$runtime;
|
||||
$module;
|
||||
{ $d( $parsed )* };
|
||||
$d( $rest )*
|
||||
);
|
||||
};
|
||||
// Parse instantiable module with generic
|
||||
(@inner
|
||||
$runtime:ident;
|
||||
$d( $system:ident )?;
|
||||
{ $d( $parsed:tt )* };
|
||||
$name:ident : $module:ident:: < $module_instance:ident >:: {
|
||||
$macro_enum_name <$event_generic:ident> $d(, $ingore:ident $d( <$ignor:ident> )* )*
|
||||
},
|
||||
$d( $rest:tt )*
|
||||
) => {
|
||||
$d crate::$macro_name!(@inner
|
||||
$runtime;
|
||||
$d( $system )?;
|
||||
{
|
||||
$d( $parsed )*
|
||||
$module $module_instance <$event_generic>,
|
||||
};
|
||||
$d( $rest )*
|
||||
);
|
||||
};
|
||||
// Parse instantiable module with no generic
|
||||
(@inner
|
||||
$runtime:ident;
|
||||
$d( $system:ident )?;
|
||||
{ $d( $parsed:tt )* };
|
||||
$name:ident : $module:ident:: < $module_instance:ident >:: {
|
||||
$macro_enum_name $d(, $ingore:ident $d( <$ignor:ident> )* )*
|
||||
},
|
||||
$d( $rest:tt )*
|
||||
) => {
|
||||
compile_error!(concat!(
|
||||
"Instantiable module with not generic ", stringify!($macro_enum_name),
|
||||
" cannot be constructed: module `", stringify!($name), "` must have generic ",
|
||||
stringify!($macro_enum_name), "."
|
||||
));
|
||||
};
|
||||
// Parse instantiable module with no generic
|
||||
(@inner
|
||||
$runtime:ident;
|
||||
$d( $system:ident )?;
|
||||
{ $d( $parsed:tt )* };
|
||||
$name:ident : $module:ident:: {
|
||||
$macro_enum_name $d( <$event_generic:ident> )* $d(, $ignore:ident $d( <$ignor:ident> )* )*
|
||||
},
|
||||
$d( $rest:tt )*
|
||||
) => {
|
||||
$d crate::$macro_name!(@inner
|
||||
$runtime;
|
||||
$d( $system )?;
|
||||
{
|
||||
$d( $parsed )*
|
||||
$module $d( <$event_generic> )*,
|
||||
};
|
||||
$d( $rest )*
|
||||
);
|
||||
};
|
||||
// Ignore keyword
|
||||
(@inner
|
||||
$runtime:ident;
|
||||
$d( $system:ident )?;
|
||||
{ $d( $parsed:tt )* };
|
||||
$name:ident : $module:ident:: $d( < $module_instance:ident >:: )? {
|
||||
$ingore:ident $d( <$ignor:ident> )* $d(, $modules:ident $d( <$modules_generic:ident> )* )*
|
||||
},
|
||||
$d( $rest:tt )*
|
||||
) => {
|
||||
$d crate::$macro_name!(@inner
|
||||
$runtime;
|
||||
$d( $system )?;
|
||||
{ $d( $parsed )* };
|
||||
$name: $module:: $d( < $module_instance >:: )? { $d( $modules $d( <$modules_generic> )* ),* },
|
||||
$d( $rest )*
|
||||
);
|
||||
};
|
||||
// Ignore module
|
||||
(@inner
|
||||
$runtime:ident;
|
||||
$d( $system:ident )?;
|
||||
{ $d( $parsed:tt )* };
|
||||
$name:ident: $module:ident:: $d( < $module_instance:ident >:: )? {},
|
||||
$d( $rest:tt )*
|
||||
) => {
|
||||
$d crate::$macro_name!(@inner
|
||||
$runtime;
|
||||
$d( $system )?;
|
||||
{ $d( $parsed )* };
|
||||
$d( $rest )*
|
||||
);
|
||||
};
|
||||
// Expand
|
||||
(@inner
|
||||
$runtime:ident;
|
||||
$system:ident;
|
||||
{ $d( $parsed_modules:ident $d( $instance:ident )? $d( <$parsed_generic:ident> )? ,)* };
|
||||
) => {
|
||||
$d crate::$macro_outer_name! {
|
||||
pub enum $macro_enum_name for $runtime where system = $system {
|
||||
$d(
|
||||
$parsed_modules $d( $instance )? $d( <$parsed_generic> )?,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__create_decl_macro!(__decl_outer_event, impl_outer_event, Event, $);
|
||||
__create_decl_macro!(__decl_outer_origin, impl_outer_origin, Origin, $);
|
||||
|
||||
/// A macro that defines all modules as an associated types of the Runtime type.
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __decl_all_modules {
|
||||
(
|
||||
$runtime:ident;
|
||||
;
|
||||
{ $( $parsed:tt )* };
|
||||
{ $( $parsed_nested:tt )* };
|
||||
System: $module:ident::{ Module $(, $modules:ident )* },
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
$crate::__decl_all_modules!(
|
||||
$runtime;
|
||||
$module;
|
||||
{ $( $parsed )* };
|
||||
{ $( $parsed_nested )* };
|
||||
$( $rest )*
|
||||
);
|
||||
};
|
||||
(
|
||||
$runtime:ident;
|
||||
$( $system:ident )?;
|
||||
{ $( $parsed:tt )* };
|
||||
{};
|
||||
$name:ident: $module:ident:: $( < $module_instance:ident >:: )? { Module $(, $modules:ident )* },
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
$crate::__decl_all_modules!(
|
||||
$runtime;
|
||||
$( $system )?;
|
||||
{
|
||||
$( $parsed )*
|
||||
$module::$name $(<$module_instance>)?,
|
||||
};
|
||||
{ $name };
|
||||
$( $rest )*
|
||||
);
|
||||
};
|
||||
(
|
||||
$runtime:ident;
|
||||
$( $system:ident )?;
|
||||
{ $( $parsed:tt )* };
|
||||
{ $( $parsed_nested:tt )* };
|
||||
$name:ident: $module:ident:: $( < $module_instance:ident >:: )? { Module $(, $modules:ident )* },
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
$crate::__decl_all_modules!(
|
||||
$runtime;
|
||||
$( $system )?;
|
||||
{
|
||||
$( $parsed )*
|
||||
$module::$name $(<$module_instance>)?,
|
||||
};
|
||||
{ ( $( $parsed_nested )*, $name, ) };
|
||||
$( $rest )*
|
||||
);
|
||||
};
|
||||
(
|
||||
$runtime:ident;
|
||||
$( $system:ident )?;
|
||||
{ $( $parsed:tt )* };
|
||||
{ $( $parsed_nested:tt )* };
|
||||
$name:ident: $module:ident:: $( < $module_instance:ident >:: )? { $ignore:ident $(, $modules:ident )* },
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
$crate::__decl_all_modules!(
|
||||
$runtime;
|
||||
$( $system )?;
|
||||
{ $( $parsed )* };
|
||||
{ $( $parsed_nested )* };
|
||||
$name: $module::{ $( $modules ),* },
|
||||
$( $rest )*
|
||||
);
|
||||
};
|
||||
(
|
||||
$runtime:ident;
|
||||
$( $system:ident )?;
|
||||
{ $( $parsed:tt )* };
|
||||
$name:ident: $module:ident:: $( < $module_instance:ident >:: )? {},
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
$crate::__decl_all_modules!(
|
||||
$runtime;
|
||||
$( $system )?;
|
||||
{ $( $parsed )* };
|
||||
{ $( $parsed_nested )* };
|
||||
$( $rest )*
|
||||
);
|
||||
};
|
||||
(
|
||||
$runtime:ident;
|
||||
$system:ident;
|
||||
{ $( $parsed_module:ident :: $parsed_name:ident $(<$instance:ident>)? ,)*};
|
||||
{ $( $parsed_nested:tt )* };
|
||||
) => {
|
||||
pub type System = system::Module<$runtime>;
|
||||
$(
|
||||
pub type $parsed_name = $parsed_module::Module<$runtime $(, $parsed_module::$instance )?>;
|
||||
)*
|
||||
type AllModules = ( $( $parsed_nested )* );
|
||||
}
|
||||
}
|
||||
|
||||
/// A macro that defines the Call enum to represent calls to functions in the modules included
|
||||
/// in the runtime (by wrapping the values of all FooModule::Call enums).
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __decl_outer_dispatch {
|
||||
(
|
||||
$runtime:ident;
|
||||
$( $parsed_modules:ident :: $parsed_name:ident ),*;
|
||||
$name:ident: $module:ident::{
|
||||
Call $(, $modules:ident $( <$modules_generic:ident> )* )*
|
||||
}
|
||||
$(, $rest_name:ident : $rest_module:ident::{
|
||||
$( $rest_modules:ident $( <$rest_modules_generic:ident> )* ),*
|
||||
})*;
|
||||
) => {
|
||||
$crate::__decl_outer_dispatch!(
|
||||
$runtime;
|
||||
$( $parsed_modules :: $parsed_name, )* $module::$name;
|
||||
$(
|
||||
$rest_name: $rest_module::{
|
||||
$( $rest_modules $( <$rest_modules_generic> )* ),*
|
||||
}
|
||||
),*;
|
||||
);
|
||||
};
|
||||
(
|
||||
$runtime:ident;
|
||||
$( $parsed_modules:ident :: $parsed_name:ident ),*;
|
||||
$name:ident: $module:ident::{
|
||||
$ignore:ident $( <$ignor:ident> )* $(, $modules:ident $( <$modules_generic:ident> )* )*
|
||||
}
|
||||
$(, $rest_name:ident : $rest_module:ident::{
|
||||
$( $rest_modules:ident $( <$rest_modules_generic:ident> )* ),*
|
||||
})*;
|
||||
) => {
|
||||
$crate::__decl_outer_dispatch!(
|
||||
$runtime;
|
||||
$( $parsed_modules :: $parsed_name ),*;
|
||||
$name: $module::{ $( $modules $( <$modules_generic> )* ),* }
|
||||
$(
|
||||
, $rest_name: $rest_module::{
|
||||
$( $rest_modules $( <$rest_modules_generic> )* ),*
|
||||
}
|
||||
)*;
|
||||
);
|
||||
};
|
||||
(
|
||||
$runtime:ident;
|
||||
$( $parsed_modules:ident :: $parsed_name:ident ),*;
|
||||
$name:ident: $module:ident::{}
|
||||
$(, $rest_name:ident : $rest_module:ident::{
|
||||
$( $rest_modules:ident $( <$rest_modules_generic:ident> )* ),*
|
||||
})*;
|
||||
) => {
|
||||
$crate::__decl_outer_dispatch!(
|
||||
$runtime;
|
||||
$( $parsed_modules :: $parsed_name ),*;
|
||||
$(
|
||||
$rest_name: $rest_module::{
|
||||
$( $rest_modules $( <$rest_modules_generic> )* ),*
|
||||
}
|
||||
),*;
|
||||
);
|
||||
};
|
||||
(
|
||||
$runtime:ident;
|
||||
$( $parsed_modules:ident :: $parsed_name:ident ),*;
|
||||
;
|
||||
) => {
|
||||
$crate::impl_outer_dispatch!(
|
||||
pub enum Call for $runtime where origin: Origin {
|
||||
$( $parsed_modules::$parsed_name, )*
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/// A private macro that generates metadata() method for the runtime. See impl_runtime_metadata macro.
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __decl_runtime_metadata {
|
||||
// leading is Module : parse
|
||||
(
|
||||
$runtime:ident;
|
||||
{ $( $parsed:tt )* };
|
||||
$( { leading_module: $( $leading_module:ident )* } )?
|
||||
$name:ident: $module:ident:: $( < $module_instance:ident >:: )? {
|
||||
Module $( $modules:ident )*
|
||||
}
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
$crate::__decl_runtime_metadata!(
|
||||
$runtime;
|
||||
{
|
||||
$( $parsed )*
|
||||
$module $( < $module_instance > )? as $name {
|
||||
$( $( $leading_module )* )? $( $modules )*
|
||||
}
|
||||
};
|
||||
$( $rest )*
|
||||
);
|
||||
};
|
||||
// leading isn't Module : put it in leadings
|
||||
(
|
||||
$runtime:ident;
|
||||
{ $( $parsed:tt )* };
|
||||
$( { leading_module: $( $leading_module:ident )* } )?
|
||||
$name:ident: $module:ident:: $( < $module_instance:ident >:: )? {
|
||||
$other_module:ident $( $modules:ident )*
|
||||
}
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
$crate::__decl_runtime_metadata!(
|
||||
$runtime;
|
||||
{ $( $parsed )* };
|
||||
{ leading_module: $( $( $leading_module )* )? $other_module }
|
||||
$name: $module:: $( < $module_instance >:: )? {
|
||||
$( $modules )*
|
||||
}
|
||||
$( $rest )*
|
||||
);
|
||||
};
|
||||
// does not contain Module : skip
|
||||
(
|
||||
$runtime:ident;
|
||||
{ $( $parsed:tt )* };
|
||||
$( { leading_module: $( $leading_module:ident )* } )?
|
||||
$name:ident: $module:ident:: $( < $module_instance:ident >:: )? {}
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
$crate::__decl_runtime_metadata!(
|
||||
$runtime;
|
||||
{ $( $parsed )* };
|
||||
$( $rest )*
|
||||
);
|
||||
};
|
||||
// end of decl
|
||||
(
|
||||
$runtime:ident;
|
||||
{
|
||||
$(
|
||||
$parsed_modules:ident $( < $module_instance:ident > )? as $parsed_name:ident {
|
||||
$( $withs:ident )*
|
||||
}
|
||||
)*
|
||||
};
|
||||
) => {
|
||||
$crate::impl_runtime_metadata!(
|
||||
for $runtime with modules
|
||||
$( $parsed_modules::Module $( < $module_instance > )? as $parsed_name
|
||||
with $( $withs )* , )*
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A private macro that generates GenesisConfig for the runtime. See `impl_outer_config!` macro.
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __decl_outer_config {
|
||||
(
|
||||
$runtime:ident;
|
||||
{ $( $parsed:tt )* };
|
||||
$name:ident: $module:ident:: $( < $module_instance:ident >:: )? {
|
||||
Config $( <$config_generic:ident> )?
|
||||
$(, $modules:ident $( <$modules_generic:ident> )* )*
|
||||
},
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
$crate::__decl_outer_config!(
|
||||
$runtime;
|
||||
{
|
||||
$( $parsed )*
|
||||
$module::$name $( $module_instance )? $( <$config_generic> )?,
|
||||
};
|
||||
$( $rest )*
|
||||
);
|
||||
};
|
||||
(
|
||||
$runtime:ident;
|
||||
{ $( $parsed:tt )* };
|
||||
$name:ident: $module:ident:: $( < $module_instance:ident >:: )? {
|
||||
$ingore:ident $( <$ignore_gen:ident> )*
|
||||
$(, $modules:ident $( <$modules_generic:ident> )* )*
|
||||
},
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
$crate::__decl_outer_config!(
|
||||
$runtime;
|
||||
{ $( $parsed )* };
|
||||
$name: $module:: $( < $module_instance >:: )? { $( $modules $( <$modules_generic> )* ),* },
|
||||
$( $rest )*
|
||||
);
|
||||
};
|
||||
(
|
||||
$runtime:ident;
|
||||
{ $( $parsed:tt )* };
|
||||
$name:ident: $module:ident:: $( < $module_instance:ident >:: )? {},
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
$crate::__decl_outer_config!(
|
||||
$runtime;
|
||||
{ $( $parsed )* };
|
||||
$( $rest )*
|
||||
);
|
||||
};
|
||||
(
|
||||
$runtime:ident;
|
||||
{
|
||||
$(
|
||||
$parsed_modules:ident :: $parsed_name:ident $( $parsed_instance:ident )?
|
||||
$(
|
||||
<$parsed_generic:ident>
|
||||
)*
|
||||
,)*
|
||||
};
|
||||
) => {
|
||||
$crate::paste::item! {
|
||||
$crate::sr_primitives::impl_outer_config!(
|
||||
pub struct GenesisConfig for $runtime {
|
||||
$(
|
||||
[< $parsed_name Config >] =>
|
||||
$parsed_modules $( $parsed_instance )? $( <$parsed_generic> )*,
|
||||
)*
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// A private macro that generates check_inherents() implementation for the runtime.
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __decl_outer_inherent {
|
||||
(
|
||||
$runtime:ident;
|
||||
$block:ident;
|
||||
$uncheckedextrinsic:ident;
|
||||
$( $parsed_name:ident :: $parsed_call:ident ),*;
|
||||
$name:ident: $module:ident::{
|
||||
Inherent $(, $modules:ident $( ( $( $modules_call:ident )* ) )* )*
|
||||
}
|
||||
$(, $rest_name:ident : $rest_module:ident::{
|
||||
$( $rest_modules:ident $( ( $( $rest_call:ident )* ) )* ),*
|
||||
})*;
|
||||
) => {
|
||||
$crate::__decl_outer_inherent!(
|
||||
$runtime;
|
||||
$block;
|
||||
$uncheckedextrinsic;
|
||||
$( $parsed_name :: $parsed_call, )* $name::$name;
|
||||
$(
|
||||
$rest_name: $rest_module::{
|
||||
$( $rest_modules $( ( $( $rest_call )* ) )* ),*
|
||||
}
|
||||
),*;
|
||||
);
|
||||
};
|
||||
(
|
||||
$runtime:ident;
|
||||
$block:ident;
|
||||
$uncheckedextrinsic:ident;
|
||||
$( $parsed_name:ident :: $parsed_call:ident ),*;
|
||||
$name:ident: $module:ident::{
|
||||
Inherent ( $call:ident ) $(, $modules:ident $( ( $( $modules_call:ident )* ) )* )*
|
||||
}
|
||||
$(, $rest_name:ident : $rest_module:ident::{
|
||||
$( $rest_modules:ident $( ( $( $rest_call:ident )* ) )* ),*
|
||||
})*;
|
||||
) => {
|
||||
$crate::__decl_outer_inherent!(
|
||||
$runtime;
|
||||
$block;
|
||||
$uncheckedextrinsic;
|
||||
$( $parsed_name :: $parsed_call, )* $name::$call;
|
||||
$(
|
||||
$rest_name: $rest_module::{
|
||||
$( $rest_modules $( ( $( $rest_call )* ) )* ),*
|
||||
}
|
||||
),*;
|
||||
);
|
||||
};
|
||||
(
|
||||
$runtime:ident;
|
||||
$block:ident;
|
||||
$uncheckedextrinsic:ident;
|
||||
$( $parsed_name:ident :: $parsed_call:ident ),*;
|
||||
$name:ident: $module:ident::{
|
||||
$ignore:ident $( ( $( $ignor:ident )* ) )*
|
||||
$(, $modules:ident $( ( $( $modules_call:ident )* ) )* )*
|
||||
}
|
||||
$(, $rest_name:ident : $rest_module:ident::{
|
||||
$( $rest_modules:ident $( ( $( $rest_call:ident )* ) )* ),*
|
||||
})*;
|
||||
) => {
|
||||
$crate::__decl_outer_inherent!(
|
||||
$runtime;
|
||||
$block;
|
||||
$uncheckedextrinsic;
|
||||
$( $parsed_name :: $parsed_call ),*;
|
||||
$name: $module::{ $( $modules $( ( $( $modules_call )* ) )* ),* }
|
||||
$(
|
||||
, $rest_name: $rest_module::{
|
||||
$( $rest_modules $( ( $( $rest_call )* ) )* ),*
|
||||
}
|
||||
)*;
|
||||
);
|
||||
};
|
||||
(
|
||||
$runtime:ident;
|
||||
$block:ident;
|
||||
$uncheckedextrinsic:ident;
|
||||
$( $parsed_name:ident :: $parsed_call:ident ),*;
|
||||
$name:ident: $module:ident::{}
|
||||
$(, $rest_name:ident : $rest_module:ident::{
|
||||
$( $rest_modules:ident $( ( $( $rest_call:ident )* ) )* ),*
|
||||
})*;
|
||||
) => {
|
||||
$crate::__decl_outer_inherent!(
|
||||
$runtime;
|
||||
$block;
|
||||
$uncheckedextrinsic;
|
||||
$( $parsed_name :: $parsed_call ),*;
|
||||
$(
|
||||
$rest_name: $rest_module::{
|
||||
$( $rest_modules $( ( $( $rest_call )* ) )* ),*
|
||||
}
|
||||
),*;
|
||||
);
|
||||
};
|
||||
(
|
||||
$runtime:ident;
|
||||
$block:ident;
|
||||
$uncheckedextrinsic:ident;
|
||||
$( $parsed_name:ident :: $parsed_call:ident ),*;
|
||||
;
|
||||
) => {
|
||||
$crate::impl_outer_inherent!(
|
||||
impl Inherents where Block = $block, UncheckedExtrinsic = $uncheckedextrinsic {
|
||||
$( $parsed_name : $parsed_call, )*
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
// Those imports are used by event, config, origin and log macros to get access to its inner type
|
||||
macro_rules! __decl_instance_import {
|
||||
( $( $module:ident <$instance:ident> )* ) => {
|
||||
$crate::paste::item! {
|
||||
$(use $module as [< $module _ $instance >];)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// A private macro that calls impl_outer_validate_unsigned for Call.
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __impl_outer_validate_unsigned {
|
||||
(
|
||||
$runtime:ident;
|
||||
{ $( $parsed:tt )* };
|
||||
$name:ident: $module:ident:: $(<$module_instance:ident>::)? {
|
||||
ValidateUnsigned $( $modules:ident $( ( $( $modules_args:ident )* ) )* )*
|
||||
}
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
$crate::__impl_outer_validate_unsigned!(
|
||||
$runtime;
|
||||
{ $( $parsed )* $name };
|
||||
$( $rest )*
|
||||
);
|
||||
};
|
||||
(
|
||||
$runtime:ident;
|
||||
{ $( $parsed:tt )* };
|
||||
$name:ident: $module:ident:: $(<$module_instance:ident>::)? {
|
||||
$ignore:ident $( ( $( $args_ignore:ident )* ) )*
|
||||
$( $modules:ident $( ( $( $modules_args:ident )* ) )* )*
|
||||
}
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
$crate::__impl_outer_validate_unsigned!(
|
||||
$runtime;
|
||||
{ $( $parsed )* };
|
||||
$name: $module:: $(<$module_instance>::)? {
|
||||
$( $modules $( ( $( $modules_args )* ) )* )*
|
||||
}
|
||||
$( $rest )*
|
||||
);
|
||||
};
|
||||
(
|
||||
$runtime:ident;
|
||||
{ $( $parsed:tt )* };
|
||||
$name:ident: $module:ident:: $(<$module_instance:ident>::)? {}
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
$crate::__impl_outer_validate_unsigned!(
|
||||
$runtime;
|
||||
{ $( $parsed )* };
|
||||
$( $rest )*
|
||||
);
|
||||
};
|
||||
(
|
||||
$runtime:ident;
|
||||
{ $(
|
||||
$parsed_modules:ident
|
||||
)* };
|
||||
) => {
|
||||
$crate::impl_outer_validate_unsigned!(
|
||||
impl ValidateUnsigned for $runtime {
|
||||
$( $parsed_modules )*
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Operation on runtime child storages.
|
||||
//!
|
||||
//! This module is a currently only a variant of unhashed with additional `storage_key`.
|
||||
//! Note that `storage_key` must be unique and strong (strong in the sense of being long enough to
|
||||
//! avoid collision from a resistant hash function (which unique implies)).
|
||||
// NOTE: could replace unhashed by having only one kind of storage (root being null storage key (storage_key can become Option<&[u8]>).
|
||||
|
||||
use crate::rstd::prelude::*;
|
||||
use codec::{Codec, Encode, Decode};
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `None` if there is no explicit entry.
|
||||
pub fn get<T: Decode + Sized>(storage_key: &[u8], key: &[u8]) -> Option<T> {
|
||||
runtime_io::storage::child_get(storage_key, key).and_then(|v| {
|
||||
Decode::decode(&mut &v[..]).map(Some).unwrap_or_else(|_| {
|
||||
// TODO #3700: error should be handleable.
|
||||
runtime_print!("ERROR: Corrupted state in child trie at {:?}/{:?}", storage_key, key);
|
||||
None
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or the type's default if there is no
|
||||
/// explicit entry.
|
||||
pub fn get_or_default<T: Decode + Sized + Default>(storage_key: &[u8], key: &[u8]) -> T {
|
||||
get(storage_key, key).unwrap_or_else(Default::default)
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value` if there is no
|
||||
/// explicit entry.
|
||||
pub fn get_or<T: Decode + Sized>(storage_key: &[u8], key: &[u8], default_value: T) -> T {
|
||||
get(storage_key, key).unwrap_or(default_value)
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
|
||||
/// explicit entry.
|
||||
pub fn get_or_else<T: Decode + Sized, F: FnOnce() -> T>(
|
||||
storage_key: &[u8],
|
||||
key: &[u8],
|
||||
default_value: F,
|
||||
) -> T {
|
||||
get(storage_key, key).unwrap_or_else(default_value)
|
||||
}
|
||||
|
||||
/// Put `value` in storage under `key`.
|
||||
pub fn put<T: Encode>(storage_key: &[u8], key: &[u8], value: &T) {
|
||||
value.using_encoded(|slice| runtime_io::storage::child_set(storage_key, key, slice));
|
||||
}
|
||||
|
||||
/// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise.
|
||||
pub fn take<T: Decode + Sized>(storage_key: &[u8], key: &[u8]) -> Option<T> {
|
||||
let r = get(storage_key, key);
|
||||
if r.is_some() {
|
||||
kill(storage_key, key);
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
/// Remove `key` from storage, returning its value, or, if there was no explicit entry in storage,
|
||||
/// the default for its type.
|
||||
pub fn take_or_default<T: Codec + Sized + Default>(storage_key: &[u8], key: &[u8]) -> T {
|
||||
take(storage_key, key).unwrap_or_else(Default::default)
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value` if there is no
|
||||
/// explicit entry. Ensure there is no explicit entry on return.
|
||||
pub fn take_or<T: Codec + Sized>(storage_key: &[u8],key: &[u8], default_value: T) -> T {
|
||||
take(storage_key, key).unwrap_or(default_value)
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
|
||||
/// explicit entry. Ensure there is no explicit entry on return.
|
||||
pub fn take_or_else<T: Codec + Sized, F: FnOnce() -> T>(
|
||||
storage_key: &[u8],
|
||||
key: &[u8],
|
||||
default_value: F,
|
||||
) -> T {
|
||||
take(storage_key, key).unwrap_or_else(default_value)
|
||||
}
|
||||
|
||||
/// Check to see if `key` has an explicit entry in storage.
|
||||
pub fn exists(storage_key: &[u8], key: &[u8]) -> bool {
|
||||
runtime_io::storage::child_read(storage_key, key, &mut [0;0][..], 0).is_some()
|
||||
}
|
||||
|
||||
/// Remove all `storage_key` key/values
|
||||
pub fn kill_storage(storage_key: &[u8]) {
|
||||
runtime_io::storage::child_storage_kill(storage_key)
|
||||
}
|
||||
|
||||
/// Ensure `key` has no explicit entry in storage.
|
||||
pub fn kill(storage_key: &[u8], key: &[u8]) {
|
||||
runtime_io::storage::child_clear(storage_key, key);
|
||||
}
|
||||
|
||||
/// Get a Vec of bytes from storage.
|
||||
pub fn get_raw(storage_key: &[u8], key: &[u8]) -> Option<Vec<u8>> {
|
||||
runtime_io::storage::child_get(storage_key, key)
|
||||
}
|
||||
|
||||
/// Put a raw byte slice into storage.
|
||||
pub fn put_raw(storage_key: &[u8], key: &[u8], value: &[u8]) {
|
||||
runtime_io::storage::child_set(storage_key, key, value)
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::borrow::Borrow;
|
||||
use codec::{Ref, FullCodec, FullEncode, Encode, EncodeLike, EncodeAppend};
|
||||
use crate::{storage::{self, unhashed}, hash::StorageHasher};
|
||||
|
||||
/// Generator for `StorageDoubleMap` used by `decl_storage`.
|
||||
///
|
||||
/// # Mapping of keys to a storage path
|
||||
///
|
||||
/// The storage key (i.e. the key under which the `Value` will be stored) is created from two parts.
|
||||
/// The first part is a hash of a concatenation of the `key1_prefix` and `Key1`. And the second part
|
||||
/// is a hash of a `Key2`.
|
||||
///
|
||||
/// Thus value for (key1, key2) is stored at:
|
||||
/// ```nocompile
|
||||
/// Hasher1(key1_prefix ++ key1) ++ Hasher2(key2)
|
||||
/// ```
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// If the key1s are not trusted (e.g. can be set by a user), a cryptographic `hasher` such as
|
||||
/// `blake2_256` must be used for Hasher1. Otherwise, other values in storage can be compromised.
|
||||
/// If the key2s are not trusted (e.g. can be set by a user), a cryptographic `hasher` such as
|
||||
/// `blake2_256` must be used for Hasher2. Otherwise, other items in storage with the same first
|
||||
/// key can be compromised.
|
||||
pub trait StorageDoubleMap<K1: FullEncode, K2: FullEncode, V: FullCodec> {
|
||||
/// The type that get/take returns.
|
||||
type Query;
|
||||
|
||||
/// Hasher for the first key.
|
||||
type Hasher1: StorageHasher;
|
||||
|
||||
/// Hasher for the second key.
|
||||
type Hasher2: StorageHasher;
|
||||
|
||||
/// Get the prefix for first key.
|
||||
fn key1_prefix() -> &'static [u8];
|
||||
|
||||
/// Convert an optional value retrieved from storage to the type queried.
|
||||
fn from_optional_value_to_query(v: Option<V>) -> Self::Query;
|
||||
|
||||
/// Convert a query to an optional value into storage.
|
||||
fn from_query_to_optional_value(v: Self::Query) -> Option<V>;
|
||||
|
||||
/// Generate the first part of the key used in top storage.
|
||||
fn storage_double_map_final_key1<KArg1>(k1: KArg1) -> <Self::Hasher1 as StorageHasher>::Output
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
{
|
||||
let mut final_key1 = Self::key1_prefix().to_vec();
|
||||
k1.encode_to(&mut final_key1);
|
||||
Self::Hasher1::hash(&final_key1)
|
||||
}
|
||||
|
||||
/// Generate the full key used in top storage.
|
||||
fn storage_double_map_final_key<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Vec<u8>
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
let mut final_key = Self::storage_double_map_final_key1(k1).as_ref().to_vec();
|
||||
final_key.extend_from_slice(k2.using_encoded(Self::Hasher2::hash).as_ref());
|
||||
final_key
|
||||
}
|
||||
}
|
||||
|
||||
impl<K1, K2, V, G> storage::StorageDoubleMap<K1, K2, V> for G
|
||||
where
|
||||
K1: FullEncode,
|
||||
K2: FullEncode,
|
||||
V: FullCodec,
|
||||
G: StorageDoubleMap<K1, K2, V>,
|
||||
{
|
||||
type Query = G::Query;
|
||||
|
||||
fn hashed_key_for<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Vec<u8>
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
Self::storage_double_map_final_key(k1, k2)
|
||||
}
|
||||
|
||||
fn exists<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> bool
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
unhashed::exists(&Self::storage_double_map_final_key(k1, k2))
|
||||
}
|
||||
|
||||
fn get<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Self::Query
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
G::from_optional_value_to_query(unhashed::get(&Self::storage_double_map_final_key(k1, k2)))
|
||||
}
|
||||
|
||||
fn take<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Self::Query
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
let final_key = Self::storage_double_map_final_key(k1, k2);
|
||||
|
||||
let value = unhashed::take(&final_key);
|
||||
G::from_optional_value_to_query(value)
|
||||
}
|
||||
|
||||
fn insert<KArg1, KArg2, VArg>(k1: KArg1, k2: KArg2, val: VArg)
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
VArg: EncodeLike<V>,
|
||||
{
|
||||
unhashed::put(&Self::storage_double_map_final_key(k1, k2), &val.borrow())
|
||||
}
|
||||
|
||||
fn remove<KArg1, KArg2>(k1: KArg1, k2: KArg2)
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
unhashed::kill(&Self::storage_double_map_final_key(k1, k2))
|
||||
}
|
||||
|
||||
fn remove_prefix<KArg1>(k1: KArg1) where KArg1: EncodeLike<K1> {
|
||||
unhashed::kill_prefix(Self::storage_double_map_final_key1(k1).as_ref())
|
||||
}
|
||||
|
||||
fn mutate<KArg1, KArg2, R, F>(k1: KArg1, k2: KArg2, f: F) -> R
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
F: FnOnce(&mut Self::Query) -> R,
|
||||
{
|
||||
let final_key = Self::storage_double_map_final_key(k1, k2);
|
||||
let mut val = G::from_optional_value_to_query(unhashed::get(final_key.as_ref()));
|
||||
|
||||
let ret = f(&mut val);
|
||||
match G::from_query_to_optional_value(val) {
|
||||
Some(ref val) => unhashed::put(final_key.as_ref(), val),
|
||||
None => unhashed::kill(final_key.as_ref()),
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn append<Items, Item, EncodeLikeItem, KArg1, KArg2>(
|
||||
k1: KArg1,
|
||||
k2: KArg2,
|
||||
items: Items,
|
||||
) -> Result<(), &'static str>
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
V: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem>,
|
||||
Items::IntoIter: ExactSizeIterator
|
||||
{
|
||||
let final_key = Self::storage_double_map_final_key(k1, k2);
|
||||
|
||||
let encoded_value = unhashed::get_raw(&final_key)
|
||||
.unwrap_or_else(|| {
|
||||
match G::from_query_to_optional_value(G::from_optional_value_to_query(None)) {
|
||||
Some(value) => value.encode(),
|
||||
None => vec![],
|
||||
}
|
||||
});
|
||||
|
||||
let new_val = V::append_or_new(
|
||||
encoded_value,
|
||||
items,
|
||||
).map_err(|_| "Could not append given item")?;
|
||||
unhashed::put_raw(&final_key, &new_val);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn append_or_insert<Items, Item, EncodeLikeItem, KArg1, KArg2>(
|
||||
k1: KArg1,
|
||||
k2: KArg2,
|
||||
items: Items,
|
||||
)
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
V: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem> + Clone + EncodeLike<V>,
|
||||
Items::IntoIter: ExactSizeIterator
|
||||
{
|
||||
Self::append(Ref::from(&k1), Ref::from(&k2), items.clone())
|
||||
.unwrap_or_else(|_| Self::insert(k1, k2, items));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,497 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use codec::{FullCodec, Encode, Decode, EncodeLike, Ref};
|
||||
use crate::{storage::{self, unhashed}, hash::StorageHasher, traits::Len};
|
||||
use rstd::marker::PhantomData;
|
||||
|
||||
/// Generator for `StorageLinkedMap` used by `decl_storage`.
|
||||
///
|
||||
/// # Mapping of keys to a storage path
|
||||
///
|
||||
/// The key for the head of the map is stored at one fixed path:
|
||||
/// ```nocompile
|
||||
/// Hasher(head_key)
|
||||
/// ```
|
||||
///
|
||||
/// For each key, the value stored under that key is appended with a
|
||||
/// [`Linkage`](struct.Linkage.html) (which hold previous and next key) at the path:
|
||||
/// ```nocompile
|
||||
/// Hasher(prefix ++ key)
|
||||
/// ```
|
||||
///
|
||||
/// Enumeration is done by getting the head of the linked map and then iterating getting the
|
||||
/// value and linkage stored at the key until the found linkage has no next key.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// If the keys are not trusted (e.g. can be set by a user), a cryptographic `hasher` such as
|
||||
/// `blake2_256` must be used. Otherwise, other values in storage can be compromised.
|
||||
pub trait StorageLinkedMap<K: FullCodec, V: FullCodec> {
|
||||
/// The type that get/take returns.
|
||||
type Query;
|
||||
|
||||
/// Hasher used to insert into storage.
|
||||
type Hasher: StorageHasher;
|
||||
|
||||
/// The family of key formats used for this map.
|
||||
type KeyFormat: KeyFormat<Hasher=Self::Hasher>;
|
||||
|
||||
/// Prefix used to prepend each key.
|
||||
fn prefix() -> &'static [u8];
|
||||
|
||||
/// The head key of the linked-map.
|
||||
fn head_key() -> &'static [u8] {
|
||||
<Self::KeyFormat as KeyFormat>::head_key()
|
||||
}
|
||||
|
||||
/// Convert an optional value retrieved from storage to the type queried.
|
||||
fn from_optional_value_to_query(v: Option<V>) -> Self::Query;
|
||||
|
||||
/// Convert a query to an optional value into storage.
|
||||
fn from_query_to_optional_value(v: Self::Query) -> Option<V>;
|
||||
|
||||
/// Generate the full key used in top storage.
|
||||
fn storage_linked_map_final_key<KeyArg>(key: KeyArg) -> <Self::Hasher as StorageHasher>::Output
|
||||
where
|
||||
KeyArg: EncodeLike<K>,
|
||||
{
|
||||
<Self::KeyFormat as KeyFormat>::storage_linked_map_final_key::<KeyArg>(Self::prefix(), &key)
|
||||
}
|
||||
|
||||
/// Generate the hashed key for head
|
||||
fn storage_linked_map_final_head_key() -> <Self::Hasher as StorageHasher>::Output {
|
||||
<Self::KeyFormat as KeyFormat>::storage_linked_map_final_head_key()
|
||||
}
|
||||
}
|
||||
|
||||
/// A type-abstracted key format used for a family of linked-map types.
|
||||
pub trait KeyFormat {
|
||||
type Hasher: StorageHasher;
|
||||
|
||||
/// Key used to store linked map head.
|
||||
fn head_key() -> &'static [u8];
|
||||
|
||||
/// Generate the full key used in top storage.
|
||||
fn storage_linked_map_final_key<K>(prefix: &[u8], key: &K)
|
||||
-> <Self::Hasher as StorageHasher>::Output
|
||||
where
|
||||
K: Encode,
|
||||
{
|
||||
let mut final_key = prefix.to_vec();
|
||||
key.encode_to(&mut final_key);
|
||||
<Self::Hasher as StorageHasher>::hash(&final_key)
|
||||
}
|
||||
|
||||
fn storage_linked_map_final_head_key()
|
||||
-> <Self::Hasher as StorageHasher>::Output
|
||||
{
|
||||
<Self::Hasher as StorageHasher>::hash(Self::head_key())
|
||||
}
|
||||
}
|
||||
|
||||
/// Linkage data of an element (it's successor and predecessor)
|
||||
#[derive(Encode, Decode)]
|
||||
pub struct Linkage<Key> {
|
||||
/// Previous element key in storage (None for the first element)
|
||||
pub previous: Option<Key>,
|
||||
/// Next element key in storage (None for the last element)
|
||||
pub next: Option<Key>,
|
||||
}
|
||||
|
||||
impl<Key> Default for Linkage<Key> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
previous: None,
|
||||
next: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Encode like a linkage.
|
||||
#[derive(Encode)]
|
||||
struct EncodeLikeLinkage<PKey: EncodeLike<Key>, NKey: EncodeLike<Key>, Key: Encode> {
|
||||
// Previous element key in storage (None for the first element)
|
||||
previous: Option<PKey>,
|
||||
// Next element key in storage (None for the last element)
|
||||
next: Option<NKey>,
|
||||
// The key of the linkage this type encode to
|
||||
phantom: core::marker::PhantomData<Key>,
|
||||
}
|
||||
|
||||
/// A key-value pair iterator for enumerable map.
|
||||
pub struct Enumerator<K, V, F> {
|
||||
next: Option<K>,
|
||||
prefix: &'static [u8],
|
||||
_phantom: PhantomData<(V, F)>,
|
||||
}
|
||||
|
||||
impl<K, V, F> Enumerator<K, V, F> {
|
||||
/// Create an explicit enumerator for testing.
|
||||
#[cfg(test)]
|
||||
pub fn from_head(head: K, prefix: &'static [u8]) -> Self {
|
||||
Enumerator {
|
||||
next: Some(head),
|
||||
prefix,
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, F> Iterator for Enumerator<K, V, F>
|
||||
where
|
||||
K: FullCodec,
|
||||
V: FullCodec,
|
||||
F: KeyFormat,
|
||||
{
|
||||
type Item = (K, V);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let next = self.next.take()?;
|
||||
|
||||
let (val, linkage): (V, Linkage<K>) = {
|
||||
let next_full_key = F::storage_linked_map_final_key(self.prefix, &next);
|
||||
match read_with_linkage::<K, V>(next_full_key.as_ref()) {
|
||||
Some(value) => value,
|
||||
None => {
|
||||
// TODO #3700: error should be handleable.
|
||||
runtime_print!(
|
||||
"ERROR: Corrupted state: linked map head_key={:?}: \
|
||||
next value doesn't exist at {:?}",
|
||||
F::head_key(), next_full_key.as_ref(),
|
||||
);
|
||||
return None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.next = linkage.next;
|
||||
Some((next, val))
|
||||
}
|
||||
}
|
||||
|
||||
/// Update linkage when this element is removed.
|
||||
///
|
||||
/// Takes care of updating previous and next elements points
|
||||
/// as well as updates head if the element is first or last.
|
||||
fn remove_linkage<K, V, F>(linkage: Linkage<K>, prefix: &[u8])
|
||||
where
|
||||
K: FullCodec,
|
||||
V: FullCodec,
|
||||
F: KeyFormat,
|
||||
{
|
||||
let next_key = linkage.next.as_ref()
|
||||
.map(|k| F::storage_linked_map_final_key(prefix, k))
|
||||
.map(|x| x.as_ref().to_vec());
|
||||
let prev_key = linkage.previous.as_ref()
|
||||
.map(|k| F::storage_linked_map_final_key(prefix, k))
|
||||
.map(|x| x.as_ref().to_vec());
|
||||
|
||||
if let Some(prev_key) = prev_key {
|
||||
// Retrieve previous element and update `next`
|
||||
if let Some(mut res) = read_with_linkage::<K, V>(prev_key.as_ref()) {
|
||||
res.1.next = linkage.next;
|
||||
unhashed::put(prev_key.as_ref(), &res);
|
||||
} else {
|
||||
// TODO #3700: error should be handleable.
|
||||
runtime_print!(
|
||||
"ERROR: Corrupted state: linked map head_key={:?}: \
|
||||
previous value doesn't exist at {:?}",
|
||||
F::head_key(), prev_key,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// we were first so let's update the head
|
||||
write_head::<&K, K, F>(linkage.next.as_ref());
|
||||
}
|
||||
if let Some(next_key) = next_key {
|
||||
// Update previous of next element
|
||||
if let Some(mut res) = read_with_linkage::<K, V>(next_key.as_ref()) {
|
||||
res.1.previous = linkage.previous;
|
||||
unhashed::put(next_key.as_ref(), &res);
|
||||
} else {
|
||||
// TODO #3700: error should be handleable.
|
||||
runtime_print!(
|
||||
"ERROR: Corrupted state: linked map head_key={:?}: \
|
||||
next value doesn't exist at {:?}",
|
||||
F::head_key(), next_key,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the contained data and its linkage.
|
||||
pub(super) fn read_with_linkage<K, V>(key: &[u8]) -> Option<(V, Linkage<K>)>
|
||||
where
|
||||
K: Decode,
|
||||
V: Decode,
|
||||
{
|
||||
unhashed::get(key)
|
||||
}
|
||||
|
||||
/// Generate linkage for newly inserted element.
|
||||
///
|
||||
/// Takes care of updating head and previous head's pointer.
|
||||
pub(super) fn new_head_linkage<KeyArg, K, V, F>(key: KeyArg, prefix: &[u8]) -> Linkage<K>
|
||||
where
|
||||
KeyArg: EncodeLike<K>,
|
||||
K: FullCodec,
|
||||
V: FullCodec,
|
||||
F: KeyFormat,
|
||||
{
|
||||
if let Some(head) = read_head::<K, F>() {
|
||||
// update previous head predecessor
|
||||
{
|
||||
let head_key = F::storage_linked_map_final_key(prefix, &head);
|
||||
if let Some((data, linkage)) = read_with_linkage::<K, V>(head_key.as_ref()) {
|
||||
let new_linkage = EncodeLikeLinkage::<_, _, K> {
|
||||
previous: Some(Ref::from(&key)),
|
||||
next: linkage.next.as_ref(),
|
||||
phantom: Default::default(),
|
||||
};
|
||||
unhashed::put(head_key.as_ref(), &(data, new_linkage));
|
||||
} else {
|
||||
// TODO #3700: error should be handleable.
|
||||
runtime_print!(
|
||||
"ERROR: Corrupted state: linked map head_key={:?}: \
|
||||
head value doesn't exist at {:?}",
|
||||
F::head_key(), head_key.as_ref(),
|
||||
);
|
||||
// Thus we consider we are first - update the head and produce empty linkage
|
||||
|
||||
write_head::<_, _, F>(Some(key));
|
||||
return Linkage::default();
|
||||
}
|
||||
}
|
||||
// update to current head
|
||||
write_head::<_, _, F>(Some(key));
|
||||
// return linkage with pointer to previous head
|
||||
let mut linkage = Linkage::default();
|
||||
linkage.next = Some(head);
|
||||
linkage
|
||||
} else {
|
||||
// we are first - update the head and produce empty linkage
|
||||
write_head::<_, _, F>(Some(key));
|
||||
Linkage::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Read current head pointer.
|
||||
pub(crate) fn read_head<K, F>() -> Option<K>
|
||||
where
|
||||
K: Decode,
|
||||
F: KeyFormat,
|
||||
{
|
||||
unhashed::get(F::storage_linked_map_final_head_key().as_ref())
|
||||
}
|
||||
|
||||
/// Overwrite current head pointer.
|
||||
///
|
||||
/// If `None` is given head is removed from storage.
|
||||
pub(super) fn write_head<KeyArg, K, F>(head: Option<KeyArg>)
|
||||
where
|
||||
KeyArg: EncodeLike<K>,
|
||||
K: FullCodec,
|
||||
F: KeyFormat,
|
||||
{
|
||||
match head.as_ref() {
|
||||
Some(head) => unhashed::put(F::storage_linked_map_final_head_key().as_ref(), head),
|
||||
None => unhashed::kill(F::storage_linked_map_final_head_key().as_ref()),
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, G> storage::StorageLinkedMap<K, V> for G
|
||||
where
|
||||
K: FullCodec,
|
||||
V: FullCodec,
|
||||
G: StorageLinkedMap<K, V>,
|
||||
{
|
||||
type Query = G::Query;
|
||||
|
||||
type Enumerator = Enumerator<K, V, G::KeyFormat>;
|
||||
|
||||
fn exists<KeyArg: EncodeLike<K>>(key: KeyArg) -> bool {
|
||||
unhashed::exists(Self::storage_linked_map_final_key(key).as_ref())
|
||||
}
|
||||
|
||||
fn get<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query {
|
||||
let val = unhashed::get(Self::storage_linked_map_final_key(key).as_ref());
|
||||
G::from_optional_value_to_query(val)
|
||||
}
|
||||
|
||||
fn swap<KeyArg1: EncodeLike<K>, KeyArg2: EncodeLike<K>>(key1: KeyArg1, key2: KeyArg2) {
|
||||
let prefix = Self::prefix();
|
||||
let final_key1 = Self::storage_linked_map_final_key(Ref::from(&key1));
|
||||
let final_key2 = Self::storage_linked_map_final_key(Ref::from(&key2));
|
||||
let full_value_1 = read_with_linkage::<K, V>(final_key1.as_ref());
|
||||
let full_value_2 = read_with_linkage::<K, V>(final_key2.as_ref());
|
||||
|
||||
match (full_value_1, full_value_2) {
|
||||
// Just keep linkage in order and only swap values.
|
||||
(Some((value1, linkage1)), Some((value2, linkage2))) => {
|
||||
unhashed::put(final_key1.as_ref(), &(value2, linkage1));
|
||||
unhashed::put(final_key2.as_ref(), &(value1, linkage2));
|
||||
}
|
||||
// Remove key and insert the new one.
|
||||
(Some((value, _linkage)), None) => {
|
||||
Self::remove(key1);
|
||||
let linkage = new_head_linkage::<_, _, V, G::KeyFormat>(key2, prefix);
|
||||
unhashed::put(final_key2.as_ref(), &(value, linkage));
|
||||
}
|
||||
// Remove key and insert the new one.
|
||||
(None, Some((value, _linkage))) => {
|
||||
Self::remove(key2);
|
||||
let linkage = new_head_linkage::<_, _, V, G::KeyFormat>(key1, prefix);
|
||||
unhashed::put(final_key1.as_ref(), &(value, linkage));
|
||||
}
|
||||
// No-op.
|
||||
(None, None) => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn insert<KeyArg: EncodeLike<K>, ValArg: EncodeLike<V>>(key: KeyArg, val: ValArg) {
|
||||
let final_key = Self::storage_linked_map_final_key(Ref::from(&key));
|
||||
let linkage = match read_with_linkage::<_, V>(final_key.as_ref()) {
|
||||
// overwrite but reuse existing linkage
|
||||
Some((_data, linkage)) => linkage,
|
||||
// create new linkage
|
||||
None => new_head_linkage::<_, _, V, G::KeyFormat>(key, Self::prefix()),
|
||||
};
|
||||
unhashed::put(final_key.as_ref(), &(val, linkage))
|
||||
}
|
||||
|
||||
fn remove<KeyArg: EncodeLike<K>>(key: KeyArg) {
|
||||
G::take(key);
|
||||
}
|
||||
|
||||
fn mutate<KeyArg: EncodeLike<K>, R, F: FnOnce(&mut Self::Query) -> R>(key: KeyArg, f: F) -> R {
|
||||
let final_key = Self::storage_linked_map_final_key(Ref::from(&key));
|
||||
|
||||
let (mut val, _linkage) = read_with_linkage::<K, V>(final_key.as_ref())
|
||||
.map(|(data, linkage)| (G::from_optional_value_to_query(Some(data)), Some(linkage)))
|
||||
.unwrap_or_else(|| (G::from_optional_value_to_query(None), None));
|
||||
|
||||
let ret = f(&mut val);
|
||||
match G::from_query_to_optional_value(val) {
|
||||
Some(ref val) => G::insert(key, val),
|
||||
None => G::remove(key),
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn take<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query {
|
||||
let final_key = Self::storage_linked_map_final_key(key);
|
||||
|
||||
let full_value: Option<(V, Linkage<K>)> = unhashed::take(final_key.as_ref());
|
||||
|
||||
let value = full_value.map(|(data, linkage)| {
|
||||
remove_linkage::<K, V, G::KeyFormat>(linkage, Self::prefix());
|
||||
data
|
||||
});
|
||||
|
||||
G::from_optional_value_to_query(value)
|
||||
}
|
||||
|
||||
fn enumerate() -> Self::Enumerator {
|
||||
Enumerator::<_, _, G::KeyFormat> {
|
||||
next: read_head::<_, G::KeyFormat>(),
|
||||
prefix: Self::prefix(),
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn head() -> Option<K> {
|
||||
read_head::<_, G::KeyFormat>()
|
||||
}
|
||||
|
||||
fn decode_len<KeyArg: EncodeLike<K>>(key: KeyArg) -> Result<usize, &'static str>
|
||||
where V: codec::DecodeLength + Len
|
||||
{
|
||||
let key = Self::storage_linked_map_final_key(key);
|
||||
if let Some(v) = unhashed::get_raw(key.as_ref()) {
|
||||
<V as codec::DecodeLength>::len(&v).map_err(|e| e.what())
|
||||
} else {
|
||||
let len = G::from_query_to_optional_value(G::from_optional_value_to_query(None))
|
||||
.map(|v| v.len())
|
||||
.unwrap_or(0);
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
fn translate<K2, V2, TK, TV>(translate_key: TK, translate_val: TV) -> Result<(), Option<K2>>
|
||||
where K2: FullCodec + Clone, V2: Decode, TK: Fn(K2) -> K, TV: Fn(V2) -> V
|
||||
{
|
||||
let head_key = read_head::<K2, G::KeyFormat>().ok_or(None)?;
|
||||
let prefix = G::prefix();
|
||||
|
||||
let mut last_key = None;
|
||||
let mut current_key = head_key.clone();
|
||||
|
||||
write_head::<&K, K, G::KeyFormat>(Some(&translate_key(head_key)));
|
||||
|
||||
let translate_linkage = |old: Linkage<K2>| -> Linkage<K> {
|
||||
Linkage {
|
||||
previous: old.previous.map(&translate_key),
|
||||
next: old.next.map(&translate_key),
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
let old_raw_key = G::KeyFormat::storage_linked_map_final_key(prefix, ¤t_key);
|
||||
let x = unhashed::take(old_raw_key.as_ref());
|
||||
let (val, linkage): (V2, Linkage<K2>) = match x {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
// we failed to read value and linkage. Update the last key's linkage
|
||||
// to end the map early, since it's impossible to iterate further.
|
||||
if let Some(last_key) = last_key {
|
||||
let last_raw_key = G::storage_linked_map_final_key(&last_key);
|
||||
if let Some((val, mut linkage))
|
||||
= read_with_linkage::<K, V>(last_raw_key.as_ref())
|
||||
{
|
||||
// defensive: should always happen, since it was just written
|
||||
// in the last iteration of the loop.
|
||||
linkage.next = None;
|
||||
unhashed::put(last_raw_key.as_ref(), &(&val, &linkage));
|
||||
}
|
||||
}
|
||||
|
||||
return Err(Some(current_key));
|
||||
}
|
||||
};
|
||||
let next = linkage.next.clone();
|
||||
|
||||
let val = translate_val(val);
|
||||
let linkage = translate_linkage(linkage);
|
||||
|
||||
// and write in the value and linkage under the new key.
|
||||
let new_key = translate_key(current_key.clone());
|
||||
let new_raw_key = G::storage_linked_map_final_key(&new_key);
|
||||
unhashed::put(new_raw_key.as_ref(), &(&val, &linkage));
|
||||
|
||||
match next {
|
||||
None => break,
|
||||
Some(next) => {
|
||||
last_key = Some(new_key);
|
||||
current_key = next
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use rstd::prelude::*;
|
||||
use rstd::borrow::Borrow;
|
||||
use codec::{FullCodec, FullEncode, Encode, EncodeLike, Ref, EncodeAppend};
|
||||
use crate::{storage::{self, unhashed}, hash::StorageHasher, traits::Len};
|
||||
|
||||
/// Generator for `StorageMap` used by `decl_storage`.
|
||||
///
|
||||
/// For each key value is stored at:
|
||||
/// ```nocompile
|
||||
/// Hasher(prefix ++ key)
|
||||
/// ```
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// If the keys are not trusted (e.g. can be set by a user), a cryptographic `hasher` such as
|
||||
/// `blake2_256` must be used. Otherwise, other values in storage can be compromised.
|
||||
pub trait StorageMap<K: FullEncode, V: FullCodec> {
|
||||
/// The type that get/take returns.
|
||||
type Query;
|
||||
|
||||
/// Hasher used to insert into storage.
|
||||
type Hasher: StorageHasher;
|
||||
|
||||
/// Prefix used to prepend each key.
|
||||
fn prefix() -> &'static [u8];
|
||||
|
||||
/// Convert an optional value retrieved from storage to the type queried.
|
||||
fn from_optional_value_to_query(v: Option<V>) -> Self::Query;
|
||||
|
||||
/// Convert a query to an optional value into storage.
|
||||
fn from_query_to_optional_value(v: Self::Query) -> Option<V>;
|
||||
|
||||
/// Generate the full key used in top storage.
|
||||
fn storage_map_final_key<KeyArg>(key: KeyArg) -> <Self::Hasher as StorageHasher>::Output
|
||||
where
|
||||
KeyArg: EncodeLike<K>,
|
||||
{
|
||||
let mut final_key = Self::prefix().to_vec();
|
||||
key.borrow().encode_to(&mut final_key);
|
||||
Self::Hasher::hash(&final_key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: FullEncode, V: FullCodec, G: StorageMap<K, V>> storage::StorageMap<K, V> for G {
|
||||
type Query = G::Query;
|
||||
|
||||
fn hashed_key_for<KeyArg: EncodeLike<K>>(key: KeyArg) -> Vec<u8> {
|
||||
Self::storage_map_final_key(key).as_ref().to_vec()
|
||||
}
|
||||
|
||||
fn swap<KeyArg1: EncodeLike<K>, KeyArg2: EncodeLike<K>>(key1: KeyArg1, key2: KeyArg2) {
|
||||
let k1 = Self::storage_map_final_key(key1);
|
||||
let k2 = Self::storage_map_final_key(key2);
|
||||
|
||||
let v1 = unhashed::get_raw(k1.as_ref());
|
||||
if let Some(val) = unhashed::get_raw(k2.as_ref()) {
|
||||
unhashed::put_raw(k1.as_ref(), &val);
|
||||
} else {
|
||||
unhashed::kill(k1.as_ref())
|
||||
}
|
||||
if let Some(val) = v1 {
|
||||
unhashed::put_raw(k2.as_ref(), &val);
|
||||
} else {
|
||||
unhashed::kill(k2.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
fn exists<KeyArg: EncodeLike<K>>(key: KeyArg) -> bool {
|
||||
unhashed::exists(Self::storage_map_final_key(key).as_ref())
|
||||
}
|
||||
|
||||
fn get<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query {
|
||||
G::from_optional_value_to_query(unhashed::get(Self::storage_map_final_key(key).as_ref()))
|
||||
}
|
||||
|
||||
fn insert<KeyArg: EncodeLike<K>, ValArg: EncodeLike<V>>(key: KeyArg, val: ValArg) {
|
||||
unhashed::put(Self::storage_map_final_key(key).as_ref(), &val.borrow())
|
||||
}
|
||||
|
||||
fn remove<KeyArg: EncodeLike<K>>(key: KeyArg) {
|
||||
unhashed::kill(Self::storage_map_final_key(key).as_ref())
|
||||
}
|
||||
|
||||
fn mutate<KeyArg: EncodeLike<K>, R, F: FnOnce(&mut Self::Query) -> R>(key: KeyArg, f: F) -> R {
|
||||
let final_key = Self::storage_map_final_key(key);
|
||||
let mut val = G::from_optional_value_to_query(unhashed::get(final_key.as_ref()));
|
||||
|
||||
let ret = f(&mut val);
|
||||
match G::from_query_to_optional_value(val) {
|
||||
Some(ref val) => unhashed::put(final_key.as_ref(), &val.borrow()),
|
||||
None => unhashed::kill(final_key.as_ref()),
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn take<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query {
|
||||
let key = Self::storage_map_final_key(key);
|
||||
let value = unhashed::take(key.as_ref());
|
||||
G::from_optional_value_to_query(value)
|
||||
}
|
||||
|
||||
fn append<Items, Item, EncodeLikeItem, KeyArg>(key: KeyArg, items: Items) -> Result<(), &'static str>
|
||||
where
|
||||
KeyArg: EncodeLike<K>,
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
V: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem>,
|
||||
Items::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let key = Self::storage_map_final_key(key);
|
||||
let encoded_value = unhashed::get_raw(key.as_ref())
|
||||
.unwrap_or_else(|| {
|
||||
match G::from_query_to_optional_value(G::from_optional_value_to_query(None)) {
|
||||
Some(value) => value.encode(),
|
||||
None => vec![],
|
||||
}
|
||||
});
|
||||
|
||||
let new_val = V::append_or_new(
|
||||
encoded_value,
|
||||
items,
|
||||
).map_err(|_| "Could not append given item")?;
|
||||
unhashed::put_raw(key.as_ref(), &new_val);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn append_or_insert<Items, Item, EncodeLikeItem, KeyArg>(key: KeyArg, items: Items)
|
||||
where
|
||||
KeyArg: EncodeLike<K>,
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
V: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem> + Clone + EncodeLike<V>,
|
||||
Items::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
Self::append(Ref::from(&key), items.clone())
|
||||
.unwrap_or_else(|_| Self::insert(key, items));
|
||||
}
|
||||
|
||||
fn decode_len<KeyArg: EncodeLike<K>>(key: KeyArg) -> Result<usize, &'static str>
|
||||
where V: codec::DecodeLength + Len
|
||||
{
|
||||
let key = Self::storage_map_final_key(key);
|
||||
if let Some(v) = unhashed::get_raw(key.as_ref()) {
|
||||
<V as codec::DecodeLength>::len(&v).map_err(|e| e.what())
|
||||
} else {
|
||||
let len = G::from_query_to_optional_value(G::from_optional_value_to_query(None))
|
||||
.map(|v| v.len())
|
||||
.unwrap_or(0);
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Generators are a set of trait on which storage traits are implemented.
|
||||
//!
|
||||
//! (i.e. implementing the generator for StorageValue on a type will automatically derive the
|
||||
//! implementation of StorageValue for this type).
|
||||
//!
|
||||
//! They are used by `decl_storage`.
|
||||
|
||||
mod linked_map;
|
||||
mod map;
|
||||
mod double_map;
|
||||
mod value;
|
||||
|
||||
pub use linked_map::{StorageLinkedMap, Enumerator, Linkage, KeyFormat as LinkedMapKeyFormat};
|
||||
pub use map::StorageMap;
|
||||
pub use double_map::StorageDoubleMap;
|
||||
pub use value::StorageValue;
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(dead_code)]
|
||||
mod tests {
|
||||
use runtime_io::TestExternalities;
|
||||
use codec::{Encode, Decode};
|
||||
use crate::storage::{unhashed, generator::{StorageValue, StorageLinkedMap}};
|
||||
|
||||
struct Runtime {}
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
type BlockNumber;
|
||||
}
|
||||
|
||||
impl Trait for Runtime {
|
||||
type Origin = u32;
|
||||
type BlockNumber = u32;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, Clone, Debug, Eq, PartialEq)]
|
||||
struct NumberNumber {
|
||||
a: u32,
|
||||
b: u32,
|
||||
}
|
||||
|
||||
crate::decl_storage! {
|
||||
trait Store for Module<T: Trait> as Runtime {
|
||||
Value get(fn value) config(): (u64, u64);
|
||||
NumberMap: linked_map NumberNumber => u64;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn value_translate_works() {
|
||||
let t = GenesisConfig::default().build_storage().unwrap();
|
||||
TestExternalities::new(t).execute_with(|| {
|
||||
// put the old value `1111u32` in the storage.
|
||||
let key = Value::storage_value_final_key();
|
||||
unhashed::put_raw(&key, &1111u32.encode());
|
||||
|
||||
// translate
|
||||
let translate_fn = |old: Option<u32>| -> Option<(u64, u64)> {
|
||||
old.map(|o| (o.into(), (o*2).into()))
|
||||
};
|
||||
let _ = Value::translate(translate_fn);
|
||||
|
||||
// new storage should be `(1111, 1111 * 2)`
|
||||
assert_eq!(Value::get(), (1111, 2222));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_map_translate_works() {
|
||||
use super::linked_map::{self, Enumerator, KeyFormat};
|
||||
|
||||
type Format = <NumberMap as StorageLinkedMap<NumberNumber, u64>>::KeyFormat;
|
||||
|
||||
let t = GenesisConfig::default().build_storage().unwrap();
|
||||
TestExternalities::new(t).execute_with(|| {
|
||||
let prefix = NumberMap::prefix();
|
||||
|
||||
// start with a map of u32 -> u32.
|
||||
for i in 0u32..100u32 {
|
||||
let final_key = <Format as KeyFormat>::storage_linked_map_final_key(
|
||||
prefix, &i,
|
||||
);
|
||||
|
||||
let linkage = linked_map::new_head_linkage::<_, u32, u32, Format>(&i, prefix);
|
||||
unhashed::put(final_key.as_ref(), &(&i, linkage));
|
||||
}
|
||||
|
||||
let head = linked_map::read_head::<u32, Format>().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
Enumerator::<u32, u32, Format>::from_head(head, prefix).collect::<Vec<_>>(),
|
||||
(0..100).rev().map(|x| (x, x)).collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
// do translation.
|
||||
NumberMap::translate(
|
||||
|k: u32| NumberNumber { a: k, b: k },
|
||||
|v: u32| (v as u64) << 32 | v as u64,
|
||||
).unwrap();
|
||||
|
||||
assert!(linked_map::read_head::<NumberNumber, Format>().is_some());
|
||||
assert_eq!(
|
||||
NumberMap::enumerate().collect::<Vec<_>>(),
|
||||
(0..100u32).rev().map(|x| (
|
||||
NumberNumber { a: x, b: x },
|
||||
(x as u64) << 32 | x as u64,
|
||||
)).collect::<Vec<_>>(),
|
||||
);
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use rstd::prelude::*;
|
||||
use codec::{FullCodec, Encode, EncodeAppend, EncodeLike, Decode};
|
||||
use crate::{storage::{self, unhashed}, hash::{Twox128, StorageHasher}, traits::Len};
|
||||
|
||||
/// Generator for `StorageValue` used by `decl_storage`.
|
||||
///
|
||||
/// Value is stored at:
|
||||
/// ```nocompile
|
||||
/// Twox128(unhashed_key)
|
||||
/// ```
|
||||
pub trait StorageValue<T: FullCodec> {
|
||||
/// The type that get/take returns.
|
||||
type Query;
|
||||
|
||||
/// Unhashed key used in storage
|
||||
fn unhashed_key() -> &'static [u8];
|
||||
|
||||
/// Convert an optional value retrieved from storage to the type queried.
|
||||
fn from_optional_value_to_query(v: Option<T>) -> Self::Query;
|
||||
|
||||
/// Convert a query to an optional value into storage.
|
||||
fn from_query_to_optional_value(v: Self::Query) -> Option<T>;
|
||||
|
||||
/// Generate the full key used in top storage.
|
||||
fn storage_value_final_key() -> [u8; 16] {
|
||||
Twox128::hash(Self::unhashed_key())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FullCodec, G: StorageValue<T>> storage::StorageValue<T> for G {
|
||||
type Query = G::Query;
|
||||
|
||||
fn hashed_key() -> [u8; 16] {
|
||||
Self::storage_value_final_key()
|
||||
}
|
||||
|
||||
fn exists() -> bool {
|
||||
unhashed::exists(&Self::storage_value_final_key())
|
||||
}
|
||||
|
||||
fn get() -> Self::Query {
|
||||
let value = unhashed::get(&Self::storage_value_final_key());
|
||||
G::from_optional_value_to_query(value)
|
||||
}
|
||||
|
||||
fn translate<O: Decode, F: FnOnce(Option<O>) -> Option<T>>(f: F) -> Result<Option<T>, ()> {
|
||||
let key = Self::storage_value_final_key();
|
||||
|
||||
// attempt to get the length directly.
|
||||
let maybe_old = match unhashed::get_raw(&key) {
|
||||
Some(old_data) => Some(O::decode(&mut &old_data[..]).map_err(|_| ())?),
|
||||
None => None,
|
||||
};
|
||||
let maybe_new = f(maybe_old);
|
||||
if let Some(new) = maybe_new.as_ref() {
|
||||
new.using_encoded(|d| unhashed::put_raw(&key, d));
|
||||
} else {
|
||||
unhashed::kill(&key);
|
||||
}
|
||||
Ok(maybe_new)
|
||||
}
|
||||
|
||||
fn put<Arg: EncodeLike<T>>(val: Arg) {
|
||||
unhashed::put(&Self::storage_value_final_key(), &val)
|
||||
}
|
||||
|
||||
fn kill() {
|
||||
unhashed::kill(&Self::storage_value_final_key())
|
||||
}
|
||||
|
||||
fn mutate<R, F: FnOnce(&mut G::Query) -> R>(f: F) -> R {
|
||||
let mut val = G::get();
|
||||
|
||||
let ret = f(&mut val);
|
||||
match G::from_query_to_optional_value(val) {
|
||||
Some(ref val) => G::put(val),
|
||||
None => G::kill(),
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn take() -> G::Query {
|
||||
let key = Self::storage_value_final_key();
|
||||
let value = unhashed::get(&key);
|
||||
if value.is_some() {
|
||||
unhashed::kill(&key)
|
||||
}
|
||||
G::from_optional_value_to_query(value)
|
||||
}
|
||||
|
||||
/// Append the given items to the value in the storage.
|
||||
///
|
||||
/// `T` is required to implement `codec::EncodeAppend`.
|
||||
fn append<Items, Item, EncodeLikeItem>(items: Items) -> Result<(), &'static str>
|
||||
where
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
T: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem>,
|
||||
Items::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let key = Self::storage_value_final_key();
|
||||
let encoded_value = unhashed::get_raw(&key)
|
||||
.unwrap_or_else(|| {
|
||||
match G::from_query_to_optional_value(G::from_optional_value_to_query(None)) {
|
||||
Some(value) => value.encode(),
|
||||
None => vec![],
|
||||
}
|
||||
});
|
||||
|
||||
let new_val = T::append_or_new(
|
||||
encoded_value,
|
||||
items,
|
||||
).map_err(|_| "Could not append given item")?;
|
||||
unhashed::put_raw(&key, &new_val);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Safely append the given items to the value in the storage. If a codec error occurs, then the
|
||||
/// old (presumably corrupt) value is replaced with the given `items`.
|
||||
///
|
||||
/// `T` is required to implement `codec::EncodeAppend`.
|
||||
fn append_or_put<Items, Item, EncodeLikeItem>(items: Items) where
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
T: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem> + Clone + EncodeLike<T>,
|
||||
Items::IntoIter: ExactSizeIterator
|
||||
{
|
||||
Self::append(items.clone()).unwrap_or_else(|_| Self::put(items));
|
||||
}
|
||||
|
||||
/// Read the length of the value in a fast way, without decoding the entire value.
|
||||
///
|
||||
/// `T` is required to implement `Codec::DecodeLength`.
|
||||
///
|
||||
/// Note that `0` is returned as the default value if no encoded value exists at the given key.
|
||||
/// Therefore, this function cannot be used as a sign of _existence_. use the `::exists()`
|
||||
/// function for this purpose.
|
||||
fn decode_len() -> Result<usize, &'static str> where T: codec::DecodeLength, T: Len {
|
||||
let key = Self::storage_value_final_key();
|
||||
|
||||
// attempt to get the length directly.
|
||||
if let Some(k) = unhashed::get_raw(&key) {
|
||||
<T as codec::DecodeLength>::len(&k).map_err(|e| e.what())
|
||||
} else {
|
||||
let len = G::from_query_to_optional_value(G::from_optional_value_to_query(None))
|
||||
.map(|v| v.len())
|
||||
.unwrap_or(0);
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Operation on runtime storage using hashed keys.
|
||||
|
||||
use super::unhashed;
|
||||
use rstd::prelude::*;
|
||||
use codec::{Encode, Decode};
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `None` if there is no explicit entry.
|
||||
pub fn get<T, HashFn, R>(hash: &HashFn, key: &[u8]) -> Option<T>
|
||||
where
|
||||
T: Decode + Sized,
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::get(&hash(key).as_ref())
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or the type's default if there is no
|
||||
/// explicit entry.
|
||||
pub fn get_or_default<T, HashFn, R>(hash: &HashFn, key: &[u8]) -> T
|
||||
where
|
||||
T: Decode + Sized + Default,
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::get_or_default(&hash(key).as_ref())
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value` if there is no
|
||||
/// explicit entry.
|
||||
pub fn get_or<T, HashFn, R>(hash: &HashFn, key: &[u8], default_value: T) -> T
|
||||
where
|
||||
T: Decode + Sized,
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::get_or(&hash(key).as_ref(), default_value)
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
|
||||
/// explicit entry.
|
||||
pub fn get_or_else<T, F, HashFn, R>(hash: &HashFn, key: &[u8], default_value: F) -> T
|
||||
where
|
||||
T: Decode + Sized,
|
||||
F: FnOnce() -> T,
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::get_or_else(&hash(key).as_ref(), default_value)
|
||||
}
|
||||
|
||||
/// Put `value` in storage under `key`.
|
||||
pub fn put<T, HashFn, R>(hash: &HashFn, key: &[u8], value: &T)
|
||||
where
|
||||
T: Encode,
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::put(&hash(key).as_ref(), value)
|
||||
}
|
||||
|
||||
/// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise.
|
||||
pub fn take<T, HashFn, R>(hash: &HashFn, key: &[u8]) -> Option<T>
|
||||
where
|
||||
T: Decode + Sized,
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::take(&hash(key).as_ref())
|
||||
}
|
||||
|
||||
/// Remove `key` from storage, returning its value, or, if there was no explicit entry in storage,
|
||||
/// the default for its type.
|
||||
pub fn take_or_default<T, HashFn, R>(hash: &HashFn, key: &[u8]) -> T
|
||||
where
|
||||
T: Decode + Sized + Default,
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::take_or_default(&hash(key).as_ref())
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value` if there is no
|
||||
/// explicit entry. Ensure there is no explicit entry on return.
|
||||
pub fn take_or<T, HashFn, R>(hash: &HashFn, key: &[u8], default_value: T) -> T
|
||||
where
|
||||
T: Decode + Sized,
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::take_or(&hash(key).as_ref(), default_value)
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
|
||||
/// explicit entry. Ensure there is no explicit entry on return.
|
||||
pub fn take_or_else<T, F, HashFn, R>(hash: &HashFn, key: &[u8], default_value: F) -> T
|
||||
where
|
||||
T: Decode + Sized,
|
||||
F: FnOnce() -> T,
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::take_or_else(&hash(key).as_ref(), default_value)
|
||||
}
|
||||
|
||||
/// Check to see if `key` has an explicit entry in storage.
|
||||
pub fn exists<HashFn, R>(hash: &HashFn, key: &[u8]) -> bool
|
||||
where
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::exists(&hash(key).as_ref())
|
||||
}
|
||||
|
||||
/// Ensure `key` has no explicit entry in storage.
|
||||
pub fn kill<HashFn, R>(hash: &HashFn, key: &[u8])
|
||||
where
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::kill(&hash(key).as_ref())
|
||||
}
|
||||
|
||||
/// Get a Vec of bytes from storage.
|
||||
pub fn get_raw<HashFn, R>(hash: &HashFn, key: &[u8]) -> Option<Vec<u8>>
|
||||
where
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::get_raw(&hash(key).as_ref())
|
||||
}
|
||||
|
||||
/// Put a raw byte slice into storage.
|
||||
pub fn put_raw<HashFn, R>(hash: &HashFn, key: &[u8], value: &[u8])
|
||||
where
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::put_raw(&hash(key).as_ref(), value)
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Stuff to do with the runtime's storage.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use codec::{FullCodec, FullEncode, Encode, EncodeAppend, EncodeLike, Decode};
|
||||
use crate::traits::Len;
|
||||
|
||||
pub mod unhashed;
|
||||
pub mod hashed;
|
||||
pub mod child;
|
||||
pub mod generator;
|
||||
|
||||
/// A trait for working with macro-generated storage values under the substrate storage API.
|
||||
///
|
||||
/// Details on implementation can be found at
|
||||
/// [`generator::StorageValue`]
|
||||
pub trait StorageValue<T: FullCodec> {
|
||||
/// The type that get/take return.
|
||||
type Query;
|
||||
|
||||
/// Get the storage key.
|
||||
fn hashed_key() -> [u8; 16];
|
||||
|
||||
/// Does the value (explicitly) exist in storage?
|
||||
fn exists() -> bool;
|
||||
|
||||
/// Load the value from the provided storage instance.
|
||||
fn get() -> Self::Query;
|
||||
|
||||
/// Translate a value from some previous type (`O`) to the current type.
|
||||
///
|
||||
/// `f: F` is the translation function.
|
||||
///
|
||||
/// Returns `Err` if the storage item could not be interpreted as the old type, and Ok, along
|
||||
/// with the new value if it could.
|
||||
///
|
||||
/// NOTE: This operates from and to `Option<_>` types; no effort is made to respect the default
|
||||
/// value of the original type.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// This function must be used with care, before being updated the storage still contains the
|
||||
/// old type, thus other calls (such as `get`) will fail at decoding it.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// This would typically be called inside the module implementation of on_initialize, while
|
||||
/// ensuring **no usage of this storage are made before the call to `on_initialize`**. (More
|
||||
/// precisely prior initialized modules doesn't make use of this storage).
|
||||
fn translate<O: Decode, F: FnOnce(Option<O>) -> Option<T>>(f: F) -> Result<Option<T>, ()>;
|
||||
|
||||
/// Store a value under this key into the provided storage instance.
|
||||
fn put<Arg: EncodeLike<T>>(val: Arg);
|
||||
|
||||
/// Mutate the value
|
||||
fn mutate<R, F: FnOnce(&mut Self::Query) -> R>(f: F) -> R;
|
||||
|
||||
/// Clear the storage value.
|
||||
fn kill();
|
||||
|
||||
/// Take a value from storage, removing it afterwards.
|
||||
fn take() -> Self::Query;
|
||||
|
||||
/// Append the given item to the value in the storage.
|
||||
///
|
||||
/// `T` is required to implement `codec::EncodeAppend`.
|
||||
fn append<Items, Item, EncodeLikeItem>(items: Items) -> Result<(), &'static str>
|
||||
where
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
T: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem>,
|
||||
Items::IntoIter: ExactSizeIterator;
|
||||
|
||||
/// Append the given items to the value in the storage.
|
||||
///
|
||||
/// `T` is required to implement `Codec::EncodeAppend`.
|
||||
///
|
||||
/// Upon any failure, it replaces `items` as the new value (assuming that the previous stored
|
||||
/// data is simply corrupt and no longer usable).
|
||||
///
|
||||
/// ### WARNING
|
||||
///
|
||||
/// use with care; if your use-case is not _exactly_ as what this function is doing,
|
||||
/// you should use append and sensibly handle failure within the runtime code if it happens.
|
||||
fn append_or_put<Items, Item, EncodeLikeItem>(items: Items) where
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
T: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem> + Clone + EncodeLike<T>,
|
||||
Items::IntoIter: ExactSizeIterator;
|
||||
|
||||
|
||||
/// Read the length of the value in a fast way, without decoding the entire value.
|
||||
///
|
||||
/// `T` is required to implement `Codec::DecodeLength`.
|
||||
fn decode_len() -> Result<usize, &'static str>
|
||||
where T: codec::DecodeLength + Len;
|
||||
}
|
||||
|
||||
/// A strongly-typed map in storage.
|
||||
///
|
||||
/// Details on implementation can be found at
|
||||
/// [`generator::StorageMap`]
|
||||
pub trait StorageMap<K: FullEncode, V: FullCodec> {
|
||||
/// The type that get/take return.
|
||||
type Query;
|
||||
|
||||
/// Get the storage key used to fetch a value corresponding to a specific key.
|
||||
fn hashed_key_for<KeyArg: EncodeLike<K>>(key: KeyArg) -> Vec<u8>;
|
||||
|
||||
/// Does the value (explicitly) exist in storage?
|
||||
fn exists<KeyArg: EncodeLike<K>>(key: KeyArg) -> bool;
|
||||
|
||||
/// Load the value associated with the given key from the map.
|
||||
fn get<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query;
|
||||
|
||||
/// Swap the values of two keys.
|
||||
fn swap<KeyArg1: EncodeLike<K>, KeyArg2: EncodeLike<K>>(key1: KeyArg1, key2: KeyArg2);
|
||||
|
||||
/// Store a value to be associated with the given key from the map.
|
||||
fn insert<KeyArg: EncodeLike<K>, ValArg: EncodeLike<V>>(key: KeyArg, val: ValArg);
|
||||
|
||||
/// Remove the value under a key.
|
||||
fn remove<KeyArg: EncodeLike<K>>(key: KeyArg);
|
||||
|
||||
/// Mutate the value under a key.
|
||||
fn mutate<KeyArg: EncodeLike<K>, R, F: FnOnce(&mut Self::Query) -> R>(key: KeyArg, f: F) -> R;
|
||||
|
||||
/// Take the value under a key.
|
||||
fn take<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query;
|
||||
|
||||
/// Append the given items to the value in the storage.
|
||||
///
|
||||
/// `V` is required to implement `codec::EncodeAppend`.
|
||||
fn append<Items, Item, EncodeLikeItem, KeyArg>(key: KeyArg, items: Items) -> Result<(), &'static str>
|
||||
where
|
||||
KeyArg: EncodeLike<K>,
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
V: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem>,
|
||||
Items::IntoIter: ExactSizeIterator;
|
||||
|
||||
/// Safely append the given items to the value in the storage. If a codec error occurs, then the
|
||||
/// old (presumably corrupt) value is replaced with the given `items`.
|
||||
///
|
||||
/// `V` is required to implement `codec::EncodeAppend`.
|
||||
fn append_or_insert<Items, Item, EncodeLikeItem, KeyArg>(key: KeyArg, items: Items)
|
||||
where
|
||||
KeyArg: EncodeLike<K>,
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
V: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem> + Clone + EncodeLike<V>,
|
||||
Items::IntoIter: ExactSizeIterator;
|
||||
|
||||
/// Read the length of the value in a fast way, without decoding the entire value.
|
||||
///
|
||||
/// `T` is required to implement `Codec::DecodeLength`.
|
||||
///
|
||||
/// Note that `0` is returned as the default value if no encoded value exists at the given key.
|
||||
/// Therefore, this function cannot be used as a sign of _existence_. use the `::exists()`
|
||||
/// function for this purpose.
|
||||
fn decode_len<KeyArg: EncodeLike<K>>(key: KeyArg) -> Result<usize, &'static str>
|
||||
where V: codec::DecodeLength + Len;
|
||||
}
|
||||
|
||||
/// A strongly-typed linked map in storage.
|
||||
///
|
||||
/// Similar to `StorageMap` but allows to enumerate other elements and doesn't implement append.
|
||||
///
|
||||
/// Details on implementation can be found at
|
||||
/// [`generator::StorageLinkedMap`]
|
||||
pub trait StorageLinkedMap<K: FullCodec, V: FullCodec> {
|
||||
/// The type that get/take return.
|
||||
type Query;
|
||||
|
||||
/// The type that iterates over all `(key, value)`.
|
||||
type Enumerator: Iterator<Item = (K, V)>;
|
||||
|
||||
/// Does the value (explicitly) exist in storage?
|
||||
fn exists<KeyArg: EncodeLike<K>>(key: KeyArg) -> bool;
|
||||
|
||||
/// Load the value associated with the given key from the map.
|
||||
fn get<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query;
|
||||
|
||||
/// Swap the values of two keys.
|
||||
fn swap<KeyArg1: EncodeLike<K>, KeyArg2: EncodeLike<K>>(key1: KeyArg1, key2: KeyArg2);
|
||||
|
||||
/// Store a value to be associated with the given key from the map.
|
||||
fn insert<KeyArg: EncodeLike<K>, ValArg: EncodeLike<V>>(key: KeyArg, val: ValArg);
|
||||
|
||||
/// Remove the value under a key.
|
||||
fn remove<KeyArg: EncodeLike<K>>(key: KeyArg);
|
||||
|
||||
/// Mutate the value under a key.
|
||||
fn mutate<KeyArg: EncodeLike<K>, R, F: FnOnce(&mut Self::Query) -> R>(key: KeyArg, f: F) -> R;
|
||||
|
||||
/// Take the value under a key.
|
||||
fn take<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query;
|
||||
|
||||
/// Return current head element.
|
||||
fn head() -> Option<K>;
|
||||
|
||||
/// Enumerate all elements in the map.
|
||||
fn enumerate() -> Self::Enumerator;
|
||||
|
||||
/// Read the length of the value in a fast way, without decoding the entire value.
|
||||
///
|
||||
/// `T` is required to implement `Codec::DecodeLength`.
|
||||
///
|
||||
/// Note that `0` is returned as the default value if no encoded value exists at the given key.
|
||||
/// Therefore, this function cannot be used as a sign of _existence_. use the `::exists()`
|
||||
/// function for this purpose.
|
||||
fn decode_len<KeyArg: EncodeLike<K>>(key: KeyArg) -> Result<usize, &'static str>
|
||||
where V: codec::DecodeLength + Len;
|
||||
|
||||
/// Translate the keys and values from some previous `(K2, V2)` to the current type.
|
||||
///
|
||||
/// `TK` translates keys from the old type, and `TV` translates values.
|
||||
///
|
||||
/// Returns `Err` if the map could not be interpreted as the old type, and Ok if it could.
|
||||
/// The `Err` contains the first key which could not be migrated, or `None` if the
|
||||
/// head of the list could not be read.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// This function must be used with care, before being updated the storage still contains the
|
||||
/// old type, thus other calls (such as `get`) will fail at decoding it.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// This would typically be called inside the module implementation of on_initialize, while
|
||||
/// ensuring **no usage of this storage are made before the call to `on_initialize`**. (More
|
||||
/// precisely prior initialized modules doesn't make use of this storage).
|
||||
fn translate<K2, V2, TK, TV>(translate_key: TK, translate_val: TV) -> Result<(), Option<K2>>
|
||||
where K2: FullCodec + Clone, V2: Decode, TK: Fn(K2) -> K, TV: Fn(V2) -> V;
|
||||
}
|
||||
|
||||
/// An implementation of a map with a two keys.
|
||||
///
|
||||
/// It provides an important ability to efficiently remove all entries
|
||||
/// that have a common first key.
|
||||
///
|
||||
/// Details on implementation can be found at
|
||||
/// [`generator::StorageDoubleMap`]
|
||||
pub trait StorageDoubleMap<K1: FullEncode, K2: FullEncode, V: FullCodec> {
|
||||
/// The type that get/take returns.
|
||||
type Query;
|
||||
|
||||
fn hashed_key_for<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Vec<u8>
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>;
|
||||
|
||||
fn exists<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> bool
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>;
|
||||
|
||||
fn get<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Self::Query
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>;
|
||||
|
||||
fn take<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Self::Query
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>;
|
||||
|
||||
fn insert<KArg1, KArg2, VArg>(k1: KArg1, k2: KArg2, val: VArg)
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
VArg: EncodeLike<V>;
|
||||
|
||||
fn remove<KArg1, KArg2>(k1: KArg1, k2: KArg2)
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>;
|
||||
|
||||
fn remove_prefix<KArg1>(k1: KArg1) where KArg1: ?Sized + EncodeLike<K1>;
|
||||
|
||||
fn mutate<KArg1, KArg2, R, F>(k1: KArg1, k2: KArg2, f: F) -> R
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
F: FnOnce(&mut Self::Query) -> R;
|
||||
|
||||
fn append<Items, Item, EncodeLikeItem, KArg1, KArg2>(
|
||||
k1: KArg1,
|
||||
k2: KArg2,
|
||||
items: Items,
|
||||
) -> Result<(), &'static str>
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
V: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem>,
|
||||
Items::IntoIter: ExactSizeIterator;
|
||||
|
||||
fn append_or_insert<Items, Item, EncodeLikeItem, KArg1, KArg2>(
|
||||
k1: KArg1,
|
||||
k2: KArg2,
|
||||
items: Items,
|
||||
)
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
V: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem> + Clone + EncodeLike<V>,
|
||||
Items::IntoIter: ExactSizeIterator;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Operation on unhashed runtime storage.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use codec::{Encode, Decode};
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `None` if there is no explicit entry.
|
||||
pub fn get<T: Decode + Sized>(key: &[u8]) -> Option<T> {
|
||||
runtime_io::storage::get(key).and_then(|val| {
|
||||
Decode::decode(&mut &val[..]).map(Some).unwrap_or_else(|_| {
|
||||
// TODO #3700: error should be handleable.
|
||||
runtime_print!("ERROR: Corrupted state at {:?}", key);
|
||||
None
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or the type's default if there is no
|
||||
/// explicit entry.
|
||||
pub fn get_or_default<T: Decode + Sized + Default>(key: &[u8]) -> T {
|
||||
get(key).unwrap_or_else(Default::default)
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value` if there is no
|
||||
/// explicit entry.
|
||||
pub fn get_or<T: Decode + Sized>(key: &[u8], default_value: T) -> T {
|
||||
get(key).unwrap_or(default_value)
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
|
||||
/// explicit entry.
|
||||
pub fn get_or_else<T: Decode + Sized, F: FnOnce() -> T>(key: &[u8], default_value: F) -> T {
|
||||
get(key).unwrap_or_else(default_value)
|
||||
}
|
||||
|
||||
/// Put `value` in storage under `key`.
|
||||
pub fn put<T: Encode + ?Sized>(key: &[u8], value: &T) {
|
||||
value.using_encoded(|slice| runtime_io::storage::set(key, slice));
|
||||
}
|
||||
|
||||
/// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise.
|
||||
pub fn take<T: Decode + Sized>(key: &[u8]) -> Option<T> {
|
||||
let r = get(key);
|
||||
if r.is_some() {
|
||||
kill(key);
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
/// Remove `key` from storage, returning its value, or, if there was no explicit entry in storage,
|
||||
/// the default for its type.
|
||||
pub fn take_or_default<T: Decode + Sized + Default>(key: &[u8]) -> T {
|
||||
take(key).unwrap_or_else(Default::default)
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value` if there is no
|
||||
/// explicit entry. Ensure there is no explicit entry on return.
|
||||
pub fn take_or<T: Decode + Sized>(key: &[u8], default_value: T) -> T {
|
||||
take(key).unwrap_or(default_value)
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
|
||||
/// explicit entry. Ensure there is no explicit entry on return.
|
||||
pub fn take_or_else<T: Decode + Sized, F: FnOnce() -> T>(key: &[u8], default_value: F) -> T {
|
||||
take(key).unwrap_or_else(default_value)
|
||||
}
|
||||
|
||||
/// Check to see if `key` has an explicit entry in storage.
|
||||
pub fn exists(key: &[u8]) -> bool {
|
||||
runtime_io::storage::read(key, &mut [0;0][..], 0).is_some()
|
||||
}
|
||||
|
||||
/// Ensure `key` has no explicit entry in storage.
|
||||
pub fn kill(key: &[u8]) {
|
||||
runtime_io::storage::clear(key);
|
||||
}
|
||||
|
||||
/// Ensure keys with the given `prefix` have no entries in storage.
|
||||
pub fn kill_prefix(prefix: &[u8]) {
|
||||
runtime_io::storage::clear_prefix(prefix);
|
||||
}
|
||||
|
||||
/// Get a Vec of bytes from storage.
|
||||
pub fn get_raw(key: &[u8]) -> Option<Vec<u8>> {
|
||||
runtime_io::storage::get(key)
|
||||
}
|
||||
|
||||
/// Put a raw byte slice into storage.
|
||||
pub fn put_raw(key: &[u8], value: &[u8]) {
|
||||
runtime_io::storage::set(key, value)
|
||||
}
|
||||
@@ -0,0 +1,746 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Traits for SRML.
|
||||
//!
|
||||
//! NOTE: If you're looking for `parameter_types`, it has moved in to the top-level module.
|
||||
|
||||
use rstd::{prelude::*, result, marker::PhantomData, ops::Div, fmt::Debug};
|
||||
use codec::{FullCodec, Codec, Encode, Decode};
|
||||
use primitives::u32_trait::Value as U32;
|
||||
use sr_primitives::{
|
||||
ConsensusEngineId,
|
||||
traits::{MaybeSerializeDeserialize, SimpleArithmetic, Saturating},
|
||||
};
|
||||
|
||||
/// Anything that can have a `::len()` method.
|
||||
pub trait Len {
|
||||
/// Return the length of data type.
|
||||
fn len(&self) -> usize;
|
||||
}
|
||||
|
||||
impl<T: IntoIterator + Clone,> Len for T where <T as IntoIterator>::IntoIter: ExactSizeIterator {
|
||||
fn len(&self) -> usize {
|
||||
self.clone().into_iter().len()
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for querying a single fixed value from a type.
|
||||
pub trait Get<T> {
|
||||
/// Return a constant value.
|
||||
fn get() -> T;
|
||||
}
|
||||
|
||||
impl<T: Default> Get<T> for () {
|
||||
fn get() -> T { T::default() }
|
||||
}
|
||||
|
||||
/// A trait for querying whether a type can be said to statically "contain" a value. Similar
|
||||
/// in nature to `Get`, except it is designed to be lazy rather than active (you can't ask it to
|
||||
/// enumerate all values that it contains) and work for multiple values rather than just one.
|
||||
pub trait Contains<T> {
|
||||
/// Return `true` if this "contains" the given value `t`.
|
||||
fn contains(t: &T) -> bool;
|
||||
}
|
||||
|
||||
impl<V: PartialEq, T: Get<V>> Contains<V> for T {
|
||||
fn contains(t: &V) -> bool {
|
||||
&Self::get() == t
|
||||
}
|
||||
}
|
||||
|
||||
/// The account with the given id was killed.
|
||||
#[impl_trait_for_tuples::impl_for_tuples(30)]
|
||||
pub trait OnFreeBalanceZero<AccountId> {
|
||||
/// The account was the given id was killed.
|
||||
fn on_free_balance_zero(who: &AccountId);
|
||||
}
|
||||
|
||||
/// Outcome of a balance update.
|
||||
pub enum UpdateBalanceOutcome {
|
||||
/// Account balance was simply updated.
|
||||
Updated,
|
||||
/// The update led to killing the account.
|
||||
AccountKilled,
|
||||
}
|
||||
|
||||
/// A trait for finding the author of a block header based on the `PreRuntime` digests contained
|
||||
/// within it.
|
||||
pub trait FindAuthor<Author> {
|
||||
/// Find the author of a block based on the pre-runtime digests.
|
||||
fn find_author<'a, I>(digests: I) -> Option<Author>
|
||||
where I: 'a + IntoIterator<Item=(ConsensusEngineId, &'a [u8])>;
|
||||
}
|
||||
|
||||
impl<A> FindAuthor<A> for () {
|
||||
fn find_author<'a, I>(_: I) -> Option<A>
|
||||
where I: 'a + IntoIterator<Item=(ConsensusEngineId, &'a [u8])>
|
||||
{
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for verifying the seal of a header and returning the author.
|
||||
pub trait VerifySeal<Header, Author> {
|
||||
/// Verify a header and return the author, if any.
|
||||
fn verify_seal(header: &Header) -> Result<Option<Author>, &'static str>;
|
||||
}
|
||||
|
||||
/// Something which can compute and check proofs of
|
||||
/// a historical key owner and return full identification data of that
|
||||
/// key owner.
|
||||
pub trait KeyOwnerProofSystem<Key> {
|
||||
/// The proof of membership itself.
|
||||
type Proof: Codec;
|
||||
/// The full identification of a key owner and the stash account.
|
||||
type IdentificationTuple: Codec;
|
||||
|
||||
/// Prove membership of a key owner in the current block-state.
|
||||
///
|
||||
/// This should typically only be called off-chain, since it may be
|
||||
/// computationally heavy.
|
||||
///
|
||||
/// Returns `Some` iff the key owner referred to by the given `key` is a
|
||||
/// member of the current set.
|
||||
fn prove(key: Key) -> Option<Self::Proof>;
|
||||
|
||||
/// Check a proof of membership on-chain. Return `Some` iff the proof is
|
||||
/// valid and recent enough to check.
|
||||
fn check_proof(key: Key, proof: Self::Proof) -> Option<Self::IdentificationTuple>;
|
||||
}
|
||||
|
||||
/// Handler for when some currency "account" decreased in balance for
|
||||
/// some reason.
|
||||
///
|
||||
/// The only reason at present for an increase would be for validator rewards, but
|
||||
/// there may be other reasons in the future or for other chains.
|
||||
///
|
||||
/// Reasons for decreases include:
|
||||
///
|
||||
/// - Someone got slashed.
|
||||
/// - Someone paid for a transaction to be included.
|
||||
pub trait OnUnbalanced<Imbalance: TryDrop> {
|
||||
/// Handler for some imbalance. Infallible.
|
||||
fn on_unbalanced(amount: Imbalance) {
|
||||
amount.try_drop().unwrap_or_else(Self::on_nonzero_unbalanced)
|
||||
}
|
||||
|
||||
/// Actually handle a non-zero imbalance. You probably want to implement this rather than
|
||||
/// `on_unbalanced`.
|
||||
fn on_nonzero_unbalanced(amount: Imbalance);
|
||||
}
|
||||
|
||||
impl<Imbalance: TryDrop> OnUnbalanced<Imbalance> for () {
|
||||
fn on_nonzero_unbalanced(amount: Imbalance) { drop(amount); }
|
||||
}
|
||||
|
||||
/// Simple boolean for whether an account needs to be kept in existence.
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub enum ExistenceRequirement {
|
||||
/// Operation must not result in the account going out of existence.
|
||||
///
|
||||
/// Note this implies that if the account never existed in the first place, then the operation
|
||||
/// may legitimately leave the account unchanged and still non-existent.
|
||||
KeepAlive,
|
||||
/// Operation may result in account going out of existence.
|
||||
AllowDeath,
|
||||
}
|
||||
|
||||
/// A type for which some values make sense to be able to drop without further consideration.
|
||||
pub trait TryDrop: Sized {
|
||||
/// Drop an instance cleanly. Only works if its value represents "no-operation".
|
||||
fn try_drop(self) -> Result<(), Self>;
|
||||
}
|
||||
|
||||
/// A trait for a not-quite Linear Type that tracks an imbalance.
|
||||
///
|
||||
/// Functions that alter account balances return an object of this trait to
|
||||
/// express how much account balances have been altered in aggregate. If
|
||||
/// dropped, the currency system will take some default steps to deal with
|
||||
/// the imbalance (`balances` module simply reduces or increases its
|
||||
/// total issuance). Your module should generally handle it in some way,
|
||||
/// good practice is to do so in a configurable manner using an
|
||||
/// `OnUnbalanced` type for each situation in which your module needs to
|
||||
/// handle an imbalance.
|
||||
///
|
||||
/// Imbalances can either be Positive (funds were added somewhere without
|
||||
/// being subtracted elsewhere - e.g. a reward) or Negative (funds deducted
|
||||
/// somewhere without an equal and opposite addition - e.g. a slash or
|
||||
/// system fee payment).
|
||||
///
|
||||
/// Since they are unsigned, the actual type is always Positive or Negative.
|
||||
/// The trait makes no distinction except to define the `Opposite` type.
|
||||
///
|
||||
/// New instances of zero value can be created (`zero`) and destroyed
|
||||
/// (`drop_zero`).
|
||||
///
|
||||
/// Existing instances can be `split` and merged either consuming `self` with
|
||||
/// `merge` or mutating `self` with `subsume`. If the target is an `Option`,
|
||||
/// then `maybe_merge` and `maybe_subsume` might work better. Instances can
|
||||
/// also be `offset` with an `Opposite` that is less than or equal to in value.
|
||||
///
|
||||
/// You can always retrieve the raw balance value using `peek`.
|
||||
#[must_use]
|
||||
pub trait Imbalance<Balance>: Sized + TryDrop {
|
||||
/// The oppositely imbalanced type. They come in pairs.
|
||||
type Opposite: Imbalance<Balance>;
|
||||
|
||||
/// The zero imbalance. Can be destroyed with `drop_zero`.
|
||||
fn zero() -> Self;
|
||||
|
||||
/// Drop an instance cleanly. Only works if its `self.value()` is zero.
|
||||
fn drop_zero(self) -> Result<(), Self>;
|
||||
|
||||
/// Consume `self` and return two independent instances; the first
|
||||
/// is guaranteed to be at most `amount` and the second will be the remainder.
|
||||
fn split(self, amount: Balance) -> (Self, Self);
|
||||
|
||||
/// Consume `self` and an `other` to return a new instance that combines
|
||||
/// both.
|
||||
fn merge(self, other: Self) -> Self;
|
||||
|
||||
/// Consume `self` and maybe an `other` to return a new instance that combines
|
||||
/// both.
|
||||
fn maybe_merge(self, other: Option<Self>) -> Self {
|
||||
if let Some(o) = other {
|
||||
self.merge(o)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Consume an `other` to mutate `self` into a new instance that combines
|
||||
/// both.
|
||||
fn subsume(&mut self, other: Self);
|
||||
|
||||
/// Maybe consume an `other` to mutate `self` into a new instance that combines
|
||||
/// both.
|
||||
fn maybe_subsume(&mut self, other: Option<Self>) {
|
||||
if let Some(o) = other {
|
||||
self.subsume(o)
|
||||
}
|
||||
}
|
||||
|
||||
/// Consume self and along with an opposite counterpart to return
|
||||
/// a combined result.
|
||||
///
|
||||
/// Returns `Ok` along with a new instance of `Self` if this instance has a
|
||||
/// greater value than the `other`. Otherwise returns `Err` with an instance of
|
||||
/// the `Opposite`. In both cases the value represents the combination of `self`
|
||||
/// and `other`.
|
||||
fn offset(self, other: Self::Opposite) -> Result<Self, Self::Opposite>;
|
||||
|
||||
/// The raw value of self.
|
||||
fn peek(&self) -> Balance;
|
||||
}
|
||||
|
||||
/// Either a positive or a negative imbalance.
|
||||
pub enum SignedImbalance<B, P: Imbalance<B>>{
|
||||
/// A positive imbalance (funds have been created but none destroyed).
|
||||
Positive(P),
|
||||
/// A negative imbalance (funds have been destroyed but none created).
|
||||
Negative(P::Opposite),
|
||||
}
|
||||
|
||||
impl<
|
||||
P: Imbalance<B, Opposite=N>,
|
||||
N: Imbalance<B, Opposite=P>,
|
||||
B: SimpleArithmetic + FullCodec + Copy + MaybeSerializeDeserialize + Debug + Default,
|
||||
> SignedImbalance<B, P> {
|
||||
pub fn zero() -> Self {
|
||||
SignedImbalance::Positive(P::zero())
|
||||
}
|
||||
|
||||
pub fn drop_zero(self) -> Result<(), Self> {
|
||||
match self {
|
||||
SignedImbalance::Positive(x) => x.drop_zero().map_err(SignedImbalance::Positive),
|
||||
SignedImbalance::Negative(x) => x.drop_zero().map_err(SignedImbalance::Negative),
|
||||
}
|
||||
}
|
||||
|
||||
/// Consume `self` and an `other` to return a new instance that combines
|
||||
/// both.
|
||||
pub fn merge(self, other: Self) -> Self {
|
||||
match (self, other) {
|
||||
(SignedImbalance::Positive(one), SignedImbalance::Positive(other)) =>
|
||||
SignedImbalance::Positive(one.merge(other)),
|
||||
(SignedImbalance::Negative(one), SignedImbalance::Negative(other)) =>
|
||||
SignedImbalance::Negative(one.merge(other)),
|
||||
(SignedImbalance::Positive(one), SignedImbalance::Negative(other)) =>
|
||||
if one.peek() > other.peek() {
|
||||
SignedImbalance::Positive(one.offset(other).ok().unwrap_or_else(P::zero))
|
||||
} else {
|
||||
SignedImbalance::Negative(other.offset(one).ok().unwrap_or_else(N::zero))
|
||||
},
|
||||
(one, other) => other.merge(one),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Split an unbalanced amount two ways between a common divisor.
|
||||
pub struct SplitTwoWays<
|
||||
Balance,
|
||||
Imbalance,
|
||||
Part1,
|
||||
Target1,
|
||||
Part2,
|
||||
Target2,
|
||||
>(PhantomData<(Balance, Imbalance, Part1, Target1, Part2, Target2)>);
|
||||
|
||||
impl<
|
||||
Balance: From<u32> + Saturating + Div<Output=Balance>,
|
||||
I: Imbalance<Balance>,
|
||||
Part1: U32,
|
||||
Target1: OnUnbalanced<I>,
|
||||
Part2: U32,
|
||||
Target2: OnUnbalanced<I>,
|
||||
> OnUnbalanced<I> for SplitTwoWays<Balance, I, Part1, Target1, Part2, Target2>
|
||||
{
|
||||
fn on_nonzero_unbalanced(amount: I) {
|
||||
let total: u32 = Part1::VALUE + Part2::VALUE;
|
||||
let amount1 = amount.peek().saturating_mul(Part1::VALUE.into()) / total.into();
|
||||
let (imb1, imb2) = amount.split(amount1);
|
||||
Target1::on_unbalanced(imb1);
|
||||
Target2::on_unbalanced(imb2);
|
||||
}
|
||||
}
|
||||
|
||||
/// Abstraction over a fungible assets system.
|
||||
pub trait Currency<AccountId> {
|
||||
/// The balance of an account.
|
||||
type Balance: SimpleArithmetic + FullCodec + Copy + MaybeSerializeDeserialize + Debug + Default;
|
||||
|
||||
/// The opaque token type for an imbalance. This is returned by unbalanced operations
|
||||
/// and must be dealt with. It may be dropped but cannot be cloned.
|
||||
type PositiveImbalance: Imbalance<Self::Balance, Opposite=Self::NegativeImbalance>;
|
||||
|
||||
/// The opaque token type for an imbalance. This is returned by unbalanced operations
|
||||
/// and must be dealt with. It may be dropped but cannot be cloned.
|
||||
type NegativeImbalance: Imbalance<Self::Balance, Opposite=Self::PositiveImbalance>;
|
||||
|
||||
// PUBLIC IMMUTABLES
|
||||
|
||||
/// The combined balance of `who`.
|
||||
fn total_balance(who: &AccountId) -> Self::Balance;
|
||||
|
||||
/// Same result as `slash(who, value)` (but without the side-effects) assuming there are no
|
||||
/// balance changes in the meantime and only the reserved balance is not taken into account.
|
||||
fn can_slash(who: &AccountId, value: Self::Balance) -> bool;
|
||||
|
||||
/// The total amount of issuance in the system.
|
||||
fn total_issuance() -> Self::Balance;
|
||||
|
||||
/// The minimum balance any single account may have. This is equivalent to the `Balances` module's
|
||||
/// `ExistentialDeposit`.
|
||||
fn minimum_balance() -> Self::Balance;
|
||||
|
||||
/// Reduce the total issuance by `amount` and return the according imbalance. The imbalance will
|
||||
/// typically be used to reduce an account by the same amount with e.g. `settle`.
|
||||
///
|
||||
/// This is infallible, but doesn't guarantee that the entire `amount` is burnt, for example
|
||||
/// in the case of underflow.
|
||||
fn burn(amount: Self::Balance) -> Self::PositiveImbalance;
|
||||
|
||||
/// Increase the total issuance by `amount` and return the according imbalance. The imbalance
|
||||
/// will typically be used to increase an account by the same amount with e.g.
|
||||
/// `resolve_into_existing` or `resolve_creating`.
|
||||
///
|
||||
/// This is infallible, but doesn't guarantee that the entire `amount` is issued, for example
|
||||
/// in the case of overflow.
|
||||
fn issue(amount: Self::Balance) -> Self::NegativeImbalance;
|
||||
|
||||
/// The 'free' balance of a given account.
|
||||
///
|
||||
/// This is the only balance that matters in terms of most operations on tokens. It alone
|
||||
/// is used to determine the balance when in the contract execution environment. When this
|
||||
/// balance falls below the value of `ExistentialDeposit`, then the 'current account' is
|
||||
/// deleted: specifically `FreeBalance`. Further, the `OnFreeBalanceZero` callback
|
||||
/// is invoked, giving a chance to external modules to clean up data associated with
|
||||
/// the deleted account.
|
||||
///
|
||||
/// `system::AccountNonce` is also deleted if `ReservedBalance` is also zero (it also gets
|
||||
/// collapsed to zero if it ever becomes less than `ExistentialDeposit`.
|
||||
fn free_balance(who: &AccountId) -> Self::Balance;
|
||||
|
||||
/// Returns `Ok` iff the account is able to make a withdrawal of the given amount
|
||||
/// for the given reason. Basically, it's just a dry-run of `withdraw`.
|
||||
///
|
||||
/// `Err(...)` with the reason why not otherwise.
|
||||
fn ensure_can_withdraw(
|
||||
who: &AccountId,
|
||||
_amount: Self::Balance,
|
||||
reasons: WithdrawReasons,
|
||||
new_balance: Self::Balance,
|
||||
) -> result::Result<(), &'static str>;
|
||||
|
||||
// PUBLIC MUTABLES (DANGEROUS)
|
||||
|
||||
/// Transfer some liquid free balance to another staker.
|
||||
///
|
||||
/// This is a very high-level function. It will ensure all appropriate fees are paid
|
||||
/// and no imbalance in the system remains.
|
||||
fn transfer(
|
||||
source: &AccountId,
|
||||
dest: &AccountId,
|
||||
value: Self::Balance,
|
||||
existence_requirement: ExistenceRequirement,
|
||||
) -> result::Result<(), &'static str>;
|
||||
|
||||
/// Deducts up to `value` from the combined balance of `who`, preferring to deduct from the
|
||||
/// free balance. This function cannot fail.
|
||||
///
|
||||
/// The resulting imbalance is the first item of the tuple returned.
|
||||
///
|
||||
/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
|
||||
/// then a non-zero second item will be returned.
|
||||
fn slash(
|
||||
who: &AccountId,
|
||||
value: Self::Balance
|
||||
) -> (Self::NegativeImbalance, Self::Balance);
|
||||
|
||||
/// Mints `value` to the free balance of `who`.
|
||||
///
|
||||
/// If `who` doesn't exist, nothing is done and an Err returned.
|
||||
fn deposit_into_existing(
|
||||
who: &AccountId,
|
||||
value: Self::Balance
|
||||
) -> result::Result<Self::PositiveImbalance, &'static str>;
|
||||
|
||||
/// Similar to deposit_creating, only accepts a `NegativeImbalance` and returns nothing on
|
||||
/// success.
|
||||
fn resolve_into_existing(
|
||||
who: &AccountId,
|
||||
value: Self::NegativeImbalance,
|
||||
) -> result::Result<(), Self::NegativeImbalance> {
|
||||
let v = value.peek();
|
||||
match Self::deposit_into_existing(who, v) {
|
||||
Ok(opposite) => Ok(drop(value.offset(opposite))),
|
||||
_ => Err(value),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds up to `value` to the free balance of `who`. If `who` doesn't exist, it is created.
|
||||
///
|
||||
/// Infallible.
|
||||
fn deposit_creating(
|
||||
who: &AccountId,
|
||||
value: Self::Balance,
|
||||
) -> Self::PositiveImbalance;
|
||||
|
||||
/// Similar to deposit_creating, only accepts a `NegativeImbalance` and returns nothing on
|
||||
/// success.
|
||||
fn resolve_creating(
|
||||
who: &AccountId,
|
||||
value: Self::NegativeImbalance,
|
||||
) {
|
||||
let v = value.peek();
|
||||
drop(value.offset(Self::deposit_creating(who, v)));
|
||||
}
|
||||
|
||||
/// Removes some free balance from `who` account for `reason` if possible. If `liveness` is
|
||||
/// `KeepAlive`, then no less than `ExistentialDeposit` must be left remaining.
|
||||
///
|
||||
/// This checks any locks, vesting, and liquidity requirements. If the removal is not possible,
|
||||
/// then it returns `Err`.
|
||||
///
|
||||
/// If the operation is successful, this will return `Ok` with a `NegativeImbalance` whose value
|
||||
/// is `value`.
|
||||
fn withdraw(
|
||||
who: &AccountId,
|
||||
value: Self::Balance,
|
||||
reasons: WithdrawReasons,
|
||||
liveness: ExistenceRequirement,
|
||||
) -> result::Result<Self::NegativeImbalance, &'static str>;
|
||||
|
||||
/// Similar to withdraw, only accepts a `PositiveImbalance` and returns nothing on success.
|
||||
fn settle(
|
||||
who: &AccountId,
|
||||
value: Self::PositiveImbalance,
|
||||
reasons: WithdrawReasons,
|
||||
liveness: ExistenceRequirement,
|
||||
) -> result::Result<(), Self::PositiveImbalance> {
|
||||
let v = value.peek();
|
||||
match Self::withdraw(who, v, reasons, liveness) {
|
||||
Ok(opposite) => Ok(drop(value.offset(opposite))),
|
||||
_ => Err(value),
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure an account's free balance equals some value; this will create the account
|
||||
/// if needed.
|
||||
///
|
||||
/// Returns a signed imbalance and status to indicate if the account was successfully updated or update
|
||||
/// has led to killing of the account.
|
||||
fn make_free_balance_be(
|
||||
who: &AccountId,
|
||||
balance: Self::Balance,
|
||||
) -> (
|
||||
SignedImbalance<Self::Balance, Self::PositiveImbalance>,
|
||||
UpdateBalanceOutcome,
|
||||
);
|
||||
}
|
||||
|
||||
/// A currency where funds can be reserved from the user.
|
||||
pub trait ReservableCurrency<AccountId>: Currency<AccountId> {
|
||||
/// Same result as `reserve(who, value)` (but without the side-effects) assuming there
|
||||
/// are no balance changes in the meantime.
|
||||
fn can_reserve(who: &AccountId, value: Self::Balance) -> bool;
|
||||
|
||||
/// Deducts up to `value` from reserved balance of `who`. This function cannot fail.
|
||||
///
|
||||
/// As much funds up to `value` will be deducted as possible. If the reserve balance of `who`
|
||||
/// is less than `value`, then a non-zero second item will be returned.
|
||||
fn slash_reserved(
|
||||
who: &AccountId,
|
||||
value: Self::Balance
|
||||
) -> (Self::NegativeImbalance, Self::Balance);
|
||||
|
||||
/// The amount of the balance of a given account that is externally reserved; this can still get
|
||||
/// slashed, but gets slashed last of all.
|
||||
///
|
||||
/// This balance is a 'reserve' balance that other subsystems use in order to set aside tokens
|
||||
/// that are still 'owned' by the account holder, but which are suspendable.
|
||||
///
|
||||
/// When this balance falls below the value of `ExistentialDeposit`, then this 'reserve account'
|
||||
/// is deleted: specifically, `ReservedBalance`.
|
||||
///
|
||||
/// `system::AccountNonce` is also deleted if `FreeBalance` is also zero (it also gets
|
||||
/// collapsed to zero if it ever becomes less than `ExistentialDeposit`.
|
||||
fn reserved_balance(who: &AccountId) -> Self::Balance;
|
||||
|
||||
|
||||
/// Moves `value` from balance to reserved balance.
|
||||
///
|
||||
/// If the free balance is lower than `value`, then no funds will be moved and an `Err` will
|
||||
/// be returned to notify of this. This is different behavior than `unreserve`.
|
||||
fn reserve(who: &AccountId, value: Self::Balance) -> result::Result<(), &'static str>;
|
||||
|
||||
/// Moves up to `value` from reserved balance to free balance. This function cannot fail.
|
||||
///
|
||||
/// As much funds up to `value` will be moved as possible. If the reserve balance of `who`
|
||||
/// is less than `value`, then the remaining amount will be returned.
|
||||
///
|
||||
/// # NOTES
|
||||
///
|
||||
/// - This is different from `reserve`.
|
||||
/// - If the remaining reserved balance is less than `ExistentialDeposit`, it will
|
||||
/// invoke `on_reserved_too_low` and could reap the account.
|
||||
fn unreserve(who: &AccountId, value: Self::Balance) -> Self::Balance;
|
||||
|
||||
/// Moves up to `value` from reserved balance of account `slashed` to free balance of account
|
||||
/// `beneficiary`. `beneficiary` must exist for this to succeed. If it does not, `Err` will be
|
||||
/// returned.
|
||||
///
|
||||
/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
|
||||
/// then `Ok(non_zero)` will be returned.
|
||||
fn repatriate_reserved(
|
||||
slashed: &AccountId,
|
||||
beneficiary: &AccountId,
|
||||
value: Self::Balance
|
||||
) -> result::Result<Self::Balance, &'static str>;
|
||||
}
|
||||
|
||||
/// An identifier for a lock. Used for disambiguating different locks so that
|
||||
/// they can be individually replaced or removed.
|
||||
pub type LockIdentifier = [u8; 8];
|
||||
|
||||
/// A currency whose accounts can have liquidity restrictions.
|
||||
pub trait LockableCurrency<AccountId>: Currency<AccountId> {
|
||||
/// The quantity used to denote time; usually just a `BlockNumber`.
|
||||
type Moment;
|
||||
|
||||
/// Create a new balance lock on account `who`.
|
||||
///
|
||||
/// If the new lock is valid (i.e. not already expired), it will push the struct to
|
||||
/// the `Locks` vec in storage. Note that you can lock more funds than a user has.
|
||||
///
|
||||
/// If the lock `id` already exists, this will update it.
|
||||
fn set_lock(
|
||||
id: LockIdentifier,
|
||||
who: &AccountId,
|
||||
amount: Self::Balance,
|
||||
until: Self::Moment,
|
||||
reasons: WithdrawReasons,
|
||||
);
|
||||
|
||||
/// Changes a balance lock (selected by `id`) so that it becomes less liquid in all
|
||||
/// parameters or creates a new one if it does not exist.
|
||||
///
|
||||
/// Calling `extend_lock` on an existing lock `id` differs from `set_lock` in that it
|
||||
/// applies the most severe constraints of the two, while `set_lock` replaces the lock
|
||||
/// with the new parameters. As in, `extend_lock` will set:
|
||||
/// - maximum `amount`
|
||||
/// - farthest duration (`until`)
|
||||
/// - bitwise mask of all `reasons`
|
||||
fn extend_lock(
|
||||
id: LockIdentifier,
|
||||
who: &AccountId,
|
||||
amount: Self::Balance,
|
||||
until: Self::Moment,
|
||||
reasons: WithdrawReasons,
|
||||
);
|
||||
|
||||
/// Remove an existing lock.
|
||||
fn remove_lock(
|
||||
id: LockIdentifier,
|
||||
who: &AccountId,
|
||||
);
|
||||
}
|
||||
|
||||
bitmask! {
|
||||
/// Reasons for moving funds out of an account.
|
||||
#[derive(Encode, Decode)]
|
||||
pub mask WithdrawReasons: i8 where
|
||||
|
||||
/// Reason for moving funds out of an account.
|
||||
#[derive(Encode, Decode)]
|
||||
flags WithdrawReason {
|
||||
/// In order to pay for (system) transaction costs.
|
||||
TransactionPayment = 0b00000001,
|
||||
/// In order to transfer ownership.
|
||||
Transfer = 0b00000010,
|
||||
/// In order to reserve some funds for a later return or repatriation
|
||||
Reserve = 0b00000100,
|
||||
/// In order to pay some other (higher-level) fees.
|
||||
Fee = 0b00001000,
|
||||
/// In order to tip a validator for transaction inclusion.
|
||||
Tip = 0b00010000,
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Time {
|
||||
type Moment: SimpleArithmetic + FullCodec + Clone + Default + Copy;
|
||||
|
||||
fn now() -> Self::Moment;
|
||||
}
|
||||
|
||||
impl WithdrawReasons {
|
||||
/// Choose all variants except for `one`.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use frame_support::traits::{WithdrawReason, WithdrawReasons};
|
||||
/// # fn main() {
|
||||
/// assert_eq!(
|
||||
/// WithdrawReason::Fee | WithdrawReason::Transfer | WithdrawReason::Reserve | WithdrawReason::Tip,
|
||||
/// WithdrawReasons::except(WithdrawReason::TransactionPayment),
|
||||
/// );
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn except(one: WithdrawReason) -> WithdrawReasons {
|
||||
let mut mask = Self::all();
|
||||
mask.toggle(one);
|
||||
mask
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for type that can handle incremental changes to a set of account IDs.
|
||||
pub trait ChangeMembers<AccountId: Clone + Ord> {
|
||||
/// A number of members `incoming` just joined the set and replaced some `outgoing` ones. The
|
||||
/// new set is given by `new`, and need not be sorted.
|
||||
fn change_members(incoming: &[AccountId], outgoing: &[AccountId], mut new: Vec<AccountId>) {
|
||||
new.sort_unstable();
|
||||
Self::change_members_sorted(incoming, outgoing, &new[..]);
|
||||
}
|
||||
|
||||
/// A number of members `_incoming` just joined the set and replaced some `_outgoing` ones. The
|
||||
/// new set is thus given by `sorted_new` and **must be sorted**.
|
||||
///
|
||||
/// NOTE: This is the only function that needs to be implemented in `ChangeMembers`.
|
||||
fn change_members_sorted(
|
||||
incoming: &[AccountId],
|
||||
outgoing: &[AccountId],
|
||||
sorted_new: &[AccountId],
|
||||
);
|
||||
|
||||
/// Set the new members; they **must already be sorted**. This will compute the diff and use it to
|
||||
/// call `change_members_sorted`.
|
||||
fn set_members_sorted(new_members: &[AccountId], old_members: &[AccountId]) {
|
||||
let (incoming, outgoing) = Self::compute_members_diff(new_members, old_members);
|
||||
Self::change_members_sorted(&incoming[..], &outgoing[..], &new_members);
|
||||
}
|
||||
|
||||
/// Set the new members; they **must already be sorted**. This will compute the diff and use it to
|
||||
/// call `change_members_sorted`.
|
||||
fn compute_members_diff(
|
||||
new_members: &[AccountId],
|
||||
old_members: &[AccountId]
|
||||
) -> (Vec<AccountId>, Vec<AccountId>) {
|
||||
let mut old_iter = old_members.iter();
|
||||
let mut new_iter = new_members.iter();
|
||||
let mut incoming = Vec::new();
|
||||
let mut outgoing = Vec::new();
|
||||
let mut old_i = old_iter.next();
|
||||
let mut new_i = new_iter.next();
|
||||
loop {
|
||||
match (old_i, new_i) {
|
||||
(None, None) => break,
|
||||
(Some(old), Some(new)) if old == new => {
|
||||
old_i = old_iter.next();
|
||||
new_i = new_iter.next();
|
||||
}
|
||||
(Some(old), Some(new)) if old < new => {
|
||||
outgoing.push(old.clone());
|
||||
old_i = old_iter.next();
|
||||
}
|
||||
(Some(old), None) => {
|
||||
outgoing.push(old.clone());
|
||||
old_i = old_iter.next();
|
||||
}
|
||||
(_, Some(new)) => {
|
||||
incoming.push(new.clone());
|
||||
new_i = new_iter.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
(incoming, outgoing)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Ord> ChangeMembers<T> for () {
|
||||
fn change_members(_: &[T], _: &[T], _: Vec<T>) {}
|
||||
fn change_members_sorted(_: &[T], _: &[T], _: &[T]) {}
|
||||
fn set_members_sorted(_: &[T], _: &[T]) {}
|
||||
}
|
||||
|
||||
/// Trait for type that can handle the initialization of account IDs at genesis.
|
||||
pub trait InitializeMembers<AccountId> {
|
||||
/// Initialize the members to the given `members`.
|
||||
fn initialize_members(members: &[AccountId]);
|
||||
}
|
||||
|
||||
impl<T> InitializeMembers<T> for () {
|
||||
fn initialize_members(_: &[T]) {}
|
||||
}
|
||||
|
||||
// A trait that is able to provide randomness.
|
||||
pub trait Randomness<Output> {
|
||||
/// Get a "random" value
|
||||
///
|
||||
/// Being a deterministic blockchain, real randomness is difficult to come by. This gives you
|
||||
/// something that approximates it. `subject` is a context identifier and allows you to get a
|
||||
/// different result to other callers of this function; use it like
|
||||
/// `random(&b"my context"[..])`.
|
||||
fn random(subject: &[u8]) -> Output;
|
||||
|
||||
/// Get the basic random seed.
|
||||
///
|
||||
/// In general you won't want to use this, but rather `Self::random` which allows you to give a
|
||||
/// subject for the random result and whose value will be independently low-influence random
|
||||
/// from any other such seeds.
|
||||
fn random_seed() -> Output {
|
||||
Self::random(&[][..])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#[doc(hidden)]
|
||||
#[allow(deprecated)]
|
||||
pub use crate::sr_primitives::traits::ValidateUnsigned;
|
||||
#[doc(hidden)]
|
||||
pub use crate::sr_primitives::transaction_validity::{
|
||||
TransactionValidity, UnknownTransaction, TransactionValidityError,
|
||||
};
|
||||
|
||||
|
||||
/// Implement `ValidateUnsigned` for `Runtime`.
|
||||
/// All given modules need to implement `ValidateUnsigned`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # mod timestamp {
|
||||
/// # pub struct Module;
|
||||
/// #
|
||||
/// # impl frame_support::unsigned::ValidateUnsigned for Module {
|
||||
/// # type Call = Call;
|
||||
/// #
|
||||
/// # fn validate_unsigned(call: &Self::Call) -> frame_support::unsigned::TransactionValidity {
|
||||
/// # unimplemented!();
|
||||
/// # }
|
||||
/// # }
|
||||
/// #
|
||||
/// # pub enum Call {
|
||||
/// # }
|
||||
/// # }
|
||||
/// #
|
||||
/// # pub type Timestamp = timestamp::Module;
|
||||
/// #
|
||||
/// #
|
||||
/// # pub enum Call {
|
||||
/// # Timestamp(timestamp::Call),
|
||||
/// # }
|
||||
/// # #[allow(unused)]
|
||||
/// pub struct Runtime;
|
||||
///
|
||||
/// frame_support::impl_outer_validate_unsigned! {
|
||||
/// impl ValidateUnsigned for Runtime {
|
||||
/// Timestamp
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! impl_outer_validate_unsigned {
|
||||
(
|
||||
impl ValidateUnsigned for $runtime:ident {
|
||||
$( $module:ident )*
|
||||
}
|
||||
) => {
|
||||
#[allow(deprecated)] // Allow ValidateUnsigned
|
||||
impl $crate::unsigned::ValidateUnsigned for $runtime {
|
||||
type Call = Call;
|
||||
|
||||
fn pre_dispatch(call: &Self::Call) -> Result<(), $crate::unsigned::TransactionValidityError> {
|
||||
#[allow(unreachable_patterns)]
|
||||
match call {
|
||||
$( Call::$module(inner_call) => $module::pre_dispatch(inner_call), )*
|
||||
// pre-dispatch should not stop inherent extrinsics, validation should prevent
|
||||
// including arbitrary (non-inherent) extrinsics to blocks.
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_unsigned(call: &Self::Call) -> $crate::unsigned::TransactionValidity {
|
||||
#[allow(unreachable_patterns)]
|
||||
match call {
|
||||
$( Call::$module(inner_call) => $module::validate_unsigned(inner_call), )*
|
||||
_ => $crate::unsigned::UnknownTransaction::NoUnsignedValidator.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_empty_call {
|
||||
pub enum Call {}
|
||||
|
||||
#[allow(unused)]
|
||||
pub struct Runtime;
|
||||
|
||||
impl_outer_validate_unsigned! {
|
||||
impl ValidateUnsigned for Runtime {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_partial_and_full_call {
|
||||
pub mod timestamp {
|
||||
pub struct Module;
|
||||
|
||||
#[allow(deprecated)] // Allow ValidateUnsigned
|
||||
impl super::super::ValidateUnsigned for Module {
|
||||
type Call = Call;
|
||||
|
||||
fn validate_unsigned(_call: &Self::Call) -> super::super::TransactionValidity {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Call {
|
||||
Foo,
|
||||
}
|
||||
}
|
||||
|
||||
mod test_full_unsigned {
|
||||
pub type Timestamp = super::timestamp::Module;
|
||||
|
||||
pub enum Call {
|
||||
Timestamp(super::timestamp::Call),
|
||||
}
|
||||
|
||||
pub struct Runtime;
|
||||
|
||||
impl_outer_validate_unsigned! {
|
||||
impl ValidateUnsigned for Runtime {
|
||||
Timestamp
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn used() {
|
||||
let _ = Call::Timestamp(super::timestamp::Call::Foo);
|
||||
let _ = Runtime;
|
||||
}
|
||||
}
|
||||
|
||||
mod test_not_full_unsigned {
|
||||
pub enum Call {
|
||||
Timestamp(super::timestamp::Call),
|
||||
}
|
||||
|
||||
pub struct Runtime;
|
||||
|
||||
impl_outer_validate_unsigned! {
|
||||
impl ValidateUnsigned for Runtime {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn used() {
|
||||
let _ = Call::Timestamp(super::timestamp::Call::Foo);
|
||||
let _ = Runtime;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! # Primitives for transaction weighting.
|
||||
//!
|
||||
//! All dispatchable functions defined in `decl_module!` must provide two trait implementations:
|
||||
//! - [`WeightData`]: To determine the weight of the dispatch.
|
||||
//! - [`ClassifyDispatch`]: To determine the class of the dispatch. See the enum definition for
|
||||
//! more information on dispatch classes.
|
||||
//!
|
||||
//! Every dispatchable function is responsible for providing this data via an optional `#[weight =
|
||||
//! $x]` attribute. In this snipped, `$x` can be any user provided struct that implements the
|
||||
//! two aforementioned traits.
|
||||
//!
|
||||
//! Substrate then bundles then output information of the two traits into [`DispatchInfo`] struct
|
||||
//! and provides it by implementing the [`GetDispatchInfo`] for all `Call` variants, and opaque
|
||||
//! extrinsic types.
|
||||
//!
|
||||
//! If no `#[weight]` is defined, the macro automatically injects the `Default` implementation of
|
||||
//! the [`SimpleDispatchInfo`].
|
||||
//!
|
||||
//! Note that the decl_module macro _cannot_ enforce this and will simply fail if an invalid struct
|
||||
//! (something that does not implement `Weighable`) is passed in.
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
use impl_trait_for_tuples::impl_for_tuples;
|
||||
use codec::{Encode, Decode};
|
||||
use sr_arithmetic::traits::{Bounded, Zero};
|
||||
use sr_primitives::{
|
||||
RuntimeDebug,
|
||||
traits::SignedExtension,
|
||||
generic::{CheckedExtrinsic, UncheckedExtrinsic},
|
||||
};
|
||||
|
||||
/// Re-export priority as type
|
||||
pub use sr_primitives::transaction_validity::TransactionPriority;
|
||||
|
||||
/// Numeric range of a transaction weight.
|
||||
pub type Weight = u32;
|
||||
|
||||
/// Means of weighing some particular kind of data (`T`).
|
||||
pub trait WeighData<T> {
|
||||
/// Weigh the data `T` given by `target`. When implementing this for a dispatchable, `T` will be
|
||||
/// a tuple of all arguments given to the function (except origin).
|
||||
fn weigh_data(&self, target: T) -> Weight;
|
||||
}
|
||||
|
||||
/// Means of classifying a dispatchable function.
|
||||
pub trait ClassifyDispatch<T> {
|
||||
/// Classify the dispatch function based on input data `target` of type `T`. When implementing
|
||||
/// this for a dispatchable, `T` will be a tuple of all arguments given to the function (except
|
||||
/// origin).
|
||||
fn classify_dispatch(&self, target: T) -> DispatchClass;
|
||||
}
|
||||
|
||||
/// Means of determining the weight of a block's lifecycle hooks: on_initialize, on_finalize and
|
||||
/// such.
|
||||
pub trait WeighBlock<BlockNumber> {
|
||||
/// Return the weight of the block's on_initialize hook.
|
||||
fn on_initialize(_: BlockNumber) -> Weight { Zero::zero() }
|
||||
/// Return the weight of the block's on_finalize hook.
|
||||
fn on_finalize(_: BlockNumber) -> Weight { Zero::zero() }
|
||||
}
|
||||
|
||||
/// Maybe I can do something to remove the duplicate code here.
|
||||
#[impl_for_tuples(30)]
|
||||
impl<BlockNumber: Copy> WeighBlock<BlockNumber> for SingleModule {
|
||||
fn on_initialize(n: BlockNumber) -> Weight {
|
||||
let mut accumulated_weight: Weight = Zero::zero();
|
||||
for_tuples!(
|
||||
#( accumulated_weight = accumulated_weight.saturating_add(SingleModule::on_initialize(n)); )*
|
||||
);
|
||||
accumulated_weight
|
||||
}
|
||||
|
||||
fn on_finalize(n: BlockNumber) -> Weight {
|
||||
let mut accumulated_weight: Weight = Zero::zero();
|
||||
for_tuples!(
|
||||
#( accumulated_weight = accumulated_weight.saturating_add(SingleModule::on_finalize(n)); )*
|
||||
);
|
||||
accumulated_weight
|
||||
}
|
||||
}
|
||||
|
||||
/// A generalized group of dispatch types. This is only distinguishing normal, user-triggered transactions
|
||||
/// (`Normal`) and anything beyond which serves a higher purpose to the system (`Operational`).
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, RuntimeDebug)]
|
||||
pub enum DispatchClass {
|
||||
/// A normal dispatch.
|
||||
Normal,
|
||||
/// An operational dispatch.
|
||||
Operational,
|
||||
}
|
||||
|
||||
impl Default for DispatchClass {
|
||||
fn default() -> Self {
|
||||
DispatchClass::Normal
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SimpleDispatchInfo> for DispatchClass {
|
||||
fn from(tx: SimpleDispatchInfo) -> Self {
|
||||
match tx {
|
||||
SimpleDispatchInfo::FixedOperational(_) => DispatchClass::Operational,
|
||||
SimpleDispatchInfo::MaxOperational => DispatchClass::Operational,
|
||||
SimpleDispatchInfo::FreeOperational => DispatchClass::Operational,
|
||||
|
||||
SimpleDispatchInfo::FixedNormal(_) => DispatchClass::Normal,
|
||||
SimpleDispatchInfo::MaxNormal => DispatchClass::Normal,
|
||||
SimpleDispatchInfo::FreeNormal => DispatchClass::Normal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A bundle of static information collected from the `#[weight = $x]` attributes.
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Default, RuntimeDebug, Encode, Decode)]
|
||||
pub struct DispatchInfo {
|
||||
/// Weight of this transaction.
|
||||
pub weight: Weight,
|
||||
/// Class of this transaction.
|
||||
pub class: DispatchClass,
|
||||
}
|
||||
|
||||
impl DispatchInfo {
|
||||
/// Determine if this dispatch should pay the base length-related fee or not.
|
||||
pub fn pay_length_fee(&self) -> bool {
|
||||
match self.class {
|
||||
DispatchClass::Normal => true,
|
||||
// For now we assume all operational transactions don't pay the length fee.
|
||||
DispatchClass::Operational => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A `Dispatchable` function (aka transaction) that can carry some static information along with
|
||||
/// it, using the `#[weight]` attribute.
|
||||
pub trait GetDispatchInfo {
|
||||
/// Return a `DispatchInfo`, containing relevant information of this dispatch.
|
||||
///
|
||||
/// This is done independently of its encoded size.
|
||||
fn get_dispatch_info(&self) -> DispatchInfo;
|
||||
}
|
||||
|
||||
/// Default type used with the `#[weight = x]` attribute in a substrate chain.
|
||||
///
|
||||
/// A user may pass in any other type that implements the correct traits. If not, the `Default`
|
||||
/// implementation of [`SimpleDispatchInfo`] is used.
|
||||
///
|
||||
/// For each generalized group (`Normal` and `Operation`):
|
||||
/// - A `Fixed` variant means weight fee is charged normally and the weight is the number
|
||||
/// specified in the inner value of the variant.
|
||||
/// - A `Free` variant is equal to `::Fixed(0)`. Note that this does not guarantee inclusion.
|
||||
/// - A `Max` variant is equal to `::Fixed(Weight::max_value())`.
|
||||
///
|
||||
/// As for the generalized groups themselves:
|
||||
/// - `Normal` variants will be assigned a priority proportional to their weight. They can only
|
||||
/// consume a portion (defined in the system module) of the maximum block resource limits.
|
||||
/// - `Operational` variants will be assigned the maximum priority. They can potentially consume
|
||||
/// the entire block resource limit.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum SimpleDispatchInfo {
|
||||
/// A normal dispatch with fixed weight.
|
||||
FixedNormal(Weight),
|
||||
/// A normal dispatch with the maximum weight.
|
||||
MaxNormal,
|
||||
/// A normal dispatch with no weight.
|
||||
FreeNormal,
|
||||
/// An operational dispatch with fixed weight.
|
||||
FixedOperational(Weight),
|
||||
/// An operational dispatch with the maximum weight.
|
||||
MaxOperational,
|
||||
/// An operational dispatch with no weight.
|
||||
FreeOperational,
|
||||
}
|
||||
|
||||
impl<T> WeighData<T> for SimpleDispatchInfo {
|
||||
fn weigh_data(&self, _: T) -> Weight {
|
||||
match self {
|
||||
SimpleDispatchInfo::FixedNormal(w) => *w,
|
||||
SimpleDispatchInfo::MaxNormal => Bounded::max_value(),
|
||||
SimpleDispatchInfo::FreeNormal => Bounded::min_value(),
|
||||
|
||||
SimpleDispatchInfo::FixedOperational(w) => *w,
|
||||
SimpleDispatchInfo::MaxOperational => Bounded::max_value(),
|
||||
SimpleDispatchInfo::FreeOperational => Bounded::min_value(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ClassifyDispatch<T> for SimpleDispatchInfo {
|
||||
fn classify_dispatch(&self, _: T) -> DispatchClass {
|
||||
DispatchClass::from(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SimpleDispatchInfo {
|
||||
fn default() -> Self {
|
||||
// Default weight of all transactions.
|
||||
SimpleDispatchInfo::FixedNormal(10_000)
|
||||
}
|
||||
}
|
||||
|
||||
impl SimpleDispatchInfo {
|
||||
/// An _additive zero_ variant of SimpleDispatchInfo.
|
||||
pub fn zero() -> Self {
|
||||
Self::FixedNormal(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation for unchecked extrinsic.
|
||||
impl<Address, Call, Signature, Extra> GetDispatchInfo
|
||||
for UncheckedExtrinsic<Address, Call, Signature, Extra>
|
||||
where
|
||||
Call: GetDispatchInfo,
|
||||
Extra: SignedExtension,
|
||||
{
|
||||
fn get_dispatch_info(&self) -> DispatchInfo {
|
||||
self.function.get_dispatch_info()
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation for checked extrinsic.
|
||||
impl<AccountId, Call, Extra> GetDispatchInfo
|
||||
for CheckedExtrinsic<AccountId, Call, Extra>
|
||||
where
|
||||
Call: GetDispatchInfo,
|
||||
{
|
||||
fn get_dispatch_info(&self) -> DispatchInfo {
|
||||
self.function.get_dispatch_info()
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation for test extrinsic.
|
||||
#[cfg(feature = "std")]
|
||||
impl<Call: Encode, Extra: Encode> GetDispatchInfo for sr_primitives::testing::TestXt<Call, Extra> {
|
||||
fn get_dispatch_info(&self) -> DispatchInfo {
|
||||
// for testing: weight == size.
|
||||
DispatchInfo {
|
||||
weight: self.encode().len() as _,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
[package]
|
||||
name = "frame-support-test"
|
||||
version = "2.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0.101", default-features = false, features = ["derive"] }
|
||||
codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] }
|
||||
runtime-io ={ package = "sr-io", path = "../../../primitives/sr-io", default-features = false }
|
||||
state-machine ={ package = "substrate-state-machine", path = "../../../primitives/state-machine", optional = true }
|
||||
support = { package = "frame-support", version = "2", path = "../", default-features = false }
|
||||
inherents = { package = "substrate-inherents", path = "../../../primitives/inherents", default-features = false }
|
||||
sr-primitives = { package = "sr-primitives", path = "../../../primitives/sr-primitives", default-features = false }
|
||||
primitives = { package = "substrate-primitives", path = "../../../primitives/core", default-features = false }
|
||||
trybuild = "1.0.17"
|
||||
pretty_assertions = "0.6.1"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"serde/std",
|
||||
"codec/std",
|
||||
"runtime-io/std",
|
||||
"support/std",
|
||||
"inherents/std",
|
||||
"primitives/std",
|
||||
"sr-primitives/std",
|
||||
"state-machine",
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Test crate for frame_support. Allow to make use of `support::decl_storage`.
|
||||
//! See tests directory.
|
||||
@@ -0,0 +1,641 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#[cfg(test)]
|
||||
// Do not complain about unused `dispatch` and `dispatch_aux`.
|
||||
#[allow(dead_code)]
|
||||
mod tests {
|
||||
use support::metadata::*;
|
||||
use std::marker::PhantomData;
|
||||
use codec::{Encode, Decode, EncodeLike};
|
||||
|
||||
support::decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
pub trait Trait {
|
||||
type Origin: Encode + Decode + EncodeLike + std::default::Default;
|
||||
type BlockNumber;
|
||||
}
|
||||
|
||||
support::decl_storage! {
|
||||
trait Store for Module<T: Trait> as TestStorage {
|
||||
// non-getters: pub / $default
|
||||
|
||||
/// Hello, this is doc!
|
||||
U32 : Option<u32>;
|
||||
pub PUBU32 : Option<u32>;
|
||||
U32MYDEF : Option<u32>;
|
||||
pub PUBU32MYDEF : Option<u32>;
|
||||
|
||||
// getters: pub / $default
|
||||
// we need at least one type which uses T, otherwise GenesisConfig will complain.
|
||||
GETU32 get(fn u32_getter): T::Origin;
|
||||
pub PUBGETU32 get(fn pub_u32_getter) build(|config: &GenesisConfig| config.u32_getter_with_config): u32;
|
||||
GETU32WITHCONFIG get(fn u32_getter_with_config) config(): u32;
|
||||
pub PUBGETU32WITHCONFIG get(fn pub_u32_getter_with_config) config(): u32;
|
||||
GETU32MYDEF get(fn u32_getter_mydef): Option<u32>;
|
||||
pub PUBGETU32MYDEF get(fn pub_u32_getter_mydef) config(): u32 = 3;
|
||||
GETU32WITHCONFIGMYDEF get(fn u32_getter_with_config_mydef) config(): u32 = 2;
|
||||
pub PUBGETU32WITHCONFIGMYDEF get(fn pub_u32_getter_with_config_mydef) config(): u32 = 1;
|
||||
PUBGETU32WITHCONFIGMYDEFOPT get(fn pub_u32_getter_with_config_mydef_opt) config(): Option<u32>;
|
||||
|
||||
// map non-getters: pub / $default
|
||||
MAPU32 : map u32 => Option<String>;
|
||||
pub PUBMAPU32 : map u32 => Option<String>;
|
||||
MAPU32MYDEF : map u32 => Option<String>;
|
||||
pub PUBMAPU32MYDEF : map u32 => Option<String>;
|
||||
|
||||
// map getters: pub / $default
|
||||
GETMAPU32 get(fn map_u32_getter): map u32 => String;
|
||||
pub PUBGETMAPU32 get(fn pub_map_u32_getter): map u32 => String;
|
||||
|
||||
GETMAPU32MYDEF get(fn map_u32_getter_mydef): map u32 => String = "map".into();
|
||||
pub PUBGETMAPU32MYDEF get(fn pub_map_u32_getter_mydef): map u32 => String = "pubmap".into();
|
||||
|
||||
// linked map
|
||||
LINKEDMAPU32 : linked_map u32 => Option<String>;
|
||||
pub PUBLINKEDMAPU32MYDEF : linked_map u32 => Option<String>;
|
||||
GETLINKEDMAPU32 get(fn linked_map_u32_getter): linked_map u32 => String;
|
||||
pub PUBGETLINKEDMAPU32MYDEF get(fn pub_linked_map_u32_getter_mydef): linked_map u32 => String = "pubmap".into();
|
||||
|
||||
COMPLEXTYPE1: ::std::vec::Vec<<T as Trait>::Origin>;
|
||||
COMPLEXTYPE2: (Vec<Vec<(u16,Box<( )>)>>, u32);
|
||||
COMPLEXTYPE3: ([u32;25]);
|
||||
}
|
||||
add_extra_genesis {
|
||||
build(|_| {});
|
||||
}
|
||||
}
|
||||
|
||||
struct TraitImpl {}
|
||||
|
||||
impl Trait for TraitImpl {
|
||||
type Origin = u32;
|
||||
type BlockNumber = u32;
|
||||
}
|
||||
|
||||
const EXPECTED_METADATA: StorageMetadata = StorageMetadata {
|
||||
prefix: DecodeDifferent::Encode("TestStorage"),
|
||||
entries: DecodeDifferent::Encode(
|
||||
&[
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("U32"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")),
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructU32(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[ " Hello, this is doc!" ]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("PUBU32"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")),
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBU32(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("U32MYDEF"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")),
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructU32MYDEF(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("PUBU32MYDEF"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")),
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBU32MYDEF(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("GETU32"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Plain(DecodeDifferent::Encode("T::Origin")),
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGETU32(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("PUBGETU32"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")),
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBGETU32(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("GETU32WITHCONFIG"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")),
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGETU32WITHCONFIG(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("PUBGETU32WITHCONFIG"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")),
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBGETU32WITHCONFIG(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("GETU32MYDEF"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")),
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGETU32MYDEF(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("PUBGETU32MYDEF"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")),
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBGETU32MYDEF(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("GETU32WITHCONFIGMYDEF"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")),
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGETU32WITHCONFIGMYDEF(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("PUBGETU32WITHCONFIGMYDEF"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")),
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBGETU32WITHCONFIGMYDEF(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("PUBGETU32WITHCONFIGMYDEFOPT"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")),
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBGETU32WITHCONFIGMYDEFOPT(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("MAPU32"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructMAPU32(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("PUBMAPU32"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBMAPU32(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("MAPU32MYDEF"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructMAPU32MYDEF(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("PUBMAPU32MYDEF"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBMAPU32MYDEF(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("GETMAPU32"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGETMAPU32(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("PUBGETMAPU32"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBGETMAPU32(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("GETMAPU32MYDEF"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGETMAPU32MYDEF(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("PUBGETMAPU32MYDEF"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBGETMAPU32MYDEF(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("LINKEDMAPU32"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: true,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructLINKEDMAPU32(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("PUBLINKEDMAPU32MYDEF"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: true,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBLINKEDMAPU32MYDEF(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("GETLINKEDMAPU32"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: true,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGETLINKEDMAPU32(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("PUBGETLINKEDMAPU32MYDEF"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: true,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBGETLINKEDMAPU32MYDEF(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("COMPLEXTYPE1"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Plain(DecodeDifferent::Encode("::std::vec::Vec<<T as Trait>::Origin>")),
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructCOMPLEXTYPE1(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("COMPLEXTYPE2"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Plain(DecodeDifferent::Encode("(Vec<Vec<(u16, Box<()>)>>, u32)")),
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructCOMPLEXTYPE2(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("COMPLEXTYPE3"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Plain(DecodeDifferent::Encode("([u32; 25])")),
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructCOMPLEXTYPE3(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
]
|
||||
),
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn store_metadata() {
|
||||
let metadata = Module::<TraitImpl>::storage_metadata();
|
||||
assert_eq!(EXPECTED_METADATA, metadata);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_genesis_config() {
|
||||
let config = GenesisConfig::default();
|
||||
assert_eq!(config.u32_getter_with_config, 0u32);
|
||||
assert_eq!(config.pub_u32_getter_with_config, 0u32);
|
||||
|
||||
assert_eq!(config.pub_u32_getter_mydef, 3u32);
|
||||
assert_eq!(config.u32_getter_with_config_mydef, 2u32);
|
||||
assert_eq!(config.pub_u32_getter_with_config_mydef, 1u32);
|
||||
assert_eq!(config.pub_u32_getter_with_config_mydef_opt, 0u32);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(dead_code)]
|
||||
mod test2 {
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
type BlockNumber;
|
||||
}
|
||||
|
||||
support::decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
type PairOf<T> = (T, T);
|
||||
|
||||
support::decl_storage! {
|
||||
trait Store for Module<T: Trait> as TestStorage {
|
||||
SingleDef : u32;
|
||||
PairDef : PairOf<u32>;
|
||||
Single : Option<u32>;
|
||||
Pair : (u32, u32);
|
||||
}
|
||||
add_extra_genesis {
|
||||
config(_marker) : ::std::marker::PhantomData<T>;
|
||||
config(extra_field) : u32 = 32;
|
||||
build(|_| {});
|
||||
}
|
||||
}
|
||||
|
||||
struct TraitImpl {}
|
||||
|
||||
impl Trait for TraitImpl {
|
||||
type Origin = u32;
|
||||
type BlockNumber = u32;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(dead_code)]
|
||||
mod test3 {
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
type BlockNumber;
|
||||
}
|
||||
support::decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
support::decl_storage! {
|
||||
trait Store for Module<T: Trait> as Test {
|
||||
Foo get(fn foo) config(initial_foo): u32;
|
||||
}
|
||||
}
|
||||
|
||||
type PairOf<T> = (T, T);
|
||||
|
||||
struct TraitImpl {}
|
||||
|
||||
impl Trait for TraitImpl {
|
||||
type Origin = u32;
|
||||
type BlockNumber = u32;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(dead_code)]
|
||||
mod test_append_and_len {
|
||||
use runtime_io::TestExternalities;
|
||||
use codec::{Encode, Decode};
|
||||
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
type BlockNumber;
|
||||
}
|
||||
|
||||
support::decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
|
||||
struct NoDef(u32);
|
||||
|
||||
support::decl_storage! {
|
||||
trait Store for Module<T: Trait> as Test {
|
||||
NoDefault: Option<NoDef>;
|
||||
|
||||
JustVec: Vec<u32>;
|
||||
JustVecWithDefault: Vec<u32> = vec![6, 9];
|
||||
OptionVec: Option<Vec<u32>>;
|
||||
|
||||
MapVec: map u32 => Vec<u32>;
|
||||
MapVecWithDefault: map u32 => Vec<u32> = vec![6, 9];
|
||||
OptionMapVec: map u32 => Option<Vec<u32>>;
|
||||
|
||||
LinkedMapVec: linked_map u32 => Vec<u32>;
|
||||
LinkedMapVecWithDefault: linked_map u32 => Vec<u32> = vec![6, 9];
|
||||
OptionLinkedMapVec: linked_map u32 => Option<Vec<u32>>;
|
||||
}
|
||||
}
|
||||
|
||||
struct Test {}
|
||||
|
||||
impl Trait for Test {
|
||||
type Origin = u32;
|
||||
type BlockNumber = u32;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_for_option() {
|
||||
TestExternalities::default().execute_with(|| {
|
||||
assert_eq!(OptionVec::get(), None);
|
||||
assert_eq!(JustVec::get(), vec![]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn append_works() {
|
||||
TestExternalities::default().execute_with(|| {
|
||||
let _ = MapVec::append(1, [1, 2, 3].iter());
|
||||
let _ = MapVec::append(1, [4, 5].iter());
|
||||
assert_eq!(MapVec::get(1), vec![1, 2, 3, 4, 5]);
|
||||
|
||||
let _ = JustVec::append([1, 2, 3].iter());
|
||||
let _ = JustVec::append([4, 5].iter());
|
||||
assert_eq!(JustVec::get(), vec![1, 2, 3, 4, 5]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn append_works_for_default() {
|
||||
TestExternalities::default().execute_with(|| {
|
||||
assert_eq!(JustVecWithDefault::get(), vec![6, 9]);
|
||||
let _ = JustVecWithDefault::append([1].iter());
|
||||
assert_eq!(JustVecWithDefault::get(), vec![6, 9, 1]);
|
||||
|
||||
assert_eq!(MapVecWithDefault::get(0), vec![6, 9]);
|
||||
let _ = MapVecWithDefault::append(0, [1].iter());
|
||||
assert_eq!(MapVecWithDefault::get(0), vec![6, 9, 1]);
|
||||
|
||||
assert_eq!(OptionVec::get(), None);
|
||||
let _ = OptionVec::append([1].iter());
|
||||
assert_eq!(OptionVec::get(), Some(vec![1]));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn append_or_put_works() {
|
||||
TestExternalities::default().execute_with(|| {
|
||||
let _ = MapVec::append_or_insert(1, &[1, 2, 3][..]);
|
||||
let _ = MapVec::append_or_insert(1, &[4, 5][..]);
|
||||
assert_eq!(MapVec::get(1), vec![1, 2, 3, 4, 5]);
|
||||
|
||||
let _ = JustVec::append_or_put(&[1, 2, 3][..]);
|
||||
let _ = JustVec::append_or_put(&[4, 5][..]);
|
||||
assert_eq!(JustVec::get(), vec![1, 2, 3, 4, 5]);
|
||||
|
||||
let _ = OptionVec::append_or_put(&[1, 2, 3][..]);
|
||||
let _ = OptionVec::append_or_put(&[4, 5][..]);
|
||||
assert_eq!(OptionVec::get(), Some(vec![1, 2, 3, 4, 5]));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn len_works() {
|
||||
TestExternalities::default().execute_with(|| {
|
||||
JustVec::put(&vec![1, 2, 3, 4]);
|
||||
OptionVec::put(&vec![1, 2, 3, 4, 5]);
|
||||
MapVec::insert(1, &vec![1, 2, 3, 4, 5, 6]);
|
||||
LinkedMapVec::insert(2, &vec![1, 2, 3]);
|
||||
|
||||
assert_eq!(JustVec::decode_len().unwrap(), 4);
|
||||
assert_eq!(OptionVec::decode_len().unwrap(), 5);
|
||||
assert_eq!(MapVec::decode_len(1).unwrap(), 6);
|
||||
assert_eq!(LinkedMapVec::decode_len(2).unwrap(), 3);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn len_works_for_default() {
|
||||
TestExternalities::default().execute_with(|| {
|
||||
// vec
|
||||
assert_eq!(JustVec::get(), vec![]);
|
||||
assert_eq!(JustVec::decode_len(), Ok(0));
|
||||
|
||||
assert_eq!(JustVecWithDefault::get(), vec![6, 9]);
|
||||
assert_eq!(JustVecWithDefault::decode_len(), Ok(2));
|
||||
|
||||
assert_eq!(OptionVec::get(), None);
|
||||
assert_eq!(OptionVec::decode_len(), Ok(0));
|
||||
|
||||
// map
|
||||
assert_eq!(MapVec::get(0), vec![]);
|
||||
assert_eq!(MapVec::decode_len(0), Ok(0));
|
||||
|
||||
assert_eq!(MapVecWithDefault::get(0), vec![6, 9]);
|
||||
assert_eq!(MapVecWithDefault::decode_len(0), Ok(2));
|
||||
|
||||
assert_eq!(OptionMapVec::get(0), None);
|
||||
assert_eq!(OptionMapVec::decode_len(0), Ok(0));
|
||||
|
||||
// linked map
|
||||
assert_eq!(LinkedMapVec::get(0), vec![]);
|
||||
assert_eq!(LinkedMapVec::decode_len(0), Ok(0));
|
||||
|
||||
assert_eq!(LinkedMapVecWithDefault::get(0), vec![6, 9]);
|
||||
assert_eq!(LinkedMapVecWithDefault::decode_len(0), Ok(2));
|
||||
|
||||
assert_eq!(OptionLinkedMapVec::get(0), None);
|
||||
assert_eq!(OptionLinkedMapVec::decode_len(0), Ok(0));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#[test]
|
||||
fn decl_storage_ui() {
|
||||
// As trybuild is using `cargo check`, we don't need the real WASM binaries.
|
||||
std::env::set_var("BUILD_DUMMY_WASM_BINARY", "1");
|
||||
|
||||
let t = trybuild::TestCases::new();
|
||||
t.compile_fail("tests/decl_storage_ui/*.rs");
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
type BlockNumber: codec::Codec + codec::EncodeLike + Default + Clone;
|
||||
}
|
||||
|
||||
support::decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
support::decl_storage!{
|
||||
trait Store for Module<T: Trait> as FinalKeysNone {
|
||||
pub Value config(value): u32;
|
||||
pub Value2 config(value): u32;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: `config()`/`get()` with the same name already defined.
|
||||
--> $DIR/config_duplicate.rs:29:21
|
||||
|
|
||||
29 | pub Value2 config(value): u32;
|
||||
| ^^^^^
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
type BlockNumber: codec::Codec + codec::EncodeLike + Default + Clone;
|
||||
}
|
||||
|
||||
support::decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
support::decl_storage!{
|
||||
trait Store for Module<T: Trait> as FinalKeysNone {
|
||||
pub Value get(fn value) config(): u32;
|
||||
pub Value2 config(value): u32;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: `config()`/`get()` with the same name already defined.
|
||||
--> $DIR/config_get_duplicate.rs:29:21
|
||||
|
|
||||
29 | pub Value2 config(value): u32;
|
||||
| ^^^^^
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
type BlockNumber: codec::Codec + codec::EncodeLike + Default + Clone;
|
||||
}
|
||||
|
||||
support::decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
support::decl_storage!{
|
||||
trait Store for Module<T: Trait> as FinalKeysNone {
|
||||
pub Value get(fn value) config(): u32;
|
||||
pub Value2 get(fn value) config(): u32;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: `config()`/`get()` with the same name already defined.
|
||||
--> $DIR/get_duplicate.rs:29:21
|
||||
|
|
||||
29 | pub Value2 get(fn value) config(): u32;
|
||||
| ^^^^^
|
||||
@@ -0,0 +1,227 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use support::storage::unhashed;
|
||||
use codec::Encode;
|
||||
use support::{StorageDoubleMap, StorageLinkedMap, StorageMap, StorageValue};
|
||||
use runtime_io::{TestExternalities, hashing};
|
||||
|
||||
mod no_instance {
|
||||
use codec::{Encode, Decode, EncodeLike};
|
||||
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
type BlockNumber: Encode + Decode + EncodeLike + Default + Clone;
|
||||
}
|
||||
|
||||
support::decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
support::decl_storage!{
|
||||
trait Store for Module<T: Trait> as FinalKeysNone {
|
||||
pub Value config(value): u32;
|
||||
|
||||
pub Map: map u32 => u32;
|
||||
pub Map2: map hasher(twox_128) u32 => u32;
|
||||
|
||||
pub LinkedMap: linked_map u32 => u32;
|
||||
pub LinkedMap2: linked_map hasher(twox_128) u32 => u32;
|
||||
|
||||
pub DoubleMap: double_map u32, blake2_256(u32) => u32;
|
||||
pub DoubleMap2: double_map hasher(twox_128) u32, blake2_128(u32) => u32;
|
||||
|
||||
pub TestGenericValue get(fn test_generic_value) config(): Option<T::BlockNumber>;
|
||||
pub TestGenericDoubleMap get(fn foo2) config(test_generic_double_map):
|
||||
double_map u32, blake2_256(T::BlockNumber) => Option<u32>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod instance {
|
||||
pub trait Trait<I = DefaultInstance>: super::no_instance::Trait {}
|
||||
|
||||
support::decl_module! {
|
||||
pub struct Module<T: Trait<I>, I: Instantiable = DefaultInstance>
|
||||
for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
support::decl_storage!{
|
||||
trait Store for Module<T: Trait<I>, I: Instantiable = DefaultInstance>
|
||||
as FinalKeysSome
|
||||
{
|
||||
pub Value config(value): u32;
|
||||
|
||||
pub Map: map u32 => u32;
|
||||
pub Map2: map hasher(twox_128) u32 => u32;
|
||||
|
||||
pub LinkedMap: linked_map u32 => u32;
|
||||
pub LinkedMap2: linked_map hasher(twox_128) u32 => u32;
|
||||
|
||||
pub DoubleMap: double_map u32, blake2_256(u32) => u32;
|
||||
pub DoubleMap2: double_map hasher(twox_128) u32, blake2_128(u32) => u32;
|
||||
|
||||
pub TestGenericValue get(fn test_generic_value) config(): Option<T::BlockNumber>;
|
||||
pub TestGenericDoubleMap get(fn foo2) config(test_generic_double_map):
|
||||
double_map u32, blake2_256(T::BlockNumber) => Option<u32>;
|
||||
}
|
||||
add_extra_genesis {
|
||||
// See `decl_storage` limitation.
|
||||
config(phantom): core::marker::PhantomData<I>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn final_keys_no_instance() {
|
||||
TestExternalities::default().execute_with(|| {
|
||||
no_instance::Value::put(1);
|
||||
assert_eq!(unhashed::get::<u32>(&hashing::twox_128(b"FinalKeysNone Value")), Some(1u32));
|
||||
|
||||
no_instance::Map::insert(1, 2);
|
||||
let mut k = b"FinalKeysNone Map".to_vec();
|
||||
k.extend(1u32.encode());
|
||||
assert_eq!(unhashed::get::<u32>(&hashing::blake2_256(&k)), Some(2u32));
|
||||
|
||||
no_instance::Map2::insert(1, 2);
|
||||
let mut k = b"FinalKeysNone Map2".to_vec();
|
||||
k.extend(1u32.encode());
|
||||
assert_eq!(unhashed::get::<u32>(&hashing::twox_128(&k)), Some(2u32));
|
||||
|
||||
let head = b"head of FinalKeysNone LinkedMap".to_vec();
|
||||
assert_eq!(unhashed::get::<u32>(&hashing::blake2_256(&head)), None);
|
||||
|
||||
no_instance::LinkedMap::insert(1, 2);
|
||||
let mut k = b"FinalKeysNone LinkedMap".to_vec();
|
||||
k.extend(1u32.encode());
|
||||
assert_eq!(unhashed::get::<u32>(&hashing::blake2_256(&k)), Some(2u32));
|
||||
assert_eq!(unhashed::get::<u32>(&hashing::blake2_256(&head)), Some(1u32));
|
||||
|
||||
no_instance::LinkedMap2::insert(1, 2);
|
||||
let mut k = b"FinalKeysNone LinkedMap2".to_vec();
|
||||
k.extend(1u32.encode());
|
||||
assert_eq!(unhashed::get::<u32>(&hashing::twox_128(&k)), Some(2u32));
|
||||
|
||||
no_instance::DoubleMap::insert(&1, &2, &3);
|
||||
let mut k = b"FinalKeysNone DoubleMap".to_vec();
|
||||
k.extend(1u32.encode());
|
||||
let mut k = hashing::blake2_256(&k).to_vec();
|
||||
k.extend(&hashing::blake2_256(&2u32.encode()));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(3u32));
|
||||
|
||||
no_instance::DoubleMap2::insert(&1, &2, &3);
|
||||
let mut k = b"FinalKeysNone DoubleMap2".to_vec();
|
||||
k.extend(1u32.encode());
|
||||
let mut k = hashing::twox_128(&k).to_vec();
|
||||
k.extend(&hashing::blake2_128(&2u32.encode()));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(3u32));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn final_keys_default_instance() {
|
||||
TestExternalities::default().execute_with(|| {
|
||||
<instance::Value<instance::DefaultInstance>>::put(1);
|
||||
assert_eq!(unhashed::get::<u32>(&hashing::twox_128(b"FinalKeysSome Value")), Some(1u32));
|
||||
|
||||
<instance::Map<instance::DefaultInstance>>::insert(1, 2);
|
||||
let mut k = b"FinalKeysSome Map".to_vec();
|
||||
k.extend(1u32.encode());
|
||||
assert_eq!(unhashed::get::<u32>(&hashing::blake2_256(&k)), Some(2u32));
|
||||
|
||||
<instance::Map2<instance::DefaultInstance>>::insert(1, 2);
|
||||
let mut k = b"FinalKeysSome Map2".to_vec();
|
||||
k.extend(1u32.encode());
|
||||
assert_eq!(unhashed::get::<u32>(&hashing::twox_128(&k)), Some(2u32));
|
||||
|
||||
let head = b"head of FinalKeysSome LinkedMap".to_vec();
|
||||
assert_eq!(unhashed::get::<u32>(&hashing::blake2_256(&head)), None);
|
||||
|
||||
<instance::LinkedMap<instance::DefaultInstance>>::insert(1, 2);
|
||||
let mut k = b"FinalKeysSome LinkedMap".to_vec();
|
||||
k.extend(1u32.encode());
|
||||
assert_eq!(unhashed::get::<u32>(&hashing::blake2_256(&k)), Some(2u32));
|
||||
assert_eq!(unhashed::get::<u32>(&hashing::blake2_256(&head)), Some(1u32));
|
||||
|
||||
<instance::LinkedMap2<instance::DefaultInstance>>::insert(1, 2);
|
||||
let mut k = b"FinalKeysSome LinkedMap2".to_vec();
|
||||
k.extend(1u32.encode());
|
||||
assert_eq!(unhashed::get::<u32>(&hashing::twox_128(&k)), Some(2u32));
|
||||
|
||||
<instance::DoubleMap<instance::DefaultInstance>>::insert(&1, &2, &3);
|
||||
let mut k = b"FinalKeysSome DoubleMap".to_vec();
|
||||
k.extend(1u32.encode());
|
||||
let mut k = hashing::blake2_256(&k).to_vec();
|
||||
k.extend(&hashing::blake2_256(&2u32.encode()));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(3u32));
|
||||
|
||||
<instance::DoubleMap2<instance::DefaultInstance>>::insert(&1, &2, &3);
|
||||
let mut k = b"FinalKeysSome DoubleMap2".to_vec();
|
||||
k.extend(1u32.encode());
|
||||
let mut k = hashing::twox_128(&k).to_vec();
|
||||
k.extend(&hashing::blake2_128(&2u32.encode()));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(3u32));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn final_keys_instance_2() {
|
||||
TestExternalities::default().execute_with(|| {
|
||||
<instance::Value<instance::Instance2>>::put(1);
|
||||
assert_eq!(
|
||||
unhashed::get::<u32>(&hashing::twox_128(b"Instance2FinalKeysSome Value")),
|
||||
Some(1u32)
|
||||
);
|
||||
|
||||
<instance::Map<instance::Instance2>>::insert(1, 2);
|
||||
let mut k = b"Instance2FinalKeysSome Map".to_vec();
|
||||
k.extend(1u32.encode());
|
||||
assert_eq!(unhashed::get::<u32>(&hashing::blake2_256(&k)), Some(2u32));
|
||||
|
||||
<instance::Map2<instance::Instance2>>::insert(1, 2);
|
||||
let mut k = b"Instance2FinalKeysSome Map2".to_vec();
|
||||
k.extend(1u32.encode());
|
||||
assert_eq!(unhashed::get::<u32>(&hashing::twox_128(&k)), Some(2u32));
|
||||
|
||||
let head = b"head of Instance2FinalKeysSome LinkedMap".to_vec();
|
||||
assert_eq!(unhashed::get::<u32>(&hashing::blake2_256(&head)), None);
|
||||
|
||||
<instance::LinkedMap<instance::Instance2>>::insert(1, 2);
|
||||
let mut k = b"Instance2FinalKeysSome LinkedMap".to_vec();
|
||||
k.extend(1u32.encode());
|
||||
assert_eq!(unhashed::get::<u32>(&hashing::blake2_256(&k)), Some(2u32));
|
||||
assert_eq!(unhashed::get::<u32>(&hashing::blake2_256(&head)), Some(1u32));
|
||||
|
||||
<instance::LinkedMap2<instance::Instance2>>::insert(1, 2);
|
||||
let mut k = b"Instance2FinalKeysSome LinkedMap2".to_vec();
|
||||
k.extend(1u32.encode());
|
||||
assert_eq!(unhashed::get::<u32>(&hashing::twox_128(&k)), Some(2u32));
|
||||
|
||||
<instance::DoubleMap<instance::Instance2>>::insert(&1, &2, &3);
|
||||
let mut k = b"Instance2FinalKeysSome DoubleMap".to_vec();
|
||||
k.extend(1u32.encode());
|
||||
let mut k = hashing::blake2_256(&k).to_vec();
|
||||
k.extend(&hashing::blake2_256(&2u32.encode()));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(3u32));
|
||||
|
||||
<instance::DoubleMap2<instance::Instance2>>::insert(&1, &2, &3);
|
||||
let mut k = b"Instance2FinalKeysSome DoubleMap2".to_vec();
|
||||
k.extend(1u32.encode());
|
||||
let mut k = hashing::twox_128(&k).to_vec();
|
||||
k.extend(&hashing::blake2_128(&2u32.encode()));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(3u32));
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
pub trait Trait {
|
||||
type BlockNumber: codec::Codec + codec::EncodeLike + Default;
|
||||
type Origin;
|
||||
}
|
||||
|
||||
support::decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
support::decl_storage! {
|
||||
trait Store for Module<T: Trait> as Example {
|
||||
pub AppendableDM config(t): double_map u32, blake2_256(T::BlockNumber) => Vec<u32>;
|
||||
}
|
||||
}
|
||||
|
||||
struct Test;
|
||||
|
||||
impl Trait for Test {
|
||||
type BlockNumber = u32;
|
||||
type Origin = ();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn init_genesis_config() {
|
||||
GenesisConfig::<Test> {
|
||||
t: Default::default(),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,485 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#![recursion_limit="128"]
|
||||
|
||||
use sr_primitives::{generic, BuildStorage, traits::{BlakeTwo256, Block as _, Verify}};
|
||||
use support::{
|
||||
Parameter, traits::Get, parameter_types,
|
||||
metadata::{
|
||||
DecodeDifferent, StorageMetadata, StorageEntryModifier, StorageEntryType, DefaultByteGetter,
|
||||
StorageEntryMetadata, StorageHasher,
|
||||
},
|
||||
StorageValue, StorageMap, StorageLinkedMap, StorageDoubleMap,
|
||||
};
|
||||
use inherents::{ProvideInherent, InherentData, InherentIdentifier, MakeFatalError};
|
||||
use primitives::{H256, sr25519};
|
||||
|
||||
mod system;
|
||||
|
||||
pub trait Currency {}
|
||||
|
||||
// Test for:
|
||||
// * No default instance
|
||||
// * Custom InstantiableTrait
|
||||
// * Origin, Inherent, Event
|
||||
mod module1 {
|
||||
use super::*;
|
||||
|
||||
pub trait Trait<I>: system::Trait where <Self as system::Trait>::BlockNumber: From<u32> {
|
||||
type Event: From<Event<Self, I>> + Into<<Self as system::Trait>::Event>;
|
||||
type Origin: From<Origin<Self, I>>;
|
||||
type SomeParameter: Get<u32>;
|
||||
type GenericType: Default + Clone + codec::Codec + codec::EncodeLike;
|
||||
}
|
||||
|
||||
support::decl_module! {
|
||||
pub struct Module<T: Trait<I>, I: InstantiableThing> for enum Call where
|
||||
origin: <T as system::Trait>::Origin,
|
||||
T::BlockNumber: From<u32>
|
||||
{
|
||||
fn offchain_worker() {}
|
||||
|
||||
fn deposit_event() = default;
|
||||
|
||||
fn one(origin) {
|
||||
system::ensure_root(origin)?;
|
||||
Self::deposit_event(RawEvent::AnotherVariant(3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
support::decl_storage! {
|
||||
trait Store for Module<T: Trait<I>, I: InstantiableThing> as Module1 where
|
||||
T::BlockNumber: From<u32> + std::fmt::Display
|
||||
{
|
||||
pub Value config(value): T::GenericType;
|
||||
pub Map: map u32 => u64;
|
||||
pub LinkedMap: linked_map u32 => u64;
|
||||
}
|
||||
|
||||
add_extra_genesis {
|
||||
config(test) : T::BlockNumber;
|
||||
build(|config: &Self| {
|
||||
println!("{}", config.test);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
support::decl_event! {
|
||||
pub enum Event<T, I> where Phantom = std::marker::PhantomData<T> {
|
||||
_Phantom(Phantom),
|
||||
AnotherVariant(u32),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, sr_primitives::RuntimeDebug)]
|
||||
pub enum Origin<T: Trait<I>, I> where T::BlockNumber: From<u32> {
|
||||
Members(u32),
|
||||
_Phantom(std::marker::PhantomData<(T, I)>),
|
||||
}
|
||||
|
||||
pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"12345678";
|
||||
|
||||
impl<T: Trait<I>, I: InstantiableThing> ProvideInherent for Module<T, I> where
|
||||
T::BlockNumber: From<u32>
|
||||
{
|
||||
type Call = Call<T, I>;
|
||||
type Error = MakeFatalError<inherents::Error>;
|
||||
const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
|
||||
|
||||
fn create_inherent(_data: &InherentData) -> Option<Self::Call> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn check_inherent(_: &Self::Call, _: &InherentData) -> std::result::Result<(), Self::Error> {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test for:
|
||||
// * default instance
|
||||
// * use of no_genesis_config_phantom_data
|
||||
mod module2 {
|
||||
use super::*;
|
||||
|
||||
pub trait Trait<I=DefaultInstance>: system::Trait {
|
||||
type Amount: Parameter + Default;
|
||||
type Event: From<Event<Self, I>> + Into<<Self as system::Trait>::Event>;
|
||||
type Origin: From<Origin<Self, I>>;
|
||||
}
|
||||
|
||||
impl<T: Trait<I>, I: Instance> Currency for Module<T, I> {}
|
||||
|
||||
support::decl_module! {
|
||||
pub struct Module<T: Trait<I>, I: Instance=DefaultInstance> for enum Call where
|
||||
origin: <T as system::Trait>::Origin
|
||||
{
|
||||
fn deposit_event() = default;
|
||||
}
|
||||
}
|
||||
|
||||
support::decl_storage! {
|
||||
trait Store for Module<T: Trait<I>, I: Instance=DefaultInstance> as Module2 {
|
||||
pub Value config(value): T::Amount;
|
||||
pub Map config(map): map u64 => u64;
|
||||
pub LinkedMap config(linked_map): linked_map u64 => Vec<u8>;
|
||||
pub DoubleMap config(double_map): double_map u64, blake2_256(u64) => u64;
|
||||
}
|
||||
}
|
||||
|
||||
support::decl_event! {
|
||||
pub enum Event<T, I=DefaultInstance> where Amount = <T as Trait<I>>::Amount {
|
||||
Variant(Amount),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, sr_primitives::RuntimeDebug)]
|
||||
pub enum Origin<T: Trait<I>, I=DefaultInstance> {
|
||||
Members(u32),
|
||||
_Phantom(std::marker::PhantomData<(T, I)>),
|
||||
}
|
||||
|
||||
pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"12345678";
|
||||
|
||||
impl<T: Trait<I>, I: Instance> ProvideInherent for Module<T, I> {
|
||||
type Call = Call<T, I>;
|
||||
type Error = MakeFatalError<inherents::Error>;
|
||||
const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
|
||||
|
||||
fn create_inherent(_data: &InherentData) -> Option<Self::Call> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn check_inherent(_call: &Self::Call, _data: &InherentData) -> std::result::Result<(), Self::Error> {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test for:
|
||||
// * Depends on multiple instances of a module with instances
|
||||
mod module3 {
|
||||
use super::*;
|
||||
|
||||
pub trait Trait: module2::Trait + module2::Trait<module2::Instance1> + system::Trait {
|
||||
type Currency: Currency;
|
||||
type Currency2: Currency;
|
||||
}
|
||||
|
||||
support::decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: <T as system::Trait>::Origin {}
|
||||
}
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const SomeValue: u32 = 100;
|
||||
}
|
||||
|
||||
impl module1::Trait<module1::Instance1> for Runtime {
|
||||
type Event = Event;
|
||||
type Origin = Origin;
|
||||
type SomeParameter = SomeValue;
|
||||
type GenericType = u32;
|
||||
}
|
||||
impl module1::Trait<module1::Instance2> for Runtime {
|
||||
type Event = Event;
|
||||
type Origin = Origin;
|
||||
type SomeParameter = SomeValue;
|
||||
type GenericType = u32;
|
||||
}
|
||||
impl module2::Trait for Runtime {
|
||||
type Amount = u16;
|
||||
type Event = Event;
|
||||
type Origin = Origin;
|
||||
}
|
||||
impl module2::Trait<module2::Instance1> for Runtime {
|
||||
type Amount = u32;
|
||||
type Event = Event;
|
||||
type Origin = Origin;
|
||||
}
|
||||
impl module2::Trait<module2::Instance2> for Runtime {
|
||||
type Amount = u32;
|
||||
type Event = Event;
|
||||
type Origin = Origin;
|
||||
}
|
||||
impl module2::Trait<module2::Instance3> for Runtime {
|
||||
type Amount = u64;
|
||||
type Event = Event;
|
||||
type Origin = Origin;
|
||||
}
|
||||
impl module3::Trait for Runtime {
|
||||
type Currency = Module2_2;
|
||||
type Currency2 = Module2_3;
|
||||
}
|
||||
|
||||
pub type Signature = sr25519::Signature;
|
||||
pub type AccountId = <Signature as Verify>::Signer;
|
||||
pub type BlockNumber = u64;
|
||||
pub type Index = u64;
|
||||
|
||||
impl system::Trait for Runtime {
|
||||
type Hash = H256;
|
||||
type Origin = Origin;
|
||||
type BlockNumber = BlockNumber;
|
||||
type AccountId = AccountId;
|
||||
type Event = Event;
|
||||
}
|
||||
|
||||
support::construct_runtime!(
|
||||
pub enum Runtime where
|
||||
Block = Block,
|
||||
NodeBlock = Block,
|
||||
UncheckedExtrinsic = UncheckedExtrinsic
|
||||
{
|
||||
System: system::{Module, Call, Event},
|
||||
Module1_1: module1::<Instance1>::{
|
||||
Module, Call, Storage, Event<T>, Config<T>, Origin<T>, Inherent
|
||||
},
|
||||
Module1_2: module1::<Instance2>::{
|
||||
Module, Call, Storage, Event<T>, Config<T>, Origin<T>, Inherent
|
||||
},
|
||||
Module2: module2::{Module, Call, Storage, Event<T>, Config<T>, Origin<T>, Inherent},
|
||||
Module2_1: module2::<Instance1>::{
|
||||
Module, Call, Storage, Event<T>, Config<T>, Origin<T>, Inherent
|
||||
},
|
||||
Module2_2: module2::<Instance2>::{
|
||||
Module, Call, Storage, Event<T>, Config<T>, Origin<T>, Inherent
|
||||
},
|
||||
Module2_3: module2::<Instance3>::{
|
||||
Module, Call, Storage, Event<T>, Config<T>, Origin<T>, Inherent
|
||||
},
|
||||
Module3: module3::{Module, Call},
|
||||
}
|
||||
);
|
||||
|
||||
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
||||
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
||||
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<u32, Call, Signature, ()>;
|
||||
|
||||
fn new_test_ext() -> runtime_io::TestExternalities {
|
||||
GenesisConfig{
|
||||
module1_Instance1: Some(module1::GenesisConfig {
|
||||
value: 3,
|
||||
test: 2,
|
||||
}),
|
||||
module1_Instance2: Some(module1::GenesisConfig {
|
||||
value: 4,
|
||||
test: 5,
|
||||
}),
|
||||
module2: Some(module2::GenesisConfig {
|
||||
value: 4,
|
||||
map: vec![(0, 0)],
|
||||
linked_map: vec![(0, vec![0])],
|
||||
double_map: vec![(0, 0, 0)],
|
||||
}),
|
||||
module2_Instance1: Some(module2::GenesisConfig {
|
||||
value: 4,
|
||||
map: vec![(0, 0)],
|
||||
linked_map: vec![(0, vec![0])],
|
||||
double_map: vec![(0, 0, 0)],
|
||||
}),
|
||||
module2_Instance2: None,
|
||||
module2_Instance3: None,
|
||||
}.build_storage().unwrap().into()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn storage_instance_independance() {
|
||||
let mut storage = (std::collections::HashMap::new(), std::collections::HashMap::new());
|
||||
state_machine::BasicExternalities::execute_with_storage(&mut storage, || {
|
||||
module2::Value::<Runtime>::put(0);
|
||||
module2::Value::<Runtime, module2::Instance1>::put(0);
|
||||
module2::Value::<Runtime, module2::Instance2>::put(0);
|
||||
module2::Value::<Runtime, module2::Instance3>::put(0);
|
||||
module2::Map::<module2::DefaultInstance>::insert(0, 0);
|
||||
module2::Map::<module2::Instance1>::insert(0, 0);
|
||||
module2::Map::<module2::Instance2>::insert(0, 0);
|
||||
module2::Map::<module2::Instance3>::insert(0, 0);
|
||||
module2::LinkedMap::<module2::DefaultInstance>::insert::<_, Vec<u8>>(0, vec![]);
|
||||
module2::LinkedMap::<module2::Instance1>::insert::<_, Vec<u8>>(0, vec![]);
|
||||
module2::LinkedMap::<module2::Instance2>::insert::<_, Vec<u8>>(0, vec![]);
|
||||
module2::LinkedMap::<module2::Instance3>::insert::<_, Vec<u8>>(0, vec![]);
|
||||
module2::DoubleMap::<module2::DefaultInstance>::insert(&0, &0, &0);
|
||||
module2::DoubleMap::<module2::Instance1>::insert(&0, &0, &0);
|
||||
module2::DoubleMap::<module2::Instance2>::insert(&0, &0, &0);
|
||||
module2::DoubleMap::<module2::Instance3>::insert(&0, &0, &0);
|
||||
});
|
||||
// 16 storage values + 4 linked_map head.
|
||||
assert_eq!(storage.0.len(), 16 + 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn storage_with_instance_basic_operation() {
|
||||
new_test_ext().execute_with(|| {
|
||||
type Value = module2::Value<Runtime, module2::Instance1>;
|
||||
type Map = module2::Map<module2::Instance1>;
|
||||
type LinkedMap = module2::LinkedMap<module2::Instance1>;
|
||||
type DoubleMap = module2::DoubleMap<module2::Instance1>;
|
||||
|
||||
assert_eq!(Value::exists(), true);
|
||||
assert_eq!(Value::get(), 4);
|
||||
Value::put(1);
|
||||
assert_eq!(Value::get(), 1);
|
||||
assert_eq!(Value::take(), 1);
|
||||
assert_eq!(Value::get(), 0);
|
||||
Value::mutate(|a| *a=2);
|
||||
assert_eq!(Value::get(), 2);
|
||||
Value::kill();
|
||||
assert_eq!(Value::exists(), false);
|
||||
assert_eq!(Value::get(), 0);
|
||||
|
||||
let key = 1;
|
||||
assert_eq!(Map::exists(0), true);
|
||||
assert_eq!(Map::exists(key), false);
|
||||
Map::insert(key, 1);
|
||||
assert_eq!(Map::get(key), 1);
|
||||
assert_eq!(Map::take(key), 1);
|
||||
assert_eq!(Map::get(key), 0);
|
||||
Map::mutate(key, |a| *a=2);
|
||||
assert_eq!(Map::get(key), 2);
|
||||
Map::remove(key);
|
||||
assert_eq!(Map::exists(key), false);
|
||||
assert_eq!(Map::get(key), 0);
|
||||
|
||||
assert_eq!(LinkedMap::exists(0), true);
|
||||
assert_eq!(LinkedMap::exists(key), false);
|
||||
LinkedMap::insert(key, vec![1]);
|
||||
assert_eq!(LinkedMap::enumerate().count(), 2);
|
||||
assert_eq!(LinkedMap::get(key), vec![1]);
|
||||
assert_eq!(LinkedMap::take(key), vec![1]);
|
||||
assert_eq!(LinkedMap::enumerate().count(), 1);
|
||||
assert_eq!(LinkedMap::get(key), vec![]);
|
||||
LinkedMap::mutate(key, |a| *a=vec![2]);
|
||||
assert_eq!(LinkedMap::enumerate().count(), 2);
|
||||
assert_eq!(LinkedMap::get(key), vec![2]);
|
||||
LinkedMap::remove(key);
|
||||
assert_eq!(LinkedMap::enumerate().count(), 1);
|
||||
assert_eq!(LinkedMap::exists(key), false);
|
||||
assert_eq!(LinkedMap::get(key), vec![]);
|
||||
assert_eq!(LinkedMap::exists(key), false);
|
||||
assert_eq!(LinkedMap::enumerate().count(), 1);
|
||||
LinkedMap::insert(key, &vec![1]);
|
||||
assert_eq!(LinkedMap::enumerate().count(), 2);
|
||||
|
||||
let key1 = 1;
|
||||
let key2 = 1;
|
||||
assert_eq!(DoubleMap::exists(&0, &0), true);
|
||||
assert_eq!(DoubleMap::exists(&key1, &key2), false);
|
||||
DoubleMap::insert(&key1, &key2, &1);
|
||||
assert_eq!(DoubleMap::get(&key1, &key2), 1);
|
||||
assert_eq!(DoubleMap::take(&key1, &key2), 1);
|
||||
assert_eq!(DoubleMap::get(&key1, &key2), 0);
|
||||
DoubleMap::mutate(&key1, &key2, |a| *a=2);
|
||||
assert_eq!(DoubleMap::get(&key1, &key2), 2);
|
||||
DoubleMap::remove(&key1, &key2);
|
||||
assert_eq!(DoubleMap::get(&key1, &key2), 0);
|
||||
});
|
||||
}
|
||||
|
||||
const EXPECTED_METADATA: StorageMetadata = StorageMetadata {
|
||||
prefix: DecodeDifferent::Encode("Instance2Module2"),
|
||||
entries: DecodeDifferent::Encode(
|
||||
&[
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("Value"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Plain(DecodeDifferent::Encode("T::Amount")),
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(
|
||||
&module2::__GetByteStructValue(
|
||||
std::marker::PhantomData::<(Runtime, module2::Instance2)>
|
||||
)
|
||||
)
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("Map"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u64"),
|
||||
value: DecodeDifferent::Encode("u64"),
|
||||
is_linked: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(
|
||||
&module2::__GetByteStructMap(
|
||||
std::marker::PhantomData::<(Runtime, module2::Instance2)>
|
||||
)
|
||||
)
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("LinkedMap"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u64"),
|
||||
value: DecodeDifferent::Encode("Vec<u8>"),
|
||||
is_linked: true,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(
|
||||
&module2::__GetByteStructLinkedMap(
|
||||
std::marker::PhantomData::<(Runtime, module2::Instance2)>
|
||||
)
|
||||
)
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("DoubleMap"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::DoubleMap {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key2_hasher: StorageHasher::Blake2_256,
|
||||
key1: DecodeDifferent::Encode("u64"),
|
||||
key2: DecodeDifferent::Encode("u64"),
|
||||
value: DecodeDifferent::Encode("u64"),
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(
|
||||
&module2::__GetByteStructDoubleMap(
|
||||
std::marker::PhantomData::<(Runtime, module2::Instance2)>
|
||||
)
|
||||
)
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
}
|
||||
]
|
||||
)
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_instance_storage_metadata() {
|
||||
let metadata = Module2_2::storage_metadata();
|
||||
pretty_assertions::assert_eq!(EXPECTED_METADATA, metadata);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn instance_prefix_is_prefix_of_entries() {
|
||||
use module2::Instance;
|
||||
|
||||
let prefix = module2::Instance2::PREFIX;
|
||||
assert!(module2::Instance2::PREFIX_FOR_Value.starts_with(prefix));
|
||||
assert!(module2::Instance2::PREFIX_FOR_Map.starts_with(prefix));
|
||||
assert!(module2::Instance2::PREFIX_FOR_LinkedMap.starts_with(prefix));
|
||||
assert!(module2::Instance2::PREFIX_FOR_DoubleMap.starts_with(prefix));
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use support::sr_primitives::generic;
|
||||
use support::sr_primitives::traits::{BlakeTwo256, Block as _, Verify};
|
||||
use support::codec::{Encode, Decode};
|
||||
use primitives::{H256, sr25519};
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
mod system;
|
||||
|
||||
mod module {
|
||||
use super::*;
|
||||
|
||||
pub type Request<T> = (
|
||||
<T as system::Trait>::AccountId,
|
||||
Role,
|
||||
<T as system::Trait>::BlockNumber,
|
||||
);
|
||||
pub type Requests<T> = Vec<Request<T>>;
|
||||
|
||||
#[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, Debug)]
|
||||
pub enum Role {
|
||||
Storage,
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, Debug)]
|
||||
pub struct RoleParameters<T: Trait> {
|
||||
// minimum actors to maintain - if role is unstaking
|
||||
// and remaining actors would be less that this value - prevent or punish for unstaking
|
||||
pub min_actors: u32,
|
||||
|
||||
// the maximum number of spots available to fill for a role
|
||||
pub max_actors: u32,
|
||||
|
||||
// payouts are made at this block interval
|
||||
pub reward_period: T::BlockNumber,
|
||||
|
||||
// minimum amount of time before being able to unstake
|
||||
pub bonding_period: T::BlockNumber,
|
||||
|
||||
// how long tokens remain locked for after unstaking
|
||||
pub unbonding_period: T::BlockNumber,
|
||||
|
||||
// minimum period required to be in service. unbonding before this time is highly penalized
|
||||
pub min_service_period: T::BlockNumber,
|
||||
|
||||
// "startup" time allowed for roles that need to sync their infrastructure
|
||||
// with other providers before they are considered in service and punishable for
|
||||
// not delivering required level of service.
|
||||
pub startup_grace_period: T::BlockNumber,
|
||||
}
|
||||
|
||||
impl<T: Trait> Default for RoleParameters<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
max_actors: 10,
|
||||
reward_period: T::BlockNumber::default(),
|
||||
unbonding_period: T::BlockNumber::default(),
|
||||
|
||||
// not currently used
|
||||
min_actors: 5,
|
||||
bonding_period: T::BlockNumber::default(),
|
||||
min_service_period: T::BlockNumber::default(),
|
||||
startup_grace_period: T::BlockNumber::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Trait: system::Trait {}
|
||||
|
||||
support::decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, Copy, Clone, Serialize, Deserialize)]
|
||||
pub struct Data<T: Trait> {
|
||||
pub data: T::BlockNumber,
|
||||
}
|
||||
|
||||
impl<T: Trait> Default for Data<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
data: T::BlockNumber::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
support::decl_storage! {
|
||||
trait Store for Module<T: Trait> as Actors {
|
||||
/// requirements to enter and maintain status in roles
|
||||
pub Parameters get(fn parameters) build(|config: &GenesisConfig| {
|
||||
if config.enable_storage_role {
|
||||
let storage_params: RoleParameters<T> = Default::default();
|
||||
vec![(Role::Storage, storage_params)]
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}): map Role => Option<RoleParameters<T>>;
|
||||
|
||||
/// the roles members can enter into
|
||||
pub AvailableRoles get(fn available_roles) build(|config: &GenesisConfig| {
|
||||
if config.enable_storage_role {
|
||||
vec![(Role::Storage)]
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}): Vec<Role>;
|
||||
|
||||
/// Actors list
|
||||
pub ActorAccountIds get(fn actor_account_ids) : Vec<T::AccountId>;
|
||||
|
||||
/// actor accounts associated with a role
|
||||
pub AccountIdsByRole get(fn account_ids_by_role) : map Role => Vec<T::AccountId>;
|
||||
|
||||
/// tokens locked until given block number
|
||||
pub Bondage get(fn bondage) : map T::AccountId => T::BlockNumber;
|
||||
|
||||
/// First step before enter a role is registering intent with a new account/key.
|
||||
/// This is done by sending a role_entry_request() from the new account.
|
||||
/// The member must then send a stake() transaction to approve the request and enter the desired role.
|
||||
/// The account making the request will be bonded and must have
|
||||
/// sufficient balance to cover the minimum stake for the role.
|
||||
/// Bonding only occurs after successful entry into a role.
|
||||
pub RoleEntryRequests get(fn role_entry_requests) : Requests<T>;
|
||||
|
||||
/// Entry request expires after this number of blocks
|
||||
pub RequestLifeTime get(fn request_life_time) config(request_life_time) : u64 = 0;
|
||||
}
|
||||
add_extra_genesis {
|
||||
config(enable_storage_role): bool;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type Signature = sr25519::Signature;
|
||||
pub type AccountId = <Signature as Verify>::Signer;
|
||||
pub type BlockNumber = u64;
|
||||
pub type Index = u64;
|
||||
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
||||
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
||||
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<u32, Call, Signature, ()>;
|
||||
|
||||
impl system::Trait for Runtime {
|
||||
type Hash = H256;
|
||||
type Origin = Origin;
|
||||
type BlockNumber = BlockNumber;
|
||||
type AccountId = AccountId;
|
||||
type Event = Event;
|
||||
}
|
||||
|
||||
impl module::Trait for Runtime {}
|
||||
|
||||
support::construct_runtime!(
|
||||
pub enum Runtime where
|
||||
Block = Block,
|
||||
NodeBlock = Block,
|
||||
UncheckedExtrinsic = UncheckedExtrinsic
|
||||
{
|
||||
System: system::{Module, Call, Event},
|
||||
Module: module::{Module, Call, Storage, Config},
|
||||
}
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn create_genesis_config() {
|
||||
GenesisConfig {
|
||||
module: Some(module::GenesisConfig {
|
||||
request_life_time: 0,
|
||||
enable_storage_role: true,
|
||||
})
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#[test]
|
||||
fn reserved_keyword() {
|
||||
// As trybuild is using `cargo check`, we don't need the real WASM binaries.
|
||||
std::env::set_var("BUILD_DUMMY_WASM_BINARY", "1");
|
||||
|
||||
let t = trybuild::TestCases::new();
|
||||
t.compile_fail("tests/reserved_keyword/*.rs");
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
macro_rules! reserved {
|
||||
($($reserved:ident)*) => {
|
||||
$(
|
||||
mod $reserved {
|
||||
pub use support::dispatch::Result;
|
||||
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
type BlockNumber: Into<u32>;
|
||||
}
|
||||
|
||||
pub mod system {
|
||||
use support::dispatch::Result;
|
||||
|
||||
pub fn ensure_root<R>(_: R) -> Result {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
support::decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
fn $reserved(_origin) -> Result { unreachable!() }
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
reserved!(on_finalize on_initialize on_finalise on_initialise offchain_worker deposit_event);
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,47 @@
|
||||
error: Invalid call fn name: `on_finalize`, name is reserved and doesn't match expected signature, please refer to `decl_module!` documentation to see the appropriate usage, or rename it to an unreserved keyword.
|
||||
--> $DIR/on_initialize.rs:30:1
|
||||
|
|
||||
30 | reserved!(on_finalize on_initialize on_finalise on_initialise offchain_worker deposit_event);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in this macro invocation
|
||||
|
|
||||
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
|
||||
|
||||
error: Invalid call fn name: `on_initialize`, name is reserved and doesn't match expected signature, please refer to `decl_module!` documentation to see the appropriate usage, or rename it to an unreserved keyword.
|
||||
--> $DIR/on_initialize.rs:30:1
|
||||
|
|
||||
30 | reserved!(on_finalize on_initialize on_finalise on_initialise offchain_worker deposit_event);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in this macro invocation
|
||||
|
|
||||
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
|
||||
|
||||
error: `on_finalise` was renamed to `on_finalize`. Please rename your function accordingly.
|
||||
--> $DIR/on_initialize.rs:30:1
|
||||
|
|
||||
30 | reserved!(on_finalize on_initialize on_finalise on_initialise offchain_worker deposit_event);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in this macro invocation
|
||||
|
|
||||
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
|
||||
|
||||
error: `on_initialise` was renamed to `on_initialize`. Please rename your function accordingly.
|
||||
--> $DIR/on_initialize.rs:30:1
|
||||
|
|
||||
30 | reserved!(on_finalize on_initialize on_finalise on_initialise offchain_worker deposit_event);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in this macro invocation
|
||||
|
|
||||
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
|
||||
|
||||
error: Invalid call fn name: `offchain_worker`, name is reserved and doesn't match expected signature, please refer to `decl_module!` documentation to see the appropriate usage, or rename it to an unreserved keyword.
|
||||
--> $DIR/on_initialize.rs:30:1
|
||||
|
|
||||
30 | reserved!(on_finalize on_initialize on_finalise on_initialise offchain_worker deposit_event);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in this macro invocation
|
||||
|
|
||||
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
|
||||
|
||||
error: `deposit_event` function is reserved and must follow the syntax: `$vis:vis fn deposit_event() = default;`
|
||||
--> $DIR/on_initialize.rs:30:1
|
||||
|
|
||||
30 | reserved!(on_finalize on_initialize on_finalise on_initialise offchain_worker deposit_event);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in this macro invocation
|
||||
|
|
||||
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
|
||||
@@ -0,0 +1,64 @@
|
||||
use support::codec::{Encode, Decode, EncodeLike};
|
||||
|
||||
pub trait Trait: 'static + Eq + Clone {
|
||||
type Origin: Into<Result<RawOrigin<Self::AccountId>, Self::Origin>>
|
||||
+ From<RawOrigin<Self::AccountId>>;
|
||||
|
||||
type BlockNumber: Decode + Encode + EncodeLike + Clone + Default;
|
||||
type Hash;
|
||||
type AccountId: Encode + EncodeLike + Decode;
|
||||
type Event: From<Event>;
|
||||
}
|
||||
|
||||
support::decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
pub fn deposit_event(_event: impl Into<T::Event>) {
|
||||
}
|
||||
}
|
||||
|
||||
support::decl_event!(
|
||||
pub enum Event {
|
||||
ExtrinsicSuccess,
|
||||
ExtrinsicFailed,
|
||||
}
|
||||
);
|
||||
|
||||
support::decl_error! {
|
||||
pub enum Error {
|
||||
/// Test error documentation
|
||||
TestError,
|
||||
/// Error documentation
|
||||
/// with multiple lines
|
||||
AnotherError
|
||||
}
|
||||
}
|
||||
|
||||
/// Origin for the system module.
|
||||
#[derive(PartialEq, Eq, Clone, sr_primitives::RuntimeDebug)]
|
||||
pub enum RawOrigin<AccountId> {
|
||||
Root,
|
||||
Signed(AccountId),
|
||||
None,
|
||||
}
|
||||
|
||||
impl<AccountId> From<Option<AccountId>> for RawOrigin<AccountId> {
|
||||
fn from(s: Option<AccountId>) -> RawOrigin<AccountId> {
|
||||
match s {
|
||||
Some(who) => RawOrigin::Signed(who),
|
||||
None => RawOrigin::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type Origin<T> = RawOrigin<<T as Trait>::AccountId>;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn ensure_root<OuterOrigin, AccountId>(o: OuterOrigin) -> Result<(), &'static str>
|
||||
where OuterOrigin: Into<Result<RawOrigin<AccountId>, OuterOrigin>>
|
||||
{
|
||||
o.into().map(|_| ()).map_err(|_| "bad origin: expected to be a root origin")
|
||||
}
|
||||
Reference in New Issue
Block a user