Update SRML Assets: add total supply query, refactor with specific tests (#1185)

* Update SRML Assets: add total supply query, refactor with specific unit tests, update assertions

* Add feature and tests to allow querying total supply

* Add assertion and tests to ensure that transfer amount is greater than or equal to one unit

* Replace broad `it_works` function test with various specific unit tests

* Fix `destroy` function by moving assertion before the action

* Fix typos `Transfered` should be `Transferred`, `requried` should be `required`

* Reference: https://hackmd.io/nr6kPD2sR4urmljtvHs0CQ?view#Assets-Module

* refactor: Order imports alphabetically

* review-fix: Replace non-zero check with shorter equivalent

* review-fix: Restore order of non-zero assertion and destroy account

* Update lib.rs
This commit is contained in:
Luke Schoen
2018-12-14 08:32:59 +01:00
committed by Gav Wood
parent 27f69def9a
commit b2ce2f4bd9
+90 -9
View File
@@ -73,6 +73,7 @@ decl_module! {
<NextAssetId<T>>::mutate(|id| *id += 1); <NextAssetId<T>>::mutate(|id| *id += 1);
<Balances<T>>::insert((id, origin.clone()), total); <Balances<T>>::insert((id, origin.clone()), total);
<TotalSupply<T>>::insert(id, total);
Self::deposit_event(RawEvent::Issued(id, origin, total)); Self::deposit_event(RawEvent::Issued(id, origin, total));
} }
@@ -82,9 +83,10 @@ decl_module! {
let origin = ensure_signed(origin)?; let origin = ensure_signed(origin)?;
let origin_account = (id, origin.clone()); let origin_account = (id, origin.clone());
let origin_balance = <Balances<T>>::get(&origin_account); let origin_balance = <Balances<T>>::get(&origin_account);
ensure!(origin_balance >= amount, "origin account balance must be greater than amount"); ensure!(!amount.is_zero(), "transfer amount should be non-zero");
ensure!(origin_balance >= amount, "origin account balance must be greater than or equal to the transfer amount");
Self::deposit_event(RawEvent::Transfered(id, origin, target.clone(), amount)); Self::deposit_event(RawEvent::Transferred(id, origin, target.clone(), amount));
<Balances<T>>::insert(origin_account, origin_balance - amount); <Balances<T>>::insert(origin_account, origin_balance - amount);
<Balances<T>>::mutate((id, target), |balance| *balance += amount); <Balances<T>>::mutate((id, target), |balance| *balance += amount);
} }
@@ -92,10 +94,10 @@ decl_module! {
/// Destroy any assets of `id` owned by `origin`. /// Destroy any assets of `id` owned by `origin`.
fn destroy(origin, id: AssetId) { fn destroy(origin, id: AssetId) {
let origin = ensure_signed(origin)?; let origin = ensure_signed(origin)?;
let balance = <Balances<T>>::take((id, origin.clone())); let balance = <Balances<T>>::take((id, origin.clone()));
ensure!(!balance.is_zero(), "origin balance should be non-zero"); ensure!(!balance.is_zero(), "origin balance should be non-zero");
<TotalSupply<T>>::mutate(id, |total_supply| *total_supply -= balance);
Self::deposit_event(RawEvent::Destroyed(id, origin, balance)); Self::deposit_event(RawEvent::Destroyed(id, origin, balance));
} }
} }
@@ -108,8 +110,8 @@ decl_event!(
pub enum Event<T> where <T as system::Trait>::AccountId, <T as Trait>::Balance { pub enum Event<T> where <T as system::Trait>::AccountId, <T as Trait>::Balance {
/// Some assets were issued. /// Some assets were issued.
Issued(AssetId, AccountId, Balance), Issued(AssetId, AccountId, Balance),
/// Some assets were transfered. /// Some assets were transferred.
Transfered(AssetId, AccountId, AccountId, Balance), Transferred(AssetId, AccountId, AccountId, Balance),
/// Some assets were destroyed. /// Some assets were destroyed.
Destroyed(AssetId, AccountId, Balance), Destroyed(AssetId, AccountId, Balance),
} }
@@ -121,6 +123,8 @@ decl_storage! {
Balances: map (AssetId, T::AccountId) => T::Balance; Balances: map (AssetId, T::AccountId) => T::Balance;
/// The next asset identifier up for grabs. /// The next asset identifier up for grabs.
NextAssetId get(next_asset_id): AssetId; NextAssetId get(next_asset_id): AssetId;
/// The total unit supply of an asset
TotalSupply: map AssetId => T::Balance;
} }
} }
@@ -132,6 +136,11 @@ impl<T: Trait> Module<T> {
pub fn balance(id: AssetId, who: T::AccountId) -> T::Balance { pub fn balance(id: AssetId, who: T::AccountId) -> T::Balance {
<Balances<T>>::get((id, who)) <Balances<T>>::get((id, who))
} }
// Get the total supply of an asset `id`
pub fn total_supply(id: AssetId) -> T::Balance {
<TotalSupply<T>>::get(id)
}
} }
#[cfg(test)] #[cfg(test)]
@@ -141,7 +150,7 @@ mod tests {
use runtime_io::with_externalities; use runtime_io::with_externalities;
use substrate_primitives::{H256, Blake2Hasher}; use substrate_primitives::{H256, Blake2Hasher};
// The testing primitives are very useful for avoiding having to work with signatures // 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. // or public keys. `u64` is used as the `AccountId` and no `Signature`s are required.
use primitives::{BuildStorage, traits::{BlakeTwo256}, testing::{Digest, DigestItem, Header}}; use primitives::{BuildStorage, traits::{BlakeTwo256}, testing::{Digest, DigestItem, Header}};
impl_outer_origin! { impl_outer_origin! {
@@ -178,16 +187,88 @@ mod tests {
} }
#[test] #[test]
fn it_works() { fn issuing_asset_units_to_issuer_should_work() {
with_externalities(&mut new_test_ext(), || {
assert_ok!(Assets::issue(Origin::signed(1), 100));
assert_eq!(Assets::balance(0, 1), 100);
});
}
#[test]
fn querying_total_supply_should_work() {
with_externalities(&mut new_test_ext(), || { with_externalities(&mut new_test_ext(), || {
assert_ok!(Assets::issue(Origin::signed(1), 100)); assert_ok!(Assets::issue(Origin::signed(1), 100));
assert_eq!(Assets::balance(0, 1), 100); assert_eq!(Assets::balance(0, 1), 100);
assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50));
assert_eq!(Assets::balance(0, 1), 50); assert_eq!(Assets::balance(0, 1), 50);
assert_eq!(Assets::balance(0, 2), 50); assert_eq!(Assets::balance(0, 2), 50);
assert_ok!(Assets::destroy(Origin::signed(2), 0)); assert_ok!(Assets::transfer(Origin::signed(2), 0, 3, 31));
assert_eq!(Assets::balance(0, 1), 50);
assert_eq!(Assets::balance(0, 2), 19);
assert_eq!(Assets::balance(0, 3), 31);
assert_ok!(Assets::destroy(Origin::signed(3), 0));
assert_eq!(Assets::total_supply(0), 69);
});
}
#[test]
fn transferring_amount_above_available_balance_should_work() {
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);
});
}
#[test]
fn transferring_amount_less_than_available_balance_should_not_work() {
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(1), 0));
assert_eq!(Assets::balance(0, 1), 0);
assert_noop!(Assets::transfer(Origin::signed(1), 0, 1, 50), "origin account balance must be greater than or equal to the transfer amount");
});
}
#[test]
fn transferring_less_than_one_unit_should_not_work() {
with_externalities(&mut new_test_ext(), || {
assert_ok!(Assets::issue(Origin::signed(1), 100));
assert_eq!(Assets::balance(0, 1), 100);
assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 0), "transfer amount should be non-zero");
});
}
#[test]
fn transferring_more_units_than_total_supply_should_not_work() {
with_externalities(&mut new_test_ext(), || {
assert_ok!(Assets::issue(Origin::signed(1), 100));
assert_eq!(Assets::balance(0, 1), 100);
assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 101), "origin account balance must be greater than or equal to the transfer amount");
});
}
#[test]
fn destroying_asset_balance_with_positive_balance_should_work() {
with_externalities(&mut new_test_ext(), || {
assert_ok!(Assets::issue(Origin::signed(1), 100));
assert_eq!(Assets::balance(0, 1), 100);
assert_ok!(Assets::destroy(Origin::signed(1), 0));
});
}
#[test]
fn destroying_asset_balance_with_zero_balance_should_not_work() {
with_externalities(&mut new_test_ext(), || {
assert_ok!(Assets::issue(Origin::signed(1), 100));
assert_eq!(Assets::balance(0, 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"); assert_noop!(Assets::destroy(Origin::signed(2), 0), "origin balance should be non-zero");
}); });
} }
} }