Add Assets module (#925)

* Add Assets module

* Fixes

* Fix

* Update comments

* Support `GenesisConfig` without any fields

Fixes: #923

* Do not generate an empty `GenesisConfig`, instead generate no `GenesisConfig`
This commit is contained in:
Gav Wood
2018-10-20 12:43:00 +02:00
committed by GitHub
parent 7f6862ba5e
commit 583c24f017
13 changed files with 360 additions and 42 deletions
+17
View File
@@ -2568,6 +2568,23 @@ dependencies = [
"sr-std 0.1.0",
]
[[package]]
name = "srml-assets"
version = "0.1.0"
dependencies = [
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-io 0.1.0",
"sr-primitives 0.1.0",
"sr-std 0.1.0",
"srml-support 0.1.0",
"srml-system 0.1.0",
"substrate-primitives 0.1.0",
]
[[package]]
name = "srml-balances"
version = "0.1.0"
+1
View File
@@ -39,6 +39,7 @@ members = [
"core/transaction-pool",
"core/transaction-pool/graph",
"srml/support",
"srml/assets",
"srml/balances",
"srml/consensus",
"srml/contract",
+1 -1
View File
@@ -791,7 +791,7 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
match self.storage.state_db.revert_one() {
Some(commit) => {
apply_state_commit(&mut transaction, commit);
let removed = best.clone();
let _removed = best.clone();
best -= As::sa(1);
let header = self.blockchain.header(BlockId::Number(best))?.ok_or_else(
|| client::error::ErrorKind::UnknownBlock(
+16 -3
View File
@@ -269,19 +269,32 @@ pub fn verify_encoded_lazy<V: Verify, T: codec::Encode>(sig: &V, item: &T, signe
#[macro_export]
macro_rules! __impl_outer_config_types {
($concrete:ident $config:ident $snake:ident $($rest:ident)*) => {
(
$concrete:ident $config:ident $snake:ident < $ignore:ident > $( $rest:tt )*
) => {
#[cfg(any(feature = "std", test))]
pub type $config = $snake::GenesisConfig<$concrete>;
__impl_outer_config_types! {$concrete $($rest)*}
};
(
$concrete:ident $config:ident $snake:ident $( $rest:tt )*
) => {
#[cfg(any(feature = "std", test))]
pub type $config = $snake::GenesisConfig;
__impl_outer_config_types! {$concrete $($rest)*}
};
($concrete:ident) => ()
}
#[macro_export]
/// Implement the output "meta" module configuration struct.
macro_rules! impl_outer_config {
( pub struct $main:ident for $concrete:ident { $( $config:ident => $snake:ident, )* } ) => {
__impl_outer_config_types! { $concrete $( $config $snake )* }
(
pub struct $main:ident for $concrete:ident {
$( $config:ident => $snake:ident $( < $generic:ident > )*, )*
}
) => {
__impl_outer_config_types! { $concrete $( $config $snake $( < $generic > )* )* }
#[cfg(any(feature = "std", test))]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
+1 -1
View File
@@ -566,5 +566,5 @@ pub trait ProvideInherent {
pub trait Extrinsic {
/// Is this `Extrinsic` signed?
/// If no information are available about signed/unsigned, `None` should be returned.
fn is_signed(&self) -> Option<bool>;
fn is_signed(&self) -> Option<bool> { None }
}
+4 -4
View File
@@ -197,18 +197,18 @@ construct_runtime!(
UncheckedExtrinsic = UncheckedExtrinsic
{
System: system::{default, Log(ChangesTrieRoot)},
Consensus: consensus::{Module, Call, Storage, Config, Log(AuthoritiesChange), Inherent},
Consensus: consensus::{Module, Call, Storage, Config<T>, Log(AuthoritiesChange), Inherent},
Balances: balances,
Timestamp: timestamp::{Module, Call, Storage, Config, Inherent},
Timestamp: timestamp::{Module, Call, Storage, Config<T>, Inherent},
Session: session,
Staking: staking,
Democracy: democracy,
Council: council::{Module, Call, Storage, Event<T>},
CouncilVoting: council_voting,
CouncilMotions: council_motions::{Module, Call, Storage, Event<T>, Origin},
CouncilSeats: council_seats::{Config},
CouncilSeats: council_seats::{Config<T>},
Treasury: treasury,
Contract: contract::{Module, Call, Config, Event<T>},
Contract: contract::{Module, Call, Config<T>, Event<T>},
}
);
+32
View File
@@ -0,0 +1,32 @@
[package]
name = "srml-assets"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
hex-literal = "0.1.0"
serde = { version = "1.0", default-features = false }
serde_derive = { version = "1.0", optional = true }
parity-codec = { version = "2.1", default-features = false }
parity-codec-derive = { version = "2.1", default-features = false }
substrate-primitives = { path = "../../core/primitives", default-features = false }
sr-std = { path = "../../core/sr-std", default-features = false }
sr-io = { path = "../../core/sr-io", default-features = false }
sr-primitives = { path = "../../core/sr-primitives", default-features = false }
srml-support = { path = "../support", default-features = false }
srml-system = { path = "../system", default-features = false }
[features]
default = ["std"]
std = [
"serde/std",
"serde_derive",
"parity-codec/std",
"parity-codec-derive/std",
"substrate-primitives/std",
"sr-std/std",
"sr-io/std",
"sr-primitives/std",
"srml-support/std",
"srml-system/std",
]
+223
View File
@@ -0,0 +1,223 @@
// Copyright 2017-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/>.
//! A simple, secure module for dealing with fungible assets.
// Ensure we're `no_std` when compiling for Wasm.
#![cfg_attr(not(feature = "std"), no_std)]
// Assert macros used in tests.
extern crate sr_std;
// Needed for tests (`with_externalities`).
#[cfg(test)]
extern crate sr_io as runtime_io;
// Needed for the set of mock primitives used in our tests.
#[cfg(test)]
extern crate substrate_primitives;
// Needed for deriving `Serialize` and `Deserialize` for various types.
// We only implement the serde traits for std builds - they're unneeded
// in the wasm runtime.
#[cfg(feature = "std")]
#[macro_use]
extern crate serde_derive;
// Needed for deriving `Encode` and `Decode` for `RawEvent`.
#[macro_use]
extern crate parity_codec_derive;
extern crate parity_codec as codec;
// Needed for type-safe access to storage DB.
#[macro_use]
extern crate srml_support as runtime_support;
// Needed for various traits. In our case, `OnFinalise`.
extern crate sr_primitives as primitives;
// `system` module provides us with all sorts of useful stuff and macros
// depend on it being around.
extern crate srml_system as system;
use primitives::traits::OnFinalise;
use runtime_support::{StorageValue, StorageMap, dispatch::Result, Parameter};
use primitives::traits::{Member, SimpleArithmetic, Zero};
use system::ensure_signed;
pub trait Trait: system::Trait {
/// The overarching event type.
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
/// The units in which we record balances.
type Balance: Member + Parameter + SimpleArithmetic + Default + Copy;
}
type AssetId = u32;
decl_module! {
// Simple declaration of the `Module` type. Lets the macro know what its working on.
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
/// Issue a new class of fungible assets. There are, and will only ever be, `total`
/// such assets and they'll all belong to the `origin` initially. It will have an
/// identifier `AssetId` instance: this will be specified in the `Issued` event.
fn issue(origin, total: T::Balance) -> Result;
/// Move some assets from one holder to another.
fn transfer(origin, id: AssetId, target: T::AccountId, total: T::Balance) -> Result;
/// Destroy any assets of `id` owned by `origin`.
fn destroy(origin, id: AssetId) -> Result;
}
}
/// An event in this module. Events are simple means of reporting specific conditions and
/// circumstances that have happened that users, Dapps and/or chain explorers would find
/// interesting and otherwise difficult to detect.
decl_event!(
pub enum Event<T> where <T as system::Trait>::AccountId, <T as Trait>::Balance {
/// Some assets were issued.
Issued(AssetId, AccountId, Balance),
/// Some assets were transfered.
Transfered(AssetId, AccountId, AccountId, Balance),
/// Some assets were destroyed.
Destroyed(AssetId, AccountId, Balance),
}
);
decl_storage! {
trait Store for Module<T: Trait> as Assets {
/// The number of units of assets held by any given account.
Balances: map (AssetId, T::AccountId) => T::Balance;
/// The next asset identifier up for grabs.
NextAssetId get(next_asset_id): AssetId;
}
}
// The main implementation block for the module.
impl<T: Trait> Module<T> {
/// Deposit one of this module's events.
// TODO: move into `decl_module` macro.
fn deposit_event(event: Event<T>) {
<system::Module<T>>::deposit_event(<T as Trait>::Event::from(event).into());
}
// Public immutables
/// Get the asset `id` balance of `who`.
pub fn balance(id: AssetId, who: T::AccountId) -> T::Balance {
<Balances<T>>::get((id, who))
}
// Implement Calls and add public immutables and private mutables.
fn issue(origin: T::Origin, total: T::Balance) -> Result {
let origin = ensure_signed(origin)?;
let id = Self::next_asset_id();
<NextAssetId<T>>::mutate(|id| *id += 1);
<Balances<T>>::insert((id, origin.clone()), total);
Self::deposit_event(RawEvent::Issued(id, origin, total));
Ok(())
}
fn transfer(origin: T::Origin, id: AssetId, target: T::AccountId, amount: T::Balance) -> Result {
let origin = ensure_signed(origin)?;
let origin_account = (id, origin.clone());
let origin_balance = <Balances<T>>::get(&origin_account);
ensure!(origin_balance >= amount, "origin account balance must be greater than amount");
Self::deposit_event(RawEvent::Transfered(id, origin, target.clone(), amount));
<Balances<T>>::insert(origin_account, origin_balance - amount);
<Balances<T>>::mutate((id, target), |balance| *balance += amount);
Ok(())
}
fn destroy(origin: T::Origin, id: AssetId) -> Result {
let origin = ensure_signed(origin)?;
let balance = <Balances<T>>::take((id, origin.clone()));
ensure!(!balance.is_zero(), "origin balance should be non-zero");
Self::deposit_event(RawEvent::Destroyed(id, origin, balance));
Ok(())
}
}
// This trait expresses what should happen when the block is finalised.
impl<T: Trait> OnFinalise<T::BlockNumber> for Module<T> {}
#[cfg(test)]
mod tests {
use super::*;
use runtime_io::with_externalities;
use substrate_primitives::{H256, Blake2Hasher};
// The testing primitives are very useful for avoiding having to work with signatures
// or public keys. `u64` is used as the `AccountId` and no `Signature`s are requried.
use primitives::{BuildStorage, traits::{BlakeTwo256}, testing::{Digest, DigestItem, Header}};
impl_outer_origin! {
pub enum Origin for Test {}
}
// For testing the module, we construct most of a mock runtime. This means
// first constructing a configuration type (`Test`) which `impl`s each of the
// configuration traits of modules we want to use.
#[derive(Clone, Eq, PartialEq)]
pub struct Test;
impl system::Trait for Test {
type Origin = Origin;
type Index = u64;
type BlockNumber = u64;
type Hash = H256;
type Hashing = BlakeTwo256;
type Digest = Digest;
type AccountId = u64;
type Header = Header;
type Event = ();
type Log = DigestItem;
}
impl Trait for Test {
type Event = ();
type Balance = u64;
}
type Assets = Module<Test>;
// This function basically just builds a genesis storage key/value store according to
// our desired mockup.
fn new_test_ext() -> runtime_io::TestExternalities<Blake2Hasher> {
system::GenesisConfig::<Test>::default().build_storage().unwrap().into()
}
#[test]
fn it_works() {
with_externalities(&mut new_test_ext(), || {
assert_ok!(Assets::issue(Origin::signed(1), 100));
assert_eq!(Assets::balance(0, 1), 100);
assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50));
assert_eq!(Assets::balance(0, 1), 50);
assert_eq!(Assets::balance(0, 2), 50);
assert_ok!(Assets::destroy(Origin::signed(2), 0));
assert_eq!(Assets::balance(0, 2), 0);
assert_noop!(Assets::transfer(Origin::signed(2), 0, 1, 50), "origin account balance must be greater than amount");
});
}
}
+5 -5
View File
@@ -20,15 +20,15 @@ srml-balances = { path = "../balances", default-features = false }
[features]
default = ["std"]
std = [
"sr-std/std",
"sr-io/std",
"srml-support/std",
"sr-primitives/std",
"srml-balances/std",
"serde/std",
"serde_derive",
"parity-codec/std",
"parity-codec-derive/std",
"sr-std/std",
"sr-io/std",
"sr-primitives/std",
"substrate-primitives/std",
"srml-support/std",
"srml-system/std",
"srml-balances/std",
]
+13 -16
View File
@@ -25,12 +25,15 @@ extern crate sr_std;
// Needed for tests (`with_externalities`).
#[cfg(test)]
extern crate sr_io as runtime_io;
extern crate sr_io;
// Needed for the set of mock primitives used in our tests.
#[cfg(test)]
extern crate substrate_primitives;
// Needed for various traits. In our case, `OnFinalise`.
extern crate sr_primitives;
// Needed for deriving `Serialize` and `Deserialize` for various types.
// We only implement the serde traits for std builds - they're unneeded
// in the wasm runtime.
@@ -45,10 +48,7 @@ extern crate parity_codec as codec;
// Needed for type-safe access to storage DB.
#[macro_use]
extern crate srml_support as runtime_support;
// Needed for various traits. In our case, `OnFinalise`.
extern crate sr_primitives as runtime_primitives;
extern crate srml_support as support;
// `system` module provides us with all sorts of useful stuff and macros
// depend on it being around.
extern crate srml_system as system;
@@ -57,8 +57,8 @@ extern crate srml_system as system;
// might find it useful).
extern crate srml_balances as balances;
use runtime_primitives::traits::OnFinalise;
use runtime_support::{StorageValue, dispatch::Result};
use sr_primitives::traits::OnFinalise;
use support::{StorageValue, dispatch::Result};
use system::ensure_signed;
/// Our module's configuration trait. All our types and consts go in here. If the
@@ -147,8 +147,8 @@ decl_storage! {
// e.g. pub Bar get(bar): map T::AccountId => Vec<(T::Balance, u64)>;
//
// For basic value items, you'll get a type which implements
// `runtime_support::StorageValue`. For map items, you'll get a type which
// implements `runtime_support::StorageMap`.
// `support::StorageValue`. For map items, you'll get a type which
// implements `support::StorageMap`.
//
// If they have a getter (`get(getter_name)`), then your module will come
// equipped with `fn getter_name() -> Type` for basic value items or
@@ -241,6 +241,7 @@ impl<T: Trait> Module<T> {
Ok(())
}
#[allow(dead_code)]
fn accumulate_foo(origin: T::Origin, increase_by: T::Balance) -> Result {
let _sender = ensure_signed(origin)?;
@@ -278,15 +279,11 @@ impl<T: Trait> OnFinalise<T::BlockNumber> for Module<T> {
mod tests {
use super::*;
use runtime_io::with_externalities;
use sr_io::with_externalities;
use substrate_primitives::{H256, Blake2Hasher};
use runtime_primitives::BuildStorage;
use runtime_primitives::traits::{BlakeTwo256};
use runtime_primitives::testing::DigestItem;
// The testing primitives are very useful for avoiding having to work with signatures
// or public keys. `u64` is used as the `AccountId` and no `Signature`s are requried.
use runtime_primitives::testing::{Digest, Header};
use sr_primitives::{BuildStorage, traits::{BlakeTwo256}, testing::{Digest, DigestItem, Header}};
impl_outer_origin! {
pub enum Origin for Test {}
@@ -323,7 +320,7 @@ mod tests {
// This function basically just builds a genesis storage key/value store according to
// our desired mockup.
fn new_test_ext() -> runtime_io::TestExternalities<Blake2Hasher> {
fn new_test_ext() -> sr_io::TestExternalities<Blake2Hasher> {
let mut t = system::GenesisConfig::<Test>::default().build_storage().unwrap();
// We use default for brevity, but you can configure as desired if needed.
t.extend(balances::GenesisConfig::<Test>::default().build_storage().unwrap());
+31 -11
View File
@@ -42,7 +42,7 @@
/// - `Storage`
/// - `Event` or `Event<T>` (if the event is generic)
/// - `Origin` or `Origin<T>` (if the origin is generic)
/// - `Config`
/// - `Config` or `Config<T>` (if the config is generic)
/// - `Log( $(IDENT),* )`
#[macro_export]
macro_rules! construct_runtime {
@@ -102,7 +102,7 @@ macro_rules! construct_runtime {
$( ( $( $expanded_modules_args ),* ) )*
),*
},
)* $name: $module::{Module, Call, Storage, Event<T>, Config};
)* $name: $module::{Module, Call, Storage, Event<T>, Config<T>};
$(
$rest_name: $rest_module $(
::{
@@ -165,7 +165,7 @@ macro_rules! construct_runtime {
},
)*
$name: $module::{
Module, Call, Storage, Event<T>, Config,
Module, Call, Storage, Event<T>, Config<T>,
$(
$modules $( <$modules_generic> )* $( ( $( $modules_args ),* ) )*
),*
@@ -952,7 +952,7 @@ macro_rules! __decl_outer_log {
macro_rules! __decl_outer_config {
(
$runtime:ident;
$( $parsed_modules:ident :: $parsed_name:ident ),*;
$( $parsed_modules:ident :: $parsed_name:ident $( < $parsed_generic:ident > )* ),*;
$name:ident: $module:ident::{
Config $(, $modules:ident $( <$modules_generic:ident> )* )*
}
@@ -962,7 +962,7 @@ macro_rules! __decl_outer_config {
) => {
__decl_outer_config!(
$runtime;
$( $parsed_modules :: $parsed_name, )* $module::$name;
$( $parsed_modules :: $parsed_name $( < $parsed_generic > )*, )* $module::$name;
$(
$rest_name: $rest_module::{
$( $rest_modules $( <$rest_modules_generic> )* ),*
@@ -972,7 +972,27 @@ macro_rules! __decl_outer_config {
};
(
$runtime:ident;
$( $parsed_modules:ident :: $parsed_name:ident ),*;
$( $parsed_modules:ident :: $parsed_name:ident $( < $parsed_generic:ident > )* ),*;
$name:ident: $module:ident::{
Config<T> $(, $modules:ident $( <$modules_generic:ident> )* )*
}
$(, $rest_name:ident : $rest_module:ident::{
$( $rest_modules:ident $( <$rest_modules_generic:ident> )* ),*
})*;
) => {
__decl_outer_config!(
$runtime;
$( $parsed_modules :: $parsed_name $( < $parsed_generic > )*, )* $module::$name<T>;
$(
$rest_name: $rest_module::{
$( $rest_modules $( <$rest_modules_generic> )* ),*
}
),*;
);
};
(
$runtime:ident;
$( $parsed_modules:ident :: $parsed_name:ident $( < $parsed_generic:ident > )* ),*;
$name:ident: $module:ident::{
$ingore:ident $( <$ignor:ident> )* $(, $modules:ident $( <$modules_generic:ident> )* )*
}
@@ -982,7 +1002,7 @@ macro_rules! __decl_outer_config {
) => {
__decl_outer_config!(
$runtime;
$( $parsed_modules :: $parsed_name ),*;
$( $parsed_modules :: $parsed_name $( < $parsed_generic > )*),*;
$name: $module::{ $( $modules $( <$modules_generic> )* ),* }
$(
, $rest_name: $rest_module::{
@@ -993,7 +1013,7 @@ macro_rules! __decl_outer_config {
};
(
$runtime:ident;
$( $parsed_modules:ident :: $parsed_name:ident ),*;
$( $parsed_modules:ident :: $parsed_name:ident $( < $parsed_generic:ident > )* ),*;
$name:ident: $module:ident::{}
$(, $rest_name:ident : $rest_module:ident::{
$( $rest_modules:ident $( <$rest_modules_generic:ident> )* ),*
@@ -1001,7 +1021,7 @@ macro_rules! __decl_outer_config {
) => {
__decl_outer_config!(
$runtime;
$( $parsed_modules :: $parsed_name ),*;
$( $parsed_modules :: $parsed_name $( < $parsed_generic > )*),*;
$(
$rest_name: $rest_module::{
$( $rest_modules $( <$rest_modules_generic> )* ),*
@@ -1011,14 +1031,14 @@ macro_rules! __decl_outer_config {
};
(
$runtime:ident;
$( $parsed_modules:ident :: $parsed_name:ident ),*;
$( $parsed_modules:ident :: $parsed_name:ident $( < $parsed_generic:ident > )* ),*;
;
) => {
substrate_generate_ident_name! {
impl_outer_config!(
pub struct GenesisConfig for $runtime {
$(
"config-ident" $parsed_name => $parsed_modules,
"config-ident" $parsed_name => $parsed_modules $( < $parsed_generic > )*,
)*
}
);
@@ -590,6 +590,21 @@ macro_rules! __generate_genesis_config {
);
};
// Do not generate any `GenesisConfig`, if we not require it.
(@GEN
[$traittype:ident $traitinstance:ident]
// normal getters
[]
// for normal builders
[$( $normalclassname:ident ($normalbuild:expr) ;)*]
// for map builders
[$( $mapclassname:ident ($mapbuild:expr) ;)*]
// extra genesis fields
[]
// final build storage call
[$call:expr]
) => {};
(@GEN
[$traittype:ident $traitinstance:ident]
// normal getters
+1 -1
View File
@@ -33,7 +33,7 @@
#![cfg_attr(not(feature = "std"), no_std)]
#[macro_use]
#[cfg_attr(not(feature = "std"), macro_use)]
extern crate sr_std as rstd;
#[macro_use]