mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-14 02:51:08 +00:00
Phase 1 of repo reorg (#719)
* Remove unneeded script * Rename Substrate Demo -> Substrate * Rename demo -> node * Build wasm from last rename. * Merge ed25519 into substrate-primitives * Minor tweak * Rename substrate -> core * Move substrate-runtime-support to core/runtime/support * Rename/move substrate-runtime-version * Move codec up a level * Rename substrate-codec -> parity-codec * Move environmental up a level * Move pwasm-* up to top, ready for removal * Remove requirement of s-r-support from s-r-primitives * Move core/runtime/primitives into core/runtime-primitives * Remove s-r-support dep from s-r-version * Remove dep of s-r-support from bft * Remove dep of s-r-support from node/consensus * Sever all other core deps from s-r-support * Forgot the no_std directive * Rename non-SRML modules to sr-* to avoid match clashes * Move runtime/* to srml/* * Rename substrate-runtime-* -> srml-* * Move srml to top-level
This commit is contained in:
committed by
Arkadiy Paronyan
parent
8fe5aa4c81
commit
1e01162505
@@ -0,0 +1,584 @@
|
||||
// Copyright 2017 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/>.
|
||||
|
||||
//! Dispatch system. Just dispatches calls.
|
||||
|
||||
pub use rstd::prelude::{Vec, Clone, Eq, PartialEq};
|
||||
#[cfg(feature = "std")]
|
||||
pub use std::fmt;
|
||||
pub use rstd::result;
|
||||
#[cfg(feature = "std")]
|
||||
use serde;
|
||||
pub use codec::{Codec, Decode, Encode, Input, Output};
|
||||
|
||||
pub type Result = result::Result<(), &'static str>;
|
||||
|
||||
pub trait Dispatchable {
|
||||
type Origin;
|
||||
type Trait;
|
||||
fn dispatch(self, origin: Self::Origin) -> Result;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub trait Callable {
|
||||
type Call: Dispatchable + Codec + ::serde::Serialize + Clone + PartialEq + Eq;
|
||||
}
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub trait Callable {
|
||||
type Call: Dispatchable + Codec + Clone + PartialEq + Eq;
|
||||
}
|
||||
|
||||
// dirty hack to work around serde_derive issue
|
||||
// https://github.com/rust-lang/rust/issues/51331
|
||||
pub type CallableCallFor<A> = <A as Callable>::Call;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub trait Parameter: Codec + serde::Serialize + Clone + Eq + fmt::Debug {}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T> Parameter for T where T: Codec + serde::Serialize + Clone + Eq + fmt::Debug {}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub trait Parameter: Codec + Clone + Eq {}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T> Parameter for T where T: Codec + Clone + Eq {}
|
||||
|
||||
/// Declare a struct for this module, then implement dispatch logic to create a pairing of several
|
||||
/// dispatch traits and enums.
|
||||
#[macro_export]
|
||||
macro_rules! decl_module {
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident>
|
||||
for enum $call_type:ident where origin: $origin_type:ty {$(
|
||||
$(#[doc = $doc_attr:tt])*
|
||||
fn $fn_name:ident(origin
|
||||
$(
|
||||
, $param_name:ident : $param:ty
|
||||
)*
|
||||
) -> $result:ty;
|
||||
)*}
|
||||
) => {
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
// TODO: switching based on std feature is because of an issue in
|
||||
// serde-derive for when we attempt to derive `Deserialize` on these types,
|
||||
// in a situation where we've imported `srml_support` as another name.
|
||||
#[cfg(feature = "std")]
|
||||
pub struct $mod_type<$trait_instance: $trait_name>(::std::marker::PhantomData<$trait_instance>);
|
||||
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub struct $mod_type<$trait_instance: $trait_name>(::core::marker::PhantomData<$trait_instance>);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
$(#[$attr])*
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum $call_type<$trait_instance: $trait_name> {
|
||||
__PhantomItem(::std::marker::PhantomData<$trait_instance>),
|
||||
__OtherPhantomItem(::std::marker::PhantomData<$trait_instance>),
|
||||
$(
|
||||
#[allow(non_camel_case_types)]
|
||||
$fn_name ( $( $param ),* ),
|
||||
)*
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
$(#[$attr])*
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum $call_type<$trait_instance: $trait_name> {
|
||||
__PhantomItem(::core::marker::PhantomData<$trait_instance>),
|
||||
__OtherPhantomItem(::core::marker::PhantomData<$trait_instance>),
|
||||
$(
|
||||
#[allow(non_camel_case_types)]
|
||||
$fn_name ( $( $param ),* ),
|
||||
)*
|
||||
}
|
||||
|
||||
// manual implementation of clone/eq/partialeq because using derive erroneously requires
|
||||
// clone/eq/partialeq from T.
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::Clone
|
||||
for $call_type<$trait_instance>
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
match *self {
|
||||
$(
|
||||
$call_type::$fn_name( $( ref $param_name ),* ) =>
|
||||
$call_type::$fn_name( $( $param_name.clone() ),* )
|
||||
,)*
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::PartialEq
|
||||
for $call_type<$trait_instance>
|
||||
{
|
||||
fn eq(&self, _other: &Self) -> bool {
|
||||
match *self {
|
||||
$(
|
||||
$call_type::$fn_name( $( ref $param_name ),* ) => {
|
||||
let self_params = ( $( $param_name, )* );
|
||||
if let $call_type::$fn_name( $( ref $param_name ),* ) = *_other {
|
||||
self_params == ( $( $param_name, )* )
|
||||
} else {
|
||||
match *_other {
|
||||
$call_type::__PhantomItem(_) => unreachable!(),
|
||||
$call_type::__OtherPhantomItem(_) => unreachable!(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::Eq
|
||||
for $call_type<$trait_instance>
|
||||
{}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::fmt::Debug
|
||||
for $call_type<$trait_instance>
|
||||
{
|
||||
fn fmt(&self, _f: &mut $crate::dispatch::fmt::Formatter) -> $crate::dispatch::result::Result<(), $crate::dispatch::fmt::Error> {
|
||||
match *self {
|
||||
$(
|
||||
$call_type::$fn_name( $( ref $param_name ),* ) =>
|
||||
write!(_f, "{}{:?}",
|
||||
stringify!($fn_name),
|
||||
( $( $param_name.clone(), )* )
|
||||
)
|
||||
,)*
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::Decode for $call_type<$trait_instance> {
|
||||
fn decode<I: $crate::dispatch::Input>(input: &mut I) -> Option<Self> {
|
||||
let _input_id = input.read_byte()?;
|
||||
__impl_decode!(input; _input_id; 0; $call_type; $( fn $fn_name( $( $param_name ),* ); )*)
|
||||
}
|
||||
}
|
||||
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::Encode for $call_type<$trait_instance> {
|
||||
fn encode_to<W: $crate::dispatch::Output>(&self, _dest: &mut W) {
|
||||
__impl_encode!(_dest; *self; 0; $call_type; $( fn $fn_name( $( $param_name ),* ); )*);
|
||||
if let $call_type::__PhantomItem(_) = *self { unreachable!() }
|
||||
if let $call_type::__OtherPhantomItem(_) = *self { unreachable!() }
|
||||
}
|
||||
}
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::Dispatchable
|
||||
for $call_type<$trait_instance>
|
||||
{
|
||||
type Trait = $trait_instance;
|
||||
type Origin = $origin_type;
|
||||
fn dispatch(self, _origin: Self::Origin) -> $crate::dispatch::Result {
|
||||
match self {
|
||||
$(
|
||||
$call_type::$fn_name( $( $param_name ),* ) =>
|
||||
<$mod_type<$trait_instance>>::$fn_name( _origin $(, $param_name )* ),
|
||||
)*
|
||||
_ => { panic!("__PhantomItem should never be used.") },
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::Callable
|
||||
for $mod_type<$trait_instance>
|
||||
{
|
||||
type Call = $call_type<$trait_instance>;
|
||||
}
|
||||
|
||||
impl<$trait_instance: $trait_name> $mod_type<$trait_instance> {
|
||||
pub fn dispatch<D: $crate::dispatch::Dispatchable<Trait = $trait_instance>>(d: D, origin: D::Origin) -> $crate::dispatch::Result {
|
||||
d.dispatch(origin)
|
||||
}
|
||||
}
|
||||
|
||||
__dispatch_impl_json_metadata! {
|
||||
$mod_type $trait_instance $trait_name $call_type $origin_type
|
||||
{$( $(#[doc = $doc_attr])* fn $fn_name(origin $(, $param_name : $param )*) -> $result; )*}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __impl_decode {
|
||||
(
|
||||
$input:expr;
|
||||
$input_id:expr;
|
||||
$fn_id:expr;
|
||||
$call_type:ident;
|
||||
fn $fn_name:ident(
|
||||
$( $param_name:ident ),*
|
||||
);
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
{
|
||||
if $input_id == ($fn_id) {
|
||||
$(
|
||||
let $param_name = $crate::dispatch::Decode::decode($input)?;
|
||||
)*
|
||||
return Some($call_type:: $fn_name( $( $param_name ),* ));
|
||||
}
|
||||
|
||||
__impl_decode!($input; $input_id; $fn_id + 1; $call_type; $($rest)*)
|
||||
}
|
||||
};
|
||||
(
|
||||
$input:expr;
|
||||
$input_id:expr;
|
||||
$fn_id:expr;
|
||||
$call_type:ident;
|
||||
) => {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __impl_encode {
|
||||
(
|
||||
$dest:expr;
|
||||
$self:expr;
|
||||
$fn_id:expr;
|
||||
$call_type:ident;
|
||||
fn $fn_name:ident(
|
||||
$( $param_name:ident ),*
|
||||
);
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
{
|
||||
if let $call_type::$fn_name(
|
||||
$(
|
||||
ref $param_name
|
||||
),*
|
||||
) = $self {
|
||||
$dest.push_byte(($fn_id) as u8);
|
||||
$(
|
||||
$param_name.encode_to($dest);
|
||||
)*
|
||||
}
|
||||
|
||||
__impl_encode!($dest; $self; $fn_id + 1; $call_type; $($rest)*)
|
||||
}
|
||||
};
|
||||
(
|
||||
$dest:expr;
|
||||
$self:expr;
|
||||
$fn_id:expr;
|
||||
$call_type:ident;
|
||||
) => {{}}
|
||||
}
|
||||
|
||||
pub trait IsSubType<T: Callable> {
|
||||
fn is_aux_sub_type(&self) -> Option<&<T as Callable>::Call>;
|
||||
}
|
||||
|
||||
/// Implement a meta-dispatch module to dispatch to other dispatchers.
|
||||
#[macro_export]
|
||||
macro_rules! impl_outer_dispatch {
|
||||
() => ();
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub enum $call_type:ident where origin: $origin:ty {
|
||||
$(
|
||||
$camelcase:ident,
|
||||
)*
|
||||
}
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
$(#[$attr])*
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
pub enum $call_type {
|
||||
$(
|
||||
$camelcase ( $crate::dispatch::CallableCallFor<$camelcase> )
|
||||
,)*
|
||||
}
|
||||
__impl_outer_dispatch_common! { $call_type, $($camelcase,)* }
|
||||
impl $crate::dispatch::Dispatchable for $call_type {
|
||||
type Origin = $origin;
|
||||
type Trait = $call_type;
|
||||
fn dispatch(self, origin: $origin) -> $crate::dispatch::Result {
|
||||
match self {
|
||||
$(
|
||||
$call_type::$camelcase(call) => call.dispatch(origin),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
$(
|
||||
impl $crate::dispatch::IsSubType<$camelcase> for $call_type {
|
||||
fn is_aux_sub_type(&self) -> Option<&<$camelcase as $crate::dispatch::Callable>::Call> {
|
||||
if let $call_type::$camelcase ( ref r ) = *self {
|
||||
Some(r)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
impl_outer_dispatch!{ $($rest)* }
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement a meta-dispatch module to dispatch to other dispatchers.
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __impl_outer_dispatch_common {
|
||||
(
|
||||
$call_type:ident, $( $camelcase:ident, )*
|
||||
) => {
|
||||
impl $crate::dispatch::Decode for $call_type {
|
||||
fn decode<I: $crate::dispatch::Input>(input: &mut I) -> Option<Self> {
|
||||
let input_id = input.read_byte()?;
|
||||
__impl_decode!(input; input_id; 0; $call_type; $( fn $camelcase ( outer_dispatch_param ); )*)
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::dispatch::Encode for $call_type {
|
||||
fn encode_to<W: $crate::dispatch::Output>(&self, dest: &mut W) {
|
||||
__impl_encode!(dest; *self; 0; $call_type; $( fn $camelcase( outer_dispatch_param ); )*)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement the `json_metadata` function.
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __dispatch_impl_json_metadata {
|
||||
(
|
||||
$mod_type:ident $trait_instance:ident $trait_name:ident
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
impl<$trait_instance: $trait_name> $mod_type<$trait_instance> {
|
||||
pub fn json_metadata() -> &'static str {
|
||||
concat!(r#"{ "name": ""#, stringify!($mod_type), r#"", "call": "#,
|
||||
__call_to_json!($($rest)*), " }")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the list of calls into their JSON representation, joined by ",".
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __call_to_json {
|
||||
// WITH AUX
|
||||
(
|
||||
$call_type:ident $origin_type:ty
|
||||
{$(
|
||||
$(#[doc = $doc_attr:tt])*
|
||||
fn $fn_name:ident(origin
|
||||
$(
|
||||
, $param_name:ident : $param:ty
|
||||
)*
|
||||
) -> $result:ty;
|
||||
)*}
|
||||
) => {
|
||||
concat!(
|
||||
r#"{ "name": ""#, stringify!($call_type),
|
||||
r#"", "functions": {"#,
|
||||
__functions_to_json!(""; 0; $origin_type; $(
|
||||
fn $fn_name(origin $(, $param_name: $param )* ) -> $result;
|
||||
__function_doc_to_json!(""; $($doc_attr)*);
|
||||
)*), " } }"
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
/// Convert a list of functions into their JSON representation, joined by ",".
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __functions_to_json {
|
||||
// WITHOUT AUX
|
||||
(
|
||||
$prefix_str:tt;
|
||||
$fn_id:expr;
|
||||
fn $fn_name:ident(
|
||||
$($param_name:ident : $param:ty),*
|
||||
) -> $result:ty;
|
||||
$fn_doc:expr;
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
concat!($prefix_str, " ",
|
||||
__function_to_json!(
|
||||
fn $fn_name(
|
||||
$($param_name : $param),*
|
||||
) -> $result;
|
||||
$fn_doc;
|
||||
$fn_id;
|
||||
), __functions_to_json!(","; $fn_id + 1; $($rest)*)
|
||||
)
|
||||
};
|
||||
// WITH AUX
|
||||
(
|
||||
$prefix_str:tt;
|
||||
$fn_id:expr;
|
||||
$origin_type:ty;
|
||||
fn $fn_name:ident(origin
|
||||
$(
|
||||
, $param_name:ident : $param:ty
|
||||
)*
|
||||
) -> $result:ty;
|
||||
$fn_doc:expr;
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
concat!($prefix_str, " ",
|
||||
__function_to_json!(
|
||||
fn $fn_name(
|
||||
origin: $origin_type
|
||||
$(, $param_name : $param)*
|
||||
) -> $result;
|
||||
$fn_doc;
|
||||
$fn_id;
|
||||
), __functions_to_json!(","; $fn_id + 1; $origin_type; $($rest)*)
|
||||
)
|
||||
};
|
||||
// BASE CASE
|
||||
(
|
||||
$prefix_str:tt;
|
||||
$fn_id:expr;
|
||||
$($origin_type:ty;)*
|
||||
) => {
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a function into its JSON representation.
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __function_to_json {
|
||||
(
|
||||
fn $fn_name:ident(
|
||||
$first_param_name:ident : $first_param:ty $(, $param_name:ident : $param:ty)*
|
||||
) -> $result:ty;
|
||||
$fn_doc:tt;
|
||||
$fn_id:expr;
|
||||
) => {
|
||||
concat!(
|
||||
r#"""#, stringify!($fn_id), r#"""#,
|
||||
r#": { "name": ""#, stringify!($fn_name),
|
||||
r#"", "params": [ "#,
|
||||
concat!(r#"{ "name": ""#, stringify!($first_param_name), r#"", "type": ""#, stringify!($first_param), r#"" }"# ),
|
||||
$(
|
||||
concat!(r#", { "name": ""#, stringify!($param_name), r#"", "type": ""#, stringify!($param), r#"" }"# ),
|
||||
)*
|
||||
r#" ], "description": ["#, $fn_doc, " ] }"
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
/// Convert a function documentation attribute into its JSON representation.
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __function_doc_to_json {
|
||||
(
|
||||
$prefix_str:tt;
|
||||
$doc_attr:tt
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
concat!(
|
||||
$prefix_str, r#" ""#,
|
||||
$doc_attr,
|
||||
r#"""#,
|
||||
__function_doc_to_json!(","; $($rest)*)
|
||||
)
|
||||
};
|
||||
(
|
||||
$prefix_str:tt;
|
||||
) => {
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
// Do not complain about unused `dispatch` and `dispatch_aux`.
|
||||
#[allow(dead_code)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde;
|
||||
use serde_json;
|
||||
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
/// Hi, this is a comment.
|
||||
fn aux_0(origin) -> Result;
|
||||
fn aux_1(origin, data: i32) -> Result;
|
||||
fn aux_2(origin, data: i32, data2: String) -> Result;
|
||||
}
|
||||
}
|
||||
|
||||
const EXPECTED_METADATA: &str = concat!(
|
||||
r#"{ "name": "Module", "call": "#,
|
||||
r#"{ "name": "Call", "functions": { "#,
|
||||
r#""0": { "name": "aux_0", "params": [ "#,
|
||||
r#"{ "name": "origin", "type": "T::Origin" }"#,
|
||||
r#" ], "description": [ " Hi, this is a comment." ] }, "#,
|
||||
r#""0 + 1": { "name": "aux_1", "params": [ "#,
|
||||
r#"{ "name": "origin", "type": "T::Origin" }, "#,
|
||||
r#"{ "name": "data", "type": "i32" }"#,
|
||||
r#" ], "description": [ ] }, "#,
|
||||
r#""0 + 1 + 1": { "name": "aux_2", "params": [ "#,
|
||||
r#"{ "name": "origin", "type": "T::Origin" }, "#,
|
||||
r#"{ "name": "data", "type": "i32" }, "#,
|
||||
r#"{ "name": "data2", "type": "String" }"#,
|
||||
r#" ], "description": [ ] }"#,
|
||||
r#" } }"#,
|
||||
r#" }"#,
|
||||
);
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
fn aux_0(_: T::Origin) -> Result {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn aux_1(_: T::Origin, _: i32) -> Result {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn aux_2(_: T::Origin, _: i32, _: String) -> Result {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
struct TraitImpl {}
|
||||
|
||||
impl Trait for TraitImpl {
|
||||
type Origin = u32;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn module_json_metadata() {
|
||||
let metadata = Module::<TraitImpl>::json_metadata();
|
||||
assert_eq!(EXPECTED_METADATA, metadata);
|
||||
let _: serde::de::IgnoredAny =
|
||||
serde_json::from_str(metadata).expect("Is valid json syntax");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
// Copyright 2018 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/>.
|
||||
|
||||
/// Implement an `Event`/`RawEvent` for a module.
|
||||
#[macro_export]
|
||||
macro_rules! decl_event {
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub enum Event<$( $evt_generic_param:ident )*> with RawEvent<$( $generic_param:ident ),*>
|
||||
where $( <$generic:ident as $trait:path>::$trait_type:ident),* {
|
||||
$(
|
||||
$(#[doc = $doc_attr:tt])*
|
||||
$event:ident( $( $param:path ),* ),
|
||||
)*
|
||||
}
|
||||
) => {
|
||||
pub type Event<$( $evt_generic_param )*> = RawEvent<$( <$generic as $trait>::$trait_type ),*>;
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, PartialEq, Eq, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
$(#[$attr])*
|
||||
pub enum RawEvent<$( $generic_param ),*> {
|
||||
$(
|
||||
$( #[doc = $doc_attr] )*
|
||||
$event($( $param ),*),
|
||||
)*
|
||||
}
|
||||
impl<$( $generic_param ),*> From<RawEvent<$( $generic_param ),*>> for () {
|
||||
fn from(_: RawEvent<$( $generic_param ),*>) -> () { () }
|
||||
}
|
||||
impl<$( $generic_param ),*> RawEvent<$( $generic_param ),*> {
|
||||
#[allow(dead_code)]
|
||||
pub fn event_json_metadata() -> &'static str {
|
||||
concat!(
|
||||
"{",
|
||||
__impl_event_json_metadata!("";
|
||||
$(
|
||||
$event ( $( $param ),* );
|
||||
__function_doc_to_json!(""; $($doc_attr)*);
|
||||
)*
|
||||
),
|
||||
" }"
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub enum Event {
|
||||
$(
|
||||
$(#[doc = $doc_attr:tt])*
|
||||
$event:ident,
|
||||
)*
|
||||
}
|
||||
) => {
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, PartialEq, Eq, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
$(#[$attr])*
|
||||
pub enum Event {
|
||||
$(
|
||||
$( #[doc = $doc_attr] )*
|
||||
$event,
|
||||
)*
|
||||
}
|
||||
impl From<Event> for () {
|
||||
fn from(_: Event) -> () { () }
|
||||
}
|
||||
impl Event {
|
||||
#[allow(dead_code)]
|
||||
pub fn event_json_metadata() -> &'static str {
|
||||
concat!(
|
||||
"{",
|
||||
__impl_event_json_metadata!("";
|
||||
$(
|
||||
$event;
|
||||
__function_doc_to_json!(""; $($doc_attr)*);
|
||||
)*
|
||||
),
|
||||
" }"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __impl_event_json_metadata {
|
||||
(
|
||||
$prefix_str:expr;
|
||||
$event:ident( $first_param:path $(, $param:path )* );
|
||||
$event_doc:expr;
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
concat!($prefix_str, " ", "\"", stringify!($event), r#"": { "params": [ ""#,
|
||||
stringify!($first_param), "\""
|
||||
$(, concat!(", \"", stringify!($param), "\"") )*, r#" ], "description": ["#,
|
||||
$event_doc, " ] }",
|
||||
__impl_event_json_metadata!(","; $( $rest )*)
|
||||
)
|
||||
};
|
||||
(
|
||||
$prefix_str:expr;
|
||||
$event:ident;
|
||||
$event_doc:expr;
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
concat!($prefix_str, " ", "\"", stringify!($event),
|
||||
r#"": { "params": null, "description": ["#, $event_doc, " ] }",
|
||||
__impl_event_json_metadata!(","; $( $rest )*)
|
||||
)
|
||||
};
|
||||
(
|
||||
$prefix_str:expr;
|
||||
) => {
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_outer_event {
|
||||
($(#[$attr:meta])* pub enum $name:ident for $runtime:ident { $( $module:ident ),* }) => {
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, PartialEq, Eq, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
$(#[$attr])*
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum $name {
|
||||
system(system::Event),
|
||||
$(
|
||||
$module($module::Event<$runtime>),
|
||||
)*
|
||||
}
|
||||
impl From<system::Event> for $name {
|
||||
fn from(x: system::Event) -> Self {
|
||||
$name::system(x)
|
||||
}
|
||||
}
|
||||
$(
|
||||
impl From<$module::Event<$runtime>> for $name {
|
||||
fn from(x: $module::Event<$runtime>) -> Self {
|
||||
$name::$module(x)
|
||||
}
|
||||
}
|
||||
)*
|
||||
__impl_outer_event_json_metadata!($runtime; $name; $( $module )*);
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __impl_outer_event_json_metadata {
|
||||
(
|
||||
$runtime:ident;
|
||||
$event_name:ident;
|
||||
$( $module:ident )*
|
||||
) => {
|
||||
impl $runtime {
|
||||
#[allow(dead_code)]
|
||||
pub fn outer_event_json_metadata() -> (&'static str, &'static [(&'static str, fn() -> &'static str)]) {
|
||||
static METADATA: &[(&str, fn() -> &'static str)] = &[
|
||||
("system", system::Event::event_json_metadata)
|
||||
$(
|
||||
, (
|
||||
stringify!($module),
|
||||
$module::Event::<$runtime>::event_json_metadata
|
||||
)
|
||||
)*
|
||||
];
|
||||
(
|
||||
stringify!($event_name),
|
||||
METADATA
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(dead_code)]
|
||||
mod tests {
|
||||
use serde;
|
||||
use serde_json;
|
||||
|
||||
mod system {
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
decl_event!(
|
||||
pub enum Event<T> with RawEvent<Balance>
|
||||
where <T as Trait>::Balance
|
||||
{
|
||||
/// Hi, I am a comment.
|
||||
TestEvent(Balance),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
mod event_module2 {
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
type Balance;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
decl_event!(
|
||||
pub enum Event<T> with RawEvent<Balance>
|
||||
where <T as Trait>::Balance
|
||||
{
|
||||
TestEvent(Balance),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Deserialize, Serialize)]
|
||||
pub struct TestRuntime;
|
||||
|
||||
impl_outer_event! {
|
||||
pub enum TestEvent for TestRuntime {
|
||||
event_module, event_module2
|
||||
}
|
||||
}
|
||||
|
||||
impl event_module::Trait for TestRuntime {
|
||||
type Origin = u32;
|
||||
type Balance = u32;
|
||||
}
|
||||
|
||||
impl event_module2::Trait for TestRuntime {
|
||||
type Origin = u32;
|
||||
type Balance = u32;
|
||||
}
|
||||
|
||||
impl system::Trait for TestRuntime {
|
||||
type Origin = u32;
|
||||
}
|
||||
|
||||
const EXPECTED_METADATA: (&str, &[(&str, &str)]) = (
|
||||
"TestEvent", &[
|
||||
("system", r#"{ "SystemEvent": { "params": null, "description": [ ] } }"#),
|
||||
("event_module", r#"{ "TestEvent": { "params": [ "Balance" ], "description": [ " Hi, I am a comment." ] } }"#),
|
||||
("event_module2", r#"{ "TestEvent": { "params": [ "Balance" ], "description": [ ] } }"#),
|
||||
]
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn outer_event_json_metadata() {
|
||||
let metadata = TestRuntime::outer_event_json_metadata();
|
||||
assert_eq!(EXPECTED_METADATA.0, metadata.0);
|
||||
assert_eq!(EXPECTED_METADATA.1.len(), metadata.1.len());
|
||||
|
||||
for (expected, got) in EXPECTED_METADATA.1.iter().zip(metadata.1.iter()) {
|
||||
assert_eq!(expected.0, got.0);
|
||||
assert_eq!(expected.1, got.1());
|
||||
let _: serde::de::IgnoredAny =
|
||||
serde_json::from_str(got.1()).expect(&format!("Is valid json syntax: {}", got.1()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2017 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/>.
|
||||
|
||||
//! Hashable trait.
|
||||
|
||||
use codec::Codec;
|
||||
use runtime_io::{blake2_256, twox_128, twox_256};
|
||||
|
||||
pub trait Hashable: Sized {
|
||||
fn blake2_256(&self) -> [u8; 32];
|
||||
fn twox_128(&self) -> [u8; 16];
|
||||
fn twox_256(&self) -> [u8; 32];
|
||||
}
|
||||
|
||||
impl<T: Codec> Hashable for T {
|
||||
fn blake2_256(&self) -> [u8; 32] {
|
||||
blake2_256(&self.encode())
|
||||
}
|
||||
fn twox_128(&self) -> [u8; 16] {
|
||||
twox_128(&self.encode())
|
||||
}
|
||||
fn twox_256(&self) -> [u8; 32] {
|
||||
twox_256(&self.encode())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
// Copyright 2017 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[]
|
||||
//! Support code for the runtime.
|
||||
// end::description[]
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
extern crate serde;
|
||||
|
||||
extern crate sr_std as rstd;
|
||||
extern crate sr_io as runtime_io;
|
||||
extern crate substrate_primitives as primitives;
|
||||
extern crate substrate_metadata;
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate pretty_assertions;
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
#[cfg(test)]
|
||||
extern crate serde_json;
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate parity_codec_derive;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub extern crate parity_codec as codec;
|
||||
pub use self::storage::generator::Storage as GenericStorage;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub mod alloc {
|
||||
pub use std::boxed;
|
||||
pub use std::vec;
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
pub mod dispatch;
|
||||
#[macro_use]
|
||||
pub mod storage;
|
||||
mod hashable;
|
||||
#[macro_use]
|
||||
mod event;
|
||||
#[macro_use]
|
||||
pub mod metadata;
|
||||
|
||||
pub use self::storage::{StorageVec, StorageList, StorageValue, StorageMap};
|
||||
pub use self::hashable::Hashable;
|
||||
pub use self::dispatch::{Parameter, Dispatchable, Callable, IsSubType};
|
||||
pub use runtime_io::print;
|
||||
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! fail {
|
||||
( $y:expr ) => {{
|
||||
return Err($y);
|
||||
}}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! ensure {
|
||||
( $x:expr, $y:expr ) => {{
|
||||
if !$x {
|
||||
fail!($y);
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[cfg(feature = "std")]
|
||||
macro_rules! assert_noop {
|
||||
( $x:expr , $y:expr ) => {
|
||||
let h = runtime_io::storage_root();
|
||||
assert_err!($x, $y);
|
||||
assert_eq!(h, runtime_io::storage_root());
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[cfg(feature = "std")]
|
||||
macro_rules! assert_err {
|
||||
( $x:expr , $y:expr ) => {
|
||||
assert_eq!($x, Err($y));
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[cfg(feature = "std")]
|
||||
macro_rules! assert_ok {
|
||||
( $x:expr ) => {
|
||||
assert_eq!($x, Ok(()));
|
||||
};
|
||||
( $x:expr, $y:expr ) => {
|
||||
assert_eq!($x, Ok($y));
|
||||
}
|
||||
}
|
||||
|
||||
/// The void type - it cannot exist.
|
||||
// Oh rust, you crack me up...
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
pub enum Void {}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_outer_origin {
|
||||
($(#[$attr:meta])* pub enum $name:ident for $trait:ident where system = $system:ident { $( $module:ident ),* }) => {
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
$(#[$attr])*
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum $name {
|
||||
system($system::Origin<$trait>),
|
||||
$(
|
||||
$module($module::Origin),
|
||||
)*
|
||||
#[allow(dead_code)]
|
||||
Void($crate::Void)
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
impl $name {
|
||||
pub const INHERENT: Self = $name::system($system::RawOrigin::Inherent);
|
||||
pub const ROOT: Self = $name::system($system::RawOrigin::Root);
|
||||
pub fn signed(by: <$trait as $system::Trait>::AccountId) -> Self {
|
||||
$name::system($system::RawOrigin::Signed(by))
|
||||
}
|
||||
}
|
||||
impl From<$system::Origin<$trait>> for $name {
|
||||
fn from(x: $system::Origin<$trait>) -> Self {
|
||||
$name::system(x)
|
||||
}
|
||||
}
|
||||
impl Into<Option<$system::Origin<$trait>>> for $name {
|
||||
fn into(self) -> Option<$system::Origin<$trait>> {
|
||||
if let $name::system(l) = self {
|
||||
Some(l)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<Option<<$trait as $system::Trait>::AccountId>> for $name {
|
||||
fn from(x: Option<<$trait as $system::Trait>::AccountId>) -> Self {
|
||||
<$system::Origin<$trait>>::from(x).into()
|
||||
}
|
||||
}
|
||||
$(
|
||||
impl From<$module::Origin> for $name {
|
||||
fn from(x: $module::Origin) -> Self {
|
||||
$name::$module(x)
|
||||
}
|
||||
}
|
||||
impl Into<Option<$module::Origin>> for $name {
|
||||
fn into(self) -> Option<$module::Origin> {
|
||||
if let $name::$module(l) = self {
|
||||
Some(l)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
($(#[$attr:meta])* pub enum $name:ident for $trait:ident { $( $module:ident ),* }) => {
|
||||
impl_outer_origin! {
|
||||
$(#[$attr])*
|
||||
pub enum $name for $trait where system = system {
|
||||
$( $module ),*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
// Copyright 2018 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 alloc;
|
||||
pub use substrate_metadata::JsonMetadata;
|
||||
|
||||
/// Make Box available on `std` and `no_std`.
|
||||
pub type Box<T> = alloc::boxed::Box<T>;
|
||||
/// Make Vec available on `std` and `no_std`.
|
||||
pub type Vec<T> = alloc::vec::Vec<T>;
|
||||
|
||||
/// Implements the json metadata support for the given runtime and all its modules.
|
||||
///
|
||||
/// Example:
|
||||
/// ```compile_fail
|
||||
/// impl_json_metadata!(for RUNTIME_NAME with modules MODULE0, MODULE2, MODULE3 with Storage);
|
||||
/// ```
|
||||
///
|
||||
/// In this example, just `MODULE3` implements the `Storage` trait.
|
||||
#[macro_export]
|
||||
macro_rules! impl_json_metadata {
|
||||
(
|
||||
for $runtime:ident with modules
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
impl $runtime {
|
||||
pub fn json_metadata() -> $crate::metadata::Vec<$crate::metadata::JsonMetadata> {
|
||||
let events = Self::outer_event_json_metadata();
|
||||
__impl_json_metadata!($runtime;
|
||||
$crate::metadata::JsonMetadata::Events {
|
||||
name: events.0,
|
||||
events: events.1,
|
||||
};
|
||||
$( $rest )*
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __impl_json_metadata {
|
||||
(
|
||||
$runtime: ident;
|
||||
$( $metadata:expr ),*;
|
||||
$mod:ident::$module:ident,
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
__impl_json_metadata!(
|
||||
$runtime;
|
||||
$( $metadata, )* $crate::metadata::JsonMetadata::Module {
|
||||
module: $mod::$module::<$runtime>::json_metadata(), prefix: stringify!($mod)
|
||||
};
|
||||
$( $rest )*
|
||||
)
|
||||
};
|
||||
(
|
||||
$runtime: ident;
|
||||
$( $metadata:expr ),*;
|
||||
$mod:ident::$module:ident
|
||||
) => {
|
||||
__impl_json_metadata!(
|
||||
$runtime;
|
||||
$( $metadata, )* $crate::metadata::JsonMetadata::Module {
|
||||
module: $mod::$module::<$runtime>::json_metadata(), prefix: stringify!($mod)
|
||||
};
|
||||
)
|
||||
};
|
||||
(
|
||||
$runtime: ident;
|
||||
$( $metadata:expr ),*;
|
||||
$mod:ident::$module:ident with Storage,
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
__impl_json_metadata!(
|
||||
$runtime;
|
||||
$( $metadata, )* $crate::metadata::JsonMetadata::ModuleWithStorage {
|
||||
module: $mod::$module::<$runtime>::json_metadata(), prefix: stringify!($mod),
|
||||
storage: $mod::$module::<$runtime>::store_json_metadata()
|
||||
};
|
||||
$( $rest )*
|
||||
)
|
||||
};
|
||||
(
|
||||
$runtime: ident;
|
||||
$( $metadata:expr ),*;
|
||||
$mod:ident::$module:ident with Storage
|
||||
) => {
|
||||
__impl_json_metadata!(
|
||||
$runtime;
|
||||
$( $metadata, )* $crate::metadata::JsonMetadata::ModuleWithStorage {
|
||||
module: $mod::$module::<$runtime>::json_metadata(), prefix: stringify!($mod),
|
||||
storage: $mod::$module::<$runtime>::store_json_metadata()
|
||||
};
|
||||
)
|
||||
};
|
||||
(
|
||||
$runtime:ident;
|
||||
$( $metadata:expr ),*;
|
||||
) => {
|
||||
<[_]>::into_vec($crate::metadata::Box::new([ $( $metadata ),* ]))
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
// Do not complain about unused `dispatch` and `dispatch_aux`.
|
||||
#[allow(dead_code)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde;
|
||||
use serde_json;
|
||||
use substrate_metadata::JsonMetadataDecodable;
|
||||
use codec::{Decode, Encode};
|
||||
|
||||
mod system {
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
decl_event!(
|
||||
pub enum Event {
|
||||
SystemEvent,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
mod event_module {
|
||||
use dispatch::Result;
|
||||
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
type Balance;
|
||||
}
|
||||
|
||||
decl_event!(
|
||||
pub enum Event<T> with RawEvent<Balance>
|
||||
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 {
|
||||
fn aux_0(origin) -> Result;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
fn aux_0(_: T::Origin) -> Result {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod event_module2 {
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
type Balance;
|
||||
}
|
||||
|
||||
decl_event!(
|
||||
pub enum Event<T> with RawEvent<Balance>
|
||||
where <T as Trait>::Balance
|
||||
{
|
||||
TestEvent(Balance),
|
||||
}
|
||||
);
|
||||
|
||||
decl_module! {
|
||||
pub struct ModuleWithStorage<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
decl_storage! {
|
||||
trait Store for ModuleWithStorage<T: Trait> as TestStorage {
|
||||
StorageMethod : u32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Deserialize, Serialize)]
|
||||
pub struct TestRuntime;
|
||||
|
||||
impl_outer_event! {
|
||||
pub enum TestEvent for TestRuntime {
|
||||
event_module, event_module2
|
||||
}
|
||||
}
|
||||
|
||||
impl event_module::Trait for TestRuntime {
|
||||
type Origin = u32;
|
||||
type Balance = u32;
|
||||
}
|
||||
|
||||
impl event_module2::Trait for TestRuntime {
|
||||
type Origin = u32;
|
||||
type Balance = u32;
|
||||
}
|
||||
|
||||
impl system::Trait for TestRuntime {
|
||||
type Origin = u32;
|
||||
}
|
||||
|
||||
fn system_event_json() -> &'static str {
|
||||
r#"{ "SystemEvent": { "params": null, "description": [ ] } }"#
|
||||
}
|
||||
|
||||
fn event_module_event_json() -> &'static str {
|
||||
r#"{ "TestEvent": { "params": [ "Balance" ], "description": [ " Hi, I am a comment." ] } }"#
|
||||
}
|
||||
|
||||
fn event_module2_event_json() -> &'static str {
|
||||
r#"{ "TestEvent": { "params": [ "Balance" ], "description": [ ] } }"#
|
||||
}
|
||||
|
||||
impl_json_metadata!(
|
||||
for TestRuntime with modules
|
||||
event_module::Module,
|
||||
event_module2::ModuleWithStorage with Storage
|
||||
);
|
||||
|
||||
const EXPECTED_METADATA: &[JsonMetadata] = &[
|
||||
JsonMetadata::Events {
|
||||
name: "TestEvent",
|
||||
events: &[
|
||||
("system", system_event_json),
|
||||
("event_module", event_module_event_json),
|
||||
("event_module2", event_module2_event_json),
|
||||
]
|
||||
},
|
||||
JsonMetadata::Module {
|
||||
module: concat!(
|
||||
r#"{ "name": "Module", "call": "#,
|
||||
r#"{ "name": "Call", "functions": "#,
|
||||
r#"{ "0": { "name": "aux_0", "params": [ "#,
|
||||
r#"{ "name": "origin", "type": "T::Origin" } ], "#,
|
||||
r#""description": [ ] } } } }"#
|
||||
),
|
||||
prefix: "event_module"
|
||||
},
|
||||
JsonMetadata::ModuleWithStorage {
|
||||
module: r#"{ "name": "ModuleWithStorage", "call": { "name": "Call", "functions": { } } }"#,
|
||||
prefix: "event_module2",
|
||||
storage: concat!(
|
||||
r#"{ "prefix": "TestStorage", "items": { "#,
|
||||
r#""StorageMethod": { "description": [ ], "modifier": null, "type": "u32" }"#,
|
||||
r#" } }"#
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn runtime_json_metadata() {
|
||||
let metadata = TestRuntime::json_metadata();
|
||||
assert_eq!(EXPECTED_METADATA, &metadata[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn json_metadata_encode_and_decode() {
|
||||
let metadata = TestRuntime::json_metadata();
|
||||
let metadata_encoded = metadata.encode();
|
||||
let metadata_decoded = Vec::<JsonMetadataDecodable>::decode(&mut &metadata_encoded[..]);
|
||||
|
||||
assert_eq!(&metadata_decoded.unwrap()[..], &metadata[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn into_json_string_is_valid_json() {
|
||||
let metadata = TestRuntime::json_metadata();
|
||||
let metadata_encoded = metadata.encode();
|
||||
let metadata_decoded = Vec::<JsonMetadataDecodable>::decode(&mut &metadata_encoded[..]);
|
||||
|
||||
for mdata in metadata_decoded.unwrap().into_iter() {
|
||||
let json = mdata.into_json_string();
|
||||
let _: serde::de::IgnoredAny =
|
||||
serde_json::from_str(&json.1).expect(&format!("Is valid json syntax: {}", json.1));
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,609 @@
|
||||
// Copyright 2017 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 rstd::borrow::Borrow;
|
||||
use runtime_io::{self, twox_128};
|
||||
use codec::{Codec, Decode, KeyedVec, Input};
|
||||
|
||||
#[macro_use]
|
||||
pub mod generator;
|
||||
|
||||
// TODO: consider using blake256 to avoid possible preimage attack.
|
||||
|
||||
struct IncrementalInput<'a> {
|
||||
key: &'a [u8],
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
impl<'a> Input for IncrementalInput<'a> {
|
||||
fn read(&mut self, into: &mut [u8]) -> usize {
|
||||
let len = runtime_io::read_storage(self.key, into, self.pos).unwrap_or(0);
|
||||
let read = ::rstd::cmp::min(len, into.len());
|
||||
self.pos += read;
|
||||
read
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `None` if there is no explicit entry.
|
||||
pub fn get<T: Codec + Sized>(key: &[u8]) -> Option<T> {
|
||||
let key = twox_128(key);
|
||||
runtime_io::read_storage(&key[..], &mut [0; 0][..], 0).map(|_| {
|
||||
let mut input = IncrementalInput {
|
||||
key: &key[..],
|
||||
pos: 0,
|
||||
};
|
||||
Decode::decode(&mut input).expect("storage is not null, therefore must be a valid type")
|
||||
})
|
||||
}
|
||||
|
||||
/// 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: Codec + 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: Codec + 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: Codec + 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: Codec>(key: &[u8], value: &T) {
|
||||
value.using_encoded(|slice| runtime_io::set_storage(&twox_128(key)[..], slice));
|
||||
}
|
||||
|
||||
/// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise.
|
||||
pub fn take<T: Codec + 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: Codec + 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: Codec + 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: Codec + 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::exists_storage(&twox_128(key)[..])
|
||||
}
|
||||
|
||||
/// Ensure `key` has no explicit entry in storage.
|
||||
pub fn kill(key: &[u8]) {
|
||||
runtime_io::clear_storage(&twox_128(key)[..]);
|
||||
}
|
||||
|
||||
/// Get a Vec of bytes from storage.
|
||||
pub fn get_raw(key: &[u8]) -> Option<Vec<u8>> {
|
||||
runtime_io::storage(&twox_128(key)[..])
|
||||
}
|
||||
|
||||
/// Put a raw byte slice into storage.
|
||||
pub fn put_raw(key: &[u8], value: &[u8]) {
|
||||
runtime_io::set_storage(&twox_128(key)[..], value)
|
||||
}
|
||||
|
||||
/// The underlying runtime storage.
|
||||
pub struct RuntimeStorage;
|
||||
|
||||
impl ::GenericStorage for RuntimeStorage {
|
||||
fn exists(&self, key: &[u8]) -> bool {
|
||||
super::storage::exists(key)
|
||||
}
|
||||
|
||||
/// Load the bytes of a key from storage. Can panic if the type is incorrect.
|
||||
fn get<T: Codec>(&self, key: &[u8]) -> Option<T> {
|
||||
super::storage::get(key)
|
||||
}
|
||||
|
||||
/// Put a value in under a key.
|
||||
fn put<T: Codec>(&self, key: &[u8], val: &T) {
|
||||
super::storage::put(key, val)
|
||||
}
|
||||
|
||||
/// Remove the bytes of a key from storage.
|
||||
fn kill(&self, key: &[u8]) {
|
||||
super::storage::kill(key)
|
||||
}
|
||||
|
||||
/// Take a value from storage, deleting it after reading.
|
||||
fn take<T: Codec>(&self, key: &[u8]) -> Option<T> {
|
||||
super::storage::take(key)
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for working with macro-generated storage values under the substrate storage API.
|
||||
pub trait StorageValue<T: Codec> {
|
||||
/// The type that get/take return.
|
||||
type Query;
|
||||
|
||||
/// Get the storage key.
|
||||
fn key() -> &'static [u8];
|
||||
|
||||
/// Does the value (explicitly) exist in storage?
|
||||
fn exists() -> bool;
|
||||
|
||||
/// Load the value from the provided storage instance.
|
||||
fn get() -> Self::Query;
|
||||
|
||||
/// Store a value under this key into the provided storage instance.
|
||||
fn put<Arg: Borrow<T>>(val: Arg);
|
||||
|
||||
/// Mutate the value
|
||||
fn mutate<F: FnOnce(&mut Self::Query)>(f: F);
|
||||
|
||||
/// Clear the storage value.
|
||||
fn kill();
|
||||
|
||||
/// Take a value from storage, removing it afterwards.
|
||||
fn take() -> Self::Query;
|
||||
}
|
||||
|
||||
impl<T: Codec, U> StorageValue<T> for U where U: generator::StorageValue<T> {
|
||||
type Query = U::Query;
|
||||
|
||||
fn key() -> &'static [u8] {
|
||||
<U as generator::StorageValue<T>>::key()
|
||||
}
|
||||
fn exists() -> bool {
|
||||
U::exists(&RuntimeStorage)
|
||||
}
|
||||
fn get() -> Self::Query {
|
||||
U::get(&RuntimeStorage)
|
||||
}
|
||||
fn put<Arg: Borrow<T>>(val: Arg) {
|
||||
U::put(val.borrow(), &RuntimeStorage)
|
||||
}
|
||||
fn mutate<F: FnOnce(&mut Self::Query)>(f: F) {
|
||||
U::mutate(f, &RuntimeStorage)
|
||||
}
|
||||
fn kill() {
|
||||
U::kill(&RuntimeStorage)
|
||||
}
|
||||
fn take() -> Self::Query {
|
||||
U::take(&RuntimeStorage)
|
||||
}
|
||||
}
|
||||
|
||||
/// A strongly-typed list in storage.
|
||||
pub trait StorageList<T: Codec> {
|
||||
/// Get the prefix key in storage.
|
||||
fn prefix() -> &'static [u8];
|
||||
|
||||
/// Get the key used to store the length field.
|
||||
fn len_key() -> Vec<u8>;
|
||||
|
||||
/// Get the storage key used to fetch a value at a given index.
|
||||
fn key_for(index: u32) -> Vec<u8>;
|
||||
|
||||
/// Read out all the items.
|
||||
fn items() -> Vec<T>;
|
||||
|
||||
/// Set the current set of items.
|
||||
fn set_items(items: &[T]);
|
||||
|
||||
/// Set the item at the given index.
|
||||
fn set_item<Arg: Borrow<T>>(index: u32, val: Arg);
|
||||
|
||||
/// Load the value at given index. Returns `None` if the index is out-of-bounds.
|
||||
fn get(index: u32) -> Option<T>;
|
||||
|
||||
/// Load the length of the list
|
||||
fn len() -> u32;
|
||||
|
||||
/// Clear the list.
|
||||
fn clear();
|
||||
}
|
||||
|
||||
impl<T: Codec, U> StorageList<T> for U where U: generator::StorageList<T> {
|
||||
fn prefix() -> &'static [u8] {
|
||||
<U as generator::StorageList<T>>::prefix()
|
||||
}
|
||||
|
||||
fn len_key() -> Vec<u8> {
|
||||
<U as generator::StorageList<T>>::len_key()
|
||||
}
|
||||
|
||||
fn key_for(index: u32) -> Vec<u8> {
|
||||
<U as generator::StorageList<T>>::key_for(index)
|
||||
}
|
||||
|
||||
fn items() -> Vec<T> {
|
||||
U::items(&RuntimeStorage)
|
||||
}
|
||||
|
||||
fn set_items(items: &[T]) {
|
||||
U::set_items(items, &RuntimeStorage)
|
||||
}
|
||||
|
||||
fn set_item<Arg: Borrow<T>>(index: u32, val: Arg) {
|
||||
U::set_item(index, val.borrow(), &RuntimeStorage)
|
||||
}
|
||||
|
||||
fn get(index: u32) -> Option<T> {
|
||||
U::get(index, &RuntimeStorage)
|
||||
}
|
||||
|
||||
fn len() -> u32 {
|
||||
U::len(&RuntimeStorage)
|
||||
}
|
||||
|
||||
fn clear() {
|
||||
U::clear(&RuntimeStorage)
|
||||
}
|
||||
}
|
||||
|
||||
/// A strongly-typed map in storage.
|
||||
pub trait StorageMap<K: Codec, V: Codec> {
|
||||
/// The type that get/take return.
|
||||
type Query;
|
||||
|
||||
/// Get the prefix key in storage.
|
||||
fn prefix() -> &'static [u8];
|
||||
|
||||
/// Get the storage key used to fetch a value corresponding to a specific key.
|
||||
fn key_for<KeyArg: Borrow<K>>(key: KeyArg) -> Vec<u8>;
|
||||
|
||||
/// Does the value (explicitly) exist in storage?
|
||||
fn exists<KeyArg: Borrow<K>>(key: KeyArg) -> bool;
|
||||
|
||||
/// Load the value associated with the given key from the map.
|
||||
fn get<KeyArg: Borrow<K>>(key: KeyArg) -> Self::Query;
|
||||
|
||||
/// Store a value to be associated with the given key from the map.
|
||||
fn insert<KeyArg: Borrow<K>, ValArg: Borrow<V>>(key: KeyArg, val: ValArg);
|
||||
|
||||
/// Remove the value under a key.
|
||||
fn remove<KeyArg: Borrow<K>>(key: KeyArg);
|
||||
|
||||
/// Mutate the value under a key.
|
||||
fn mutate<KeyArg: Borrow<K>, F: FnOnce(&mut Self::Query)>(key: KeyArg, f: F);
|
||||
|
||||
/// Take the value under a key.
|
||||
fn take<KeyArg: Borrow<K>>(key: KeyArg) -> Self::Query;
|
||||
}
|
||||
|
||||
impl<K: Codec, V: Codec, U> StorageMap<K, V> for U where U: generator::StorageMap<K, V> {
|
||||
type Query = U::Query;
|
||||
|
||||
fn prefix() -> &'static [u8] {
|
||||
<U as generator::StorageMap<K, V>>::prefix()
|
||||
}
|
||||
|
||||
fn key_for<KeyArg: Borrow<K>>(key: KeyArg) -> Vec<u8> {
|
||||
<U as generator::StorageMap<K, V>>::key_for(key.borrow())
|
||||
}
|
||||
|
||||
fn exists<KeyArg: Borrow<K>>(key: KeyArg) -> bool {
|
||||
U::exists(key.borrow(), &RuntimeStorage)
|
||||
}
|
||||
|
||||
fn get<KeyArg: Borrow<K>>(key: KeyArg) -> Self::Query {
|
||||
U::get(key.borrow(), &RuntimeStorage)
|
||||
}
|
||||
|
||||
fn insert<KeyArg: Borrow<K>, ValArg: Borrow<V>>(key: KeyArg, val: ValArg) {
|
||||
U::insert(key.borrow(), val.borrow(), &RuntimeStorage)
|
||||
}
|
||||
|
||||
fn remove<KeyArg: Borrow<K>>(key: KeyArg) {
|
||||
U::remove(key.borrow(), &RuntimeStorage)
|
||||
}
|
||||
|
||||
fn mutate<KeyArg: Borrow<K>, F: FnOnce(&mut Self::Query)>(key: KeyArg, f: F) {
|
||||
U::mutate(key.borrow(), f, &RuntimeStorage)
|
||||
}
|
||||
|
||||
fn take<KeyArg: Borrow<K>>(key: KeyArg) -> Self::Query {
|
||||
U::take(key.borrow(), &RuntimeStorage)
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait to conveniently store a vector of storable data.
|
||||
pub trait StorageVec {
|
||||
type Item: Default + Sized + Codec;
|
||||
const PREFIX: &'static [u8];
|
||||
|
||||
/// Get the current set of items.
|
||||
fn items() -> Vec<Self::Item> {
|
||||
(0..Self::count()).into_iter().map(Self::item).collect()
|
||||
}
|
||||
|
||||
/// Set the current set of items.
|
||||
fn set_items<I, T>(items: I)
|
||||
where
|
||||
I: IntoIterator<Item=T>,
|
||||
T: Borrow<Self::Item>,
|
||||
{
|
||||
let mut count: u32 = 0;
|
||||
|
||||
for i in items.into_iter() {
|
||||
put(&count.to_keyed_vec(Self::PREFIX), i.borrow());
|
||||
count = count.checked_add(1).expect("exceeded runtime storage capacity");
|
||||
}
|
||||
|
||||
Self::set_count(count);
|
||||
}
|
||||
|
||||
/// Push an item.
|
||||
fn push(item: &Self::Item) {
|
||||
let len = Self::count();
|
||||
put(&len.to_keyed_vec(Self::PREFIX), item);
|
||||
Self::set_count(len + 1);
|
||||
}
|
||||
|
||||
fn set_item(index: u32, item: &Self::Item) {
|
||||
if index < Self::count() {
|
||||
put(&index.to_keyed_vec(Self::PREFIX), item);
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_item(index: u32) {
|
||||
if index < Self::count() {
|
||||
kill(&index.to_keyed_vec(Self::PREFIX));
|
||||
}
|
||||
}
|
||||
|
||||
fn item(index: u32) -> Self::Item {
|
||||
get_or_default(&index.to_keyed_vec(Self::PREFIX))
|
||||
}
|
||||
|
||||
fn set_count(count: u32) {
|
||||
(count..Self::count()).for_each(Self::clear_item);
|
||||
put(&b"len".to_keyed_vec(Self::PREFIX), &count);
|
||||
}
|
||||
|
||||
fn count() -> u32 {
|
||||
get_or_default(&b"len".to_keyed_vec(Self::PREFIX))
|
||||
}
|
||||
}
|
||||
|
||||
pub mod unhashed {
|
||||
use rstd::borrow::Borrow;
|
||||
use super::{runtime_io, Codec, Decode, KeyedVec, Vec, IncrementalInput};
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `None` if there is no explicit entry.
|
||||
pub fn get<T: Codec + Sized>(key: &[u8]) -> Option<T> {
|
||||
runtime_io::read_storage(key, &mut [0; 0][..], 0).map(|_| {
|
||||
let mut input = IncrementalInput {
|
||||
key,
|
||||
pos: 0,
|
||||
};
|
||||
Decode::decode(&mut input).expect("storage is not null, therefore must be a valid type")
|
||||
})
|
||||
}
|
||||
|
||||
/// 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: Codec + 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: Codec + 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: Codec + 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: Codec>(key: &[u8], value: &T) {
|
||||
value.using_encoded(|slice| runtime_io::set_storage(key, slice));
|
||||
}
|
||||
|
||||
/// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise.
|
||||
pub fn take<T: Codec + 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: Codec + 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: Codec + 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: Codec + 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::read_storage(key, &mut [0;0][..], 0).is_some()
|
||||
}
|
||||
|
||||
/// Ensure `key` has no explicit entry in storage.
|
||||
pub fn kill(key: &[u8]) {
|
||||
runtime_io::clear_storage(key);
|
||||
}
|
||||
|
||||
/// Ensure keys with the given `prefix` have no entries in storage.
|
||||
pub fn kill_prefix(prefix: &[u8]) {
|
||||
runtime_io::clear_prefix(prefix);
|
||||
}
|
||||
|
||||
/// Get a Vec of bytes from storage.
|
||||
pub fn get_raw(key: &[u8]) -> Option<Vec<u8>> {
|
||||
runtime_io::storage(key)
|
||||
}
|
||||
|
||||
/// Put a raw byte slice into storage.
|
||||
pub fn put_raw(key: &[u8], value: &[u8]) {
|
||||
runtime_io::set_storage(key, value)
|
||||
}
|
||||
|
||||
/// A trait to conveniently store a vector of storable data.
|
||||
pub trait StorageVec {
|
||||
type Item: Default + Sized + Codec;
|
||||
const PREFIX: &'static [u8];
|
||||
|
||||
/// Get the current set of items.
|
||||
fn items() -> Vec<Self::Item> {
|
||||
(0..Self::count()).into_iter().map(Self::item).collect()
|
||||
}
|
||||
|
||||
/// Set the current set of items.
|
||||
fn set_items<I, T>(items: I)
|
||||
where
|
||||
I: IntoIterator<Item=T>,
|
||||
T: Borrow<Self::Item>,
|
||||
{
|
||||
let mut count: u32 = 0;
|
||||
|
||||
for i in items.into_iter() {
|
||||
put(&count.to_keyed_vec(Self::PREFIX), i.borrow());
|
||||
count = count.checked_add(1).expect("exceeded runtime storage capacity");
|
||||
}
|
||||
|
||||
Self::set_count(count);
|
||||
}
|
||||
|
||||
fn set_item(index: u32, item: &Self::Item) {
|
||||
if index < Self::count() {
|
||||
put(&index.to_keyed_vec(Self::PREFIX), item);
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_item(index: u32) {
|
||||
if index < Self::count() {
|
||||
kill(&index.to_keyed_vec(Self::PREFIX));
|
||||
}
|
||||
}
|
||||
|
||||
fn item(index: u32) -> Self::Item {
|
||||
get_or_default(&index.to_keyed_vec(Self::PREFIX))
|
||||
}
|
||||
|
||||
fn set_count(count: u32) {
|
||||
(count..Self::count()).for_each(Self::clear_item);
|
||||
put(&b"len".to_keyed_vec(Self::PREFIX), &count);
|
||||
}
|
||||
|
||||
fn count() -> u32 {
|
||||
get_or_default(&b"len".to_keyed_vec(Self::PREFIX))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use runtime_io::{twox_128, TestExternalities, with_externalities};
|
||||
|
||||
#[test]
|
||||
fn integers_can_be_stored() {
|
||||
let mut t = TestExternalities::new();
|
||||
with_externalities(&mut t, || {
|
||||
let x = 69u32;
|
||||
put(b":test", &x);
|
||||
let y: u32 = get(b":test").unwrap();
|
||||
assert_eq!(x, y);
|
||||
});
|
||||
with_externalities(&mut t, || {
|
||||
let x = 69426942i64;
|
||||
put(b":test", &x);
|
||||
let y: i64 = get(b":test").unwrap();
|
||||
assert_eq!(x, y);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bools_can_be_stored() {
|
||||
let mut t = TestExternalities::new();
|
||||
with_externalities(&mut t, || {
|
||||
let x = true;
|
||||
put(b":test", &x);
|
||||
let y: bool = get(b":test").unwrap();
|
||||
assert_eq!(x, y);
|
||||
});
|
||||
|
||||
with_externalities(&mut t, || {
|
||||
let x = false;
|
||||
put(b":test", &x);
|
||||
let y: bool = get(b":test").unwrap();
|
||||
assert_eq!(x, y);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vecs_can_be_retrieved() {
|
||||
let mut t = TestExternalities::new();
|
||||
with_externalities(&mut t, || {
|
||||
runtime_io::set_storage(&twox_128(b":test"), b"\x0b\0\0\0Hello world");
|
||||
let x = b"Hello world".to_vec();
|
||||
let y = get::<Vec<u8>>(b":test").unwrap();
|
||||
assert_eq!(x, y);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vecs_can_be_stored() {
|
||||
let mut t = TestExternalities::new();
|
||||
let x = b"Hello world".to_vec();
|
||||
|
||||
with_externalities(&mut t, || {
|
||||
put(b":test", &x);
|
||||
});
|
||||
|
||||
with_externalities(&mut t, || {
|
||||
let y: Vec<u8> = get(b":test").unwrap();
|
||||
assert_eq!(x, y);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user